⬢ DragonFlyBSD Kernel Audit
DF-5533 / fix.diff
← back to finding ↓ download raw
--- a/sys/bus/u4b/usb_process.c
+++ b/sys/bus/u4b/usb_process.c
@@ -57,7 +57,7 @@
 static int usb_pcount;
 #define	USB_THREAD_CREATE(f, s, p, ...) \
 		kthread_create((f), (s), (p), __VA_ARGS__)
-#define	USB_THREAD_SUSPEND_CHECK() kthread_suspend_check(curproc)
+#define	USB_THREAD_SUSPEND_CHECK() kproc_suspend_loop()
 #define	USB_THREAD_SUSPEND(p)   suspend_kproc(p,0)
 #define	USB_THREAD_EXIT(err)	kthread_exit()
 
@@ -97,6 +97,20 @@
 
 	while (1) {
 
+		/*
+		 * DF-5533: honour suspend_kproc() requests so that
+		 * usb_proc_drain()'s cold-boot branch can complete.
+		 * The bus lock must be dropped while parking: the
+		 * suspender sleeps until we acknowledge and then needs
+		 * to re-acquire the lock itself, so parking while
+		 * holding it deadlocks both threads.
+		 */
+		if (curthread->td_mpflags & TDF_MP_STOPREQ) {
+			lockmgr(up->up_lock, LK_RELEASE);
+			USB_THREAD_SUSPEND_CHECK();
+			lockmgr(up->up_lock, LK_EXCLUSIVE);
+		}
+
 		if (up->up_gone)
 			break;
 
@@ -155,6 +169,18 @@
 				/* nothing changed */
 				TAILQ_REMOVE(&up->up_qhead, pm, pm_qentry);
 				pm->pm_qentry.tqe_prev = NULL;
+				/*
+				 * DF-5532: a completed message must be
+				 * able to release usb_proc_mwait() waiters
+				 * even when the queue never becomes empty.
+				 * Previously the only wakeup was the
+				 * empty-queue transition below, so any
+				 * continuously refilled queue starved
+				 * waiters whose messages had long
+				 * completed.
+				 */
+				if (up->up_dsleep)
+					cv_broadcast(&up->up_drain);
 			}
 			DPRINTF("Message pm=%p (leave)\n", pm);
 
@@ -433,7 +459,16 @@
 		/* Check if we are still cold booted */
 
 		if (cold) {
+			/*
+			 * DF-5533: suspend_kproc() sleeps until the
+			 * target thread acknowledges the stop request,
+			 * so the bus lock must be dropped while
+			 * waiting, else the target can never run its
+			 * suspend check and both threads deadlock.
+			 */
+			lockmgr(up->up_lock, LK_RELEASE);
 			USB_THREAD_SUSPEND(up->up_ptr);
+			lockmgr(up->up_lock, LK_EXCLUSIVE);
 			kprintf("WARNING: A USB process has "
 			    "been left suspended\n");
 			break;