Unverified Commit 3477aa10 authored by openeuler-ci-bot's avatar openeuler-ci-bot Committed by Gitee
Browse files

!9240 v2 cachefiles: introduce failover mechanism

Merge Pull Request from: @ci-robot 
 
PR sync from: Zizhi Wo <wozizhi@huawei.com>
https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/ER6NW2UM2NMM5WBZCW7HM2BIQYXAMDLS/ 
V2: add subsequent bugfix patches and some clean up.

V1: adapt mainline patch for main function

Baokun Li (11):
  cachefiles: add output string to cachefiles_obj_[get|put]_ondemand_fd
  cachefiles: remove requests from xarray during flushing requests
  cachefiles: fix slab-use-after-free in cachefiles_ondemand_get_fd()
  cachefiles: fix slab-use-after-free in
    cachefiles_ondemand_daemon_read()
  cachefiles: remove err_put_fd label in
    cachefiles_ondemand_daemon_read()
  cachefiles: add consistency check for copen/cread
  cachefiles: add spin_lock for cachefiles_ondemand_info
  cachefiles: never get a new anonymous fd if ondemand_id is valid
  cachefiles: defer exposing anon_fd until after copy_to_user() succeeds
  cachefiles: flush all requests after setting CACHEFILES_DEAD
  cachefiles: make on-demand read killable

David Howells (1):
  cachefiles, erofs: Fix NULL deref in when cachefiles is not doing

Jia Zhu (5):
  cachefiles: introduce object ondemand state
  cachefiles: extract ondemand info field from cachefiles_object
  cachefiles: resend an open request if the read request's object is
    closed
  cachefiles: narrow the scope of triggering EPOLLIN events in ondemand
  cachefiles: add restore command to recover inflight ondemand read
    requests

Zizhi Wo (1):
  cachefiles: Set object to close if ondemand_id < 0 in copen


-- 
2.39.2
 
https://gitee.com/openeuler/kernel/issues/IA6I1T 
 
Link:https://gitee.com/openeuler/kernel/pulls/9240

 

