diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/sysfs/dir.c | 18 | ||||
-rw-r--r-- | fs/sysfs/symlink.c | 20 | ||||
-rw-r--r-- | fs/sysfs/sysfs.h | 2 |
3 files changed, 30 insertions, 10 deletions
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index de47ed32d5c7..08c66969d52a 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c | |||
@@ -26,7 +26,7 @@ | |||
26 | #include "sysfs.h" | 26 | #include "sysfs.h" |
27 | 27 | ||
28 | DEFINE_MUTEX(sysfs_mutex); | 28 | DEFINE_MUTEX(sysfs_mutex); |
29 | DEFINE_SPINLOCK(sysfs_assoc_lock); | 29 | DEFINE_SPINLOCK(sysfs_symlink_target_lock); |
30 | 30 | ||
31 | #define to_sysfs_dirent(X) rb_entry((X), struct sysfs_dirent, s_rb) | 31 | #define to_sysfs_dirent(X) rb_entry((X), struct sysfs_dirent, s_rb) |
32 | 32 | ||
@@ -902,9 +902,21 @@ void sysfs_remove_dir(struct kobject *kobj) | |||
902 | { | 902 | { |
903 | struct sysfs_dirent *sd = kobj->sd; | 903 | struct sysfs_dirent *sd = kobj->sd; |
904 | 904 | ||
905 | spin_lock(&sysfs_assoc_lock); | 905 | /* |
906 | * In general, kboject owner is responsible for ensuring removal | ||
907 | * doesn't race with other operations and sysfs doesn't provide any | ||
908 | * protection; however, when @kobj is used as a symlink target, the | ||
909 | * symlinking entity usually doesn't own @kobj and thus has no | ||
910 | * control over removal. @kobj->sd may be removed anytime and | ||
911 | * symlink code may end up dereferencing an already freed sd. | ||
912 | * | ||
913 | * sysfs_symlink_target_lock synchronizes @kobj->sd disassociation | ||
914 | * against symlink operations so that symlink code can safely | ||
915 | * dereference @kobj->sd. | ||
916 | */ | ||
917 | spin_lock(&sysfs_symlink_target_lock); | ||
906 | kobj->sd = NULL; | 918 | kobj->sd = NULL; |
907 | spin_unlock(&sysfs_assoc_lock); | 919 | spin_unlock(&sysfs_symlink_target_lock); |
908 | 920 | ||
909 | if (sd) { | 921 | if (sd) { |
910 | WARN_ON_ONCE(sysfs_type(sd) != SYSFS_DIR); | 922 | WARN_ON_ONCE(sysfs_type(sd) != SYSFS_DIR); |
diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index 22ea2f5796f5..1a23681b8179 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c | |||
@@ -32,13 +32,15 @@ static int sysfs_do_create_link_sd(struct sysfs_dirent *parent_sd, | |||
32 | 32 | ||
33 | BUG_ON(!name || !parent_sd); | 33 | BUG_ON(!name || !parent_sd); |
34 | 34 | ||
35 | /* target->sd can go away beneath us but is protected with | 35 | /* |
36 | * sysfs_assoc_lock. Fetch target_sd from it. | 36 | * We don't own @target and it may be removed at any time. |
37 | * Synchronize using sysfs_symlink_target_lock. See | ||
38 | * sysfs_remove_dir() for details. | ||
37 | */ | 39 | */ |
38 | spin_lock(&sysfs_assoc_lock); | 40 | spin_lock(&sysfs_symlink_target_lock); |
39 | if (target->sd) | 41 | if (target->sd) |
40 | target_sd = sysfs_get(target->sd); | 42 | target_sd = sysfs_get(target->sd); |
41 | spin_unlock(&sysfs_assoc_lock); | 43 | spin_unlock(&sysfs_symlink_target_lock); |
42 | 44 | ||
43 | error = -ENOENT; | 45 | error = -ENOENT; |
44 | if (!target_sd) | 46 | if (!target_sd) |
@@ -140,10 +142,16 @@ void sysfs_delete_link(struct kobject *kobj, struct kobject *targ, | |||
140 | const char *name) | 142 | const char *name) |
141 | { | 143 | { |
142 | const void *ns = NULL; | 144 | const void *ns = NULL; |
143 | spin_lock(&sysfs_assoc_lock); | 145 | |
146 | /* | ||
147 | * We don't own @target and it may be removed at any time. | ||
148 | * Synchronize using sysfs_symlink_target_lock. See | ||
149 | * sysfs_remove_dir() for details. | ||
150 | */ | ||
151 | spin_lock(&sysfs_symlink_target_lock); | ||
144 | if (targ->sd) | 152 | if (targ->sd) |
145 | ns = targ->sd->s_ns; | 153 | ns = targ->sd->s_ns; |
146 | spin_unlock(&sysfs_assoc_lock); | 154 | spin_unlock(&sysfs_symlink_target_lock); |
147 | sysfs_hash_and_remove(kobj->sd, name, ns); | 155 | sysfs_hash_and_remove(kobj->sd, name, ns); |
148 | } | 156 | } |
149 | 157 | ||
diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 05d063fe69c7..e3aea92ebfa3 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h | |||
@@ -159,7 +159,7 @@ extern struct kmem_cache *sysfs_dir_cachep; | |||
159 | * dir.c | 159 | * dir.c |
160 | */ | 160 | */ |
161 | extern struct mutex sysfs_mutex; | 161 | extern struct mutex sysfs_mutex; |
162 | extern spinlock_t sysfs_assoc_lock; | 162 | extern spinlock_t sysfs_symlink_target_lock; |
163 | extern const struct dentry_operations sysfs_dentry_ops; | 163 | extern const struct dentry_operations sysfs_dentry_ops; |
164 | 164 | ||
165 | extern const struct file_operations sysfs_dir_operations; | 165 | extern const struct file_operations sysfs_dir_operations; |