⬢ DragonFlyBSD Kernel Audit
DF-4800 / fix.diff
← back to finding ↓ download raw
--- 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);