Unverified Commit 40a1ac08 authored by openeuler-ci-bot's avatar openeuler-ci-bot Committed by Gitee
Browse files

!3757 Fix bugs from LTS patches

Merge Pull Request from: @ci-robot 
 
PR sync from: Zhang Zekun <zhangzekun11@huawei.com>
https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/T5CUEQO4HJONQ3Q2KJRTPN2N6RRKXSTM/ 
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


-- 
2.17.1
 
https://gitee.com/openeuler/kernel/issues/I8T1U8 
 
Link:https://gitee.com/openeuler/kernel/pulls/3757

 

Reviewed-by: default avatarWeilong Chen <chenweilong@huawei.com>
Reviewed-by: default avatarLiu YongQiang <liuyongqiang13@huawei.com>
Signed-off-by: default avatarZhang Changzhong <zhangchangzhong@huawei.com>
parents 2d032cb8 c221b29d
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -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)
+84 −2
Original line number Diff line number Diff line
@@ -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);

	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,17 @@ 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);
	dev_set_uevent_suppress(&devcd->devcd_dev, true);
	if (device_add(&devcd->devcd_dev))
		goto put_device;

@@ -309,12 +388,15 @@ 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);
	return;
 put_device:
	put_device(&devcd->devcd_dev);
	mutex_unlock(&devcd->mutex);
 put_module:
	module_put(owner);
 free: