Commit 3b463ae0c6264f70e5d4c0a9c46af20fed43c96e
Committed by
Miklos Szeredi
1 parent
e0a43ddcc0
Exists in
master
and in
7 other branches
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 <csaba@gluster.com> Cc: Anand Avati <avati@zresearch.com> Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Showing 5 changed files with 214 additions and 3 deletions Side-by-side Diff
fs/fuse/dev.c
... | ... | @@ -849,12 +849,93 @@ |
849 | 849 | return err; |
850 | 850 | } |
851 | 851 | |
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 | + | |
852 | 927 | static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code, |
853 | 928 | unsigned int size, struct fuse_copy_state *cs) |
854 | 929 | { |
855 | 930 | switch (code) { |
856 | 931 | case FUSE_NOTIFY_POLL: |
857 | 932 | return fuse_notify_poll(fc, size, cs); |
933 | + | |
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); | |
858 | 939 | |
859 | 940 | default: |
860 | 941 | fuse_copy_finish(cs); |
fs/fuse/dir.c
... | ... | @@ -859,6 +859,43 @@ |
859 | 859 | return err; |
860 | 860 | } |
861 | 861 | |
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 | + | |
862 | 899 | /* |
863 | 900 | * Calling into a user-controlled filesystem gives the filesystem |
864 | 901 | * daemon ptrace-like capabilities over the requester process. This |
fs/fuse/fuse_i.h
... | ... | @@ -484,6 +484,12 @@ |
484 | 484 | |
485 | 485 | /** Called on final put */ |
486 | 486 | 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; | |
487 | 493 | }; |
488 | 494 | |
489 | 495 | static inline struct fuse_conn *get_fuse_conn_super(struct super_block *sb) |
... | ... | @@ -512,6 +518,11 @@ |
512 | 518 | extern const struct dentry_operations fuse_dentry_operations; |
513 | 519 | |
514 | 520 | /** |
521 | + * Inode to nodeid comparison. | |
522 | + */ | |
523 | +int fuse_inode_eq(struct inode *inode, void *_nodeidp); | |
524 | + | |
525 | +/** | |
515 | 526 | * Get a filled in inode |
516 | 527 | */ |
517 | 528 | struct inode *fuse_iget(struct super_block *sb, u64 nodeid, |
... | ... | @@ -710,6 +721,19 @@ |
710 | 721 | void fuse_release_nowrite(struct inode *inode); |
711 | 722 | |
712 | 723 | u64 fuse_get_attr_version(struct fuse_conn *fc); |
724 | + | |
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); | |
713 | 737 | |
714 | 738 | int fuse_do_open(struct fuse_conn *fc, u64 nodeid, struct file *file, |
715 | 739 | bool isdir); |
fs/fuse/inode.c
... | ... | @@ -206,7 +206,7 @@ |
206 | 206 | BUG(); |
207 | 207 | } |
208 | 208 | |
209 | -static int fuse_inode_eq(struct inode *inode, void *_nodeidp) | |
209 | +int fuse_inode_eq(struct inode *inode, void *_nodeidp) | |
210 | 210 | { |
211 | 211 | u64 nodeid = *(u64 *) _nodeidp; |
212 | 212 | if (get_node_id(inode) == nodeid) |
... | ... | @@ -257,6 +257,31 @@ |
257 | 257 | return inode; |
258 | 258 | } |
259 | 259 | |
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 | + | |
260 | 285 | static void fuse_umount_begin(struct super_block *sb) |
261 | 286 | { |
262 | 287 | fuse_abort_conn(get_fuse_conn_super(sb)); |
... | ... | @@ -480,6 +505,7 @@ |
480 | 505 | memset(fc, 0, sizeof(*fc)); |
481 | 506 | spin_lock_init(&fc->lock); |
482 | 507 | mutex_init(&fc->inst_mutex); |
508 | + init_rwsem(&fc->killsb); | |
483 | 509 | atomic_set(&fc->count, 1); |
484 | 510 | init_waitqueue_head(&fc->waitq); |
485 | 511 | init_waitqueue_head(&fc->blocked_waitq); |
... | ... | @@ -862,6 +888,7 @@ |
862 | 888 | fuse_conn_init(fc); |
863 | 889 | |
864 | 890 | fc->dev = sb->s_dev; |
891 | + fc->sb = sb; | |
865 | 892 | err = fuse_bdi_init(fc, sb); |
866 | 893 | if (err) |
867 | 894 | goto err_put_conn; |
868 | 895 | |
... | ... | @@ -948,12 +975,25 @@ |
948 | 975 | return get_sb_nodev(fs_type, flags, raw_data, fuse_fill_super, mnt); |
949 | 976 | } |
950 | 977 | |
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 | + | |
951 | 991 | static struct file_system_type fuse_fs_type = { |
952 | 992 | .owner = THIS_MODULE, |
953 | 993 | .name = "fuse", |
954 | 994 | .fs_flags = FS_HAS_SUBTYPE, |
955 | 995 | .get_sb = fuse_get_sb, |
956 | - .kill_sb = kill_anon_super, | |
996 | + .kill_sb = fuse_kill_sb_anon, | |
957 | 997 | }; |
958 | 998 | |
959 | 999 | #ifdef CONFIG_BLOCK |
960 | 1000 | |
... | ... | @@ -965,11 +1005,24 @@ |
965 | 1005 | mnt); |
966 | 1006 | } |
967 | 1007 | |
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 | + | |
968 | 1021 | static struct file_system_type fuseblk_fs_type = { |
969 | 1022 | .owner = THIS_MODULE, |
970 | 1023 | .name = "fuseblk", |
971 | 1024 | .get_sb = fuse_get_sb_blk, |
972 | - .kill_sb = kill_block_super, | |
1025 | + .kill_sb = fuse_kill_sb_blk, | |
973 | 1026 | .fs_flags = FS_REQUIRES_DEV | FS_HAS_SUBTYPE, |
974 | 1027 | }; |
975 | 1028 |
include/linux/fuse.h
... | ... | @@ -28,6 +28,8 @@ |
28 | 28 | * |
29 | 29 | * 7.12 |
30 | 30 | * - add umask flag to input argument of open, mknod and mkdir |
31 | + * - add notification messages for invalidation of inodes and | |
32 | + * directory entries | |
31 | 33 | */ |
32 | 34 | |
33 | 35 | #ifndef _LINUX_FUSE_H |
... | ... | @@ -229,6 +231,8 @@ |
229 | 231 | |
230 | 232 | enum fuse_notify_code { |
231 | 233 | FUSE_NOTIFY_POLL = 1, |
234 | + FUSE_NOTIFY_INVAL_INODE = 2, | |
235 | + FUSE_NOTIFY_INVAL_ENTRY = 3, | |
232 | 236 | FUSE_NOTIFY_CODE_MAX, |
233 | 237 | }; |
234 | 238 | |
... | ... | @@ -523,6 +527,18 @@ |
523 | 527 | #define FUSE_DIRENT_ALIGN(x) (((x) + sizeof(__u64) - 1) & ~(sizeof(__u64) - 1)) |
524 | 528 | #define FUSE_DIRENT_SIZE(d) \ |
525 | 529 | FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen) |
530 | + | |
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 | +}; | |
526 | 542 | |
527 | 543 | #endif /* _LINUX_FUSE_H */ |