From 3165ca2068855d7118d78396adce4c49a4e7f636 Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Mon, 4 May 2009 20:43:02 -0400 Subject: dvb_frontend: fix race condition resulting in dropped tuning commands From: Devin Heitmueller A race condition was detected in the case that putting the tuner to sleep takes an unusually long period of time, combined with applications that quickly close/open the dvb frontend. The kaffeine channel scanner closes and reopens the dvb frontend between each tuning attempt. If it takes an unusually longer period of time to put the tuner to sleep (for example, the Pinnacle 801e takes 660 ms), the dvb_frontend thread will still be in a running state (and hence fepriv->thread is still set) but the fepriv->exit field will still be zero. As a result, if a dvb_frontend_start() call arrives while the frontend thread is in the process of terminating, the call will return 0 without actually starting a new thread. This results in the tuning request being dropped. To address this, mark fepriv->exit as soon as we know the thread is going to be terminated, so that dvb_frontend_start() knows to start a new instance. Problem encountered with Kaffeine 0.8.7 doing ATSC scanning against the Pinnacle 801e tuner, in conjunction with new code to power down the xc5000 when not in use. Priority: normal Signed-off-by: Devin Heitmueller --- linux/drivers/media/dvb/dvb-core/dvb_frontend.c | 1 + 1 file changed, 1 insertion(+) (limited to 'linux/drivers/media/dvb/dvb-core') diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c index da2890b22..d359f6841 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -554,6 +554,7 @@ restart: if (kthread_should_stop() || dvb_frontend_is_exiting(fe)) { /* got signal or quitting */ + fepriv->exit = 1; break; } -- cgit v1.2.3 From 3e94e24e59b323cdacd22598b28769753866bfc3 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 May 2009 20:39:28 +0000 Subject: core: fix potential mutex_unlock without mutex_lock in dvb_dvr_read From: Simon Arlott dvb_dvr_read may unlock the dmxdev mutex and return -ENODEV, except this function is a file op and will never be called with the mutex held. There's existing mutex_lock and mutex_unlock around the actual read but it's commented out. These should probably be uncommented but the read blocks and this could block another non-blocking reader on the mutex instead. This change comments out the extra mutex_unlock. [akpm@linux-foundation.org: cleanups, simplification] Signed-off-by: Simon Arlott Signed-off-by: Andrew Morton Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/dvb/dvb-core/dmxdev.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) (limited to 'linux/drivers/media/dvb/dvb-core') diff --git a/linux/drivers/media/dvb/dvb-core/dmxdev.c b/linux/drivers/media/dvb/dvb-core/dmxdev.c index c35fbb8d8..6d6121eb5 100644 --- a/linux/drivers/media/dvb/dvb-core/dmxdev.c +++ b/linux/drivers/media/dvb/dvb-core/dmxdev.c @@ -244,19 +244,13 @@ static ssize_t dvb_dvr_read(struct file *file, char __user *buf, size_t count, { struct dvb_device *dvbdev = file->private_data; struct dmxdev *dmxdev = dvbdev->priv; - int ret; - if (dmxdev->exit) { - mutex_unlock(&dmxdev->mutex); + if (dmxdev->exit) return -ENODEV; - } - //mutex_lock(&dmxdev->mutex); - ret = dvb_dmxdev_buffer_read(&dmxdev->dvr_buffer, - file->f_flags & O_NONBLOCK, - buf, count, ppos); - //mutex_unlock(&dmxdev->mutex); - return ret; + return dvb_dmxdev_buffer_read(&dmxdev->dvr_buffer, + file->f_flags & O_NONBLOCK, + buf, count, ppos); } static int dvb_dvr_set_buffer_size(struct dmxdev *dmxdev, -- cgit v1.2.3 From 5f623f9662748ffe9662ee5b0561fe746ef3279d Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Wed, 20 May 2009 22:58:16 -0400 Subject: dvb_frontend: fix case where fepriv->exit not reset From: Devin Heitmueller The fact that we now explicitly set fepriv->exit = 1 when the thread is shutting down exposed an edge case where it was not being reset back to zero once the thread went away in some cases. This resulted in failures in cases where the frontend was closed, and then opened O_RDONLY, since in that case the thread is not being restarted but it was checking the fepriv->exit flag. Thanks to Thierry Lelegard, who and encountered and debugged a large portion of the issue in the same twelve hours that I did (as well as testing my patch). Priority: high Signed-off-by: Devin Heitmueller Cc: Thierry Lelegard --- linux/drivers/media/dvb/dvb-core/dvb_frontend.c | 1 + 1 file changed, 1 insertion(+) (limited to 'linux/drivers/media/dvb/dvb-core') diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c index d359f6841..41166d407 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -668,6 +668,7 @@ restart: } fepriv->thread = NULL; + fepriv->exit = 0; mb(); dvb_frontend_wakeup(fe); -- cgit v1.2.3 From 7ef1e386396d4ebc645310511099bf2526e519c4 Mon Sep 17 00:00:00 2001 From: Abylay Ospan Date: Sat, 6 Jun 2009 16:31:34 +0400 Subject: TS continuity check: show error message when discontinuity detected or TEI flag detected in header Signed-off-by: Abylay Ospan --- linux/drivers/media/dvb/dvb-core/dvb_demux.c | 42 ++++++++++++++++++++++++++++ linux/drivers/media/dvb/dvb-core/dvb_demux.h | 4 +++ 2 files changed, 46 insertions(+) (limited to 'linux/drivers/media/dvb/dvb-core') diff --git a/linux/drivers/media/dvb/dvb-core/dvb_demux.c b/linux/drivers/media/dvb/dvb-core/dvb_demux.c index e2eca0b1f..cfe2768d2 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_demux.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_demux.c @@ -38,6 +38,16 @@ */ // #define DVB_DEMUX_SECTION_LOSS_LOG +static int dvb_demux_tscheck; +module_param(dvb_demux_tscheck, int, 0644); +MODULE_PARM_DESC(dvb_demux_tscheck, + "enable transport stream continuity and TEI check"); + +#define dprintk_tscheck(x...) do { \ + if (dvb_demux_tscheck && printk_ratelimit()) \ + printk(x); \ + } while (0) + /****************************************************************************** * static inlined helper functions ******************************************************************************/ @@ -376,6 +386,36 @@ static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf) u16 pid = ts_pid(buf); int dvr_done = 0; + if (dvb_demux_tscheck) { + if (!demux->cnt_storage) + demux->cnt_storage = vmalloc(MAX_PID + 1); + + if (!demux->cnt_storage) { + printk(KERN_WARNING "Couldn't allocate memory for TS/TEI check. Disabling it\n"); + dvb_demux_tscheck = 0; + goto no_dvb_demux_tscheck; + } + + /* check pkt counter */ + if (pid < MAX_PID) { + if (buf[1] & 0x80) + dprintk_tscheck("TEI detected. " + "PID=0x%x data1=0x%x\n", + pid, buf[1]); + + if ((buf[3] & 0xf) != demux->cnt_storage[pid]) + dprintk_tscheck("TS packet counter mismatch. " + "PID=0x%x expected 0x%x " + "got 0x%x\n", + pid, demux->cnt_storage[pid], + buf[3] & 0xf); + + demux->cnt_storage[pid] = ((buf[3] & 0xf) + 1)&0xf; + }; + /* end check */ + }; +no_dvb_demux_tscheck: + list_for_each_entry(feed, &demux->feed_list, list_head) { if ((feed->pid != pid) && (feed->pid != 0x2000)) continue; @@ -1160,6 +1200,7 @@ int dvb_dmx_init(struct dvb_demux *dvbdemux) int i; struct dmx_demux *dmx = &dvbdemux->dmx; + dvbdemux->cnt_storage = NULL; dvbdemux->users = 0; dvbdemux->filter = vmalloc(dvbdemux->filternum * sizeof(struct dvb_demux_filter)); @@ -1226,6 +1267,7 @@ EXPORT_SYMBOL(dvb_dmx_init); void dvb_dmx_release(struct dvb_demux *dvbdemux) { + vfree(dvbdemux->cnt_storage); vfree(dvbdemux->filter); vfree(dvbdemux->feed); } diff --git a/linux/drivers/media/dvb/dvb-core/dvb_demux.h b/linux/drivers/media/dvb/dvb-core/dvb_demux.h index 933397c24..1e0ade4ec 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_demux.h +++ b/linux/drivers/media/dvb/dvb-core/dvb_demux.h @@ -43,6 +43,8 @@ #define DVB_DEMUX_MASK_MAX 18 +#define MAX_PID 0x1fff + struct dvb_demux_filter { struct dmx_section_filter filter; u8 maskandmode[DMX_MAX_FILTER_SIZE]; @@ -128,6 +130,8 @@ struct dvb_demux { struct mutex mutex; spinlock_t lock; + + uint8_t *cnt_storage; /* for TS continuity check */ }; int dvb_dmx_init(struct dvb_demux *dvbdemux); -- cgit v1.2.3 From 9204bc59f7e30d4441efb2262a1debc8782b5b74 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 16 Jun 2009 19:02:10 -0300 Subject: dvb-net: backport upstream changes fb875333c8 and 0eade1f930 From: Mauro Carvalho Chehab kernel-sync: commit fb875333c80942455551f690f306a899ceeee5df Author: Stephen Hemminger Date: Wed Jan 7 18:02:53 2009 -0800 dvb: update network device to current API Use internal network_device_stats that exist in network device and net_device_ops. Compile tested only. commit 0eade1f930fa3ba6461b34baf3ed8fa7c4abc40f Author: Wang Chen Date: Wed Dec 3 21:13:13 2008 -0800 dvb: Kill directly reference of netdev->priv Simply replace netdev->priv with netdev_priv(). Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/dvb/dvb-core/dvb_net.c | 65 +++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) (limited to 'linux/drivers/media/dvb/dvb-core') diff --git a/linux/drivers/media/dvb/dvb-core/dvb_net.c b/linux/drivers/media/dvb/dvb-core/dvb_net.c index 5f4856c6d..1eede48a9 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_net.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_net.c @@ -126,7 +126,9 @@ static void hexdump( const unsigned char *buf, unsigned short len ) struct dvb_net_priv { int in_use; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) struct net_device_stats stats; +#endif u16 pid; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) struct net_device *net; @@ -391,8 +393,13 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) if (priv->ule_skb) { dev_kfree_skb( priv->ule_skb ); /* Prepare for next SNDU. */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) priv->stats.rx_errors++; priv->stats.rx_frame_errors++; +#else + dev->stats.rx_errors++; + dev->stats.rx_frame_errors++; +#endif } reset_ule(priv); priv->need_pusi = 1; @@ -445,8 +452,13 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) dev_kfree_skb( priv->ule_skb ); /* Prepare for next SNDU. */ // reset_ule(priv); moved to below. +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) priv->stats.rx_errors++; priv->stats.rx_frame_errors++; +#else + dev->stats.rx_errors++; + dev->stats.rx_frame_errors++; +#endif } reset_ule(priv); /* skip to next PUSI. */ @@ -467,8 +479,13 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) /* Drop partly decoded SNDU, reset state, resync on PUSI. */ if (priv->ule_skb) { dev_kfree_skb( priv->ule_skb ); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) priv->stats.rx_errors++; priv->stats.rx_frame_errors++; +#else + dev->stats.rx_errors++; + dev->stats.rx_frame_errors++; +#endif } reset_ule(priv); priv->need_pusi = 1; @@ -484,8 +501,13 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) if (priv->ule_sndu_remain > 183) { /* Current SNDU lacks more data than there could be available in the * current TS cell. */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) priv->stats.rx_errors++; priv->stats.rx_length_errors++; +#else + dev->stats.rx_errors++; + dev->stats.rx_length_errors++; +#endif printk(KERN_WARNING "%lu: Expected %d more SNDU bytes, but " "got PUSI (pf %d, ts_remain %d). Flushing incomplete payload.\n", priv->ts_count, priv->ule_sndu_remain, ts[4], ts_remain); @@ -527,8 +549,13 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) if (priv->ule_sndu_len < 5) { printk(KERN_WARNING "%lu: Invalid ULE SNDU length %u. " "Resyncing.\n", priv->ts_count, priv->ule_sndu_len); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) priv->stats.rx_errors++; priv->stats.rx_length_errors++; +#else + dev->stats.rx_errors++; + dev->stats.rx_length_errors++; +#endif priv->ule_sndu_len = 0; priv->need_pusi = 1; new_ts = 1; @@ -580,7 +607,11 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) if (priv->ule_skb == NULL) { printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) priv->stats.rx_dropped++; +#else + dev->stats.rx_dropped++; +#endif return; } @@ -653,8 +684,13 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) ule_dump = 1; #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) priv->stats.rx_errors++; priv->stats.rx_crc_errors++; +#else + dev->stats.rx_errors++; + dev->stats.rx_crc_errors++; +#endif dev_kfree_skb(priv->ule_skb); } else { /* CRC32 verified OK. */ @@ -764,8 +800,13 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) * receive the packet anyhow. */ /* if (priv->ule_dbit && skb->pkt_type == PACKET_OTHERHOST) priv->ule_skb->pkt_type = PACKET_HOST; */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) priv->stats.rx_packets++; priv->stats.rx_bytes += priv->ule_skb->len; +#else + dev->stats.rx_packets++; + dev->stats.rx_bytes += priv->ule_skb->len; +#endif netif_rx(priv->ule_skb); } sndu_done: @@ -820,8 +861,12 @@ static void dvb_net_sec(struct net_device *dev, { u8 *eth; struct sk_buff *skb; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) struct net_device_stats *stats = &((struct dvb_net_priv *) netdev_priv(dev))->stats; +#else + struct net_device_stats *stats = &dev->stats; +#endif int snap = 0; /* note: pkt_len includes a 32bit checksum */ @@ -1269,11 +1314,12 @@ static int dvb_net_stop(struct net_device *dev) return dvb_net_feed_stop(dev); } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) static struct net_device_stats * dvb_net_get_stats(struct net_device *dev) { return &((struct dvb_net_priv *) netdev_priv(dev))->stats; } - +#endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) static const struct header_ops dvb_header_ops = { .create = eth_header, @@ -1282,6 +1328,19 @@ static const struct header_ops dvb_header_ops = { }; #endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) +static const struct net_device_ops dvb_netdev_ops = { + .ndo_open = dvb_net_open, + .ndo_stop = dvb_net_stop, + .ndo_start_xmit = dvb_net_tx, + .ndo_set_multicast_list = dvb_net_set_multicast_list, + .ndo_set_mac_address = dvb_net_set_mac, + .ndo_change_mtu = eth_change_mtu, + .ndo_validate_addr = eth_validate_addr, +}; +#endif + static void dvb_net_setup(struct net_device *dev) { ether_setup(dev); @@ -1291,12 +1350,16 @@ static void dvb_net_setup(struct net_device *dev) #else dev->hard_header_cache = NULL; #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) dev->open = dvb_net_open; dev->stop = dvb_net_stop; dev->hard_start_xmit = dvb_net_tx; dev->get_stats = dvb_net_get_stats; dev->set_multicast_list = dvb_net_set_multicast_list; dev->set_mac_address = dvb_net_set_mac; +#else + dev->netdev_ops = &dvb_netdev_ops; +#endif dev->mtu = 4096; dev->mc_count = 0; -- cgit v1.2.3