Commit 2396356a authored by Luis Henriques's avatar Luis Henriques Committed by Miklos Szeredi
Browse files

fuse: add more control over cache invalidation behaviour



Currently userspace is able to notify the kernel to invalidate the cache
for an inode.  This means that, if all the inodes in a filesystem need to
be invalidated, then userspace needs to iterate through all of them and do
this kernel notification separately.

This patch adds the concept of 'epoch': each fuse connection will have the
current epoch initialized and every new dentry will have it's d_time set to
the current epoch value.  A new operation will then allow userspace to
increment the epoch value.  Every time a dentry is d_revalidate()'ed, it's
epoch is compared with the current connection epoch and invalidated if it's
value is different.

Signed-off-by: default avatarLuis Henriques <luis@igalia.com>
Tested-by: default avatarLaura Promberger <laura.promberger@cern.ch>
Signed-off-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
parent faa794dd
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -2021,6 +2021,19 @@ static int fuse_notify_resend(struct fuse_conn *fc)
	return 0;
}

/*
 * Increments the fuse connection epoch.  This will result of dentries from
 * previous epochs to be invalidated.
 *
 * XXX optimization: add call to shrink_dcache_sb()?
 */
static int fuse_notify_inc_epoch(struct fuse_conn *fc)
{
	atomic_inc(&fc->epoch);

	return 0;
}

static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code,
		       unsigned int size, struct fuse_copy_state *cs)
{
@@ -2049,6 +2062,9 @@ static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code,
	case FUSE_NOTIFY_RESEND:
		return fuse_notify_resend(fc);

	case FUSE_NOTIFY_INC_EPOCH:
		return fuse_notify_inc_epoch(fc);

	default:
		fuse_copy_finish(cs);
		return -EINVAL;
+22 −5
Original line number Diff line number Diff line
@@ -200,9 +200,14 @@ static int fuse_dentry_revalidate(struct inode *dir, const struct qstr *name,
{
	struct inode *inode;
	struct fuse_mount *fm;
	struct fuse_conn *fc;
	struct fuse_inode *fi;
	int ret;

	fc = get_fuse_conn_super(dir->i_sb);
	if (entry->d_time < atomic_read(&fc->epoch))
		goto invalid;

	inode = d_inode_rcu(entry);
	if (inode && fuse_is_bad(inode))
		goto invalid;
@@ -415,16 +420,20 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name
static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
				  unsigned int flags)
{
	int err;
	struct fuse_entry_out outarg;
	struct fuse_conn *fc;
	struct inode *inode;
	struct dentry *newent;
	int err, epoch;
	bool outarg_valid = true;
	bool locked;

	if (fuse_is_bad(dir))
		return ERR_PTR(-EIO);

	fc = get_fuse_conn_super(dir->i_sb);
	epoch = atomic_read(&fc->epoch);

	locked = fuse_lock_inode(dir);
	err = fuse_lookup_name(dir->i_sb, get_node_id(dir), &entry->d_name,
			       &outarg, &inode);
@@ -446,6 +455,7 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
		goto out_err;

	entry = newent ? newent : entry;
	entry->d_time = epoch;
	if (outarg_valid)
		fuse_change_entry_timeout(entry, &outarg);
	else
@@ -619,7 +629,6 @@ static int fuse_create_open(struct mnt_idmap *idmap, struct inode *dir,
			    struct dentry *entry, struct file *file,
			    unsigned int flags, umode_t mode, u32 opcode)
{
	int err;
	struct inode *inode;
	struct fuse_mount *fm = get_fuse_mount(dir);
	FUSE_ARGS(args);
@@ -629,11 +638,13 @@ static int fuse_create_open(struct mnt_idmap *idmap, struct inode *dir,
	struct fuse_entry_out outentry;
	struct fuse_inode *fi;
	struct fuse_file *ff;
	int epoch, err;
	bool trunc = flags & O_TRUNC;

	/* Userspace expects S_IFREG in create mode */
	BUG_ON((mode & S_IFMT) != S_IFREG);

	epoch = atomic_read(&fm->fc->epoch);
	forget = fuse_alloc_forget();
	err = -ENOMEM;
	if (!forget)
@@ -702,6 +713,7 @@ static int fuse_create_open(struct mnt_idmap *idmap, struct inode *dir,
	}
	kfree(forget);
	d_instantiate(entry, inode);
	entry->d_time = epoch;
	fuse_change_entry_timeout(entry, &outentry);
	fuse_dir_changed(dir);
	err = generic_file_open(inode, file);
@@ -788,12 +800,14 @@ static struct dentry *create_new_entry(struct mnt_idmap *idmap, struct fuse_moun
	struct fuse_entry_out outarg;
	struct inode *inode;
	struct dentry *d;
	int err;
	struct fuse_forget_link *forget;
	int epoch, err;

	if (fuse_is_bad(dir))
		return ERR_PTR(-EIO);

	epoch = atomic_read(&fm->fc->epoch);

	forget = fuse_alloc_forget();
	if (!forget)
		return ERR_PTR(-ENOMEM);
@@ -835,10 +849,13 @@ static struct dentry *create_new_entry(struct mnt_idmap *idmap, struct fuse_moun
	if (IS_ERR(d))
		return d;

	if (d)
	if (d) {
		d->d_time = epoch;
		fuse_change_entry_timeout(d, &outarg);
	else
	} else {
		entry->d_time = epoch;
		fuse_change_entry_timeout(entry, &outarg);
	}
	fuse_dir_changed(dir);
	return d;

+3 −0
Original line number Diff line number Diff line
@@ -636,6 +636,9 @@ struct fuse_conn {
	/** Number of fuse_dev's */
	atomic_t dev_count;

	/** Current epoch for up-to-date dentries */
	atomic_t epoch;

	struct rcu_head rcu;

	/** The user id for this mount */
+1 −0
Original line number Diff line number Diff line
@@ -962,6 +962,7 @@ void fuse_conn_init(struct fuse_conn *fc, struct fuse_mount *fm,
	init_rwsem(&fc->killsb);
	refcount_set(&fc->count, 1);
	atomic_set(&fc->dev_count, 1);
	atomic_set(&fc->epoch, 1);
	init_waitqueue_head(&fc->blocked_waitq);
	fuse_iqueue_init(&fc->iq, fiq_ops, fiq_priv);
	INIT_LIST_HEAD(&fc->bg_queue);
+3 −0
Original line number Diff line number Diff line
@@ -161,6 +161,7 @@ static int fuse_direntplus_link(struct file *file,
	struct fuse_conn *fc;
	struct inode *inode;
	DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
	int epoch;

	if (!o->nodeid) {
		/*
@@ -190,6 +191,7 @@ static int fuse_direntplus_link(struct file *file,
		return -EIO;

	fc = get_fuse_conn(dir);
	epoch = atomic_read(&fc->epoch);

	name.hash = full_name_hash(parent, name.name, name.len);
	dentry = d_lookup(parent, &name);
@@ -256,6 +258,7 @@ static int fuse_direntplus_link(struct file *file,
	}
	if (fc->readdirplus_auto)
		set_bit(FUSE_I_INIT_RDPLUS, &get_fuse_inode(inode)->state);
	dentry->d_time = epoch;
	fuse_change_entry_timeout(dentry, o);

	dput(dentry);
Loading