#include "vinumhdr.h"
#include "request.h"
#include <sys/resourcevar.h>
enum requeststatus bre(struct request *rq,
int plexno,
vinum_off_t * diskstart,
vinum_off_t diskend);
enum requeststatus bre5(struct request *rq,
int plexno,
vinum_off_t * diskstart,
vinum_off_t diskend);
enum requeststatus build_read_request(struct request *rq, int volplexno);
enum requeststatus build_write_request(struct request *rq);
enum requeststatus build_rq_buffer(struct rqelement *rqe, struct plex *plex);
int find_alternate_sd(struct request *rq);
int check_range_covered(struct request *);
void complete_rqe(struct bio *bio);
void complete_raid5_write(struct rqelement *);
int abortrequest(struct request *rq, int error);
void sdio_done(struct bio *bio);
struct bio *vinum_bounds_check(struct bio *bio, struct volume *vol);
caddr_t allocdatabuf(struct rqelement *rqe);
void freedatabuf(struct rqelement *rqe);
#ifdef VINUMDEBUG
struct rqinfo rqinfo[RQINFO_SIZE];
struct rqinfo *rqip = rqinfo;
void
logrq(enum rqinfo_type type, union rqinfou info, struct bio *ubio)
{
cdev_t dev;
crit_enter();
microtime(&rqip->timestamp);
rqip->type = type;
rqip->bio = ubio;
switch (type) {
case loginfo_user_bp:
case loginfo_user_bpl:
case loginfo_sdio:
case loginfo_sdiol:
case loginfo_sdiodone:
bcopy(info.bio, &rqip->info.bio, sizeof(struct bio));
dev = info.bio->bio_driver_info;
rqip->devmajor = major(dev);
rqip->devminor = minor(dev);
break;
case loginfo_iodone:
case loginfo_rqe:
case loginfo_raid5_data:
case loginfo_raid5_parity:
bcopy(info.rqe, &rqip->info.rqe, sizeof(struct rqelement));
dev = info.rqe->b.b_bio1.bio_driver_info;
rqip->devmajor = major(dev);
rqip->devminor = minor(dev);
break;
case loginfo_lockwait:
case loginfo_lock:
case loginfo_unlock:
bcopy(info.lockinfo, &rqip->info.lockinfo, sizeof(struct rangelock));
break;
case loginfo_unused:
break;
}
rqip++;
if (rqip >= &rqinfo[RQINFO_SIZE])
rqip = rqinfo;
crit_exit();
}
#endif
int
vinumstrategy(struct dev_strategy_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
struct bio *bio = ap->a_bio;
struct buf *bp = bio->bio_buf;
struct bio *nbio = bio;
struct volume *vol = NULL;
int volno;
switch (DEVTYPE(dev)) {
case VINUM_SD_TYPE:
case VINUM_RAWSD_TYPE:
bio->bio_driver_info = dev;
sdio(bio);
break;
case VINUM_DRIVE_TYPE:
default:
bp->b_error = EIO;
bp->b_flags |= B_ERROR;
biodone(bio);
break;
case VINUM_VOLUME_TYPE:
volno = Volno(dev);
vol = &VOL[volno];
if (vol->state != volume_up) {
bp->b_error = EIO;
bp->b_flags |= B_ERROR;
biodone(bio);
break;
}
nbio = vinum_bounds_check(bio, vol);
if (nbio == NULL) {
biodone(bio);
break;
}
case VINUM_PLEX_TYPE:
case VINUM_RAWPLEX_TYPE:
bp->b_resid = bp->b_bcount;
vinumstart(dev, nbio, 0);
break;
}
return(0);
}
int
vinumstart(cdev_t dev, struct bio *bio, int reviveok)
{
struct buf *bp = bio->bio_buf;
int plexno;
struct volume *vol;
struct request *rq;
enum requeststatus status;
bio->bio_driver_info = dev;
#ifdef VINUMDEBUG
if (debug & DEBUG_LASTREQS)
logrq(loginfo_user_bp, (union rqinfou) bio, bio);
#endif
if ((bp->b_bcount % DEV_BSIZE) != 0) {
bp->b_error = EINVAL;
bp->b_flags |= B_ERROR;
biodone(bio);
return -1;
}
rq = (struct request *) Malloc(sizeof(struct request));
if (rq == NULL) {
bp->b_error = ENOMEM;
bp->b_flags |= B_ERROR;
biodone(bio);
return -1;
}
bzero(rq, sizeof(struct request));
rq->bio = bio;
if (DEVTYPE(dev) == VINUM_VOLUME_TYPE) {
rq->volplex.volno = Volno(dev);
vol = &VOL[rq->volplex.volno];
vol->active++;
} else {
vol = NULL;
rq->volplex.plexno = Plexno(dev);
rq->isplex = 1;
}
if (bp->b_cmd == BUF_CMD_READ) {
if (vol != NULL) {
plexno = vol->preferred_plex;
if (plexno < 0) {
plexno = vol->last_plex_read;
vol->last_plex_read++;
if (vol->last_plex_read >= vol->plexes)
vol->last_plex_read = 0;
}
status = build_read_request(rq, plexno);
} else {
vinum_off_t diskaddr = (vinum_off_t)(bio->bio_offset >> DEV_BSHIFT);
status = bre(rq,
rq->volplex.plexno,
&diskaddr,
diskaddr + (bp->b_bcount / DEV_BSIZE));
}
if (status > REQUEST_RECOVERED) {
if (status == REQUEST_DOWN) {
bp->b_error = EIO;
bp->b_flags |= B_ERROR;
}
biodone(bio);
freerq(rq);
return -1;
}
return launch_requests(rq, reviveok);
} else
{
if (vol != NULL)
status = build_write_request(rq);
else {
vinum_off_t diskstart;
vinum_off_t diskend;
diskstart = (vinum_off_t)(bio->bio_offset >> DEV_BSHIFT);
diskend = diskstart + bp->b_bcount / DEV_BSIZE;
status = bre(rq, Plexno(dev),
&diskstart, diskend);
}
if (status > REQUEST_RECOVERED) {
if (status == REQUEST_DOWN) {
bp->b_error = EIO;
bp->b_flags |= B_ERROR;
}
biodone(bio);
freerq(rq);
return -1;
}
return launch_requests(rq, reviveok);
}
}
int
launch_requests(struct request *rq, int reviveok)
{
struct rqgroup *rqg;
int rqno;
struct rqelement *rqe;
struct drive *drive;
int rcount;
if ((rq->flags & XFR_REVIVECONFLICT)
&&(!reviveok)) {
struct sd *sd;
struct request *waitlist;
sd = &SD[rq->sdno];
if (sd->waitlist != NULL) {
waitlist = sd->waitlist;
while (waitlist->next != NULL)
waitlist = waitlist->next;
waitlist->next = rq;
} else
sd->waitlist = rq;
#ifdef VINUMDEBUG
if (debug & DEBUG_REVIVECONFLICT) {
log(LOG_DEBUG,
"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(((cdev_t)rq->bio->bio_driver_info)),
minor(((cdev_t)rq->bio->bio_driver_info)),
(uintmax_t)rq->bio->bio_offset,
rq->bio->bio_buf->b_bcount);
}
#endif
return 0;
}
rq->active = 0;
#ifdef VINUMDEBUG
if (debug & DEBUG_ADDRESSES)
log(LOG_DEBUG,
"Request: %p\n%s dev %d.%d, offset 0x%jx, length %d\n",
rq,
(rq->bio->bio_buf->b_cmd == BUF_CMD_READ) ? "Read" : "Write",
major(((cdev_t)rq->bio->bio_driver_info)),
minor(((cdev_t)rq->bio->bio_driver_info)),
(uintmax_t)rq->bio->bio_offset,
rq->bio->bio_buf->b_bcount);
vinum_conf.lastrq = rq;
vinum_conf.lastbio = rq->bio;
if (debug & DEBUG_LASTREQS)
logrq(loginfo_user_bpl, (union rqinfou) rq->bio, rq->bio);
#endif
for (rqg = rq->rqg; rqg != NULL; rqg = rqg->next) {
rqg->active = rqg->count;
for (rqno = 0; rqno < rqg->count; rqno++) {
rqe = &rqg->rqe[rqno];
if (rqe->flags & XFR_BAD_SUBDISK)
rqg->active--;
}
if (rqg->active)
rq->active++;
}
crit_enter();
for (rqg = rq->rqg; rqg != NULL;) {
if (rqg->lockbase >= 0)
rqg->lock = lockrange(rqg->lockbase, rqg->rq->bio->bio_buf, &PLEX[rqg->plexno]);
rcount = rqg->count;
for (rqno = 0; rqno < rcount;) {
cdev_t dev;
rqe = &rqg->rqe[rqno];
if (++rqno >= rcount)
rqg = rqg->next;
if ((rqe->flags & XFR_BAD_SUBDISK) == 0) {
drive = &DRIVE[rqe->driveno];
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;
dev = rqe->b.b_bio1.bio_driver_info;
#ifdef VINUMDEBUG
if (debug & DEBUG_ADDRESSES)
log(LOG_DEBUG,
" %s dev %d.%d, sd %d, offset 0x%jx, devoffset 0x%jx, length %d\n",
(rqe->b.b_cmd == BUF_CMD_READ) ? "Read" : "Write",
major(dev),
minor(dev),
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_rqe, (union rqinfou) rqe, rq->bio);
#endif
dev_dstrategy(dev, &rqe->b.b_bio1);
}
}
}
crit_exit();
return 0;
}
enum requeststatus
bre(struct request *rq,
int plexno,
vinum_off_t * diskaddr,
vinum_off_t diskend)
{
int sdno;
struct sd *sd;
struct rqgroup *rqg;
struct bio *bio;
struct buf *bp;
struct plex *plex;
enum requeststatus status;
vinum_off_t plexoffset;
vinum_off_t stripebase;
vinum_off_t stripeoffset;
vinum_off_t blockoffset;
struct rqelement *rqe;
vinum_off_t diskstart = *diskaddr;
enum requeststatus s;
bio = rq->bio;
bp = bio->bio_buf;
status = REQUEST_OK;
plex = &PLEX[plexno];
switch (plex->organization) {
case plex_concat:
sd = NULL;
for (sdno = 0; sdno < plex->subdisks; sdno++) {
sd = &SD[plex->sdnos[sdno]];
if (*diskaddr < sd->plexoffset)
status = REQUEST_DEGRADED;
if (*diskaddr < (sd->plexoffset + sd->sectors)) {
rqg = allocrqg(rq, 1);
if (rqg == NULL) {
bp->b_error = ENOMEM;
bp->b_flags |= B_ERROR;
return REQUEST_ENOMEM;
}
rqg->plexno = plexno;
rqe = &rqg->rqe[0];
rqe->rqg = rqg;
rqe->sdno = sd->sdno;
plexoffset = *diskaddr;
rqe->sdoffset = plexoffset - sd->plexoffset;
rqe->useroffset = plexoffset - diskstart;
rqe->dataoffset = 0;
rqe->datalen = u64min(diskend - *diskaddr,
sd->sectors - rqe->sdoffset);
rqe->groupoffset = 0;
rqe->grouplen = 0;
rqe->buflen = rqe->datalen;
rqe->flags = 0;
rqe->driveno = sd->driveno;
if (sd->state != sd_up) {
s = checksdstate(sd, rq, *diskaddr, diskend);
if (s == REQUEST_DOWN) {
rqe->flags = XFR_BAD_SUBDISK;
if (rq->bio->bio_buf->b_cmd == BUF_CMD_READ)
return REQUEST_DEGRADED;
status = REQUEST_DEGRADED;
}
}
*diskaddr += rqe->datalen;
if (build_rq_buffer(rqe, plex)) {
deallocrqg(rqg);
bp->b_error = ENOMEM;
bp->b_flags |= B_ERROR;
return REQUEST_ENOMEM;
}
}
if (*diskaddr == diskend)
break;
}
if (diskend > sd->sectors + sd->plexoffset)
status = REQUEST_EOF;
break;
case plex_striped:
{
while (*diskaddr < diskend) {
if (*diskaddr >= plex->length)
return REQUEST_EOF;
stripeoffset = *diskaddr % (plex->stripesize * plex->subdisks);
stripebase = *diskaddr - stripeoffset;
sdno = stripeoffset / plex->stripesize;
blockoffset = stripeoffset % plex->stripesize;
sd = &SD[plex->sdnos[sdno]];
rqg = allocrqg(rq, 1);
if (rqg == NULL) {
bp->b_error = ENOMEM;
bp->b_flags |= B_ERROR;
return REQUEST_ENOMEM;
}
rqg->plexno = plexno;
rqe = &rqg->rqe[0];
rqe->rqg = rqg;
rqe->sdoffset = stripebase / plex->subdisks + blockoffset;
rqe->useroffset = *diskaddr - diskstart;
rqe->dataoffset = 0;
rqe->datalen = u64min(diskend - *diskaddr,
plex->stripesize - blockoffset);
rqe->groupoffset = 0;
rqe->grouplen = 0;
rqe->buflen = rqe->datalen;
rqe->flags = 0;
rqe->sdno = sd->sdno;
rqe->driveno = sd->driveno;
if (sd->state != sd_up) {
s = checksdstate(sd, rq, *diskaddr, diskend);
if (s == REQUEST_DOWN) {
rqe->flags = XFR_BAD_SUBDISK;
if (rq->bio->bio_buf->b_cmd == BUF_CMD_READ)
return REQUEST_DEGRADED;
status = REQUEST_DEGRADED;
}
}
if (rqe->sdoffset + rqe->datalen > sd->sectors) {
rqe->datalen = sd->sectors - rqe->sdoffset;
#ifdef VINUMDEBUG
if (debug & DEBUG_EOFINFO) {
log(LOG_DEBUG,
"vinum: EOF on plex %s, sd %s offset %jx (user offset %jx)\n",
plex->name,
sd->name,
(uintmax_t)sd->sectors,
(uintmax_t)bp->b_bio1.bio_offset);
log(LOG_DEBUG,
"vinum: stripebase 0x%llx, stripeoffset 0x%llx, "
"blockoffset 0x%llx\n",
(long long)stripebase,
(long long)stripeoffset,
(long long)blockoffset);
}
#endif
}
if (build_rq_buffer(rqe, plex)) {
deallocrqg(rqg);
bp->b_error = ENOMEM;
bp->b_flags |= B_ERROR;
return REQUEST_ENOMEM;
}
*diskaddr += rqe->datalen;
if ((*diskaddr < diskend)
&&(*diskaddr < plex->length)) {
plex->multiblock++;
if (sdno == plex->subdisks - 1)
plex->multistripe++;
}
}
}
break;
case plex_raid4:
case plex_raid5:
status = bre5(rq, plexno, diskaddr, diskend);
break;
default:
log(LOG_ERR, "vinum: invalid plex type %d in bre\n", plex->organization);
status = REQUEST_DOWN;
}
return status;
}
enum requeststatus
build_read_request(struct request *rq,
int plexindex)
{
struct bio *bio;
struct buf *bp;
vinum_off_t startaddr;
vinum_off_t diskaddr;
vinum_off_t diskend;
int plexno;
struct volume *vol;
int recovered = 0;
enum requeststatus status = REQUEST_OK;
int plexmask;
bio = rq->bio;
bp = bio->bio_buf;
diskaddr = bio->bio_offset >> DEV_BSHIFT;
diskend = diskaddr + (bp->b_bcount / DEV_BSIZE);
vol = &VOL[rq->volplex.volno];
while (diskaddr < diskend) {
startaddr = diskaddr;
status = bre(rq, vol->plex[plexindex], &diskaddr, diskend);
switch (status) {
case REQUEST_OK:
continue;
case REQUEST_RECOVERED:
recovered = 1;
break;
case REQUEST_ENOMEM:
return status;
case REQUEST_EOF:
case REQUEST_DOWN:
case REQUEST_DEGRADED:
plexmask = ((1 << vol->plexes) - 1)
&~(1 << plexindex);
for (plexno = 0; plexno < vol->plexes; plexno++) {
if (plexmask == 0)
return REQUEST_DOWN;
diskaddr = startaddr;
if (plexmask & (1 << plexno)) {
bre(rq, vol->plex[plexno], &diskaddr, diskend);
if (diskaddr > startaddr) {
recovered = 1;
status = REQUEST_OK;
break;
}
}
}
if (diskaddr == startaddr)
return status;
}
if (recovered)
vol->recovered_reads += recovered;
}
return status;
}
enum requeststatus
build_write_request(struct request *rq)
{
struct bio *bio;
struct buf *bp;
vinum_off_t diskstart;
vinum_off_t diskend;
int plexno;
struct volume *vol;
enum requeststatus status;
bio = rq->bio;
bp = bio->bio_buf;
vol = &VOL[rq->volplex.volno];
diskend = (vinum_off_t)(bio->bio_offset >> DEV_BSHIFT) + (bp->b_bcount / DEV_BSIZE);
status = REQUEST_DOWN;
for (plexno = 0; plexno < vol->plexes; plexno++) {
diskstart = (vinum_off_t)(bio->bio_offset >> DEV_BSHIFT);
status = u64min(status,
bre(rq, vol->plex[plexno], &diskstart, diskend));
}
return status;
}
enum requeststatus
build_rq_buffer(struct rqelement *rqe, struct plex *plex)
{
struct sd *sd;
struct buf *bp;
struct buf *ubp;
struct bio *ubio;
sd = &SD[rqe->sdno];
bp = &rqe->b;
ubio = rqe->rqg->rq->bio;
ubp = ubio->bio_buf;
bp->b_flags = ubp->b_flags & B_NOCACHE;
bp->b_cmd = ubp->b_cmd;
#ifdef VINUMDEBUG
if (rqe->flags & XFR_BUFLOCKED)
panic("build_rq_buffer: rqe already locked");
#endif
initbufbio(bp);
BUF_LOCK(bp, LK_EXCLUSIVE);
BUF_KERNPROC(bp);
rqe->flags |= XFR_BUFLOCKED;
bp->b_bio1.bio_done = complete_rqe;
if ((rqe->flags & XFR_BAD_SUBDISK) == 0)
bp->b_bio1.bio_driver_info = DRIVE[rqe->driveno].dev;
bp->b_bio1.bio_offset = (off_t)(rqe->sdoffset + sd->driveoffset) << DEV_BSHIFT;
bp->b_bcount = rqe->buflen << DEV_BSHIFT;
bp->b_resid = bp->b_bcount;
if (rqe->flags & XFR_MALLOCED) {
bp->b_data = Malloc(bp->b_bcount);
if (bp->b_data == NULL) {
abortrequest(rqe->rqg->rq, ENOMEM);
return REQUEST_ENOMEM;
}
} else
bp->b_data = ubp->b_data + rqe->useroffset * DEV_BSIZE;
if ((rqe->flags & (XFR_RECOVERY_READ | XFR_BAD_SUBDISK))
== (XFR_RECOVERY_READ | XFR_BAD_SUBDISK)) {
int length = rqe->grouplen << DEV_BSHIFT;
char *data = (char *) &rqe->b.b_data[rqe->groupoffset << DEV_BSHIFT];
bzero(data, length);
}
return 0;
}
int
abortrequest(struct request *rq, int error)
{
struct buf *bp = rq->bio->bio_buf;
bp->b_error = error;
freerq(rq);
bp->b_flags |= B_ERROR;
return error;
}
int
check_range_covered(struct request *rq)
{
return 1;
}
void
sdio(struct bio *bio)
{
cdev_t dev;
struct sd *sd;
struct sdbuf *sbp;
vinum_off_t endoffset;
struct drive *drive;
struct buf *bp = bio->bio_buf;
dev = bio->bio_driver_info;
#ifdef VINUMDEBUG
if (debug & DEBUG_LASTREQS)
logrq(loginfo_sdio, (union rqinfou) bio, bio);
#endif
sd = &SD[Sdno(dev)];
drive = &DRIVE[sd->driveno];
if (drive->state != drive_up) {
if (sd->state >= sd_crashed) {
if (bp->b_cmd != BUF_CMD_READ)
set_sd_state(sd->sdno, sd_stale, setstate_force);
else
set_sd_state(sd->sdno, sd_crashed, setstate_force);
}
bp->b_error = EIO;
bp->b_flags |= B_ERROR;
biodone(bio);
return;
}
if (sd->state < sd_empty) {
bp->b_error = EIO;
bp->b_flags |= B_ERROR;
biodone(bio);
return;
}
sbp = (struct sdbuf *) Malloc(sizeof(struct sdbuf));
if (sbp == NULL) {
bp->b_error = ENOMEM;
bp->b_flags |= B_ERROR;
biodone(bio);
return;
}
bzero(sbp, sizeof(struct sdbuf));
sbp->b.b_cmd = bp->b_cmd;
sbp->b.b_bcount = bp->b_bcount;
sbp->b.b_resid = bp->b_resid;
sbp->b.b_data = bp->b_data;
initbufbio(&sbp->b);
BUF_LOCK(&sbp->b, LK_EXCLUSIVE);
BUF_KERNPROC(&sbp->b);
sbp->b.b_bio1.bio_offset = bio->bio_offset + ((off_t)sd->driveoffset << DEV_BSHIFT);
sbp->b.b_bio1.bio_done = sdio_done;
sbp->b.b_bio1.bio_flags |= BIO_SYNC;
sbp->bio = bio;
sbp->sdno = sd->sdno;
sbp->driveno = sd->driveno;
endoffset = (vinum_off_t)(bio->bio_offset >> DEV_BSHIFT) + sbp->b.b_bcount / DEV_BSIZE;
if (endoffset > sd->sectors) {
sbp->b.b_bcount -= (endoffset - sd->sectors) * DEV_BSIZE;
if (sbp->b.b_bcount <= 0) {
bp->b_resid = bp->b_bcount;
biodone(bio);
BUF_UNLOCK(&sbp->b);
uninitbufbio(&sbp->b);
Free(sbp);
return;
}
}
#ifdef VINUMDEBUG
if (debug & DEBUG_ADDRESSES)
log(LOG_DEBUG,
" %s dev %s, sd %d, offset 0x%jx, devoffset 0x%jx, length %d\n",
(sbp->b.b_cmd == BUF_CMD_READ) ? "Read" : "Write",
drive->devicename,
sbp->sdno,
(uintmax_t)(sbp->b.b_bio1.bio_offset - ((off_t)SD[sbp->sdno].driveoffset << DEV_BSHIFT)),
(uintmax_t)sbp->b.b_bio1.bio_offset,
sbp->b.b_bcount);
#endif
crit_enter();
#ifdef VINUMDEBUG
if (debug & DEBUG_LASTREQS)
logrq(loginfo_sdiol, (union rqinfou) &sbp->b.b_bio1, &sbp->b.b_bio1);
#endif
vn_strategy(drive->vp, &sbp->b.b_bio1);
crit_exit();
}
struct bio *
vinum_bounds_check(struct bio *bio, struct volume *vol)
{
struct buf *bp = bio->bio_buf;
struct bio *nbio;
vinum_off_t maxsize = vol->size;
int size = (bp->b_bcount + DEV_BSIZE - 1) >> DEV_BSHIFT;
vinum_off_t blkno = (vinum_off_t)(bio->bio_offset >> DEV_BSHIFT);
if (size == 0)
return 0;
if (bio->bio_offset < 0
|| blkno + size > maxsize) {
if (blkno == maxsize) {
bp->b_resid = bp->b_bcount;
return (NULL);
}
size = maxsize - blkno;
if (size <= 0) {
bp->b_error = EINVAL;
bp->b_flags |= B_ERROR;
return (NULL);
}
bp->b_bcount = size << DEV_BSHIFT;
}
nbio = push_bio(bio);
nbio->bio_offset = bio->bio_offset;
return (nbio);
}
struct rqgroup *
allocrqg(struct request *rq, int elements)
{
struct rqgroup *rqg;
int size = sizeof(struct rqgroup) + elements * sizeof(struct rqelement);
rqg = (struct rqgroup *) Malloc(size);
if (rqg != NULL) {
if (rq->rqg)
rq->lrqg->next = rqg;
else
rq->rqg = rqg;
rq->lrqg = rqg;
bzero(rqg, size);
rqg->rq = rq;
rqg->count = elements;
rqg->lockbase = -1;
}
return rqg;
}
void
deallocrqg(struct rqgroup *rqg)
{
struct rqgroup *rqgc = rqg->rq->rqg;
if (rqg->lock)
unlockrange(rqg->plexno, rqg->lock);
if (rqgc == rqg)
rqg->rq->rqg = rqg->next;
else {
while ((rqgc->next != NULL)
&&(rqgc->next != rqg))
rqgc = rqgc->next;
if (rqgc->next == NULL)
log(LOG_ERR,
"vinum deallocrqg: rqg %p not found in request %p\n",
rqg->rq,
rqg);
else
rqgc->next = rqg->next;
}
Free(rqg);
}