aboutsummaryrefslogtreecommitdiffstats
path: root/fs/sysfs/dir.c
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2013-09-18 17:15:37 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-10-03 19:38:52 -0400
commitbcdde7e221a8750f9b62b6d0bd31b72ea4ad9309 (patch)
treeb86620a59f4629804fc213430c2d2446d78f68b8 /fs/sysfs/dir.c
parent26ea12dec0c84133add937455be76d44fe253d85 (diff)
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 <tj@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs/sysfs/dir.c')
-rw-r--r--fs/sysfs/dir.c75
1 files changed, 64 insertions, 11 deletions
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
index 105a7e2d1660..0cdfd8128d3e 100644
--- a/fs/sysfs/dir.c
+++ b/fs/sysfs/dir.c
@@ -789,27 +789,81 @@ void sysfs_remove_subdir(struct sysfs_dirent *sd)
789 remove_dir(sd); 789 remove_dir(sd);
790} 790}
791 791
792static 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 */
823static 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}
792 846
793static void __sysfs_remove_dir(struct sysfs_dirent *dir_sd) 847static void __sysfs_remove_dir(struct sysfs_dirent *dir_sd)
794{ 848{
795 struct sysfs_addrm_cxt acxt; 849 struct sysfs_addrm_cxt acxt;
796 struct rb_node *pos; 850 struct sysfs_dirent *pos, *next;
797 851
798 if (!dir_sd) 852 if (!dir_sd)
799 return; 853 return;
800 854
801 pr_debug("sysfs %s: removing dir\n", dir_sd->s_name); 855 pr_debug("sysfs %s: removing dir\n", dir_sd->s_name);
802 sysfs_addrm_start(&acxt); 856 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);
811 857
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);
813} 867}
814 868
815/** 869/**
@@ -820,7 +874,6 @@ static void __sysfs_remove_dir(struct sysfs_dirent *dir_sd)
820 * the directory before we remove the directory, and we've inlined 874 * the directory before we remove the directory, and we've inlined
821 * what used to be sysfs_rmdir() below, instead of calling separately. 875 * what used to be sysfs_rmdir() below, instead of calling separately.
822 */ 876 */
823
824void sysfs_remove_dir(struct kobject *kobj) 877void sysfs_remove_dir(struct kobject *kobj)
825{ 878{
826 struct sysfs_dirent *sd = kobj->sd; 879 struct sysfs_dirent *sd = kobj->sd;