aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
Diffstat (limited to 'fs')
-rw-r--r--fs/sysfs/dir.c18
-rw-r--r--fs/sysfs/symlink.c20
-rw-r--r--fs/sysfs/sysfs.h2
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
28DEFINE_MUTEX(sysfs_mutex); 28DEFINE_MUTEX(sysfs_mutex);
29DEFINE_SPINLOCK(sysfs_assoc_lock); 29DEFINE_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 */
161extern struct mutex sysfs_mutex; 161extern struct mutex sysfs_mutex;
162extern spinlock_t sysfs_assoc_lock; 162extern spinlock_t sysfs_symlink_target_lock;
163extern const struct dentry_operations sysfs_dentry_ops; 163extern const struct dentry_operations sysfs_dentry_ops;
164 164
165extern const struct file_operations sysfs_dir_operations; 165extern const struct file_operations sysfs_dir_operations;