Skip to content

Commit 3b463ae

Browse files
John MuirMiklos Szeredi
authored andcommitted
fuse: invalidation reverse calls
Add notification messages that allow the filesystem to invalidate VFS caches. Two notifications are added: 1) inode invalidation - invalidate cached attributes - invalidate a range of pages in the page cache (this is optional) 2) dentry invalidation - try to invalidate a subtree in the dentry cache Care must be taken while accessing the 'struct super_block' for the mount, as it can go away while an invalidation is in progress. To prevent this, introduce a rw-semaphore, that is taken for read during the invalidation and taken for write in the ->kill_sb callback. Cc: Csaba Henk <[email protected]> Cc: Anand Avati <[email protected]> Signed-off-by: Miklos Szeredi <[email protected]>
1 parent e0a43dd commit 3b463ae

File tree

5 files changed

+214
-3
lines changed

5 files changed

+214
-3
lines changed

fs/fuse/dev.c

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -849,13 +849,94 @@ static int fuse_notify_poll(struct fuse_conn *fc, unsigned int size,
849849
return err;
850850
}
851851

852+
static int fuse_notify_inval_inode(struct fuse_conn *fc, unsigned int size,
853+
struct fuse_copy_state *cs)
854+
{
855+
struct fuse_notify_inval_inode_out outarg;
856+
int err = -EINVAL;
857+
858+
if (size != sizeof(outarg))
859+
goto err;
860+
861+
err = fuse_copy_one(cs, &outarg, sizeof(outarg));
862+
if (err)
863+
goto err;
864+
fuse_copy_finish(cs);
865+
866+
down_read(&fc->killsb);
867+
err = -ENOENT;
868+
if (!fc->sb)
869+
goto err_unlock;
870+
871+
err = fuse_reverse_inval_inode(fc->sb, outarg.ino,
872+
outarg.off, outarg.len);
873+
874+
err_unlock:
875+
up_read(&fc->killsb);
876+
return err;
877+
878+
err:
879+
fuse_copy_finish(cs);
880+
return err;
881+
}
882+
883+
static int fuse_notify_inval_entry(struct fuse_conn *fc, unsigned int size,
884+
struct fuse_copy_state *cs)
885+
{
886+
struct fuse_notify_inval_entry_out outarg;
887+
int err = -EINVAL;
888+
char buf[FUSE_NAME_MAX+1];
889+
struct qstr name;
890+
891+
if (size < sizeof(outarg))
892+
goto err;
893+
894+
err = fuse_copy_one(cs, &outarg, sizeof(outarg));
895+
if (err)
896+
goto err;
897+
898+
err = -ENAMETOOLONG;
899+
if (outarg.namelen > FUSE_NAME_MAX)
900+
goto err;
901+
902+
name.name = buf;
903+
name.len = outarg.namelen;
904+
err = fuse_copy_one(cs, buf, outarg.namelen + 1);
905+
if (err)
906+
goto err;
907+
fuse_copy_finish(cs);
908+
buf[outarg.namelen] = 0;
909+
name.hash = full_name_hash(name.name, name.len);
910+
911+
down_read(&fc->killsb);
912+
err = -ENOENT;
913+
if (!fc->sb)
914+
goto err_unlock;
915+
916+
err = fuse_reverse_inval_entry(fc->sb, outarg.parent, &name);
917+
918+
err_unlock:
919+
up_read(&fc->killsb);
920+
return err;
921+
922+
err:
923+
fuse_copy_finish(cs);
924+
return err;
925+
}
926+
852927
static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code,
853928
unsigned int size, struct fuse_copy_state *cs)
854929
{
855930
switch (code) {
856931
case FUSE_NOTIFY_POLL:
857932
return fuse_notify_poll(fc, size, cs);
858933

934+
case FUSE_NOTIFY_INVAL_INODE:
935+
return fuse_notify_inval_inode(fc, size, cs);
936+
937+
case FUSE_NOTIFY_INVAL_ENTRY:
938+
return fuse_notify_inval_entry(fc, size, cs);
939+
859940
default:
860941
fuse_copy_finish(cs);
861942
return -EINVAL;

fs/fuse/dir.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -859,6 +859,43 @@ int fuse_update_attributes(struct inode *inode, struct kstat *stat,
859859
return err;
860860
}
861861

862+
int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid,
863+
struct qstr *name)
864+
{
865+
int err = -ENOTDIR;
866+
struct inode *parent;
867+
struct dentry *dir;
868+
struct dentry *entry;
869+
870+
parent = ilookup5(sb, parent_nodeid, fuse_inode_eq, &parent_nodeid);
871+
if (!parent)
872+
return -ENOENT;
873+
874+
mutex_lock(&parent->i_mutex);
875+
if (!S_ISDIR(parent->i_mode))
876+
goto unlock;
877+
878+
err = -ENOENT;
879+
dir = d_find_alias(parent);
880+
if (!dir)
881+
goto unlock;
882+
883+
entry = d_lookup(dir, name);
884+
dput(dir);
885+
if (!entry)
886+
goto unlock;
887+
888+
fuse_invalidate_attr(parent);
889+
fuse_invalidate_entry(entry);
890+
dput(entry);
891+
err = 0;
892+
893+
unlock:
894+
mutex_unlock(&parent->i_mutex);
895+
iput(parent);
896+
return err;
897+
}
898+
862899
/*
863900
* Calling into a user-controlled filesystem gives the filesystem
864901
* daemon ptrace-like capabilities over the requester process. This

fs/fuse/fuse_i.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,12 @@ struct fuse_conn {
484484

485485
/** Called on final put */
486486
void (*release)(struct fuse_conn *);
487+
488+
/** Super block for this connection. */
489+
struct super_block *sb;
490+
491+
/** Read/write semaphore to hold when accessing sb. */
492+
struct rw_semaphore killsb;
487493
};
488494

