Skip to content

Commit

Permalink
cachefiles: notify the user daemon 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 a remote
networking fs. A new on-demand read mode will be introduced for
CacheFiles, which can boost the scenario where on-demand read semantics
are needed, e.g. container image distribution.

The essential difference between these two modes is seen when a cache
miss occurs: In the original mode, the netfs will fetch the data from
the remote server and then write it to the cache file; in on-demand
read mode, fetching the data and writing it into the cache is delegated
to a user daemon.

As the first step, notify the user daemon when looking up cookie. In
this case, an anonymous fd is sent to the user daemon, through which the
user daemon can write the fetched data to the cache file. Since the user
daemon may move the anonymous fd around, e.g. through dup(), an object
ID uniquely identifying the cache file is also attached.

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

Signed-off-by: Jeffle Xu <jefflexu@linux.alibaba.com>
Link: https://lore.kernel.org/r/20220509074028.74954-3-jefflexu@linux.alibaba.com
Acked-by: David Howells <dhowells@redhat.com>
Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
  • Loading branch information
Jeffle Xu authored and Gao Xiang committed May 17, 2022
1 parent a06fac1 commit c838305
Show file tree
Hide file tree
Showing 9 changed files with 577 additions and 15 deletions.
12 changes: 12 additions & 0 deletions fs/cachefiles/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,15 @@ 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 userspace to enable the cachefiles on-demand read mode.
In this mode, when a cache miss occurs, responsibility for fetching
the data lies with the cachefiles backend instead of with the netfs
and is delegated to userspace.

If unsure, say N.
1 change: 1 addition & 0 deletions fs/cachefiles/Makefile
Original file line number Diff line number Diff line change
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
81 changes: 68 additions & 13 deletions fs/cachefiles/daemon.c
Original file line number Diff line number Diff line change
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
{ "copen", cachefiles_ondemand_copen },
#endif
{ "", NULL }
};

Expand Down Expand Up @@ -108,6 +111,8 @@ 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);
xa_init_flags(&cache->reqs, XA_FLAGS_ALLOC);
xa_init_flags(&cache->ondemand_ids, XA_FLAGS_ALLOC1);

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

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

/*
* Make sure the following two operations won't be reordered.
* 1) set CACHEFILES_DEAD bit
* 2) flush requests in the xarray
* Otherwise the request may be enqueued after xarray has been
* flushed, leaving the orphan request never being completed.
*
* CPU 1 CPU 2
* ===== =====
* flush requests in the xarray
* test CACHEFILES_DEAD bit
* enqueue the request
* set CACHEFILES_DEAD bit
*/
smp_mb();

xa_lock(xa);
xa_for_each(xa, index, req) {
req->error = -EIO;
complete(&req->done);
}
xa_unlock(xa);

xa_destroy(&cache->reqs);
xa_destroy(&cache->ondemand_ids);
}

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

set_bit(CACHEFILES_DEAD, &cache->flags);

if (cachefiles_in_ondemand_mode(cache))
cachefiles_flush_reqs(cache);
cachefiles_daemon_unbind(cache);

/* clean up the control file interface */
Expand All @@ -152,23 +192,14 @@ 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 +237,25 @@ 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 (cachefiles_in_ondemand_mode(cache))
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 +347,13 @@ static __poll_t cachefiles_daemon_poll(struct file *file,
poll_wait(file, &cache->daemon_pollwq, poll);
mask = 0;

if (test_bit(CACHEFILES_STATE_CHANGED, &cache->flags))
mask |= EPOLLIN;
if (cachefiles_in_ondemand_mode(cache)) {
if (!xa_empty(&cache->reqs))
mask |= EPOLLIN;
} else {
if (test_bit(CACHEFILES_STATE_CHANGED, &cache->flags))
mask |= EPOLLIN;
}

if (test_bit(CACHEFILES_CULLING, &cache->flags))
mask |= EPOLLOUT;
Expand Down
51 changes: 51 additions & 0 deletions fs/cachefiles/internal.h
Original file line number Diff line number Diff line change
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,8 +60,13 @@ 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 ondemand_id;
#endif
};

#define CACHEFILES_ONDEMAND_ID_CLOSED -1

/*
* Cache files cache definition
*/
Expand Down Expand Up @@ -98,11 +105,30 @@ 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 */
struct xarray reqs; /* xarray of pending on-demand requests */
struct xarray ondemand_ids; /* xarray for ondemand_id allocation */
u32 ondemand_id_next;
};

static inline bool cachefiles_in_ondemand_mode(struct cachefiles_cache *cache)
{
return IS_ENABLED(CONFIG_CACHEFILES_ONDEMAND) &&
test_bit(CACHEFILES_ONDEMAND_MODE, &cache->flags);
}

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 +276,31 @@ 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_copen(struct cachefiles_cache *cache,
char *args);

extern int cachefiles_ondemand_init_object(struct cachefiles_object *object);

#else
static inline 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
Original file line number Diff line number Diff line change
Expand Up @@ -452,10 +452,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 @@ -481,6 +480,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_unuse;
}

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 @@ -586,6 +594,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
Loading

0 comments on commit c838305

Please sign in to comment.