[vortex] MTU setting controls ?

Matti Aarnio matti.aarnio@zmailer.org
Wed, 3 Jan 2001 15:11:41 +0200


Hi,

  I need the card to support frames of larger than normal size,
and ways to do it seem to be simple at least in case one has 3c905B
(or latter) card.  One way to do it is to supply global 'mtu'
value of e.g. 1504, and let the existing code to handle the thing.

However the way I did "ifconfig ethX mtu NNN" setting function below can
only be described as questionable - it may cause card dysfunctionality
when done in system where the interface is up (and under load?).
(But it is the live time command which is questionable, doing this
 by setting ones  ifcfg-eth0  with  MTU=1504  cames up very reliably
 with desired MTU value -- I run 802.1Q VLAN tags on the card.)

There really should be separate ways to set the actual maximum received
packet size, and the interface-MTU -- I really would not like to tell to
IP/TCP that the interface MTU is larger than 1500, so that I could receive
VLAN tagged frames at the shared physical interface.


Another issue with Vortex cards is that they don't seem to have multicast
reception filter machinery, while *some* (newer) cards have working multicast
filters, those have no need to blindly receive all multicasts!


A generic network layer statistics thing is that there apparently is no
counter for broadcasts, and multicasts and broadcasts do get folded into
same counter.   Partly this is interface problem, as we can't change the
/proc/net/dev  file format which has device counters on one long line
for each interface.  (I will raise this issue elsewere.)
I really would like to know separately the received (and sent!) broadcasts
and multicasts.

  /Matti Aarnio

 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- Changes done to 3c59x.c driver (my test machine has these)
  are "a bit" shaky -- the  change_mtu()  function clearly needs
  to poke at the card at selected moments, but it is *VERY*
  timing sensitive!  Doing it at system startup with present
  code may get the system up just fine, or it may start spewing
  lots and lots of network card diagnostics to the console.
  (Doing   "ifconfig vlan0123 mtu 3000"  at active traffic
   interface is presently a surefire way to cause spewage..
   Ball to driver specialists.)

- There is   vortex_set_mtu()  -- actual register poking is questionable
  code in live system

- Packet allocations are done with configured size variable, not with
  magic #define:d constant.


diff -u -r linux-2400t10vl14/drivers/net/3c59x.c linux-2400t10vl14m/drivers/net/3c59x.c
--- linux-2400t10vl14/drivers/net/3c59x.c	Mon Oct 16 22:58:51 2000
+++ linux-2400t10vl14m/drivers/net/3c59x.c	Sat Nov  4 00:33:32 2000
@@ -663,6 +663,7 @@
 		has_nway:1,
 		open:1,
 		must_free_region:1;				/* Flag: if zero, Cardbus owns the I/O region */
+	int pkt_buf_sz;						/* MTU + 36 ??? (MAX_HEADER ?), plus alignment */
 	int drv_flags;
 	u16 status_enable;
 	u16 intr_enable;
@@ -729,6 +730,7 @@
 static int vortex_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
 static void vortex_tx_timeout(struct net_device *dev);
 static void acpi_set_WOL(struct net_device *dev);
+static void wait_for_completion(struct net_device *, int);
 
 /* This driver uses 'options' to pass the media type, full-duplex flag, etc. */
 /* Option count limit only -- unlimited interfaces are supported. */
@@ -777,6 +779,54 @@
 	}
 }
 
