--- a/sys/dev/raid/vinum/vinumvar.h +++ b/sys/dev/raid/vinum/vinumvar.h @@ -520,6 +520,7 @@ int revive_blocksize; /* revive block size (bytes) */ int revive_interval; /* and time to wait between transfers */ pid_t reviver; /* PID of reviving process */ + int revive_busy; /* DF-4800 fix: one revival at a time */ /* init parameters */ u_int64_t initialized; /* blkno of current init request */ int init_blocksize; /* init block size (bytes) */ --- a/sys/dev/raid/vinum/vinumrevive.c +++ b/sys/dev/raid/vinum/vinumrevive.c @@ -76,6 +76,16 @@ lock = NULL; if (sd->plexno < 0) /* no plex? */ return EINVAL; + /* + * DF-4800 fix: only one revival at a time per subdisk. Concurrent + * revive_block() calls race sd->revived (read at the top, advanced + * after the write): both copy the same block, both advance the + * cursor, and the block between the two advances is silently + * skipped -- the subdisk comes up with data that was never copied. + * The second caller gets EBUSY and userland stops retrying. + */ + if (atomic_cmpset_int(&sd->revive_busy, 0, 1) == 0) + return EBUSY; plex = &PLEX[sd->plexno]; /* point to plex */ if (plex->volno >= 0) vol = &VOL[plex->volno]; @@ -137,8 +147,10 @@ if (paritysd) { /* we're reviving a parity block, */ bp = parityrebuild(plex, sd->revived, size, rebuildparity, &lock, NULL); /* do the grunt work */ - if (bp == NULL) /* no buffer space */ + if (bp == NULL) { /* no buffer space */ + atomic_store_int(&sd->revive_busy, 0); /* DF-4800 fix */ return ENOMEM; /* chicken out */ + } } else { /* data block */ bp = getpbuf(&vinum_conf.physbufs); /* Get a buffer */ bp->b_data = Malloc(size);