NAPI for eepro100

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

 



hi,all
   Recently i've converted eepro100 driver to use napi,in order to improve
network performance of my poor 150M mips machine. It does eliminate
the interrupt live lock seen before,maintaining a peak throughput under
heavy load.
  In case anybody are interested,i post the patches to the list. They are
3 incremental patchs:
   eepro100-napi.patch is against 2.5.20 eepro100.c and provide basic
napi support
   eepro100-proc.patch is proc file system support adapted from intel's
e100 driver. I am using it for debugging.
   eepro100-mips.patch is mips specific patch to make it work(well) for 
my mips
platform.
(i suppose people use: patch eepro100.c < x.patch to apply patch)

   Tests are mainly done on my mips machine for 2.4 kernel,though i think
 it should work for at least x86 on which minimal test is performed. Be 
careful.

   A little pitty is that to achieve good performance under heavy load, the
/proc/sys/net/core/netdev_max_backlog value may need to be adjusted.
   
  Feedbacks are always welcome:). But i am not on linux-kernel list,so 
people
there please CC me.
--- eepro100-napi-proc.c	Wed Jun 12 17:33:51 2002
+++ eepro100-mips.c	Wed Jun 12 19:22:29 2002
@@ -45,7 +45,7 @@
 
 /* Set the copy breakpoint for the copy-only-tiny-buffer Rx method.
    Lower values use more memory, but are faster. */
-#if defined(__alpha__) || defined(__sparc__) || defined(__mips__) || \
+#if defined(__alpha__) || defined(__sparc__) || /*defined(__mips__) ||*/ \
     defined(__arm__)
 static int rx_copybreak = 1518;
 #else
@@ -66,8 +66,8 @@
 
 /* A few values that may be tweaked. */
 /* The ring sizes should be a power of two for efficiency. */
-#define TX_RING_SIZE	64
-#define RX_RING_SIZE	64
+#define TX_RING_SIZE	32
+#define RX_RING_SIZE	32
 /* How much slots multicast filter setup may take.
    Do not descrease without changing set_rx_mode() implementaion. */
 #define TX_MULTICAST_SIZE   2
@@ -1298,7 +1298,14 @@
 		if (skb == NULL)
 			break;			/* OK.  Just initially short of Rx bufs. */
 		skb->dev = dev;			/* Mark as being used by this device. */
+#ifndef __mips__
 		rxf = (struct RxFD *)skb->tail;
+#else
+		/* use uncached address,use pci_dma_sync_xx seems 
+		 * problematic in my mips platform
+		 */
+		rxf = (struct RxFD *)(KSEG1ADDR(skb->tail));
+#endif
 		sp->rx_ringp[i] = rxf;
 		sp->rx_ring_dma[i] =
 			pci_map_single(sp->pdev, rxf,
@@ -1306,8 +1313,10 @@
 		skb_reserve(skb, sizeof(struct RxFD));
 		if (last_rxf) {
 			last_rxf->link = cpu_to_le32(sp->rx_ring_dma[i]);
+#ifndef __mips__
 			pci_dma_sync_single(sp->pdev, last_rxf_dma,
 					sizeof(struct RxFD), PCI_DMA_TODEVICE);
+#endif
 		}
 		last_rxf = rxf;
 		last_rxf_dma = sp->rx_ring_dma[i];
@@ -1316,14 +1325,18 @@
 		/* This field unused by i82557. */
 		rxf->rx_buf_addr = 0xffffffff;
 		rxf->count = cpu_to_le32(PKT_BUF_SZ << 16);
+#ifndef __mips__
 		pci_dma_sync_single(sp->pdev, sp->rx_ring_dma[i],
 				sizeof(struct RxFD), PCI_DMA_TODEVICE);
+#endif
 	}
 	sp->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
 	/* Mark the last entry as end-of-list. */
 	last_rxf->status = cpu_to_le32(0xC0000002);	/* '2' is flag value only. */
+#ifndef __mips__
 	pci_dma_sync_single(sp->pdev, sp->rx_ring_dma[RX_RING_SIZE-1],
 			sizeof(struct RxFD), PCI_DMA_TODEVICE);