489495
static inline struct fuse_conn *get_fuse_conn_super(struct super_block *sb)
@@ -511,6 +517,11 @@ extern const struct file_operations fuse_dev_operations;
511517

512518
extern const struct dentry_operations fuse_dentry_operations;
513519

520+
/**
521+
* Inode to nodeid comparison.
522+
*/
523+
int fuse_inode_eq(struct inode *inode, void *_nodeidp);
524+
514525
/**
515526
* Get a filled in inode
516527
*/
@@ -711,6 +722,19 @@ void fuse_release_nowrite(struct inode *inode);
711722

712723
u64 fuse_get_attr_version(struct fuse_conn *fc);
713724

725+
/**
726+
* File-system tells the kernel to invalidate cache for the given node id.
727+
*/
728+
int fuse_reverse_inval_inode(struct super_block *sb, u64 nodeid,
729+
loff_t offset, loff_t len);
730+
731+
/**
732+
* File-system tells the kernel to invalidate parent attributes and
733+
* the dentry matching parent/name.
734+
*/
735+
int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid,
736+
struct qstr *name);
737+
714738
int fuse_do_open(struct fuse_conn *fc, u64 nodeid, struct file *file,
715739
bool isdir);
716740
ssize_t fuse_direct_io(struct file *file, const char __user *buf,

fs/fuse/inode.c

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr)
206206
BUG();
207207
}
208208

