diff options
Diffstat (limited to 'fs/configfs')
-rw-r--r-- | fs/configfs/configfs_internal.h | 4 | ||||
-rw-r--r-- | fs/configfs/dir.c | 147 | ||||
-rw-r--r-- | fs/configfs/inode.c | 2 | ||||
-rw-r--r-- | fs/configfs/symlink.c | 16 |
4 files changed, 125 insertions, 44 deletions
diff --git a/fs/configfs/configfs_internal.h b/fs/configfs/configfs_internal.h index cca98609aa7f..da015c12e3ea 100644 --- a/fs/configfs/configfs_internal.h +++ b/fs/configfs/configfs_internal.h | |||
@@ -26,6 +26,7 @@ | |||
26 | 26 | ||
27 | #include <linux/slab.h> | 27 | #include <linux/slab.h> |
28 | #include <linux/list.h> | 28 | #include <linux/list.h> |
29 | #include <linux/spinlock.h> | ||
29 | 30 | ||
30 | struct configfs_dirent { | 31 | struct configfs_dirent { |
31 | atomic_t s_count; | 32 | atomic_t s_count; |
@@ -47,8 +48,11 @@ struct configfs_dirent { | |||
47 | #define CONFIGFS_USET_DIR 0x0040 | 48 | #define CONFIGFS_USET_DIR 0x0040 |
48 | #define CONFIGFS_USET_DEFAULT 0x0080 | 49 | #define CONFIGFS_USET_DEFAULT 0x0080 |
49 | #define CONFIGFS_USET_DROPPING 0x0100 | 50 | #define CONFIGFS_USET_DROPPING 0x0100 |
51 | #define CONFIGFS_USET_IN_MKDIR 0x0200 | ||
50 | #define CONFIGFS_NOT_PINNED (CONFIGFS_ITEM_ATTR) | 52 | #define CONFIGFS_NOT_PINNED (CONFIGFS_ITEM_ATTR) |
51 | 53 | ||
54 | extern spinlock_t configfs_dirent_lock; | ||
55 | |||
52 | extern struct vfsmount * configfs_mount; | 56 | extern struct vfsmount * configfs_mount; |
53 | extern struct kmem_cache *configfs_dir_cachep; | 57 | extern struct kmem_cache *configfs_dir_cachep; |
54 | 58 | ||
diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index a48dc7dd8765..0e64312a084c 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c | |||
@@ -30,11 +30,25 @@ | |||
30 | #include <linux/mount.h> | 30 | #include <linux/mount.h> |
31 | #include <linux/module.h> | 31 | #include <linux/module.h> |
32 | #include <linux/slab.h> | 32 | #include <linux/slab.h> |
33 | #include <linux/err.h> | ||
33 | 34 | ||
34 | #include <linux/configfs.h> | 35 | #include <linux/configfs.h> |
35 | #include "configfs_internal.h" | 36 | #include "configfs_internal.h" |
36 | 37 | ||
37 | DECLARE_RWSEM(configfs_rename_sem); | 38 | DECLARE_RWSEM(configfs_rename_sem); |
39 | /* | ||
40 | * Protects mutations of configfs_dirent linkage together with proper i_mutex | ||
41 | * Also protects mutations of symlinks linkage to target configfs_dirent | ||
42 | * Mutators of configfs_dirent linkage must *both* have the proper inode locked | ||
43 | * and configfs_dirent_lock locked, in that order. | ||
44 | * This allows one to safely traverse configfs_dirent trees and symlinks without | ||
45 | * having to lock inodes. | ||
46 | * | ||
47 | * Protects setting of CONFIGFS_USET_DROPPING: checking the flag | ||
48 | * unlocked is not reliable unless in detach_groups() called from | ||
49 | * rmdir()/unregister() and from configfs_attach_group() | ||
50 | */ | ||
51 | DEFINE_SPINLOCK(configfs_dirent_lock); | ||
38 | 52 | ||
39 | static void configfs_d_iput(struct dentry * dentry, | 53 | static void configfs_d_iput(struct dentry * dentry, |
40 | struct inode * inode) | 54 | struct inode * inode) |
@@ -74,13 +88,20 @@ static struct configfs_dirent *configfs_new_dirent(struct configfs_dirent * pare | |||
74 | 88 | ||
75 | sd = kmem_cache_zalloc(configfs_dir_cachep, GFP_KERNEL); | 89 | sd = kmem_cache_zalloc(configfs_dir_cachep, GFP_KERNEL); |
76 | if (!sd) | 90 | if (!sd) |
77 | return NULL; | 91 | return ERR_PTR(-ENOMEM); |
78 | 92 | ||
79 | atomic_set(&sd->s_count, 1); | 93 | atomic_set(&sd->s_count, 1); |
80 | INIT_LIST_HEAD(&sd->s_links); | 94 | INIT_LIST_HEAD(&sd->s_links); |
81 | INIT_LIST_HEAD(&sd->s_children); | 95 | INIT_LIST_HEAD(&sd->s_children); |
82 | list_add(&sd->s_sibling, &parent_sd->s_children); | ||
83 | sd->s_element = element; | 96 | sd->s_element = element; |
97 | spin_lock(&configfs_dirent_lock); | ||
98 | if (parent_sd->s_type & CONFIGFS_USET_DROPPING) { | ||
99 | spin_unlock(&configfs_dirent_lock); | ||
100 | kmem_cache_free(configfs_dir_cachep, sd); | ||
101 | return ERR_PTR(-ENOENT); | ||
102 | } | ||
103 | list_add(&sd->s_sibling, &parent_sd->s_children); | ||
104 | spin_unlock(&configfs_dirent_lock); | ||
84 | 105 | ||
85 | return sd; | 106 | return sd; |
86 | } | 107 | } |
@@ -118,8 +139,8 @@ int configfs_make_dirent(struct configfs_dirent * parent_sd, | |||
118 | struct configfs_dirent * sd; | 139 | struct configfs_dirent * sd; |
119 | 140 | ||
120 | sd = configfs_new_dirent(parent_sd, element); | 141 | sd = configfs_new_dirent(parent_sd, element); |
121 | if (!sd) | 142 | if (IS_ERR(sd)) |
122 | return -ENOMEM; | 143 | return PTR_ERR(sd); |
123 | 144 | ||
124 | sd->s_mode = mode; | 145 | sd->s_mode = mode; |
125 | sd->s_type = type; | 146 | sd->s_type = type; |
@@ -173,7 +194,9 @@ static int create_dir(struct config_item * k, struct dentry * p, | |||
173 | } else { | 194 | } else { |
174 | struct configfs_dirent *sd = d->d_fsdata; | 195 | struct configfs_dirent *sd = d->d_fsdata; |
175 | if (sd) { | 196 | if (sd) { |
197 | spin_lock(&configfs_dirent_lock); | ||
176 | list_del_init(&sd->s_sibling); | 198 | list_del_init(&sd->s_sibling); |
199 | spin_unlock(&configfs_dirent_lock); | ||
177 | configfs_put(sd); | 200 | configfs_put(sd); |
178 | } | 201 | } |
179 | } | 202 | } |
@@ -224,7 +247,9 @@ int configfs_create_link(struct configfs_symlink *sl, | |||
224 | else { | 247 | else { |
225 | struct configfs_dirent *sd = dentry->d_fsdata; | 248 | struct configfs_dirent *sd = dentry->d_fsdata; |
226 | if (sd) { | 249 | if (sd) { |
250 | spin_lock(&configfs_dirent_lock); | ||
227 | list_del_init(&sd->s_sibling); | 251 | list_del_init(&sd->s_sibling); |
252 | spin_unlock(&configfs_dirent_lock); | ||
228 | configfs_put(sd); | 253 | configfs_put(sd); |
229 | } | 254 | } |
230 | } | 255 | } |
@@ -238,7 +263,9 @@ static void remove_dir(struct dentry * d) | |||
238 | struct configfs_dirent * sd; | 263 | struct configfs_dirent * sd; |
239 | 264 | ||
240 | sd = d->d_fsdata; | 265 | sd = d->d_fsdata; |
266 | spin_lock(&configfs_dirent_lock); | ||
241 | list_del_init(&sd->s_sibling); | 267 | list_del_init(&sd->s_sibling); |
268 | spin_unlock(&configfs_dirent_lock); | ||
242 | configfs_put(sd); | 269 | configfs_put(sd); |
243 | if (d->d_inode) | 270 | if (d->d_inode) |
244 | simple_rmdir(parent->d_inode,d); | 271 | simple_rmdir(parent->d_inode,d); |
@@ -331,13 +358,13 @@ static struct dentry * configfs_lookup(struct inode *dir, | |||
331 | 358 | ||
332 | /* | 359 | /* |
333 | * Only subdirectories count here. Files (CONFIGFS_NOT_PINNED) are | 360 | * Only subdirectories count here. Files (CONFIGFS_NOT_PINNED) are |
334 | * attributes and are removed by rmdir(). We recurse, taking i_mutex | 361 | * attributes and are removed by rmdir(). We recurse, setting |
335 | * on all children that are candidates for default detach. If the | 362 | * CONFIGFS_USET_DROPPING on all children that are candidates for |
336 | * result is clean, then configfs_detach_group() will handle dropping | 363 | * default detach. |
337 | * i_mutex. If there is an error, the caller will clean up the i_mutex | 364 | * If there is an error, the caller will reset the flags via |
338 | * holders via configfs_detach_rollback(). | 365 | * configfs_detach_rollback(). |
339 | */ | 366 | */ |
340 | static int configfs_detach_prep(struct dentry *dentry) | 367 | static int configfs_detach_prep(struct dentry *dentry, struct mutex **wait_mutex) |
341 | { | 368 | { |
342 | struct configfs_dirent *parent_sd = dentry->d_fsdata; | 369 | struct configfs_dirent *parent_sd = dentry->d_fsdata; |
343 | struct configfs_dirent *sd; | 370 | struct configfs_dirent *sd; |
@@ -352,15 +379,20 @@ static int configfs_detach_prep(struct dentry *dentry) | |||
352 | if (sd->s_type & CONFIGFS_NOT_PINNED) | 379 | if (sd->s_type & CONFIGFS_NOT_PINNED) |
353 | continue; | 380 | continue; |
354 | if (sd->s_type & CONFIGFS_USET_DEFAULT) { | 381 | if (sd->s_type & CONFIGFS_USET_DEFAULT) { |
355 | mutex_lock(&sd->s_dentry->d_inode->i_mutex); | 382 | /* Abort if racing with mkdir() */ |
356 | /* Mark that we've taken i_mutex */ | 383 | if (sd->s_type & CONFIGFS_USET_IN_MKDIR) { |
384 | if (wait_mutex) | ||
385 | *wait_mutex = &sd->s_dentry->d_inode->i_mutex; | ||
386 | return -EAGAIN; | ||
387 | } | ||
388 | /* Mark that we're trying to drop the group */ | ||
357 | sd->s_type |= CONFIGFS_USET_DROPPING; | 389 | sd->s_type |= CONFIGFS_USET_DROPPING; |
358 | 390 | ||
359 | /* | 391 | /* |
360 | * Yup, recursive. If there's a problem, blame | 392 | * Yup, recursive. If there's a problem, blame |
361 | * deep nesting of default_groups | 393 | * deep nesting of default_groups |
362 | */ | 394 | */ |
363 | ret = configfs_detach_prep(sd->s_dentry); | 395 | ret = configfs_detach_prep(sd->s_dentry, wait_mutex); |
364 | if (!ret) | 396 | if (!ret) |
365 | continue; | 397 | continue; |
366 | } else | 398 | } else |
@@ -374,7 +406,7 @@ out: | |||
374 | } | 406 | } |
375 | 407 | ||
376 | /* | 408 | /* |
377 | * Walk the tree, dropping i_mutex wherever CONFIGFS_USET_DROPPING is | 409 | * Walk the tree, resetting CONFIGFS_USET_DROPPING wherever it was |
378 | * set. | 410 | * set. |
379 | */ | 411 | */ |
380 | static void configfs_detach_rollback(struct dentry *dentry) | 412 | static void configfs_detach_rollback(struct dentry *dentry) |
@@ -385,11 +417,7 @@ static void configfs_detach_rollback(struct dentry *dentry) | |||
385 | list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { | 417 | list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { |
386 | if (sd->s_type & CONFIGFS_USET_DEFAULT) { | 418 | if (sd->s_type & CONFIGFS_USET_DEFAULT) { |
387 | configfs_detach_rollback(sd->s_dentry); | 419 | configfs_detach_rollback(sd->s_dentry); |
388 | 420 | sd->s_type &= ~CONFIGFS_USET_DROPPING; | |
389 | if (sd->s_type & CONFIGFS_USET_DROPPING) { | ||
390 | sd->s_type &= ~CONFIGFS_USET_DROPPING; | ||
391 | mutex_unlock(&sd->s_dentry->d_inode->i_mutex); | ||
392 | } | ||
393 | } | 421 | } |
394 | } | 422 | } |
395 | } | 423 | } |
@@ -410,7 +438,9 @@ static void detach_attrs(struct config_item * item) | |||
410 | list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) { | 438 | list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) { |
411 | if (!sd->s_element || !(sd->s_type & CONFIGFS_NOT_PINNED)) | 439 | if (!sd->s_element || !(sd->s_type & CONFIGFS_NOT_PINNED)) |
412 | continue; | 440 | continue; |
441 | spin_lock(&configfs_dirent_lock); | ||
413 | list_del_init(&sd->s_sibling); | 442 | list_del_init(&sd->s_sibling); |
443 | spin_unlock(&configfs_dirent_lock); | ||
414 | configfs_drop_dentry(sd, dentry); | 444 | configfs_drop_dentry(sd, dentry); |
415 | configfs_put(sd); | 445 | configfs_put(sd); |
416 | } | 446 | } |
@@ -466,16 +496,12 @@ static void detach_groups(struct config_group *group) | |||
466 | 496 | ||
467 | child = sd->s_dentry; | 497 | child = sd->s_dentry; |
468 | 498 | ||
499 | mutex_lock(&child->d_inode->i_mutex); | ||
500 | |||
469 | configfs_detach_group(sd->s_element); | 501 | configfs_detach_group(sd->s_element); |
470 | child->d_inode->i_flags |= S_DEAD; | 502 | child->d_inode->i_flags |= S_DEAD; |
471 | 503 | ||
472 | /* | 504 | mutex_unlock(&child->d_inode->i_mutex); |
473 | * From rmdir/unregister, a configfs_detach_prep() pass | ||
474 | * has taken our i_mutex for us. Drop it. | ||
475 | * From mkdir/register cleanup, there is no sem held. | ||
476 | */ | ||
477 | if (sd->s_type & CONFIGFS_USET_DROPPING) | ||
478 | mutex_unlock(&child->d_inode->i_mutex); | ||
479 | 505 | ||
480 | d_delete(child); | 506 | d_delete(child); |
481 | dput(child); | 507 | dput(child); |
@@ -1047,25 +1073,24 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) | |||
1047 | group = NULL; | 1073 | group = NULL; |
1048 | item = NULL; | 1074 | item = NULL; |
1049 | if (type->ct_group_ops->make_group) { | 1075 | if (type->ct_group_ops->make_group) { |
1050 | group = type->ct_group_ops->make_group(to_config_group(parent_item), name); | 1076 | ret = type->ct_group_ops->make_group(to_config_group(parent_item), name, &group); |
1051 | if (group) { | 1077 | if (!ret) { |
1052 | link_group(to_config_group(parent_item), group); | 1078 | link_group(to_config_group(parent_item), group); |
1053 | item = &group->cg_item; | 1079 | item = &group->cg_item; |
1054 | } | 1080 | } |
1055 | } else { | 1081 | } else { |
1056 | item = type->ct_group_ops->make_item(to_config_group(parent_item), name); | 1082 | ret = type->ct_group_ops->make_item(to_config_group(parent_item), name, &item); |
1057 | if (item) | 1083 | if (!ret) |
1058 | link_obj(parent_item, item); | 1084 | link_obj(parent_item, item); |
1059 | } | 1085 | } |
1060 | mutex_unlock(&subsys->su_mutex); | 1086 | mutex_unlock(&subsys->su_mutex); |
1061 | 1087 | ||
1062 | kfree(name); | 1088 | kfree(name); |
1063 | if (!item) { | 1089 | if (ret) { |
1064 | /* | 1090 | /* |
1065 | * If item == NULL, then link_obj() was never called. | 1091 | * If ret != 0, then link_obj() was never called. |
1066 | * There are no extra references to clean up. | 1092 | * There are no extra references to clean up. |
1067 | */ | 1093 | */ |
1068 | ret = -ENOMEM; | ||
1069 | goto out_put; | 1094 | goto out_put; |
1070 | } | 1095 | } |
1071 | 1096 | ||
@@ -1093,11 +1118,26 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) | |||
1093 | */ | 1118 | */ |
1094 | module_got = 1; | 1119 | module_got = 1; |
1095 | 1120 | ||
1121 | /* | ||
1122 | * Make racing rmdir() fail if it did not tag parent with | ||
1123 | * CONFIGFS_USET_DROPPING | ||
1124 | * Note: if CONFIGFS_USET_DROPPING is already set, attach_group() will | ||
1125 | * fail and let rmdir() terminate correctly | ||
1126 | */ | ||
1127 | spin_lock(&configfs_dirent_lock); | ||
1128 | /* This will make configfs_detach_prep() fail */ | ||
1129 | sd->s_type |= CONFIGFS_USET_IN_MKDIR; | ||
1130 | spin_unlock(&configfs_dirent_lock); | ||
1131 | |||
1096 | if (group) | 1132 | if (group) |
1097 | ret = configfs_attach_group(parent_item, item, dentry); | 1133 | ret = configfs_attach_group(parent_item, item, dentry); |
1098 | else | 1134 | else |
1099 | ret = configfs_attach_item(parent_item, item, dentry); | 1135 | ret = configfs_attach_item(parent_item, item, dentry); |
1100 | 1136 | ||
1137 | spin_lock(&configfs_dirent_lock); | ||
1138 | sd->s_type &= ~CONFIGFS_USET_IN_MKDIR; | ||
1139 | spin_unlock(&configfs_dirent_lock); | ||
1140 | |||
1101 | out_unlink: | 1141 | out_unlink: |
1102 | if (ret) { | 1142 | if (ret) { |
1103 | /* Tear down everything we built up */ | 1143 | /* Tear down everything we built up */ |
@@ -1161,12 +1201,27 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry) | |||
1161 | return -EINVAL; | 1201 | return -EINVAL; |
1162 | } | 1202 | } |
1163 | 1203 | ||
1164 | ret = configfs_detach_prep(dentry); | 1204 | spin_lock(&configfs_dirent_lock); |
1165 | if (ret) { | 1205 | do { |
1166 | configfs_detach_rollback(dentry); | 1206 | struct mutex *wait_mutex; |
1167 | config_item_put(parent_item); | 1207 | |
1168 | return ret; | 1208 | ret = configfs_detach_prep(dentry, &wait_mutex); |
1169 | } | 1209 | if (ret) { |
1210 | configfs_detach_rollback(dentry); | ||
1211 | spin_unlock(&configfs_dirent_lock); | ||
1212 | if (ret != -EAGAIN) { | ||
1213 | config_item_put(parent_item); | ||
1214 | return ret; | ||
1215 | } | ||
1216 | |||
1217 | /* Wait until the racing operation terminates */ | ||
1218 | mutex_lock(wait_mutex); | ||
1219 | mutex_unlock(wait_mutex); | ||
1220 | |||
1221 | spin_lock(&configfs_dirent_lock); | ||
1222 | } | ||
1223 | } while (ret == -EAGAIN); | ||
1224 | spin_unlock(&configfs_dirent_lock); | ||
1170 | 1225 | ||
1171 | /* Get a working ref for the duration of this function */ | 1226 | /* Get a working ref for the duration of this function */ |
1172 | item = configfs_get_config_item(dentry); | 1227 | item = configfs_get_config_item(dentry); |
@@ -1258,7 +1313,7 @@ static int configfs_dir_open(struct inode *inode, struct file *file) | |||
1258 | file->private_data = configfs_new_dirent(parent_sd, NULL); | 1313 | file->private_data = configfs_new_dirent(parent_sd, NULL); |
1259 | mutex_unlock(&dentry->d_inode->i_mutex); | 1314 | mutex_unlock(&dentry->d_inode->i_mutex); |
1260 | 1315 | ||
1261 | return file->private_data ? 0 : -ENOMEM; | 1316 | return IS_ERR(file->private_data) ? PTR_ERR(file->private_data) : 0; |
1262 | 1317 | ||
1263 | } | 1318 | } |
1264 | 1319 | ||
@@ -1268,7 +1323,9 @@ static int configfs_dir_close(struct inode *inode, struct file *file) | |||
1268 | struct configfs_dirent * cursor = file->private_data; | 1323 | struct configfs_dirent * cursor = file->private_data; |
1269 | 1324 | ||
1270 | mutex_lock(&dentry->d_inode->i_mutex); | 1325 | mutex_lock(&dentry->d_inode->i_mutex); |
1326 | spin_lock(&configfs_dirent_lock); | ||
1271 | list_del_init(&cursor->s_sibling); | 1327 | list_del_init(&cursor->s_sibling); |
1328 | spin_unlock(&configfs_dirent_lock); | ||
1272 | mutex_unlock(&dentry->d_inode->i_mutex); | 1329 | mutex_unlock(&dentry->d_inode->i_mutex); |
1273 | 1330 | ||
1274 | release_configfs_dirent(cursor); | 1331 | release_configfs_dirent(cursor); |
@@ -1308,7 +1365,9 @@ static int configfs_readdir(struct file * filp, void * dirent, filldir_t filldir | |||
1308 | /* fallthrough */ | 1365 | /* fallthrough */ |
1309 | default: | 1366 | default: |
1310 | if (filp->f_pos == 2) { | 1367 | if (filp->f_pos == 2) { |
1368 | spin_lock(&configfs_dirent_lock); | ||
1311 | list_move(q, &parent_sd->s_children); | 1369 | list_move(q, &parent_sd->s_children); |
1370 | spin_unlock(&configfs_dirent_lock); | ||
1312 | } | 1371 | } |
1313 | for (p=q->next; p!= &parent_sd->s_children; p=p->next) { | 1372 | for (p=q->next; p!= &parent_sd->s_children; p=p->next) { |
1314 | struct configfs_dirent *next; | 1373 | struct configfs_dirent *next; |
@@ -1331,7 +1390,9 @@ static int configfs_readdir(struct file * filp, void * dirent, filldir_t filldir | |||
1331 | dt_type(next)) < 0) | 1390 | dt_type(next)) < 0) |
1332 | return 0; | 1391 | return 0; |
1333 | 1392 | ||
1393 | spin_lock(&configfs_dirent_lock); | ||
1334 | list_move(q, p); | 1394 | list_move(q, p); |
1395 | spin_unlock(&configfs_dirent_lock); | ||
1335 | p = q; | 1396 | p = q; |
1336 | filp->f_pos++; | 1397 | filp->f_pos++; |
1337 | } | 1398 | } |
@@ -1362,6 +1423,7 @@ static loff_t configfs_dir_lseek(struct file * file, loff_t offset, int origin) | |||
1362 | struct list_head *p; | 1423 | struct list_head *p; |
1363 | loff_t n = file->f_pos - 2; | 1424 | loff_t n = file->f_pos - 2; |
1364 | 1425 | ||
1426 | spin_lock(&configfs_dirent_lock); | ||
1365 | list_del(&cursor->s_sibling); | 1427 | list_del(&cursor->s_sibling); |
1366 | p = sd->s_children.next; | 1428 | p = sd->s_children.next; |
1367 | while (n && p != &sd->s_children) { | 1429 | while (n && p != &sd->s_children) { |
@@ -1373,6 +1435,7 @@ static loff_t configfs_dir_lseek(struct file * file, loff_t offset, int origin) | |||
1373 | p = p->next; | 1435 | p = p->next; |
1374 | } | 1436 | } |
1375 | list_add_tail(&cursor->s_sibling, p); | 1437 | list_add_tail(&cursor->s_sibling, p); |
1438 | spin_unlock(&configfs_dirent_lock); | ||
1376 | } | 1439 | } |
1377 | } | 1440 | } |
1378 | mutex_unlock(&dentry->d_inode->i_mutex); | 1441 | mutex_unlock(&dentry->d_inode->i_mutex); |
@@ -1448,9 +1511,11 @@ void configfs_unregister_subsystem(struct configfs_subsystem *subsys) | |||
1448 | mutex_lock_nested(&configfs_sb->s_root->d_inode->i_mutex, | 1511 | mutex_lock_nested(&configfs_sb->s_root->d_inode->i_mutex, |
1449 | I_MUTEX_PARENT); | 1512 | I_MUTEX_PARENT); |
1450 | mutex_lock_nested(&dentry->d_inode->i_mutex, I_MUTEX_CHILD); | 1513 | mutex_lock_nested(&dentry->d_inode->i_mutex, I_MUTEX_CHILD); |
1451 | if (configfs_detach_prep(dentry)) { | 1514 | spin_lock(&configfs_dirent_lock); |
1515 | if (configfs_detach_prep(dentry, NULL)) { | ||
1452 | printk(KERN_ERR "configfs: Tried to unregister non-empty subsystem!\n"); | 1516 | printk(KERN_ERR "configfs: Tried to unregister non-empty subsystem!\n"); |
1453 | } | 1517 | } |
1518 | spin_unlock(&configfs_dirent_lock); | ||
1454 | configfs_detach_group(&group->cg_item); | 1519 | configfs_detach_group(&group->cg_item); |
1455 | dentry->d_inode->i_flags |= S_DEAD; | 1520 | dentry->d_inode->i_flags |= S_DEAD; |
1456 | mutex_unlock(&dentry->d_inode->i_mutex); | 1521 | mutex_unlock(&dentry->d_inode->i_mutex); |
diff --git a/fs/configfs/inode.c b/fs/configfs/inode.c index b9a1d810346d..4803ccc94480 100644 --- a/fs/configfs/inode.c +++ b/fs/configfs/inode.c | |||
@@ -247,7 +247,9 @@ void configfs_hash_and_remove(struct dentry * dir, const char * name) | |||
247 | if (!sd->s_element) | 247 | if (!sd->s_element) |
248 | continue; | 248 | continue; |
249 | if (!strcmp(configfs_get_name(sd), name)) { | 249 | if (!strcmp(configfs_get_name(sd), name)) { |
250 | spin_lock(&configfs_dirent_lock); | ||
250 | list_del_init(&sd->s_sibling); | 251 | list_del_init(&sd->s_sibling); |
252 | spin_unlock(&configfs_dirent_lock); | ||
251 | configfs_drop_dentry(sd, dir); | 253 | configfs_drop_dentry(sd, dir); |
252 | configfs_put(sd); | 254 | configfs_put(sd); |
253 | break; | 255 | break; |
diff --git a/fs/configfs/symlink.c b/fs/configfs/symlink.c index 2a731ef5f305..0004d18c40ac 100644 --- a/fs/configfs/symlink.c +++ b/fs/configfs/symlink.c | |||
@@ -77,12 +77,15 @@ static int create_link(struct config_item *parent_item, | |||
77 | sl = kmalloc(sizeof(struct configfs_symlink), GFP_KERNEL); | 77 | sl = kmalloc(sizeof(struct configfs_symlink), GFP_KERNEL); |
78 | if (sl) { | 78 | if (sl) { |
79 | sl->sl_target = config_item_get(item); | 79 | sl->sl_target = config_item_get(item); |
80 | /* FIXME: needs a lock, I'd bet */ | 80 | spin_lock(&configfs_dirent_lock); |
81 | list_add(&sl->sl_list, &target_sd->s_links); | 81 | list_add(&sl->sl_list, &target_sd->s_links); |
82 | spin_unlock(&configfs_dirent_lock); | ||
82 | ret = configfs_create_link(sl, parent_item->ci_dentry, | 83 | ret = configfs_create_link(sl, parent_item->ci_dentry, |
83 | dentry); | 84 | dentry); |
84 | if (ret) { | 85 | if (ret) { |
86 | spin_lock(&configfs_dirent_lock); | ||
85 | list_del_init(&sl->sl_list); | 87 | list_del_init(&sl->sl_list); |
88 | spin_unlock(&configfs_dirent_lock); | ||
86 | config_item_put(item); | 89 | config_item_put(item); |
87 | kfree(sl); | 90 | kfree(sl); |
88 | } | 91 | } |
@@ -137,8 +140,12 @@ int configfs_symlink(struct inode *dir, struct dentry *dentry, const char *symna | |||
137 | goto out_put; | 140 | goto out_put; |
138 | 141 | ||
139 | ret = type->ct_item_ops->allow_link(parent_item, target_item); | 142 | ret = type->ct_item_ops->allow_link(parent_item, target_item); |
140 | if (!ret) | 143 | if (!ret) { |
141 | ret = create_link(parent_item, target_item, dentry); | 144 | ret = create_link(parent_item, target_item, dentry); |
145 | if (ret && type->ct_item_ops->drop_link) | ||
146 | type->ct_item_ops->drop_link(parent_item, | ||
147 | target_item); | ||
148 | } | ||
142 | 149 | ||
143 | config_item_put(target_item); | 150 | config_item_put(target_item); |
144 | path_put(&nd.path); | 151 | path_put(&nd.path); |
@@ -169,7 +176,9 @@ int configfs_unlink(struct inode *dir, struct dentry *dentry) | |||
169 | parent_item = configfs_get_config_item(dentry->d_parent); | 176 | parent_item = configfs_get_config_item(dentry->d_parent); |
170 | type = parent_item->ci_type; | 177 | type = parent_item->ci_type; |
171 | 178 | ||
179 | spin_lock(&configfs_dirent_lock); | ||
172 | list_del_init(&sd->s_sibling); | 180 | list_del_init(&sd->s_sibling); |
181 | spin_unlock(&configfs_dirent_lock); | ||
173 | configfs_drop_dentry(sd, dentry->d_parent); | 182 | configfs_drop_dentry(sd, dentry->d_parent); |
174 | dput(dentry); | 183 | dput(dentry); |
175 | configfs_put(sd); | 184 | configfs_put(sd); |
@@ -184,8 +193,9 @@ int configfs_unlink(struct inode *dir, struct dentry *dentry) | |||
184 | type->ct_item_ops->drop_link(parent_item, | 193 | type->ct_item_ops->drop_link(parent_item, |
185 | sl->sl_target); | 194 | sl->sl_target); |
186 | 195 | ||
187 | /* FIXME: Needs lock */ | 196 | spin_lock(&configfs_dirent_lock); |
188 | list_del_init(&sl->sl_list); | 197 | list_del_init(&sl->sl_list); |
198 | spin_unlock(&configfs_dirent_lock); | ||
189 | 199 | ||
190 | /* Put reference from create_link() */ | 200 | /* Put reference from create_link() */ |
191 | config_item_put(sl->sl_target); | 201 | config_item_put(sl->sl_target); |