#include "vinumhdr.h"
#include "request.h"
#include <sys/resourcevar.h>
void complete_raid5_write(struct rqelement *);
void complete_rqe(struct bio *bio);
void sdio_done(struct bio *bio);
void
complete_rqe(struct bio *bio)
{
union daemoninfo di;
struct buf *bp = bio->bio_buf;
struct rqelement *rqe;
struct request *rq;
struct rqgroup *rqg;
struct bio *ubio;
struct drive *drive;
struct sd *sd;
char *gravity;
get_mplock();
rqe = (struct rqelement *) bp;
rqg = rqe->rqg;
rq = rqg->rq;
ubio = rq->bio;
#ifdef VINUMDEBUG
if (debug & DEBUG_LASTREQS)
logrq(loginfo_iodone, (union rqinfou) rqe, ubio);
#endif
drive = &DRIVE[rqe->driveno];
drive->active--;
vinum_conf.active--;
if ((drive->active == (DRIVE_MAXACTIVE - 1))
||(vinum_conf.active == VINUM_MAXACTIVE))
wakeup(&launch_requests);
if ((bp->b_flags & B_ERROR) != 0) {
gravity = "";
sd = &SD[rqe->sdno];
if (bp->b_error != 0)
rq->error = bp->b_error;
else if (rq->error == 0)
rq->error = EIO;
sd->lasterror = rq->error;
if (bp->b_cmd == BUF_CMD_READ) {
if ((rq->error == ENXIO) || (sd->flags & VF_RETRYERRORS) == 0) {
gravity = " fatal";
set_sd_state(rqe->sdno, sd_crashed, setstate_force);
}
log(LOG_ERR,
"%s:%s read error, offset %lld for %d bytes\n",
gravity,
sd->name,
(long long)bio->bio_offset,
bp->b_bcount);
} else {
if ((rq->error == ENXIO) || (sd->flags & VF_RETRYERRORS) == 0) {
gravity = "fatal ";
set_sd_state(rqe->sdno, sd_stale, setstate_force);
}
log(LOG_ERR,
"%s:%s write error, offset %lld for %d bytes\n",
gravity,
sd->name,
(long long)bio->bio_offset,
bp->b_bcount);
}
log(LOG_ERR,
"%s: user buffer offset %lld for %d bytes\n",
sd->name,
(long long)ubio->bio_offset,
ubio->bio_buf->b_bcount);
if (rq->error == ENXIO) {
log(LOG_ERR,
"%s: fatal drive I/O error, offset %lld for %d bytes\n",
DRIVE[rqe->driveno].label.name,
(long long)bio->bio_offset,
bp->b_bcount);
DRIVE[rqe->driveno].lasterror = rq->error;
set_drive_state(rqe->driveno,
drive_down,
setstate_force);
}
}
if (bp->b_cmd == BUF_CMD_READ) {
DRIVE[rqe->driveno].reads++;
DRIVE[rqe->driveno].bytes_read += bp->b_bcount;
SD[rqe->sdno].reads++;
SD[rqe->sdno].bytes_read += bp->b_bcount;
PLEX[rqe->rqg->plexno].reads++;
PLEX[rqe->rqg->plexno].bytes_read += bp->b_bcount;
if (PLEX[rqe->rqg->plexno].volno >= 0) {
VOL[PLEX[rqe->rqg->plexno].volno].reads++;
VOL[PLEX[rqe->rqg->plexno].volno].bytes_read += bp->b_bcount;
}
} else {
DRIVE[rqe->driveno].writes++;
DRIVE[rqe->driveno].bytes_written += bp->b_bcount;
SD[rqe->sdno].writes++;
SD[rqe->sdno].bytes_written += bp->b_bcount;
PLEX[rqe->rqg->plexno].writes++;
PLEX[rqe->rqg->plexno].bytes_written += bp->b_bcount;
if (PLEX[rqe->rqg->plexno].volno >= 0) {
VOL[PLEX[rqe->rqg->plexno].volno].writes++;
VOL[PLEX[rqe->rqg->plexno].volno].bytes_written += bp->b_bcount;
}
}
if (rqg->flags & XFR_RECOVERY_READ) {
int *sdata;
int *data;
int length;
int count;
struct rqelement *urqe = &rqg->rqe[rqg->badsdno];
sdata = (int *) &rqe->b.b_data[rqe->groupoffset << DEV_BSHIFT];
data = (int *) &urqe->b.b_data[urqe->groupoffset << DEV_BSHIFT];
length = urqe->grouplen * (DEV_BSIZE / sizeof(int));
for (count = 0; count < length; count++)
data[count] ^= sdata[count];
if (rqe->flags & XFR_NORMAL_READ) {
char *src = &rqe->b.b_data[rqe->dataoffset << DEV_BSHIFT];
char *dst;
dst = (char *) ubio->bio_buf->b_data + (rqe->useroffset << DEV_BSHIFT);
length = rqe->datalen << DEV_BSHIFT;
bcopy(src, dst, length);
}
} else if ((rqg->flags & (XFR_NORMAL_WRITE | XFR_DEGRADED_WRITE))
&&(rqg->active == 1))
complete_raid5_write(rqe);
rqg->active--;
if (rqg->active == 0) {
rq->active--;
if (rqg->lock) {
unlockrange(rqg->plexno, rqg->lock);
rqg->lock = 0;
}
}
if (rq->active == 0) {
#ifdef VINUMDEBUG
if (debug & DEBUG_RESID) {
if (ubio->bio_buf->b_resid != 0)
Debugger("resid");
}
#endif
if (rq->error) {
if (rq->isplex) {
ubio->bio_buf->b_flags |= B_ERROR;
ubio->bio_buf->b_error = rq->error;
} else {
di.rq = rq;
queue_daemon_request(daemonrq_ioerror, di);
}
} else {
ubio->bio_buf->b_resid = 0;
if (rq->isplex == 0)
VOL[rq->volplex.volno].active--;
biodone(ubio);
freerq(rq);
}
}
rel_mplock();
}
void
freerq(struct request *rq)
{
struct rqgroup *rqg;
struct rqgroup *nrqg;
int rqno;
for (rqg = rq->rqg; rqg != NULL; rqg = nrqg) {
if (rqg->lock)
unlockrange(rqg->plexno, rqg->lock);
for (rqno = 0; rqno < rqg->count; rqno++) {
if ((rqg->rqe[rqno].flags & XFR_MALLOCED)
&&rqg->rqe[rqno].b.b_data)
Free(rqg->rqe[rqno].b.b_data);
if (rqg->rqe[rqno].flags & XFR_BUFLOCKED) {
BUF_UNLOCK(&rqg->rqe[rqno].b);
uninitbufbio(&rqg->rqe[rqno].b);
}
}
nrqg = rqg->next;
Free(rqg);
}
Free(rq);
}
void
sdio_done(struct bio *bio)
{
struct sdbuf *sbp;
get_mplock();
sbp = (struct sdbuf *) bio->bio_buf;
if (sbp->b.b_flags & B_ERROR) {
sbp->bio->bio_buf->b_flags |= B_ERROR;
sbp->bio->bio_buf->b_error = sbp->b.b_error;
}
#ifdef VINUMDEBUG
if (debug & DEBUG_LASTREQS)
logrq(loginfo_sdiodone, (union rqinfou)bio, bio);
#endif
sbp->bio->bio_buf->b_resid = sbp->b.b_resid;
if (sbp->b.b_cmd == BUF_CMD_READ) {
DRIVE[sbp->driveno].reads++;
DRIVE[sbp->driveno].bytes_read += sbp->b.b_bcount;
SD[sbp->sdno].reads++;
SD[sbp->sdno].bytes_read += sbp->b.b_bcount;
} else {
DRIVE[sbp->driveno].writes++;
DRIVE[sbp->driveno].bytes_written += sbp->b.b_bcount;
SD[sbp->sdno].writes++;
SD[sbp->sdno].bytes_written += sbp->b.b_bcount;
}
biodone_sync(bio);
biodone(sbp->bio);
BUF_UNLOCK(&sbp->b);
uninitbufbio(&sbp->b);
Free(sbp);
rel_mplock();
}
void
complete_raid5_write(struct rqelement *rqe)
{
int *sdata;
int *pdata;
int length;
int count;
int rqno;
int rqoffset;
struct bio *ubio;
struct request *rq;
struct rqgroup *rqg;
struct rqelement *prqe;
struct drive *drive;
rqg = rqe->rqg;
rq = rqg->rq;
ubio = rq->bio;
prqe = &rqg->rqe[0];
if (rqe->flags & XFR_DEGRADED_WRITE) {
pdata = (int *) (&prqe->b.b_data[(prqe->groupoffset) << DEV_BSHIFT]);
bzero(pdata, prqe->grouplen << DEV_BSHIFT);
for (rqno = 1; rqno < rqg->count; rqno++) {
rqe = &rqg->rqe[rqno];
sdata = (int *) (&rqe->b.b_data[rqe->groupoffset << DEV_BSHIFT]);
length = rqe->grouplen << (DEV_BSHIFT - 2);
for (count = 0; count < length; count++)
pdata[count] ^= sdata[count];
if ((rqe->flags & XFR_MALLOCED)
&&((rqg->flags & XFR_NORMAL_WRITE) == 0)) {
Free(rqe->b.b_data);
rqe->flags &= ~XFR_MALLOCED;
}
}
}
if (rqg->flags & XFR_NORMAL_WRITE) {
for (rqno = 1; rqno < rqg->count; rqno++) {
rqe = &rqg->rqe[rqno];
if ((rqe->flags & (XFR_DATA_BLOCK | XFR_BAD_SUBDISK | XFR_NORMAL_WRITE))
== (XFR_DATA_BLOCK | XFR_NORMAL_WRITE)) {
sdata = (int *) &rqe->b.b_data[rqe->dataoffset << DEV_BSHIFT];
rqoffset = rqe->dataoffset + rqe->sdoffset - prqe->sdoffset;
pdata = (int *) (&prqe->b.b_data[rqoffset << DEV_BSHIFT]);
length = rqe->datalen * (DEV_BSIZE / sizeof(int));
if ((pdata < ((int *) prqe->b.b_data))
|| (&pdata[length] > ((int *) (prqe->b.b_data + prqe->b.b_bcount)))
|| (sdata < ((int *) rqe->b.b_data))
|| (&sdata[length] > ((int *) (rqe->b.b_data + rqe->b.b_bcount))))
panic("complete_raid5_write: bounds overflow");
for (count = 0; count < length; count++)
pdata[count] ^= sdata[count];
sdata = (int *) (&ubio->bio_buf->b_data[rqe->useroffset << DEV_BSHIFT]);
if ((sdata < ((int *) ubio->bio_buf->b_data))
|| (&sdata[length] > ((int *) (ubio->bio_buf->b_data + ubio->bio_buf->b_bcount))))
panic("complete_raid5_write: bounds overflow");
for (count = 0; count < length; count++)
pdata[count] ^= sdata[count];
if (rqe->flags & XFR_MALLOCED) {
Free(rqe->b.b_data);
rqe->flags &= ~XFR_MALLOCED;
} else
panic("complete_raid5_write: malloc conflict");
if ((rqe->b.b_cmd == BUF_CMD_READ)
&&((rqe->flags & XFR_BAD_SUBDISK) == 0)) {
rqe->b.b_cmd = BUF_CMD_WRITE;
rqe->b.b_bio1.bio_done = complete_rqe;
rqe->flags &= ~XFR_PARITYOP;
rqe->b.b_data = &ubio->bio_buf->b_data[rqe->useroffset << DEV_BSHIFT];
rqe->b.b_bcount = rqe->datalen << DEV_BSHIFT;
rqe->b.b_resid = rqe->b.b_bcount;
rqe->b.b_bio1.bio_offset += (off_t)rqe->dataoffset << DEV_BSHIFT;
drive = &DRIVE[rqe->driveno];
rqe->b.b_bio1.bio_driver_info = drive->dev;
rqg->active++;
drive->active++;
if (drive->active >= drive->maxactive)
drive->maxactive = drive->active;
vinum_conf.active++;
if (vinum_conf.active >= vinum_conf.maxactive)
vinum_conf.maxactive = vinum_conf.active;
#ifdef VINUMDEBUG
if (debug & DEBUG_ADDRESSES)
log(LOG_DEBUG,
" %s dev %s, sd %d, offset 0x%jx, devoffset 0x%jx, length %d\n",
(rqe->b.b_cmd == BUF_CMD_READ) ? "Read" : "Write",
drive->devicename,
rqe->sdno,
(uintmax_t)(rqe->b.b_bio1.bio_offset - ((off_t)SD[rqe->sdno].driveoffset << DEV_BSHIFT)),
(uintmax_t)rqe->b.b_bio1.bio_offset,
rqe->b.b_bcount);
if (debug & DEBUG_LASTREQS)
logrq(loginfo_raid5_data, (union rqinfou) rqe, ubio);
#endif
vn_strategy(drive->vp, &rqe->b.b_bio1);
}
}
}
}
rqe = &rqg->rqe[0];
rqe->b.b_cmd = BUF_CMD_WRITE;
rqe->b.b_bio1.bio_done = complete_rqe;
rqg->flags &= ~XFR_PARITYOP;
rqe->b.b_bcount = rqe->buflen << DEV_BSHIFT;
rqe->b.b_resid = rqe->b.b_bcount;
drive = &DRIVE[rqe->driveno];
rqe->b.b_bio1.bio_driver_info = drive->dev;
rqg->active++;
drive->active++;
if (drive->active >= drive->maxactive)
drive->maxactive = drive->active;
vinum_conf.active++;
if (vinum_conf.active >= vinum_conf.maxactive)
vinum_conf.maxactive = vinum_conf.active;
#ifdef VINUMDEBUG
if (debug & DEBUG_ADDRESSES)
log(LOG_DEBUG,
" %s dev %s, sd %d, offset 0x%jx, devoffset 0x%jx, length %d\n",
(rqe->b.b_cmd == BUF_CMD_READ) ? "Read" : "Write",
drive->devicename,
rqe->sdno,
(uintmax_t)(rqe->b.b_bio1.bio_offset - ((off_t)SD[rqe->sdno].driveoffset << DEV_BSHIFT)),
(uintmax_t)rqe->b.b_bio1.bio_offset,
rqe->b.b_bcount);
if (debug & DEBUG_LASTREQS)
logrq(loginfo_raid5_parity, (union rqinfou) rqe, ubio);
#endif
vn_strategy(drive->vp, &rqe->b.b_bio1);
}