+static int vortex_set_mtu(struct net_device *dev, int mtu)
+{
+	struct vortex_private *vp = dev->priv;
+	unsigned int old_mtu = dev->mtu;
+
+	if (mtu < 68 || mtu > 4000)
+		return -EINVAL; /* FIXME: MODEL SPECIFIC LIMITATIONS ??? */
+
+	dev->mtu = mtu;
+	vp->pkt_buf_sz = mtu + 36; /* FIXME: What is this MAGIC 36 ??? */
+
+	/* Size transition under or above the magic 1500 byte marker. */
+
+	if ((old_mtu <= 1500 && mtu > 1500) ||
+		(old_mtu > 1500 && mtu <= 1500)) {
+
+		/* FIXME: IS THIS RACE SAFE ? DEADLOCK SAFE ?  */
+
+		unsigned long flags;
+		long ioaddr = dev->base_addr;
+		unsigned int config;
+
+		local_irq_save(flags);
+
+		EL3WINDOW(3);
+
+		/* Set the full-duplex bit. */
+		config = (((vp->info1 & 0x8000) || vp->full_duplex ? 0x20 : 0) |
+				  ((dev->mtu > 1500) ? 0x40 : 0) |
+				  ((vp->full_duplex && vp->flow_ctrl &&
+					vp->partner_flow_ctrl) ? 0x100 : 0));
+		outw(config, ioaddr + Wn3_MAC_Ctrl);
+
+		wait_for_completion(dev, TxReset);
+		wait_for_completion(dev, RxReset);
+
+		outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
+
+		local_irq_restore(flags);
+
+		if (vortex_debug > 0)
+			printk(KERN_DEBUG "%s: vortex_set_mtu() New mtu=%d Wn3MACCtrl=%4.4x.\n",
+				   dev->name, mtu, config);
+	}
+	return mtu;
+}
+
+
 /* returns count found (>= 0), or negative on error */
 static int __init vortex_eisa_init (void)
 {
@@ -881,6 +931,7 @@
 	dev->base_addr = ioaddr;
 	dev->irq = irq;
 	dev->mtu = mtu;
+	vp->pkt_buf_sz = mtu + 36; /* FIXME: What is this MAGIC 36 ??? */
 	vp->drv_flags = vci->drv_flags;
 	vp->has_nway = (vci->drv_flags & HAS_NWAY) ? 1 : 0;
 	vp->io_size = vci->io_size;
@@ -1109,9 +1160,10 @@
 	dev->hard_start_xmit = vp->full_bus_master_tx ?
 					boomerang_start_xmit : vortex_start_xmit;
 	dev->stop = vortex_close;
-	dev->get_stats = vortex_get_stats;
-	dev->do_ioctl = vortex_ioctl;
+	dev->get_stats  = vortex_get_stats;
+	dev->do_ioctl   = vortex_ioctl;
 	dev->set_multicast_list = set_rx_mode;
+	dev->change_mtu = vortex_set_mtu;
 	dev->tx_timeout = vortex_tx_timeout;
 	dev->watchdog_timeo = (watchdog * HZ) / 1000;
 
@@ -1227,7 +1279,7 @@
 
 	/* Set the full-duplex bit. */
 	outw(	((vp->info1 & 0x8000) || vp->full_duplex ? 0x20 : 0) |
-		 	(dev->mtu > 1500 ? 0x40 : 0) |
+			((dev->mtu > 1500) ? 0x40 : 0) |
 			((vp->full_duplex && vp->flow_ctrl && vp->partner_flow_ctrl) ? 0x100 : 0),
 			ioaddr + Wn3_MAC_Ctrl);
 
@@ -1298,7 +1350,7 @@
 	if (vp->full_bus_master_tx) { 		/* Boomerang bus master Tx. */
 		vp->cur_tx = vp->dirty_tx = 0;
 		if (vp->drv_flags & IS_BOOMERANG)
-			outb(PKT_BUF_SZ>>8, ioaddr + TxFreeThreshold); /* Room for a packet. */
+			outb(vp->pkt_buf_sz>>8, ioaddr + TxFreeThreshold); /* Room for a packet. */
 		/* Clear the Rx, Tx rings. */
 		for (i = 0; i < RX_RING_SIZE; i++)	/* AKPM: this is done in vortex_open, too */
 			vp->rx_ring[i].status = 0;
@@ -1354,14 +1406,14 @@
 			struct sk_buff *skb;
 			vp->rx_ring[i].next = cpu_to_le32(vp->rx_ring_dma + sizeof(struct boom_rx_desc) * (i+1));
 			vp->rx_ring[i].status = 0;	/* Clear complete bit. */
-			vp->rx_ring[i].length = cpu_to_le32(PKT_BUF_SZ | LAST_FRAG);
-			skb = dev_alloc_skb(PKT_BUF_SZ);
+			vp->rx_ring[i].length = cpu_to_le32(vp->pkt_buf_sz | LAST_FRAG);
+			skb = dev_alloc_skb(vp->pkt_buf_sz);
 			vp->rx_skbuff[i] = skb;
 			if (skb == NULL)
 				break;			/* Bad news!  */
 			skb->dev = dev;			/* Mark as being used by this device. */
 			skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
-			vp->rx_ring[i].addr = cpu_to_le32(pci_map_single(vp->pdev, skb->tail, PKT_BUF_SZ, PCI_DMA_FROMDEVICE));
+			vp->rx_ring[i].addr = cpu_to_le32(pci_map_single(vp->pdev, skb->tail, vp->pkt_buf_sz, PCI_DMA_FROMDEVICE));
 		}
 		if (i != RX_RING_SIZE) {
 			int j;
@@ -1443,12 +1495,15 @@
 						/* Set the full-duplex bit. */
 						EL3WINDOW(3);	/* AKPM: this was missing from 2.3.99 3c59x.c! */
 						outw(	(vp->full_duplex ? 0x20 : 0) |
-								(dev->mtu > 1500 ? 0x40 : 0) |
+								((dev->mtu > 1500) ? 0x40 : 0) |
 								((vp->full_duplex && vp->flow_ctrl && vp->partner_flow_ctrl) ? 0x100 : 0),
 								ioaddr + Wn3_MAC_Ctrl);
 						if (vortex_debug > 1)
 							printk(KERN_DEBUG "Setting duplex in Wn3_MAC_Ctrl\n");
 						/* AKPM: bug: should reset Tx and Rx after setting Duplex.  Page 180 */
+
+						wait_for_completion(dev, TxReset);
+						wait_for_completion(dev, RxReset);
 					}
 				}
 			}
