diff options
| -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); |
