Skip to content

Commit

Permalink
cachefiles: notify user daemon with anon_fd when looking up cookie
Browse files Browse the repository at this point in the history
Fscache/cachefiles used to serve as a local cache for remote fs. This
patch, along with the following patches, introduces a new on-demand read
mode for cachefiles, which can boost the scenario where on-demand read
semantics is needed, e.g. container image distribution.

The essential difference between the original mode and on-demand read
mode is that, in the original mode, when cache miss, netfs itself will
fetch data from remote, and then write the fetched data into cache file.
While in on-demand read mode, a user daemon is responsible for fetching
data and then writing to the cache file.

As the first step, notify user daemon with anon_fd when looking up
cookie.

Send the anonymous fd to user daemon when looking up cookie, no matter
whether the cache file exist there or not. With the given anonymous fd,
user daemon can fetch and then write data into cache file in advance,
even when cache miss has not happended yet.

Also add one advisory flag (FSCACHE_ADV_WANT_CACHE_SIZE) suggesting that
cache file size shall be retrieved at runtime. This helps the scenario
where one cache file can contain multiple netfs files for the purpose of
deduplication, e.g. In this case, netfs itself has no idea the cache
file size, whilst user daemon needs to offer the hint on the cache file
size.

Signed-off-by: Jeffle Xu <jefflexu@linux.alibaba.com>
  • Loading branch information
lostjeffle authored and intel-lab-lkp committed Mar 25, 2022
1 parent dbec43e commit ec8aa2f
Show file tree
Hide file tree
Showing 9 changed files with 529 additions and 13 deletions.
11 changes: 11 additions & 0 deletions fs/cachefiles/Kconfig
Expand Up @@ -26,3 +26,14 @@ config CACHEFILES_ERROR_INJECTION
help
This permits error injection to be enabled in cachefiles whilst a
cache is in service.

config CACHEFILES_ONDEMAND
bool "Support for on-demand read"
depends on CACHEFILES
default n
help
This permits on-demand read mode of cachefiles. In this mode, when
cache miss, the cachefiles backend instead of netfs, is responsible
for fetching data, e.g. through user daemon.

If unsure, say N.
1 change: 1 addition & 0 deletions fs/cachefiles/Makefile
Expand Up @@ -16,5 +16,6 @@ cachefiles-y := \
xattr.o

cachefiles-$(CONFIG_CACHEFILES_ERROR_INJECTION) += error_inject.o
cachefiles-$(CONFIG_CACHEFILES_ONDEMAND) += ondemand.o

obj-$(CONFIG_CACHEFILES) := cachefiles.o
76 changes: 65 additions & 11 deletions fs/cachefiles/daemon.c
Expand Up @@ -75,6 +75,9 @@ static const struct cachefiles_daemon_cmd cachefiles_daemon_cmds[] = {
{ "inuse", cachefiles_daemon_inuse },
{ "secctx", cachefiles_daemon_secctx },
{ "tag", cachefiles_daemon_tag },
#ifdef CONFIG_CACHEFILES_ONDEMAND
{ "cinit", cachefiles_ondemand_cinit },
#endif
{ "", NULL }
};

Expand Down Expand Up @@ -108,6 +111,9 @@ static int cachefiles_daemon_open(struct inode *inode, struct file *file)
INIT_LIST_HEAD(&cache->volumes);
INIT_LIST_HEAD(&cache->object_list);
spin_lock_init(&cache->object_list_lock);
#ifdef CONFIG_CACHEFILES_ONDEMAND
xa_init_flags(&cache->reqs, XA_FLAGS_ALLOC);
#endif