@@ -1558,7 +1613,7 @@
 		if (vp->tx_full)
 			netif_stop_queue (dev);
 		if (vp->drv_flags & IS_BOOMERANG)
-			outb(PKT_BUF_SZ>>8, ioaddr + TxFreeThreshold);
+			outb(vp->pkt_buf_sz>>8, ioaddr + TxFreeThreshold);
 		outw(DownUnstall, ioaddr + EL3_CMD);
 	} else {
 		vp->stats.tx_dropped++;
@@ -2053,10 +2108,19 @@
 						 (pkt_len + 3) >> 2);
 				}
 				outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
+
+				if (skb->data[0] & 1) {
+					/* Destination MAC address has at its first octet
+					   the lowest bit set -> multicast or broadcast.. */
+					if (memcmp(skb->data, dev->broadcast, ETH_ALEN) != 0)
+						/* Multicast! */
+						vp->stats.multicast++;
+				}
+
 				skb->protocol = eth_type_trans(skb, dev);
-				netif_rx(skb);
 				dev->last_rx = jiffies;
 				vp->stats.rx_packets++;
+				netif_rx(skb);
 				/* Wait a limited time to go to next packet. */
 				for (i = 200; i >= 0; i--)
 					if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
@@ -2114,7 +2178,7 @@
 			if (pkt_len < rx_copybreak && (skb = dev_alloc_skb(pkt_len + 2)) != 0) {
 				skb->dev = dev;
 				skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
-				pci_dma_sync_single(vp->pdev, dma, PKT_BUF_SZ, PCI_DMA_FROMDEVICE);
+				pci_dma_sync_single(vp->pdev, dma, vp->pkt_buf_sz, PCI_DMA_FROMDEVICE);
 				/* 'skb_put()' points to the start of sk_buff data area. */
 				memcpy(skb_put(skb, pkt_len),
 					   vp->rx_skbuff[entry]->tail,
@@ -2125,9 +2189,18 @@
 				skb = vp->rx_skbuff[entry];
 				vp->rx_skbuff[entry] = NULL;
 				skb_put(skb, pkt_len);
-				pci_unmap_single(vp->pdev, dma, PKT_BUF_SZ, PCI_DMA_FROMDEVICE);
+				pci_unmap_single(vp->pdev, dma, vp->pkt_buf_sz, PCI_DMA_FROMDEVICE);
 				rx_nocopy++;
 			}
+
+			if (skb->data[0] & 1) {
+				/* Destination MAC address has at its first octet
+				   the lowest bit set -> multicast or broadcast.. */
+				if (memcmp(skb->data, dev->broadcast, ETH_ALEN) != 0)
+					/* Multicast! */
+					vp->stats.multicast++;
+			}
+
 			skb->protocol = eth_type_trans(skb, dev);
 			{					/* Use hardware checksum info. */
 				int csum_bits = rx_status & 0xee000000;
@@ -2138,9 +2211,9 @@
 					rx_csumhits++;
 				}
 			}
