From d6760b14d4a1243f918d983bba1e35c5a5cd5a6d Mon Sep 17 00:00:00 2001 From: Erico Nunes Date: Tue, 3 May 2016 15:45:43 -0300 Subject: i2c: dev: switch from register_chrdev to cdev API 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 --- drivers/i2c/i2c-dev.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'drivers/i2c/i2c-dev.c') diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c index 0b1108d3c2f3..2562a45ff152 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); @@ -552,6 +554,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, @@ -565,6 +573,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; } @@ -584,6 +594,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; @@ -620,7 +631,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; @@ -644,7 +655,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; @@ -655,7 +666,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 " -- cgit v1.2.3 From 72a71f869c95dc11b73f09fe18c593d4a0618c3f Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 27 May 2016 13:13:01 +0200 Subject: i2c: dev: don't start function name with 'return' 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 --- drivers/i2c/i2c-dev.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/i2c/i2c-dev.c') diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c index 2562a45ff152..89593dcb79f0 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); @@ -575,7 +575,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; } @@ -592,7 +592,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); -- cgit v1.2.3 From e6be18f6d62c1d3b331ae020b76a29c2ccf6b0bf Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 28 May 2016 08:01:46 +0300 Subject: i2c: dev: use after free in detach 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 --- drivers/i2c/i2c-dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/i2c/i2c-dev.c') diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c index 89593dcb79f0..6ecfd76270f2 100644 --- a/drivers/i2c/i2c-dev.c +++ b/drivers/i2c/i2c-dev.c @@ -592,9 +592,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; -- cgit v1.2.3