diff options
author | Eric W. Biederman <ebiederm@xmission.com> | 2009-11-20 19:08:56 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-12-11 14:24:54 -0500 |
commit | 832b6af198aefe6034310e124594cc8b833c0ef9 (patch) | |
tree | 853eec8f7449dd7fc51c6d4c9b70ea44b81cf0d9 /fs | |
parent | a16bbc3430ed94b543222f4c8ef68025f8493e93 (diff) |
sysfs: Propagate renames to the vfs on demand
By teaching sysfs_revalidate to hide a dentry for
a sysfs_dirent if the sysfs_dirent has been renamed,
and by teaching sysfs_lookup to return the original
dentry if the sysfs dirent has been renamed. I can
show the results of renames correctly without having to
update the dcache during the directory rename.
This massively simplifies the rename logic allowing a lot
of weird sysfs special cases to be removed along with
a lot of now unnecesary helper code.
Acked-by: Tejun Heo <tj@kernel.org>
Signed-off-by: Eric W. Biederman <ebiederm@aristanetworks.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/namei.c | 22 | ||||
-rw-r--r-- | fs/sysfs/dir.c | 158 | ||||
-rw-r--r-- | fs/sysfs/inode.c | 12 | ||||
-rw-r--r-- | fs/sysfs/sysfs.h | 1 |
4 files changed, 32 insertions, 161 deletions
diff --git a/fs/namei.c b/fs/namei.c index d11f404667e9..d3c190c35fcc 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -1279,28 +1279,6 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len) | |||
1279 | return __lookup_hash(&this, base, NULL); | 1279 | return __lookup_hash(&this, base, NULL); |
1280 | } | 1280 | } |
1281 | 1281 | ||
1282 | /** | ||
1283 | * lookup_one_noperm - bad hack for sysfs | ||
1284 | * @name: pathname component to lookup | ||
1285 | * @base: base directory to lookup from | ||
1286 | * | ||
1287 | * This is a variant of lookup_one_len that doesn't perform any permission | ||
1288 | * checks. It's a horrible hack to work around the braindead sysfs | ||
1289 | * architecture and should not be used anywhere else. | ||
1290 | * | ||
1291 | * DON'T USE THIS FUNCTION EVER, thanks. | ||
1292 | */ | ||
1293 | struct dentry *lookup_one_noperm(const char *name, struct dentry *base) | ||
1294 | { | ||
1295 | int err; | ||
1296 | struct qstr this; | ||
1297 | |||
1298 | err = __lookup_one_len(name, &this, base, strlen(name)); | ||
1299 | if (err) | ||
1300 | return ERR_PTR(err); | ||
1301 | return __lookup_hash(&this, base, NULL); | ||
1302 | } | ||
1303 | |||
1304 | int user_path_at(int dfd, const char __user *name, unsigned flags, | 1282 | int user_path_at(int dfd, const char __user *name, unsigned flags, |
1305 | struct path *path) | 1283 | struct path *path) |
1306 | { | 1284 | { |
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index f3af45e47eaa..97954c69ff0e 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c | |||
@@ -25,7 +25,6 @@ | |||
25 | #include "sysfs.h" | 25 | #include "sysfs.h" |
26 | 26 | ||
27 | DEFINE_MUTEX(sysfs_mutex); | 27 | DEFINE_MUTEX(sysfs_mutex); |
28 | DEFINE_MUTEX(sysfs_rename_mutex); | ||
29 | DEFINE_SPINLOCK(sysfs_assoc_lock); | 28 | DEFINE_SPINLOCK(sysfs_assoc_lock); |
30 | 29 | ||
31 | static DEFINE_SPINLOCK(sysfs_ino_lock); | 30 | static DEFINE_SPINLOCK(sysfs_ino_lock); |
@@ -85,46 +84,6 @@ static void sysfs_unlink_sibling(struct sysfs_dirent *sd) | |||
85 | } | 84 | } |
86 | 85 | ||
87 | /** | 86 | /** |
88 | * sysfs_get_dentry - get dentry for the given sysfs_dirent | ||
89 | * @sd: sysfs_dirent of interest | ||
90 | * | ||
91 | * Get dentry for @sd. Dentry is looked up if currently not | ||
92 | * present. This function descends from the root looking up | ||
93 | * dentry for each step. | ||
94 | * | ||
95 | * LOCKING: | ||
96 | * mutex_lock(sysfs_rename_mutex) | ||
97 | * | ||
98 | * RETURNS: | ||
99 | * Pointer to found dentry on success, ERR_PTR() value on error. | ||
100 | */ | ||
101 | struct dentry *sysfs_get_dentry(struct sysfs_dirent *sd) | ||
102 | { | ||
103 | struct dentry *dentry = dget(sysfs_sb->s_root); | ||
104 | |||
105 | while (dentry->d_fsdata != sd) { | ||
106 | struct sysfs_dirent *cur; | ||
107 | struct dentry *parent; | ||
108 | |||
109 | /* find the first ancestor which hasn't been looked up */ | ||
110 | cur = sd; | ||
111 | while (cur->s_parent != dentry->d_fsdata) | ||
112 | cur = cur->s_parent; | ||
113 | |||
114 | /* look it up */ | ||
115 | parent = dentry; | ||
116 | mutex_lock(&parent->d_inode->i_mutex); | ||
117 | dentry = lookup_one_noperm(cur->s_name, parent); | ||
118 | mutex_unlock(&parent->d_inode->i_mutex); | ||
119 | dput(parent); | ||
120 | |||
121 | if (IS_ERR(dentry)) | ||
122 | break; | ||
123 | } | ||
124 | return dentry; | ||
125 | } | ||
126 | |||
127 | /** | ||
128 | * sysfs_get_active - get an active reference to sysfs_dirent | 87 | * sysfs_get_active - get an active reference to sysfs_dirent |
129 | * @sd: sysfs_dirent to get an active reference to | 88 | * @sd: sysfs_dirent to get an active reference to |
130 | * | 89 | * |
@@ -315,6 +274,14 @@ static int sysfs_dentry_revalidate(struct dentry *dentry, struct nameidata *nd) | |||
315 | if (sd->s_flags & SYSFS_FLAG_REMOVED) | 274 | if (sd->s_flags & SYSFS_FLAG_REMOVED) |
316 | goto out_bad; | 275 | goto out_bad; |
317 | 276 | ||
277 | /* The sysfs dirent has been moved? */ | ||
278 | if (dentry->d_parent->d_fsdata != sd->s_parent) | ||
279 | goto out_bad; | ||
280 | |||
281 | /* The sysfs dirent has been renamed */ | ||
282 | if (strcmp(dentry->d_name.name, sd->s_name) != 0) | ||
283 | goto out_bad; | ||
284 | |||
318 | mutex_unlock(&sysfs_mutex); | 285 | mutex_unlock(&sysfs_mutex); |
319 | out_valid: | 286 | out_valid: |
320 | return 1; | 287 | return 1; |
@@ -322,6 +289,12 @@ out_bad: | |||
322 | /* Remove the dentry from the dcache hashes. | 289 | /* Remove the dentry from the dcache hashes. |
323 | * If this is a deleted dentry we use d_drop instead of d_delete | 290 | * If this is a deleted dentry we use d_drop instead of d_delete |
324 | * so sysfs doesn't need to cope with negative dentries. | 291 | * so sysfs doesn't need to cope with negative dentries. |
292 | * | ||
293 | * If this is a dentry that has simply been renamed we | ||
294 | * use d_drop to remove it from the dcache lookup on its | ||
295 | * old parent. If this dentry persists later when a lookup | ||
296 | * is performed at its new name the dentry will be readded | ||
297 | * to the dcache hashes. | ||
325 | */ | 298 | */ |
326 | is_dir = (sysfs_type(sd) == SYSFS_DIR); | 299 | is_dir = (sysfs_type(sd) == SYSFS_DIR); |
327 | mutex_unlock(&sysfs_mutex); | 300 | mutex_unlock(&sysfs_mutex); |
@@ -705,10 +678,15 @@ static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry, | |||
705 | } | 678 | } |
706 | 679 | ||
707 | /* instantiate and hash dentry */ | 680 | /* instantiate and hash dentry */ |
708 | dentry->d_op = &sysfs_dentry_ops; | 681 | ret = d_find_alias(inode); |
709 | dentry->d_fsdata = sysfs_get(sd); | 682 | if (!ret) { |
710 | d_instantiate(dentry, inode); | 683 | dentry->d_op = &sysfs_dentry_ops; |
711 | d_rehash(dentry); | 684 | dentry->d_fsdata = sysfs_get(sd); |
685 | d_add(dentry, inode); | ||
686 | } else { | ||
687 | d_move(ret, dentry); | ||
688 | iput(inode); | ||
689 | } | ||
712 | 690 | ||
713 | out_unlock: | 691 | out_unlock: |
714 | mutex_unlock(&sysfs_mutex); | 692 | mutex_unlock(&sysfs_mutex); |
@@ -785,62 +763,32 @@ void sysfs_remove_dir(struct kobject * kobj) | |||
785 | int sysfs_rename_dir(struct kobject * kobj, const char *new_name) | 763 | int sysfs_rename_dir(struct kobject * kobj, const char *new_name) |
786 | { | 764 | { |
787 | struct sysfs_dirent *sd = kobj->sd; | 765 | struct sysfs_dirent *sd = kobj->sd; |
788 | struct dentry *parent = NULL; | ||
789 | struct dentry *old_dentry = NULL, *new_dentry = NULL; | ||
790 | const char *dup_name = NULL; | 766 | const char *dup_name = NULL; |
791 | int error; | 767 | int error; |
792 | 768 | ||
793 | mutex_lock(&sysfs_rename_mutex); | 769 | mutex_lock(&sysfs_mutex); |
794 | 770 | ||
795 | error = 0; | 771 | error = 0; |
796 | if (strcmp(sd->s_name, new_name) == 0) | 772 | if (strcmp(sd->s_name, new_name) == 0) |
797 | goto out; /* nothing to rename */ | 773 | goto out; /* nothing to rename */ |
798 | 774 | ||
799 | /* get the original dentry */ | ||
800 | old_dentry = sysfs_get_dentry(sd); | ||
801 | if (IS_ERR(old_dentry)) { | ||
802 | error = PTR_ERR(old_dentry); | ||
803 | old_dentry = NULL; | ||
804 | goto out; | ||
805 | } | ||
806 | |||
807 | parent = old_dentry->d_parent; | ||
808 | |||
809 | /* lock parent and get dentry for new name */ | ||
810 | mutex_lock(&parent->d_inode->i_mutex); | ||
811 | mutex_lock(&sysfs_mutex); | ||
812 | |||
813 | error = -EEXIST; | 775 | error = -EEXIST; |
814 | if (sysfs_find_dirent(sd->s_parent, new_name)) | 776 | if (sysfs_find_dirent(sd->s_parent, new_name)) |
815 | goto out_unlock; | 777 | goto out; |
816 | |||
817 | error = -ENOMEM; | ||
818 | new_dentry = d_alloc_name(parent, new_name); | ||
819 | if (!new_dentry) | ||
820 | goto out_unlock; | ||
821 | 778 | ||
822 | /* rename sysfs_dirent */ | 779 | /* rename sysfs_dirent */ |
823 | error = -ENOMEM; | 780 | error = -ENOMEM; |
824 | new_name = dup_name = kstrdup(new_name, GFP_KERNEL); | 781 | new_name = dup_name = kstrdup(new_name, GFP_KERNEL); |
825 | if (!new_name) | 782 | if (!new_name) |
826 | goto out_unlock; | 783 | goto out; |
827 | 784 | ||
828 | dup_name = sd->s_name; | 785 | dup_name = sd->s_name; |
829 | sd->s_name = new_name; | 786 | sd->s_name = new_name; |
830 | 787 | ||
831 | /* rename */ | ||
832 | d_add(new_dentry, NULL); | ||
833 | d_move(old_dentry, new_dentry); | ||
834 | |||
835 | error = 0; | 788 | error = 0; |
836 | out_unlock: | 789 | out: |
837 | mutex_unlock(&sysfs_mutex); | 790 | mutex_unlock(&sysfs_mutex); |
838 | mutex_unlock(&parent->d_inode->i_mutex); | ||
839 | kfree(dup_name); | 791 | kfree(dup_name); |
840 | dput(old_dentry); | ||
841 | dput(new_dentry); | ||
842 | out: | ||
843 | mutex_unlock(&sysfs_rename_mutex); | ||
844 | return error; | 792 | return error; |
845 | } | 793 | } |
846 | 794 | ||
@@ -848,12 +796,11 @@ int sysfs_move_dir(struct kobject *kobj, struct kobject *new_parent_kobj) | |||
848 | { | 796 | { |
849 | struct sysfs_dirent *sd = kobj->sd; | 797 | struct sysfs_dirent *sd = kobj->sd; |
850 | struct sysfs_dirent *new_parent_sd; | 798 | struct sysfs_dirent *new_parent_sd; |
851 | struct dentry *old_parent, *new_parent = NULL; | ||
852 | struct dentry *old_dentry = NULL, *new_dentry = NULL; | ||
853 | int error; | 799 | int error; |
854 | 800 | ||
855 | mutex_lock(&sysfs_rename_mutex); | ||
856 | BUG_ON(!sd->s_parent); | 801 | BUG_ON(!sd->s_parent); |
802 | |||
803 | mutex_lock(&sysfs_mutex); | ||
857 | new_parent_sd = (new_parent_kobj && new_parent_kobj->sd) ? | 804 | new_parent_sd = (new_parent_kobj && new_parent_kobj->sd) ? |
858 | new_parent_kobj->sd : &sysfs_root; | 805 | new_parent_kobj->sd : &sysfs_root; |
859 | 806 | ||
@@ -861,61 +808,20 @@ int sysfs_move_dir(struct kobject *kobj, struct kobject *new_parent_kobj) | |||
861 | if (sd->s_parent == new_parent_sd) | 808 | if (sd->s_parent == new_parent_sd) |
862 | goto out; /* nothing to move */ | 809 | goto out; /* nothing to move */ |
863 | 810 | ||
864 | /* get dentries */ | ||
865 | old_dentry = sysfs_get_dentry(sd); | ||
866 | if (IS_ERR(old_dentry)) { | ||
867 | error = PTR_ERR(old_dentry); | ||
868 | old_dentry = NULL; | ||
869 | goto out; | ||
870 | } | ||
871 | old_parent = old_dentry->d_parent; | ||
872 | |||
873 | new_parent = sysfs_get_dentry(new_parent_sd); | ||
874 | if (IS_ERR(new_parent)) { | ||
875 | error = PTR_ERR(new_parent); | ||
876 | new_parent = NULL; | ||
877 | goto out; | ||
878 | } | ||
879 | |||
880 | again: | ||
881 | mutex_lock(&old_parent->d_inode->i_mutex); | ||
882 | if (!mutex_trylock(&new_parent->d_inode->i_mutex)) { | ||
883 | mutex_unlock(&old_parent->d_inode->i_mutex); | ||
884 | goto again; | ||
885 | } | ||
886 | mutex_lock(&sysfs_mutex); | ||
887 | |||
888 | error = -EEXIST; | 811 | error = -EEXIST; |
889 | if (sysfs_find_dirent(new_parent_sd, sd->s_name)) | 812 | if (sysfs_find_dirent(new_parent_sd, sd->s_name)) |
890 | goto out_unlock; | 813 | goto out; |
891 | |||
892 | error = -ENOMEM; | ||
893 | new_dentry = d_alloc_name(new_parent, sd->s_name); | ||
894 | if (!new_dentry) | ||
895 | goto out_unlock; | ||
896 | |||
897 | error = 0; | ||
898 | d_add(new_dentry, NULL); | ||
899 | d_move(old_dentry, new_dentry); | ||
900 | 814 | ||
901 | /* Remove from old parent's list and insert into new parent's list. */ | 815 | /* Remove from old parent's list and insert into new parent's list. */ |
902 | sysfs_unlink_sibling(sd); | 816 | sysfs_unlink_sibling(sd); |
903 | sysfs_get(new_parent_sd); | 817 | sysfs_get(new_parent_sd); |
904 | drop_nlink(old_parent->d_inode); | ||
905 | sysfs_put(sd->s_parent); | 818 | sysfs_put(sd->s_parent); |
906 | sd->s_parent = new_parent_sd; | 819 | sd->s_parent = new_parent_sd; |
907 | inc_nlink(new_parent->d_inode); | ||
908 | sysfs_link_sibling(sd); | 820 | sysfs_link_sibling(sd); |
909 | 821 | ||
910 | out_unlock: | 822 | error = 0; |
823 | out: | ||
911 | mutex_unlock(&sysfs_mutex); | 824 | mutex_unlock(&sysfs_mutex); |
912 | mutex_unlock(&new_parent->d_inode->i_mutex); | ||
913 | mutex_unlock(&old_parent->d_inode->i_mutex); | ||
914 | out: | ||
915 | dput(new_parent); | ||
916 | dput(old_dentry); | ||
917 | dput(new_dentry); | ||
918 | mutex_unlock(&sysfs_rename_mutex); | ||
919 | return error; | 825 | return error; |
920 | } | 826 | } |
921 | 827 | ||
diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 1ffd5559926f..9f783d4e4b51 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c | |||
@@ -205,17 +205,6 @@ static inline void set_inode_attr(struct inode * inode, struct iattr * iattr) | |||
205 | inode->i_ctime = iattr->ia_ctime; | 205 | inode->i_ctime = iattr->ia_ctime; |
206 | } | 206 | } |
207 | 207 | ||
208 | |||
209 | /* | ||
210 | * sysfs has a different i_mutex lock order behavior for i_mutex than other | ||
211 | * filesystems; sysfs i_mutex is called in many places with subsystem locks | ||
212 | * held. At the same time, many of the VFS locking rules do not apply to | ||
213 | * sysfs at all (cross directory rename for example). To untangle this mess | ||
214 | * (which gives false positives in lockdep), we're giving sysfs inodes their | ||
215 | * own class for i_mutex. | ||
216 | */ | ||
217 | static struct lock_class_key sysfs_inode_imutex_key; | ||
218 | |||
219 | static int sysfs_count_nlink(struct sysfs_dirent *sd) | 208 | static int sysfs_count_nlink(struct sysfs_dirent *sd) |
220 | { | 209 | { |
221 | struct sysfs_dirent *child; | 210 | struct sysfs_dirent *child; |
@@ -268,7 +257,6 @@ static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode) | |||
268 | inode->i_mapping->a_ops = &sysfs_aops; | 257 | inode->i_mapping->a_ops = &sysfs_aops; |
269 | inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info; | 258 | inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info; |
270 | inode->i_op = &sysfs_inode_operations; | 259 | inode->i_op = &sysfs_inode_operations; |
271 | lockdep_set_class(&inode->i_mutex, &sysfs_inode_imutex_key); | ||
272 | 260 | ||
273 | set_default_inode_attr(inode, sd->s_mode); | 261 | set_default_inode_attr(inode, sd->s_mode); |
274 | sysfs_refresh_inode(sd, inode); | 262 | sysfs_refresh_inode(sd, inode); |
diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 90b35012abb2..98a15bf1efe1 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h | |||
@@ -103,7 +103,6 @@ extern struct kmem_cache *sysfs_dir_cachep; | |||
103 | * dir.c | 103 | * dir.c |
104 | */ | 104 | */ |
105 | extern struct mutex sysfs_mutex; | 105 | extern struct mutex sysfs_mutex; |
106 | extern struct mutex sysfs_rename_mutex; | ||
107 | extern spinlock_t sysfs_assoc_lock; | 106 | extern spinlock_t sysfs_assoc_lock; |
108 | 107 | ||
109 | extern const struct file_operations sysfs_dir_operations; | 108 | extern const struct file_operations sysfs_dir_operations; |