diff options
| author | Maarten Lankhorst <maarten.lankhorst@linux.intel.com> | 2019-08-12 15:14:03 +0200 | 
|---|---|---|
| committer | Maarten Lankhorst <maarten.lankhorst@linux.intel.com> | 2019-08-12 15:14:03 +0200 | 
| commit | 181ae8844578d0a80f188c1d195fd6bb91bcec81 (patch) | |
| tree | bd6ebfb8eb390ea6927603ca4e33c263c82b2cd7 /drivers/base/core.c | |
| parent | 8f1c748b9a7751ee1297b4880788a09f7c802eb4 (diff) | |
| parent | d45331b00ddb179e291766617259261c112db872 (diff) | |
Merge remote-tracking branch 'drm/drm-fixes' into drm-misc-fixes
Backport requested for omap dma mask fix. I'm not sure it still
requires it, but just in case. :)
Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
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);  | 
