diff options
-rw-r--r-- | fs/sysfs/dir.c | 7 | ||||
-rw-r--r-- | lib/kobject.c | 12 |
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); |