summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/sysfs/dir.c7
-rw-r--r--lib/kobject.c12
2 files changed, 18 insertions, 1 deletions
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
index 671868914b5b..105a7e2d1660 100644
--- a/fs/sysfs/dir.c
+++ b/fs/sysfs/dir.c
@@ -549,7 +549,12 @@ void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
{
struct sysfs_inode_attrs *ps_iattr;
- BUG_ON(sd->s_flags & SYSFS_FLAG_REMOVED);
+ /*
+ * Removal can be called multiple times on the same node. Only the
+ * first invocation is effective and puts the base ref.
+ */
+ if (sd->s_flags & SYSFS_FLAG_REMOVED)
+ return;
sysfs_unlink_sibling(sd);
diff --git a/lib/kobject.c b/lib/kobject.c
index 151089788c21..2fdf7fa9e9bd 100644
--- a/lib/kobject.c
+++ b/lib/kobject.c
@@ -76,6 +76,13 @@ static int create_dir(struct kobject *kobj)
if (error)
sysfs_remove_dir(kobj);
}
+
+ /*
+ * @kobj->sd may be deleted by an ancestor going away. Hold an
+ * extra reference so that it stays until @kobj is gone.
+ */
+ sysfs_get(kobj->sd);
+
return error;
}
@@ -532,10 +539,15 @@ out:
*/
void kobject_del(struct kobject *kobj)
{
+ struct sysfs_dirent *sd;
+
if (!kobj)
return;
+ sd = kobj->sd;
sysfs_remove_dir(kobj);
+ sysfs_put(sd);
+
kobj->state_in_sysfs = 0;
kobj_kset_leave(kobj);
kobject_put(kobj->parent);