hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IB5UKT
--------------------------------
In ondemand mode, when the daemon is processing an open request, if the kernel flags the cache as CACHEFILES_DEAD due to -EIO, cachefiles_daemon_write() will always return -EIO, so the daemon can't pass the copen to the kernel. Then the kernel process that is waiting for the copen triggers the following hung_task because it can't receive the copen.
INFO: task kworker/u8:2:255269 blocked for more than 1212 seconds. Not tainted 5.10.0-00001-g24c450967e57-dirty #21 "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. task:kworker/u8:2 state:D stack:0 pid:255269 ppid:2 flags:0x00004080 Workqueue: fscache_object fscache_object_work_func Call Trace: __schedule+0x623/0xed0 schedule+0x84/0x140 schedule_timeout+0x559/0x610 wait_for_common+0x156/0x270 cachefiles_ondemand_send_req+0x2bd/0x390 cachefiles_ondemand_init_object+0x10a/0x120 cachefiles_walk_to_object+0x849/0xee0 cachefiles_lookup_object+0xa1/0x190 fscache_look_up_object+0x24e/0x320 fscache_object_sm_dispatcher+0xe0/0x5d0 fscache_object_work_func+0x30/0x40 process_one_work+0x40e/0x810 worker_thread+0x96/0x700 kthread+0x1f4/0x250
Since the DEAD state is irreversible, it can only be exited by reopening /dev/cachefiles. Therefore, after calling cachefiles_io_error() to mark the cache as CACHEFILES_DEAD, if in ondemand mode, flush all requests to avoid the above hungtask. We may still be able to read some of the cached data before releasing the fd of /dev/cachefiles.
Note that this relies on the patch that adds reference counting to the req, otherwise it will UAF.
Signed-off-by: Baokun Li libaokun1@huawei.com --- fs/cachefiles/daemon.c | 2 +- fs/cachefiles/internal.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/fs/cachefiles/daemon.c b/fs/cachefiles/daemon.c index e26ebbc89806..50433c6024dd 100644 --- a/fs/cachefiles/daemon.c +++ b/fs/cachefiles/daemon.c @@ -131,7 +131,7 @@ static int cachefiles_daemon_open(struct inode *inode, struct file *file) return 0; }
-static void cachefiles_flush_reqs(struct cachefiles_cache *cache) +void cachefiles_flush_reqs(struct cachefiles_cache *cache) { void **slot; struct radix_tree_iter iter; diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h index 38c33146f4a8..355fb9b79741 100644 --- a/fs/cachefiles/internal.h +++ b/fs/cachefiles/internal.h @@ -184,6 +184,7 @@ extern void cachefiles_daemon_unbind(struct cachefiles_cache *cache); * daemon.c */ extern const struct file_operations cachefiles_daemon_fops; +extern void cachefiles_flush_reqs(struct cachefiles_cache *cache); extern void cachefiles_get_unbind_pincount(struct cachefiles_cache *cache); extern void cachefiles_put_unbind_pincount(struct cachefiles_cache *cache);
@@ -384,6 +385,8 @@ do { \ pr_err("I/O Error: " FMT"\n", ##__VA_ARGS__); \ fscache_io_error(&(___cache)->cache); \ set_bit(CACHEFILES_DEAD, &(___cache)->flags); \ + if (cachefiles_in_ondemand_mode(___cache)) \ + cachefiles_flush_reqs(___cache); \ } while (0)
#define cachefiles_io_error_obj(object, FMT, ...) \