Skip to content

Commit cebf8fd

Browse files
Ming Leigregkh
authored andcommitted
driver core: fix race between creating/querying glue dir and its cleanup
The global mutex of 'gdp_mutex' is used to serialize creating/querying glue dir and its cleanup. Turns out it isn't a perfect way because part(kobj_kset_leave()) of the actual cleanup action() is done inside the release handler of the glue dir kobject. That means gdp_mutex has to be held before releasing the last reference count of the glue dir kobject. This patch moves glue dir's cleanup after kobject_del() in device_del() for avoiding the race. Cc: Yijing Wang <[email protected]> Reported-by: Chandra Sekhar Lingutla <[email protected]> Signed-off-by: Ming Lei <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 24ef5f3 commit cebf8fd

File tree

1 file changed

+29
-10
lines changed

1 file changed

+29
-10
lines changed

drivers/base/core.c

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -836,23 +836,36 @@ static struct kobject *get_device_parent(struct device *dev,
836836
return NULL;
837837
}
838838

839+
static inline bool live_in_glue_dir(struct kobject *kobj,
840+
struct device *dev)
841+
{
842+
if (!kobj || !dev->class ||
843+
kobj->kset != &dev->class->p->glue_dirs)
844+
return false;
845+
return true;
846+
}
847+
848+
static inline struct kobject *get_glue_dir(struct device *dev)
849+
{
850+
return dev->kobj.parent;
851+
}
852+
853+
/*
854+
* make sure cleaning up dir as the last step, we need to make
855+
* sure .release handler of kobject is run with holding the
856+
* global lock
857+
*/
839858
static void cleanup_glue_dir(struct device *dev, struct kobject *glue_dir)
840859
{
841860
/* see if we live in a "glue" directory */
842-
if (!glue_dir || !dev->class ||
843-
glue_dir->kset != &dev->class->p->glue_dirs)
861+
if (!live_in_glue_dir(glue_dir, dev))
844862
return;
845863

846864
mutex_lock(&gdp_mutex);
847865
kobject_put(glue_dir);
848866
mutex_unlock(&gdp_mutex);
849867
}
850868

851-
static void cleanup_device_parent(struct device *dev)
852-
{
853-
cleanup_glue_dir(dev, dev->kobj.parent);
854-
}
855-
856869
static int device_add_class_symlinks(struct device *dev)
857870
{
858871
struct device_node *of_node = dev_of_node(dev);
@@ -1028,6 +1041,7 @@ int device_add(struct device *dev)
10281041
struct kobject *kobj;
10291042
struct class_interface *class_intf;
10301043
int error = -EINVAL;
1044+
struct kobject *glue_dir = NULL;
10311045

10321046
dev = get_device(dev);
10331047
if (!dev)
@@ -1072,8 +1086,10 @@ int device_add(struct device *dev)
10721086
/* first, register with generic layer. */
10731087
/* we require the name to be set before, and pass NULL */
10741088
error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
1075-
if (error)
1089+
if (error) {
1090+
glue_dir = get_glue_dir(dev);
10761091
goto Error;
1092+
}
10771093

10781094
/* notify platform of device entry */
10791095
if (platform_notify)
@@ -1154,9 +1170,10 @@ int device_add(struct device *dev)
11541170
device_remove_file(dev, &dev_attr_uevent);
11551171
attrError:
11561172
kobject_uevent(&dev->kobj, KOBJ_REMOVE);
1173+
glue_dir = get_glue_dir(dev);
11571174
kobject_del(&dev->kobj);
11581175
Error:
1159-
cleanup_device_parent(dev);
1176+
cleanup_glue_dir(dev, glue_dir);
11601177
put_device(parent);
11611178
name_error:
11621179
kfree(dev->p);
@@ -1232,6 +1249,7 @@ EXPORT_SYMBOL_GPL(put_device);
12321249
void device_del(struct device *dev)
12331250
{
12341251
struct device *parent = dev->parent;
1252+
struct kobject *glue_dir = NULL;
12351253
struct class_interface *class_intf;
12361254

12371255
/* Notify clients of device removal. This call must come
@@ -1276,8 +1294,9 @@ void device_del(struct device *dev)
12761294
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
12771295
BUS_NOTIFY_REMOVED_DEVICE, dev);
12781296
kobject_uevent(&dev->kobj, KOBJ_REMOVE);
1279-
cleanup_device_parent(dev);
1297+
glue_dir = get_glue_dir(dev);
12801298
kobject_del(&dev->kobj);
1299+
cleanup_glue_dir(dev, glue_dir);
12811300
put_device(parent);
12821301
}
12831302
EXPORT_SYMBOL_GPL(device_del);

0 commit comments

Comments
 (0)