DF-3813 / fix.diff
--- a/sys/dev/raid/mpr/mpr.c +++ b/sys/dev/raid/mpr/mpr.c @@ -573,6 +573,7 @@ /* Record that we reallocated everything */ sc->mpr_flags |= MPR_FLAGS_REALLOCATED; + sc->realloc_gen++; } /* @@ -3560,6 +3561,7 @@ int error, rc; struct timeval cur_time, start_time; struct mpr_command *cm = *cmp; + u_int realloc_gen = sc->realloc_gen; if (sc->mpr_flags & MPR_FLAGS_DIAGRESET) return EBUSY; @@ -3617,6 +3619,19 @@ } error = ETIMEDOUT; } + /* + * A concurrent diag reset may have reallocated the command pool + * while we slept: mprsas_complete_all_commands() woke us without + * freeing our command, and mpr_iocfacts_free() then kfreed the + * whole commands[] array under us. Detect that via the generation + * counter and tell the caller the command is gone, exactly like + * the timeout path above. + */ + if (sc->realloc_gen != realloc_gen) { + *cmp = NULL; + if (error == 0) + error = ENXIO; + } return (error); } @@ -3630,6 +3645,7 @@ int error, rc; struct timeval cur_time, start_time; struct mpr_command *cm = *cmp; + u_int realloc_gen = sc->realloc_gen; error = 0; @@ -3639,6 +3655,12 @@ getmicrouptime(&start_time); while ((cm->cm_flags & MPR_CM_FLAGS_COMPLETE) == 0) { + if (sc->realloc_gen != realloc_gen) { + /* commands[] was reallocated out from under us */ + *cmp = NULL; + error = ENXIO; + break; + } mpr_intr_locked(sc); if (lockowned(&sc->mpr_lock)) --- a/sys/dev/raid/mpr/mprvar.h +++ b/sys/dev/raid/mpr/mprvar.h @@ -313,6 +313,7 @@ u_int reqframesz; u_int replyframesz; u_int atomic_desc_capable; + u_int realloc_gen; /* ++ when commands[] is reallocated */ int tm_cmds_active; int io_cmds_active; int io_cmds_highwater; |