DragonFlyBSD Kernel Audit
DF-2813 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/kern/kern_objcache.c b/sys/kern/kern_objcache.c
index 21563ad..1209331 100644
--- a/sys/kern/kern_objcache.c
+++ b/sys/kern/kern_objcache.c
@@ -641,6 +641,44 @@ retry:
 	 * It is lock-free and uses a critical section to block out interrupt
 	 * handlers on the same processor.
 	 */
+	depot = &oc->depot[myclusterid];
+	if (__predict_false(depot->waiting && cpucache->waiting == 0 &&
+			    MAGAZINE_NOTEMPTY(cpucache->loaded_magazine))) {
+		/*
+		 * A remote cpu is sleeping in objcache_get() on this depot
+		 * (M_WAITOK exhaustion path).  Objects cached in our
+		 * per-cpu magazines are invisible to it: the hot-path
+		 * wakeup below only checks *this* cpu's waiters
+		 * (wakeup_mycpu), and with the magazine rebalance callout
+		 * disabled there is nothing that ever migrates partially
+		 * filled magazines to the depot.  The sleeper would thus
+		 * block forever (ssleep with flags=0/no PCATCH, timo=0).
+		 *
+		 * Flush our loaded magazine to the depot so the cached
+		 * objects become globally visible, then cache this object
+		 * in the replacement magazine as usual.
+		 *
+		 * depot->waiting is read unlocked; the decision is
+		 * re-checked under the depot spinlock, so a stale read is
+		 * harmless (at worst we skip a flush or flush with no
+		 * waiter left).
+		 */
+		spin_lock(&depot->spin);
+		if (depot->waiting &&
+		    !SLIST_EMPTY(&depot->emptymagazines)) {
+			loadedmag = cpucache->loaded_magazine;
+			cpucache->loaded_magazine =
+			    SLIST_FIRST(&depot->emptymagazines);
+			SLIST_REMOVE_HEAD(&depot->emptymagazines,
+					  nextmagazine);
+			SLIST_INSERT_HEAD(&depot->fullmagazines,
+					  loadedmag, nextmagazine);
+			spin_unlock(&depot->spin);
+			wakeup(depot);
+		} else {
+			spin_unlock(&depot->spin);
+		}
+	}
 	loadedmag = cpucache->loaded_magazine;
 	if (!MAGAZINE_FULL(loadedmag)) {
 		loadedmag->objects[loadedmag->rounds++] = obj;