diff options
author | Louis Rilling <Louis.Rilling@kerlabs.com> | 2008-06-16 13:01:01 -0400 |
---|---|---|
committer | Mark Fasheh <mfasheh@suse.com> | 2008-07-14 16:57:16 -0400 |
commit | b3e76af87441fc36eef3516d73ab2314e7b2d911 (patch) | |
tree | 36c6c6a21ac16b609302845a7dcb91749a972e4c /fs | |
parent | 107ed40bd070df5e4a0a012042c45c40963dc574 (diff) |
configfs: Fix deadlock with racing rmdir() and rename()
This patch fixes the deadlock between racing sys_rename() and configfs_rmdir().
The idea is to avoid locking i_mutexes of default groups in
configfs_detach_prep(), and rely instead on the new configfs_dirent_lock to
protect against configfs_dirent's linkage mutations. To ensure that an mkdir()
racing with rmdir() will not create new items in a to-be-removed default group,
we make configfs_new_dirent() check for the CONFIGFS_USET_DROPPING flag right
before linking the new dirent, and return error if the flag is set. This makes
racing mkdir()/symlink()/dir_open() fail in places where errors could already
happen, resp. in (attach_item()|attach_group())/create_link()/new_dirent().
configfs_depend() remains safe since it locks all the path from configfs root,
and is thus mutually exclusive with rmdir().
An advantage of this is that now detach_groups() unconditionnaly takes the
default groups i_mutex, which makes it more consistent with populate_groups().
Signed-off-by: Louis Rilling <Louis.Rilling@kerlabs.com>
Signed-off-by: Joel Becker <joel.becker@oracle.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/configfs/dir.c | 45 |
1 files changed, 25 insertions, 20 deletions
diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index 918a332babfe..d5b5985716ba 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c | |||
@@ -43,6 +43,10 @@ DECLARE_RWSEM(configfs_rename_sem); | |||
43 | * and configfs_dirent_lock locked, in that order. | 43 | * and configfs_dirent_lock locked, in that order. |
44 | * This allows one to safely traverse configfs_dirent trees and symlinks without | 44 | * This allows one to safely traverse configfs_dirent trees and symlinks without |
45 | * having to lock inodes. | 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() | ||
46 | */ | 50 | */ |
47 | DEFINE_SPINLOCK(configfs_dirent_lock); | 51 | DEFINE_SPINLOCK(configfs_dirent_lock); |
48 | 52 | ||
@@ -91,6 +95,11 @@ static struct configfs_dirent *configfs_new_dirent(struct configfs_dirent * pare | |||
91 | INIT_LIST_HEAD(&sd->s_children); | 95 | INIT_LIST_HEAD(&sd->s_children); |
92 | sd->s_element = element; | 96 | sd->s_element = element; |
93 | spin_lock(&configfs_dirent_lock); | 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 | } | ||
94 | list_add(&sd->s_sibling, &parent_sd->s_children); | 103 | list_add(&sd->s_sibling, &parent_sd->s_children); |
95 | spin_unlock(&configfs_dirent_lock); | 104 | spin_unlock(&configfs_dirent_lock); |
96 | 105 | ||
@@ -349,11 +358,11 @@ static struct dentry * configfs_lookup(struct inode *dir, | |||
349 | 358 | ||
350 | /* | 359 | /* |
351 | * Only subdirectories count here. Files (CONFIGFS_NOT_PINNED) are | 360 | * Only subdirectories count here. Files (CONFIGFS_NOT_PINNED) are |
352 | * attributes and are removed by rmdir(). We recurse, taking i_mutex | 361 | * attributes and are removed by rmdir(). We recurse, setting |
353 | * on all children that are candidates for default detach. If the | 362 | * CONFIGFS_USET_DROPPING on all children that are candidates for |
354 | * result is clean, then configfs_detach_group() will handle dropping | 363 | * default detach. |
355 | * 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 |
356 | * holders via configfs_detach_rollback(). | 365 | * configfs_detach_rollback(). |
357 | */ | 366 | */ |
358 | static int configfs_detach_prep(struct dentry *dentry) | 367 | static int configfs_detach_prep(struct dentry *dentry) |
359 | { | 368 | { |
@@ -370,8 +379,7 @@ static int configfs_detach_prep(struct dentry *dentry) | |||
370 | if (sd->s_type & CONFIGFS_NOT_PINNED) | 379 | if (sd->s_type & CONFIGFS_NOT_PINNED) |
371 | continue; | 380 | continue; |
372 | if (sd->s_type & CONFIGFS_USET_DEFAULT) { | 381 | if (sd->s_type & CONFIGFS_USET_DEFAULT) { |
373 | mutex_lock(&sd->s_dentry->d_inode->i_mutex); | 382 | /* Mark that we're trying to drop the group */ |
374 | /* Mark that we've taken i_mutex */ | ||
375 | sd->s_type |= CONFIGFS_USET_DROPPING; | 383 | sd->s_type |= CONFIGFS_USET_DROPPING; |
376 | 384 | ||
377 | /* | 385 | /* |
@@ -392,7 +400,7 @@ out: | |||
392 | } | 400 | } |
393 | 401 | ||
394 | /* | 402 | /* |
395 | * Walk the tree, dropping i_mutex wherever CONFIGFS_USET_DROPPING is | 403 | * Walk the tree, resetting CONFIGFS_USET_DROPPING wherever it was |
396 | * set. | 404 | * set. |
397 | */ | 405 | */ |
398 | static void configfs_detach_rollback(struct dentry *dentry) | 406 | static void configfs_detach_rollback(struct dentry *dentry) |
@@ -403,11 +411,7 @@ static void configfs_detach_rollback(struct dentry *dentry) | |||
403 | list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { | 411 | list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { |
404 | if (sd->s_type & CONFIGFS_USET_DEFAULT) { | 412 | if (sd->s_type & CONFIGFS_USET_DEFAULT) { |
405 | configfs_detach_rollback(sd->s_dentry); | 413 | configfs_detach_rollback(sd->s_dentry); |
406 | 414 | sd->s_type &= ~CONFIGFS_USET_DROPPING; | |
407 | if (sd->s_type & CONFIGFS_USET_DROPPING) { | ||
408 | sd->s_type &= ~CONFIGFS_USET_DROPPING; | ||
409 | mutex_unlock(&sd->s_dentry->d_inode->i_mutex); | ||
410 | } | ||
411 | } | 415 | } |
412 | } | 416 | } |
413 | } | 417 | } |
@@ -486,16 +490,12 @@ static void detach_groups(struct config_group *group) | |||
486 | 490 | ||
487 | child = sd->s_dentry; | 491 | child = sd->s_dentry; |
488 | 492 | ||
493 | mutex_lock(&child->d_inode->i_mutex); | ||
494 | |||
489 | configfs_detach_group(sd->s_element); | 495 | configfs_detach_group(sd->s_element); |
490 | child->d_inode->i_flags |= S_DEAD; | 496 | child->d_inode->i_flags |= S_DEAD; |
491 | 497 | ||
492 | /* | 498 | mutex_unlock(&child->d_inode->i_mutex); |
493 | * From rmdir/unregister, a configfs_detach_prep() pass | ||
494 | * has taken our i_mutex for us. Drop it. | ||
495 | * From mkdir/register cleanup, there is no sem held. | ||
496 | */ | ||
497 | if (sd->s_type & CONFIGFS_USET_DROPPING) | ||
498 | mutex_unlock(&child->d_inode->i_mutex); | ||
499 | 499 | ||
500 | d_delete(child); | 500 | d_delete(child); |
501 | dput(child); | 501 | dput(child); |
@@ -1181,12 +1181,15 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry) | |||
1181 | return -EINVAL; | 1181 | return -EINVAL; |
1182 | } | 1182 | } |
1183 | 1183 | ||
1184 | spin_lock(&configfs_dirent_lock); | ||
1184 | ret = configfs_detach_prep(dentry); | 1185 | ret = configfs_detach_prep(dentry); |
1185 | if (ret) { | 1186 | if (ret) { |
1186 | configfs_detach_rollback(dentry); | 1187 | configfs_detach_rollback(dentry); |
1188 | spin_unlock(&configfs_dirent_lock); | ||
1187 | config_item_put(parent_item); | 1189 | config_item_put(parent_item); |
1188 | return ret; | 1190 | return ret; |
1189 | } | 1191 | } |
1192 | spin_unlock(&configfs_dirent_lock); | ||
1190 | 1193 | ||
1191 | /* Get a working ref for the duration of this function */ | 1194 | /* Get a working ref for the duration of this function */ |
1192 | item = configfs_get_config_item(dentry); | 1195 | item = configfs_get_config_item(dentry); |
@@ -1476,9 +1479,11 @@ void configfs_unregister_subsystem(struct configfs_subsystem *subsys) | |||
1476 | mutex_lock_nested(&configfs_sb->s_root->d_inode->i_mutex, | 1479 | mutex_lock_nested(&configfs_sb->s_root->d_inode->i_mutex, |
1477 | I_MUTEX_PARENT); | 1480 | I_MUTEX_PARENT); |
1478 | mutex_lock_nested(&dentry->d_inode->i_mutex, I_MUTEX_CHILD); | 1481 | mutex_lock_nested(&dentry->d_inode->i_mutex, I_MUTEX_CHILD); |
1482 | spin_lock(&configfs_dirent_lock); | ||
1479 | if (configfs_detach_prep(dentry)) { | 1483 | if (configfs_detach_prep(dentry)) { |
1480 | printk(KERN_ERR "configfs: Tried to unregister non-empty subsystem!\n"); | 1484 | printk(KERN_ERR "configfs: Tried to unregister non-empty subsystem!\n"); |
1481 | } | 1485 | } |
1486 | spin_unlock(&configfs_dirent_lock); | ||
1482 | configfs_detach_group(&group->cg_item); | 1487 | configfs_detach_group(&group->cg_item); |
1483 | dentry->d_inode->i_flags |= S_DEAD; | 1488 | dentry->d_inode->i_flags |= S_DEAD; |
1484 | mutex_unlock(&dentry->d_inode->i_mutex); | 1489 | mutex_unlock(&dentry->d_inode->i_mutex); |