diff options
| author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2019-08-12 07:33:23 +0200 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2019-08-12 07:33:23 +0200 |
| commit | 77e38c19f373c077712e60cd190ca9cba13b7177 (patch) | |
| tree | c7a743735cffdcaee26ca559888196d229c4ec1e /drivers/base/core.c | |
| parent | cec2caedd979e8778b61ca7dd15fa7ec8fcf7162 (diff) | |
| parent | d45331b00ddb179e291766617259261c112db872 (diff) | |
| download | lwn-77e38c19f373c077712e60cd190ca9cba13b7177.tar.gz lwn-77e38c19f373c077712e60cd190ca9cba13b7177.zip | |
Merge 5.3-rc4 into char-misc-next
We need the char-misc fixes in here as well.
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/base/core.c')
| -rw-r--r-- | drivers/base/core.c | 53 |
1 files changed, 52 insertions, 1 deletions
diff --git a/drivers/base/core.c b/drivers/base/core.c index 636058bbf48a..1669d41fcddc 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1823,12 +1823,63 @@ static inline struct kobject *get_glue_dir(struct device *dev) */ static void cleanup_glue_dir(struct device *dev, struct kobject *glue_dir) { + unsigned int ref; + /* see if we live in a "glue" directory */ if (!live_in_glue_dir(glue_dir, dev)) return; mutex_lock(&gdp_mutex); - if (!kobject_has_children(glue_dir)) + /** + * There is a race condition between removing glue directory + * and adding a new device under the glue directory. + * + * CPU1: CPU2: + * + * device_add() + * get_device_parent() + * class_dir_create_and_add() + * kobject_add_internal() + * create_dir() // create glue_dir + * + * device_add() + * get_device_parent() + * kobject_get() // get glue_dir + * + * device_del() + * cleanup_glue_dir() + * kobject_del(glue_dir) + * + * kobject_add() + * kobject_add_internal() + * create_dir() // in glue_dir + * sysfs_create_dir_ns() + * kernfs_create_dir_ns(sd) + * + * sysfs_remove_dir() // glue_dir->sd=NULL + * sysfs_put() // free glue_dir->sd + * + * // sd is freed + * kernfs_new_node(sd) + * kernfs_get(glue_dir) + * kernfs_add_one() + * kernfs_put() + * + * Before CPU1 remove last child device under glue dir, if CPU2 add + * a new device under glue dir, the glue_dir kobject reference count + * will be increase to 2 in kobject_get(k). And CPU2 has been called + * kernfs_create_dir_ns(). Meanwhile, CPU1 call sysfs_remove_dir() + * and sysfs_put(). This result in glue_dir->sd is freed. + * + * Then the CPU2 will see a stale "empty" but still potentially used + * glue dir around in kernfs_new_node(). + * + * In order to avoid this happening, we also should make sure that + * kernfs_node for glue_dir is released in CPU1 only when refcount + * for glue_dir kobj is 1. + */ + ref = kref_read(&glue_dir->kref); + if (!kobject_has_children(glue_dir) && !--ref) kobject_del(glue_dir); kobject_put(glue_dir); mutex_unlock(&gdp_mutex); |