+#endif
 	sp->last_rxf = last_rxf;
 	sp->last_rxf_dma = last_rxf_dma;
 }
@@ -1733,15 +1746,21 @@
 #endif
 		return NULL;
 	}
+#ifndef __mips__
 	rxf = sp->rx_ringp[entry] = (struct RxFD *)skb->tail;
+#else
+	rxf = sp->rx_ringp[entry] = (struct RxFD *)(KSEG1ADDR(skb->tail));
+#endif
 	sp->rx_ring_dma[entry] =
 		pci_map_single(sp->pdev, rxf,
 					   PKT_BUF_SZ + sizeof(struct RxFD), PCI_DMA_FROMDEVICE);
 	skb->dev = dev;
 	skb_reserve(skb, sizeof(struct RxFD));
 	rxf->rx_buf_addr = 0xffffffff;
+#ifndef __mips__
 	pci_dma_sync_single(sp->pdev, sp->rx_ring_dma[entry],
 			sizeof(struct RxFD), PCI_DMA_TODEVICE);
+#endif
 	return rxf;
 }
 
@@ -1754,8 +1773,10 @@
 	rxf->count = cpu_to_le32(PKT_BUF_SZ << 16);
 	sp->last_rxf->link = cpu_to_le32(rxf_dma);
 	sp->last_rxf->status &= cpu_to_le32(~0xC0000000);
+#ifndef __mips__
 	pci_dma_sync_single(sp->pdev, sp->last_rxf_dma,
 			sizeof(struct RxFD), PCI_DMA_TODEVICE);
+#endif
 	sp->last_rxf = rxf;
 	sp->last_rxf_dma = rxf_dma;
 }
@@ -2274,8 +2295,10 @@
 		int status;
 		int pkt_len;
 
+#ifndef __mips__
 		pci_dma_sync_single(sp->pdev, sp->rx_ring_dma[entry],
 			sizeof(struct RxFD), PCI_DMA_FROMDEVICE);
+#endif
 		status = le32_to_cpu(sp->rx_ringp[entry]->status);
 		pkt_len = le32_to_cpu(sp->rx_ringp[entry]->count) & 0x3fff;
 
--- eepro100.c.ori	Wed Jun 12 17:25:28 2002
+++ eepro100-napi.c	Wed Jun 12 17:11:38 2002
@@ -25,6 +25,8 @@
 		Disabled FC and ER, to avoid lockups when when we get FCP interrupts.
 	2000 Jul 17 Goutham Rao <goutham.rao@intel.com>
 		PCI DMA API fixes, adding pci_dma_sync_single calls where neccesary
+	2002 Jun 12 Zhang Fuxin <fxzhang@ict.ac.cn>
+		add NAPI support
 */
 
 static const char *version =
@@ -115,6 +117,8 @@
 #include <linux/skbuff.h>
 #include <linux/ethtool.h>
 
+#define CONFIG_EEPRO100_NAPI
+
 MODULE_AUTHOR("Maintainer: Andrey V. Savochkin <saw@saw.sw.com.sg>");
 MODULE_DESCRIPTION("Intel i82557/i82558/i82559 PCI EtherExpressPro driver");
 MODULE_LICENSE("GPL");
@@ -494,8 +498,34 @@
 #ifdef CONFIG_PM
 	u32 pm_state[16];
 #endif
+
+	/* added by zfx for NAPI*/
+#ifdef CONFIG_EEPRO100_NAPI
+
+	/* used to pass rx_work_limit into speedo_rx,i don't want to 
+	 * change its prototype
+	 */
+	int curr_work_limit;
+	unsigned long poll_switch;
+	unsigned long failed_poll_switch;
+	unsigned long done_poll;
+	unsigned long notdone_poll;
+	unsigned long empty_poll;
+	unsigned long soft_reset_count;
+	unsigned long rx_resume_count;
+	unsigned long alloc_fail;
+	unsigned long long poll_cycles;
+
+#ifdef CONFIG_NET_FASTROUTE
+	unsigned long fastroute_hit;
+	unsigned long fastroute_success;
+	unsigned long fastroute_defer;
+#endif
+
+#endif
 };
 