/* set default caching limits
* - limit at 1% free space and/or free files
Expand All @@ -126,6 +132,27 @@ static int cachefiles_daemon_open(struct inode *inode, struct file *file)
return 0;
}

#ifdef CONFIG_CACHEFILES_ONDEMAND
static inline void cachefiles_flush_reqs(struct cachefiles_cache *cache)
{
struct xarray *xa = &cache->reqs;
struct cachefiles_req *req;
unsigned long index;

/*
* 1) Cache has been marked as dead state, and then 2) flush all
* pending requests in @reqs xarray. The barrier inside set_bit()
* will ensure that above two ops won't be reordered.
*/
xa_lock(xa);
xa_for_each(xa, index, req) {
req->error = -EIO;
complete(&req->done);
}
xa_unlock(xa);
}
#endif

/*
* Release a cache.
*/
Expand All @@ -139,6 +166,11 @@ static int cachefiles_daemon_release(struct inode *inode, struct file *file)

set_bit(CACHEFILES_DEAD, &cache->flags);

#ifdef CONFIG_CACHEFILES_ONDEMAND
cachefiles_flush_reqs(cache);
xa_destroy(&cache->reqs);
#endif

cachefiles_daemon_unbind(cache);

/* clean up the control file interface */
Expand All @@ -152,23 +184,15 @@ static int cachefiles_daemon_release(struct inode *inode, struct file *file)
return 0;
}

/*
* Read the cache state.
*/
static ssize_t cachefiles_daemon_read(struct file *file, char __user *_buffer,
size_t buflen, loff_t *pos)
static ssize_t cachefiles_do_daemon_read(struct cachefiles_cache *cache,
char __user *_buffer,
size_t buflen)
{
struct cachefiles_cache *cache = file->private_data;
unsigned long long b_released;
unsigned f_released;
char buffer[256];
int n;

//_enter(",,%zu,", buflen);

if (!test_bit(CACHEFILES_READY, &cache->flags))
return 0;

/* check how much space the cache has */
cachefiles_has_space(cache, 0, 0, cachefiles_has_space_check);

Expand Down Expand Up @@ -206,6 +230,26 @@ static ssize_t cachefiles_daemon_read(struct file *file, char __user *_buffer,
return n;
}

/*
* Read the cache state.
*/
static ssize_t cachefiles_daemon_read(struct file *file, char __user *_buffer,
size_t buflen, loff_t *pos)
{
struct cachefiles_cache *cache = file->private_data;

//_enter(",,%zu,", buflen);

if (!test_bit(CACHEFILES_READY, &cache->flags))
return 0;

if (IS_ENABLED(CONFIG_CACHEFILES_ONDEMAND) &&
test_bit(CACHEFILES_ONDEMAND_MODE, &cache->flags))
return cachefiles_ondemand_daemon_read(cache, _buffer, buflen);
else
return cachefiles_do_daemon_read(cache, _buffer, buflen);
}

/*
* Take a command from cachefilesd, parse it and act on it.
*/
Expand Down Expand Up @@ -297,8 +341,18 @@ static __poll_t cachefiles_daemon_poll(struct file *file,
poll_wait(file, &cache->daemon_pollwq, poll);
mask = 0;

#ifdef CONFIG_CACHEFILES_ONDEMAND
if (test_bit(CACHEFILES_ONDEMAND_MODE, &cache->flags)) {
if (!xa_empty(&cache->reqs))
mask |= EPOLLIN;
} else {
if (test_bit(CACHEFILES_STATE_CHANGED, &cache->flags))
mask |= EPOLLIN;
}
#else
if (test_bit(CACHEFILES_STATE_CHANGED, &cache->flags))
mask |= EPOLLIN;
#endif

if (test_bit(CACHEFILES_CULLING, &cache->flags))
mask |= EPOLLOUT;
Expand Down
44 changes: 44 additions & 0 deletions fs/cachefiles/internal.h
Expand Up @@ -15,6 +15,8 @@
#include <linux/fscache-cache.h>
#include <linux/cred.h>
#include <linux/security.h>
#include <linux/xarray.h>
#include <linux/cachefiles.h>

#define CACHEFILES_DIO_BLOCK_SIZE 4096

Expand Down Expand Up @@ -58,6 +60,9 @@ struct cachefiles_object {
enum cachefiles_content content_info:8; /* Info about content presence */
unsigned long flags;
#define CACHEFILES_OBJECT_USING_TMPFILE 0 /* Have an unlinked tmpfile */
#ifdef CONFIG_CACHEFILES_ONDEMAND
int fd; /* anonymous fd */
#endif
};

/*
Expand Down Expand Up @@ -98,11 +103,24 @@ struct cachefiles_cache {
#define CACHEFILES_DEAD 1 /* T if cache dead */
#define CACHEFILES_CULLING 2 /* T if cull engaged */
#define CACHEFILES_STATE_CHANGED 3 /* T if state changed (poll trigger) */
#define CACHEFILES_ONDEMAND_MODE 4 /* T if in on-demand read mode */
char *rootdirname; /* name of cache root directory */
char *secctx; /* LSM security context */
char *tag; /* cache binding tag */
#ifdef CONFIG_CACHEFILES_ONDEMAND
struct xarray reqs; /* xarray of pending on-demand requests */
#endif
};

struct cachefiles_req {
struct cachefiles_object *object;
struct completion done;
int error;
struct cachefiles_msg msg;
};

#define CACHEFILES_REQ_NEW XA_MARK_1

#include <trace/events/cachefiles.h>

static inline
Expand Down Expand Up @@ -250,6 +268,32 @@ extern struct file *cachefiles_create_tmpfile(struct cachefiles_object *object);
extern bool cachefiles_commit_tmpfile(struct cachefiles_cache *cache,
struct cachefiles_object *object);

/*
* ondemand.c
*/
#ifdef CONFIG_CACHEFILES_ONDEMAND
extern ssize_t cachefiles_ondemand_daemon_read(struct cachefiles_cache *cache,
char __user *_buffer,
size_t buflen);

extern int cachefiles_ondemand_cinit(struct cachefiles_cache *cache,
char *args);

extern int cachefiles_ondemand_init_object(struct cachefiles_object *object);

#else
ssize_t cachefiles_ondemand_daemon_read(struct cachefiles_cache *cache,
char __user *_buffer, size_t buflen)
{
return -EOPNOTSUPP;
}

static inline int cachefiles_ondemand_init_object(struct cachefiles_object *object)
{
return 0;
}
#endif

/*
* security.c
*/
Expand Down
16 changes: 14 additions & 2 deletions fs/cachefiles/namei.c
Expand Up @@ -444,10 +444,9 @@ struct file *cachefiles_create_tmpfile(struct cachefiles_object *object)
struct dentry *fan = volume->fanout[(u8)object->cookie->key_hash];
struct file *file;
struct path path;
uint64_t ni_size = object->cookie->object_size;
uint64_t ni_size;
long ret;

ni_size = round_up(ni_size, CACHEFILES_DIO_BLOCK_SIZE);

cachefiles_begin_secure(cache, &saved_cred);

Expand All @@ -473,6 +472,15 @@ struct file *cachefiles_create_tmpfile(struct cachefiles_object *object)
goto out_dput;
}

ret = cachefiles_ondemand_init_object(object);
if (ret < 0) {
file = ERR_PTR(ret);
goto out_dput;
}

ni_size = object->cookie->object_size;
ni_size = round_up(ni_size, CACHEFILES_DIO_BLOCK_SIZE);

if (ni_size > 0) {
trace_cachefiles_trunc(object, d_backing_inode(path.dentry), 0, ni_size,
cachefiles_trunc_expand_tmpfile);
Expand Down Expand Up @@ -573,6 +581,10 @@ static bool cachefiles_open_file(struct cachefiles_object *object,
}
_debug("file -> %pd positive", dentry);

ret = cachefiles_ondemand_init_object(object);
if (ret < 0)
goto error_fput;

ret = cachefiles_check_auxdata(object, file);
if (ret < 0)
goto check_failed;
Expand Down

0 comments on commit ec8aa2f

Please sign in to comment.