From 6ddf82829e71a71f39bd02d6ce35ec518443fe7a Mon Sep 17 00:00:00 2001 From: wenlxie Date: Wed, 30 Sep 2026 13:50:15 -0700 Subject: [PATCH] scst_user: yield cleanup while shared SGV remains checked out With SGV sharing, device B can reuse a buffer whose allocation owner is a command from device A. If A's user handle closes while B still has the SGV checked out, A's command remains in its hash. The unjam pass sees that command, but there is no ready A command, so get_next_cmd() returns -EAGAIN. Once cleanup_done is set, the old inner loop keeps retrying A and prevents the single cleanup worker from reaching B. The post-unjam pool flush added by PR #378 frees SGV objects that have already returned to the recycling lists. It cannot free the object still checked out by B, so it cannot resolve this dependency. Yield A to the outer cleanup loop when no ready command exists but the unjam pass still reports commands. The worker can process B and retry A after its normal sleep. Signal A's cleanup completion only when its release is done and no commands remain. Link: https://github.com/SCST-project/scst/issues/279 --- scst/src/dev_handlers/scst_user.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/scst/src/dev_handlers/scst_user.c b/scst/src/dev_handlers/scst_user.c index b930921d5..82e389b76 100644 --- a/scst/src/dev_handlers/scst_user.c +++ b/scst/src/dev_handlers/scst_user.c @@ -2738,7 +2738,8 @@ repeat: * The pre-unjam flush above misses these objects. Without this second * flush, dev_user_free_sg_entries() never fires, the alloc_pages * ucmd_get() ref is never balanced, and the ucmd stays in ucmd_hash - * indefinitely — causing dev_user_process_cleanup() to loop forever. + * indefinitely. A checked-out object cannot be flushed until its user + * returns it; cleanup must allow that user to make progress. */ sgv_pool_flush(dev->pool); sgv_pool_flush(dev->pool_clust); @@ -3883,8 +3884,6 @@ static int dev_user_process_cleanup(struct scst_user_dev *dev) TRACE_DBG("Cleanuping dev %p", dev); rc1 = dev_user_unjam_dev(dev); - if (rc1 == 0 && rc == -EAGAIN && dev->cleanup_done) - break; spin_lock_irq(&dev->udev_cmd_threads.cmd_list_lock); @@ -3895,10 +3894,16 @@ static int dev_user_process_cleanup(struct scst_user_dev *dev) spin_unlock_irq(&dev->udev_cmd_threads.cmd_list_lock); if (rc == -EAGAIN) { - if (!dev->cleanup_done) { - TRACE_DBG("No more commands (dev %p)", dev); + if (!dev->cleanup_done || rc1 != 0) { + /* + * An SGV object may still be checked out from a shared pool. + * Let other devices finish cleanup before retrying this one. + */ + TRACE_DBG("Waiting for cleanup (dev %p, pending %d)", + dev, rc1); goto out; } + break; } }