+
 /* The parameters for a CmdConfigure operation.
    There are so many options that it would be difficult to document each bit.
    We mostly use the default or recommended settings. */
@@ -546,6 +576,14 @@
 static void set_rx_mode(struct net_device *dev);
 static void speedo_show_state(struct net_device *dev);
 
+#ifdef CONFIG_EEPRO100_NAPI
+
+static int speedo_poll (struct net_device *dev, int *budget);
+static void enable_rx_and_rxnobuf_ints(struct net_device *dev);
+static void disable_rx_and_rxnobuf_ints(struct net_device *dev);
+
+#endif
+
 
 
 #ifdef honor_default_port
@@ -842,6 +880,10 @@
 	dev->set_multicast_list = &set_rx_mode;
 	dev->do_ioctl = &speedo_ioctl;
 
+#ifdef CONFIG_EEPRO100_NAPI
+	dev->poll = speedo_poll;
+	dev->quota = dev->weight = RX_RING_SIZE;
+#endif
 	return 0;
 }
 
@@ -1517,6 +1559,9 @@
 	struct speedo_private *sp;
 	long ioaddr, boguscnt = max_interrupt_work;
 	unsigned short status;
+#ifdef CONFIG_EEPRO100_NAPI
+	int first = 1;
+#endif
 
 #ifndef final_version
 	if (dev == NULL) {
@@ -1543,16 +1588,21 @@
 		/* Acknowledge all of the current interrupt sources ASAP. */
 		/* Will change from 0xfc00 to 0xff00 when we start handling
 		   FCP and ER interrupts --Dragan */
+#ifndef CONFIG_EEPRO100_NAPI
 		outw(status & 0xfc00, ioaddr + SCBStatus);
+#else
+		/* Rx & RxNoBuf is acked in speedo_poll */
+		outw(status & 0xac00, ioaddr + SCBStatus);
+#endif
 
 		if (speedo_debug > 4)
 			printk(KERN_DEBUG "%s: interrupt  status=%#4.4x.\n",
 				   dev->name, status);
 
+#ifndef CONFIG_EEPRO100_NAPI
 		if ((status & 0xfc00) == 0)
 			break;
 
-
 		if ((status & 0x5000) ||	/* Packet received, or Rx error. */
 			(sp->rx_ring_state&(RrNoMem|RrPostponed)) == RrPostponed)
 									/* Need to gather the postponed packet. */
@@ -1560,8 +1610,33 @@
 
 		/* Always check if all rx buffers are allocated.  --SAW */
 		speedo_refill_rx_buffers(dev, 0);
+#else
+		/* Packet received, or Rx error. */
+		if (first && ((status & 0x5000) || (sp->rx_ring_state&(RrNoMem|RrPostponed)) == RrPostponed || (status & 0x3c) != 0x10 ))
+			/* Need to gather the postponed packet. */
+		{
+			if (speedo_debug > 4) 
+				printk("switching to poll,status=%x\n",status);
+			first = 0;
+			if (netif_rx_schedule_prep(dev)) {
+				sp->poll_switch++;
+				/* disable interrupts caused by arriving packets */
+				disable_rx_and_rxnobuf_ints(dev);
+				/* tell system we have work to be done. */
+				__netif_rx_schedule(dev);
+			}else {
+				sp->failed_poll_switch++;
+			}
+
+		}
+
+		if ((status & 0xac00) == 0)
+			break;
+#endif
 		
 		spin_lock(&sp->lock);
+
+#ifndef CONFIG_EEPRO100_NAPI
 		/*
 		 * The chip may have suspended reception for various reasons.
 		 * Check for that, and re-prime it should this be the case.
@@ -1581,7 +1656,7 @@
 			/* these are all reserved values */
 			break;
 		}
