DF-4721 / fix.diff
--- a/sys/dev/raid/vinum/vinumrequest.c +++ b/sys/dev/raid/vinum/vinumrequest.c @@ -393,20 +393,31 @@ */ crit_enter(); for (rqg = rq->rqg; rqg != NULL;) { /* through the whole request chain */ + struct rqgroup *nextrqg; /* group snapshot */ + int lastrqno; /* last rqe we will launch */ + if (rqg->lockbase >= 0) /* this rqg needs a lock first */ rqg->lock = lockrange(rqg->lockbase, rqg->rq->bio->bio_buf, &PLEX[rqg->plexno]); rcount = rqg->count; - for (rqno = 0; rqno < rcount;) { + /* + * Snapshot the group chain and find the last request we will + * launch BEFORE firing anything: with drives that complete + * inline (e.g. md malloc disks), the final completion can run + * complete_rqe() -> complete_raid5_write() -> freerq() inside + * dev_dstrategy(), freeing the whole request while we are + * still in this loop. We must not touch the group after the + * last launch - neither trailing XFR_BAD_SUBDISK entries nor + * rqg->next. + */ + nextrqg = rqg->next; + for (lastrqno = rcount - 1; lastrqno >= 0; lastrqno--) + if ((rqg->rqe[lastrqno].flags & XFR_BAD_SUBDISK) == 0) + break; + for (rqno = 0; rqno <= lastrqno;) { cdev_t dev; rqe = &rqg->rqe[rqno]; - - /* - * Point to next rqg before the bottom end - * changes the structures. - */ - if (++rqno >= rcount) - rqg = rqg->next; + rqno++; if ((rqe->flags & XFR_BAD_SUBDISK) == 0) { /* this subdisk is good, */ drive = &DRIVE[rqe->driveno]; /* look at drive */ drive->active++; @@ -436,6 +447,7 @@ dev_dstrategy(dev, &rqe->b.b_bio1); } } + rqg = nextrqg; } crit_exit(); return 0; |