aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorOleg Nesterov <oleg@tv-sign.ru>2007-07-02 11:26:20 -0400
committerMauro Carvalho Chehab <mchehab@infradead.org>2007-07-03 14:11:19 -0400
commit1e4597e8f0049dccedb0e011934007309fa2aeab (patch)
tree74e8413d6bd1c5202fb3767ce18ae22fae235a25 /drivers
parentf057131fb6eb2c45f6023e3da41ccd6e4e71aee9 (diff)
V4L/DVB (5818): CinergyT2: fix flush_workqueue() vs work->func() deadlock
Spotted and tested by Thomas Sattler <tsattler@gmx.de>. cinergyT2.c does cancel_delayed_work() + flush_scheduled_work() while holding cinergyt2->sem. This leads to deadlock because work->func() needs the same mutex to complete. Another bug is that this code in fact can't reliably stop the re-arming delayed_work. Convert this code to use cancel_rearming_delayed_work() and move it out of ->sem. Another mutex, ->wq_sem, was added to protect against the concurrent open/resume. This patch is a horrible hack to fix the lockup which happens in practice. As Dmitry Torokhov pointed out this driver has other problems and needs further changes. Signed-off-by: Oleg Nesterov <oleg@tv-sign.ru> Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/media/dvb/cinergyT2/cinergyT2.c66
1 files changed, 40 insertions, 26 deletions
diff --git a/drivers/media/dvb/cinergyT2/cinergyT2.c b/drivers/media/dvb/cinergyT2/cinergyT2.c
index 6aba5b39ed14..b40af48a2edb 100644
--- a/drivers/media/dvb/cinergyT2/cinergyT2.c
+++ b/drivers/media/dvb/cinergyT2/cinergyT2.c
@@ -118,6 +118,7 @@ struct cinergyt2 {
118 struct dvb_demux demux; 118 struct dvb_demux demux;
119 struct usb_device *udev; 119 struct usb_device *udev;
120 struct mutex sem; 120 struct mutex sem;
121 struct mutex wq_sem;
121 struct dvb_adapter adapter; 122 struct dvb_adapter adapter;
122 struct dvb_device *fedev; 123 struct dvb_device *fedev;
123 struct dmxdev dmxdev; 124 struct dmxdev dmxdev;
@@ -482,14 +483,14 @@ static int cinergyt2_open (struct inode *inode, struct file *file)
482 struct cinergyt2 *cinergyt2 = dvbdev->priv; 483 struct cinergyt2 *cinergyt2 = dvbdev->priv;
483 int err = -ERESTARTSYS; 484 int err = -ERESTARTSYS;
484 485
485 if (cinergyt2->disconnect_pending || mutex_lock_interruptible(&cinergyt2->sem)) 486 if (cinergyt2->disconnect_pending || mutex_lock_interruptible(&cinergyt2->wq_sem))
486 return -ERESTARTSYS; 487 goto out;
487 488
488 if ((err = dvb_generic_open(inode, file))) { 489 if (mutex_lock_interruptible(&cinergyt2->sem))
489 mutex_unlock(&cinergyt2->sem); 490 goto out_unlock1;
490 return err;
491 }
492 491
492 if ((err = dvb_generic_open(inode, file)))
493 goto out_unlock2;
493 494
494 if ((file->f_flags & O_ACCMODE) != O_RDONLY) { 495 if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
495 cinergyt2_sleep(cinergyt2, 0); 496 cinergyt2_sleep(cinergyt2, 0);
@@ -498,8 +499,12 @@ static int cinergyt2_open (struct inode *inode, struct file *file)
498 499
499 atomic_inc(&cinergyt2->inuse); 500 atomic_inc(&cinergyt2->inuse);
500 501
502out_unlock2:
501 mutex_unlock(&cinergyt2->sem); 503 mutex_unlock(&cinergyt2->sem);
502 return 0; 504out_unlock1:
505 mutex_unlock(&cinergyt2->wq_sem);
506out:
507 return err;
503} 508}
504 509
505static void cinergyt2_unregister(struct cinergyt2 *cinergyt2) 510static void cinergyt2_unregister(struct cinergyt2 *cinergyt2)
@@ -519,15 +524,17 @@ static int cinergyt2_release (struct inode *inode, struct file *file)
519 struct dvb_device *dvbdev = file->private_data; 524 struct dvb_device *dvbdev = file->private_data;
520 struct cinergyt2 *cinergyt2 = dvbdev->priv; 525 struct cinergyt2 *cinergyt2 = dvbdev->priv;
521 526
522 mutex_lock(&cinergyt2->sem); 527 mutex_lock(&cinergyt2->wq_sem);
523 528
524 if (!cinergyt2->disconnect_pending && (file->f_flags & O_ACCMODE) != O_RDONLY) { 529 if (!cinergyt2->disconnect_pending && (file->f_flags & O_ACCMODE) != O_RDONLY) {
525 cancel_delayed_work(&cinergyt2->query_work); 530 cancel_rearming_delayed_work(&cinergyt2->query_work);
526 flush_scheduled_work(); 531
532 mutex_lock(&cinergyt2->sem);
527 cinergyt2_sleep(cinergyt2, 1); 533 cinergyt2_sleep(cinergyt2, 1);
534 mutex_unlock(&cinergyt2->sem);
528 } 535 }
529 536
530 mutex_unlock(&cinergyt2->sem); 537 mutex_unlock(&cinergyt2->wq_sem);
531 538
532 if (atomic_dec_and_test(&cinergyt2->inuse) && cinergyt2->disconnect_pending) { 539 if (atomic_dec_and_test(&cinergyt2->inuse) && cinergyt2->disconnect_pending) {
533 warn("delayed unregister in release"); 540 warn("delayed unregister in release");
@@ -838,13 +845,13 @@ static int cinergyt2_register_rc(struct cinergyt2 *cinergyt2)
838 845
839static void cinergyt2_unregister_rc(struct cinergyt2 *cinergyt2) 846static void cinergyt2_unregister_rc(struct cinergyt2 *cinergyt2)
840{ 847{
841 cancel_delayed_work(&cinergyt2->rc_query_work); 848 cancel_rearming_delayed_work(&cinergyt2->rc_query_work);
842 input_unregister_device(cinergyt2->rc_input_dev); 849 input_unregister_device(cinergyt2->rc_input_dev);
843} 850}
844 851
845static inline void cinergyt2_suspend_rc(struct cinergyt2 *cinergyt2) 852static inline void cinergyt2_suspend_rc(struct cinergyt2 *cinergyt2)
846{ 853{
847 cancel_delayed_work(&cinergyt2->rc_query_work); 854 cancel_rearming_delayed_work(&cinergyt2->rc_query_work);
848} 855}
849 856
850static inline void cinergyt2_resume_rc(struct cinergyt2 *cinergyt2) 857static inline void cinergyt2_resume_rc(struct cinergyt2 *cinergyt2)
@@ -907,6 +914,7 @@ static int cinergyt2_probe (struct usb_interface *intf,
907 usb_set_intfdata (intf, (void *) cinergyt2); 914 usb_set_intfdata (intf, (void *) cinergyt2);
908 915
909 mutex_init(&cinergyt2->sem); 916 mutex_init(&cinergyt2->sem);
917 mutex_init(&cinergyt2->wq_sem);
910 init_waitqueue_head (&cinergyt2->poll_wq); 918 init_waitqueue_head (&cinergyt2->poll_wq);
911 INIT_DELAYED_WORK(&cinergyt2->query_work, cinergyt2_query); 919 INIT_DELAYED_WORK(&cinergyt2->query_work, cinergyt2_query);
912 920
@@ -974,11 +982,8 @@ static void cinergyt2_disconnect (struct usb_interface *intf)
974{ 982{
975 struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf); 983 struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf);
976 984
977 flush_scheduled_work();
978
979 cinergyt2_unregister_rc(cinergyt2); 985 cinergyt2_unregister_rc(cinergyt2);
980 986 cancel_rearming_delayed_work(&cinergyt2->query_work);
981 cancel_delayed_work(&cinergyt2->query_work);
982 wake_up_interruptible(&cinergyt2->poll_wq); 987 wake_up_interruptible(&cinergyt2->poll_wq);
983 988
984 cinergyt2->demux.dmx.close(&cinergyt2->demux.dmx); 989 cinergyt2->demux.dmx.close(&cinergyt2->demux.dmx);
@@ -992,21 +997,21 @@ static int cinergyt2_suspend (struct usb_interface *intf, pm_message_t state)
992{ 997{
993 struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf); 998 struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf);
994 999
995 if (cinergyt2->disconnect_pending || mutex_lock_interruptible(&cinergyt2->sem)) 1000 if (cinergyt2->disconnect_pending || mutex_lock_interruptible(&cinergyt2->wq_sem))
996 return -ERESTARTSYS; 1001 return -ERESTARTSYS;
997 1002
998 if (1) { 1003 if (1) {
999 struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf);
1000
1001 cinergyt2_suspend_rc(cinergyt2); 1004 cinergyt2_suspend_rc(cinergyt2);
1002 cancel_delayed_work(&cinergyt2->query_work); 1005 cancel_rearming_delayed_work(&cinergyt2->query_work);
1006
1007 mutex_lock(&cinergyt2->sem);
1003 if (cinergyt2->streaming) 1008 if (cinergyt2->streaming)
1004 cinergyt2_stop_stream_xfer(cinergyt2); 1009 cinergyt2_stop_stream_xfer(cinergyt2);
1005 flush_scheduled_work();
1006 cinergyt2_sleep(cinergyt2, 1); 1010 cinergyt2_sleep(cinergyt2, 1);
1011 mutex_unlock(&cinergyt2->sem);
1007 } 1012 }
1008 1013
1009 mutex_unlock(&cinergyt2->sem); 1014 mutex_unlock(&cinergyt2->wq_sem);
1010 return 0; 1015 return 0;
1011} 1016}
1012 1017
@@ -1014,9 +1019,15 @@ static int cinergyt2_resume (struct usb_interface *intf)
1014{ 1019{
1015 struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf); 1020 struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf);
1016 struct dvbt_set_parameters_msg *param = &cinergyt2->param; 1021 struct dvbt_set_parameters_msg *param = &cinergyt2->param;
1022 int err = -ERESTARTSYS;
1017 1023
1018 if (cinergyt2->disconnect_pending || mutex_lock_interruptible(&cinergyt2->sem)) 1024 if (cinergyt2->disconnect_pending || mutex_lock_interruptible(&cinergyt2->wq_sem))
1019 return -ERESTARTSYS; 1025 goto out;
1026
1027 if (mutex_lock_interruptible(&cinergyt2->sem))
1028 goto out_unlock1;
1029
1030 err = 0;
1020 1031
1021 if (!cinergyt2->sleeping) { 1032 if (!cinergyt2->sleeping) {
1022 cinergyt2_sleep(cinergyt2, 0); 1033 cinergyt2_sleep(cinergyt2, 0);
@@ -1029,7 +1040,10 @@ static int cinergyt2_resume (struct usb_interface *intf)
1029 cinergyt2_resume_rc(cinergyt2); 1040 cinergyt2_resume_rc(cinergyt2);
1030 1041
1031 mutex_unlock(&cinergyt2->sem); 1042 mutex_unlock(&cinergyt2->sem);
1032 return 0; 1043out_unlock1:
1044 mutex_unlock(&cinergyt2->wq_sem);
1045out:
1046 return err;
1033} 1047}
1034 1048
1035static const struct usb_device_id cinergyt2_table [] __devinitdata = { 1049static const struct usb_device_id cinergyt2_table [] __devinitdata = {