-		
+#endif
 		
 		/* User interrupt, Command/Tx unit interrupt or CU not active. */
 		if (status & 0xA400) {
@@ -1602,7 +1677,12 @@
 			/* Clear all interrupt sources. */
 			/* Will change from 0xfc00 to 0xff00 when we start handling
 			   FCP and ER interrupts --Dragan */
+#ifndef CONFIG_EEPRO100_NAPI
 			outw(0xfc00, ioaddr + SCBStatus);
+#else
+			outw(0xac00, ioaddr + SCBStatus);
+#endif
+
 			break;
 		}
 	} while (1);
@@ -1611,7 +1691,9 @@
 		printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n",
 			   dev->name, inw(ioaddr + SCBStatus));
 
+#ifndef final_version
 	clear_bit(0, (void*)&sp->in_interrupt);
+#endif
 	return;
 }
 
@@ -1625,6 +1707,9 @@
 	sp->rx_skbuff[entry] = skb;
 	if (skb == NULL) {
 		sp->rx_ringp[entry] = NULL;
+#ifdef CONFIG_EEPRO100_NAPI
+		sp->alloc_fail++;
+#endif
 		return NULL;
 	}
 	rxf = sp->rx_ringp[entry] = (struct RxFD *)skb->tail;
@@ -1705,12 +1790,112 @@
 			speedo_refill_rx_buf(dev, force) != -1);
 }
 
+#ifdef CONFIG_EEPRO100_NAPI
+static void enable_rx_and_rxnobuf_ints(struct net_device *dev)
+{
+	long ioaddr = dev->base_addr;
+	outw(SCBMaskEarlyRx | SCBMaskFlowCtl, ioaddr + SCBCmd);
+	inw(ioaddr + SCBStatus); /* flushes last write, read-safe */
+};
+
+static void disable_rx_and_rxnobuf_ints(struct net_device *dev)
+{
+	long ioaddr = dev->base_addr;
+	outw(SCBMaskRxDone | SCBMaskRxSuspend | SCBMaskEarlyRx | SCBMaskFlowCtl, ioaddr + SCBCmd);
+	inw(ioaddr + SCBStatus); /* flushes last write, read-safe */
+};
+
+static int speedo_poll (struct net_device *dev, int *budget)
+{
+	struct speedo_private *sp = (struct speedo_private *)dev->priv;
+	long ioaddr, received = 0;
+	unsigned short intr_status;
+
+	ioaddr = dev->base_addr;
+	intr_status = inw(ioaddr + SCBStatus);
+
+	if (speedo_debug > 4)
+		printk(KERN_DEBUG " In speedo_poll().\n");
+
+	sp->curr_work_limit = *budget;
+	if (sp->curr_work_limit > dev->quota) 
+		sp->curr_work_limit = dev->quota;
+
+	do {  
+		/* ack Rx & RxNobuf intrs*/
+		outw(intr_status & 0x5000, ioaddr + SCBStatus);
+
+		received += speedo_rx(dev);
+
+		if (sp->curr_work_limit < 0) /* out of quota */
+			goto not_done;
+
+		/* no packets on ring; but new ones can arrive since we last checked  */
+		intr_status = inw(ioaddr + SCBStatus);
+
+		if ((intr_status & 0x5000) == 0) {
+			/* If something arrives in this narrow window,an interrupt 
+			 * will be generated 
+			 */
+			goto done;
+		}
+		/* done! at least thats what it looks like ;->
+		   if new packets came in after our last check on status bits
+		   they'll be caught by the while check and we go back and clear them
+		   since we havent exceeded our quota 
+		 */
+	} while (intr_status & 0x5000);
+
+done:
+	if (!received) {
+		if (speedo_debug > 4) printk("received==0\n");
+		received = 1;
+		sp->empty_poll++;
+	}
+	dev->quota -= received;
+	*budget -= received;
+
+	/* we are happy/done, no more packets on ring; put us back
+	 * to where we can start processing interrupts again 
+	 */
+	netif_rx_complete(dev);
+	enable_rx_and_rxnobuf_ints(dev);
+
+	sp->done_poll++;
+
+	if (speedo_debug > 3)
+		printk("done,received=%lu\n",received);
+
+        return 0;   /* done */
+
+not_done:
+	if (!received) {
+		if (speedo_debug > 4) printk("received==0\n");
+		received = 1;
+		sp->empty_poll++;
+	}
+	dev->quota -= received;
+	*budget -= received;
+
+	sp->notdone_poll++;
+
+	if (speedo_debug > 3)
+		printk("not done,received=%lu\n",received);
+
+	return 1;  /* not_done */
+}
+
+#endif /* NAPI */
+
 static int
 speedo_rx(struct net_device *dev)
 {
 	struct speedo_private *sp = (struct speedo_private *)dev->priv;
 	int entry = sp->cur_rx % RX_RING_SIZE;
+#ifndef CONFIG_EEPRO100_NAPI
 	int rx_work_limit = sp->dirty_rx + RX_RING_SIZE - sp->cur_rx;
+#endif
+	int received = 0;
 	int alloc_ok = 1;
 
 	if (speedo_debug > 4)
@@ -1725,11 +1910,42 @@
 		status = le32_to_cpu(sp->rx_ringp[entry]->status);
 		pkt_len = le32_to_cpu(sp->rx_ringp[entry]->count) & 0x3fff;
 
+#ifndef CONFIG_EEPRO100_NAPI
 		if (!(status & RxComplete))
 			break;
 
 		if (--rx_work_limit < 0)
 			break;
+#else
+		if (!(status & RxComplete)) {
+			int intr_status;
+			unsigned long ioaddr = dev->base_addr;
+
+			intr_status = inw(ioaddr + SCBStatus);
+			/* We check receiver state here because if 
+			 * we have to do soft reset,sp->cur_rx should
+			 * point to an empty entry or something 
+			 * unexpected will happen
+			 */
+			if (intr_status | 0x1000) { /* suspended */
+				outw(0x5000,ioaddr + SCBStatus);
+				/* No resources */
+				if ((intr_status & 0x3c) == 0x28) {
+					outw(RxResumeNoResources,ioaddr+SCBCmd);
+					sp->rx_resume_count++;
+				}else if ((intr_status & 0x3c) == 0x8) {
+					if (speedo_debug > 4) 
+						printk("No resource,reset\n");
+					speedo_rx_soft_reset(dev);
+					sp->soft_reset_count++;
+				}
+			}
+			break;
+		}
+
+		if (--sp->curr_work_limit < 0) 
+			break;
+#endif
 
 		/* Check for a rare out-of-memory case: the current buffer is
 		   the last buffer allocated in the RX ring.  --SAW */
@@ -1793,7 +2009,12 @@
 						PKT_BUF_SZ + sizeof(struct RxFD), PCI_DMA_FROMDEVICE);
 			}
 			skb->protocol = eth_type_trans(skb, dev);
