Skip to content

Commit bcdde7e

Browse files
htejungregkh
authored andcommitted
sysfs: make __sysfs_remove_dir() recursive
Currently, sysfs directory removal is inconsistent in that it would remove any files directly under it but wouldn't recurse into directories. Thanks to group subdirectories, this doesn't even match with kobject boundaries. sysfs is in the process of being separated out so that it can be used by multiple subsystems and we want to have a consistent behavior - either removal of a sysfs_dirent should remove every descendant entries or none instead of something inbetween. This patch implements proper recursive removal in __sysfs_remove_dir(). The function now walks its subtree in a post-order walk to remove all descendants. This is a behavior change but kobject / driver layer, which currently is the only consumer, has already been updated to handle duplicate removal attempts, so nothing should be broken after this change. Signed-off-by: Tejun Heo <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 26ea12d commit bcdde7e

File tree

1 file changed

+64
-11
lines changed

1 file changed

+64
-11
lines changed

fs/sysfs/dir.c

Lines changed: 64 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -789,27 +789,81 @@ void sysfs_remove_subdir(struct sysfs_dirent *sd)
789789
remove_dir(sd);
790790
}
791791

792+
static struct sysfs_dirent *sysfs_leftmost_descendant(struct sysfs_dirent *pos)
793+
{
794+
struct sysfs_dirent *last;
795+
796+
while (true) {
797+
struct rb_node *rbn;
798+
799+
last = pos;
800+
801+
if (sysfs_type(pos) != SYSFS_DIR)
802+
break;
803+
804+
rbn = rb_first(&pos->s_dir.children);
805+
if (!rbn)
806+
break;
807+
808+
pos = to_sysfs_dirent(rbn);
809+
}
810+
811+
return last;
812+
}
813+
814+
/**
815+
* sysfs_next_descendant_post - find the next descendant for post-order walk
816+
* @pos: the current position (%NULL to initiate traversal)
817+
* @root: sysfs_dirent whose descendants to walk
818+
*
819+
* Find the next descendant to visit for post-order traversal of @root's
820+
* descendants. @root is included in the iteration and the last node to be
821+
* visited.
822+
*/
823+
static struct sysfs_dirent *sysfs_next_descendant_post(struct sysfs_dirent *pos,
824+
struct sysfs_dirent *root)
825+
{
826+
struct rb_node *rbn;
827+
828+
lockdep_assert_held(&sysfs_mutex);
829+
830+
/* if first iteration, visit leftmost descendant which may be root */
831+
if (!pos)
832+
return sysfs_leftmost_descendant(root);
833+
834+
/* if we visited @root, we're done */
835+
if (pos == root)
836+
return NULL;
837+
838+
/* if there's an unvisited sibling, visit its leftmost descendant */
839+
rbn = rb_next(&pos->s_rb);
840+
if (rbn)
841+
return sysfs_leftmost_descendant(to_sysfs_dirent(rbn));
842+
843+
/* no sibling left, visit parent */
844+
return pos->s_parent;
845+
}
792846

793847
static void __sysfs_remove_dir(struct sysfs_dirent *dir_sd)
794848
{
795849
struct sysfs_addrm_cxt acxt;
796-
struct rb_node *pos;
850+
struct sysfs_dirent *pos, *next;
797851

798852
if (!dir_sd)
799853
return;
800854

801855
pr_debug("sysfs %s: removing dir\n", dir_sd->s_name);
802856
sysfs_addrm_start(&acxt);
803-
pos = rb_first(&dir_sd->s_dir.children);
804-
while (pos) {
805-
struct sysfs_dirent *sd = to_sysfs_dirent(pos);
806-
pos = rb_next(pos);
807-
if (sysfs_type(sd) != SYSFS_DIR)
808-
sysfs_remove_one(&acxt, sd);
809-
}
810-
sysfs_addrm_finish(&acxt);
811857

812-
remove_dir(dir_sd);
858+
next = NULL;
859+
do {
860+
pos = next;
861+
next = sysfs_next_descendant_post(pos, dir_sd);
862+
if (pos)
863+
sysfs_remove_one(&acxt, pos);
864+
} while (next);
865+
866+
sysfs_addrm_finish(&acxt);
813867
}
814868

815869
/**
@@ -820,7 +874,6 @@ static void __sysfs_remove_dir(struct sysfs_dirent *dir_sd)
820874
* the directory before we remove the directory, and we've inlined
821875
* what used to be sysfs_rmdir() below, instead of calling separately.
822876
*/
823-
824877
void sysfs_remove_dir(struct kobject *kobj)
825878
{
826879
struct sysfs_dirent *sd = kobj->sd;

0 commit comments

Comments
 (0)