mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
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: Luis Henriques <luis@igalia.com> Tested-by: Laura Promberger <laura.promberger@cern.ch> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
This commit is contained in:
committed by
Miklos Szeredi
parent
faa794dd2e
commit
2396356a94
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 @@ retry:
|
||||
}
|
||||
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);
|
||||
|
||||
@@ -232,6 +232,9 @@
|
||||
*
|
||||
* 7.43
|
||||
* - add FUSE_REQUEST_TIMEOUT
|
||||
*
|
||||
* 7.44
|
||||
* - add FUSE_NOTIFY_INC_EPOCH
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_FUSE_H
|
||||
@@ -267,7 +270,7 @@
|
||||
#define FUSE_KERNEL_VERSION 7
|
||||
|
||||
/** Minor version number of this interface */
|
||||
#define FUSE_KERNEL_MINOR_VERSION 43
|
||||
#define FUSE_KERNEL_MINOR_VERSION 44
|
||||
|
||||
/** The node ID of the root inode */
|
||||
#define FUSE_ROOT_ID 1
|
||||
@@ -671,6 +674,7 @@ enum fuse_notify_code {
|
||||
FUSE_NOTIFY_RETRIEVE = 5,
|
||||
FUSE_NOTIFY_DELETE = 6,
|
||||
FUSE_NOTIFY_RESEND = 7,
|
||||
FUSE_NOTIFY_INC_EPOCH = 8,
|
||||
FUSE_NOTIFY_CODE_MAX,
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user