+#ifndef CONFIG_EEPRO100_NAPI
 			netif_rx(skb);
+#else
+			netif_receive_skb(skb);
+			received ++;
+#endif
 			dev->last_rx = jiffies;
 			sp->stats.rx_packets++;
 			sp->stats.rx_bytes += pkt_len;
@@ -1811,7 +2032,7 @@
 
 	sp->last_rx_time = jiffies;
 
-	return 0;
+	return received;
 }
 
 static int
--- eepro100-napi.c	Wed Jun 12 17:11:38 2002
+++ eepro100-napi-proc.c	Wed Jun 12 17:33:51 2002
@@ -119,6 +119,10 @@
 
 #define CONFIG_EEPRO100_NAPI
 
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#endif
+
 MODULE_AUTHOR("Maintainer: Andrey V. Savochkin <saw@saw.sw.com.sg>");
 MODULE_DESCRIPTION("Intel i82557/i82558/i82559 PCI EtherExpressPro driver");
 MODULE_LICENSE("GPL");
@@ -516,6 +520,10 @@
 	unsigned long alloc_fail;
 	unsigned long long poll_cycles;
 
+#ifdef CONFIG_PROC_FS
+	struct proc_dir_entry *proc_parent;
+#endif
+
 #ifdef CONFIG_NET_FASTROUTE
 	unsigned long fastroute_hit;
 	unsigned long fastroute_success;
@@ -582,6 +590,11 @@
 static void enable_rx_and_rxnobuf_ints(struct net_device *dev);
 static void disable_rx_and_rxnobuf_ints(struct net_device *dev);
 
