DF-3660 / fix.diff
--- a/sys/dev/disk/isp/isp_freebsd.h +++ b/sys/dev/disk/isp/isp_freebsd.h @@ -152,6 +152,7 @@ struct isp_fc { struct cam_sim *sim; struct cam_path *path; struct ispsoftc *isp; struct thread *kthread; + int num_threads; /* live worker threads for this channel */ bus_dma_tag_t tdmat; @@ -249,6 +250,7 @@ struct isposinfo { forcemulti : 1, timer_active : 1, + is_exiting : 1, autoconf : 1, --- a/sys/dev/disk/isp/isp_freebsd.c +++ b/sys/dev/disk/isp/isp_freebsd.c @@ -159,6 +159,7 @@ isp_attach_chan(ispsoftc_t *isp, struct cam_devq *devq, int chan) cam_sim_free(fc->sim); return (ENOMEM); } + fc->num_threads += 1; #ifdef ISP_INTERNAL_TARGET @@ -4185,6 +4186,9 @@ isp_kthread(void *arg) lockmgr(&isp->isp_osinfo.lock, LK_EXCLUSIVE); for (;;) { int lb, lim; + + if (isp->isp_osinfo.is_exiting) + break; isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "%s: Chan %d checking FC state", __func__, chan); @@ -4277,6 +4281,12 @@ isp_kthread(void *arg) lockmgr(&isp->isp_osinfo.lock, LK_EXCLUSIVE); } } + fc->num_threads -= 1; + wakeup(&fc->num_threads); lockmgr(&isp->isp_osinfo.lock, LK_RELEASE); + kthread_exit(); } @@ -272,6 +273,16 @@ isp_detach(ispsoftc_t *isp) ISP_LOCK(isp); + /* Stop channel workers before freeing anything they touch. */ + isp->isp_osinfo.is_exiting = 1; + for (chan = isp->isp_nchan - 1; chan >= 0; chan -= 1) { + if (IS_FC(isp)) { + struct isp_fc *fc = ISP_FC_PC(isp, chan); + callout_stop(&fc->ldt); + callout_stop(&fc->gdt); + fc->ready = 0; + wakeup(fc); + } + } for (chan = isp->isp_nchan - 1; chan >= 0; chan -= 1) { if (IS_FC(isp)) { sim = ISP_FC_PC(isp, chan)->sim; @@ -309,6 +320,15 @@ isp_detach(ispsoftc_t *isp) xpt_bus_deregister(cam_sim_path(sim)); cam_sim_free(sim); } + /* Reap the FC worker threads while the softc is still alive. */ + for (chan = isp->isp_nchan - 1; chan >= 0; chan -= 1) { + if (IS_FC(isp)) { + struct isp_fc *fc = ISP_FC_PC(isp, chan); + while (fc->num_threads != 0) + lksleep(&fc->num_threads, &isp->isp_osinfo.lock, + PRIBIO, "isp_reap", 0); + } + } ISP_UNLOCK(isp); |