From: Eric W. Biederman <ebiederm@xxxxxxxxxxxx> Modify sysfs to properly remove directories containing attributes and subdirectories. The code is relatively simple and means we don't have to worry about what might use this logic. In a quick survey I have only found /sys/dev/char and /sys/dev/block that are removing non-enmpty directories today (and they are exclusively filled with symlinks). So only removing empty directories does not appear to be an option. I don't hold sysfs_mutex across the entire operation as that is unneeded for coherence at the sysfs level and some level of coordination is expected at the upper layers. Signed-off-by: Eric W. Biederman <ebiederm@xxxxxxxxxxxxxxxxxx> --- fs/sysfs/dir.c | 54 ++++++++++++++++++++++++++---------------------------- 1 files changed, 26 insertions(+), 28 deletions(-) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index b95cc07..50702b3 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -732,43 +732,41 @@ const struct inode_operations sysfs_dir_inode_operations = { .setattr = sysfs_setattr, }; -static void remove_dir(struct sysfs_dirent *sd) +static struct sysfs_dirent *sysfs_get_one(struct sysfs_dirent *dir_sd) { - struct sysfs_addrm_cxt acxt; - - sysfs_addrm_start(&acxt, sd->s_parent); - sysfs_remove_one(&acxt, sd); - sysfs_addrm_finish(&acxt); -} - -void sysfs_remove_subdir(struct sysfs_dirent *sd) -{ - remove_dir(sd); + struct sysfs_dirent *sd = dir_sd; + mutex_lock(&sysfs_mutex); + while ((sysfs_type(sd) == SYSFS_DIR) && sd->s_dir.children) + sd = sd->s_dir.children; + if (sd != dir_sd) + sysfs_get(sd); + else + sd = NULL; + mutex_unlock(&sysfs_mutex); + return sd; } - -static void __sysfs_remove_dir(struct sysfs_dirent *dir_sd) +static void remove_dir(struct sysfs_dirent *dir_sd) { struct sysfs_addrm_cxt acxt; - struct sysfs_dirent **pos; - - if (!dir_sd) - return; + struct sysfs_dirent *sd; pr_debug("sysfs %s: removing dir\n", dir_sd->s_name); - sysfs_addrm_start(&acxt, dir_sd); - pos = &dir_sd->s_dir.children; - while (*pos) { - struct sysfs_dirent *sd = *pos; - if (sysfs_type(sd) != SYSFS_DIR) - sysfs_remove_one(&acxt, sd); - else - pos = &(*pos)->s_sibling; + while ((sd = sysfs_get_one(dir_sd))) { + sysfs_addrm_start(&acxt, sd->s_parent); + sysfs_remove_one(&acxt, sd); + sysfs_addrm_finish(&acxt); + sysfs_put(sd); } + sysfs_addrm_start(&acxt, dir_sd->s_parent); + sysfs_remove_one(&acxt, dir_sd); sysfs_addrm_finish(&acxt); +} - remove_dir(dir_sd); +void sysfs_remove_subdir(struct sysfs_dirent *sd) +{ + remove_dir(sd); } /** @@ -788,7 +786,7 @@ void sysfs_remove_dir(struct kobject * kobj) kobj->sd = NULL; spin_unlock(&sysfs_assoc_lock); - __sysfs_remove_dir(sd); + remove_dir(sd); } int sysfs_rename_dir(struct kobject * kobj, const char *new_name) -- 1.6.1.2.350.g88cc -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html