From: Sasha Levin Date: Fri, 22 May 2020 00:42:56 +0000 (-0400) Subject: Fixes for 4.4 X-Git-Tag: v4.4.225~61 X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=f10483ba827860d6c83fef896ea520f4d7cc10d6;p=thirdparty%2Fkernel%2Fstable-queue.git Fixes for 4.4 Signed-off-by: Sasha Levin --- diff --git a/queue-4.4/drivers-media-media-devnode-clear-private_data-befor.patch b/queue-4.4/drivers-media-media-devnode-clear-private_data-befor.patch new file mode 100644 index 00000000000..df0ba5f3743 --- /dev/null +++ b/queue-4.4/drivers-media-media-devnode-clear-private_data-befor.patch @@ -0,0 +1,40 @@ +From 5d135c6a3e6cfb279e3fde493f856173d484d5e7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 21 Mar 2016 10:30:28 -0300 +Subject: drivers/media/media-devnode: clear private_data before put_device() + +From: Max Kellermann + +commit bf244f665d76d20312c80524689b32a752888838 upstream. + +Callbacks invoked from put_device() may free the struct media_devnode +pointer, so any cleanup needs to be done before put_device(). + +Signed-off-by: Max Kellermann +Signed-off-by: Mauro Carvalho Chehab +Signed-off-by: Ben Hutchings +Signed-off-by: Sasha Levin +--- + drivers/media/media-devnode.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c +index a8cb52dc8c4f..6c56aebd8db0 100644 +--- a/drivers/media/media-devnode.c ++++ b/drivers/media/media-devnode.c +@@ -197,10 +197,11 @@ static int media_release(struct inode *inode, struct file *filp) + if (mdev->fops->release) + mdev->fops->release(filp); + ++ filp->private_data = NULL; ++ + /* decrease the refcount unconditionally since the release() + return value is ignored. */ + put_device(&mdev->dev); +- filp->private_data = NULL; + return 0; + } + +-- +2.25.1 + diff --git a/queue-4.4/i2c-dev-don-t-get-i2c-adapter-via-i2c_dev.patch b/queue-4.4/i2c-dev-don-t-get-i2c-adapter-via-i2c_dev.patch new file mode 100644 index 00000000000..50f43781a02 --- /dev/null +++ b/queue-4.4/i2c-dev-don-t-get-i2c-adapter-via-i2c_dev.patch @@ -0,0 +1,47 @@ +From 940eeed2adac118127c453aff2000fd6f02c021e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 5 Jul 2016 19:57:06 -0700 +Subject: i2c-dev: don't get i2c adapter via i2c_dev + +From: viresh kumar + +commit 5136ed4fcb05cd4981cc6034a11e66370ed84789 upstream. + +There is no code protecting i2c_dev to be freed after it is returned +from i2c_dev_get_by_minor() and using it to access the value which we +already have (minor) isn't safe really. + +Avoid using it and get the adapter directly from 'minor'. + +Signed-off-by: Viresh Kumar +Reviewed-by: Jean Delvare +Tested-by: Jean Delvare +Signed-off-by: Wolfram Sang +Signed-off-by: Ben Hutchings +Signed-off-by: Sasha Levin +--- + drivers/i2c/i2c-dev.c | 7 +------ + 1 file changed, 1 insertion(+), 6 deletions(-) + +diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c +index e5cd307ebfc9..5543b49e2e05 100644 +--- a/drivers/i2c/i2c-dev.c ++++ b/drivers/i2c/i2c-dev.c +@@ -492,13 +492,8 @@ static int i2cdev_open(struct inode *inode, struct file *file) + unsigned int minor = iminor(inode); + struct i2c_client *client; + struct i2c_adapter *adap; +- struct i2c_dev *i2c_dev; +- +- i2c_dev = i2c_dev_get_by_minor(minor); +- if (!i2c_dev) +- return -ENODEV; + +- adap = i2c_get_adapter(i2c_dev->adap->nr); ++ adap = i2c_get_adapter(minor); + if (!adap) + return -ENODEV; + +-- +2.25.1 + diff --git a/queue-4.4/i2c-dev-don-t-start-function-name-with-return.patch b/queue-4.4/i2c-dev-don-t-start-function-name-with-return.patch new file mode 100644 index 00000000000..723f86f4860 --- /dev/null +++ b/queue-4.4/i2c-dev-don-t-start-function-name-with-return.patch @@ -0,0 +1,54 @@ +From 108a1d68bee0b317808828d0f0d3354e45a411ad Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 27 May 2016 13:13:01 +0200 +Subject: i2c: dev: don't start function name with 'return' + +From: Wolfram Sang + +commit 72a71f869c95dc11b73f09fe18c593d4a0618c3f upstream. + +I stumbled multiple times over 'return_i2c_dev', especially before the +actual 'return res'. It makes the code hard to read, so reanme the +function to 'put_i2c_dev' which also better matches 'get_free_i2c_dev'. + +Signed-off-by: Wolfram Sang +Signed-off-by: Ben Hutchings +Signed-off-by: Sasha Levin +--- + drivers/i2c/i2c-dev.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c +index 5fecc1d9e0a1..382c66d5a470 100644 +--- a/drivers/i2c/i2c-dev.c ++++ b/drivers/i2c/i2c-dev.c +@@ -91,7 +91,7 @@ static struct i2c_dev *get_free_i2c_dev(struct i2c_adapter *adap) + return i2c_dev; + } + +-static void return_i2c_dev(struct i2c_dev *i2c_dev) ++static void put_i2c_dev(struct i2c_dev *i2c_dev) + { + spin_lock(&i2c_dev_list_lock); + list_del(&i2c_dev->list); +@@ -582,7 +582,7 @@ static int i2cdev_attach_adapter(struct device *dev, void *dummy) + error: + cdev_del(&i2c_dev->cdev); + error_cdev: +- return_i2c_dev(i2c_dev); ++ put_i2c_dev(i2c_dev); + return res; + } + +@@ -599,7 +599,7 @@ static int i2cdev_detach_adapter(struct device *dev, void *dummy) + if (!i2c_dev) /* attach_adapter must have failed */ + return 0; + +- return_i2c_dev(i2c_dev); ++ put_i2c_dev(i2c_dev); + device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr)); + cdev_del(&i2c_dev->cdev); + +-- +2.25.1 + diff --git a/queue-4.4/i2c-dev-fix-the-race-between-the-release-of-i2c_dev-.patch b/queue-4.4/i2c-dev-fix-the-race-between-the-release-of-i2c_dev-.patch new file mode 100644 index 00000000000..c026157e19c --- /dev/null +++ b/queue-4.4/i2c-dev-fix-the-race-between-the-release-of-i2c_dev-.patch @@ -0,0 +1,188 @@ +From 1329cc3082b4fe3e34bbb839a4c8bfb22084729e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 11 Oct 2019 23:00:14 +0800 +Subject: i2c: dev: Fix the race between the release of i2c_dev and cdev + +From: Kevin Hao + +commit 1413ef638abae4ab5621901cf4d8ef08a4a48ba6 upstream. + +The struct cdev is embedded in the struct i2c_dev. In the current code, +we would free the i2c_dev struct directly in put_i2c_dev(), but the +cdev is manged by a kobject, and the release of it is not predictable. +So it is very possible that the i2c_dev is freed before the cdev is +entirely released. We can easily get the following call trace with +CONFIG_DEBUG_KOBJECT_RELEASE and CONFIG_DEBUG_OBJECTS_TIMERS enabled. + ODEBUG: free active (active state 0) object type: timer_list hint: delayed_work_timer_fn+0x0/0x38 + WARNING: CPU: 19 PID: 1 at lib/debugobjects.c:325 debug_print_object+0xb0/0xf0 + Modules linked in: + CPU: 19 PID: 1 Comm: swapper/0 Tainted: G W 5.2.20-yocto-standard+ #120 + Hardware name: Marvell OcteonTX CN96XX board (DT) + pstate: 80c00089 (Nzcv daIf +PAN +UAO) + pc : debug_print_object+0xb0/0xf0 + lr : debug_print_object+0xb0/0xf0 + sp : ffff00001292f7d0 + x29: ffff00001292f7d0 x28: ffff800b82151788 + x27: 0000000000000001 x26: ffff800b892c0000 + x25: ffff0000124a2558 x24: 0000000000000000 + x23: ffff00001107a1d8 x22: ffff0000116b5088 + x21: ffff800bdc6afca8 x20: ffff000012471ae8 + x19: ffff00001168f2c8 x18: 0000000000000010 + x17: 00000000fd6f304b x16: 00000000ee79de43 + x15: ffff800bc0e80568 x14: 79616c6564203a74 + x13: 6e6968207473696c x12: 5f72656d6974203a + x11: ffff0000113f0018 x10: 0000000000000000 + x9 : 000000000000001f x8 : 0000000000000000 + x7 : ffff0000101294cc x6 : 0000000000000000 + x5 : 0000000000000000 x4 : 0000000000000001 + x3 : 00000000ffffffff x2 : 0000000000000000 + x1 : 387fc15c8ec0f200 x0 : 0000000000000000 + Call trace: + debug_print_object+0xb0/0xf0 + __debug_check_no_obj_freed+0x19c/0x228 + debug_check_no_obj_freed+0x1c/0x28 + kfree+0x250/0x440 + put_i2c_dev+0x68/0x78 + i2cdev_detach_adapter+0x60/0xc8 + i2cdev_notifier_call+0x3c/0x70 + notifier_call_chain+0x8c/0xe8 + blocking_notifier_call_chain+0x64/0x88 + device_del+0x74/0x380 + device_unregister+0x54/0x78 + i2c_del_adapter+0x278/0x2d0 + unittest_i2c_bus_remove+0x3c/0x80 + platform_drv_remove+0x30/0x50 + device_release_driver_internal+0xf4/0x1c0 + driver_detach+0x58/0xa0 + bus_remove_driver+0x84/0xd8 + driver_unregister+0x34/0x60 + platform_driver_unregister+0x20/0x30 + of_unittest_overlay+0x8d4/0xbe0 + of_unittest+0xae8/0xb3c + do_one_initcall+0xac/0x450 + do_initcall_level+0x208/0x224 + kernel_init_freeable+0x2d8/0x36c + kernel_init+0x18/0x108 + ret_from_fork+0x10/0x1c + irq event stamp: 3934661 + hardirqs last enabled at (3934661): [] debug_exception_exit+0x4c/0x58 + hardirqs last disabled at (3934660): [] debug_exception_enter+0xa4/0xe0 + softirqs last enabled at (3934654): [] __do_softirq+0x46c/0x628 + softirqs last disabled at (3934649): [] irq_exit+0x104/0x118 + +This is a common issue when using cdev embedded in a struct. +Fortunately, we already have a mechanism to solve this kind of issue. +Please see commit 233ed09d7fda ("chardev: add helper function to +register char devs with a struct device") for more detail. + +In this patch, we choose to embed the struct device into the i2c_dev, +and use the API provided by the commit 233ed09d7fda to make sure that +the release of i2c_dev and cdev are in sequence. + +Signed-off-by: Kevin Hao +Signed-off-by: Wolfram Sang +Signed-off-by: Ben Hutchings +Signed-off-by: Sasha Levin +--- + drivers/i2c/i2c-dev.c | 48 +++++++++++++++++++++++-------------------- + 1 file changed, 26 insertions(+), 22 deletions(-) + +diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c +index 5543b49e2e05..7584f292e2fd 100644 +--- a/drivers/i2c/i2c-dev.c ++++ b/drivers/i2c/i2c-dev.c +@@ -47,7 +47,7 @@ + struct i2c_dev { + struct list_head list; + struct i2c_adapter *adap; +- struct device *dev; ++ struct device dev; + struct cdev cdev; + }; + +@@ -91,12 +91,14 @@ static struct i2c_dev *get_free_i2c_dev(struct i2c_adapter *adap) + return i2c_dev; + } + +-static void put_i2c_dev(struct i2c_dev *i2c_dev) ++static void put_i2c_dev(struct i2c_dev *i2c_dev, bool del_cdev) + { + spin_lock(&i2c_dev_list_lock); + list_del(&i2c_dev->list); + spin_unlock(&i2c_dev_list_lock); +- kfree(i2c_dev); ++ if (del_cdev) ++ cdev_device_del(&i2c_dev->cdev, &i2c_dev->dev); ++ put_device(&i2c_dev->dev); + } + + static ssize_t name_show(struct device *dev, +@@ -542,6 +544,14 @@ static const struct file_operations i2cdev_fops = { + + static struct class *i2c_dev_class; + ++static void i2cdev_dev_release(struct device *dev) ++{ ++ struct i2c_dev *i2c_dev; ++ ++ i2c_dev = container_of(dev, struct i2c_dev, dev); ++ kfree(i2c_dev); ++} ++ + static int i2cdev_attach_adapter(struct device *dev, void *dummy) + { + struct i2c_adapter *adap; +@@ -558,27 +568,23 @@ static int i2cdev_attach_adapter(struct device *dev, void *dummy) + + cdev_init(&i2c_dev->cdev, &i2cdev_fops); + i2c_dev->cdev.owner = THIS_MODULE; +- res = cdev_add(&i2c_dev->cdev, MKDEV(I2C_MAJOR, adap->nr), 1); +- if (res) +- goto error_cdev; +- +- /* register this i2c device with the driver core */ +- i2c_dev->dev = device_create(i2c_dev_class, &adap->dev, +- MKDEV(I2C_MAJOR, adap->nr), NULL, +- "i2c-%d", adap->nr); +- if (IS_ERR(i2c_dev->dev)) { +- res = PTR_ERR(i2c_dev->dev); +- goto error; ++ ++ device_initialize(&i2c_dev->dev); ++ i2c_dev->dev.devt = MKDEV(I2C_MAJOR, adap->nr); ++ i2c_dev->dev.class = i2c_dev_class; ++ i2c_dev->dev.parent = &adap->dev; ++ i2c_dev->dev.release = i2cdev_dev_release; ++ dev_set_name(&i2c_dev->dev, "i2c-%d", adap->nr); ++ ++ res = cdev_device_add(&i2c_dev->cdev, &i2c_dev->dev); ++ if (res) { ++ put_i2c_dev(i2c_dev, false); ++ return res; + } + + pr_debug("i2c-dev: adapter [%s] registered as minor %d\n", + adap->name, adap->nr); + return 0; +-error: +- cdev_del(&i2c_dev->cdev); +-error_cdev: +- put_i2c_dev(i2c_dev); +- return res; + } + + static int i2cdev_detach_adapter(struct device *dev, void *dummy) +@@ -594,9 +600,7 @@ static int i2cdev_detach_adapter(struct device *dev, void *dummy) + if (!i2c_dev) /* attach_adapter must have failed */ + return 0; + +- cdev_del(&i2c_dev->cdev); +- put_i2c_dev(i2c_dev); +- device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr)); ++ put_i2c_dev(i2c_dev, true); + + pr_debug("i2c-dev: adapter [%s] unregistered\n", adap->name); + return 0; +-- +2.25.1 + diff --git a/queue-4.4/i2c-dev-switch-from-register_chrdev-to-cdev-api.patch b/queue-4.4/i2c-dev-switch-from-register_chrdev-to-cdev-api.patch new file mode 100644 index 00000000000..5f304b356ce --- /dev/null +++ b/queue-4.4/i2c-dev-switch-from-register_chrdev-to-cdev-api.patch @@ -0,0 +1,124 @@ +From 528303832f25e979851e632cb34b24577b8d43b3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 3 May 2016 15:45:43 -0300 +Subject: i2c: dev: switch from register_chrdev to cdev API + +From: Erico Nunes + +commit d6760b14d4a1243f918d983bba1e35c5a5cd5a6d upstream. + +i2c-dev had never moved away from the older register_chrdev interface to +implement its char device registration. The register_chrdev API has the +limitation of enabling only up to 256 i2c-dev busses to exist. + +Large platforms with lots of i2c devices (i.e. pluggable transceivers) +with dedicated busses may have to exceed that limit. +In particular, there are also platforms making use of the i2c bus +multiplexing API, which instantiates a virtual bus for each possible +multiplexed selection. + +This patch removes the register_chrdev usage and replaces it with the +less old cdev API, which takes away the 256 i2c-dev bus limitation. +It should not have any other impact for i2c bus drivers or user space. + +This patch has been tested on qemu x86 and qemu powerpc platforms with +the aid of a module which adds and removes 5000 virtual i2c busses, as +well as validated on an existing powerpc hardware platform which makes +use of the i2c bus multiplexing API. +i2c-dev busses with device minor numbers larger than 256 have also been +validated to work with the existing i2c-tools. + +Signed-off-by: Erico Nunes +[wsa: kept includes sorted] +Signed-off-by: Wolfram Sang +[bwh: Backported to 4.4: adjust context] +Signed-off-by: Ben Hutchings +Signed-off-by: Sasha Levin +--- + drivers/i2c/i2c-dev.c | 19 +++++++++++++++---- + 1 file changed, 15 insertions(+), 4 deletions(-) + +diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c +index e56b774e7cf9..5fecc1d9e0a1 100644 +--- a/drivers/i2c/i2c-dev.c ++++ b/drivers/i2c/i2c-dev.c +@@ -22,6 +22,7 @@ + + /* The I2C_RDWR ioctl code is written by Kolja Waschk */ + ++#include + #include + #include + #include +@@ -47,9 +48,10 @@ struct i2c_dev { + struct list_head list; + struct i2c_adapter *adap; + struct device *dev; ++ struct cdev cdev; + }; + +-#define I2C_MINORS 256 ++#define I2C_MINORS MINORMASK + static LIST_HEAD(i2c_dev_list); + static DEFINE_SPINLOCK(i2c_dev_list_lock); + +@@ -559,6 +561,12 @@ static int i2cdev_attach_adapter(struct device *dev, void *dummy) + if (IS_ERR(i2c_dev)) + return PTR_ERR(i2c_dev); + ++ cdev_init(&i2c_dev->cdev, &i2cdev_fops); ++ i2c_dev->cdev.owner = THIS_MODULE; ++ res = cdev_add(&i2c_dev->cdev, MKDEV(I2C_MAJOR, adap->nr), 1); ++ if (res) ++ goto error_cdev; ++ + /* register this i2c device with the driver core */ + i2c_dev->dev = device_create(i2c_dev_class, &adap->dev, + MKDEV(I2C_MAJOR, adap->nr), NULL, +@@ -572,6 +580,8 @@ static int i2cdev_attach_adapter(struct device *dev, void *dummy) + adap->name, adap->nr); + return 0; + error: ++ cdev_del(&i2c_dev->cdev); ++error_cdev: + return_i2c_dev(i2c_dev); + return res; + } +@@ -591,6 +601,7 @@ static int i2cdev_detach_adapter(struct device *dev, void *dummy) + + return_i2c_dev(i2c_dev); + device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr)); ++ cdev_del(&i2c_dev->cdev); + + pr_debug("i2c-dev: adapter [%s] unregistered\n", adap->name); + return 0; +@@ -627,7 +638,7 @@ static int __init i2c_dev_init(void) + + printk(KERN_INFO "i2c /dev entries driver\n"); + +- res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops); ++ res = register_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS, "i2c"); + if (res) + goto out; + +@@ -651,7 +662,7 @@ static int __init i2c_dev_init(void) + out_unreg_class: + class_destroy(i2c_dev_class); + out_unreg_chrdev: +- unregister_chrdev(I2C_MAJOR, "i2c"); ++ unregister_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS); + out: + printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__); + return res; +@@ -662,7 +673,7 @@ static void __exit i2c_dev_exit(void) + bus_unregister_notifier(&i2c_bus_type, &i2cdev_notifier); + i2c_for_each_dev(NULL, i2cdev_detach_adapter); + class_destroy(i2c_dev_class); +- unregister_chrdev(I2C_MAJOR, "i2c"); ++ unregister_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS); + } + + MODULE_AUTHOR("Frodo Looijaard and " +-- +2.25.1 + diff --git a/queue-4.4/i2c-dev-use-after-free-in-detach.patch b/queue-4.4/i2c-dev-use-after-free-in-detach.patch new file mode 100644 index 00000000000..b6d498b7559 --- /dev/null +++ b/queue-4.4/i2c-dev-use-after-free-in-detach.patch @@ -0,0 +1,39 @@ +From 7d36e754a433001e08a1339949e5192be29d966f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 28 May 2016 08:01:46 +0300 +Subject: i2c: dev: use after free in detach + +From: Dan Carpenter + +commit e6be18f6d62c1d3b331ae020b76a29c2ccf6b0bf upstream. + +The call to put_i2c_dev() frees "i2c_dev" so there is a use after +free when we call cdev_del(&i2c_dev->cdev). + +Fixes: d6760b14d4a1 ('i2c: dev: switch from register_chrdev to cdev API') +Signed-off-by: Dan Carpenter +Signed-off-by: Wolfram Sang +Signed-off-by: Ben Hutchings +Signed-off-by: Sasha Levin +--- + drivers/i2c/i2c-dev.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c +index 382c66d5a470..e5cd307ebfc9 100644 +--- a/drivers/i2c/i2c-dev.c ++++ b/drivers/i2c/i2c-dev.c +@@ -599,9 +599,9 @@ static int i2cdev_detach_adapter(struct device *dev, void *dummy) + if (!i2c_dev) /* attach_adapter must have failed */ + return 0; + ++ cdev_del(&i2c_dev->cdev); + put_i2c_dev(i2c_dev); + device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr)); +- cdev_del(&i2c_dev->cdev); + + pr_debug("i2c-dev: adapter [%s] unregistered\n", adap->name); + return 0; +-- +2.25.1 + diff --git a/queue-4.4/media-device-dynamically-allocate-struct-media_devno.patch b/queue-4.4/media-device-dynamically-allocate-struct-media_devno.patch new file mode 100644 index 00000000000..a8068dcb990 --- /dev/null +++ b/queue-4.4/media-device-dynamically-allocate-struct-media_devno.patch @@ -0,0 +1,265 @@ +From 82b2a743620ff7f9f16ee889a400753dd2ea74e5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 27 Apr 2016 19:28:26 -0300 +Subject: media-device: dynamically allocate struct media_devnode + +From: Mauro Carvalho Chehab + +commit a087ce704b802becbb4b0f2a20f2cb3f6911802e upstream. + +struct media_devnode is currently embedded at struct media_device. + +While this works fine during normal usage, it leads to a race +condition during devnode unregister. the problem is that drivers +assume that, after calling media_device_unregister(), the struct +that contains media_device can be freed. This is not true, as it +can't be freed until userspace closes all opened /dev/media devnodes. + +In other words, if the media devnode is still open, and media_device +gets freed, any call to an ioctl will make the core to try to access +struct media_device, with will cause an use-after-free and even GPF. + +Fix this by dynamically allocating the struct media_devnode and only +freeing it when it is safe. + +Signed-off-by: Mauro Carvalho Chehab +Signed-off-by: Mauro Carvalho Chehab +[bwh: Backported to 4.4: + - Drop change in au0828 + - Include in media-device.c + - Adjust context] +Signed-off-by: Ben Hutchings +Signed-off-by: Sasha Levin +--- + drivers/media/media-device.c | 40 +++++++++++++++++++++--------- + drivers/media/media-devnode.c | 8 +++++- + drivers/media/usb/uvc/uvc_driver.c | 2 +- + include/media/media-device.h | 5 +--- + include/media/media-devnode.h | 10 +++++++- + 5 files changed, 46 insertions(+), 19 deletions(-) + +diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c +index 7b39440192d6..fb018fe1a8f7 100644 +--- a/drivers/media/media-device.c ++++ b/drivers/media/media-device.c +@@ -24,6 +24,7 @@ + #include + #include + #include ++#include + #include + + #include +@@ -234,7 +235,7 @@ static long media_device_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) + { + struct media_devnode *devnode = media_devnode_data(filp); +- struct media_device *dev = to_media_device(devnode); ++ struct media_device *dev = devnode->media_dev; + long ret; + + switch (cmd) { +@@ -303,7 +304,7 @@ static long media_device_compat_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) + { + struct media_devnode *devnode = media_devnode_data(filp); +- struct media_device *dev = to_media_device(devnode); ++ struct media_device *dev = devnode->media_dev; + long ret; + + switch (cmd) { +@@ -344,7 +345,8 @@ static const struct media_file_operations media_device_fops = { + static ssize_t show_model(struct device *cd, + struct device_attribute *attr, char *buf) + { +- struct media_device *mdev = to_media_device(to_media_devnode(cd)); ++ struct media_devnode *devnode = to_media_devnode(cd); ++ struct media_device *mdev = devnode->media_dev; + + return sprintf(buf, "%.*s\n", (int)sizeof(mdev->model), mdev->model); + } +@@ -372,6 +374,7 @@ static void media_device_release(struct media_devnode *mdev) + int __must_check __media_device_register(struct media_device *mdev, + struct module *owner) + { ++ struct media_devnode *devnode; + int ret; + + if (WARN_ON(mdev->dev == NULL || mdev->model[0] == 0)) +@@ -382,17 +385,27 @@ int __must_check __media_device_register(struct media_device *mdev, + spin_lock_init(&mdev->lock); + mutex_init(&mdev->graph_mutex); + ++ devnode = kzalloc(sizeof(*devnode), GFP_KERNEL); ++ if (!devnode) ++ return -ENOMEM; ++ + /* Register the device node. */ +- mdev->devnode.fops = &media_device_fops; +- mdev->devnode.parent = mdev->dev; +- mdev->devnode.release = media_device_release; +- ret = media_devnode_register(&mdev->devnode, owner); +- if (ret < 0) ++ mdev->devnode = devnode; ++ devnode->fops = &media_device_fops; ++ devnode->parent = mdev->dev; ++ devnode->release = media_device_release; ++ ret = media_devnode_register(mdev, devnode, owner); ++ if (ret < 0) { ++ mdev->devnode = NULL; ++ kfree(devnode); + return ret; ++ } + +- ret = device_create_file(&mdev->devnode.dev, &dev_attr_model); ++ ret = device_create_file(&devnode->dev, &dev_attr_model); + if (ret < 0) { +- media_devnode_unregister(&mdev->devnode); ++ mdev->devnode = NULL; ++ media_devnode_unregister(devnode); ++ kfree(devnode); + return ret; + } + +@@ -413,8 +426,11 @@ void media_device_unregister(struct media_device *mdev) + list_for_each_entry_safe(entity, next, &mdev->entities, list) + media_device_unregister_entity(entity); + +- device_remove_file(&mdev->devnode.dev, &dev_attr_model); +- media_devnode_unregister(&mdev->devnode); ++ /* Check if mdev devnode was registered */ ++ if (media_devnode_is_registered(mdev->devnode)) { ++ device_remove_file(&mdev->devnode->dev, &dev_attr_model); ++ media_devnode_unregister(mdev->devnode); ++ } + } + EXPORT_SYMBOL_GPL(media_device_unregister); + +diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c +index 98211c570e11..000efb17b95b 100644 +--- a/drivers/media/media-devnode.c ++++ b/drivers/media/media-devnode.c +@@ -44,6 +44,7 @@ + #include + + #include ++#include + + #define MEDIA_NUM_DEVICES 256 + #define MEDIA_NAME "media" +@@ -74,6 +75,8 @@ static void media_devnode_release(struct device *cd) + /* Release media_devnode and perform other cleanups as needed. */ + if (devnode->release) + devnode->release(devnode); ++ ++ kfree(devnode); + } + + static struct bus_type media_bus_type = { +@@ -221,6 +224,7 @@ static const struct file_operations media_devnode_fops = { + + /** + * media_devnode_register - register a media device node ++ * @media_dev: struct media_device we want to register a device node + * @devnode: media device node structure we want to register + * + * The registration code assigns minor numbers and registers the new device node +@@ -233,7 +237,8 @@ static const struct file_operations media_devnode_fops = { + * the media_devnode structure is *not* called, so the caller is responsible for + * freeing any data. + */ +-int __must_check media_devnode_register(struct media_devnode *devnode, ++int __must_check media_devnode_register(struct media_device *mdev, ++ struct media_devnode *devnode, + struct module *owner) + { + int minor; +@@ -252,6 +257,7 @@ int __must_check media_devnode_register(struct media_devnode *devnode, + mutex_unlock(&media_devnode_lock); + + devnode->minor = minor; ++ devnode->media_dev = mdev; + + /* Part 2: Initialize and register the character device */ + cdev_init(&devnode->cdev, &media_devnode_fops); +diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c +index 9cd0268b2767..f353ab569b8e 100644 +--- a/drivers/media/usb/uvc/uvc_driver.c ++++ b/drivers/media/usb/uvc/uvc_driver.c +@@ -1800,7 +1800,7 @@ static void uvc_delete(struct uvc_device *dev) + if (dev->vdev.dev) + v4l2_device_unregister(&dev->vdev); + #ifdef CONFIG_MEDIA_CONTROLLER +- if (media_devnode_is_registered(&dev->mdev.devnode)) ++ if (media_devnode_is_registered(dev->mdev.devnode)) + media_device_unregister(&dev->mdev); + #endif + +diff --git a/include/media/media-device.h b/include/media/media-device.h +index 6e6db78f1ee2..00bbd679864a 100644 +--- a/include/media/media-device.h ++++ b/include/media/media-device.h +@@ -60,7 +60,7 @@ struct device; + struct media_device { + /* dev->driver_data points to this struct. */ + struct device *dev; +- struct media_devnode devnode; ++ struct media_devnode *devnode; + + char model[32]; + char serial[40]; +@@ -84,9 +84,6 @@ struct media_device { + #define MEDIA_DEV_NOTIFY_PRE_LINK_CH 0 + #define MEDIA_DEV_NOTIFY_POST_LINK_CH 1 + +-/* media_devnode to media_device */ +-#define to_media_device(node) container_of(node, struct media_device, devnode) +- + int __must_check __media_device_register(struct media_device *mdev, + struct module *owner); + #define media_device_register(mdev) __media_device_register(mdev, THIS_MODULE) +diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h +index 79f702d26d1f..8b854c044032 100644 +--- a/include/media/media-devnode.h ++++ b/include/media/media-devnode.h +@@ -33,6 +33,8 @@ + #include + #include + ++struct media_device; ++ + /* + * Flag to mark the media_devnode struct as registered. Drivers must not touch + * this flag directly, it will be set and cleared by media_devnode_register and +@@ -67,6 +69,8 @@ struct media_file_operations { + * before registering the node. + */ + struct media_devnode { ++ struct media_device *media_dev; ++ + /* device ops */ + const struct media_file_operations *fops; + +@@ -86,7 +90,8 @@ struct media_devnode { + /* dev to media_devnode */ + #define to_media_devnode(cd) container_of(cd, struct media_devnode, dev) + +-int __must_check media_devnode_register(struct media_devnode *devnode, ++int __must_check media_devnode_register(struct media_device *mdev, ++ struct media_devnode *devnode, + struct module *owner); + void media_devnode_unregister(struct media_devnode *devnode); + +@@ -97,6 +102,9 @@ static inline struct media_devnode *media_devnode_data(struct file *filp) + + static inline int media_devnode_is_registered(struct media_devnode *devnode) + { ++ if (!devnode) ++ return false; ++ + return test_bit(MEDIA_FLAG_REGISTERED, &devnode->flags); + } + +-- +2.25.1 + diff --git a/queue-4.4/media-devnode-add-missing-mutex-lock-in-error-handle.patch b/queue-4.4/media-devnode-add-missing-mutex-lock-in-error-handle.patch new file mode 100644 index 00000000000..6678f8733fa --- /dev/null +++ b/queue-4.4/media-devnode-add-missing-mutex-lock-in-error-handle.patch @@ -0,0 +1,39 @@ +From 677ab4f9d477410d1a3216d4d51287d075c64195 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 21 Mar 2016 04:33:12 -0700 +Subject: media-devnode: add missing mutex lock in error handler + +From: Max Kellermann + +commit 88336e174645948da269e1812f138f727cd2896b upstream. + +We should protect the device unregister patch too, at the error +condition. + +Signed-off-by: Max Kellermann +Signed-off-by: Mauro Carvalho Chehab +Signed-off-by: Ben Hutchings +Signed-off-by: Sasha Levin +--- + drivers/media/media-devnode.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c +index 6c56aebd8db0..86c7c3732c84 100644 +--- a/drivers/media/media-devnode.c ++++ b/drivers/media/media-devnode.c +@@ -282,8 +282,11 @@ int __must_check media_devnode_register(struct media_devnode *mdev, + return 0; + + error: ++ mutex_lock(&media_devnode_lock); + cdev_del(&mdev->cdev); + clear_bit(mdev->minor, media_devnode_nums); ++ mutex_unlock(&media_devnode_lock); ++ + return ret; + } + +-- +2.25.1 + diff --git a/queue-4.4/media-devnode-fix-namespace-mess.patch b/queue-4.4/media-devnode-fix-namespace-mess.patch new file mode 100644 index 00000000000..279b6e7603c --- /dev/null +++ b/queue-4.4/media-devnode-fix-namespace-mess.patch @@ -0,0 +1,355 @@ +From 2657e725986ccd5cdae49ec62d204042f6df2f44 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 23 Mar 2016 11:22:57 -0300 +Subject: media-devnode: fix namespace mess + +From: Mauro Carvalho Chehab + +commit 163f1e93e995048b894c5fc86a6034d16beed740 upstream. + +Along all media controller code, "mdev" is used to represent +a pointer to struct media_device, and "devnode" for a pointer +to struct media_devnode. + +However, inside media-devnode.[ch], "mdev" is used to represent +a pointer to struct media_devnode. + +This is very confusing and may lead to development errors. + +So, let's change all occurrences at media-devnode.[ch] to +also use "devnode" for such pointers. + +This patch doesn't make any functional changes. + +Signed-off-by: Mauro Carvalho Chehab +Signed-off-by: Mauro Carvalho Chehab +[bwh: Backported to 4.4: adjust filename, context] +Signed-off-by: Ben Hutchings +Signed-off-by: Sasha Levin +--- + drivers/media/media-devnode.c | 114 +++++++++++++++++----------------- + include/media/media-devnode.h | 10 +-- + 2 files changed, 62 insertions(+), 62 deletions(-) + +diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c +index 86c7c3732c84..98211c570e11 100644 +--- a/drivers/media/media-devnode.c ++++ b/drivers/media/media-devnode.c +@@ -59,21 +59,21 @@ static DECLARE_BITMAP(media_devnode_nums, MEDIA_NUM_DEVICES); + /* Called when the last user of the media device exits. */ + static void media_devnode_release(struct device *cd) + { +- struct media_devnode *mdev = to_media_devnode(cd); ++ struct media_devnode *devnode = to_media_devnode(cd); + + mutex_lock(&media_devnode_lock); + + /* Delete the cdev on this minor as well */ +- cdev_del(&mdev->cdev); ++ cdev_del(&devnode->cdev); + + /* Mark device node number as free */ +- clear_bit(mdev->minor, media_devnode_nums); ++ clear_bit(devnode->minor, media_devnode_nums); + + mutex_unlock(&media_devnode_lock); + + /* Release media_devnode and perform other cleanups as needed. */ +- if (mdev->release) +- mdev->release(mdev); ++ if (devnode->release) ++ devnode->release(devnode); + } + + static struct bus_type media_bus_type = { +@@ -83,37 +83,37 @@ static struct bus_type media_bus_type = { + static ssize_t media_read(struct file *filp, char __user *buf, + size_t sz, loff_t *off) + { +- struct media_devnode *mdev = media_devnode_data(filp); ++ struct media_devnode *devnode = media_devnode_data(filp); + +- if (!mdev->fops->read) ++ if (!devnode->fops->read) + return -EINVAL; +- if (!media_devnode_is_registered(mdev)) ++ if (!media_devnode_is_registered(devnode)) + return -EIO; +- return mdev->fops->read(filp, buf, sz, off); ++ return devnode->fops->read(filp, buf, sz, off); + } + + static ssize_t media_write(struct file *filp, const char __user *buf, + size_t sz, loff_t *off) + { +- struct media_devnode *mdev = media_devnode_data(filp); ++ struct media_devnode *devnode = media_devnode_data(filp); + +- if (!mdev->fops->write) ++ if (!devnode->fops->write) + return -EINVAL; +- if (!media_devnode_is_registered(mdev)) ++ if (!media_devnode_is_registered(devnode)) + return -EIO; +- return mdev->fops->write(filp, buf, sz, off); ++ return devnode->fops->write(filp, buf, sz, off); + } + + static unsigned int media_poll(struct file *filp, + struct poll_table_struct *poll) + { +- struct media_devnode *mdev = media_devnode_data(filp); ++ struct media_devnode *devnode = media_devnode_data(filp); + +- if (!media_devnode_is_registered(mdev)) ++ if (!media_devnode_is_registered(devnode)) + return POLLERR | POLLHUP; +- if (!mdev->fops->poll) ++ if (!devnode->fops->poll) + return DEFAULT_POLLMASK; +- return mdev->fops->poll(filp, poll); ++ return devnode->fops->poll(filp, poll); + } + + static long +@@ -121,12 +121,12 @@ __media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg, + long (*ioctl_func)(struct file *filp, unsigned int cmd, + unsigned long arg)) + { +- struct media_devnode *mdev = media_devnode_data(filp); ++ struct media_devnode *devnode = media_devnode_data(filp); + + if (!ioctl_func) + return -ENOTTY; + +- if (!media_devnode_is_registered(mdev)) ++ if (!media_devnode_is_registered(devnode)) + return -EIO; + + return ioctl_func(filp, cmd, arg); +@@ -134,9 +134,9 @@ __media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg, + + static long media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) + { +- struct media_devnode *mdev = media_devnode_data(filp); ++ struct media_devnode *devnode = media_devnode_data(filp); + +- return __media_ioctl(filp, cmd, arg, mdev->fops->ioctl); ++ return __media_ioctl(filp, cmd, arg, devnode->fops->ioctl); + } + + #ifdef CONFIG_COMPAT +@@ -144,9 +144,9 @@ static long media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) + static long media_compat_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) + { +- struct media_devnode *mdev = media_devnode_data(filp); ++ struct media_devnode *devnode = media_devnode_data(filp); + +- return __media_ioctl(filp, cmd, arg, mdev->fops->compat_ioctl); ++ return __media_ioctl(filp, cmd, arg, devnode->fops->compat_ioctl); + } + + #endif /* CONFIG_COMPAT */ +@@ -154,7 +154,7 @@ static long media_compat_ioctl(struct file *filp, unsigned int cmd, + /* Override for the open function */ + static int media_open(struct inode *inode, struct file *filp) + { +- struct media_devnode *mdev; ++ struct media_devnode *devnode; + int ret; + + /* Check if the media device is available. This needs to be done with +@@ -164,23 +164,23 @@ static int media_open(struct inode *inode, struct file *filp) + * a crash. + */ + mutex_lock(&media_devnode_lock); +- mdev = container_of(inode->i_cdev, struct media_devnode, cdev); ++ devnode = container_of(inode->i_cdev, struct media_devnode, cdev); + /* return ENXIO if the media device has been removed + already or if it is not registered anymore. */ +- if (!media_devnode_is_registered(mdev)) { ++ if (!media_devnode_is_registered(devnode)) { + mutex_unlock(&media_devnode_lock); + return -ENXIO; + } + /* and increase the device refcount */ +- get_device(&mdev->dev); ++ get_device(&devnode->dev); + mutex_unlock(&media_devnode_lock); + +- filp->private_data = mdev; ++ filp->private_data = devnode; + +- if (mdev->fops->open) { +- ret = mdev->fops->open(filp); ++ if (devnode->fops->open) { ++ ret = devnode->fops->open(filp); + if (ret) { +- put_device(&mdev->dev); ++ put_device(&devnode->dev); + filp->private_data = NULL; + return ret; + } +@@ -192,16 +192,16 @@ static int media_open(struct inode *inode, struct file *filp) + /* Override for the release function */ + static int media_release(struct inode *inode, struct file *filp) + { +- struct media_devnode *mdev = media_devnode_data(filp); ++ struct media_devnode *devnode = media_devnode_data(filp); + +- if (mdev->fops->release) +- mdev->fops->release(filp); ++ if (devnode->fops->release) ++ devnode->fops->release(filp); + + filp->private_data = NULL; + + /* decrease the refcount unconditionally since the release() + return value is ignored. */ +- put_device(&mdev->dev); ++ put_device(&devnode->dev); + return 0; + } + +@@ -221,7 +221,7 @@ static const struct file_operations media_devnode_fops = { + + /** + * media_devnode_register - register a media device node +- * @mdev: media device node structure we want to register ++ * @devnode: media device node structure we want to register + * + * The registration code assigns minor numbers and registers the new device node + * with the kernel. An error is returned if no free minor number can be found, +@@ -233,7 +233,7 @@ static const struct file_operations media_devnode_fops = { + * the media_devnode structure is *not* called, so the caller is responsible for + * freeing any data. + */ +-int __must_check media_devnode_register(struct media_devnode *mdev, ++int __must_check media_devnode_register(struct media_devnode *devnode, + struct module *owner) + { + int minor; +@@ -251,40 +251,40 @@ int __must_check media_devnode_register(struct media_devnode *mdev, + set_bit(minor, media_devnode_nums); + mutex_unlock(&media_devnode_lock); + +- mdev->minor = minor; ++ devnode->minor = minor; + + /* Part 2: Initialize and register the character device */ +- cdev_init(&mdev->cdev, &media_devnode_fops); +- mdev->cdev.owner = owner; ++ cdev_init(&devnode->cdev, &media_devnode_fops); ++ devnode->cdev.owner = owner; + +- ret = cdev_add(&mdev->cdev, MKDEV(MAJOR(media_dev_t), mdev->minor), 1); ++ ret = cdev_add(&devnode->cdev, MKDEV(MAJOR(media_dev_t), devnode->minor), 1); + if (ret < 0) { + pr_err("%s: cdev_add failed\n", __func__); + goto error; + } + + /* Part 3: Register the media device */ +- mdev->dev.bus = &media_bus_type; +- mdev->dev.devt = MKDEV(MAJOR(media_dev_t), mdev->minor); +- mdev->dev.release = media_devnode_release; +- if (mdev->parent) +- mdev->dev.parent = mdev->parent; +- dev_set_name(&mdev->dev, "media%d", mdev->minor); +- ret = device_register(&mdev->dev); ++ devnode->dev.bus = &media_bus_type; ++ devnode->dev.devt = MKDEV(MAJOR(media_dev_t), devnode->minor); ++ devnode->dev.release = media_devnode_release; ++ if (devnode->parent) ++ devnode->dev.parent = devnode->parent; ++ dev_set_name(&devnode->dev, "media%d", devnode->minor); ++ ret = device_register(&devnode->dev); + if (ret < 0) { + pr_err("%s: device_register failed\n", __func__); + goto error; + } + + /* Part 4: Activate this minor. The char device can now be used. */ +- set_bit(MEDIA_FLAG_REGISTERED, &mdev->flags); ++ set_bit(MEDIA_FLAG_REGISTERED, &devnode->flags); + + return 0; + + error: + mutex_lock(&media_devnode_lock); +- cdev_del(&mdev->cdev); +- clear_bit(mdev->minor, media_devnode_nums); ++ cdev_del(&devnode->cdev); ++ clear_bit(devnode->minor, media_devnode_nums); + mutex_unlock(&media_devnode_lock); + + return ret; +@@ -292,7 +292,7 @@ int __must_check media_devnode_register(struct media_devnode *mdev, + + /** + * media_devnode_unregister - unregister a media device node +- * @mdev: the device node to unregister ++ * @devnode: the device node to unregister + * + * This unregisters the passed device. Future open calls will be met with + * errors. +@@ -300,16 +300,16 @@ int __must_check media_devnode_register(struct media_devnode *mdev, + * This function can safely be called if the device node has never been + * registered or has already been unregistered. + */ +-void media_devnode_unregister(struct media_devnode *mdev) ++void media_devnode_unregister(struct media_devnode *devnode) + { +- /* Check if mdev was ever registered at all */ +- if (!media_devnode_is_registered(mdev)) ++ /* Check if devnode was ever registered at all */ ++ if (!media_devnode_is_registered(devnode)) + return; + + mutex_lock(&media_devnode_lock); +- clear_bit(MEDIA_FLAG_REGISTERED, &mdev->flags); ++ clear_bit(MEDIA_FLAG_REGISTERED, &devnode->flags); + mutex_unlock(&media_devnode_lock); +- device_unregister(&mdev->dev); ++ device_unregister(&devnode->dev); + } + + /* +diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h +index 17ddae32060d..79f702d26d1f 100644 +--- a/include/media/media-devnode.h ++++ b/include/media/media-devnode.h +@@ -80,24 +80,24 @@ struct media_devnode { + unsigned long flags; /* Use bitops to access flags */ + + /* callbacks */ +- void (*release)(struct media_devnode *mdev); ++ void (*release)(struct media_devnode *devnode); + }; + + /* dev to media_devnode */ + #define to_media_devnode(cd) container_of(cd, struct media_devnode, dev) + +-int __must_check media_devnode_register(struct media_devnode *mdev, ++int __must_check media_devnode_register(struct media_devnode *devnode, + struct module *owner); +-void media_devnode_unregister(struct media_devnode *mdev); ++void media_devnode_unregister(struct media_devnode *devnode); + + static inline struct media_devnode *media_devnode_data(struct file *filp) + { + return filp->private_data; + } + +-static inline int media_devnode_is_registered(struct media_devnode *mdev) ++static inline int media_devnode_is_registered(struct media_devnode *devnode) + { +- return test_bit(MEDIA_FLAG_REGISTERED, &mdev->flags); ++ return test_bit(MEDIA_FLAG_REGISTERED, &devnode->flags); + } + + #endif /* _MEDIA_DEVNODE_H */ +-- +2.25.1 + diff --git a/queue-4.4/media-fix-media-devnode-ioctl-syscall-and-unregister.patch b/queue-4.4/media-fix-media-devnode-ioctl-syscall-and-unregister.patch new file mode 100644 index 00000000000..cc8e22bbf5a --- /dev/null +++ b/queue-4.4/media-fix-media-devnode-ioctl-syscall-and-unregister.patch @@ -0,0 +1,186 @@ +From 653727fd92f2a8d2db75dcf55ba5c2cbad1cfdd4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 10 Jun 2016 14:37:23 -0300 +Subject: media: fix media devnode ioctl/syscall and unregister race + +From: Shuah Khan + +commit 6f0dd24a084a17f9984dd49dffbf7055bf123993 upstream. + +Media devnode open/ioctl could be in progress when media device unregister +is initiated. System calls and ioctls check media device registered status +at the beginning, however, there is a window where unregister could be in +progress without changing the media devnode status to unregistered. + +process 1 process 2 +fd = open(/dev/media0) +media_devnode_is_registered() + (returns true here) + + media_device_unregister() + (unregister is in progress + and devnode isn't + unregistered yet) + ... +ioctl(fd, ...) +__media_ioctl() +media_devnode_is_registered() + (returns true here) + ... + media_devnode_unregister() + ... + (driver releases the media device + memory) + +media_device_ioctl() + (By this point + devnode->media_dev does not + point to allocated memory. + use-after free in in mutex_lock_nested) + +BUG: KASAN: use-after-free in mutex_lock_nested+0x79c/0x800 at addr +ffff8801ebe914f0 + +Fix it by clearing register bit when unregister starts to avoid the race. + +process 1 process 2 +fd = open(/dev/media0) +media_devnode_is_registered() + (could return true here) + + media_device_unregister() + (clear the register bit, + then start unregister.) + ... +ioctl(fd, ...) +__media_ioctl() +media_devnode_is_registered() + (return false here, ioctl + returns I/O error, and + will not access media + device memory) + ... + media_devnode_unregister() + ... + (driver releases the media device + memory) + +Signed-off-by: Shuah Khan +Suggested-by: Sakari Ailus +Reported-by: Mauro Carvalho Chehab +Tested-by: Mauro Carvalho Chehab +Signed-off-by: Mauro Carvalho Chehab +[bwh: Backported to 4.4: adjut filename, context] +Signed-off-by: Ben Hutchings +Signed-off-by: Sasha Levin +--- + drivers/media/media-device.c | 15 ++++++++------- + drivers/media/media-devnode.c | 19 ++++++++++++------- + include/media/media-devnode.h | 14 ++++++++++++++ + 3 files changed, 34 insertions(+), 14 deletions(-) + +diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c +index 5d79cd481730..0ca9506f4654 100644 +--- a/drivers/media/media-device.c ++++ b/drivers/media/media-device.c +@@ -405,6 +405,7 @@ int __must_check __media_device_register(struct media_device *mdev, + if (ret < 0) { + /* devnode free is handled in media_devnode_*() */ + mdev->devnode = NULL; ++ media_devnode_unregister_prepare(devnode); + media_devnode_unregister(devnode); + return ret; + } +@@ -423,16 +424,16 @@ void media_device_unregister(struct media_device *mdev) + struct media_entity *entity; + struct media_entity *next; + ++ /* Clear the devnode register bit to avoid races with media dev open */ ++ media_devnode_unregister_prepare(mdev->devnode); ++ + list_for_each_entry_safe(entity, next, &mdev->entities, list) + media_device_unregister_entity(entity); + +- /* Check if mdev devnode was registered */ +- if (media_devnode_is_registered(mdev->devnode)) { +- device_remove_file(&mdev->devnode->dev, &dev_attr_model); +- media_devnode_unregister(mdev->devnode); +- /* devnode free is handled in media_devnode_*() */ +- mdev->devnode = NULL; +- } ++ device_remove_file(&mdev->devnode->dev, &dev_attr_model); ++ media_devnode_unregister(mdev->devnode); ++ /* devnode free is handled in media_devnode_*() */ ++ mdev->devnode = NULL; + } + EXPORT_SYMBOL_GPL(media_device_unregister); + +diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c +index 45bb70d27224..e887120d19aa 100644 +--- a/drivers/media/media-devnode.c ++++ b/drivers/media/media-devnode.c +@@ -302,6 +302,17 @@ int __must_check media_devnode_register(struct media_device *mdev, + return ret; + } + ++void media_devnode_unregister_prepare(struct media_devnode *devnode) ++{ ++ /* Check if devnode was ever registered at all */ ++ if (!media_devnode_is_registered(devnode)) ++ return; ++ ++ mutex_lock(&media_devnode_lock); ++ clear_bit(MEDIA_FLAG_REGISTERED, &devnode->flags); ++ mutex_unlock(&media_devnode_lock); ++} ++ + /** + * media_devnode_unregister - unregister a media device node + * @devnode: the device node to unregister +@@ -309,17 +320,11 @@ int __must_check media_devnode_register(struct media_device *mdev, + * This unregisters the passed device. Future open calls will be met with + * errors. + * +- * This function can safely be called if the device node has never been +- * registered or has already been unregistered. ++ * Should be called after media_devnode_unregister_prepare() + */ + void media_devnode_unregister(struct media_devnode *devnode) + { +- /* Check if devnode was ever registered at all */ +- if (!media_devnode_is_registered(devnode)) +- return; +- + mutex_lock(&media_devnode_lock); +- clear_bit(MEDIA_FLAG_REGISTERED, &devnode->flags); + /* Delete the cdev on this minor as well */ + cdev_del(&devnode->cdev); + mutex_unlock(&media_devnode_lock); +diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h +index 8b854c044032..d5ff95bf2d4b 100644 +--- a/include/media/media-devnode.h ++++ b/include/media/media-devnode.h +@@ -93,6 +93,20 @@ struct media_devnode { + int __must_check media_devnode_register(struct media_device *mdev, + struct media_devnode *devnode, + struct module *owner); ++ ++/** ++ * media_devnode_unregister_prepare - clear the media device node register bit ++ * @devnode: the device node to prepare for unregister ++ * ++ * This clears the passed device register bit. Future open calls will be met ++ * with errors. Should be called before media_devnode_unregister() to avoid ++ * races with unregister and device file open calls. ++ * ++ * This function can safely be called if the device node has never been ++ * registered or has already been unregistered. ++ */ ++void media_devnode_unregister_prepare(struct media_devnode *devnode); ++ + void media_devnode_unregister(struct media_devnode *devnode); + + static inline struct media_devnode *media_devnode_data(struct file *filp) +-- +2.25.1 + diff --git a/queue-4.4/media-fix-media_open-to-clear-filp-private_data-in-e.patch b/queue-4.4/media-fix-media_open-to-clear-filp-private_data-in-e.patch new file mode 100644 index 00000000000..8b5ca3ca742 --- /dev/null +++ b/queue-4.4/media-fix-media_open-to-clear-filp-private_data-in-e.patch @@ -0,0 +1,36 @@ +From b977a5248a55f01ce98e002d5398f38b9f4c991e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 27 Jan 2016 21:49:33 -0200 +Subject: media: Fix media_open() to clear filp->private_data in error leg + +From: Shuah Khan + +commit d40ec6fdb0b03b7be4c7923a3da0e46bf943740a upstream. + +Fix media_open() to clear filp->private_data when file open +fails. + +Signed-off-by: Shuah Khan +Acked-by: Sakari Ailus +Signed-off-by: Mauro Carvalho Chehab +Signed-off-by: Ben Hutchings +Signed-off-by: Sasha Levin +--- + drivers/media/media-devnode.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c +index ebf9626e5ae5..a8cb52dc8c4f 100644 +--- a/drivers/media/media-devnode.c ++++ b/drivers/media/media-devnode.c +@@ -181,6 +181,7 @@ static int media_open(struct inode *inode, struct file *filp) + ret = mdev->fops->open(filp); + if (ret) { + put_device(&mdev->dev); ++ filp->private_data = NULL; + return ret; + } + } +-- +2.25.1 + diff --git a/queue-4.4/media-fix-use-after-free-in-cdev_put-when-app-exits-.patch b/queue-4.4/media-fix-use-after-free-in-cdev_put-when-app-exits-.patch new file mode 100644 index 00000000000..41f0ac55ca2 --- /dev/null +++ b/queue-4.4/media-fix-use-after-free-in-cdev_put-when-app-exits-.patch @@ -0,0 +1,217 @@ +From 3725671d2b4f9d4d74c307c446691e9078ae0d24 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 4 May 2016 16:48:28 -0300 +Subject: media: fix use-after-free in cdev_put() when app exits after driver + unbind + +From: Shuah Khan + +commit 5b28dde51d0ccc54cee70756e1800d70bed7114a upstream. + +When driver unbinds while media_ioctl is in progress, cdev_put() fails with +when app exits after driver unbinds. + +Add devnode struct device kobj as the cdev parent kobject. cdev_add() gets +a reference to it and releases it in cdev_del() ensuring that the devnode +is not deallocated as long as the application has the device file open. + +media_devnode_register() initializes the struct device kobj before calling +cdev_add(). media_devnode_unregister() does cdev_del() and then deletes the +device. devnode is released when the last reference to the struct device is +gone. + +This problem is found on uvcvideo, em28xx, and au0828 drivers and fix has +been tested on all three. + +kernel: [ 193.599736] BUG: KASAN: use-after-free in cdev_put+0x4e/0x50 +kernel: [ 193.599745] Read of size 8 by task media_device_te/1851 +kernel: [ 193.599792] INFO: Allocated in __media_device_register+0x54 +kernel: [ 193.599951] INFO: Freed in media_devnode_release+0xa4/0xc0 + +kernel: [ 193.601083] Call Trace: +kernel: [ 193.601093] [] dump_stack+0x67/0x94 +kernel: [ 193.601102] [] print_trailer+0x112/0x1a0 +kernel: [ 193.601111] [] object_err+0x34/0x40 +kernel: [ 193.601119] [] kasan_report_error+0x224/0x530 +kernel: [ 193.601128] [] ? kzfree+0x2d/0x40 +kernel: [ 193.601137] [] ? kfree+0x1d2/0x1f0 +kernel: [ 193.601154] [] ? cdev_put+0x4e/0x50 +kernel: [ 193.601162] [] cdev_put+0x4e/0x50 +kernel: [ 193.601170] [] __fput+0x52b/0x6c0 +kernel: [ 193.601179] [] ? switch_task_namespaces+0x2a +kernel: [ 193.601188] [] ____fput+0xe/0x10 +kernel: [ 193.601196] [] task_work_run+0x133/0x1f0 +kernel: [ 193.601204] [] ? switch_task_namespaces+0x5e +kernel: [ 193.601213] [] do_exit+0x72c/0x2c20 +kernel: [ 193.601224] [] ? release_task+0x1250/0x1250 +- +- +- +kernel: [ 193.601360] [] ? exit_to_usermode_loop+0xe7 +kernel: [ 193.601368] [] exit_to_usermode_loop+0x120 +kernel: [ 193.601376] [] syscall_return_slowpath+0x16a +kernel: [ 193.601386] [] entry_SYSCALL_64_fastpath+0xa6 + +Signed-off-by: Shuah Khan +Tested-by: Mauro Carvalho Chehab +Signed-off-by: Mauro Carvalho Chehab +Signed-off-by: Ben Hutchings +Signed-off-by: Sasha Levin +--- + drivers/media/media-device.c | 6 +++-- + drivers/media/media-devnode.c | 48 +++++++++++++++++++++-------------- + 2 files changed, 33 insertions(+), 21 deletions(-) + +diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c +index fb018fe1a8f7..5d79cd481730 100644 +--- a/drivers/media/media-device.c ++++ b/drivers/media/media-device.c +@@ -396,16 +396,16 @@ int __must_check __media_device_register(struct media_device *mdev, + devnode->release = media_device_release; + ret = media_devnode_register(mdev, devnode, owner); + if (ret < 0) { ++ /* devnode free is handled in media_devnode_*() */ + mdev->devnode = NULL; +- kfree(devnode); + return ret; + } + + ret = device_create_file(&devnode->dev, &dev_attr_model); + if (ret < 0) { ++ /* devnode free is handled in media_devnode_*() */ + mdev->devnode = NULL; + media_devnode_unregister(devnode); +- kfree(devnode); + return ret; + } + +@@ -430,6 +430,8 @@ void media_device_unregister(struct media_device *mdev) + if (media_devnode_is_registered(mdev->devnode)) { + device_remove_file(&mdev->devnode->dev, &dev_attr_model); + media_devnode_unregister(mdev->devnode); ++ /* devnode free is handled in media_devnode_*() */ ++ mdev->devnode = NULL; + } + } + EXPORT_SYMBOL_GPL(media_device_unregister); +diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c +index 000efb17b95b..45bb70d27224 100644 +--- a/drivers/media/media-devnode.c ++++ b/drivers/media/media-devnode.c +@@ -63,13 +63,8 @@ static void media_devnode_release(struct device *cd) + struct media_devnode *devnode = to_media_devnode(cd); + + mutex_lock(&media_devnode_lock); +- +- /* Delete the cdev on this minor as well */ +- cdev_del(&devnode->cdev); +- + /* Mark device node number as free */ + clear_bit(devnode->minor, media_devnode_nums); +- + mutex_unlock(&media_devnode_lock); + + /* Release media_devnode and perform other cleanups as needed. */ +@@ -77,6 +72,7 @@ static void media_devnode_release(struct device *cd) + devnode->release(devnode); + + kfree(devnode); ++ pr_debug("%s: Media Devnode Deallocated\n", __func__); + } + + static struct bus_type media_bus_type = { +@@ -205,6 +201,8 @@ static int media_release(struct inode *inode, struct file *filp) + /* decrease the refcount unconditionally since the release() + return value is ignored. */ + put_device(&devnode->dev); ++ ++ pr_debug("%s: Media Release\n", __func__); + return 0; + } + +@@ -250,6 +248,7 @@ int __must_check media_devnode_register(struct media_device *mdev, + if (minor == MEDIA_NUM_DEVICES) { + mutex_unlock(&media_devnode_lock); + pr_err("could not get a free minor\n"); ++ kfree(devnode); + return -ENFILE; + } + +@@ -259,27 +258,31 @@ int __must_check media_devnode_register(struct media_device *mdev, + devnode->minor = minor; + devnode->media_dev = mdev; + ++ /* Part 1: Initialize dev now to use dev.kobj for cdev.kobj.parent */ ++ devnode->dev.bus = &media_bus_type; ++ devnode->dev.devt = MKDEV(MAJOR(media_dev_t), devnode->minor); ++ devnode->dev.release = media_devnode_release; ++ if (devnode->parent) ++ devnode->dev.parent = devnode->parent; ++ dev_set_name(&devnode->dev, "media%d", devnode->minor); ++ device_initialize(&devnode->dev); ++ + /* Part 2: Initialize and register the character device */ + cdev_init(&devnode->cdev, &media_devnode_fops); + devnode->cdev.owner = owner; ++ devnode->cdev.kobj.parent = &devnode->dev.kobj; + + ret = cdev_add(&devnode->cdev, MKDEV(MAJOR(media_dev_t), devnode->minor), 1); + if (ret < 0) { + pr_err("%s: cdev_add failed\n", __func__); +- goto error; ++ goto cdev_add_error; + } + +- /* Part 3: Register the media device */ +- devnode->dev.bus = &media_bus_type; +- devnode->dev.devt = MKDEV(MAJOR(media_dev_t), devnode->minor); +- devnode->dev.release = media_devnode_release; +- if (devnode->parent) +- devnode->dev.parent = devnode->parent; +- dev_set_name(&devnode->dev, "media%d", devnode->minor); +- ret = device_register(&devnode->dev); ++ /* Part 3: Add the media device */ ++ ret = device_add(&devnode->dev); + if (ret < 0) { +- pr_err("%s: device_register failed\n", __func__); +- goto error; ++ pr_err("%s: device_add failed\n", __func__); ++ goto device_add_error; + } + + /* Part 4: Activate this minor. The char device can now be used. */ +@@ -287,12 +290,15 @@ int __must_check media_devnode_register(struct media_device *mdev, + + return 0; + +-error: +- mutex_lock(&media_devnode_lock); ++device_add_error: + cdev_del(&devnode->cdev); ++cdev_add_error: ++ mutex_lock(&media_devnode_lock); + clear_bit(devnode->minor, media_devnode_nums); ++ devnode->media_dev = NULL; + mutex_unlock(&media_devnode_lock); + ++ put_device(&devnode->dev); + return ret; + } + +@@ -314,8 +320,12 @@ void media_devnode_unregister(struct media_devnode *devnode) + + mutex_lock(&media_devnode_lock); + clear_bit(MEDIA_FLAG_REGISTERED, &devnode->flags); ++ /* Delete the cdev on this minor as well */ ++ cdev_del(&devnode->cdev); + mutex_unlock(&media_devnode_lock); +- device_unregister(&devnode->dev); ++ device_del(&devnode->dev); ++ devnode->media_dev = NULL; ++ put_device(&devnode->dev); + } + + /* +-- +2.25.1 + diff --git a/queue-4.4/padata-initialize-pd-cpu-with-effective-cpumask.patch b/queue-4.4/padata-initialize-pd-cpu-with-effective-cpumask.patch new file mode 100644 index 00000000000..229369117e0 --- /dev/null +++ b/queue-4.4/padata-initialize-pd-cpu-with-effective-cpumask.patch @@ -0,0 +1,76 @@ +From 0f5c19f725997d7b5a5bd056a0128ff323cf0d86 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 May 2020 16:51:44 -0400 +Subject: padata: initialize pd->cpu with effective cpumask + +From: Daniel Jordan + +[ Upstream commit ec9c7d19336ee98ecba8de80128aa405c45feebb ] + +Exercising CPU hotplug on a 5.2 kernel with recent padata fixes from +cryptodev-2.6.git in an 8-CPU kvm guest... + + # modprobe tcrypt alg="pcrypt(rfc4106(gcm(aes)))" type=3 + # echo 0 > /sys/devices/system/cpu/cpu1/online + # echo c > /sys/kernel/pcrypt/pencrypt/parallel_cpumask + # modprobe tcrypt mode=215 + +...caused the following crash: + + BUG: kernel NULL pointer dereference, address: 0000000000000000 + #PF: supervisor read access in kernel mode + #PF: error_code(0x0000) - not-present page + PGD 0 P4D 0 + Oops: 0000 [#1] SMP PTI + CPU: 2 PID: 134 Comm: kworker/2:2 Not tainted 5.2.0-padata-base+ #7 + Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.12.0- + Workqueue: pencrypt padata_parallel_worker + RIP: 0010:padata_reorder+0xcb/0x180 + ... + Call Trace: + padata_do_serial+0x57/0x60 + pcrypt_aead_enc+0x3a/0x50 [pcrypt] + padata_parallel_worker+0x9b/0xe0 + process_one_work+0x1b5/0x3f0 + worker_thread+0x4a/0x3c0 + ... + +In padata_alloc_pd, pd->cpu is set using the user-supplied cpumask +instead of the effective cpumask, and in this case cpumask_first picked +an offline CPU. + +The offline CPU's reorder->list.next is NULL in padata_reorder because +the list wasn't initialized in padata_init_pqueues, which only operates +on CPUs in the effective mask. + +Fix by using the effective mask in padata_alloc_pd. + +Fixes: 6fc4dbcf0276 ("padata: Replace delayed timer with immediate workqueue in padata_reorder") +Signed-off-by: Daniel Jordan +Cc: Herbert Xu +Cc: Steffen Klassert +Cc: linux-crypto@vger.kernel.org +Cc: linux-kernel@vger.kernel.org +Signed-off-by: Herbert Xu +Signed-off-by: Daniel Jordan +Signed-off-by: Sasha Levin +--- + kernel/padata.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/kernel/padata.c b/kernel/padata.c +index e5966eedfa36..43b72f5dfe07 100644 +--- a/kernel/padata.c ++++ b/kernel/padata.c +@@ -449,7 +449,7 @@ static struct parallel_data *padata_alloc_pd(struct padata_instance *pinst, + atomic_set(&pd->refcnt, 1); + pd->pinst = pinst; + spin_lock_init(&pd->lock); +- pd->cpu = cpumask_first(pcpumask); ++ pd->cpu = cpumask_first(pd->cpumask.pcpu); + INIT_WORK(&pd->reorder_work, invoke_padata_reorder); + + return pd; +-- +2.25.1 + diff --git a/queue-4.4/padata-purge-get_cpu-and-reorder_via_wq-from-padata_.patch b/queue-4.4/padata-purge-get_cpu-and-reorder_via_wq-from-padata_.patch new file mode 100644 index 00000000000..70e6151f702 --- /dev/null +++ b/queue-4.4/padata-purge-get_cpu-and-reorder_via_wq-from-padata_.patch @@ -0,0 +1,68 @@ +From 05539e0e77745aac87da493d8452295cb67fc7a8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 May 2020 16:51:45 -0400 +Subject: padata: purge get_cpu and reorder_via_wq from padata_do_serial + +From: Daniel Jordan + +[ Upstream commit 065cf577135a4977931c7a1e1edf442bfd9773dd] + +With the removal of the padata timer, padata_do_serial no longer +needs special CPU handling, so remove it. + +Signed-off-by: Daniel Jordan +Cc: Herbert Xu +Cc: Steffen Klassert +Cc: linux-crypto@vger.kernel.org +Cc: linux-kernel@vger.kernel.org +Signed-off-by: Herbert Xu +Signed-off-by: Daniel Jordan +Signed-off-by: Sasha Levin +--- + kernel/padata.c | 23 +++-------------------- + 1 file changed, 3 insertions(+), 20 deletions(-) + +diff --git a/kernel/padata.c b/kernel/padata.c +index 43b72f5dfe07..c50975f43b34 100644 +--- a/kernel/padata.c ++++ b/kernel/padata.c +@@ -322,24 +322,9 @@ static void padata_serial_worker(struct work_struct *serial_work) + */ + void padata_do_serial(struct padata_priv *padata) + { +- int cpu; +- struct padata_parallel_queue *pqueue; +- struct parallel_data *pd; +- int reorder_via_wq = 0; +- +- pd = padata->pd; +- +- cpu = get_cpu(); +- +- /* We need to enqueue the padata object into the correct +- * per-cpu queue. +- */ +- if (cpu != padata->cpu) { +- reorder_via_wq = 1; +- cpu = padata->cpu; +- } +- +- pqueue = per_cpu_ptr(pd->pqueue, cpu); ++ struct parallel_data *pd = padata->pd; ++ struct padata_parallel_queue *pqueue = per_cpu_ptr(pd->pqueue, ++ padata->cpu); + + spin_lock(&pqueue->reorder.lock); + list_add_tail(&padata->list, &pqueue->reorder.list); +@@ -353,8 +338,6 @@ void padata_do_serial(struct padata_priv *padata) + */ + smp_mb__after_atomic(); + +- put_cpu(); +- + padata_reorder(pd); + } + EXPORT_SYMBOL(padata_do_serial); +-- +2.25.1 + diff --git a/queue-4.4/padata-replace-delayed-timer-with-immediate-workqueu.patch b/queue-4.4/padata-replace-delayed-timer-with-immediate-workqueu.patch new file mode 100644 index 00000000000..2a042139d9a --- /dev/null +++ b/queue-4.4/padata-replace-delayed-timer-with-immediate-workqueu.patch @@ -0,0 +1,308 @@ +From 4c099d6cdd80618907639cb023ce9a879eb83f6c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 May 2020 16:51:43 -0400 +Subject: padata: Replace delayed timer with immediate workqueue in + padata_reorder + +From: Herbert Xu + +[ Upstream commit 6fc4dbcf0276279d488c5fbbfabe94734134f4fa ] + +The function padata_reorder will use a timer when it cannot progress +while completed jobs are outstanding (pd->reorder_objects > 0). This +is suboptimal as if we do end up using the timer then it would have +introduced a gratuitous delay of one second. + +In fact we can easily distinguish between whether completed jobs +are outstanding and whether we can make progress. All we have to +do is look at the next pqueue list. + +This patch does that by replacing pd->processed with pd->cpu so +that the next pqueue is more accessible. + +A work queue is used instead of the original try_again to avoid +hogging the CPU. + +Note that we don't bother removing the work queue in +padata_flush_queues because the whole premise is broken. You +cannot flush async crypto requests so it makes no sense to even +try. A subsequent patch will fix it by replacing it with a ref +counting scheme. + +Signed-off-by: Herbert Xu +[dj: - adjust context + - corrected setup_timer -> timer_setup to delete hunk + - skip padata_flush_queues() hunk, function already removed + in 4.4] +Signed-off-by: Daniel Jordan +Signed-off-by: Sasha Levin +--- + include/linux/padata.h | 13 ++---- + kernel/padata.c | 95 ++++++++---------------------------------- + 2 files changed, 22 insertions(+), 86 deletions(-) + +diff --git a/include/linux/padata.h b/include/linux/padata.h +index e74d61fa50fe..547a8d1e4a3b 100644 +--- a/include/linux/padata.h ++++ b/include/linux/padata.h +@@ -24,7 +24,6 @@ + #include + #include + #include +-#include + #include + #include + +@@ -85,18 +84,14 @@ struct padata_serial_queue { + * @serial: List to wait for serialization after reordering. + * @pwork: work struct for parallelization. + * @swork: work struct for serialization. +- * @pd: Backpointer to the internal control structure. + * @work: work struct for parallelization. +- * @reorder_work: work struct for reordering. + * @num_obj: Number of objects that are processed by this cpu. + * @cpu_index: Index of the cpu. + */ + struct padata_parallel_queue { + struct padata_list parallel; + struct padata_list reorder; +- struct parallel_data *pd; + struct work_struct work; +- struct work_struct reorder_work; + atomic_t num_obj; + int cpu_index; + }; +@@ -122,10 +117,10 @@ struct padata_cpumask { + * @reorder_objects: Number of objects waiting in the reorder queues. + * @refcnt: Number of objects holding a reference on this parallel_data. + * @max_seq_nr: Maximal used sequence number. ++ * @cpu: Next CPU to be processed. + * @cpumask: The cpumasks in use for parallel and serial workers. ++ * @reorder_work: work struct for reordering. + * @lock: Reorder lock. +- * @processed: Number of already processed objects. +- * @timer: Reorder timer. + */ + struct parallel_data { + struct padata_instance *pinst; +@@ -134,10 +129,10 @@ struct parallel_data { + atomic_t reorder_objects; + atomic_t refcnt; + atomic_t seq_nr; ++ int cpu; + struct padata_cpumask cpumask; ++ struct work_struct reorder_work; + spinlock_t lock ____cacheline_aligned; +- unsigned int processed; +- struct timer_list timer; + }; + + /** +diff --git a/kernel/padata.c b/kernel/padata.c +index 4f860043a8e5..e5966eedfa36 100644 +--- a/kernel/padata.c ++++ b/kernel/padata.c +@@ -165,23 +165,12 @@ EXPORT_SYMBOL(padata_do_parallel); + */ + static struct padata_priv *padata_get_next(struct parallel_data *pd) + { +- int cpu, num_cpus; +- unsigned int next_nr, next_index; + struct padata_parallel_queue *next_queue; + struct padata_priv *padata; + struct padata_list *reorder; ++ int cpu = pd->cpu; + +- num_cpus = cpumask_weight(pd->cpumask.pcpu); +- +- /* +- * Calculate the percpu reorder queue and the sequence +- * number of the next object. +- */ +- next_nr = pd->processed; +- next_index = next_nr % num_cpus; +- cpu = padata_index_to_cpu(pd, next_index); + next_queue = per_cpu_ptr(pd->pqueue, cpu); +- + reorder = &next_queue->reorder; + + spin_lock(&reorder->lock); +@@ -192,7 +181,8 @@ static struct padata_priv *padata_get_next(struct parallel_data *pd) + list_del_init(&padata->list); + atomic_dec(&pd->reorder_objects); + +- pd->processed++; ++ pd->cpu = cpumask_next_wrap(cpu, pd->cpumask.pcpu, -1, ++ false); + + spin_unlock(&reorder->lock); + goto out; +@@ -215,6 +205,7 @@ static void padata_reorder(struct parallel_data *pd) + struct padata_priv *padata; + struct padata_serial_queue *squeue; + struct padata_instance *pinst = pd->pinst; ++ struct padata_parallel_queue *next_queue; + + /* + * We need to ensure that only one cpu can work on dequeueing of +@@ -246,7 +237,6 @@ static void padata_reorder(struct parallel_data *pd) + * so exit immediately. + */ + if (PTR_ERR(padata) == -ENODATA) { +- del_timer(&pd->timer); + spin_unlock_bh(&pd->lock); + return; + } +@@ -265,70 +255,29 @@ static void padata_reorder(struct parallel_data *pd) + + /* + * The next object that needs serialization might have arrived to +- * the reorder queues in the meantime, we will be called again +- * from the timer function if no one else cares for it. ++ * the reorder queues in the meantime. + * +- * Ensure reorder_objects is read after pd->lock is dropped so we see +- * an increment from another task in padata_do_serial. Pairs with ++ * Ensure reorder queue is read after pd->lock is dropped so we see ++ * new objects from another task in padata_do_serial. Pairs with + * smp_mb__after_atomic in padata_do_serial. + */ + smp_mb(); +- if (atomic_read(&pd->reorder_objects) +- && !(pinst->flags & PADATA_RESET)) +- mod_timer(&pd->timer, jiffies + HZ); +- else +- del_timer(&pd->timer); + +- return; ++ next_queue = per_cpu_ptr(pd->pqueue, pd->cpu); ++ if (!list_empty(&next_queue->reorder.list)) ++ queue_work(pinst->wq, &pd->reorder_work); + } + + static void invoke_padata_reorder(struct work_struct *work) + { +- struct padata_parallel_queue *pqueue; + struct parallel_data *pd; + + local_bh_disable(); +- pqueue = container_of(work, struct padata_parallel_queue, reorder_work); +- pd = pqueue->pd; ++ pd = container_of(work, struct parallel_data, reorder_work); + padata_reorder(pd); + local_bh_enable(); + } + +-static void padata_reorder_timer(unsigned long arg) +-{ +- struct parallel_data *pd = (struct parallel_data *)arg; +- unsigned int weight; +- int target_cpu, cpu; +- +- cpu = get_cpu(); +- +- /* We don't lock pd here to not interfere with parallel processing +- * padata_reorder() calls on other CPUs. We just need any CPU out of +- * the cpumask.pcpu set. It would be nice if it's the right one but +- * it doesn't matter if we're off to the next one by using an outdated +- * pd->processed value. +- */ +- weight = cpumask_weight(pd->cpumask.pcpu); +- target_cpu = padata_index_to_cpu(pd, pd->processed % weight); +- +- /* ensure to call the reorder callback on the correct CPU */ +- if (cpu != target_cpu) { +- struct padata_parallel_queue *pqueue; +- struct padata_instance *pinst; +- +- /* The timer function is serialized wrt itself -- no locking +- * needed. +- */ +- pinst = pd->pinst; +- pqueue = per_cpu_ptr(pd->pqueue, target_cpu); +- queue_work_on(target_cpu, pinst->wq, &pqueue->reorder_work); +- } else { +- padata_reorder(pd); +- } +- +- put_cpu(); +-} +- + static void padata_serial_worker(struct work_struct *serial_work) + { + struct padata_serial_queue *squeue; +@@ -382,9 +331,8 @@ void padata_do_serial(struct padata_priv *padata) + + cpu = get_cpu(); + +- /* We need to run on the same CPU padata_do_parallel(.., padata, ..) +- * was called on -- or, at least, enqueue the padata object into the +- * correct per-cpu queue. ++ /* We need to enqueue the padata object into the correct ++ * per-cpu queue. + */ + if (cpu != padata->cpu) { + reorder_via_wq = 1; +@@ -394,12 +342,12 @@ void padata_do_serial(struct padata_priv *padata) + pqueue = per_cpu_ptr(pd->pqueue, cpu); + + spin_lock(&pqueue->reorder.lock); +- atomic_inc(&pd->reorder_objects); + list_add_tail(&padata->list, &pqueue->reorder.list); ++ atomic_inc(&pd->reorder_objects); + spin_unlock(&pqueue->reorder.lock); + + /* +- * Ensure the atomic_inc of reorder_objects above is ordered correctly ++ * Ensure the addition to the reorder list is ordered correctly + * with the trylock of pd->lock in padata_reorder. Pairs with smp_mb + * in padata_reorder. + */ +@@ -407,13 +355,7 @@ void padata_do_serial(struct padata_priv *padata) + + put_cpu(); + +- /* If we're running on the wrong CPU, call padata_reorder() via a +- * kernel worker. +- */ +- if (reorder_via_wq) +- queue_work_on(cpu, pd->pinst->wq, &pqueue->reorder_work); +- else +- padata_reorder(pd); ++ padata_reorder(pd); + } + EXPORT_SYMBOL(padata_do_serial); + +@@ -469,14 +411,12 @@ static void padata_init_pqueues(struct parallel_data *pd) + continue; + } + +- pqueue->pd = pd; + pqueue->cpu_index = cpu_index; + cpu_index++; + + __padata_list_init(&pqueue->reorder); + __padata_list_init(&pqueue->parallel); + INIT_WORK(&pqueue->work, padata_parallel_worker); +- INIT_WORK(&pqueue->reorder_work, invoke_padata_reorder); + atomic_set(&pqueue->num_obj, 0); + } + } +@@ -504,12 +444,13 @@ static struct parallel_data *padata_alloc_pd(struct padata_instance *pinst, + + padata_init_pqueues(pd); + padata_init_squeues(pd); +- setup_timer(&pd->timer, padata_reorder_timer, (unsigned long)pd); + atomic_set(&pd->seq_nr, -1); + atomic_set(&pd->reorder_objects, 0); + atomic_set(&pd->refcnt, 1); + pd->pinst = pinst; + spin_lock_init(&pd->lock); ++ pd->cpu = cpumask_first(pcpumask); ++ INIT_WORK(&pd->reorder_work, invoke_padata_reorder); + + return pd; + +-- +2.25.1 + diff --git a/queue-4.4/padata-set-cpu_index-of-unused-cpus-to-1.patch b/queue-4.4/padata-set-cpu_index-of-unused-cpus-to-1.patch new file mode 100644 index 00000000000..a46cdfafb5d --- /dev/null +++ b/queue-4.4/padata-set-cpu_index-of-unused-cpus-to-1.patch @@ -0,0 +1,50 @@ +From 0f46a45bb313a0489b467856ebeb3c168f76bbf3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 May 2020 16:51:41 -0400 +Subject: padata: set cpu_index of unused CPUs to -1 + +From: Mathias Krause + +[ Upstream commit 1bd845bcb41d5b7f83745e0cb99273eb376f2ec5 ] + +The parallel queue per-cpu data structure gets initialized only for CPUs +in the 'pcpu' CPU mask set. This is not sufficient as the reorder timer +may run on a different CPU and might wrongly decide it's the target CPU +for the next reorder item as per-cpu memory gets memset(0) and we might +be waiting for the first CPU in cpumask.pcpu, i.e. cpu_index 0. + +Make the '__this_cpu_read(pd->pqueue->cpu_index) == next_queue->cpu_index' +compare in padata_get_next() fail in this case by initializing the +cpu_index member of all per-cpu parallel queues. Use -1 for unused ones. + +Signed-off-by: Mathias Krause +Signed-off-by: Herbert Xu +Signed-off-by: Daniel Jordan +Signed-off-by: Sasha Levin +--- + kernel/padata.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/kernel/padata.c b/kernel/padata.c +index 8aef48c3267b..4f860043a8e5 100644 +--- a/kernel/padata.c ++++ b/kernel/padata.c +@@ -461,8 +461,14 @@ static void padata_init_pqueues(struct parallel_data *pd) + struct padata_parallel_queue *pqueue; + + cpu_index = 0; +- for_each_cpu(cpu, pd->cpumask.pcpu) { ++ for_each_possible_cpu(cpu) { + pqueue = per_cpu_ptr(pd->pqueue, cpu); ++ ++ if (!cpumask_test_cpu(cpu, pd->cpumask.pcpu)) { ++ pqueue->cpu_index = -1; ++ continue; ++ } ++ + pqueue->pd = pd; + pqueue->cpu_index = cpu_index; + cpu_index++; +-- +2.25.1 + diff --git a/queue-4.4/sched-fair-cpumask-export-for_each_cpu_wrap.patch b/queue-4.4/sched-fair-cpumask-export-for_each_cpu_wrap.patch new file mode 100644 index 00000000000..814519460ad --- /dev/null +++ b/queue-4.4/sched-fair-cpumask-export-for_each_cpu_wrap.patch @@ -0,0 +1,107 @@ +From 6ab43c8f5881b8385e74fee50517c67847f77565 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 May 2020 16:51:42 -0400 +Subject: sched/fair, cpumask: Export for_each_cpu_wrap() + +From: Peter Zijlstra + +[ Upstream commit c743f0a5c50f2fcbc628526279cfa24f3dabe182 ] + +More users for for_each_cpu_wrap() have appeared. Promote the construct +to generic cpumask interface. + +The implementation is slightly modified to reduce arguments. + +Signed-off-by: Peter Zijlstra (Intel) +Cc: Lauro Ramos Venancio +Cc: Linus Torvalds +Cc: Mike Galbraith +Cc: Peter Zijlstra +Cc: Rik van Riel +Cc: Thomas Gleixner +Cc: lwang@redhat.com +Link: http://lkml.kernel.org/r/20170414122005.o35me2h5nowqkxbv@hirez.programming.kicks-ass.net +Signed-off-by: Ingo Molnar +[dj: include only what's added to the cpumask interface, 4.4 doesn't + have them in the scheduler] +Signed-off-by: Daniel Jordan +Signed-off-by: Sasha Levin +--- + include/linux/cpumask.h | 17 +++++++++++++++++ + lib/cpumask.c | 32 ++++++++++++++++++++++++++++++++ + 2 files changed, 49 insertions(+) + +diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h +index bb3a4bb35183..1322883e7b46 100644 +--- a/include/linux/cpumask.h ++++ b/include/linux/cpumask.h +@@ -232,6 +232,23 @@ unsigned int cpumask_local_spread(unsigned int i, int node); + (cpu) = cpumask_next_zero((cpu), (mask)), \ + (cpu) < nr_cpu_ids;) + ++extern int cpumask_next_wrap(int n, const struct cpumask *mask, int start, bool wrap); ++ ++/** ++ * for_each_cpu_wrap - iterate over every cpu in a mask, starting at a specified location ++ * @cpu: the (optionally unsigned) integer iterator ++ * @mask: the cpumask poiter ++ * @start: the start location ++ * ++ * The implementation does not assume any bit in @mask is set (including @start). ++ * ++ * After the loop, cpu is >= nr_cpu_ids. ++ */ ++#define for_each_cpu_wrap(cpu, mask, start) \ ++ for ((cpu) = cpumask_next_wrap((start)-1, (mask), (start), false); \ ++ (cpu) < nr_cpumask_bits; \ ++ (cpu) = cpumask_next_wrap((cpu), (mask), (start), true)) ++ + /** + * for_each_cpu_and - iterate over every cpu in both masks + * @cpu: the (optionally unsigned) integer iterator +diff --git a/lib/cpumask.c b/lib/cpumask.c +index 5a70f6196f57..24f06e7abf92 100644 +--- a/lib/cpumask.c ++++ b/lib/cpumask.c +@@ -42,6 +42,38 @@ int cpumask_any_but(const struct cpumask *mask, unsigned int cpu) + return i; + } + ++/** ++ * cpumask_next_wrap - helper to implement for_each_cpu_wrap ++ * @n: the cpu prior to the place to search ++ * @mask: the cpumask pointer ++ * @start: the start point of the iteration ++ * @wrap: assume @n crossing @start terminates the iteration ++ * ++ * Returns >= nr_cpu_ids on completion ++ * ++ * Note: the @wrap argument is required for the start condition when ++ * we cannot assume @start is set in @mask. ++ */ ++int cpumask_next_wrap(int n, const struct cpumask *mask, int start, bool wrap) ++{ ++ int next; ++ ++again: ++ next = cpumask_next(n, mask); ++ ++ if (wrap && n < start && next >= start) { ++ return nr_cpumask_bits; ++ ++ } else if (next >= nr_cpumask_bits) { ++ wrap = true; ++ n = -1; ++ goto again; ++ } ++ ++ return next; ++} ++EXPORT_SYMBOL(cpumask_next_wrap); ++ + /* These are not inline because of header tangles. */ + #ifdef CONFIG_CPUMASK_OFFSTACK + /** +-- +2.25.1 + diff --git a/queue-4.4/series b/queue-4.4/series index aedf85126e3..b6d61579a74 100644 --- a/queue-4.4/series +++ b/queue-4.4/series @@ -10,3 +10,20 @@ ceph-fix-double-unlock-in-handle_cap_export.patch usb-core-fix-misleading-driver-bug-report.patch platform-x86-asus-nb-wmi-do-not-load-on-asus-t100ta-.patch arm-futex-address-build-warning.patch +media-fix-media_open-to-clear-filp-private_data-in-e.patch +drivers-media-media-devnode-clear-private_data-befor.patch +media-devnode-add-missing-mutex-lock-in-error-handle.patch +media-devnode-fix-namespace-mess.patch +media-device-dynamically-allocate-struct-media_devno.patch +media-fix-use-after-free-in-cdev_put-when-app-exits-.patch +media-fix-media-devnode-ioctl-syscall-and-unregister.patch +i2c-dev-switch-from-register_chrdev-to-cdev-api.patch +i2c-dev-don-t-start-function-name-with-return.patch +i2c-dev-use-after-free-in-detach.patch +i2c-dev-don-t-get-i2c-adapter-via-i2c_dev.patch +i2c-dev-fix-the-race-between-the-release-of-i2c_dev-.patch +padata-set-cpu_index-of-unused-cpus-to-1.patch +sched-fair-cpumask-export-for_each_cpu_wrap.patch +padata-replace-delayed-timer-with-immediate-workqueu.patch +padata-initialize-pd-cpu-with-effective-cpumask.patch +padata-purge-get_cpu-and-reorder_via_wq-from-padata_.patch