Mailing List archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[linux-dvb] Re: linux-dvb Re Nova-s CAMCI support



On Thursday 13 May 2004 11:53, you wrote:
> >>Hi, if you have a moment, can you give the attached patch a go? I've
> >>hopefully sorted out the locking....
> >
> >Oops - found a slight locking bug, sorry.
>
> Thanks for looking into this! I've tried the patch but it doesn't seem
> to make it either better or worse. I've attached logs from two tests.
> The first error was at 12:23:42, and the second at 12:41:16. Two
> different channels. I also did some other tests but didn't keep the logs
> (they looked about the same anyway). Do we need more logging?
>
> I patched against a pure Linux 2.6.6 source. I think the affected files
> are the same version as those in CVS so that should work right? Or do I
> need CVS?

2.6.6 should be fine - there haven't been any changes to the CVS CA code since 
then.

May 13 12:23:42 htpc kernel: dvb_ca_en50221_io_poll
May 13 12:23:42 htpc kernel: dvb_ca_en50221_io_read
May 13 12:23:42 htpc kernel: dvb_ca_en50221_io_write
May 13 12:23:42 htpc kernel: dvb_ca_en50221_write_data
May 13 12:23:42 htpc kernel: dvb_ca_en50221_io_poll
May 13 12:23:42 htpc kernel: FR/DA IRQ slot:0
May 13 12:23:42 htpc kernel: dvb_ca_en50221_thread_wakeup
May 13 12:23:42 htpc kernel: dvb_ca_en50221_read_data
May 13 12:23:42 htpc kernel: FR/DA IRQ slot:0
May 13 12:23:42 htpc kernel: dvb_ca_en50221_read_data

There is something a bit odd here: the second "FR/DA IRQ slot:0" should 
generate a call to dvb_ca_en50221_thread_wakeup() as I've only enabled DA 
IRQs... but it isn't. Hmm, theres actually a superfluous read of the CAM 
control register in the IRQ tasklet. Maybe your CAM doesn't like that 
happening in the middle of reading a packet and is getting confused.

This new patch should remove it. Hmm, if this _is_ what is happening, I'll 
need to revamp the locking yet again - writing data at the wrong time would 
trigger the problem as well.
Index: linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.c
===================================================================
RCS file: /cvs/linuxtv/dvb-kernel/linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.c,v
retrieving revision 1.9
diff -a -u -b -r1.9 dvb_ca_en50221.c
--- linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.c	3 May 2004 16:29:27 -0000	1.9
+++ linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.c	13 May 2004 11:39:16 -0000
@@ -34,7 +34,7 @@
 #include <linux/module.h>
 #include <linux/vmalloc.h>
 #include <linux/delay.h>
-#include <asm/semaphore.h>
+#include <asm/rwsem.h>
 #include <asm/atomic.h>
 
 #include "dvb_ca_en50221.h"
@@ -108,7 +108,7 @@
         int link_buf_size;
 
         /* semaphore for syncing access to slot structure */
-        struct semaphore sem;
+        struct rw_semaphore sem;
 
         /* buffer for incoming packets */
         struct dvb_ringbuffer rx_buffer;
@@ -199,7 +199,6 @@
 static int dvb_ca_en50221_check_camstatus(struct dvb_ca_private* ca, int slot)
 {
         int slot_status;
-        int status;
         int cam_present_now;
         int cam_changed;
 
@@ -209,9 +208,7 @@
         }
 
         /* poll mode */
-        if ((status = down_interruptible(&ca->slot_info[slot].sem)) != 0) return status;
         slot_status = ca->pub->poll_slot_status(ca->pub, slot);
-        up(&ca->slot_info[slot].sem);
 
         cam_present_now = (slot_status & DVB_CA_EN50221_POLL_CAM_PRESENT) ? 1: 0;
         cam_changed = (slot_status & DVB_CA_EN50221_POLL_CAM_CHANGED) ? 1: 0;
@@ -550,11 +547,9 @@
 
         dprintk ("%s\n", __FUNCTION__);
 
-        /* acquire the slot */
-        if ((status = down_interruptible(&ca->slot_info[slot].sem)) != 0) return status;
-
         /* check if we have space for a link buf in the rx_buffer */
         if (ebuf == NULL) {
+		down_read(&ca->slot_info[slot].sem);
                 if (dvb_ringbuffer_free(&ca->slot_info[slot].rx_buffer) <
                     (ca->slot_info[slot].link_buf_size + DVB_RINGBUFFER_PKTHDRSIZE)) {
                         status = -EAGAIN;
@@ -562,13 +557,8 @@
                 }
         }
 
-        /* reset the interface if there's been a tx error */
+        /* check if there is data available */
         if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) goto exit;
-        if (status & STATUSREG_TXERR) {
-                ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
-                status = -EIO;
-                goto exit;
-        }
         if (!(status & STATUSREG_DA)) {
                 /* no data */
                 status = 0;
@@ -612,9 +602,10 @@
                 buf[i] = status;
         }
 
-        /* check for read error (RE should now go to 0) */
+        /* check for read error (RE should now be 0) */
         if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) goto exit;
         if (status & STATUSREG_RE) {
+		ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
                 status = -EIO;
                 goto exit;
         }
@@ -630,11 +621,10 @@
         if ((buf[1] & 0x80) == 0x00) {
                 wake_up_interruptible(&ca->wait_queue);
         }
-
         status = bytes_read;
 
 exit:
