[PATCH] btrfs: correct zstd workspace manager lock to use spin_lock_bh()

From: Dennis Zhou
Date: Fri May 17 2019 - 19:18:41 EST


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 <dennis@xxxxxxxxxx>
---
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