Bugfix from linux stable patches
Mukesh Ojha (2): devcoredump : Serialize devcd_del work devcoredump: Send uevent once devcd is ready
Saravana Kannan (1): driver core: Release all resources during unbind before updating device links
drivers/base/dd.c | 4 +- drivers/base/devcoredump.c | 86 +++++++++++++++++++++++++++++++++++++- 2 files changed, 86 insertions(+), 4 deletions(-)
From: Saravana Kannan saravanak@google.com
stable inclusion from stable-v4.19.301 commit 46100607c6f4dbae4121c370c3295ad8b75637e7 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I8T1U8
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=...
----------------------------------------------
commit 2e84dc37920012b458e9458b19fc4ed33f81bc74 upstream.
This commit fixes a bug in commit 9ed9895370ae ("driver core: Functional dependencies tracking support") where the device link status was incorrectly updated in the driver unbind path before all the device's resources were released.
Fixes: 9ed9895370ae ("driver core: Functional dependencies tracking support") Cc: stable stable@kernel.org Reported-by: Uwe Kleine-König u.kleine-koenig@pengutronix.de Closes: https://lore.kernel.org/all/20231014161721.f4iqyroddkcyoefo@pengutronix.de/ Signed-off-by: Saravana Kannan saravanak@google.com Cc: Thierry Reding thierry.reding@gmail.com Cc: Yang Yingliang yangyingliang@huawei.com Cc: Andy Shevchenko andriy.shevchenko@linux.intel.com Cc: Mark Brown broonie@kernel.org Cc: Matti Vaittinen mazziesaccount@gmail.com Cc: James Clark james.clark@arm.com Acked-by: "Rafael J. Wysocki" rafael@kernel.org Tested-by: Uwe Kleine-König u.kleine-koenig@pengutronix.de Acked-by: Uwe Kleine-König u.kleine-koenig@pengutronix.de Link: https://lore.kernel.org/r/20231018013851.3303928-1-saravanak@google.com Signed-off-by: Uwe Kleine-König u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org Signed-off-by: Zhang Zekun zhangzekun11@huawei.com --- drivers/base/dd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 0377c3c0f2d4..432a5645bac3 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -1026,8 +1026,6 @@ static void __device_release_driver(struct device *dev, struct device *parent) else if (drv->remove) drv->remove(dev);
- device_links_driver_cleanup(dev); - devres_release_all(dev); dma_deconfigure(dev); dev->driver = NULL; @@ -1037,6 +1035,8 @@ static void __device_release_driver(struct device *dev, struct device *parent) pm_runtime_reinit(dev); dev_pm_set_driver_flags(dev, 0);
+ device_links_driver_cleanup(dev); + klist_remove(&dev->p->knode_driver); device_pm_check_callbacks(dev); if (dev->bus)
From: Mukesh Ojha quic_mojha@quicinc.com
stable inclusion from stable-v4.19.302 commit 7c452e5f9fbfcb710c6154d54d70688b4dc63431 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I8T1U8
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=...
----------------------------------------------
[ Upstream commit 01daccf748323dfc61112f474cf2ba81015446b0 ]
In following scenario(diagram), when one thread X running dev_coredumpm() adds devcd device to the framework which sends uevent notification to userspace and another thread Y reads this uevent and call to devcd_data_write() which eventually try to delete the queued timer that is not initialized/queued yet.
So, debug object reports some warning and in the meantime, timer is initialized and queued from X path. and from Y path, it gets reinitialized again and timer->entry.pprev=NULL and try_to_grab_pending() stucks.
To fix this, introduce mutex and a boolean flag to serialize the behaviour.
cpu0(X) cpu1(Y)
dev_coredump() uevent sent to user space device_add() ======================> user space process Y reads the uevents writes to devcd fd which results into writes to
devcd_data_write() mod_delayed_work() try_to_grab_pending() del_timer() debug_assert_init() INIT_DELAYED_WORK() schedule_delayed_work() debug_object_fixup() timer_fixup_assert_init() timer_setup() do_init_timer() /* Above call reinitializes the timer to timer->entry.pprev=NULL and this will be checked later in timer_pending() call. */ timer_pending() !hlist_unhashed_lockless(&timer->entry) !h->pprev /* del_timer() checks h->pprev and finds it to be NULL due to which try_to_grab_pending() stucks. */
Link: https://lore.kernel.org/lkml/2e1f81e2-428c-f11f-ce92-eb11048cb271@quicinc.co... Signed-off-by: Mukesh Ojha quic_mojha@quicinc.com Link: https://lore.kernel.org/r/1663073424-13663-1-git-send-email-quic_mojha@quici... Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org Stable-dep-of: af54d778a038 ("devcoredump: Send uevent once devcd is ready") Signed-off-by: Sasha Levin sashal@kernel.org Signed-off-by: Zhang Zekun zhangzekun11@huawei.com --- drivers/base/devcoredump.c | 83 +++++++++++++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 2 deletions(-)
diff --git a/drivers/base/devcoredump.c b/drivers/base/devcoredump.c index 22deb0dd8a8c..7a5e4971943c 100644 --- a/drivers/base/devcoredump.c +++ b/drivers/base/devcoredump.c @@ -29,6 +29,47 @@ struct devcd_entry { struct device devcd_dev; void *data; size_t datalen; + /* + * Here, mutex is required to serialize the calls to del_wk work between + * user/kernel space which happens when devcd is added with device_add() + * and that sends uevent to user space. User space reads the uevents, + * and calls to devcd_data_write() which try to modify the work which is + * not even initialized/queued from devcoredump. + * + * + * + * cpu0(X) cpu1(Y) + * + * dev_coredump() uevent sent to user space + * device_add() ======================> user space process Y reads the + * uevents writes to devcd fd + * which results into writes to + * + * devcd_data_write() + * mod_delayed_work() + * try_to_grab_pending() + * del_timer() + * debug_assert_init() + * INIT_DELAYED_WORK() + * schedule_delayed_work() + * + * + * Also, mutex alone would not be enough to avoid scheduling of + * del_wk work after it get flush from a call to devcd_free() + * mentioned as below. + * + * disabled_store() + * devcd_free() + * mutex_lock() devcd_data_write() + * flush_delayed_work() + * mutex_unlock() + * mutex_lock() + * mod_delayed_work() + * mutex_unlock() + * So, delete_work flag is required. + */ + struct mutex mutex; + bool delete_work; struct module *owner; ssize_t (*read)(char *buffer, loff_t offset, size_t count, void *data, size_t datalen); @@ -88,7 +129,12 @@ static ssize_t devcd_data_write(struct file *filp, struct kobject *kobj, struct device *dev = kobj_to_dev(kobj); struct devcd_entry *devcd = dev_to_devcd(dev);
- mod_delayed_work(system_wq, &devcd->del_wk, 0); + mutex_lock(&devcd->mutex); + if (!devcd->delete_work) { + devcd->delete_work = true; + mod_delayed_work(system_wq, &devcd->del_wk, 0); + } + mutex_unlock(&devcd->mutex);
return count; } @@ -116,7 +162,12 @@ static int devcd_free(struct device *dev, void *data) { struct devcd_entry *devcd = dev_to_devcd(dev);
+ mutex_lock(&devcd->mutex); + if (!devcd->delete_work) + devcd->delete_work = true; + flush_delayed_work(&devcd->del_wk); + mutex_unlock(&devcd->mutex); return 0; }
@@ -126,6 +177,30 @@ static ssize_t disabled_show(struct class *class, struct class_attribute *attr, return sysfs_emit(buf, "%d\n", devcd_disabled); }
+/* + * + * disabled_store() worker() + * class_for_each_device(&devcd_class, + * NULL, NULL, devcd_free) + * ... + * ... + * while ((dev = class_dev_iter_next(&iter)) + * devcd_del() + * device_del() + * put_device() <- last reference + * error = fn(dev, data) devcd_dev_release() + * devcd_free(dev, data) kfree(devcd) + * mutex_lock(&devcd->mutex); + * + * + * In the above diagram, It looks like disabled_store() would be racing with parallely + * running devcd_del() and result in memory abort while acquiring devcd->mutex which + * is called after kfree of devcd memory after dropping its last reference with + * put_device(). However, this will not happens as fn(dev, data) runs + * with its own reference to device via klist_node so it is not its last reference. + * so, above situation would not occur. + */ + static ssize_t disabled_store(struct class *class, struct class_attribute *attr, const char *buf, size_t count) { @@ -291,13 +366,16 @@ void dev_coredumpm(struct device *dev, struct module *owner, devcd->read = read; devcd->free = free; devcd->failing_dev = get_device(dev); + devcd->delete_work = false;
+ mutex_init(&devcd->mutex); device_initialize(&devcd->devcd_dev);
dev_set_name(&devcd->devcd_dev, "devcd%d", atomic_inc_return(&devcd_count)); devcd->devcd_dev.class = &devcd_class;
+ mutex_lock(&devcd->mutex); if (device_add(&devcd->devcd_dev)) goto put_device;
@@ -311,10 +389,11 @@ void dev_coredumpm(struct device *dev, struct module *owner,
INIT_DELAYED_WORK(&devcd->del_wk, devcd_del); schedule_delayed_work(&devcd->del_wk, DEVCD_TIMEOUT); - + mutex_unlock(&devcd->mutex); return; put_device: put_device(&devcd->devcd_dev); + mutex_unlock(&devcd->mutex); put_module: module_put(owner); free:
From: Mukesh Ojha quic_mojha@quicinc.com
stable inclusion from stable-v4.19.302 commit 49d11d329a92f348d3e6bcac9ebfe05d785f1bb1 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I8T1U8
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=...
-----------------------------------------------
[ Upstream commit af54d778a03853801d681c98c0c2a6c316ef9ca7 ]
dev_coredumpm() creates a devcoredump device and adds it to the core kernel framework which eventually end up sending uevent to the user space and later creates a symbolic link to the failed device. An application running in userspace may be interested in this symbolic link to get the name of the failed device.
In a issue scenario, once uevent sent to the user space it start reading '/sys/class/devcoredump/devcdX/failing_device' to get the actual name of the device which might not been created and it is in its path of creation.
To fix this, suppress sending uevent till the failing device symbolic link gets created and send uevent once symbolic link is created successfully.
Fixes: 833c95456a70 ("device coredump: add new device coredump class") Signed-off-by: Mukesh Ojha quic_mojha@quicinc.com Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/1700232572-25823-1-git-send-email-quic_mojha@quici... Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org Signed-off-by: Sasha Levin sashal@kernel.org Signed-off-by: Zhang Zekun zhangzekun11@huawei.com --- drivers/base/devcoredump.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/drivers/base/devcoredump.c b/drivers/base/devcoredump.c index 7a5e4971943c..7f74a1f61d0c 100644 --- a/drivers/base/devcoredump.c +++ b/drivers/base/devcoredump.c @@ -376,6 +376,7 @@ void dev_coredumpm(struct device *dev, struct module *owner, devcd->devcd_dev.class = &devcd_class;
mutex_lock(&devcd->mutex); + dev_set_uevent_suppress(&devcd->devcd_dev, true); if (device_add(&devcd->devcd_dev)) goto put_device;
@@ -387,6 +388,8 @@ void dev_coredumpm(struct device *dev, struct module *owner, "devcoredump")) /* nothing - symlink will be missing */;
+ dev_set_uevent_suppress(&devcd->devcd_dev, false); + kobject_uevent(&devcd->devcd_dev.kobj, KOBJ_ADD); INIT_DELAYED_WORK(&devcd->del_wk, devcd_del); schedule_delayed_work(&devcd->del_wk, DEVCD_TIMEOUT); mutex_unlock(&devcd->mutex);
反馈: 您发送到kernel@openeuler.org的补丁/补丁集,转换为PR失败! 邮件列表地址:https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/R... 失败原因:补丁/补丁集的标题分支与仓库分支列表不匹配 建议解决方法:请确认补丁标题中的分支是否正确,若有误则修改,无则忽略
FeedBack: The patch(es) which you have sent to kernel@openeuler.org has been converted to PR failed! Mailing list address: https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/R... Failed Reason: branch in patch(es)'s title can not match any branches in repository's branch list Suggest Solution: please checkout if the patch(es)'s branch in title is wrong and fix it, if not ignore this