209-
static int fuse_inode_eq(struct inode *inode, void *_nodeidp)
209+
int fuse_inode_eq(struct inode *inode, void *_nodeidp)
210210
{
211211
u64 nodeid = *(u64 *) _nodeidp;
212212
if (get_node_id(inode) == nodeid)
@@ -257,6 +257,31 @@ struct inode *fuse_iget(struct super_block *sb, u64 nodeid,
257257
return inode;
258258
}
259259

260+
int fuse_reverse_inval_inode(struct super_block *sb, u64 nodeid,
261+
loff_t offset, loff_t len)
262+
{
263+
struct inode *inode;
264+
pgoff_t pg_start;
265+
pgoff_t pg_end;
266+
267+
inode = ilookup5(sb, nodeid, fuse_inode_eq, &nodeid);
268+
if (!inode)
269+
return -ENOENT;
270+
271+
fuse_invalidate_attr(inode);
272+
if (offset >= 0) {
273+
pg_start = offset >> PAGE_CACHE_SHIFT;
274+
if (len <= 0)
275+
pg_end = -1;
276+
else
277+
pg_end = (offset + len - 1) >> PAGE_CACHE_SHIFT;
278+
invalidate_inode_pages2_range(inode->i_mapping,
279+
pg_start, pg_end);
280+
}
281+
iput(inode);
282+
return 0;
283+
}
284+
260285
static void fuse_umount_begin(struct super_block *sb)
261286
{
262287
fuse_abort_conn(get_fuse_conn_super(sb));
@@ -480,6 +505,7 @@ void fuse_conn_init(struct fuse_conn *fc)
480505
memset(fc, 0, sizeof(*fc));
481506
spin_lock_init(&fc->lock);
482507
mutex_init(&fc->inst_mutex);
508+
init_rwsem(&fc->killsb);
483509
atomic_set(&fc->count, 1);
484510
init_waitqueue_head(&fc->waitq);
485511
init_waitqueue_head(&fc->blocked_waitq);
@@ -862,6 +888,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
862888
fuse_conn_init(fc);
863889

864890
fc->dev = sb->s_dev;
891+
fc->sb = sb;
865892
err = fuse_bdi_init(fc, sb);
866893
if (err)
867894
goto err_put_conn;
@@ -948,12 +975,25 @@ static int fuse_get_sb(struct file_system_type *fs_type,
948975
return get_sb_nodev(fs_type, flags, raw_data, fuse_fill_super, mnt);
949976
}
950977

978+
static void fuse_kill_sb_anon(struct super_block *sb)
979+
{
980+
struct fuse_conn *fc = get_fuse_conn_super(sb);
981+
982+
if (fc) {
983+
down_write(&fc->killsb);
984+
fc->sb = NULL;
985+
up_write(&fc->killsb);
986+
}
987+
988+
kill_anon_super(sb);
989+
}
990+
951991
static struct file_system_type fuse_fs_type = {
952992
.owner = THIS_MODULE,
953993
.name = "fuse",
954994
.fs_flags = FS_HAS_SUBTYPE,
955995
.get_sb = fuse_get_sb,
956-
.kill_sb = kill_anon_super,
996+
.kill_sb = fuse_kill_sb_anon,
957997
};
958998

959999
#ifdef CONFIG_BLOCK
@@ -965,11 +1005,24 @@ static int fuse_get_sb_blk(struct file_system_type *fs_type,
9651005
mnt);
9661006
}
9671007

1008+
static void fuse_kill_sb_blk(struct super_block *sb)
1009+
{
1010+
struct fuse_conn *fc = get_fuse_conn_super(sb);
1011+
1012+
if (fc) {
1013+
down_write(&fc->killsb);
1014+
fc->sb = NULL;
1015+
up_write(&fc->killsb);
1016+
}
1017+
1018+
kill_block_super(sb);
1019+
}
1020+
9681021
static struct file_system_type fuseblk_fs_type = {
9691022
.owner = THIS_MODULE,
9701023
.name = "fuseblk",
9711024
.get_sb = fuse_get_sb_blk,
972-
.kill_sb = kill_block_super,
1025+
.kill_sb = fuse_kill_sb_blk,
9731026
.fs_flags = FS_REQUIRES_DEV | FS_HAS_SUBTYPE,
9741027
};
9751028

include/linux/fuse.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
*
2929
* 7.12
3030
* - add umask flag to input argument of open, mknod and mkdir
31+
* - add notification messages for invalidation of inodes and
32+
* directory entries
3133
*/
3234

3335
#ifndef _LINUX_FUSE_H
@@ -229,6 +231,8 @@ enum fuse_opcode {
229231

230232
enum fuse_notify_code {
231233
FUSE_NOTIFY_POLL = 1,
234+
FUSE_NOTIFY_INVAL_INODE = 2,
235+
FUSE_NOTIFY_INVAL_ENTRY = 3,
232236
FUSE_NOTIFY_CODE_MAX,
233237
};
234238

@@ -524,4 +528,16 @@ struct fuse_dirent {
524528
#define FUSE_DIRENT_SIZE(d) \
525529
FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen)
526530

531+
struct fuse_notify_inval_inode_out {
532+
__u64 ino;
533+
__s64 off;
534+
__s64 len;
535+
};
536+
537+
struct fuse_notify_inval_entry_out {
538+
__u64 parent;
539+
__u32 namelen;
540+
__u32 padding;
541+
};
542+
527543
#endif /* _LINUX_FUSE_H */

0 commit comments

Comments
 (0)