From: Jeffle Xu jefflexu@linux.alibaba.com
anolis inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IB5UKT
Reference: https://gitee.com/anolis/cloud-kernel/commit/419c7d84a2d2
--------------------------------
ANBZ: #1666
commit d11b0b043b4008d64abaf1a26eea3dbcd906ee59 upstream.
Add a refcount to avoid the deadlock in on-demand read mode. The on-demand read mode will pin the corresponding cachefiles object for each anonymous fd. The cachefiles object is unpinned when the anonymous fd gets closed. When the user daemon exits and the fd of "/dev/cachefiles" device node gets closed, it will wait for all cahcefiles objects getting withdrawn. Then if there's any anonymous fd getting closed after the fd of the device node, the user daemon will hang forever, waiting for all objects getting withdrawn.
To fix this, add a refcount indicating if there's any object pinned by anonymous fds. The cachefiles cache gets unbound and withdrawn when the refcount is decreased to 0. It won't change the behaviour of the original mode, in which case the cachefiles cache gets unbound and withdrawn as long as the fd of the device node gets closed.
Signed-off-by: Jeffle Xu jefflexu@linux.alibaba.com Link: https://lore.kernel.org/r/20220509074028.74954-4-jefflexu@linux.alibaba.com Acked-by: David Howells dhowells@redhat.com Signed-off-by: Gao Xiang hsiangkao@linux.alibaba.com Signed-off-by: Huang Jianan jnhuang@linux.alibaba.com Reviewed-by: Gao Xiang hsiangkao@linux.alibaba.com Reviewed-by: Jeffle Xu jefflexu@linux.alibaba.com Signed-off-by: Baokun Li libaokun1@huawei.com --- fs/cachefiles/daemon.c | 23 +++++++++++++++++------ fs/cachefiles/internal.h | 3 +++ fs/cachefiles/ondemand.c | 3 +++ 3 files changed, 23 insertions(+), 6 deletions(-)
diff --git a/fs/cachefiles/daemon.c b/fs/cachefiles/daemon.c index 1c85c8dcc0c4..1a3a9fda60c8 100644 --- a/fs/cachefiles/daemon.c +++ b/fs/cachefiles/daemon.c @@ -108,6 +108,7 @@ static int cachefiles_daemon_open(struct inode *inode, struct file *file) cache->active_nodes = RB_ROOT; rwlock_init(&cache->active_lock); init_waitqueue_head(&cache->daemon_pollwq); + refcount_set(&cache->unbind_pincount, 1);
INIT_RADIX_TREE(&cache->reqs, GFP_ATOMIC); idr_init(&cache->ondemand_ids); @@ -167,6 +168,21 @@ static void cachefiles_flush_reqs(struct cachefiles_cache *cache) xa_unlock(&cache->ondemand_ids.idr_rt); }
+void cachefiles_put_unbind_pincount(struct cachefiles_cache *cache) +{ + if (refcount_dec_and_test(&cache->unbind_pincount)) { + cachefiles_daemon_unbind(cache); + ASSERT(!cache->active_nodes.rb_node); + cachefiles_open = 0; + kfree(cache); + } +} + +void cachefiles_get_unbind_pincount(struct cachefiles_cache *cache) +{ + refcount_inc(&cache->unbind_pincount); +} + /* * release a cache */ @@ -182,17 +198,12 @@ static int cachefiles_daemon_release(struct inode *inode, struct file *file)
if (cachefiles_in_ondemand_mode(cache)) cachefiles_flush_reqs(cache); - cachefiles_daemon_unbind(cache); - - ASSERT(!cache->active_nodes.rb_node);
/* clean up the control file interface */ cache->cachefilesd = NULL; file->private_data = NULL; - cachefiles_open = 0; - - kfree(cache);
+ cachefiles_put_unbind_pincount(cache); _leave(""); return 0; } diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h index fed71ad90808..52188b42081f 100644 --- a/fs/cachefiles/internal.h +++ b/fs/cachefiles/internal.h @@ -95,6 +95,7 @@ struct cachefiles_cache { char *rootdirname; /* name of cache root directory */ char *secctx; /* LSM security context */ char *tag; /* cache binding tag */ + refcount_t unbind_pincount;/* refcount to do daemon unbind */ struct radix_tree_root reqs; /* xarray of pending on-demand requests */ struct idr ondemand_ids; /* xarray for ondemand_id allocation */ u32 ondemand_id_next; @@ -167,6 +168,8 @@ extern void cachefiles_daemon_unbind(struct cachefiles_cache *cache); * daemon.c */ extern const struct file_operations cachefiles_daemon_fops; +extern void cachefiles_get_unbind_pincount(struct cachefiles_cache *cache); +extern void cachefiles_put_unbind_pincount(struct cachefiles_cache *cache);
extern int cachefiles_has_space(struct cachefiles_cache *cache, unsigned fnr, unsigned bnr); diff --git a/fs/cachefiles/ondemand.c b/fs/cachefiles/ondemand.c index ff2f00cfa5d4..d134f22e3818 100644 --- a/fs/cachefiles/ondemand.c +++ b/fs/cachefiles/ondemand.c @@ -21,6 +21,7 @@ static int cachefiles_ondemand_fd_release(struct inode *inode, xa_unlock(&cache->ondemand_ids.idr_rt); object->fscache.cache->ops->put_object(&object->fscache, cachefiles_obj_put_ondemand_fd); + cachefiles_put_unbind_pincount(cache); return 0; }
@@ -178,6 +179,8 @@ static int cachefiles_ondemand_get_fd(struct cachefiles_req *req) load->fd = fd; req->msg.object_id = object_id; object->ondemand_id = object_id; + + cachefiles_get_unbind_pincount(cache); return 0;
err_put_fd: