The btrfs zstd workspace manager uses a background timer to reclaim
not recently used workspaces. I dumbly call spin_lock() from this
context which I should have caught with lockdep but.. This deadlock was
reported in [1]. The fix is to switch the zstd wsm lock to use
spin_lock_bh().

[1] https://bugzilla.kernel.org/show_bug.cgi?id=203517

Signed-off-by: Dennis Zhou <den...@kernel.org>
---
 fs/btrfs/zstd.c | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/fs/btrfs/zstd.c b/fs/btrfs/zstd.c
index a6ff07cf11d5..3837ca180d52 100644
--- a/fs/btrfs/zstd.c
+++ b/fs/btrfs/zstd.c
@@ -105,10 +105,10 @@ static void zstd_reclaim_timer_fn(struct timer_list 
*timer)
        unsigned long reclaim_threshold = jiffies - ZSTD_BTRFS_RECLAIM_JIFFIES;
        struct list_head *pos, *next;
 
-       spin_lock(&wsm.lock);
+       spin_lock_bh(&wsm.lock);
 
        if (list_empty(&wsm.lru_list)) {
-               spin_unlock(&wsm.lock);
+               spin_unlock_bh(&wsm.lock);
                return;
        }
 
@@ -137,7 +137,7 @@ static void zstd_reclaim_timer_fn(struct timer_list *timer)
        if (!list_empty(&wsm.lru_list))
                mod_timer(&wsm.timer, jiffies + ZSTD_BTRFS_RECLAIM_JIFFIES);
 
-       spin_unlock(&wsm.lock);
+       spin_unlock_bh(&wsm.lock);
 }
 
 /*
@@ -198,7 +198,7 @@ static void zstd_cleanup_workspace_manager(void)
        struct workspace *workspace;
        int i;
 
-       spin_lock(&wsm.lock);
+       spin_lock_bh(&wsm.lock);
        for (i = 0; i < ZSTD_BTRFS_MAX_LEVEL; i++) {
                while (!list_empty(&wsm.idle_ws[i])) {
                        workspace = container_of(wsm.idle_ws[i].next,
@@ -208,7 +208,7 @@ static void zstd_cleanup_workspace_manager(void)
                        zstd_free_workspace(&workspace->list);
                }
        }
-       spin_unlock(&wsm.lock);
+       spin_unlock_bh(&wsm.lock);
 
        del_timer_sync(&wsm.timer);
 }
@@ -230,7 +230,7 @@ static struct list_head *zstd_find_workspace(unsigned int 
level)
        struct workspace *workspace;
        int i = level - 1;
 
-       spin_lock(&wsm.lock);
+       spin_lock_bh(&wsm.lock);
        for_each_set_bit_from(i, &wsm.active_map, ZSTD_BTRFS_MAX_LEVEL) {
                if (!list_empty(&wsm.idle_ws[i])) {
                        ws = wsm.idle_ws[i].next;
@@ -242,11 +242,11 @@ static struct list_head *zstd_find_workspace(unsigned int 
level)
                                list_del(&workspace->lru_list);
                        if (list_empty(&wsm.idle_ws[i]))
                                clear_bit(i, &wsm.active_map);
-                       spin_unlock(&wsm.lock);
+                       spin_unlock_bh(&wsm.lock);
                        return ws;
                }
        }
-       spin_unlock(&wsm.lock);
+       spin_unlock_bh(&wsm.lock);
 
        return NULL;
 }
@@ -305,7 +305,7 @@ static void zstd_put_workspace(struct list_head *ws)
 {
        struct workspace *workspace = list_to_workspace(ws);
 
-       spin_lock(&wsm.lock);
+       spin_lock_bh(&wsm.lock);
 
        /* A node is only taken off the lru if we are the corresponding level */
        if (workspace->req_level == workspace->level) {
@@ -325,7 +325,7 @@ static void zstd_put_workspace(struct list_head *ws)
        list_add(&workspace->list, &wsm.idle_ws[workspace->level - 1]);
        workspace->req_level = 0;
 
-       spin_unlock(&wsm.lock);
+       spin_unlock_bh(&wsm.lock);
 
        if (workspace->level == ZSTD_BTRFS_MAX_LEVEL)
                cond_wake_up(&wsm.wait);
-- 
2.17.1

Reply via email to