[linux-dvb] [PATCH] dvb_net - Fix oops when stopping multiple dvb
interfaces
Holger Kiehl
Holger.Kiehl at dwd.de
Fri Nov 4 11:17:06 CET 2005
Hello
This patch is from Ralph Metzler <rjkm at metzlerbros.de> (see discussion under
the thread: [Patch] Error in dvb_net.c on 64bit platforms) and has not made
it into CVS nor into linux-kernel. It fixes a serious issue when removing
multiple dvb network interfaces at once and the card does have hardware
PID filters. These systems cannot be rebooted because they always hang
when kernel or some scripts want to remove the interfaces. With this patch
applied systems reboot without problems and there are no more oopses.
Please, please apply this patch.
Thanks,
Holger
--
--- dvb-kernel/linux/drivers/media/dvb/dvb-core/dvb_net.c.orig 2005-08-12 17:37:59.000000000 +0200
+++ dvb-kernel/linux/drivers/media/dvb/dvb-core/dvb_net.c 2005-11-04 10:55:21.000000000 +0100
@@ -62,6 +62,7 @@
#include <linux/uio.h>
#include <asm/uaccess.h>
#include <linux/crc32.h>
+#include <linux/version.h>
#include "dvb_demux.h"
#include "dvb_net.h"
@@ -151,6 +152,9 @@
unsigned char ule_bridged; /* Whether the ULE_BRIDGED extension header was found. */
int ule_sndu_remain; /* Nr. of bytes still required for current ULE SNDU. */
unsigned long ts_count; /* Current ts cell counter. */
+
+ struct semaphore mutex;
+ spinlock_t lock;
};
@@ -170,7 +174,11 @@
skb->mac.raw=skb->data;
skb_pull(skb,dev->hard_header_len);
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,8)
+ eth = skb->mac.ethernet;
+#else
eth = eth_hdr(skb);
+#endif
if (*eth->h_dest & 1) {
if(memcmp(eth->h_dest,dev->broadcast, ETH_ALEN)==0)
@@ -881,12 +889,13 @@
static int dvb_net_feed_start(struct net_device *dev)
{
- int ret, i;
+ int ret=0, i;
struct dvb_net_priv *priv = dev->priv;
struct dmx_demux *demux = priv->demux;
unsigned char *mac = (unsigned char *) dev->dev_addr;
dprintk("%s: rx_mode %i\n", __FUNCTION__, priv->rx_mode);
+ down(&priv->mutex);
if (priv->tsfeed || priv->secfeed || priv->secfilter || priv->multi_secfilter[0])
printk("%s: BUG %d\n", __FUNCTION__, __LINE__);
@@ -900,7 +909,7 @@
dvb_net_sec_callback);
if (ret<0) {
printk("%s: could not allocate section feed\n", dev->name);
- return ret;
+ goto error;
}
ret = priv->secfeed->set(priv->secfeed, priv->pid, 32768, 1);
@@ -909,7 +918,7 @@
printk("%s: could not set section feed\n", dev->name);
priv->demux->release_section_feed(priv->demux, priv->secfeed);
priv->secfeed=NULL;
- return ret;
+ goto error;
}
if (priv->rx_mode != RX_MODE_PROMISC) {
@@ -948,7 +957,7 @@
ret = demux->allocate_ts_feed(demux, &priv->tsfeed, dvb_net_ts_callback);
if (ret < 0) {
printk("%s: could not allocate ts feed\n", dev->name);
- return ret;
+ goto error;
}
/* Set netdevice pointer for ts decaps callback. */
@@ -962,23 +971,26 @@
printk("%s: could not set ts feed\n", dev->name);
priv->demux->release_ts_feed(priv->demux, priv->tsfeed);
priv->tsfeed = NULL;
- return ret;
+ goto error;
}
dprintk("%s: start filtering\n", __FUNCTION__);
priv->tsfeed->start_filtering(priv->tsfeed);
} else
- return -EINVAL;
+ ret=-EINVAL;
- return 0;
+error:
+ up(&priv->mutex);
+ return ret;
}
static int dvb_net_feed_stop(struct net_device *dev)
{
struct dvb_net_priv *priv = dev->priv;
- int i;
+ int i, ret=0;
dprintk("%s\n", __FUNCTION__);
+ down(&priv->mutex);
if (priv->feedtype == DVB_NET_FEEDTYPE_MPE) {
if (priv->secfeed) {
if (priv->secfeed->is_filtering) {
@@ -1019,8 +1031,9 @@
else
printk("%s: no ts feed to stop\n", dev->name);
} else
- return -EINVAL;
- return 0;
+ ret=-EINVAL;
+ up(&priv->mutex);
+ return ret;
}
@@ -1047,6 +1060,7 @@
priv->rx_mode = RX_MODE_UNI;
+ spin_lock_bh(&dev->xmit_lock);
if (dev->flags & IFF_PROMISC) {
dprintk("%s: promiscuous mode\n", dev->name);
priv->rx_mode = RX_MODE_PROMISC;
@@ -1069,7 +1083,7 @@
dvb_set_mc_filter(dev, mc);
}
}
-
+ spin_unlock_bh(&dev->xmit_lock);
dvb_net_feed_start(dev);
}
@@ -1183,7 +1197,6 @@
/* compatibility fix to keep dvb0_0 format */
snprintf(net->name, IFNAMSIZ, "dvb%d_%d",
dvbnet->dvbdev->adapter->num, if_num);
-
net->addr_len = 6;
memcpy(net->dev_addr, dvbnet->dvbdev->adapter->proposed_mac, 6);
@@ -1200,6 +1213,8 @@
INIT_WORK(&priv->set_multicast_list_wq, wq_set_multicast_list, net);
INIT_WORK(&priv->restart_net_feed_wq, wq_restart_net_feed, net);
+ sema_init(&priv->mutex, 1);
+ spin_lock_init(&priv->lock);
net->base_addr = pid;
More information about the linux-dvb
mailing list