hulk inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IB5UKT
--------------------------------
In the current erofs ondemand scenario, fd holds a reference to object, object holds a reference to cookie, and cookie holds a reference to volume. The cookie->ref is subtracted when the object->ref reaches 0. And only when cookie->ref reaches 0 will volume->ref be decremented by one. It means a dependency chain is formed between fd->object->cookie->volume.
The unhashing of the cookie does not depend on its reference count being zero; it only requires its state to be FSCACHE_COOKIE_STATE_RELINQUISHING.
But for the volume, the situation is different. Volume will be unhashed only when its reference count is zero. That means it depends on the user space closing the fd of the cachefiles_object. If the relinquish process is finished but the user-space fails to properly close the fd, it will cause the kernel to hang in fscache_wait_on_volume_collision(), which is an inappropriate behavior.
Modify the waiting mechanism to be interruptible to fix this problem.
Fixes: 62ab63352350 ("fscache: Implement volume registration") Signed-off-by: Zizhi Wo wozizhi@huawei.com --- fs/fscache/volume.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-)
diff --git a/fs/fscache/volume.c b/fs/fscache/volume.c index ced14ac78cc1..89efb8071a75 100644 --- a/fs/fscache/volume.c +++ b/fs/fscache/volume.c @@ -151,18 +151,23 @@ static bool fscache_is_acquire_pending(struct fscache_volume *volume) return test_bit(FSCACHE_VOLUME_ACQUIRE_PENDING, &volume->flags); }
-static void fscache_wait_on_volume_collision(struct fscache_volume *candidate, +static int fscache_wait_on_volume_collision(struct fscache_volume *candidate, unsigned int collidee_debug_id) { - wait_on_bit_timeout(&candidate->flags, FSCACHE_VOLUME_ACQUIRE_PENDING, - TASK_UNINTERRUPTIBLE, 20 * HZ); + int ret; + + ret = wait_on_bit_timeout(&candidate->flags, FSCACHE_VOLUME_ACQUIRE_PENDING, + TASK_INTERRUPTIBLE, 20 * HZ); + if (ret == -EINTR) + return ret; if (fscache_is_acquire_pending(candidate)) { pr_notice("Potential volume collision new=%08x old=%08x", candidate->debug_id, collidee_debug_id); fscache_stat(&fscache_n_volumes_collision); - wait_on_bit(&candidate->flags, FSCACHE_VOLUME_ACQUIRE_PENDING, - TASK_UNINTERRUPTIBLE); + return wait_on_bit(&candidate->flags, FSCACHE_VOLUME_ACQUIRE_PENDING, + TASK_INTERRUPTIBLE); } + return 0; }
/* @@ -183,8 +188,10 @@ static bool fscache_hash_volume(struct fscache_volume *candidate) hlist_bl_lock(h); hlist_bl_for_each_entry(cursor, p, h, hash_link) { if (fscache_volume_same(candidate, cursor)) { - if (!test_bit(FSCACHE_VOLUME_RELINQUISHED, &cursor->flags)) + if (!test_bit(FSCACHE_VOLUME_RELINQUISHED, &cursor->flags)) { + fscache_see_volume(cursor, fscache_volume_collision); goto collision; + } fscache_see_volume(cursor, fscache_volume_get_hash_collision); set_bit(FSCACHE_VOLUME_COLLIDED_WITH, &cursor->flags); set_bit(FSCACHE_VOLUME_ACQUIRE_PENDING, &candidate->flags); @@ -196,12 +203,16 @@ static bool fscache_hash_volume(struct fscache_volume *candidate) hlist_bl_add_head(&candidate->hash_link, h); hlist_bl_unlock(h);
- if (fscache_is_acquire_pending(candidate)) - fscache_wait_on_volume_collision(candidate, collidee_debug_id); + if (fscache_is_acquire_pending(candidate) && + fscache_wait_on_volume_collision(candidate, collidee_debug_id)) { + hlist_bl_lock(h); + hlist_bl_del_init(&candidate->hash_link); + pr_err("Wait duplicate volume unhashed interrupted\n"); + goto collision; + } return true;
collision: - fscache_see_volume(cursor, fscache_volume_collision); hlist_bl_unlock(h); return false; }