[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