diff options
author | John Muir <muirj@nortel.com> | 2009-05-31 11:13:57 -0400 |
---|---|---|
committer | Miklos Szeredi <mszeredi@suse.cz> | 2009-06-30 14:12:24 -0400 |
commit | 3b463ae0c6264f70e5d4c0a9c46af20fed43c96e (patch) | |
tree | 03c6abdd0671d74452cbd1f6fc6d637363eb6389 /fs | |
parent | e0a43ddcc08c34dbd666d93600fd23914505f4aa (diff) |
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>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/fuse/dev.c | 81 | ||||
-rw-r--r-- | fs/fuse/dir.c | 37 | ||||
-rw-r--r-- | fs/fuse/fuse_i.h | 24 | ||||
-rw-r--r-- | fs/fuse/inode.c | 59 |
4 files changed, 198 insertions, 3 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 8a11a8c67c42..f58ecbc416c8 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c | |||
@@ -849,6 +849,81 @@ err: | |||
849 | return err; | 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 | static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code, | 927 | static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code, |
853 | unsigned int size, struct fuse_copy_state *cs) | 928 | unsigned int size, struct fuse_copy_state *cs) |
854 | { | 929 | { |
@@ -856,6 +931,12 @@ static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code, | |||
856 | case FUSE_NOTIFY_POLL: | 931 | case FUSE_NOTIFY_POLL: |
857 | return fuse_notify_poll(fc, size, cs); | 932 | return fuse_notify_poll(fc, size, cs); |
858 | 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); | ||
939 | |||
859 | default: | 940 | default: |
860 | fuse_copy_finish(cs); | 941 | fuse_copy_finish(cs); |
861 | return -EINVAL; | 942 | return -EINVAL; |
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 6b700734e519..e703654e7f40 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c | |||
@@ -859,6 +859,43 @@ int fuse_update_attributes(struct inode *inode, struct kstat *stat, | |||
859 | return err; | 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 | * Calling into a user-controlled filesystem gives the filesystem | 900 | * Calling into a user-controlled filesystem gives the filesystem |
864 | * daemon ptrace-like capabilities over the requester process. This | 901 | * daemon ptrace-like capabilities over the requester process. This |
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index ede4f77b2d6c..52b641fc0faf 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h | |||
@@ -484,6 +484,12 @@ struct fuse_conn { | |||
484 | 484 | ||
485 | /** Called on final put */ | 485 | /** Called on final put */ |
486 | void (*release)(struct fuse_conn *); | 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 | static inline struct fuse_conn *get_fuse_conn_super(struct super_block *sb) | 495 | static inline struct fuse_conn *get_fuse_conn_super(struct super_block *sb) |
@@ -512,6 +518,11 @@ extern const struct file_operations fuse_dev_operations; | |||
512 | extern const struct dentry_operations fuse_dentry_operations; | 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 | * Get a filled in inode | 526 | * Get a filled in inode |
516 | */ | 527 | */ |
517 | struct inode *fuse_iget(struct super_block *sb, u64 nodeid, | 528 | struct inode *fuse_iget(struct super_block *sb, u64 nodeid, |
@@ -711,6 +722,19 @@ void fuse_release_nowrite(struct inode *inode); | |||
711 | 722 | ||
712 | u64 fuse_get_attr_version(struct fuse_conn *fc); | 723 | u64 fuse_get_attr_version(struct fuse_conn *fc); |
713 | 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); | ||
737 | |||
714 | int fuse_do_open(struct fuse_conn *fc, u64 nodeid, struct file *file, | 738 | int fuse_do_open(struct fuse_conn *fc, u64 nodeid, struct file *file, |
715 | bool isdir); | 739 | bool isdir); |
716 | ssize_t fuse_direct_io(struct file *file, const char __user *buf, | 740 | ssize_t fuse_direct_io(struct file *file, const char __user *buf, |
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 6cc501bd0187..f91ccc4a189d 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c | |||
@@ -206,7 +206,7 @@ static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr) | |||
206 | BUG(); | 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 | u64 nodeid = *(u64 *) _nodeidp; | 211 | u64 nodeid = *(u64 *) _nodeidp; |
212 | if (get_node_id(inode) == nodeid) | 212 | if (get_node_id(inode) == nodeid) |
@@ -257,6 +257,31 @@ struct inode *fuse_iget(struct super_block *sb, u64 nodeid, | |||
257 | return inode; | 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 | static void fuse_umount_begin(struct super_block *sb) | 285 | static void fuse_umount_begin(struct super_block *sb) |
261 | { | 286 | { |
262 | fuse_abort_conn(get_fuse_conn_super(sb)); | 287 | fuse_abort_conn(get_fuse_conn_super(sb)); |
@@ -480,6 +505,7 @@ void fuse_conn_init(struct fuse_conn *fc) | |||
480 | memset(fc, 0, sizeof(*fc)); | 505 | memset(fc, 0, sizeof(*fc)); |
481 | spin_lock_init(&fc->lock); | 506 | spin_lock_init(&fc->lock); |
482 | mutex_init(&fc->inst_mutex); | 507 | mutex_init(&fc->inst_mutex); |
508 | init_rwsem(&fc->killsb); | ||
483 | atomic_set(&fc->count, 1); | 509 | atomic_set(&fc->count, 1); |
484 | init_waitqueue_head(&fc->waitq); | 510 | init_waitqueue_head(&fc->waitq); |
485 | init_waitqueue_head(&fc->blocked_waitq); | 511 | init_waitqueue_head(&fc->blocked_waitq); |
@@ -862,6 +888,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) | |||
862 | fuse_conn_init(fc); | 888 | fuse_conn_init(fc); |
863 | 889 | ||
864 | fc->dev = sb->s_dev; | 890 | fc->dev = sb->s_dev; |
891 | fc->sb = sb; | ||
865 | err = fuse_bdi_init(fc, sb); | 892 | err = fuse_bdi_init(fc, sb); |
866 | if (err) | 893 | if (err) |
867 | goto err_put_conn; | 894 | goto err_put_conn; |
@@ -948,12 +975,25 @@ static int fuse_get_sb(struct file_system_type *fs_type, | |||
948 | return get_sb_nodev(fs_type, flags, raw_data, fuse_fill_super, mnt); | 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 | static struct file_system_type fuse_fs_type = { | 991 | static struct file_system_type fuse_fs_type = { |
952 | .owner = THIS_MODULE, | 992 | .owner = THIS_MODULE, |
953 | .name = "fuse", | 993 | .name = "fuse", |
954 | .fs_flags = FS_HAS_SUBTYPE, | 994 | .fs_flags = FS_HAS_SUBTYPE, |
955 | .get_sb = fuse_get_sb, | 995 | .get_sb = fuse_get_sb, |
956 | .kill_sb = kill_anon_super, | 996 | .kill_sb = fuse_kill_sb_anon, |
957 | }; | 997 | }; |
958 | 998 | ||
959 | #ifdef CONFIG_BLOCK | 999 | #ifdef CONFIG_BLOCK |
@@ -965,11 +1005,24 @@ static int fuse_get_sb_blk(struct file_system_type *fs_type, | |||
965 | mnt); | 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 | static struct file_system_type fuseblk_fs_type = { | 1021 | static struct file_system_type fuseblk_fs_type = { |
969 | .owner = THIS_MODULE, | 1022 | .owner = THIS_MODULE, |
970 | .name = "fuseblk", | 1023 | .name = "fuseblk", |
971 | .get_sb = fuse_get_sb_blk, | 1024 | .get_sb = fuse_get_sb_blk, |
972 | .kill_sb = kill_block_super, | 1025 | .kill_sb = fuse_kill_sb_blk, |
973 | .fs_flags = FS_REQUIRES_DEV | FS_HAS_SUBTYPE, | 1026 | .fs_flags = FS_REQUIRES_DEV | FS_HAS_SUBTYPE, |
974 | }; | 1027 | }; |
975 | 1028 | ||