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