From 6b060ffea0722cfe4f5156a73a6424130d3d804a Mon Sep 17 00:00:00 2001 From: Frank Zago Date: Sun, 28 Sep 2008 08:12:22 -0300 Subject: V4L/DVB (9086): gspca: Use a kref to avoid potentialy blocking forever in disconnect. Signed-off-by: Frank Zago Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gspca.c | 39 +++++++++++++++++++++++++++------------ drivers/media/video/gspca/gspca.h | 1 + 2 files changed, 28 insertions(+), 12 deletions(-) (limited to 'drivers/media/video/gspca') 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 @@ #include #include #include +#include #include #include #include @@ -834,6 +835,16 @@ out: return ret; } +static void gspca_delete(struct kref *kref) +{ + struct gspca_dev *gspca_dev = container_of(kref, struct gspca_dev, kref); + + PDEBUG(D_STREAM, "device deleted"); + + kfree(gspca_dev->usb_buf); + kfree(gspca_dev); +} + static int dev_open(struct inode *inode, struct file *file) { struct gspca_dev *gspca_dev; @@ -853,6 +864,10 @@ static int dev_open(struct inode *inode, struct file *file) goto out; } gspca_dev->users++; + + /* one more user */ + kref_get(&gspca_dev->kref); + file->private_data = gspca_dev; #ifdef GSPCA_DEBUG /* activate the v4l2 debug */ @@ -895,7 +910,11 @@ static int dev_close(struct inode *inode, struct file *file) } file->private_data = NULL; mutex_unlock(&gspca_dev->queue_lock); + PDEBUG(D_STREAM, "close done"); + + kref_put(&gspca_dev->kref, gspca_delete); + return 0; } @@ -1809,6 +1828,7 @@ int gspca_dev_probe(struct usb_interface *intf, err("couldn't kzalloc gspca struct"); return -EIO; } + kref_init(&gspca_dev->kref); gspca_dev->usb_buf = kmalloc(USB_BUF_SZ, GFP_KERNEL); if (!gspca_dev->usb_buf) { err("out of memory"); @@ -1858,8 +1878,7 @@ int gspca_dev_probe(struct usb_interface *intf, PDEBUG(D_PROBE, "probe ok"); return 0; out: - kfree(gspca_dev->usb_buf); - kfree(gspca_dev); + kref_put(&gspca_dev->kref, gspca_delete); return ret; } EXPORT_SYMBOL(gspca_dev_probe); @@ -1874,8 +1893,8 @@ void gspca_disconnect(struct usb_interface *intf) { struct gspca_dev *gspca_dev = usb_get_intfdata(intf); - if (!gspca_dev) - return; + usb_set_intfdata(intf, NULL); + gspca_dev->present = 0; mutex_lock(&gspca_dev->queue_lock); mutex_lock(&gspca_dev->usb_lock); @@ -1883,16 +1902,12 @@ void gspca_disconnect(struct usb_interface *intf) destroy_urbs(gspca_dev); mutex_unlock(&gspca_dev->usb_lock); mutex_unlock(&gspca_dev->queue_lock); - while (gspca_dev->users != 0) { /* wait until fully closed */ - atomic_inc(&gspca_dev->nevent); - wake_up_interruptible(&gspca_dev->wq); /* wake processes */ - schedule(); - } + /* We don't want people trying to open up the device */ video_unregister_device(&gspca_dev->vdev); -/* Free the memory */ - kfree(gspca_dev->usb_buf); - kfree(gspca_dev); + + kref_put(&gspca_dev->kref, gspca_delete); + PDEBUG(D_PROBE, "disconnect complete"); } 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 { struct video_device vdev; /* !! must be the first item */ struct file_operations fops; struct usb_device *dev; + struct kref kref; struct file *capt_file; /* file doing video capture */ struct cam cam; /* device information */ -- cgit v1.2.3