aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media
diff options
context:
space:
mode:
authorMatthias Dahl <mldvb@mortal-soul.de>2008-09-26 05:29:03 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2009-01-29 05:35:37 -0500
commitd7e43844e40e07cadc48f1733b9738659f83b38c (patch)
tree8b00c8ae908ec481403de93e60ccb7026815a7fa /drivers/media
parent0c37dd7a9052529cd9346b04530be7878c03e429 (diff)
V4L/DVB (9054): implement proper locking in the dvb ca en50221 driver
Concurrent access to a single DVB CA 50221 interface slot is generally discouraged. The underlying drivers (budget-av, budget-ci) do not implement proper locking and thus two transactions could (and do) interfere with on another. This fixes the following problems seen by others and myself: - sudden i/o errors when writing to the ci device which usually would result in an undefined state of the hw and require a software restart - errors about the CAM trying to send a buffer larger than the agreed size usually also resulting in an undefined state of the hw Due the to design of the DVB CA 50221 driver, implementing the locks in the underlying drivers would not be enough and still leave some race conditions, even though they were harder to trigger. Signed-off-by: Matthias Dahl <devel@mortal-soul.de> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media')
-rw-r--r--drivers/media/dvb/dvb-core/dvb_ca_en50221.c24
-rw-r--r--drivers/media/dvb/dvb-core/dvb_ca_en50221.h6
2 files changed, 25 insertions, 5 deletions
diff --git a/drivers/media/dvb/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb/dvb-core/dvb_ca_en50221.c
index 98ee16773ff2..7e3aeaa7370f 100644
--- a/drivers/media/dvb/dvb-core/dvb_ca_en50221.c
+++ b/drivers/media/dvb/dvb-core/dvb_ca_en50221.c
@@ -93,6 +93,9 @@ struct dvb_ca_slot {
93 /* current state of the CAM */ 93 /* current state of the CAM */
94 int slot_state; 94 int slot_state;
95 95
96 /* mutex used for serializing access to one CI slot */
97 struct mutex slot_lock;
98
96 /* Number of CAMCHANGES that have occurred since last processing */ 99 /* Number of CAMCHANGES that have occurred since last processing */
97 atomic_t camchange_count; 100 atomic_t camchange_count;
98 101
@@ -711,14 +714,20 @@ static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, u8 * b
711 dprintk("%s\n", __func__); 714 dprintk("%s\n", __func__);
712 715
713 716
714 // sanity check 717 /* sanity check */
715 if (bytes_write > ca->slot_info[slot].link_buf_size) 718 if (bytes_write > ca->slot_info[slot].link_buf_size)
716 return -EINVAL; 719 return -EINVAL;
717 720
718 /* check if interface is actually waiting for us to read from it, or if a read is in progress */ 721 /* it is possible we are dealing with a single buffer implementation,
722 thus if there is data available for read or if there is even a read
723 already in progress, we do nothing but awake the kernel thread to
724 process the data if necessary. */
719 if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) 725 if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0)
720 goto exitnowrite; 726 goto exitnowrite;
721 if (status & (STATUSREG_DA | STATUSREG_RE)) { 727 if (status & (STATUSREG_DA | STATUSREG_RE)) {
728 if (status & STATUSREG_DA)
729 dvb_ca_en50221_thread_wakeup(ca);
730
722 status = -EAGAIN; 731 status = -EAGAIN;
723 goto exitnowrite; 732 goto exitnowrite;
724 } 733 }
@@ -987,6 +996,8 @@ static int dvb_ca_en50221_thread(void *data)
987 /* go through all the slots processing them */ 996 /* go through all the slots processing them */
988 for (slot = 0; slot < ca->slot_count; slot++) { 997 for (slot = 0; slot < ca->slot_count; slot++) {
989 998
999 mutex_lock(&ca->slot_info[slot].slot_lock);
1000
990 // check the cam status + deal with CAMCHANGEs 1001 // check the cam status + deal with CAMCHANGEs
991 while (dvb_ca_en50221_check_camstatus(ca, slot)) { 1002 while (dvb_ca_en50221_check_camstatus(ca, slot)) {
992 /* clear down an old CI slot if necessary */ 1003 /* clear down an old CI slot if necessary */
@@ -1122,7 +1133,7 @@ static int dvb_ca_en50221_thread(void *data)
1122 1133
1123 case DVB_CA_SLOTSTATE_RUNNING: 1134 case DVB_CA_SLOTSTATE_RUNNING:
1124 if (!ca->open) 1135 if (!ca->open)
1125 continue; 1136 break;
1126 1137
1127 // poll slots for data 1138 // poll slots for data
1128 pktcount = 0; 1139 pktcount = 0;
@@ -1146,6 +1157,8 @@ static int dvb_ca_en50221_thread(void *data)
1146 } 1157 }
1147 break; 1158 break;
1148 } 1159 }
1160
1161 mutex_unlock(&ca->slot_info[slot].slot_lock);
1149 } 1162 }
1150 } 1163 }
1151 1164
@@ -1181,6 +1194,7 @@ static int dvb_ca_en50221_io_do_ioctl(struct inode *inode, struct file *file,
1181 switch (cmd) { 1194 switch (cmd) {
1182 case CA_RESET: 1195 case CA_RESET:
1183 for (slot = 0; slot < ca->slot_count; slot++) { 1196 for (slot = 0; slot < ca->slot_count; slot++) {
1197 mutex_lock(&ca->slot_info[slot].slot_lock);
1184 if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_NONE) { 1198 if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_NONE) {
1185 dvb_ca_en50221_slot_shutdown(ca, slot); 1199 dvb_ca_en50221_slot_shutdown(ca, slot);
1186 if (ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE) 1200 if (ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)
@@ -1188,6 +1202,7 @@ static int dvb_ca_en50221_io_do_ioctl(struct inode *inode, struct file *file,
1188 slot, 1202 slot,
1189 DVB_CA_EN50221_CAMCHANGE_INSERTED); 1203 DVB_CA_EN50221_CAMCHANGE_INSERTED);
1190 } 1204 }
1205 mutex_unlock(&ca->slot_info[slot].slot_lock);
1191 } 1206 }
1192 ca->next_read_slot = 0; 1207 ca->next_read_slot = 0;
1193 dvb_ca_en50221_thread_wakeup(ca); 1208 dvb_ca_en50221_thread_wakeup(ca);
@@ -1308,7 +1323,9 @@ static ssize_t dvb_ca_en50221_io_write(struct file *file,
1308 goto exit; 1323 goto exit;
1309 } 1324 }
1310 1325
1326 mutex_lock(&ca->slot_info[slot].slot_lock);
1311 status = dvb_ca_en50221_write_data(ca, slot, fragbuf, fraglen + 2); 1327 status = dvb_ca_en50221_write_data(ca, slot, fragbuf, fraglen + 2);
1328 mutex_unlock(&ca->slot_info[slot].slot_lock);
1312 if (status == (fraglen + 2)) { 1329 if (status == (fraglen + 2)) {
1313 written = 1; 1330 written = 1;
1314 break; 1331 break;
@@ -1664,6 +1681,7 @@ int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter,
1664 ca->slot_info[i].slot_state = DVB_CA_SLOTSTATE_NONE; 1681 ca->slot_info[i].slot_state = DVB_CA_SLOTSTATE_NONE;
1665 atomic_set(&ca->slot_info[i].camchange_count, 0); 1682 atomic_set(&ca->slot_info[i].camchange_count, 0);
1666 ca->slot_info[i].camchange_type = DVB_CA_EN50221_CAMCHANGE_REMOVED; 1683 ca->slot_info[i].camchange_type = DVB_CA_EN50221_CAMCHANGE_REMOVED;
1684 mutex_init(&ca->slot_info[i].slot_lock);
1667 } 1685 }
1668 1686
1669 if (signal_pending(current)) { 1687 if (signal_pending(current)) {
diff --git a/drivers/media/dvb/dvb-core/dvb_ca_en50221.h b/drivers/media/dvb/dvb-core/dvb_ca_en50221.h
index 8467e63ddc0d..7df2e141187a 100644
--- a/drivers/media/dvb/dvb-core/dvb_ca_en50221.h
+++ b/drivers/media/dvb/dvb-core/dvb_ca_en50221.h
@@ -45,8 +45,10 @@ struct dvb_ca_en50221 {
45 /* the module owning this structure */ 45 /* the module owning this structure */
46 struct module* owner; 46 struct module* owner;
47 47
48 /* NOTE: the read_*, write_* and poll_slot_status functions must use locks as 48 /* NOTE: the read_*, write_* and poll_slot_status functions will be
49 * they may be called from several threads at once */ 49 * called for different slots concurrently and need to use locks where
50 * and if appropriate. There will be no concurrent access to one slot.
51 */
50 52
51 /* functions for accessing attribute memory on the CAM */ 53 /* functions for accessing attribute memory on the CAM */
52 int (*read_attribute_mem)(struct dvb_ca_en50221* ca, int slot, int address); 54 int (*read_attribute_mem)(struct dvb_ca_en50221* ca, int slot, int address);