#include "vinumhdr.h"
#include "request.h"
int
revive_block(int sdno)
{
struct sd *sd;
struct plex *plex;
struct volume *vol;
struct buf *bp;
cdev_t dev;
int error = EAGAIN;
int size;
vinum_off_t plexblkno;
int psd;
u_int64_t stripe;
int paritysd = 0;
struct rangelock *lock;
vinum_off_t stripeoffset;
plexblkno = 0;
sd = &SD[sdno];
lock = NULL;
if (sd->plexno < 0)
return EINVAL;
plex = &PLEX[sd->plexno];
if (plex->volno >= 0)
vol = &VOL[plex->volno];
else
vol = NULL;
if ((sd->revive_blocksize == 0)
||(sd->revive_blocksize & ((1 << DEV_BSHIFT) - 1)))
sd->revive_blocksize = DEFAULT_REVIVE_BLOCKSIZE;
else if (sd->revive_blocksize > MAX_REVIVE_BLOCKSIZE)
sd->revive_blocksize = MAX_REVIVE_BLOCKSIZE;
size = u64min(sd->revive_blocksize >> DEV_BSHIFT, sd->sectors - sd->revived) << DEV_BSHIFT;
sd->reviver = curproc->p_pid;
switch (plex->organization) {
case plex_concat:
plexblkno = sd->revived + sd->plexoffset;
break;
case plex_striped:
stripeoffset = sd->revived % plex->stripesize;
if (stripeoffset + (size >> DEV_BSHIFT) > plex->stripesize)
size = (plex->stripesize - stripeoffset) << DEV_BSHIFT;
plexblkno = sd->plexoffset
+ (sd->revived - stripeoffset) * plex->subdisks
+ stripeoffset;
break;
case plex_raid4:
case plex_raid5:
stripeoffset = sd->revived % plex->stripesize;
plexblkno = sd->plexoffset
+ (sd->revived - stripeoffset) * (plex->subdisks - 1)
+stripeoffset;
stripe = (sd->revived / plex->stripesize);
size = u64min(size, (plex->stripesize - stripeoffset) << DEV_BSHIFT);
if (plex->organization == plex_raid4)
psd = plex->subdisks - 1;
else
psd = plex->subdisks - 1 - stripe % plex->subdisks;
paritysd = plex->sdnos[psd] == sdno;
if (sd->plexsdno > psd)
plexblkno -= plex->stripesize;
else if (paritysd)
plexblkno -= plex->stripesize * sd->plexsdno;
break;
case plex_disorg:
break;
}
if (paritysd) {
bp = parityrebuild(plex, sd->revived, size, rebuildparity, &lock, NULL);
if (bp == NULL)
return ENOMEM;
} else {
bp = getpbuf(&vinum_conf.physbufs);
bp->b_data = Malloc(size);
bp->b_bcount = size;
bp->b_resid = bp->b_bcount;
bp->b_bio1.bio_offset = (off_t)plexblkno << DEV_BSHIFT;
bp->b_bio1.bio_done = biodone_sync;
bp->b_bio1.bio_flags |= BIO_SYNC;
if (isstriped(plex))
lock = lockrange(plexblkno << DEV_BSHIFT, bp, plex);
if (vol != NULL)
dev = vol->vol_dev;
else
dev = PLEX[sd->plexno].plex_dev;
bp->b_cmd = BUF_CMD_READ;
vinumstart(dev, &bp->b_bio1, 1);
biowait(&bp->b_bio1, "drvrd");
}
if (bp->b_flags & B_ERROR)
error = bp->b_error;
else
{
dev = SD[sdno].sd_dev;
KKASSERT(dev != NULL);
bp->b_cmd = BUF_CMD_WRITE;
bp->b_resid = bp->b_bcount;
bp->b_bio1.bio_offset = (off_t)sd->revived << DEV_BSHIFT;
bp->b_bio1.bio_driver_info = dev;
bp->b_bio1.bio_done = biodone_sync;
sdio(&bp->b_bio1);
biowait(&bp->b_bio1, "drvwr");
if (bp->b_flags & B_ERROR)
error = bp->b_error;
else {
sd->revived += bp->b_bcount >> DEV_BSHIFT;
if (sd->revived >= sd->sectors) {
sd->revived = 0;
set_sd_state(sdno, sd_up, setstate_force);
log(LOG_INFO, "vinum: %s is %s\n", sd->name, sd_state(sd->state));
save_config();
error = 0;
}
}
if (lock)
unlockrange(sd->plexno, lock);
while (sd->waitlist) {
#ifdef VINUMDEBUG
struct request *rq = sd->waitlist;
cdev_t dev;
if (debug & DEBUG_REVIVECONFLICT) {
dev = rq->bio->bio_driver_info;
log(LOG_DEBUG,
"Relaunch revive conflict sd %d: %p\n%s dev %d.%d, offset 0x%jx, length %d\n",
rq->sdno,
rq,
(rq->bio->bio_buf->b_cmd == BUF_CMD_READ) ? "Read" : "Write",
major(dev),
minor(dev),
(uintmax_t)rq->bio->bio_offset,
rq->bio->bio_buf->b_bcount);
}
#endif
launch_requests(sd->waitlist, 1);
sd->waitlist = sd->waitlist->next;
}
}
Free(bp->b_data);
relpbuf(bp, &vinum_conf.physbufs);
return error;
}
void
parityops(struct vinum_ioctl_msg *data)
{
int plexno;
struct plex *plex;
int size;
struct rangelock *lock;
struct _ioctl_reply *reply;
off_t pstripe;
struct buf *pbp;
off_t errorloc;
enum parityop op;
plexno = data->index;
op = data->op;
pbp = NULL;
reply = (struct _ioctl_reply *) data;
reply->error = EAGAIN;
plex = &PLEX[plexno];
if (!isparity(plex)) {
reply->error = EINVAL;
return;
} else if (plex->state < plex_flaky) {
reply->error = EIO;
strcpy(reply->msg, "Plex is not completely accessible\n");
return;
}
pstripe = data->offset;
size = imin(DEFAULT_REVIVE_BLOCKSIZE,
plex->stripesize << DEV_BSHIFT);
errorloc = 0;
pbp = parityrebuild(plex, pstripe, size, op, &lock, &errorloc);
if (pbp == NULL) {
reply->error = ENOMEM;
return;
}
reply->msg[0] = '\0';
if ((pbp->b_flags & B_ERROR) == 0) {
if ((op == rebuildparity)
|| (op == rebuildandcheckparity)) {
pbp->b_cmd = BUF_CMD_WRITE;
pbp->b_resid = pbp->b_bcount;
pbp->b_bio1.bio_done = biodone_sync;
sdio(&pbp->b_bio1);
biowait(&pbp->b_bio1, "drvwr");
}
if (((op == checkparity)
|| (op == rebuildandcheckparity))
&& (errorloc != -1)) {
if (op == checkparity)
reply->error = EIO;
ksprintf(reply->msg,
"Parity incorrect at offset 0x%llx\n",
(long long)errorloc);
}
if (reply->error == EAGAIN) {
plex->checkblock = pstripe + (pbp->b_bcount >> DEV_BSHIFT);
if (plex->checkblock >= SD[plex->sdnos[0]].sectors) {
plex->checkblock = 0;
reply->error = 0;
}
}
}
if (pbp->b_flags & B_ERROR)
reply->error = pbp->b_error;
Free(pbp->b_data);
relpbuf(pbp, &vinum_conf.physbufs);
unlockrange(plexno, lock);
}
struct buf *
parityrebuild(struct plex *plex,
vinum_off_t pstripe,
int size,
enum parityop op,
struct rangelock **lockp,
off_t * errorloc)
{
int error;
int sdno;
u_int64_t stripe;
int *parity_buf;
int *newparity_buf;
int mysize;
int isize;
int i;
int psd;
int newpsd;
struct buf **bpp;
struct buf *pbp;
int *sbuf;
int bufcount;
stripe = pstripe / plex->stripesize;
psd = plex->subdisks - 1 - stripe % plex->subdisks;
parity_buf = NULL;
error = 0;
mysize = u64min(size, (plex->stripesize * (stripe + 1) - pstripe) << DEV_BSHIFT);
isize = mysize / (sizeof(int));
bufcount = plex->subdisks + 1;
newpsd = plex->subdisks;
bpp = (struct buf **) Malloc(bufcount * sizeof(struct buf *));
for (sdno = 0; sdno < bufcount; sdno++) {
if ((sdno != psd) || (op != rebuildparity)) {
bpp[sdno] = getpbuf(&vinum_conf.physbufs);
bpp[sdno]->b_data = Malloc(mysize);
if (sdno == psd)
parity_buf = (int *) bpp[sdno]->b_data;
if (sdno == newpsd)
bpp[sdno]->b_bio1.bio_driver_info = SD[plex->sdnos[psd]].sd_dev;
else
bpp[sdno]->b_bio1.bio_driver_info = SD[plex->sdnos[sdno]].sd_dev;
KKASSERT(bpp[sdno]->b_bio1.bio_driver_info);
bpp[sdno]->b_cmd = BUF_CMD_READ;
bpp[sdno]->b_bcount = mysize;
bpp[sdno]->b_resid = bpp[sdno]->b_bcount;
bpp[sdno]->b_bio1.bio_offset = (off_t)pstripe << DEV_BSHIFT;
bpp[sdno]->b_bio1.bio_done = biodone_sync;
}
}
pbp = bpp[newpsd];
newparity_buf = (int *) bpp[newpsd]->b_data;
bzero(newparity_buf, mysize);
*lockp = lockrange(pstripe * plex->stripesize * (plex->subdisks - 1),
bpp[psd ? 0 : 1],
plex);
for (sdno = 0; sdno < plex->subdisks; sdno++) {
if ((sdno != psd) || (op != rebuildparity)) {
sdio(&bpp[sdno]->b_bio1);
}
}
for (sdno = 0; sdno < plex->subdisks; sdno++) {
if ((sdno != psd) || (op != rebuildparity)) {
biowait(&bpp[sdno]->b_bio1, "drvio");
if (bpp[sdno]->b_flags & B_ERROR)
error = bpp[sdno]->b_error;
else if (sdno != psd) {
sbuf = (int *) bpp[sdno]->b_data;
for (i = 0; i < isize; i++)
newparity_buf[i] ^= sbuf[i];
}
}
if (sdno != psd) {
Free(bpp[sdno]->b_data);
relpbuf(bpp[sdno], &vinum_conf.physbufs);
}
}
if ((op == checkparity)
|| (op == rebuildandcheckparity)) {
*errorloc = -1;
for (i = 0; i < isize; i++) {
if (parity_buf[i] != newparity_buf[i]) {
*errorloc = (off_t) (pstripe << DEV_BSHIFT) * (plex->subdisks - 1)
+ i * sizeof(int);
break;
}
}
Free(bpp[psd]->b_data);
relpbuf(bpp[psd], &vinum_conf.physbufs);
}
Free(bpp);
if (error) {
pbp->b_flags |= B_ERROR;
pbp->b_error = error;
}
return pbp;
}
int
initsd(int sdno, int verify)
{
struct sd *sd;
struct plex *plex;
struct buf *bp;
int error;
int size;
int verified;
error = 0;
sd = &SD[sdno];
if (sd->plexno < 0)
return EINVAL;
plex = &PLEX[sd->plexno];
if (sd->init_blocksize == 0) {
if (plex->stripesize != 0)
sd->init_blocksize = u64min(DEFAULT_REVIVE_BLOCKSIZE,
plex->stripesize << DEV_BSHIFT);
else
sd->init_blocksize = DEFAULT_REVIVE_BLOCKSIZE;
} else if (sd->init_blocksize > MAX_REVIVE_BLOCKSIZE)
sd->init_blocksize = MAX_REVIVE_BLOCKSIZE;
size = u64min(sd->init_blocksize >> DEV_BSHIFT, sd->sectors - sd->initialized) << DEV_BSHIFT;
bp = getpbuf(&vinum_conf.physbufs);
bp->b_data = Malloc(size);
verified = 0;
while (!verified) {
bp->b_bcount = size;
bp->b_resid = bp->b_bcount;
bp->b_bio1.bio_offset = (off_t)sd->initialized << DEV_BSHIFT;
bp->b_bio1.bio_driver_info = SD[sdno].sd_dev;
bp->b_bio1.bio_done = biodone_sync;
KKASSERT(bp->b_bio1.bio_driver_info);
bzero(bp->b_data, bp->b_bcount);
bp->b_cmd = BUF_CMD_WRITE;
sdio(&bp->b_bio1);
biowait(&bp->b_bio1, "drvwr");
if (bp->b_flags & B_ERROR)
error = bp->b_error;
if ((error == 0) && verify) {
bp->b_bcount = size;
bp->b_resid = bp->b_bcount;
bp->b_bio1.bio_offset = (off_t)sd->initialized << DEV_BSHIFT;
bp->b_bio1.bio_driver_info = SD[sdno].sd_dev;
bp->b_bio1.bio_done = biodone_sync;
KKASSERT(bp->b_bio1.bio_driver_info);
bp->b_cmd = BUF_CMD_READ;
sdio(&bp->b_bio1);
biowait(&bp->b_bio1, "drvrd");
if (bp->b_flags & B_ERROR)
error = bp->b_error;
else if ((*bp->b_data != 0)
||(bcmp(bp->b_data, &bp->b_data[1], bp->b_bcount - 1))) {
kprintf("vinum: init error on %s, offset 0x%llx sectors\n",
sd->name,
(long long) sd->initialized);
verified = 0;
} else
verified = 1;
} else
verified = 1;
}
Free(bp->b_data);
relpbuf(bp, &vinum_conf.physbufs);
if (error == 0) {
sd->initialized += size >> DEV_BSHIFT;
if (sd->initialized >= sd->sectors) {
sd->initialized = 0;
set_sd_state(sdno, sd_initialized, setstate_force);
log(LOG_INFO, "vinum: %s is %s\n", sd->name, sd_state(sd->state));
save_config();
} else
error = EAGAIN;
}
return error;
}