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>
This commit is contained in:
Matthias Dahl 2008-09-26 06:29:03 -03:00 committed by Mauro Carvalho Chehab
parent 0c37dd7a90
commit d7e43844e4
2 changed files with 25 additions and 5 deletions

View File

@ -93,6 +93,9 @@ struct dvb_ca_slot {
/* current state of the CAM */
int slot_state;
/* mutex used for serializing access to one CI slot */
struct mutex slot_lock;
/* Number of CAMCHANGES that have occurred since last processing */
atomic_t camchange_count;
@ -711,14 +714,20 @@ static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, u8 * b
dprintk("%s\n", __func__);
// sanity check
/* sanity check */
if (bytes_write > ca->slot_info[slot].link_buf_size)
return -EINVAL;
/* check if interface is actually waiting for us to read from it, or if a read is in progress */
/* it is possible we are dealing with a single buffer implementation,
thus if there is data available for read or if there is even a read
already in progress, we do nothing but awake the kernel thread to
process the data if necessary. */
if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0)
goto exitnowrite;
if (status & (STATUSREG_DA | STATUSREG_RE)) {
if (status & STATUSREG_DA)
dvb_ca_en50221_thread_wakeup(ca);
status = -EAGAIN;
goto exitnowrite;
}
@ -987,6 +996,8 @@ static int dvb_ca_en50221_thread(void *data)
/* go through all the slots processing them */
for (slot = 0; slot < ca->slot_count; slot++) {
mutex_lock(&ca->slot_info[slot].slot_lock);
// check the cam status + deal with CAMCHANGEs
while (dvb_ca_en50221_check_camstatus(ca, slot)) {
/* clear down an old CI slot if necessary */
@ -1122,7 +1133,7 @@ static int dvb_ca_en50221_thread(void *data)
case DVB_CA_SLOTSTATE_RUNNING:
if (!ca->open)
continue;
break;
// poll slots for data
pktcount = 0;
@ -1146,6 +1157,8 @@ static int dvb_ca_en50221_thread(void *data)
}
break;
}
mutex_unlock(&ca->slot_info[slot].slot_lock);
}
}
@ -1181,6 +1194,7 @@ static int dvb_ca_en50221_io_do_ioctl(struct inode *inode, struct file *file,
switch (cmd) {
case CA_RESET:
for (slot = 0; slot < ca->slot_count; slot++) {
mutex_lock(&ca->slot_info[slot].slot_lock);
if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_NONE) {
dvb_ca_en50221_slot_shutdown(ca, slot);
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,
slot,
DVB_CA_EN50221_CAMCHANGE_INSERTED);
}
mutex_unlock(&ca->slot_info[slot].slot_lock);
}
ca->next_read_slot = 0;
dvb_ca_en50221_thread_wakeup(ca);
@ -1308,7 +1323,9 @@ static ssize_t dvb_ca_en50221_io_write(struct file *file,
goto exit;
}
mutex_lock(&ca->slot_info[slot].slot_lock);
status = dvb_ca_en50221_write_data(ca, slot, fragbuf, fraglen + 2);
mutex_unlock(&ca->slot_info[slot].slot_lock);
if (status == (fraglen + 2)) {
written = 1;
break;
@ -1664,6 +1681,7 @@ int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter,
ca->slot_info[i].slot_state = DVB_CA_SLOTSTATE_NONE;
atomic_set(&ca->slot_info[i].camchange_count, 0);
ca->slot_info[i].camchange_type = DVB_CA_EN50221_CAMCHANGE_REMOVED;
mutex_init(&ca->slot_info[i].slot_lock);
}
if (signal_pending(current)) {

View File

@ -45,8 +45,10 @@ struct dvb_ca_en50221 {
/* the module owning this structure */
struct module* owner;
/* NOTE: the read_*, write_* and poll_slot_status functions must use locks as
* they may be called from several threads at once */
/* NOTE: the read_*, write_* and poll_slot_status functions will be
* called for different slots concurrently and need to use locks where
* and if appropriate. There will be no concurrent access to one slot.
*/
/* functions for accessing attribute memory on the CAM */
int (*read_attribute_mem)(struct dvb_ca_en50221* ca, int slot, int address);