diff options
author | Frank Zago <frank@zago.net> | 2008-09-28 07:12:22 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2008-10-12 07:37:12 -0400 |
commit | 6b060ffea0722cfe4f5156a73a6424130d3d804a (patch) | |
tree | fa3351600d2fe6dd3ac955e2954c42d6675e78bf | |
parent | 6c49da7f169c25eb39b80ff168f5bdbacf8457e1 (diff) |
V4L/DVB (9086): gspca: Use a kref to avoid potentialy blocking forever in disconnect.
Signed-off-by: Frank Zago <frank@zago.net>
Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r-- | drivers/media/video/gspca/gspca.c | 39 | ||||
-rw-r--r-- | drivers/media/video/gspca/gspca.h | 1 |
2 files changed, 28 insertions, 12 deletions
diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 3f0fd44f19ab..342a0f39e5d5 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #include <linux/string.h> | 29 | #include <linux/string.h> |
30 | #include <linux/pagemap.h> | 30 | #include <linux/pagemap.h> |
31 | #include <linux/io.h> | 31 | #include <linux/io.h> |
32 | #include <linux/kref.h> | ||
32 | #include <asm/page.h> | 33 | #include <asm/page.h> |
33 | #include <linux/uaccess.h> | 34 | #include <linux/uaccess.h> |
34 | #include <linux/jiffies.h> | 35 | #include <linux/jiffies.h> |
@@ -834,6 +835,16 @@ out: | |||
834 | return ret; | 835 | return ret; |
835 | } | 836 | } |
836 | 837 | ||
838 | static void gspca_delete(struct kref *kref) | ||
839 | { | ||
840 | struct gspca_dev *gspca_dev = container_of(kref, struct gspca_dev, kref); | ||
841 | |||
842 | PDEBUG(D_STREAM, "device deleted"); | ||
843 | |||
844 | kfree(gspca_dev->usb_buf); | ||
845 | kfree(gspca_dev); | ||
846 | } | ||
847 | |||
837 | static int dev_open(struct inode *inode, struct file *file) | 848 | static int dev_open(struct inode *inode, struct file *file) |
838 | { | 849 | { |
839 | struct gspca_dev *gspca_dev; | 850 | struct gspca_dev *gspca_dev; |
@@ -853,6 +864,10 @@ static int dev_open(struct inode *inode, struct file *file) | |||
853 | goto out; | 864 | goto out; |
854 | } | 865 | } |
855 | gspca_dev->users++; | 866 | gspca_dev->users++; |
867 | |||
868 | /* one more user */ | ||
869 | kref_get(&gspca_dev->kref); | ||
870 | |||
856 | file->private_data = gspca_dev; | 871 | file->private_data = gspca_dev; |
857 | #ifdef GSPCA_DEBUG | 872 | #ifdef GSPCA_DEBUG |
858 | /* activate the v4l2 debug */ | 873 | /* activate the v4l2 debug */ |
@@ -895,7 +910,11 @@ static int dev_close(struct inode *inode, struct file *file) | |||
895 | } | 910 | } |
896 | file->private_data = NULL; | 911 | file->private_data = NULL; |
897 | mutex_unlock(&gspca_dev->queue_lock); | 912 | mutex_unlock(&gspca_dev->queue_lock); |
913 | |||
898 | PDEBUG(D_STREAM, "close done"); | 914 | PDEBUG(D_STREAM, "close done"); |
915 | |||
916 | kref_put(&gspca_dev->kref, gspca_delete); | ||
917 | |||
899 | return 0; | 918 | return 0; |
900 | } | 919 | } |
901 | 920 | ||
@@ -1809,6 +1828,7 @@ int gspca_dev_probe(struct usb_interface *intf, | |||
1809 | err("couldn't kzalloc gspca struct"); | 1828 | err("couldn't kzalloc gspca struct"); |
1810 | return -EIO; | 1829 | return -EIO; |
1811 | } | 1830 | } |
1831 | kref_init(&gspca_dev->kref); | ||
1812 | gspca_dev->usb_buf = kmalloc(USB_BUF_SZ, GFP_KERNEL); | 1832 | gspca_dev->usb_buf = kmalloc(USB_BUF_SZ, GFP_KERNEL); |
1813 | if (!gspca_dev->usb_buf) { | 1833 | if (!gspca_dev->usb_buf) { |
1814 | err("out of memory"); | 1834 | err("out of memory"); |
@@ -1858,8 +1878,7 @@ int gspca_dev_probe(struct usb_interface *intf, | |||
1858 | PDEBUG(D_PROBE, "probe ok"); | 1878 | PDEBUG(D_PROBE, "probe ok"); |
1859 | return 0; | 1879 | return 0; |
1860 | out: | 1880 | out: |
1861 | kfree(gspca_dev->usb_buf); | 1881 | kref_put(&gspca_dev->kref, gspca_delete); |
1862 | kfree(gspca_dev); | ||
1863 | return ret; | 1882 | return ret; |
1864 | } | 1883 | } |
1865 | EXPORT_SYMBOL(gspca_dev_probe); | 1884 | EXPORT_SYMBOL(gspca_dev_probe); |
@@ -1874,8 +1893,8 @@ void gspca_disconnect(struct usb_interface *intf) | |||
1874 | { | 1893 | { |
1875 | struct gspca_dev *gspca_dev = usb_get_intfdata(intf); | 1894 | struct gspca_dev *gspca_dev = usb_get_intfdata(intf); |
1876 | 1895 | ||
1877 | if (!gspca_dev) | 1896 | usb_set_intfdata(intf, NULL); |
1878 | return; | 1897 | |
1879 | gspca_dev->present = 0; | 1898 | gspca_dev->present = 0; |
1880 | mutex_lock(&gspca_dev->queue_lock); | 1899 | mutex_lock(&gspca_dev->queue_lock); |
1881 | mutex_lock(&gspca_dev->usb_lock); | 1900 | mutex_lock(&gspca_dev->usb_lock); |
@@ -1883,16 +1902,12 @@ void gspca_disconnect(struct usb_interface *intf) | |||
1883 | destroy_urbs(gspca_dev); | 1902 | destroy_urbs(gspca_dev); |
1884 | mutex_unlock(&gspca_dev->usb_lock); | 1903 | mutex_unlock(&gspca_dev->usb_lock); |
1885 | mutex_unlock(&gspca_dev->queue_lock); | 1904 | mutex_unlock(&gspca_dev->queue_lock); |
1886 | while (gspca_dev->users != 0) { /* wait until fully closed */ | 1905 | |
1887 | atomic_inc(&gspca_dev->nevent); | ||
1888 | wake_up_interruptible(&gspca_dev->wq); /* wake processes */ | ||
1889 | schedule(); | ||
1890 | } | ||
1891 | /* We don't want people trying to open up the device */ | 1906 | /* We don't want people trying to open up the device */ |
1892 | video_unregister_device(&gspca_dev->vdev); | 1907 | video_unregister_device(&gspca_dev->vdev); |
1893 | /* Free the memory */ | 1908 | |
1894 | kfree(gspca_dev->usb_buf); | 1909 | kref_put(&gspca_dev->kref, gspca_delete); |
1895 | kfree(gspca_dev); | 1910 | |
1896 | PDEBUG(D_PROBE, "disconnect complete"); | 1911 | PDEBUG(D_PROBE, "disconnect complete"); |
1897 | } | 1912 | } |
1898 | EXPORT_SYMBOL(gspca_disconnect); | 1913 | EXPORT_SYMBOL(gspca_disconnect); |
diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index b0bdae194bb6..192dffdcd9cd 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h | |||
@@ -121,6 +121,7 @@ struct gspca_dev { | |||
121 | struct video_device vdev; /* !! must be the first item */ | 121 | struct video_device vdev; /* !! must be the first item */ |
122 | struct file_operations fops; | 122 | struct file_operations fops; |
123 | struct usb_device *dev; | 123 | struct usb_device *dev; |
124 | struct kref kref; | ||
124 | struct file *capt_file; /* file doing video capture */ | 125 | struct file *capt_file; /* file doing video capture */ |
125 | 126 | ||
126 | struct cam cam; /* device information */ | 127 | struct cam cam; /* device information */ |