configfs: fix a race in configfs_{,un}register_subsystem()
When configfs_register_subsystem() or configfs_unregister_subsystem() is executing link_group() or unlink_group(), it is possible that two processes add or delete list concurrently. Some unfortunate interleavings of them can cause kernel panic. One of cases is: A --> B --> C --> D A <-- B <-- C <-- D delete list_head *B | delete list_head *C --------------------------------|----------------------------------- configfs_unregister_subsystem | configfs_unregister_subsystem unlink_group | unlink_group unlink_obj | unlink_obj list_del_init | list_del_init __list_del_entry | __list_del_entry __list_del | __list_del // next == C | next->prev = prev | | next->prev = prev prev->next = next | | // prev == B | prev->next = next Fix this by adding mutex when calling link_group() or unlink_group(), but parent configfs_subsystem is NULL when config_item is root. So I create a mutex configfs_subsystem_mutex. Fixes: 7063fbf22611 ("[PATCH] configfs: User-driven configuration filesystem") Signed-off-by: ChenXiaoSong <chenxiaosong2@huawei.com> Signed-off-by: Laibin Qiu <qiulaibin@huawei.com> Signed-off-by: Christoph Hellwig <hch@lst.de>
This commit is contained in:
parent
038101e6b2
commit
84ec758fb2
@ -34,6 +34,14 @@
|
||||
*/
|
||||
DEFINE_SPINLOCK(configfs_dirent_lock);
|
||||
|
||||
/*
|
||||
* All of link_obj/unlink_obj/link_group/unlink_group require that
|
||||
* subsys->su_mutex is held.
|
||||
* But parent configfs_subsystem is NULL when config_item is root.
|
||||
* Use this mutex when config_item is root.
|
||||
*/
|
||||
static DEFINE_MUTEX(configfs_subsystem_mutex);
|
||||
|
||||
static void configfs_d_iput(struct dentry * dentry,
|
||||
struct inode * inode)
|
||||
{
|
||||
@ -1859,7 +1867,9 @@ int configfs_register_subsystem(struct configfs_subsystem *subsys)
|
||||
group->cg_item.ci_name = group->cg_item.ci_namebuf;
|
||||
|
||||
sd = root->d_fsdata;
|
||||
mutex_lock(&configfs_subsystem_mutex);
|
||||
link_group(to_config_group(sd->s_element), group);
|
||||
mutex_unlock(&configfs_subsystem_mutex);
|
||||
|
||||
inode_lock_nested(d_inode(root), I_MUTEX_PARENT);
|
||||
|
||||
@ -1884,7 +1894,9 @@ int configfs_register_subsystem(struct configfs_subsystem *subsys)
|
||||
inode_unlock(d_inode(root));
|
||||
|
||||
if (err) {
|
||||
mutex_lock(&configfs_subsystem_mutex);
|
||||
unlink_group(group);
|
||||
mutex_unlock(&configfs_subsystem_mutex);
|
||||
configfs_release_fs();
|
||||
}
|
||||
put_fragment(frag);
|
||||
@ -1931,7 +1943,9 @@ void configfs_unregister_subsystem(struct configfs_subsystem *subsys)
|
||||
|
||||
dput(dentry);
|
||||
|
||||
mutex_lock(&configfs_subsystem_mutex);
|
||||
unlink_group(group);
|
||||
mutex_unlock(&configfs_subsystem_mutex);
|
||||
configfs_release_fs();
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user