uio: Fix uio driver to refcount device
PATCH v3 1/1 uio: Fix uio driver to refcount device
Protect uio driver from crashing if its owner being unplugged while there are open fds. Embed struct device in struct uio_device, use refcounting on device, free uio_device on release.
Signed-off-by Brian Russell <[email protected]>
--- drivers/uio/uio.c | 54 ++++++++++++++++++++++++++++++++-------------- include/linux/uio_driver.h | 2 +- 2 files changed, 39 insertions(+), 17 deletions(-)
drivers/uio/uio.c +38 -16
@@ -270,7 +270,7 @@ static int uio_dev_add_attributes(struct uio_device *idev)if (!map_found) { map_found =1 ; idev->map_dir =kobject_create_and_add ("maps" , - &idev->dev->kobj); + &idev->dev.kobj);if (!idev->map_dir) goto err_map; }
@@ -295,7 +295,7 @@ static int uio_dev_add_attributes(struct uio_device *idev)if (!portio_found) { portio_found =1 ; idev->portio_dir =kobject_create_and_add ("portio" , - &idev->dev->kobj); + &idev->dev.kobj);if (!idev->portio_dir) goto err_portio; }
@@ -334,7 +334,7 @@ err_map_kobj:kobject_put (&map->kobj); }kobject_put (idev->map_dir); -dev_err (idev->dev,"error creating sysfs files (%d)\n" , ret); +dev_err (&idev->dev,"error creating sysfs files (%d)\n" , ret);return ret; }
@@ -371,7 +371,7 @@ static int uio_get_minor(struct uio_device *idev) idev->minor = retval; retval =0 ; }else if (retval == -ENOSPC ) { -dev_err (idev->dev,"too many uio devices\n" ); +dev_err (&idev->dev,"too many uio devices\n" ); retval = -EINVAL ; }mutex_unlock (&minor_lock);
@@ -434,9 +434,11 @@ static int uio_open(struct inode *inode, struct file *filep) goto out; } +get_device (&idev->dev); +if (!try_module_get (idev->owner)) { ret = -ENODEV ; - goto out; + goto err_module_get; } listener =kmalloc (sizeof (*listener),GFP_KERNEL );
@@ -462,6 +464,9 @@ err_infoopen: err_alloc_listener:module_put (idev->owner); +err_module_get: +put_device (&idev->dev); + out:return ret; }
@@ -485,6 +490,7 @@ static int uio_release(struct inode *inode, struct file *filep)module_put (idev->owner);kfree (listener); +put_device (&idev->dev);return ret; }
@@ -785,6 +791,13 @@ static void release_uio_class(void)uio_major_cleanup (); } +static void uio_device_release (struct device *dev) +{ +struct uio_device *idev =dev_get_drvdata (dev); + +kfree (idev); +} +/** * uio_register_device - register a new userspace IO device * @owner: module that creates the new device
@@ -805,7 +818,7 @@ int __uio_register_device(struct module *owner, info->uio_dev =NULL ; - idev =devm_kzalloc (parent,sizeof (*idev),GFP_KERNEL ); + idev =kzalloc (sizeof (*idev),GFP_KERNEL );if (!idev) {return -ENOMEM ; }
@@ -819,14 +832,19 @@ int __uio_register_device(struct module *owner,if (ret)return ret; - idev->dev =device_create (&uio_class, parent, -MKDEV (uio_major, idev->minor), idev, -"uio%d" , idev->minor); -if (IS_ERR (idev->dev)) { -printk (KERN_ERR "UIO: device register failed\n" ); - ret =PTR_ERR (idev->dev); + idev->dev.devt =MKDEV (uio_major, idev->minor); + idev->dev.class = &uio_class; + idev->dev.parent = parent; + idev->dev.release = uio_device_release; +dev_set_drvdata (&idev->dev, idev); + + ret =kobject_set_name (&idev->dev.kobj,"uio%d" , idev->minor); +if (ret) + goto err_device_create; + + ret =device_register (&idev->dev); +if (ret) goto err_device_create; - } ret =uio_dev_add_attributes (idev);if (ret)
@@ -835,7 +853,7 @@ int __uio_register_device(struct module *owner, info->uio_dev = idev;if (info->irq && (info->irq !=UIO_IRQ_CUSTOM )) { - ret =devm_request_irq (idev->dev, info->irq, uio_interrupt, + ret =request_irq (info->irq, uio_interrupt, info->irq_flags, info->name, idev);if (ret) goto err_request_irq;
@@ -846,7 +864,10 @@ int __uio_register_device(struct module *owner, err_request_irq:uio_dev_del_attributes (idev); err_uio_dev_add_attributes: -device_destroy (&uio_class,MKDEV (uio_major, idev->minor)); +uio_free_minor (idev); +device_unregister (&idev->dev); +return ret; + err_device_create:uio_free_minor (idev);return ret;
@@ -871,7 +892,8 @@ void uio_unregister_device(struct uio_info *info)uio_dev_del_attributes (idev); -device_destroy (&uio_class,MKDEV (uio_major, idev->minor)); +device_unregister (&idev->dev); +free_irq (idev->info->irq, idev);return ; }
include/linux/uio_driver.h +1 -1
@@ -65,7 +65,7 @@ struct uio_port {struct uio_device {struct module *owner; -struct device *dev; +struct device dev;int minor;atomic_t event;struct fasync_struct *async_queue;
Signature · 1 line
2.1.4