aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/cx88
diff options
context:
space:
mode:
authorJonathan Nieder <jrnieder@gmail.com>2011-05-01 05:29:16 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2011-05-20 08:29:34 -0400
commit8a317a8760cfffa8185b56ff59fb4b6c58488d79 (patch)
tree210f2f43a145a495ec20c44f33d8a1aeeec04c25 /drivers/media/video/cx88
parentcae72c7c63fd4a8f20efc001108f12f34076c17b (diff)
[media] cx88: protect per-device driver list with device lock
The BKL conversion of this driver seems to have gone wrong. Various uses of the sub-device and driver lists appear to be subject to race conditions. In particular, some functions access drvlist without a relevant lock held, which will race against removal of drivers. Let's start with that --- clean up by consistently protecting dev->drvlist with dev->core->lock, noting driver functions that require the device lock to be held or not to be held. After this patch, there are still some races --- e.g., cx8802_blackbird_remove can run between the time the blackbird driver is acquired and the time it is used in mpeg_release, and there's a similar race in cx88_dvb_bus_ctrl. Later patches will address the remaining known races and the deadlock noticed by Andi. This patch just makes the semantics clearer in preparation for those later changes. Based on work by Ben Hutchings <ben@decadent.org.uk>. Tested-by: Andi Huber <hobrom@gmx.at> Tested-by: Marlon de Boer <marlon@hyves.nl> Cc: stable@kernel.org Signed-off-by: Jonathan Nieder <jrnieder@gmail.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video/cx88')
-rw-r--r--drivers/media/video/cx88/cx88-blackbird.c3
-rw-r--r--drivers/media/video/cx88/cx88-dvb.c3
-rw-r--r--drivers/media/video/cx88/cx88-mpeg.c11
-rw-r--r--drivers/media/video/cx88/cx88.h9
4 files changed, 20 insertions, 6 deletions
diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c
index bca307eb1e24..b93fbd39a39e 100644
--- a/drivers/media/video/cx88/cx88-blackbird.c
+++ b/drivers/media/video/cx88/cx88-blackbird.c
@@ -1122,10 +1122,11 @@ static int mpeg_release(struct file *file)
1122 mutex_lock(&dev->core->lock); 1122 mutex_lock(&dev->core->lock);
1123 file->private_data = NULL; 1123 file->private_data = NULL;
1124 kfree(fh); 1124 kfree(fh);
1125 mutex_unlock(&dev->core->lock);
1126 1125
1127 /* Make sure we release the hardware */ 1126 /* Make sure we release the hardware */
1128 drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD); 1127 drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD);
1128 mutex_unlock(&dev->core->lock);
1129
1129 if (drv) 1130 if (drv)
1130 drv->request_release(drv); 1131 drv->request_release(drv);
1131 1132
diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c
index 7b8c9d3b6efc..84002bc80db5 100644
--- a/drivers/media/video/cx88/cx88-dvb.c
+++ b/drivers/media/video/cx88/cx88-dvb.c
@@ -133,7 +133,10 @@ static int cx88_dvb_bus_ctrl(struct dvb_frontend* fe, int acquire)
133 return -EINVAL; 133 return -EINVAL;
134 } 134 }
135 135
136 mutex_lock(&dev->core->lock);
136 drv = cx8802_get_driver(dev, CX88_MPEG_DVB); 137 drv = cx8802_get_driver(dev, CX88_MPEG_DVB);
138 mutex_unlock(&dev->core->lock);
139
137 if (drv) { 140 if (drv) {
138 if (acquire){ 141 if (acquire){
139 dev->frontends.active_fe_id = fe_id; 142 dev->frontends.active_fe_id = fe_id;
diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c
index 9b500e691a5c..a092c3a4c56d 100644
--- a/drivers/media/video/cx88/cx88-mpeg.c
+++ b/drivers/media/video/cx88/cx88-mpeg.c
@@ -748,6 +748,8 @@ int cx8802_unregister_driver(struct cx8802_driver *drv)
748 dev->pci->subsystem_device, dev->core->board.name, 748 dev->pci->subsystem_device, dev->core->board.name,
749 dev->core->boardnr); 749 dev->core->boardnr);
750 750
751 mutex_lock(&dev->core->lock);
752
751 list_for_each_entry_safe(d, dtmp, &dev->drvlist, drvlist) { 753 list_for_each_entry_safe(d, dtmp, &dev->drvlist, drvlist) {
752 /* only unregister the correct driver type */ 754 /* only unregister the correct driver type */
753 if (d->type_id != drv->type_id) 755 if (d->type_id != drv->type_id)
@@ -755,15 +757,14 @@ int cx8802_unregister_driver(struct cx8802_driver *drv)
755 757
756 err = d->remove(d); 758 err = d->remove(d);
757 if (err == 0) { 759 if (err == 0) {
758 mutex_lock(&drv->core->lock);
759 list_del(&d->drvlist); 760 list_del(&d->drvlist);
760 mutex_unlock(&drv->core->lock);
761 kfree(d); 761 kfree(d);
762 } else 762 } else
763 printk(KERN_ERR "%s/2: cx8802 driver remove " 763 printk(KERN_ERR "%s/2: cx8802 driver remove "
764 "failed (%d)\n", dev->core->name, err); 764 "failed (%d)\n", dev->core->name, err);
765 } 765 }
766 766
767 mutex_unlock(&dev->core->lock);
767 } 768 }
768 769
769 return err; 770 return err;
@@ -827,6 +828,8 @@ static void __devexit cx8802_remove(struct pci_dev *pci_dev)
827 828
828 flush_request_modules(dev); 829 flush_request_modules(dev);
829 830
831 mutex_lock(&dev->core->lock);
832
830 if (!list_empty(&dev->drvlist)) { 833 if (!list_empty(&dev->drvlist)) {
831 struct cx8802_driver *drv, *tmp; 834 struct cx8802_driver *drv, *tmp;
832 int err; 835 int err;
@@ -838,9 +841,7 @@ static void __devexit cx8802_remove(struct pci_dev *pci_dev)
838 list_for_each_entry_safe(drv, tmp, &dev->drvlist, drvlist) { 841 list_for_each_entry_safe(drv, tmp, &dev->drvlist, drvlist) {
839 err = drv->remove(drv); 842 err = drv->remove(drv);
840 if (err == 0) { 843 if (err == 0) {
841 mutex_lock(&drv->core->lock);
842 list_del(&drv->drvlist); 844 list_del(&drv->drvlist);
843 mutex_unlock(&drv->core->lock);
844 } else 845 } else
845 printk(KERN_ERR "%s/2: cx8802 driver remove " 846 printk(KERN_ERR "%s/2: cx8802 driver remove "
846 "failed (%d)\n", dev->core->name, err); 847 "failed (%d)\n", dev->core->name, err);
@@ -848,6 +849,8 @@ static void __devexit cx8802_remove(struct pci_dev *pci_dev)
848 } 849 }
849 } 850 }
850 851
852 mutex_unlock(&dev->core->lock);
853
851 /* Destroy any 8802 reference. */ 854 /* Destroy any 8802 reference. */
852 dev->core->dvbdev = NULL; 855 dev->core->dvbdev = NULL;
853 856
diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h
index 9b3742a7746c..e3d56c235203 100644
--- a/drivers/media/video/cx88/cx88.h
+++ b/drivers/media/video/cx88/cx88.h
@@ -506,7 +506,11 @@ struct cx8802_driver {
506 int (*resume)(struct pci_dev *pci_dev); 506 int (*resume)(struct pci_dev *pci_dev);
507 507
508 /* MPEG 8802 -> mini driver - Driver probe and configuration */ 508 /* MPEG 8802 -> mini driver - Driver probe and configuration */
509
510 /* Caller must _not_ hold core->lock */
509 int (*probe)(struct cx8802_driver *drv); 511 int (*probe)(struct cx8802_driver *drv);
512
513 /* Caller must hold core->lock */
510 int (*remove)(struct cx8802_driver *drv); 514 int (*remove)(struct cx8802_driver *drv);
511 515
512 /* MPEG 8802 -> mini driver - Access for hardware control */ 516 /* MPEG 8802 -> mini driver - Access for hardware control */
@@ -561,8 +565,9 @@ struct cx8802_dev {
561 /* for switching modulation types */ 565 /* for switching modulation types */
562 unsigned char ts_gen_cntrl; 566 unsigned char ts_gen_cntrl;
563 567
564 /* List of attached drivers */ 568 /* List of attached drivers; must hold core->lock to access */
565 struct list_head drvlist; 569 struct list_head drvlist;
570
566 struct work_struct request_module_wk; 571 struct work_struct request_module_wk;
567}; 572};
568 573
@@ -685,6 +690,8 @@ int cx88_audio_thread(void *data);
685 690
686int cx8802_register_driver(struct cx8802_driver *drv); 691int cx8802_register_driver(struct cx8802_driver *drv);
687int cx8802_unregister_driver(struct cx8802_driver *drv); 692int cx8802_unregister_driver(struct cx8802_driver *drv);
693
694/* Caller must hold core->lock */
688struct cx8802_driver * cx8802_get_driver(struct cx8802_dev *dev, enum cx88_board_type btype); 695struct cx8802_driver * cx8802_get_driver(struct cx8802_dev *dev, enum cx88_board_type btype);
689 696
690/* ----------------------------------------------------------- */ 697/* ----------------------------------------------------------- */