+#ifdef CONFIG_PROC_FS
+int __devinit speedo_create_proc_subdir(struct net_device *sp);
+void speedo_remove_proc_subdir(struct net_device *sp);
+#endif
+
 #endif
 
 
@@ -883,6 +896,14 @@
 #ifdef CONFIG_EEPRO100_NAPI
 	dev->poll = speedo_poll;
 	dev->quota = dev->weight = RX_RING_SIZE;
+
+#ifdef CONFIG_PROC_FS
+	if (speedo_create_proc_subdir(dev) < 0) {
+		printk(KERN_ERR "Failed to create proc directory for %s\n",
+				dev->name);
+	}              
+#endif
+
 #endif
 	return 0;
 }
@@ -1885,6 +1906,354 @@
 	return 1;  /* not_done */
 }
 
+#ifdef CONFIG_PROC_FS
+/* adapted from intel's e100 code */
+static struct proc_dir_entry *adapters_proc_dir = 0;
+
+static void speedo_proc_cleanup(void);
+static unsigned char speedo_init_proc_dir(void);
+
+#define ADAPTERS_PROC_DIR "eepro100"
+#define WRITE_BUF_MAX_LEN 20	
+#define READ_BUF_MAX_LEN  256
+#define SPEEDO_PE_LEN       25
+
+#define sp_off(off) (unsigned long)(offsetof(struct speedo_private, off))
+
+typedef struct _speedo_proc_entry {
+	char *name;
+	read_proc_t *read_proc;
+	write_proc_t *write_proc;
+	unsigned long offset;	/* offset into sp. ~0 means no value, pass NULL. */
+} speedo_proc_entry;
+
+static int
+generic_read(char *page, char **start, off_t off, int count, int *eof, int len)
+{
+	if (len <= off + count)
+		*eof = 1;
+
+	*start = page + off;
+	len -= off;
+	if (len > count)
+		len = count;
+
+	if (len < 0)
+		len = 0;
+
+	return len;
+}
+
+static int
+read_ulong(char *page, char **start, off_t off,
+	   int count, int *eof, unsigned long l)
+{
+	int len;
+
+	len = sprintf(page, "%lu\n", l);
+
+	return generic_read(page, start, off, count, eof, len);
+}
+
+static int
+read_gen_ulong(char *page, char **start, off_t off,
+	       int count, int *eof, void *data)
+{
+	unsigned long val = 0;
+
+	if (data)
+		val = *((unsigned long *) data);
+
+	return read_ulong(page, start, off, count, eof, val);
+}
+
+static int
+read_ulonglong(char *page, char **start, off_t off,
+	   int count, int *eof, unsigned long long ll)
+{
+	int len;
+
+	len = sprintf(page, "%llu\n", ll);
+
+	return generic_read(page, start, off, count, eof, len);
+}
+
+static int
+read_gen_ulonglong(char *page, char **start, off_t off,
+	       int count, int *eof, void *data)
+{
+	unsigned long val = 0;
+
+	if (data)
+		val = *((unsigned long long *) data);
+
+	return read_ulonglong(page, start, off, count, eof, val);
+}
+
+static int
+set_debug(struct file *file, const char *buffer,
+		    unsigned long count, void *data)
+
+{
+	if (speedo_debug == 1) 
+		speedo_debug = 6;
+	else
+		speedo_debug = 1;
+	return count;
+}
+
+static int
+_speedo_show_state(struct file *file, const char *buffer,
+		    unsigned long count, void *data)
+{
+	
+	struct net_device *dev = (struct net_device *)data;
+
+	speedo_show_state(dev);
+
+	return count;
+}
+
+static speedo_proc_entry speedo_proc_list[] = {
+	{"set_debug", 0, set_debug, ~0},
+	{"show_state", 0, _speedo_show_state, ~0},
+	{"poll_switch",read_gen_ulong,0,sp_off(poll_switch)},
+	{"failed_poll_switch",read_gen_ulong,0,sp_off(failed_poll_switch)},
+	{"done_poll",read_gen_ulong,0,sp_off(done_poll)},
+	{"notdone_poll",read_gen_ulong,0,sp_off(notdone_poll)},
+	{"empty_poll",read_gen_ulong,0,sp_off(empty_poll)},
+	{"soft_reset_count",read_gen_ulong,0,sp_off(soft_reset_count)},
+	{"rx_resume_count",read_gen_ulong,0,sp_off(rx_resume_count)},
+	{"alloc_fail",read_gen_ulong,0,sp_off(alloc_fail)},
+	{"poll_cycles",read_gen_ulonglong,0,sp_off(poll_cycles)},
+	{"fastroute_hit",read_gen_ulonglong,0,sp_off(fastroute_hit)},
+	{"fastroute_success",read_gen_ulonglong,0,sp_off(fastroute_success)},
+	{"fastroute_defer",read_gen_ulonglong,0,sp_off(fastroute_defer)},
+	{"", 0, 0, 0}
+};
+
+static int
+read_info(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+	struct speedo_private *sp = data;
+	speedo_proc_entry *pe;
+	int tmp;
+	void *val;
+	int len = 0;
+
+	for (pe = speedo_proc_list; pe->name[0]; pe++) {
+		if (pe->name[0] == '\n') {
+			len += sprintf(page + len, "\n");
+			continue;
+		}
+
+		if (pe->read_proc) {
+			if ((len + READ_BUF_MAX_LEN + SPEEDO_PE_LEN + 1) >=
+			    PAGE_SIZE)
+				break;
+
+			if (pe->offset != ~0)
+				val = ((char *) sp) + pe->offset;
+			else
+				val = NULL;
+
+			len += sprintf(page + len, "%-"
+				       __MODULE_STRING(SPEEDO_PE_LEN)
+				       "s ", pe->name);
+			len += pe->read_proc(page + len, start, 0,
+					     READ_BUF_MAX_LEN + 1, &tmp, val);
+		}
+	}
+
+	return generic_read(page, start, off, count, eof, len);
+}
+
+static struct proc_dir_entry * __devinit
+create_proc_rw(char *name, void *data, struct proc_dir_entry *parent,
+	       read_proc_t * read_proc, write_proc_t * write_proc)
+{
+	struct proc_dir_entry *pdep;
+	mode_t mode = S_IFREG;
+
+	if (write_proc) {
+		mode |= S_IWUSR;
+		if (read_proc) {
+			mode |= S_IRUSR;
+		}
+
+	} else if (read_proc) {
+		mode |= S_IRUGO;
+	}
+
+	if (!(pdep = create_proc_entry(name, mode, parent)))
+		return NULL;
+
+	pdep->read_proc = read_proc;
+	pdep->write_proc = write_proc;
+	pdep->data = data;
+	return pdep;
+}
+
+void
+speedo_remove_proc_subdir(struct net_device *dev)
+{
+	struct speedo_private *sp = (struct speedo_private *)dev->priv;
+	speedo_proc_entry *pe;
+	char info[256];
+	int len;
+
+	/* If our root /proc dir was not created, there is nothing to remove */
+	if (adapters_proc_dir == NULL) {
+		return;
+	}
+
+	len = strlen(dev->name);
+	strncpy(info, dev->name, sizeof (info));
+	strncat(info + len, ".info", sizeof (info) - len);
+
+	if (sp->proc_parent) {
+		for (pe = speedo_proc_list; pe->name[0]; pe++) {
+			if (pe->name[0] == '\n')
+				continue;
+
+			remove_proc_entry(pe->name, sp->proc_parent);
+		}
+
+		remove_proc_entry(dev->name, adapters_proc_dir);
+		sp->proc_parent = NULL;
+	}
+
+	remove_proc_entry(info, adapters_proc_dir);
+
+	/* try to remove the main /proc dir, if it's empty */
+	speedo_proc_cleanup();
+}
+
+int __devinit
+speedo_create_proc_subdir(struct net_device *dev)
+{
+	struct speedo_private *sp = (struct speedo_private *)dev->priv;
+	struct proc_dir_entry *dev_dir;
+	speedo_proc_entry *pe;
+	char info[256];
+	int len;
+	void *data;
+
+	/* create the main /proc dir if needed */
+	if (!adapters_proc_dir) {
+		if (!speedo_init_proc_dir())
+			return -ENOMEM;
+	}
+
+	strncpy(info, dev->name, sizeof (info));
+	len = strlen(info);
+	strncat(info + len, ".info", sizeof (info) - len);
+
+	/* info */
+	if (!(create_proc_rw(info, sp, adapters_proc_dir, read_info, 0))) {
+		speedo_proc_cleanup();
+		return -ENOMEM;
+	}
+
+	dev_dir = create_proc_entry(dev->name, S_IFDIR,
+				    adapters_proc_dir);
+	sp->proc_parent = dev_dir;
+
+	if (!dev_dir) {
+		speedo_remove_proc_subdir(dev);
+		return -ENOMEM;
+	}
+
+	for (pe = speedo_proc_list; pe->name[0]; pe++) {
+		if (pe->name[0] == '\n')
+			continue;
+
+		if (pe->offset != ~0)
+			data = ((char *) sp) + pe->offset;
+		else
+			data = dev;
+
+		if (!(create_proc_rw(pe->name, data, dev_dir,
+				     pe->read_proc, pe->write_proc))) {
+			speedo_remove_proc_subdir(dev);
+			return -ENOMEM;
+		}
+	}
+
+	return 0;
+}
+
+/****************************************************************************
+ * Name:          speedo_init_proc_dir
+ *
+ * Description:   This routine creates the top-level /proc directory for the
+ *                driver in /proc/net
+ *
+ * Arguments:     none
+ *
+ * Returns:       true on success, false on fail
+ *
+ ***************************************************************************/
+static unsigned char
+speedo_init_proc_dir(void)
+{
+	int len;
+
+	/* first check if adapters_proc_dir already exists */
+	len = strlen(ADAPTERS_PROC_DIR);
+	for (adapters_proc_dir = proc_net->subdir;
+	     adapters_proc_dir; adapters_proc_dir = adapters_proc_dir->next) {
+
+		if ((adapters_proc_dir->namelen == len) &&
+		    (!memcmp(adapters_proc_dir->name, ADAPTERS_PROC_DIR, len)))
+			break;
+	}
+
+	if (!adapters_proc_dir)
+		adapters_proc_dir =
+			create_proc_entry(ADAPTERS_PROC_DIR, S_IFDIR, proc_net);
+
+	if (!adapters_proc_dir)
+		return 0;
+
+	return 1;
+}
+
+/****************************************************************************
+ * Name:          speedo_proc_cleanup
+ *
+ * Description:   This routine clears the top-level /proc directory, if empty.
+ *
+ * Arguments:     none
+ *
+ * Returns:       none
+ *
+ ***************************************************************************/
+static void
+speedo_proc_cleanup(void)
+{
+	struct proc_dir_entry *de;
+
+	if (adapters_proc_dir == NULL) {
+		return;
+	}
+
+	/* check if subdir list is empty before removing adapters_proc_dir */
+	for (de = adapters_proc_dir->subdir; de; de = de->next) {
+		/* ignore . and .. */
+		if (*(de->name) != '.')
+			break;
+	}
+
+	if (de)
+		return;
+
+	remove_proc_entry(ADAPTERS_PROC_DIR, proc_net);
+	adapters_proc_dir = NULL;
+}
+
+#endif /* CONFIG_PROC_FS */
+
 #endif /* NAPI */
 
 static int
@@ -2474,6 +2843,9 @@
 	
 	unregister_netdev(dev);
 
+#if defined(CONFIG_EEPRO100_NAPI) && defined(CONFIG_PROC_FS)
+	speedo_remove_proc_subdir(dev);
+#endif
 	release_region(pci_resource_start(pdev, 1), pci_resource_len(pdev, 1));
 	release_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
 

[Index of Archives]     [Linux MIPS Home]     [LKML Archive]     [Linux ARM Kernel]     [Linux ARM]     [Linux]     [Git]     [Yosemite News]     [Linux SCSI]     [Linux Hams]

  Powered by Linux