aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/pwc/pwc-v4l.c
diff options
context:
space:
mode:
authorHans de Goede <hdegoede@redhat.com>2011-10-09 08:16:46 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2012-01-06 07:44:17 -0500
commitc20d78cde37018caa0313469c9320424995cc489 (patch)
tree4b05a8e120a297db144b79bbcda8c409ee0a51bf /drivers/media/video/pwc/pwc-v4l.c
parentf4af65958a6ea987ff61504ad9f053f8ba8da674 (diff)
[media] pwc: Rework locking
While testing gtk-v4l's new ctrl event code, I hit the following deadlock in the pwc driver: Thread 1: -Does a VIDIOC_G_CTRL -video2_ioctl takes the modlock -video2_ioctl calls v4l2_g_ctrl -v4l2_g_ctrl takes the ctrl_handler lock -v4l2_g_ctrl calls pwc_g_volatile_ctrl -pwc_g_volatile_ctrl releases the modlock as the usb transfer can take a significant amount of time and we don't want to block DQBUF / QBUF too long Thread 2: -Does a VIDIOC_FOO_CTRL -video2_ioctl takes the modlock -video2_ioctl calls v4l2_foo_ctrl -v4l2_foo_ctrl blocks while trying to take the ctrl_handler lock Thread 1: -Blocks while trying to re-take the modlock, as its caller will eventually unlock that Now we have thread 1 waiting for the modlock while holding the ctrl_handler lock and thread 2 waiting for the ctrl_handler lock while holding the modlock -> deadlock. Conclusion: 1) We cannot unlock modlock from pwc_s_ctrl / pwc_g_volatile_ctrl, but this can cause QBUF / DQBUF to block for up to a full second 2) After evaluating various option I came to the conclusion that pwc should stop using the v4l2 core locking, and instead do its own locking Thus this patch stops pwc using the v4l2 core locking, and replaces that with it doing its own locking where necessary. Signed-off-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video/pwc/pwc-v4l.c')
-rw-r--r--drivers/media/video/pwc/pwc-v4l.c60
1 files changed, 22 insertions, 38 deletions
diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c
index 1303641c70c1..97e8d629582c 100644
--- a/drivers/media/video/pwc/pwc-v4l.c
+++ b/drivers/media/video/pwc/pwc-v4l.c
@@ -475,17 +475,11 @@ static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
475 struct pwc_device *pdev = video_drvdata(file); 475 struct pwc_device *pdev = video_drvdata(file);
476 int ret, fps, snapshot, compression, pixelformat; 476 int ret, fps, snapshot, compression, pixelformat;
477 477
478 if (!pdev->udev) 478 if (pwc_test_n_set_capt_file(pdev, file))
479 return -ENODEV;
480
481 if (pdev->capt_file != NULL &&
482 pdev->capt_file != file)
483 return -EBUSY; 479 return -EBUSY;
484 480
485 pdev->capt_file = file;
486
487 ret = pwc_vidioc_try_fmt(pdev, f); 481 ret = pwc_vidioc_try_fmt(pdev, f);
488 if (ret<0) 482 if (ret < 0)
489 return ret; 483 return ret;
490 484
491 pixelformat = f->fmt.pix.pixelformat; 485 pixelformat = f->fmt.pix.pixelformat;
@@ -505,8 +499,16 @@ static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
505 pixelformat != V4L2_PIX_FMT_PWC2) 499 pixelformat != V4L2_PIX_FMT_PWC2)
506 return -EINVAL; 500 return -EINVAL;
507 501
508 if (vb2_is_streaming(&pdev->vb_queue)) 502 mutex_lock(&pdev->udevlock);
509 return -EBUSY; 503 if (!pdev->udev) {
504 ret = -ENODEV;
505 goto leave;
506 }
507
508 if (pdev->iso_init) {
509 ret = -EBUSY;
510 goto leave;
511 }
510 512
511 PWC_DEBUG_IOCTL("Trying to set format to: width=%d height=%d fps=%d " 513 PWC_DEBUG_IOCTL("Trying to set format to: width=%d height=%d fps=%d "
512 "compression=%d snapshot=%d format=%c%c%c%c\n", 514 "compression=%d snapshot=%d format=%c%c%c%c\n",
@@ -526,15 +528,14 @@ static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
526 528
527 PWC_DEBUG_IOCTL("pwc_set_video_mode(), return=%d\n", ret); 529 PWC_DEBUG_IOCTL("pwc_set_video_mode(), return=%d\n", ret);
528 530
529 if (ret) 531 if (ret == 0) {
530 return ret; 532 pdev->pixfmt = pixelformat;
531 533 pwc_vidioc_fill_fmt(pdev, f);
532 pdev->pixfmt = pixelformat; 534 }
533
534 pwc_vidioc_fill_fmt(pdev, f);
535
536 return 0;
537 535
536leave:
537 mutex_unlock(&pdev->udevlock);
538 return ret;
538} 539}
539 540
540static int pwc_querycap(struct file *file, void *fh, struct v4l2_capability *cap) 541static int pwc_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
@@ -580,18 +581,6 @@ static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
580 container_of(ctrl->handler, struct pwc_device, ctrl_handler); 581 container_of(ctrl->handler, struct pwc_device, ctrl_handler);
581 int ret = 0; 582 int ret = 0;
582 583
583 /*
584 * Sometimes it can take quite long for the pwc to complete usb control
585 * transfers, so release the modlock to give streaming by another
586 * process / thread the chance to continue with a dqbuf.
587 */
588 mutex_unlock(&pdev->modlock);
589
590 /*
591 * Take the udev-lock to protect against the disconnect handler
592 * completing and setting dev->udev to NULL underneath us. Other code
593 * does not need to do this since it is protected by the modlock.
594 */
595 mutex_lock(&pdev->udevlock); 584 mutex_lock(&pdev->udevlock);
596 585
597 if (!pdev->udev) { 586 if (!pdev->udev) {
@@ -664,7 +653,6 @@ static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
664 653
665leave: 654leave:
666 mutex_unlock(&pdev->udevlock); 655 mutex_unlock(&pdev->udevlock);
667 mutex_lock(&pdev->modlock);
668 return ret; 656 return ret;
669} 657}
670 658
@@ -844,8 +832,6 @@ static int pwc_s_ctrl(struct v4l2_ctrl *ctrl)
844 container_of(ctrl->handler, struct pwc_device, ctrl_handler); 832 container_of(ctrl->handler, struct pwc_device, ctrl_handler);
845 int ret = 0; 833 int ret = 0;
846 834
847 /* See the comments on locking in pwc_g_volatile_ctrl */
848 mutex_unlock(&pdev->modlock);
849 mutex_lock(&pdev->udevlock); 835 mutex_lock(&pdev->udevlock);
850 836
851 if (!pdev->udev) { 837 if (!pdev->udev) {
@@ -951,7 +937,6 @@ static int pwc_s_ctrl(struct v4l2_ctrl *ctrl)
951 937
952leave: 938leave:
953 mutex_unlock(&pdev->udevlock); 939 mutex_unlock(&pdev->udevlock);
954 mutex_lock(&pdev->modlock);
955 return ret; 940 return ret;
956} 941}
957 942
@@ -981,9 +966,11 @@ static int pwc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
981{ 966{
982 struct pwc_device *pdev = video_drvdata(file); 967 struct pwc_device *pdev = video_drvdata(file);
983 968
969 mutex_lock(&pdev->udevlock); /* To avoid race with s_fmt */
984 PWC_DEBUG_IOCTL("ioctl(VIDIOC_G_FMT) return size %dx%d\n", 970 PWC_DEBUG_IOCTL("ioctl(VIDIOC_G_FMT) return size %dx%d\n",
985 pdev->image.x, pdev->image.y); 971 pdev->image.x, pdev->image.y);
986 pwc_vidioc_fill_fmt(pdev, f); 972 pwc_vidioc_fill_fmt(pdev, f);
973 mutex_unlock(&pdev->udevlock);
987 return 0; 974 return 0;
988} 975}
989 976
@@ -999,12 +986,9 @@ static int pwc_reqbufs(struct file *file, void *fh,
999{ 986{
1000 struct pwc_device *pdev = video_drvdata(file); 987 struct pwc_device *pdev = video_drvdata(file);
1001 988
1002 if (pdev->capt_file != NULL && 989 if (pwc_test_n_set_capt_file(pdev, file))
1003 pdev->capt_file != file)
1004 return -EBUSY; 990 return -EBUSY;
1005 991
1006 pdev->capt_file = file;
1007
1008 return vb2_reqbufs(&pdev->vb_queue, rb); 992 return vb2_reqbufs(&pdev->vb_queue, rb);
1009} 993}
1010 994