From: Zizhi Wo wozizhi@huawei.com
hulk inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IB5UKT
--------------------------------
In the erofs ondemand loading scenario, repeatedly mount and umount the image with identical fsid or device_id may occasionally result "duplicate cookie found!" error. This is because the cookie is still hashed.
The cookie only unhashed when the cookie->ref reaching zero, which making it undetectable. Cookie->ref count reaching zero depends on 3 conditions: a) fscache_object exits the state machine process b) user-space close requests being issued c) the completion of the umount process.
In other words, if the object has not exited the state machine or if the user-space has not issued a close request after the umount process, an error will occur during mounting. Linux-mainline solves this problem by overwriting fscache and cachefiles, adding a synchronous wait mechanism during mount. To address the issue in this version, we can borrow the idea of mainline, adding a waiting mechanism to ensure that the next mount operation waits until the duplicate cookie is unhashed. In addition, the waiting mechanism is interruptible to avoid hang.
Signed-off-by: Zizhi Wo wozizhi@huawei.com Signed-off-by: Baokun Li libaokun1@huawei.com --- fs/fscache/cookie.c | 49 ++++++++++++++++++++++++++++++++++++++--- include/linux/fscache.h | 2 ++ 2 files changed, 48 insertions(+), 3 deletions(-)
diff --git a/fs/fscache/cookie.c b/fs/fscache/cookie.c index 6104f627cc71..e6341f85d9f3 100644 --- a/fs/fscache/cookie.c +++ b/fs/fscache/cookie.c @@ -159,6 +159,7 @@ struct fscache_cookie *fscache_alloc_cookie(
cookie->def = def; cookie->parent = parent; + cookie->collision = NULL; cookie->netfs_data = netfs_data; cookie->flags = (1 << FSCACHE_COOKIE_NO_DATA_YET); cookie->type = def->type; @@ -176,6 +177,27 @@ struct fscache_cookie *fscache_alloc_cookie( return NULL; }
+static bool fscache_is_acquire_pending(struct fscache_cookie *cookie) +{ + return test_bit(FSCACHE_COOKIE_ACQUIRE_PENDING, &cookie->flags); +} + +static int fscache_wait_on_cookie_collision(struct fscache_cookie *candidate) +{ + int ret; + + ret = wait_on_bit_timeout(&candidate->flags, FSCACHE_COOKIE_ACQUIRE_PENDING, + TASK_INTERRUPTIBLE, 20 * HZ); + if (ret == -EINTR) + return ret; + if (fscache_is_acquire_pending(candidate)) { + pr_notice("Potential cookie collision!"); + return wait_on_bit(&candidate->flags, FSCACHE_COOKIE_ACQUIRE_PENDING, + TASK_INTERRUPTIBLE); + } + return 0; +} + /* * Attempt to insert the new cookie into the hash. If there's a collision, we * return the old cookie if it's not in use and an error otherwise. @@ -192,8 +214,13 @@ struct fscache_cookie *fscache_hash_cookie(struct fscache_cookie *candidate)
hlist_bl_lock(h); hlist_bl_for_each_entry(cursor, p, h, hash_link) { - if (fscache_compare_cookie(candidate, cursor) == 0) - goto collision; + if (fscache_compare_cookie(candidate, cursor) == 0) { + if (!test_bit(FSCACHE_COOKIE_RELINQUISHED, &cursor->flags)) + goto collision; + cursor->collision = candidate; + set_bit(FSCACHE_COOKIE_ACQUIRE_PENDING, &candidate->flags); + break; + } }
__set_bit(FSCACHE_COOKIE_ACQUIRED, &candidate->flags); @@ -201,16 +228,29 @@ struct fscache_cookie *fscache_hash_cookie(struct fscache_cookie *candidate) atomic_inc(&candidate->parent->n_children); hlist_bl_add_head(&candidate->hash_link, h); hlist_bl_unlock(h); + + if (fscache_is_acquire_pending(candidate) && + fscache_wait_on_cookie_collision(candidate)) { + fscache_cookie_put(candidate->parent, fscache_cookie_put_acquire_nobufs); + atomic_dec(&candidate->parent->n_children); + hlist_bl_lock(h); + hlist_bl_del(&candidate->hash_link); + if (fscache_is_acquire_pending(candidate)) + cursor->collision = NULL; + hlist_bl_unlock(h); + pr_err("Wait duplicate cookie unhashed interrupted\n"); + return NULL; + } return candidate;
collision: if (test_and_set_bit(FSCACHE_COOKIE_ACQUIRED, &cursor->flags)) { trace_fscache_cookie(cursor, fscache_cookie_collision, atomic_read(&cursor->usage)); - pr_err("Duplicate cookie detected\n"); fscache_print_cookie(cursor, 'O'); fscache_print_cookie(candidate, 'N'); hlist_bl_unlock(h); + pr_err("Duplicate cookie detected\n"); return NULL; }
@@ -835,6 +875,9 @@ static void fscache_unhash_cookie(struct fscache_cookie *cookie)
hlist_bl_lock(h); hlist_bl_del(&cookie->hash_link); + if (cookie->collision) + clear_and_wake_up_bit(FSCACHE_COOKIE_ACQUIRE_PENDING, + &cookie->collision->flags); hlist_bl_unlock(h); }
diff --git a/include/linux/fscache.h b/include/linux/fscache.h index f262446f3a49..6931c144a2fd 100644 --- a/include/linux/fscache.h +++ b/include/linux/fscache.h @@ -139,6 +139,7 @@ struct fscache_cookie { struct hlist_head backing_objects; /* object(s) backing this file/index */ const struct fscache_cookie_def *def; /* definition */ struct fscache_cookie *parent; /* parent of this entry */ + struct fscache_cookie *collision; /* collision cookie */ struct hlist_bl_node hash_link; /* Link in hash table */ void *netfs_data; /* back pointer to netfs */ struct radix_tree_root stores; /* pages to be stored on this cookie */ @@ -156,6 +157,7 @@ struct fscache_cookie { #define FSCACHE_COOKIE_AUX_UPDATED 8 /* T if the auxiliary data was updated */ #define FSCACHE_COOKIE_ACQUIRED 9 /* T if cookie is in use */ #define FSCACHE_COOKIE_RELINQUISHING 10 /* T if cookie is being relinquished */ +#define FSCACHE_COOKIE_ACQUIRE_PENDING 11 /* T if cookie is waiting to complete acquisition */
u8 type; /* Type of object */ u8 key_len; /* Length of index key */