DF-2813 / fix.diff
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; |