Reviewed-by: default avatarHou Tao <houtao1@huawei.com>
Signed-off-by: default avatarZhang Peng <zhangpeng362@huawei.com>
parents 86f0c3f2 bd5b0ab7
Loading
Loading
Loading
Loading
+15 −3
Original line number Diff line number Diff line
@@ -77,6 +77,7 @@ static const struct cachefiles_daemon_cmd cachefiles_daemon_cmds[] = {
	{ "tag",	cachefiles_daemon_tag		},
#ifdef CONFIG_CACHEFILES_ONDEMAND
	{ "copen",	cachefiles_ondemand_copen	},
	{ "restore",	cachefiles_ondemand_restore	},
#endif
	{ "",		NULL				}
};
@@ -132,7 +133,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)
{
	struct xarray *xa = &cache->reqs;
	struct cachefiles_req *req;
@@ -158,6 +159,7 @@ static void cachefiles_flush_reqs(struct cachefiles_cache *cache)
	xa_for_each(xa, index, req) {
		req->error = -EIO;
		complete(&req->done);
		__xa_erase(xa, index);
	}
	xa_unlock(xa);

@@ -355,14 +357,24 @@ static __poll_t cachefiles_daemon_poll(struct file *file,
					   struct poll_table_struct *poll)
{
	struct cachefiles_cache *cache = file->private_data;
	XA_STATE(xas, &cache->reqs, 0);
	struct cachefiles_req *req;
	__poll_t mask;

	poll_wait(file, &cache->daemon_pollwq, poll);
	mask = 0;

	if (cachefiles_in_ondemand_mode(cache)) {
		if (!xa_empty(&cache->reqs))
		if (!xa_empty(&cache->reqs)) {
			rcu_read_lock();
			xas_for_each_marked(&xas, req, ULONG_MAX, CACHEFILES_REQ_NEW) {
				if (!cachefiles_ondemand_is_reopening_read(req)) {
					mask |= EPOLLIN;
					break;
				}
			}
			rcu_read_unlock();
		}
	} else {
		if (test_bit(CACHEFILES_STATE_CHANGED, &cache->flags))
			mask |= EPOLLIN;
+6 −1
Original line number Diff line number Diff line
@@ -31,6 +31,11 @@ struct cachefiles_object *cachefiles_alloc_object(struct fscache_cookie *cookie)
	if (!object)
		return NULL;

	if (cachefiles_ondemand_init_obj_info(object, volume)) {
		kmem_cache_free(cachefiles_object_jar, object);
		return NULL;
	}

	refcount_set(&object->ref, 1);

	spin_lock_init(&object->lock);
@@ -88,7 +93,7 @@ void cachefiles_put_object(struct cachefiles_object *object,
		ASSERTCMP(object->file, ==, NULL);

		kfree(object->d_name);

		cachefiles_ondemand_deinit_obj_info(object);
		cache = object->volume->cache->cache;
		fscache_put_cookie(object->cookie, fscache_cookie_put_object);
		object->cookie = NULL;
+63 −1
Original line number Diff line number Diff line
@@ -44,6 +44,20 @@ struct cachefiles_volume {
	struct dentry			*fanout[256];	/* Fanout subdirs */
};

enum cachefiles_object_state {
	CACHEFILES_ONDEMAND_OBJSTATE_CLOSE, /* Anonymous fd closed by daemon or initial state */
	CACHEFILES_ONDEMAND_OBJSTATE_OPEN, /* Anonymous fd associated with object is available */
	CACHEFILES_ONDEMAND_OBJSTATE_REOPENING, /* Object that was closed and is being reopened. */
};

struct cachefiles_ondemand_info {
	struct work_struct		ondemand_work;
	int				ondemand_id;
	enum cachefiles_object_state	state;
	struct cachefiles_object	*object;
	spinlock_t			lock;
};

/*
 * Backing file state.
 */
@@ -61,7 +75,7 @@ struct cachefiles_object {
	unsigned long			flags;
#define CACHEFILES_OBJECT_USING_TMPFILE	0		/* Have an unlinked tmpfile */
#ifdef CONFIG_CACHEFILES_ONDEMAND
	int				ondemand_id;
	struct cachefiles_ondemand_info	*ondemand;
#endif
};

@@ -125,6 +139,7 @@ static inline bool cachefiles_in_ondemand_mode(struct cachefiles_cache *cache)
struct cachefiles_req {
	struct cachefiles_object *object;
	struct completion done;
	refcount_t ref;
	int error;
	struct cachefiles_msg msg;
};
@@ -173,6 +188,7 @@ extern int cachefiles_has_space(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);

@@ -290,12 +306,42 @@ extern ssize_t cachefiles_ondemand_daemon_read(struct cachefiles_cache *cache,
extern int cachefiles_ondemand_copen(struct cachefiles_cache *cache,
				     char *args);

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

extern int cachefiles_ondemand_init_object(struct cachefiles_object *object);
extern void cachefiles_ondemand_clean_object(struct cachefiles_object *object);

extern int cachefiles_ondemand_read(struct cachefiles_object *object,
				    loff_t pos, size_t len);

extern int cachefiles_ondemand_init_obj_info(struct cachefiles_object *obj,
					struct cachefiles_volume *volume);
extern void cachefiles_ondemand_deinit_obj_info(struct cachefiles_object *obj);

#define CACHEFILES_OBJECT_STATE_FUNCS(_state, _STATE)	\
static inline bool								\
cachefiles_ondemand_object_is_##_state(const struct cachefiles_object *object) \
{												\
	return object->ondemand->state == CACHEFILES_ONDEMAND_OBJSTATE_##_STATE; \
}												\
												\
static inline void								\
cachefiles_ondemand_set_object_##_state(struct cachefiles_object *object) \
{												\
	object->ondemand->state = CACHEFILES_ONDEMAND_OBJSTATE_##_STATE; \
}

CACHEFILES_OBJECT_STATE_FUNCS(open, OPEN);
CACHEFILES_OBJECT_STATE_FUNCS(close, CLOSE);
CACHEFILES_OBJECT_STATE_FUNCS(reopening, REOPENING);

static inline bool cachefiles_ondemand_is_reopening_read(struct cachefiles_req *req)
{
	return cachefiles_ondemand_object_is_reopening(req->object) &&
			req->msg.opcode == CACHEFILES_OP_READ;
}

#else
static inline ssize_t cachefiles_ondemand_daemon_read(struct cachefiles_cache *cache,
					char __user *_buffer, size_t buflen)
@@ -317,6 +363,20 @@ static inline int cachefiles_ondemand_read(struct cachefiles_object *object,
{
	return -EOPNOTSUPP;
}

static inline int cachefiles_ondemand_init_obj_info(struct cachefiles_object *obj,
						struct cachefiles_volume *volume)
{
	return 0;
}
static inline void cachefiles_ondemand_deinit_obj_info(struct cachefiles_object *obj)
{
}

static inline bool cachefiles_ondemand_is_reopening_read(struct cachefiles_req *req)
{
	return false;
}
#endif

/*
@@ -367,6 +427,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, ...)			\
+282 −90
Original line number Diff line number Diff line
@@ -4,26 +4,45 @@
#include <linux/uio.h>
#include "internal.h"

struct ondemand_anon_file {
	struct file *file;
	int fd;
};

static inline void cachefiles_req_put(struct cachefiles_req *req)
{
	if (refcount_dec_and_test(&req->ref))
		kfree(req);
}

static int cachefiles_ondemand_fd_release(struct inode *inode,
					  struct file *file)
{
	struct cachefiles_object *object = file->private_data;
	struct cachefiles_cache *cache = object->volume->cache;
	int object_id = object->ondemand_id;
	struct cachefiles_cache *cache;
	struct cachefiles_ondemand_info *info;
	int object_id;
	struct cachefiles_req *req;
	XA_STATE(xas, &cache->reqs, 0);
	XA_STATE(xas, NULL, 0);

	xa_lock(&cache->reqs);
	object->ondemand_id = CACHEFILES_ONDEMAND_ID_CLOSED;
	if (!object)
		return 0;

	/*
	 * Flush all pending READ requests since their completion depends on
	 * anon_fd.
	 */
	xas_for_each(&xas, req, ULONG_MAX) {
	info = object->ondemand;
	cache = object->volume->cache;
	xas.xa = &cache->reqs;

	xa_lock(&cache->reqs);
	spin_lock(&info->lock);
	object_id = info->ondemand_id;
	info->ondemand_id = CACHEFILES_ONDEMAND_ID_CLOSED;
	cachefiles_ondemand_set_object_close(object);
	spin_unlock(&info->lock);

	/* Only flush CACHEFILES_REQ_NEW marked req to avoid race with daemon_read */
	xas_for_each_marked(&xas, req, ULONG_MAX, CACHEFILES_REQ_NEW) {
		if (req->msg.object_id == object_id &&
		    req->msg.opcode == CACHEFILES_OP_READ) {
			req->error = -EIO;
		    req->msg.opcode == CACHEFILES_OP_CLOSE) {
			complete(&req->done);
			xas_store(&xas, NULL);
		}
@@ -78,12 +97,12 @@ static loff_t cachefiles_ondemand_fd_llseek(struct file *filp, loff_t pos,
}

static long cachefiles_ondemand_fd_ioctl(struct file *filp, unsigned int ioctl,
					 unsigned long arg)
					 unsigned long id)
{
	struct cachefiles_object *object = filp->private_data;
	struct cachefiles_cache *cache = object->volume->cache;
	struct cachefiles_req *req;
	unsigned long id;
	XA_STATE(xas, &cache->reqs, id);

	if (ioctl != CACHEFILES_IOC_READ_COMPLETE)
		return -EINVAL;
@@ -91,10 +110,15 @@ static long cachefiles_ondemand_fd_ioctl(struct file *filp, unsigned int ioctl,
	if (!test_bit(CACHEFILES_ONDEMAND_MODE, &cache->flags))
		return -EOPNOTSUPP;

	id = arg;
	req = xa_erase(&cache->reqs, id);
	if (!req)
	xa_lock(&cache->reqs);
	req = xas_load(&xas);
	if (!req || req->msg.opcode != CACHEFILES_OP_READ ||
	    req->object != object) {
		xa_unlock(&cache->reqs);
		return -EINVAL;
	}
	xas_store(&xas, NULL);
	xa_unlock(&cache->reqs);

	trace_cachefiles_ondemand_cread(object, id);
	complete(&req->done);
@@ -118,10 +142,12 @@ int cachefiles_ondemand_copen(struct cachefiles_cache *cache, char *args)
{
	struct cachefiles_req *req;
	struct fscache_cookie *cookie;
	struct cachefiles_ondemand_info *info;
	char *pid, *psize;
	unsigned long id;
	long size;
	int ret;
	XA_STATE(xas, &cache->reqs, 0);

	if (!test_bit(CACHEFILES_ONDEMAND_MODE, &cache->flags))
		return -EOPNOTSUPP;
@@ -145,10 +171,18 @@ int cachefiles_ondemand_copen(struct cachefiles_cache *cache, char *args)
	if (ret)
		return ret;

	req = xa_erase(&cache->reqs, id);
	if (!req)
	xa_lock(&cache->reqs);
	xas.xa_index = id;
	req = xas_load(&xas);
	if (!req || req->msg.opcode != CACHEFILES_OP_OPEN ||
	    !req->object->ondemand->ondemand_id) {
		xa_unlock(&cache->reqs);
		return -EINVAL;
	}
	xas_store(&xas, NULL);
	xa_unlock(&cache->reqs);

	info = req->object->ondemand;
	/* fail OPEN request if copen format is invalid */
	ret = kstrtol(psize, 0, &size);
	if (ret) {
@@ -168,6 +202,32 @@ int cachefiles_ondemand_copen(struct cachefiles_cache *cache, char *args)
		goto out;
	}

	spin_lock(&info->lock);
	/*
	 * The anonymous fd was closed before copen ? Fail the request.
	 *
	 *             t1             |             t2
	 * ---------------------------------------------------------
	 *                             cachefiles_ondemand_copen
	 *                             req = xa_erase(&cache->reqs, id)
	 * // Anon fd is maliciously closed.
	 * cachefiles_ondemand_fd_release
	 * xa_lock(&cache->reqs)
	 * cachefiles_ondemand_set_object_close(object)
	 * xa_unlock(&cache->reqs)
	 *                             cachefiles_ondemand_set_object_open
	 *                             // No one will ever close it again.
	 * cachefiles_ondemand_daemon_read
	 * cachefiles_ondemand_select_req
	 *
	 * Get a read req but its fd is already closed. The daemon can't
	 * issue a cread ioctl with an closed fd, then hung.
	 */
	if (info->ondemand_id == CACHEFILES_ONDEMAND_ID_CLOSED) {
		spin_unlock(&info->lock);
		req->error = -EBADFD;
		goto out;
	}
	cookie = req->object->cookie;
	cookie->object_size = size;
	if (size)
@@ -176,19 +236,51 @@ int cachefiles_ondemand_copen(struct cachefiles_cache *cache, char *args)
		set_bit(FSCACHE_COOKIE_NO_DATA_TO_READ, &cookie->flags);
	trace_cachefiles_ondemand_copen(req->object, id, size);

	cachefiles_ondemand_set_object_open(req->object);
	spin_unlock(&info->lock);
	wake_up_all(&cache->daemon_pollwq);

out:
	spin_lock(&info->lock);
	/* Need to set object close to avoid reopen status continuing */
	if (info->ondemand_id == CACHEFILES_ONDEMAND_ID_CLOSED)
		cachefiles_ondemand_set_object_close(req->object);
	spin_unlock(&info->lock);
	complete(&req->done);
	return ret;
}

static int cachefiles_ondemand_get_fd(struct cachefiles_req *req)
int cachefiles_ondemand_restore(struct cachefiles_cache *cache, char *args)
{
	struct cachefiles_req *req;

	XA_STATE(xas, &cache->reqs, 0);

	if (!test_bit(CACHEFILES_ONDEMAND_MODE, &cache->flags))
		return -EOPNOTSUPP;

	/*
	 * Reset the requests to CACHEFILES_REQ_NEW state, so that the
	 * requests have been processed halfway before the crash of the
	 * user daemon could be reprocessed after the recovery.
	 */
	xas_lock(&xas);
	xas_for_each(&xas, req, ULONG_MAX)
		xas_set_mark(&xas, CACHEFILES_REQ_NEW);
	xas_unlock(&xas);

	wake_up_all(&cache->daemon_pollwq);
	return 0;
}

static int cachefiles_ondemand_get_fd(struct cachefiles_req *req,
				      struct ondemand_anon_file *anon_file)
{
	struct cachefiles_object *object;
	struct cachefiles_cache *cache;
	struct cachefiles_open *load;
	struct file *file;
	u32 object_id;
	int ret, fd;
	int ret;

	object = cachefiles_grab_object(req->object,
			cachefiles_obj_get_ondemand_fd);
@@ -200,60 +292,128 @@ static int cachefiles_ondemand_get_fd(struct cachefiles_req *req)
	if (ret < 0)
		goto err;

	fd = get_unused_fd_flags(O_WRONLY);
	if (fd < 0) {
		ret = fd;
	anon_file->fd = get_unused_fd_flags(O_WRONLY);
	if (anon_file->fd < 0) {
		ret = anon_file->fd;
		goto err_free_id;
	}

	file = anon_inode_getfile("[cachefiles]", &cachefiles_ondemand_fd_fops,
				  object, O_WRONLY);
	if (IS_ERR(file)) {
		ret = PTR_ERR(file);
	anon_file->file = anon_inode_getfile("[cachefiles]",
				&cachefiles_ondemand_fd_fops, object, O_WRONLY);
	if (IS_ERR(anon_file->file)) {
		ret = PTR_ERR(anon_file->file);
		goto err_put_fd;
	}

	file->f_mode |= FMODE_PWRITE | FMODE_LSEEK;
	fd_install(fd, file);
	spin_lock(&object->ondemand->lock);
	if (object->ondemand->ondemand_id > 0) {
		spin_unlock(&object->ondemand->lock);
		/* Pair with check in cachefiles_ondemand_fd_release(). */
		anon_file->file->private_data = NULL;
		ret = -EEXIST;
		goto err_put_file;
	}

	anon_file->file->f_mode |= FMODE_PWRITE | FMODE_LSEEK;

	load = (void *)req->msg.data;
	load->fd = fd;
	req->msg.object_id = object_id;
	object->ondemand_id = object_id;
	load->fd = anon_file->fd;
	object->ondemand->ondemand_id = object_id;
	spin_unlock(&object->ondemand->lock);

	cachefiles_get_unbind_pincount(cache);
	trace_cachefiles_ondemand_open(object, &req->msg, load);
	return 0;

err_put_file:
	fput(anon_file->file);
	anon_file->file = NULL;
err_put_fd:
	put_unused_fd(fd);
	put_unused_fd(anon_file->fd);
	anon_file->fd = ret;
err_free_id:
	xa_erase(&cache->ondemand_ids, object_id);
err:
	spin_lock(&object->ondemand->lock);
	/* Avoid marking an opened object as closed. */
	if (object->ondemand->ondemand_id <= 0)
		cachefiles_ondemand_set_object_close(object);
	spin_unlock(&object->ondemand->lock);
	cachefiles_put_object(object, cachefiles_obj_put_ondemand_fd);
	return ret;
}

static void ondemand_object_worker(struct work_struct *work)
{
	struct cachefiles_ondemand_info *info =
		container_of(work, struct cachefiles_ondemand_info, ondemand_work);

	cachefiles_ondemand_init_object(info->object);
}

/*
 * If there are any inflight or subsequent READ requests on the
 * closed object, reopen it.
 * Skip read requests whose related object is reopening.
 */
static struct cachefiles_req *cachefiles_ondemand_select_req(struct xa_state *xas,
							      unsigned long xa_max)
{
	struct cachefiles_req *req;
	struct cachefiles_object *object;
	struct cachefiles_ondemand_info *info;

	xas_for_each_marked(xas, req, xa_max, CACHEFILES_REQ_NEW) {
		if (req->msg.opcode != CACHEFILES_OP_READ)
			return req;
		object = req->object;
		info = object->ondemand;
		if (cachefiles_ondemand_object_is_close(object)) {
			cachefiles_ondemand_set_object_reopening(object);
			queue_work(fscache_wq, &info->ondemand_work);
			continue;
		}
		if (cachefiles_ondemand_object_is_reopening(object))
			continue;
		return req;
	}
	return NULL;
}

static inline bool cachefiles_ondemand_finish_req(struct cachefiles_req *req,
						  struct xa_state *xas, int err)
{
	if (unlikely(!xas || !req))
		return false;

	if (xa_cmpxchg(xas->xa, xas->xa_index, req, NULL, 0) != req)
		return false;

	req->error = err;
	complete(&req->done);
	return true;
}

ssize_t cachefiles_ondemand_daemon_read(struct cachefiles_cache *cache,
					char __user *_buffer, size_t buflen)
{
	struct cachefiles_req *req;
	struct cachefiles_msg *msg;
	unsigned long id = 0;
	size_t n;
	int ret = 0;
	struct ondemand_anon_file anon_file;
	XA_STATE(xas, &cache->reqs, cache->req_id_next);

	xa_lock(&cache->reqs);
	/*
	 * 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);
	req = xas_find_marked(&xas, UINT_MAX, CACHEFILES_REQ_NEW);
	req = cachefiles_ondemand_select_req(&xas, ULONG_MAX);
	if (!req && cache->req_id_next > 0) {
		xas_set(&xas, 0);
		req = xas_find_marked(&xas, cache->req_id_next - 1, CACHEFILES_REQ_NEW);
		req = cachefiles_ondemand_select_req(&xas, cache->req_id_next - 1);
	}
	if (!req) {
		xa_unlock(&cache->reqs);
@@ -270,38 +430,37 @@ ssize_t cachefiles_ondemand_daemon_read(struct cachefiles_cache *cache,

	xas_clear_mark(&xas, CACHEFILES_REQ_NEW);
	cache->req_id_next = xas.xa_index + 1;
	refcount_inc(&req->ref);
	cachefiles_grab_object(req->object, cachefiles_obj_get_read_req);
	xa_unlock(&cache->reqs);

	id = xas.xa_index;
	msg->msg_id = id;

	if (msg->opcode == CACHEFILES_OP_OPEN) {
		ret = cachefiles_ondemand_get_fd(req);
		ret = cachefiles_ondemand_get_fd(req, &anon_file);
		if (ret)
			goto error;
			goto out;
	}

	if (copy_to_user(_buffer, msg, n) != 0) {
	msg->msg_id = xas.xa_index;
	msg->object_id = req->object->ondemand->ondemand_id;

	if (copy_to_user(_buffer, msg, n) != 0)
		ret = -EFAULT;
		goto err_put_fd;
	}

	/* CLOSE request has no reply */
	if (msg->opcode == CACHEFILES_OP_CLOSE) {
		xa_erase(&cache->reqs, id);
		complete(&req->done);
	if (msg->opcode == CACHEFILES_OP_OPEN) {
		if (ret < 0) {
			fput(anon_file.file);
			put_unused_fd(anon_file.fd);
			goto out;
		}

	return n;

err_put_fd:
	if (msg->opcode == CACHEFILES_OP_OPEN)
		close_fd(((struct cachefiles_open *)msg->data)->fd);
error:
	xa_erase(&cache->reqs, id);
	req->error = ret;
	complete(&req->done);
	return ret;
		fd_install(anon_file.fd, anon_file.file);
	}
out:
	cachefiles_put_object(req->object, cachefiles_obj_put_read_req);
	/* Remove error request and CLOSE request has no reply */
	if (ret || msg->opcode == CACHEFILES_OP_CLOSE)
		cachefiles_ondemand_finish_req(req, &xas, ret);
	cachefiles_req_put(req);
	return ret ? ret : n;
}

typedef int (*init_req_fn)(struct cachefiles_req *req, void *private);
@@ -313,20 +472,25 @@ static int cachefiles_ondemand_send_req(struct cachefiles_object *object,
					void *private)
{
	struct cachefiles_cache *cache = object->volume->cache;
	struct cachefiles_req *req;
	struct cachefiles_req *req = NULL;
	XA_STATE(xas, &cache->reqs, 0);
	int ret;

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

	if (test_bit(CACHEFILES_DEAD, &cache->flags))
		return -EIO;
	if (test_bit(CACHEFILES_DEAD, &cache->flags)) {
		ret = -EIO;
		goto out;
	}

	req = kzalloc(sizeof(*req) + data_len, GFP_KERNEL);
	if (!req)
		return -ENOMEM;
	if (!req) {
		ret = -ENOMEM;
		goto out;
	}

	refcount_set(&req->ref, 1);
	req->object = object;
	init_completion(&req->done);
	req->msg.opcode = opcode;
@@ -363,8 +527,9 @@ static int cachefiles_ondemand_send_req(struct cachefiles_object *object,
		/* coupled with the barrier in cachefiles_flush_reqs() */
		smp_mb();

		if (opcode != CACHEFILES_OP_OPEN && object->ondemand_id <= 0) {
			WARN_ON_ONCE(object->ondemand_id == 0);
		if (opcode == CACHEFILES_OP_CLOSE &&
			!cachefiles_ondemand_object_is_open(object)) {
			WARN_ON_ONCE(object->ondemand->ondemand_id == 0);
			xas_unlock(&xas);
			ret = -EIO;
			goto out;
@@ -385,9 +550,27 @@ static int cachefiles_ondemand_send_req(struct cachefiles_object *object,
		goto out;

	wake_up_all(&cache->daemon_pollwq);
	wait_for_completion(&req->done);
wait:
	ret = wait_for_completion_killable(&req->done);
	if (!ret) {
		ret = req->error;
	} else {
		ret = -EINTR;
		if (!cachefiles_ondemand_finish_req(req, &xas, ret)) {
			/* Someone will complete it soon. */
			cpu_relax();
			goto wait;
		}
	}
	cachefiles_req_put(req);
	return ret;
out:
	/* Reset the object to close state in error handling path.
	 * If error occurs after creating the anonymous fd,
	 * cachefiles_ondemand_fd_release() will set object to close.
	 */
	if (opcode == CACHEFILES_OP_OPEN)
		cachefiles_ondemand_set_object_close(object);
	kfree(req);
	return ret;
}
@@ -430,18 +613,10 @@ static int cachefiles_ondemand_init_close_req(struct cachefiles_req *req,
					      void *private)
{
	struct cachefiles_object *object = req->object;
	int object_id = object->ondemand_id;

	/*
	 * It's possible that object id is still 0 if the cookie looking up
	 * phase failed before OPEN request has ever been sent. Also avoid
	 * sending CLOSE request for CACHEFILES_ONDEMAND_ID_CLOSED, which means
	 * anon_fd has already been closed.
	 */
	if (object_id <= 0)
	if (!cachefiles_ondemand_object_is_open(object))
		return -ENOENT;

	req->msg.object_id = object_id;
	trace_cachefiles_ondemand_close(object, &req->msg);
	return 0;
}
@@ -457,16 +632,7 @@ static int cachefiles_ondemand_init_read_req(struct cachefiles_req *req,
	struct cachefiles_object *object = req->object;
	struct cachefiles_read *load = (void *)req->msg.data;
	struct cachefiles_read_ctx *read_ctx = private;
	int object_id = object->ondemand_id;

	/* Stop enqueuing requests when daemon has closed anon_fd. */
	if (object_id <= 0) {
		WARN_ON_ONCE(object_id == 0);
		pr_info_once("READ: anonymous fd closed prematurely.\n");
		return -EIO;
	}

	req->msg.object_id = object_id;
	load->off = read_ctx->off;
	load->len = read_ctx->len;
	trace_cachefiles_ondemand_read(object, &req->msg, load);
@@ -479,13 +645,16 @@ int cachefiles_ondemand_init_object(struct cachefiles_object *object)
	struct fscache_volume *volume = object->volume->vcookie;
	size_t volume_key_size, cookie_key_size, data_len;

	if (!object->ondemand)
		return 0;

	/*
	 * CacheFiles will firstly check the cache file under the root cache
	 * directory. If the coherency check failed, it will fallback to
	 * creating a new tmpfile as the cache file. Reuse the previously
	 * allocated object ID if any.
	 */
	if (object->ondemand_id > 0)
	if (cachefiles_ondemand_object_is_open(object))
		return 0;

	volume_key_size = volume->key[0] + 1;
@@ -503,6 +672,29 @@ void cachefiles_ondemand_clean_object(struct cachefiles_object *object)
			cachefiles_ondemand_init_close_req, NULL);
}

int cachefiles_ondemand_init_obj_info(struct cachefiles_object *object,
				struct cachefiles_volume *volume)
{
	if (!cachefiles_in_ondemand_mode(volume->cache))
		return 0;

	object->ondemand = kzalloc(sizeof(struct cachefiles_ondemand_info),
					GFP_KERNEL);
	if (!object->ondemand)
		return -ENOMEM;

	object->ondemand->object = object;
	spin_lock_init(&object->ondemand->lock);
	INIT_WORK(&object->ondemand->ondemand_work, ondemand_object_worker);
	return 0;
}

void cachefiles_ondemand_deinit_obj_info(struct cachefiles_object *object)
{
	kfree(object->ondemand);
	object->ondemand = NULL;
}

int cachefiles_ondemand_read(struct cachefiles_object *object,
			     loff_t pos, size_t len)
{
+7 −1
Original line number Diff line number Diff line
@@ -33,6 +33,8 @@ enum cachefiles_obj_ref_trace {
	cachefiles_obj_see_withdrawal,
	cachefiles_obj_get_ondemand_fd,
	cachefiles_obj_put_ondemand_fd,
	cachefiles_obj_get_read_req,
	cachefiles_obj_put_read_req,
};

enum fscache_why_object_killed {
@@ -127,7 +129,11 @@ enum cachefiles_error_trace {
	EM(cachefiles_obj_see_lookup_cookie,	"SEE lookup_cookie")	\
	EM(cachefiles_obj_see_lookup_failed,	"SEE lookup_failed")	\
	EM(cachefiles_obj_see_withdraw_cookie,	"SEE withdraw_cookie")	\
	E_(cachefiles_obj_see_withdrawal,	"SEE withdrawal")
	EM(cachefiles_obj_see_withdrawal,	"SEE withdrawal")	\
	EM(cachefiles_obj_get_ondemand_fd,      "GET ondemand_fd")	\
	EM(cachefiles_obj_put_ondemand_fd,      "PUT ondemand_fd")	\
	EM(cachefiles_obj_get_read_req,		"GET read_req")		\
	E_(cachefiles_obj_put_read_req,		"PUT read_req")

#define cachefiles_coherency_traces					\
	EM(cachefiles_coherency_check_aux,	"BAD aux ")		\