-			netif_rx(skb);
 			dev->last_rx = jiffies;
 			vp->stats.rx_packets++;
+			netif_rx(skb);
 		}
 		entry = (++vp->cur_rx) % RX_RING_SIZE;
 	}
@@ -2149,7 +2222,7 @@
 		struct sk_buff *skb;
 		entry = vp->dirty_rx % RX_RING_SIZE;
 		if (vp->rx_skbuff[entry] == NULL) {
-			skb = dev_alloc_skb(PKT_BUF_SZ);
+			skb = dev_alloc_skb(vp->pkt_buf_sz);
 			if (skb == NULL) {
 				static unsigned long last_jif;
 				if ((jiffies - last_jif) > 10 * HZ) {
@@ -2162,7 +2235,7 @@
 			}
 			skb->dev = dev;			/* Mark as being used by this device. */
 			skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
-			vp->rx_ring[entry].addr = cpu_to_le32(pci_map_single(vp->pdev, skb->tail, PKT_BUF_SZ, PCI_DMA_FROMDEVICE));
+			vp->rx_ring[entry].addr = cpu_to_le32(pci_map_single(vp->pdev, skb->tail, vp->pkt_buf_sz, PCI_DMA_FROMDEVICE));
 			vp->rx_skbuff[entry] = skb;
 		}
 		vp->rx_ring[entry].status = 0;	/* Clear complete bit. */
@@ -2249,7 +2322,7 @@
 		for (i = 0; i < RX_RING_SIZE; i++)
 			if (vp->rx_skbuff[i]) {
 				pci_unmap_single(	vp->pdev, le32_to_cpu(vp->rx_ring[i].addr),
-									PKT_BUF_SZ, PCI_DMA_FROMDEVICE);
+									vp->pkt_buf_sz, PCI_DMA_FROMDEVICE);
 				dev_kfree_skb(vp->rx_skbuff[i]);
 				vp->rx_skbuff[i] = 0;
 			}
@@ -2402,16 +2475,23 @@
 static void set_rx_mode(struct net_device *dev)
 {
 	long ioaddr = dev->base_addr;
-	int new_mode;
+	int new_mode = SetRxFilter | RxStation;
+
+	if (dev->flags & IFF_BROADCAST)
+		new_mode |= RxBroadcast;
 
 	if (dev->flags & IFF_PROMISC) {
 		if (vortex_debug > 0)
-			printk(KERN_NOTICE "%s: Setting promiscuous mode.\n", dev->name);
-		new_mode = SetRxFilter|RxStation|RxMulticast|RxBroadcast|RxProm;
-	} else	if ((dev->mc_list)  ||  (dev->flags & IFF_ALLMULTI)) {
-		new_mode = SetRxFilter|RxStation|RxMulticast|RxBroadcast;
-	} else
-		new_mode = SetRxFilter | RxStation | RxBroadcast;
+			printk(KERN_NOTICE "%s: Setting promiscuous mode; cnt=%d\n", dev->name, dev->promiscuity);
+		new_mode |= RxProm;
+	}
+
+	/* FIXME: Some of the 3c905 family cards actually have a WORKING
+			  multicast reception filter, take it into use! */
+
+	if ((dev->mc_list)  ||  (dev->flags & IFF_ALLMULTI)) {
+		new_mode |= RxMulticast;
+	}
 
 	outw(new_mode, ioaddr + EL3_CMD);
 }