Re: [PATCH] pwc: better usb disconnect handling

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Em 06-06-2011 14:43, Hans de Goede escreveu:
> Unplugging a pwc cam while an app has the /dev/video# node open leads
> to an oops in pwc_video_close when the app closes the node, because
> the disconnect handler has free-ed the pdev struct pwc_video_close
> tries to use. Instead of adding some sort of bandaid for this.
> fix it properly using the v4l2 core's new(ish) behavior of keeping the
> v4l2_dev structure around until both unregister has been called, and
> all file handles referring  to it have been closed:
> 
> Embed the v4l2_dev structure in the pdev structure and define a v4l2 dev
> release callback releasing the pdev structure (and thus also the embedded
> v4l2 dev structure.

Hans,

I'm adding this patch into my fixes tree. After applying upstream, it
will be sent automatically to the stable team. Next time, instead of
c/c them on an email, all you need to do is to add an extra tag after your
SOB, like:

> Signed-off-by: Hans de Goede <hdegoede@xxxxxxxxxx>
Cc: stable@xxxxxxxxxx

Cheers,
Mauro

> ---
>  drivers/media/video/pwc/pwc-ctrl.c |    2 +-
>  drivers/media/video/pwc/pwc-if.c   |  152 +++++++++++-------------------------
>  drivers/media/video/pwc/pwc.h      |    4 +-
>  3 files changed, 50 insertions(+), 108 deletions(-)
> 
> diff --git a/drivers/media/video/pwc/pwc-ctrl.c b/drivers/media/video/pwc/pwc-ctrl.c
> index 1593f8d..760b4de 100644
> --- a/drivers/media/video/pwc/pwc-ctrl.c
> +++ b/drivers/media/video/pwc/pwc-ctrl.c
> @@ -1414,7 +1414,7 @@ long pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg)
>  	{
>  		ARG_DEF(struct pwc_probe, probe)
>  
> -		strcpy(ARGR(probe).name, pdev->vdev->name);
> +		strcpy(ARGR(probe).name, pdev->vdev.name);
>  		ARGR(probe).type = pdev->type;
>  		ARG_OUT(probe)
>  		break;
> diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c
> index b81024c..592f966 100644
> --- a/drivers/media/video/pwc/pwc-if.c
> +++ b/drivers/media/video/pwc/pwc-if.c
> @@ -40,7 +40,7 @@
>     Oh yes, convention: to disctinguish between all the various pointers to
>     device-structures, I use these names for the pointer variables:
>     udev: struct usb_device *
> -   vdev: struct video_device *
> +   vdev: struct video_device (member of pwc_dev)
>     pdev: struct pwc_devive *
>  */
>  
> @@ -152,6 +152,7 @@ static ssize_t pwc_video_read(struct file *file, char __user *buf,
>  			  size_t count, loff_t *ppos);
>  static unsigned int pwc_video_poll(struct file *file, poll_table *wait);
>  static int  pwc_video_mmap(struct file *file, struct vm_area_struct *vma);
> +static void pwc_video_release(struct video_device *vfd);
>  
>  static const struct v4l2_file_operations pwc_fops = {
>  	.owner =	THIS_MODULE,
> @@ -164,42 +165,12 @@ static const struct v4l2_file_operations pwc_fops = {
>  };
>  static struct video_device pwc_template = {
>  	.name =		"Philips Webcam",	/* Filled in later */
> -	.release =	video_device_release,
> +	.release =	pwc_video_release,
>  	.fops =         &pwc_fops,
> +	.ioctl_ops =	&pwc_ioctl_ops,
>  };
>  
>  /***************************************************************************/
> -
> -/* Okay, this is some magic that I worked out and the reasoning behind it...
> -
> -   The biggest problem with any USB device is of course: "what to do
> -   when the user unplugs the device while it is in use by an application?"
> -   We have several options:
> -   1) Curse them with the 7 plagues when they do (requires divine intervention)
> -   2) Tell them not to (won't work: they'll do it anyway)
> -   3) Oops the kernel (this will have a negative effect on a user's uptime)
> -   4) Do something sensible.
> -
> -   Of course, we go for option 4.
> -
> -   It happens that this device will be linked to two times, once from
> -   usb_device and once from the video_device in their respective 'private'
> -   pointers. This is done when the device is probed() and all initialization
> -   succeeded. The pwc_device struct links back to both structures.
> -
> -   When a device is unplugged while in use it will be removed from the
> -   list of known USB devices; I also de-register it as a V4L device, but
> -   unfortunately I can't free the memory since the struct is still in use
> -   by the file descriptor. This free-ing is then deferend until the first
> -   opportunity. Crude, but it works.
> -
> -   A small 'advantage' is that if a user unplugs the cam and plugs it back
> -   in, it should get assigned the same video device minor, but unfortunately
> -   it's non-trivial to re-link the cam back to the video device... (that
> -   would surely be magic! :))
> -*/
> -
> -/***************************************************************************/
>  /* Private functions */
>  
>  /* Here we want the physical address of the memory.
> @@ -1017,16 +988,15 @@ static ssize_t show_snapshot_button_status(struct device *class_dev,
>  static DEVICE_ATTR(button, S_IRUGO | S_IWUSR, show_snapshot_button_status,
>  		   NULL);
>  
> -static int pwc_create_sysfs_files(struct video_device *vdev)
> +static int pwc_create_sysfs_files(struct pwc_device *pdev)
>  {
> -	struct pwc_device *pdev = video_get_drvdata(vdev);
>  	int rc;
>  
> -	rc = device_create_file(&vdev->dev, &dev_attr_button);
> +	rc = device_create_file(&pdev->vdev.dev, &dev_attr_button);
>  	if (rc)
>  		goto err;
>  	if (pdev->features & FEATURE_MOTOR_PANTILT) {
> -		rc = device_create_file(&vdev->dev, &dev_attr_pan_tilt);
> +		rc = device_create_file(&pdev->vdev.dev, &dev_attr_pan_tilt);
>  		if (rc)
>  			goto err_button;
>  	}
> @@ -1034,19 +1004,17 @@ static int pwc_create_sysfs_files(struct video_device *vdev)
>  	return 0;
>  
>  err_button:
> -	device_remove_file(&vdev->dev, &dev_attr_button);
> +	device_remove_file(&pdev->vdev.dev, &dev_attr_button);
>  err:
>  	PWC_ERROR("Could not create sysfs files.\n");
>  	return rc;
>  }
>  
> -static void pwc_remove_sysfs_files(struct video_device *vdev)
> +static void pwc_remove_sysfs_files(struct pwc_device *pdev)
>  {
> -	struct pwc_device *pdev = video_get_drvdata(vdev);
> -
>  	if (pdev->features & FEATURE_MOTOR_PANTILT)
> -		device_remove_file(&vdev->dev, &dev_attr_pan_tilt);
> -	device_remove_file(&vdev->dev, &dev_attr_button);
> +		device_remove_file(&pdev->vdev.dev, &dev_attr_pan_tilt);
> +	device_remove_file(&pdev->vdev.dev, &dev_attr_button);
>  }
>  
>  #ifdef CONFIG_USB_PWC_DEBUG
> @@ -1107,7 +1075,7 @@ static int pwc_video_open(struct file *file)
>  		if (ret >= 0)
>  		{
>  			PWC_DEBUG_OPEN("This %s camera is equipped with a %s (%d).\n",
> -					pdev->vdev->name,
> +					pdev->vdev.name,
>  					pwc_sensor_type_to_string(i), i);
>  		}
>  	}
> @@ -1181,16 +1149,15 @@ static int pwc_video_open(struct file *file)
>  	return 0;
>  }
>  
> -
> -static void pwc_cleanup(struct pwc_device *pdev)
> +static void pwc_video_release(struct video_device *vfd)
>  {
> -	pwc_remove_sysfs_files(pdev->vdev);
> -	video_unregister_device(pdev->vdev);
> +	struct pwc_device *pdev = container_of(vfd, struct pwc_device, vdev);
> +	int hint;
>  
> -#ifdef CONFIG_USB_PWC_INPUT_EVDEV
> -	if (pdev->button_dev)
> -		input_unregister_device(pdev->button_dev);
> -#endif
> +	/* search device_hint[] table if we occupy a slot, by any chance */
> +	for (hint = 0; hint < MAX_DEV_HINTS; hint++)
> +		if (device_hint[hint].pdev == pdev)
> +			device_hint[hint].pdev = NULL;
>  
>  	kfree(pdev);
>  }
> @@ -1200,7 +1167,7 @@ static int pwc_video_close(struct file *file)
>  {
>  	struct video_device *vdev = file->private_data;
>  	struct pwc_device *pdev;
> -	int i, hint;
> +	int i;
>  
>  	PWC_DEBUG_OPEN(">> video_close called(vdev = 0x%p).\n", vdev);
>  
> @@ -1235,12 +1202,6 @@ static int pwc_video_close(struct file *file)
>  		}
>  		pdev->vopen--;
>  		PWC_DEBUG_OPEN("<< video_close() vopen=%d\n", pdev->vopen);
> -	} else {
> -		pwc_cleanup(pdev);
> -		/* search device_hint[] table if we occupy a slot, by any chance */
> -		for (hint = 0; hint < MAX_DEV_HINTS; hint++)
> -			if (device_hint[hint].pdev == pdev)
> -				device_hint[hint].pdev = NULL;
>  	}
>  
>  	return 0;
> @@ -1716,19 +1677,12 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
>  	init_waitqueue_head(&pdev->frameq);
>  	pdev->vcompression = pwc_preferred_compression;
>  
> -	/* Allocate video_device structure */
> -	pdev->vdev = video_device_alloc();
> -	if (!pdev->vdev) {
> -		PWC_ERROR("Err, cannot allocate video_device struture. Failing probe.");
> -		rc = -ENOMEM;
> -		goto err_free_mem;
> -	}
> -	memcpy(pdev->vdev, &pwc_template, sizeof(pwc_template));
> -	pdev->vdev->parent = &intf->dev;
> -	pdev->vdev->lock = &pdev->modlock;
> -	pdev->vdev->ioctl_ops = &pwc_ioctl_ops;
> -	strcpy(pdev->vdev->name, name);
> -	video_set_drvdata(pdev->vdev, pdev);
> +	/* Init video_device structure */
> +	memcpy(&pdev->vdev, &pwc_template, sizeof(pwc_template));
> +	pdev->vdev.parent = &intf->dev;
> +	pdev->vdev.lock = &pdev->modlock;
> +	strcpy(pdev->vdev.name, name);
> +	video_set_drvdata(&pdev->vdev, pdev);
>  
>  	pdev->release = le16_to_cpu(udev->descriptor.bcdDevice);
>  	PWC_DEBUG_PROBE("Release: %04x\n", pdev->release);
> @@ -1747,8 +1701,6 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
>  		}
>  	}
>  
> -	pdev->vdev->release = video_device_release;
> -
>  	/* occupy slot */
>  	if (hint < MAX_DEV_HINTS)
>  		device_hint[hint].pdev = pdev;
> @@ -1760,16 +1712,16 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
>  	pwc_set_leds(pdev, 0, 0);
>  	pwc_camera_power(pdev, 0);
>  
> -	rc = video_register_device(pdev->vdev, VFL_TYPE_GRABBER, video_nr);
> +	rc = video_register_device(&pdev->vdev, VFL_TYPE_GRABBER, video_nr);
>  	if (rc < 0) {
>  		PWC_ERROR("Failed to register as video device (%d).\n", rc);
> -		goto err_video_release;
> +		goto err_free_mem;
>  	}
> -	rc = pwc_create_sysfs_files(pdev->vdev);
> +	rc = pwc_create_sysfs_files(pdev);
>  	if (rc)
>  		goto err_video_unreg;
>  
> -	PWC_INFO("Registered as %s.\n", video_device_node_name(pdev->vdev));
> +	PWC_INFO("Registered as %s.\n", video_device_node_name(&pdev->vdev));
>  
>  #ifdef CONFIG_USB_PWC_INPUT_EVDEV
>  	/* register webcam snapshot button input device */
> @@ -1777,7 +1729,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
>  	if (!pdev->button_dev) {
>  		PWC_ERROR("Err, insufficient memory for webcam snapshot button device.");
>  		rc = -ENOMEM;
> -		pwc_remove_sysfs_files(pdev->vdev);
> +		pwc_remove_sysfs_files(pdev);
>  		goto err_video_unreg;
>  	}
>  
> @@ -1795,7 +1747,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
>  	if (rc) {
>  		input_free_device(pdev->button_dev);
>  		pdev->button_dev = NULL;
> -		pwc_remove_sysfs_files(pdev->vdev);
> +		pwc_remove_sysfs_files(pdev);
>  		goto err_video_unreg;
>  	}
>  #endif
> @@ -1805,10 +1757,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
>  err_video_unreg:
>  	if (hint < MAX_DEV_HINTS)
>  		device_hint[hint].pdev = NULL;
> -	video_unregister_device(pdev->vdev);
> -	pdev->vdev = NULL;	/* So we don't try to release it below */
> -err_video_release:
> -	video_device_release(pdev->vdev);
> +	video_unregister_device(&pdev->vdev);
>  err_free_mem:
>  	kfree(pdev);
>  	return rc;
> @@ -1817,10 +1766,8 @@ err_free_mem:
>  /* The user yanked out the cable... */
>  static void usb_pwc_disconnect(struct usb_interface *intf)
>  {
> -	struct pwc_device *pdev;
> -	int hint;
> +	struct pwc_device *pdev  = usb_get_intfdata(intf);
>  
> -	pdev = usb_get_intfdata (intf);
>  	mutex_lock(&pdev->modlock);
>  	usb_set_intfdata (intf, NULL);
>  	if (pdev == NULL) {
> @@ -1837,30 +1784,25 @@ static void usb_pwc_disconnect(struct usb_interface *intf)
>  	}
>  
>  	/* We got unplugged; this is signalled by an EPIPE error code */
> -	if (pdev->vopen) {
> -		PWC_INFO("Disconnected while webcam is in use!\n");
> -		pdev->error_status = EPIPE;
> -	}
> +	pdev->error_status = EPIPE;
> +	pdev->unplugged = 1;
>  
>  	/* Alert waiting processes */
>  	wake_up_interruptible(&pdev->frameq);
> -	/* Wait until device is closed */
> -	if (pdev->vopen) {
> -		pdev->unplugged = 1;
> -		pwc_iso_stop(pdev);
> -	} else {
> -		/* Device is closed, so we can safely unregister it */
> -		PWC_DEBUG_PROBE("Unregistering video device in disconnect().\n");
>  
> -disconnect_out:
> -		/* search device_hint[] table if we occupy a slot, by any chance */
> -		for (hint = 0; hint < MAX_DEV_HINTS; hint++)
> -			if (device_hint[hint].pdev == pdev)
> -				device_hint[hint].pdev = NULL;
> -	}
> +	/* No need to keep the urbs around after disconnection */
> +	pwc_isoc_cleanup(pdev);
>  
> +disconnect_out:
>  	mutex_unlock(&pdev->modlock);
> -	pwc_cleanup(pdev);
> +
> +	pwc_remove_sysfs_files(pdev);
> +	video_unregister_device(&pdev->vdev);
> +
> +#ifdef CONFIG_USB_PWC_INPUT_EVDEV
> +	if (pdev->button_dev)
> +		input_unregister_device(pdev->button_dev);
> +#endif
>  }
>  
>  
> diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h
> index e947766..083f8b1 100644
> --- a/drivers/media/video/pwc/pwc.h
> +++ b/drivers/media/video/pwc/pwc.h
> @@ -162,9 +162,9 @@ struct pwc_imgbuf
>  
>  struct pwc_device
>  {
> -   struct video_device *vdev;
> +	struct video_device vdev;
>  
> -   /* Pointer to our usb_device */
> +   /* Pointer to our usb_device, may be NULL after unplug */
>     struct usb_device *udev;
>  
>     int type;                    /* type of cam (645, 646, 675, 680, 690, 720, 730, 740, 750) */

--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux