diff --git a/fs/cachefiles/daemon.c b/fs/cachefiles/daemon.c index 1a3a9fda60c8df1fe25a93bb824711d72487076a..b531373400d7825b41a75eacb163597e902d8ba1 100644 --- a/fs/cachefiles/daemon.c +++ b/fs/cachefiles/daemon.c @@ -156,7 +156,8 @@ static void cachefiles_flush_reqs(struct cachefiles_cache *cache) radix_tree_for_each_slot(slot, &cache->reqs, &iter, 0) { req = radix_tree_deref_slot_protected(slot, &cache->reqs.xa_lock); - BUG_ON(!req); + if (WARN_ON(!req)) + continue; radix_tree_delete(&cache->reqs, iter.index); req->error = -EIO; complete(&req->done); diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h index a787fe8ef8ccd5fa7ec0f6aa6bc2bce7d80520e6..b8ef5be59005f1a19230dabccb9485ecdcc577ea 100644 --- a/fs/cachefiles/internal.h +++ b/fs/cachefiles/internal.h @@ -98,6 +98,7 @@ struct cachefiles_cache { 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 */ + unsigned long req_id_next; struct idr ondemand_ids; /* xarray for ondemand_id allocation */ u32 ondemand_id_next; }; diff --git a/fs/cachefiles/ondemand.c b/fs/cachefiles/ondemand.c index a7c3e5a6e0b92e94e11e4f325e1f6f53349ab4cf..250b98e9820c6856f2c74a2f44ccfdf51771cdc9 100644 --- a/fs/cachefiles/ondemand.c +++ b/fs/cachefiles/ondemand.c @@ -27,8 +27,8 @@ static int cachefiles_ondemand_fd_release(struct inode *inode, radix_tree_for_each_slot(slot, &cache->reqs, &iter, 0) { req = radix_tree_deref_slot_protected(slot, &cache->reqs.xa_lock); - BUG_ON(!req); - + if (WARN_ON(!req)) + continue; if (req->msg.object_id == object_id && req->msg.opcode == CACHEFILES_OP_READ) { req->error = -EIO; @@ -150,9 +150,13 @@ int cachefiles_ondemand_copen(struct cachefiles_cache *cache, char *args) /* fail OPEN request if daemon reports an error */ if (size < 0) { - if (!IS_ERR_VALUE(size)) - size = -EINVAL; - req->error = size; + if (!IS_ERR_VALUE(size)) { + req->error = -EINVAL; + ret = -EINVAL; + } else { + req->error = size; + ret = 0; + } goto out; } @@ -233,7 +237,7 @@ static int cachefiles_ondemand_get_fd(struct cachefiles_req *req) ssize_t cachefiles_ondemand_daemon_read(struct cachefiles_cache *cache, char __user *_buffer, size_t buflen, loff_t *pos) { - struct cachefiles_req *req; + struct cachefiles_req *req = NULL; struct cachefiles_msg *msg; unsigned long id = 0; size_t n; @@ -242,52 +246,69 @@ ssize_t cachefiles_ondemand_daemon_read(struct cachefiles_cache *cache, void **slot; /* - * Search for a request that has not ever been processed, to prevent - * requests from being processed repeatedly. + * Cyclically search for a request that has not ever been processed, + * to prevent requests from being processed repeatedly, and make + * request distribution fair. */ xa_lock(&cache->reqs); - radix_tree_for_each_tagged(slot, &cache->reqs, &iter, 0, + radix_tree_for_each_tagged(slot, &cache->reqs, &iter, cache->req_id_next, CACHEFILES_REQ_NEW) { - req = radix_tree_deref_slot_protected(slot, - &cache->reqs.xa_lock); - - msg = &req->msg; - n = msg->len; + req = radix_tree_deref_slot_protected(slot, &cache->reqs.xa_lock); + WARN_ON(!req); + break; + } - if (n > buflen) { - xa_unlock(&cache->reqs); - return -EMSGSIZE; + if (!req && cache->req_id_next > 0) { + radix_tree_for_each_tagged(slot, &cache->reqs, &iter, 0, + CACHEFILES_REQ_NEW) { + if (iter.index >= cache->req_id_next) + break; + req = radix_tree_deref_slot_protected(slot, &cache->reqs.xa_lock); + WARN_ON(!req); + break; } + } - radix_tree_iter_tag_clear(&cache->reqs, &iter, - CACHEFILES_REQ_NEW); + /* no request tagged with CACHEFILES_REQ_NEW found */ + if (!req) { xa_unlock(&cache->reqs); + return 0; + } - id = iter.index; - msg->msg_id = id; + msg = &req->msg; + n = msg->len; - if (msg->opcode == CACHEFILES_OP_OPEN) { - ret = cachefiles_ondemand_get_fd(req); - if (ret) - goto error; - } + if (n > buflen) { + xa_unlock(&cache->reqs); + return -EMSGSIZE; + } - if (copy_to_user(_buffer, msg, n) != 0) { - ret = -EFAULT; - goto err_put_fd; - } + radix_tree_iter_tag_clear(&cache->reqs, &iter, CACHEFILES_REQ_NEW); + cache->req_id_next = iter.index + 1; + xa_unlock(&cache->reqs); - /* CLOSE request has no reply */ - if (msg->opcode == CACHEFILES_OP_CLOSE) { - xa_lock(&cache->reqs); - radix_tree_delete(&cache->reqs, id); - xa_unlock(&cache->reqs); - complete(&req->done); - } - return n; + id = iter.index; + msg->msg_id = id; + + if (msg->opcode == CACHEFILES_OP_OPEN) { + ret = cachefiles_ondemand_get_fd(req); + if (ret) + goto error; + } + + if (copy_to_user(_buffer, msg, n) != 0) { + ret = -EFAULT; + goto err_put_fd; } - xa_unlock(&cache->reqs); - return 0; + + /* CLOSE request has no reply */ + if (msg->opcode == CACHEFILES_OP_CLOSE) { + xa_lock(&cache->reqs); + radix_tree_delete(&cache->reqs, id); + xa_unlock(&cache->reqs); + complete(&req->done); + } + return n; err_put_fd: if (msg->opcode == CACHEFILES_OP_OPEN) @@ -391,21 +412,27 @@ static int cachefiles_ondemand_init_open_req(struct cachefiles_req *req, { struct cachefiles_object *object = req->object; struct fscache_cookie *cookie = object->fscache.cookie; - struct fscache_cookie *volume; + struct fscache_cookie *volume = object->fscache.parent->cookie; struct cachefiles_open *load = (void *)req->msg.data; size_t volume_key_size, cookie_key_size; char *cookie_key, *volume_key; - /* Cookie key is binary data, which is netfs specific. */ + /* + * cookie_key is a string without trailing '\0', while cachefiles_open + * expects cookie key a string without trailing '\0'. + */ cookie_key_size = cookie->key_len; if (cookie->key_len <= sizeof(cookie->inline_key)) cookie_key = cookie->inline_key; else cookie_key = cookie->key; - volume = object->fscache.parent->cookie; + /* + * volume_key is a string without trailing '\0', while cachefiles_open + * expects volume key a string with trailing '\0'. + */ volume_key_size = volume->key_len + 1; - if (volume_key_size <= sizeof(cookie->inline_key)) + if (volume->key_len <= sizeof(volume->inline_key)) volume_key = volume->inline_key; else volume_key = volume->key; diff --git a/fs/erofs/super.c b/fs/erofs/super.c index 75e069ad7a66eda9a988e7d0124e9e4ccffe5a7e..3e05b7ac3726bffae16d493b438fddd5b5ae9594 100644 --- a/fs/erofs/super.c +++ b/fs/erofs/super.c @@ -681,7 +681,7 @@ static void erofs_kill_sb(struct super_block *sb) WARN_ON(sb->s_magic != EROFS_SUPER_MAGIC); if (erofs_is_fscache_mode(sb)) - generic_shutdown_super(sb); + kill_anon_super(sb); else kill_block_super(sb); diff --git a/include/uapi/linux/cachefiles.h b/include/uapi/linux/cachefiles.h index 78caa73e5343419421a92bcc881f4d9c6e4915f2..b6746a2fe57c474493c01d7e1ddb25e97d9c9b61 100644 --- a/include/uapi/linux/cachefiles.h +++ b/include/uapi/linux/cachefiles.h @@ -37,8 +37,9 @@ struct cachefiles_msg { /* * @data contains the volume_key followed directly by the cookie_key. volume_key * is a NUL-terminated string; @volume_key_size indicates the size of the volume - * key in bytes. cookie_key is binary data, which is netfs specific; - * @cookie_key_size indicates the size of the cookie key in bytes. + * key in bytes (with trailing NUL). cookie_key is a string without trailing + * NUL; @cookie_key_size indicates the size of the cookie key in bytes (without + * trailing NUL). * * @fd identifies an anon_fd referring to the cache file. */