Re: [PATCH 3/3] drm/exynos: move Exynos platform drivers registration to init
On 2014년 11월 21일 08:42, Gustavo Padovan wrote: From: Gustavo Padovan gustavo.pado...@collabora.co.uk Registering the Exynos DRM subdevices platform drivers in the probe function is causing an infinite loop. Fix this by moving it to the exynos_drm_init() function to register the drivers on module init. Registering drivers in the probe functions causes a deadlock in the parent device lock. See Grant Likely explanation on the topic: I think the problem is that exynos_drm_init() is registering a normal (non-OF) platform device, so the parent will be /sys/devices/platform. It immediately gets bound against exynos_drm_platform_driver which calls the exynos drm_platform_probe() hook. The driver core obtains device_lock() on the device *and on the device parent*. Inside the probe hook, additional platform_drivers get registered. Each time one does, it tries to bind against every platform device in the system, which includes the ones created by OF. When it attempts to bind, it obtains device_lock() on the device *and on the device parent*. Before the change to move of-generated platform devices into /sys/devices/platform, the devices had different parents. Now both devices have /sys/devices/platform as the parent, so yes they are going to deadlock. The real problem is registering drivers from within a probe hook. That is completely wrong for the above deadlock reason. __driver_attach() will deadlock. Those registrations must be pulled out of .probe(). Registering devices in .probe() is okay because __device_attach() doesn't try to obtain device_lock() on the parent. INFO: task swapper/0:1 blocked for more than 120 seconds. Not tainted 3.18.0-rc3-next-20141105 #794 echo 0 /proc/sys/kernel/hung_task_timeout_secs disables this message. swapper/0 D c052534c 0 1 0 0x [c052534c] (__schedule) from [c0525b34] (schedule_preempt_disabled+0x14/0x20) [c0525b34] (schedule_preempt_disabled) from [c0526d44] (mutex_lock_nested+0x1c4/0x464 [c0526d44] (mutex_lock_nested) from [c02be908] (__driver_attach+0x48/0x98) [c02be908] (__driver_attach) from [c02bcc00] (bus_for_each_dev+0x54/0x88) [c02bcc00] (bus_for_each_dev) from [c02bdce0] (bus_add_driver+0xe4/0x200) [c02bdce0] (bus_add_driver) from [c02bef94] (driver_register+0x78/0xf4) [c02bef94] (driver_register) from [c029e99c] (exynos_drm_platform_probe+0x34/0x234) [c029e99c] (exynos_drm_platform_probe) from [c02bfcf0] (platform_drv_probe+0x48/0xa4) [c02bfcf0] (platform_drv_probe) from [c02be680] (driver_probe_device+0x13c/0x37c) [c02be680] (driver_probe_device) from [c02be954] (__driver_attach+0x94/0x98) [c02be954] (__driver_attach) from [c02bcc00] (bus_for_each_dev+0x54/0x88) [c02bcc00] (bus_for_each_dev) from [c02bdce0] (bus_add_driver+0xe4/0x200) [c02bdce0] (bus_add_driver) from [c02bef94] (driver_register+0x78/0xf4) [c02bef94] (driver_register) from [c029e938] (exynos_drm_init+0x70/0xa0) [c029e938] (exynos_drm_init) from [c00089b0] (do_one_initcall+0xac/0x1f0) [c00089b0] (do_one_initcall) from [c074bd90] (kernel_init_freeable+0x10c/0x1d8) [c074bd90] (kernel_init_freeable) from [c051eabc] (kernel_init+0x8/0xec) [c051eabc] (kernel_init) from [c000f268] (ret_from_fork+0x14/0x2c) 3 locks held by swapper/0/1: #0: (dev-mutex){..}, at: [c02be908] __driver_attach+0x48/0x98 #1: (dev-mutex){..}, at: [c02be918] __driver_attach+0x58/0x98 #2: (dev-mutex){..}, at: [c02be908] __driver_attach+0x48/0x98 Signed-off-by: Javier Martinez Canillas javier.marti...@collabora.co.uk Signed-off-by: Gustavo Padovan gustavo.pado...@collabora.co.uk --- drivers/gpu/drm/exynos/exynos_drm_drv.c | 124 +++- 1 file changed, 59 insertions(+), 65 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 91891cf..cb3ed9b 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -548,6 +548,38 @@ static const struct component_master_ops exynos_drm_ops = { .unbind = exynos_drm_unbind, }; +static int exynos_drm_platform_probe(struct platform_device *pdev) +{ + struct component_match *match; + + pdev-dev.coherent_dma_mask = DMA_BIT_MASK(32); + exynos_drm_driver.num_ioctls = ARRAY_SIZE(exynos_ioctls); + + match = exynos_drm_match_add(pdev-dev); + if (IS_ERR(match)) { + return PTR_ERR(match); + } + + return component_master_add_with_match(pdev-dev, exynos_drm_ops, +match); +} + +static int exynos_drm_platform_remove(struct platform_device *pdev) +{ + component_master_del(pdev-dev, exynos_drm_ops); + return 0; +} + +static struct platform_driver exynos_drm_platform_driver = { + .probe = exynos_drm_platform_probe, + .remove = exynos_drm_platform_remove, + .driver = { +
[PATCH 3/3] drm/exynos: move Exynos platform drivers registration to init
From: Gustavo Padovan gustavo.pado...@collabora.co.uk Registering the Exynos DRM subdevices platform drivers in the probe function is causing an infinite loop. Fix this by moving it to the exynos_drm_init() function to register the drivers on module init. Registering drivers in the probe functions causes a deadlock in the parent device lock. See Grant Likely explanation on the topic: I think the problem is that exynos_drm_init() is registering a normal (non-OF) platform device, so the parent will be /sys/devices/platform. It immediately gets bound against exynos_drm_platform_driver which calls the exynos drm_platform_probe() hook. The driver core obtains device_lock() on the device *and on the device parent*. Inside the probe hook, additional platform_drivers get registered. Each time one does, it tries to bind against every platform device in the system, which includes the ones created by OF. When it attempts to bind, it obtains device_lock() on the device *and on the device parent*. Before the change to move of-generated platform devices into /sys/devices/platform, the devices had different parents. Now both devices have /sys/devices/platform as the parent, so yes they are going to deadlock. The real problem is registering drivers from within a probe hook. That is completely wrong for the above deadlock reason. __driver_attach() will deadlock. Those registrations must be pulled out of .probe(). Registering devices in .probe() is okay because __device_attach() doesn't try to obtain device_lock() on the parent. INFO: task swapper/0:1 blocked for more than 120 seconds. Not tainted 3.18.0-rc3-next-20141105 #794 echo 0 /proc/sys/kernel/hung_task_timeout_secs disables this message. swapper/0 D c052534c 0 1 0 0x [c052534c] (__schedule) from [c0525b34] (schedule_preempt_disabled+0x14/0x20) [c0525b34] (schedule_preempt_disabled) from [c0526d44] (mutex_lock_nested+0x1c4/0x464 [c0526d44] (mutex_lock_nested) from [c02be908] (__driver_attach+0x48/0x98) [c02be908] (__driver_attach) from [c02bcc00] (bus_for_each_dev+0x54/0x88) [c02bcc00] (bus_for_each_dev) from [c02bdce0] (bus_add_driver+0xe4/0x200) [c02bdce0] (bus_add_driver) from [c02bef94] (driver_register+0x78/0xf4) [c02bef94] (driver_register) from [c029e99c] (exynos_drm_platform_probe+0x34/0x234) [c029e99c] (exynos_drm_platform_probe) from [c02bfcf0] (platform_drv_probe+0x48/0xa4) [c02bfcf0] (platform_drv_probe) from [c02be680] (driver_probe_device+0x13c/0x37c) [c02be680] (driver_probe_device) from [c02be954] (__driver_attach+0x94/0x98) [c02be954] (__driver_attach) from [c02bcc00] (bus_for_each_dev+0x54/0x88) [c02bcc00] (bus_for_each_dev) from [c02bdce0] (bus_add_driver+0xe4/0x200) [c02bdce0] (bus_add_driver) from [c02bef94] (driver_register+0x78/0xf4) [c02bef94] (driver_register) from [c029e938] (exynos_drm_init+0x70/0xa0) [c029e938] (exynos_drm_init) from [c00089b0] (do_one_initcall+0xac/0x1f0) [c00089b0] (do_one_initcall) from [c074bd90] (kernel_init_freeable+0x10c/0x1d8) [c074bd90] (kernel_init_freeable) from [c051eabc] (kernel_init+0x8/0xec) [c051eabc] (kernel_init) from [c000f268] (ret_from_fork+0x14/0x2c) 3 locks held by swapper/0/1: #0: (dev-mutex){..}, at: [c02be908] __driver_attach+0x48/0x98 #1: (dev-mutex){..}, at: [c02be918] __driver_attach+0x58/0x98 #2: (dev-mutex){..}, at: [c02be908] __driver_attach+0x48/0x98 Signed-off-by: Javier Martinez Canillas javier.marti...@collabora.co.uk Signed-off-by: Gustavo Padovan gustavo.pado...@collabora.co.uk --- drivers/gpu/drm/exynos/exynos_drm_drv.c | 124 +++- 1 file changed, 59 insertions(+), 65 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 91891cf..cb3ed9b 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -548,6 +548,38 @@ static const struct component_master_ops exynos_drm_ops = { .unbind = exynos_drm_unbind, }; +static int exynos_drm_platform_probe(struct platform_device *pdev) +{ + struct component_match *match; + + pdev-dev.coherent_dma_mask = DMA_BIT_MASK(32); + exynos_drm_driver.num_ioctls = ARRAY_SIZE(exynos_ioctls); + + match = exynos_drm_match_add(pdev-dev); + if (IS_ERR(match)) { + return PTR_ERR(match); + } + + return component_master_add_with_match(pdev-dev, exynos_drm_ops, + match); +} + +static int exynos_drm_platform_remove(struct platform_device *pdev) +{ + component_master_del(pdev-dev, exynos_drm_ops); + return 0; +} + +static struct platform_driver exynos_drm_platform_driver = { + .probe = exynos_drm_platform_probe, + .remove = exynos_drm_platform_remove, + .driver = { + .owner = THIS_MODULE, + .name = exynos-drm, + .pm = exynos_drm_pm_ops, + },