-        up(&ca->slot_info[slot].sem);
+	if (ebuf == NULL) up_read(&ca->slot_info[slot].sem);
         return status;
 }
 
@@ -662,19 +652,9 @@
         // sanity check
         if (bytes_write > ca->slot_info[slot].link_buf_size) return -EINVAL;
 
-        /* acquire the slot */
-        if ((status = down_interruptible(&ca->slot_info[slot].sem)) != 0) return status;
-
-        /* reset the interface if there's been a tx error */
+        /* check if interface is actually waiting for us to read from it, or if a read is in progress */
         if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) goto exitnowrite;
-        if (status & STATUSREG_TXERR) {
-                ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
-                status = -EIO;
-                goto exitnowrite;
-        }
-
-        /* check if interface is actually waiting for us to read from it */
-        if (status & STATUSREG_DA) {
+        if (status & (STATUSREG_DA|STATUSREG_RE)) {
                 status = -EAGAIN;
                 goto exitnowrite;
         }
@@ -702,6 +682,7 @@
         /* check for write error (WE should now be 0) */
         if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) goto exit;
         if (status & STATUSREG_WE) {
+		ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
                 status = -EIO;
                 goto exit;
         }
@@ -711,7 +692,6 @@
         ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN);
 
 exitnowrite:
-        up(&ca->slot_info[slot].sem);
         return status;
 }
 
@@ -729,16 +709,14 @@
  */
 static int dvb_ca_en50221_slot_shutdown(struct dvb_ca_private* ca, int slot)
 {
-        int status;
-
         dprintk ("%s\n", __FUNCTION__);
 
-        if ((status = down_interruptible(&ca->slot_info[slot].sem)) != 0) return status;
+        down_write(&ca->slot_info[slot].sem);
         ca->pub->slot_shutdown(ca->pub, slot);
         ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_NONE;
         if (ca->slot_info[slot].rx_buffer.data) vfree(ca->slot_info[slot].rx_buffer.data);
         ca->slot_info[slot].rx_buffer.data = NULL;
-        up(&ca->slot_info[slot].sem);
+        up_write(&ca->slot_info[slot].sem);
 
         /* need to wake up all processes to check if they're now
            trying to write to a defunct CAM */
@@ -821,10 +799,7 @@
                 break;
 
         case DVB_CA_SLOTSTATE_RUNNING:
-                flags = ca->pub->read_cam_control(pubca, slot, CTRLIF_STATUS);
-                if (flags & STATUSREG_DA) {
                         dvb_ca_en50221_thread_wakeup(ca);
-                }
                 break;
         }
 }
@@ -1266,7 +1241,7 @@
         while((slot_count < ca->slot_count) && (!found)) {
                 if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_RUNNING) goto nextslot;
 
-                if ((*result = down_interruptible(&ca->slot_info[slot].sem)) != 0) return 1;
+                down_read(&ca->slot_info[slot].sem);
 
                 idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, -1, &fraglen);
                 while(idx != -1) {
@@ -1281,7 +1256,7 @@
                         idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, idx, &fraglen);
                 }
 
-                if (!found) up(&ca->slot_info[slot].sem);
+                if (!found) up_read(&ca->slot_info[slot].sem);
 
 nextslot:
                 slot = (slot + 1) % ca->slot_count;
@@ -1378,7 +1353,7 @@
         status = pktlen;
 
 exit:
-        up(&ca->slot_info[slot].sem);
+        up_read(&ca->slot_info[slot].sem);
         return status;
 }
 
@@ -1406,7 +1381,9 @@
 
         for(i=0; i< ca->slot_count; i++) {
                 if (ca->slot_info[i].slot_state == DVB_CA_SLOTSTATE_RUNNING) {
+		        down_write(&ca->slot_info[i].sem);
                         dvb_ringbuffer_flush(&ca->slot_info[i].rx_buffer);
+		        up_write(&ca->slot_info[i].sem);
                 }
         }
 
@@ -1464,7 +1441,7 @@
         dprintk ("%s\n", __FUNCTION__);
 
         if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1) {
-                up(&ca->slot_info[slot].sem);
+                up_read(&ca->slot_info[slot].sem);
                 mask |= POLLIN;
         }
 
@@ -1475,7 +1452,7 @@
         poll_wait(file, &ca->wait_queue, wait);
 
         if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1) {
-                up(&ca->slot_info[slot].sem);
+                up_read(&ca->slot_info[slot].sem);
                 mask |= POLLIN;
         }
 
@@ -1559,7 +1536,7 @@
                 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;
-                init_MUTEX(&ca->slot_info[i].sem);
+                init_rwsem(&ca->slot_info[i].sem);
         }
 
         if (signal_pending(current)) {
Index: linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.h
===================================================================
RCS file: /cvs/linuxtv/dvb-kernel/linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.h,v
retrieving revision 1.1
diff -a -u -b -r1.1 dvb_ca_en50221.h
--- linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.h	5 Apr 2004 12:17:33 -0000	1.1
+++ linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.h	13 May 2004 11:39:16 -0000
@@ -42,6 +42,9 @@
 /* Structure describing a CA interface */
 struct dvb_ca_en50221 {
 
+	/* NOTE: the read_*, write_* and poll_slot_status functions must use locks as
+	 * they may be called from several threads at once */
+
 	/* functions for accessing attribute memory on the CAM */
 	int (*read_attribute_mem)(struct dvb_ca_en50221* ca, int slot, int address);
 	int (*write_attribute_mem)(struct dvb_ca_en50221* ca, int slot, int address, u8 value);

Home | Main Index | Thread Index