diff -r -c --exclude=*.o --exclude=*.flags ../linux-2.2.19-pure/drivers/net/acenic.c ./drivers/net/acenic.c *** ../linux-2.2.19-pure/drivers/net/acenic.c Sun Mar 25 08:31:20 2001 --- ./drivers/net/acenic.c Sat May 4 23:13:01 2002 *************** *** 409,414 **** --- 409,477 ---- static int probed __initdata = 0; + /* Click - polling extension */ + static int ace_tx_queue(struct device *dev, struct sk_buff *skb); + static int ace_tx_start(struct device *dev); + static int ace_tx_eob(struct device *dev); + static int ace_rx_refill(struct device *dev, struct sk_buff **); + static struct sk_buff *ace_tx_clean(struct device *dev); + static struct sk_buff *ace_rx_poll(struct device *dev, int *want); + static int ace_poll_on(struct device *dev); + static int ace_poll_off(struct device *dev); + + /* + * only recycle normal size pkts... no jumbo frames. + * set to 0 if don't want to do recycle. + */ + #define SKB_RECYCLED 256 + + unsigned acenic_recycled_skb_size = 0; + static int acenic_recycled_skb_cnt = 0; + static unsigned long acenic_recycle_skb_lock = 0; + static struct sk_buff *acenic_recycled_skbs[SKB_RECYCLED]; + + /* WARNING: these functions are not reentrant! */ + static inline struct sk_buff* + acenic_get_recycled_skb(void) + { + struct sk_buff* skb = 0; + if (acenic_recycled_skb_cnt > 0) { + while (test_and_set_bit(0, (void*)&acenic_recycle_skb_lock)) { + while(acenic_recycle_skb_lock) + asm volatile("" ::: "memory"); + } + if (acenic_recycled_skb_cnt > 0) { + acenic_recycled_skb_cnt--; + skb = acenic_recycled_skbs[acenic_recycled_skb_cnt]; + } + clear_bit(0, (void*)&acenic_recycle_skb_lock); + } + return skb; + } + + /* tries to recycle an skb. if not successful, the skb passed in is freed */ + static inline void + acenic_recycle_or_free_skb(struct sk_buff *skb) + { + if (skb->truesize == acenic_recycled_skb_size) { + if (acenic_recycled_skb_cnt < SKB_RECYCLED) { + while (test_and_set_bit(0, (void*)&acenic_recycle_skb_lock)) { + while(acenic_recycle_skb_lock) + asm volatile("" ::: "memory"); + } + if (acenic_recycled_skb_cnt < SKB_RECYCLED) { + if (skb_recycle(skb)) { + acenic_recycled_skbs[acenic_recycled_skb_cnt] = skb; + acenic_recycled_skb_cnt++; + } + skb = 0; + } + clear_bit(0, (void*)&acenic_recycle_skb_lock); + } + } + if (skb != 0) dev_kfree_skb(skb); + } + #ifdef NEW_NETINIT int __init acenic_probe (void) *************** *** 482,487 **** --- 545,561 ---- dev->set_mac_address = &ace_set_mac_addr; dev->change_mtu = &ace_change_mtu; + /* Click - polling extensions */ + dev->polling = 0; + dev->rx_poll = ace_rx_poll; + dev->rx_refill = ace_rx_refill; + dev->tx_clean = ace_tx_clean; + dev->tx_queue = ace_tx_queue; + dev->tx_start = ace_tx_start; + dev->tx_eob = ace_tx_eob; + dev->poll_off = ace_poll_off; + dev->poll_on = ace_poll_on; + /* display version info if adapter is found */ if (!version_disp) { *************** *** 1579,1584 **** --- 1653,1659 ---- { struct ace_regs *regs; short i, idx; + extern unsigned acenic_recycled_skb_size; regs = ap->regs; *************** *** 1589,1597 **** struct rx_desc *rd; dma_addr_t mapping; ! skb = alloc_skb(ACE_STD_BUFSIZE, GFP_ATOMIC); if (!skb) break; /* * Make sure IP header starts on a fresh cache line. --- 1664,1677 ---- struct rx_desc *rd; dma_addr_t mapping; ! if (acenic_recycled_skb_size == 0 || ! (skb = acenic_get_recycled_skb()) == 0L) ! skb = alloc_skb(ACE_STD_BUFSIZE, GFP_ATOMIC); if (!skb) break; + /* Click: save the size of the skb we need for the std rx ring. */ + if (acenic_recycled_skb_size == 0) + acenic_recycled_skb_size = skb->truesize; /* * Make sure IP header starts on a fresh cache line. *************** *** 1862,1877 **** return evtcsm; } ! ! static void ace_rx_int(struct net_device *dev, u32 rxretprd, u32 rxretcsm) { ! struct ace_private *ap = dev->priv; ! u32 idx; ! int mini_count = 0, std_count = 0; ! ! idx = rxretcsm; ! ! while (idx != rxretprd) { struct ring_info *rip; struct sk_buff *skb; struct rx_desc *rxdesc, *retdesc; --- 1942,1951 ---- return evtcsm; } ! static struct sk_buff* ! ace_rx_get_skb(struct net_device *dev, u32 idx, int *mini_count, int *std_count) { ! struct ace_private *ap = dev->priv; struct ring_info *rip; struct sk_buff *skb; struct rx_desc *rxdesc, *retdesc; *************** *** 1895,1901 **** rip = &ap->skb->rx_std_skbuff[skbidx]; mapsize = ACE_STD_BUFSIZE - (2 + 16); rxdesc = &ap->rx_std_ring[skbidx]; ! std_count++; break; case BD_FLG_JUMBO: rip = &ap->skb->rx_jumbo_skbuff[skbidx]; --- 1969,1975 ---- rip = &ap->skb->rx_std_skbuff[skbidx]; mapsize = ACE_STD_BUFSIZE - (2 + 16); rxdesc = &ap->rx_std_ring[skbidx]; ! (*std_count)++; break; case BD_FLG_JUMBO: rip = &ap->skb->rx_jumbo_skbuff[skbidx]; *************** *** 1907,1919 **** rip = &ap->skb->rx_mini_skbuff[skbidx]; mapsize = ACE_MINI_BUFSIZE - (2 + 16); rxdesc = &ap->rx_mini_ring[skbidx]; ! mini_count++; break; default: printk(KERN_INFO "%s: unknown frame type (0x%02x) " "returned by NIC\n", dev->name, retdesc->flags); ! goto error; } skb = rip->skb; --- 1981,1993 ---- rip = &ap->skb->rx_mini_skbuff[skbidx]; mapsize = ACE_MINI_BUFSIZE - (2 + 16); rxdesc = &ap->rx_mini_ring[skbidx]; ! (*mini_count)++; break; default: printk(KERN_INFO "%s: unknown frame type (0x%02x) " "returned by NIC\n", dev->name, retdesc->flags); ! return 0L; } skb = rip->skb; *************** *** 1945,1955 **** else skb->ip_summed = CHECKSUM_NONE; - netif_rx(skb); /* send it up */ - ap->stats.rx_packets++; ap->stats.rx_bytes += retdesc->size; idx = (idx + 1) % RX_RETURN_RING_ENTRIES; } --- 2019,2047 ---- else skb->ip_summed = CHECKSUM_NONE; ap->stats.rx_packets++; ap->stats.rx_bytes += retdesc->size; + return skb; + } + + + static void ace_rx_int(struct net_device *dev, u32 rxretprd, u32 rxretcsm) + { + struct ace_private *ap = dev->priv; + u32 idx; + int mini_count = 0, std_count = 0; + + idx = rxretcsm; + + while (idx != rxretprd) { + struct sk_buff *skb; + + skb = ace_rx_get_skb(dev, idx, &mini_count, &std_count); + if (skb == 0L) goto error; + + netif_rx(skb); /* send it up */ + idx = (idx + 1) % RX_RETURN_RING_ENTRIES; } *************** *** 1974,1979 **** --- 2066,2123 ---- goto out; } + static void ace_service_tx(struct net_device *dev, int polling_ext) + { + struct ace_private *ap; + struct ace_regs *regs; + u32 txcsm, idx; + + ap = dev->priv; + regs = ap->regs; + + txcsm = *ap->tx_csm; + idx = ap->tx_ret_csm; + + do { + struct sk_buff *skb; + + skb = ap->skb->tx_skbuff[idx].skb; + if (skb) { + dma_addr_t mapping; + + mapping = ap->skb->tx_skbuff[idx].mapping; + + ap->stats.tx_packets++; + ap->stats.tx_bytes += skb->len; + pci_unmap_single(ap->pdev, mapping, skb->len, + PCI_DMA_TODEVICE); + if (!polling_ext) { + dev_kfree_skb_irq(skb); + } else { + acenic_recycle_or_free_skb(skb); + } + + ap->skb->tx_skbuff[idx].skb = NULL; + } + + /* + * Question here is whether one should not skip + * these writes - I have never seen any errors + * caused by the NIC actually trying to access + * these incorrectly. + */ + #if (BITS_PER_LONG == 64) + writel(0, &ap->tx_ring[idx].addr.addrhi); + #endif + writel(0, &ap->tx_ring[idx].addr.addrlo); + writel(0, &ap->tx_ring[idx].flagsize); + + idx = (idx + 1) % TX_RING_ENTRIES; + } while (idx != txcsm); + } + + static unsigned long ace_poll_intr = 0; + static unsigned long ace_poll_evtintr = 0; static void ace_interrupt(int irq, void *dev_id, struct pt_regs *ptregs) { *************** *** 1995,2000 **** --- 2139,2147 ---- if (!(readl(®s->HostCtrl) & IN_INT)) return; + if (dev->polling) + ace_poll_intr++; + /* * Tell the card not to generate interrupts while we are in here. */ *************** *** 2010,2022 **** rxretprd = *ap->rx_ret_prd; rxretcsm = ap->cur_rx; ! if (rxretprd != rxretcsm) ace_rx_int(dev, rxretprd, rxretcsm); txcsm = *ap->tx_csm; idx = ap->tx_ret_csm; ! if (txcsm != idx) { do { struct sk_buff *skb; --- 2157,2169 ---- rxretprd = *ap->rx_ret_prd; rxretcsm = ap->cur_rx; ! if (rxretprd != rxretcsm && !dev->polling) ace_rx_int(dev, rxretprd, rxretcsm); txcsm = *ap->tx_csm; idx = ap->tx_ret_csm; ! if (txcsm != idx && !dev->polling) { do { struct sk_buff *skb; *************** *** 2092,2097 **** --- 2239,2246 ---- evtprd = *ap->evt_prd; if (evtcsm != evtprd) { + if (dev->polling) + ace_poll_evtintr++; evtcsm = ace_handle_event(dev, evtcsm, evtprd); writel(evtcsm, ®s->EvtCsm); } *************** *** 2100,2106 **** * This has to go last in the interrupt handler and run with * the spin lock released ... what lock? */ ! if (netif_running(dev)) { int cur_size; int run_bh = 0; --- 2249,2255 ---- * This has to go last in the interrupt handler and run with * the spin lock released ... what lock? */ ! if (!dev->polling && netif_running(dev)) { int cur_size; int run_bh = 0; *************** *** 2304,2317 **** return 0; } ! ! static int ace_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct ace_private *ap = dev->priv; struct ace_regs *regs = ap->regs; unsigned long addr; ! u32 idx, flagsize; /* * ARGH, there is just no pretty way to do this */ --- 2453,2495 ---- return 0; } ! static int ace_queue_skb(struct sk_buff *skb, struct net_device *dev, u32 idx) { struct ace_private *ap = dev->priv; struct ace_regs *regs = ap->regs; unsigned long addr; ! u32 flagsize; ! ! ap->skb->tx_skbuff[idx].skb = skb; ! ap->skb->tx_skbuff[idx].mapping = ! pci_map_single(ap->pdev, skb->data, skb->len, ! PCI_DMA_TODEVICE); ! addr = (unsigned long) ap->skb->tx_skbuff[idx].mapping; ! #if (BITS_PER_LONG == 64) ! writel(addr >> 32, &ap->tx_ring[idx].addr.addrhi); ! #endif ! writel(addr & 0xffffffff, &ap->tx_ring[idx].addr.addrlo); ! flagsize = (skb->len << 16) | (BD_FLG_END) ; ! writel(flagsize, &ap->tx_ring[idx].flagsize); ! wmb(); ! idx = (idx + 1) % TX_RING_ENTRIES; ! ! ap->tx_prd = idx; ! ace_set_txprd(regs, ap, idx); ! return idx; ! } ! ! static int ace_start_xmit(struct sk_buff *skb, struct net_device *dev) ! { ! struct ace_private *ap = dev->priv; ! u32 idx; + /* polling extension: we have to use tbusy to prevent ace_start_xmit from + * colliding with ace_tx_queue */ + #if 1 + if (test_and_set_bit(0, &dev->tbusy)) + return 1; + #else /* * ARGH, there is just no pretty way to do this */ *************** *** 2321,2326 **** --- 2499,2505 ---- #else netif_stop_queue(dev); #endif + #endif idx = ap->tx_prd; *************** *** 2333,2354 **** return 1; } ! ap->skb->tx_skbuff[idx].skb = skb; ! ap->skb->tx_skbuff[idx].mapping = ! pci_map_single(ap->pdev, skb->data, skb->len, ! PCI_DMA_TODEVICE); ! addr = (unsigned long) ap->skb->tx_skbuff[idx].mapping; ! #if (BITS_PER_LONG == 64) ! writel(addr >> 32, &ap->tx_ring[idx].addr.addrhi); ! #endif ! writel(addr & 0xffffffff, &ap->tx_ring[idx].addr.addrlo); ! flagsize = (skb->len << 16) | (BD_FLG_END) ; ! writel(flagsize, &ap->tx_ring[idx].flagsize); ! wmb(); ! idx = (idx + 1) % TX_RING_ENTRIES; ! ! ap->tx_prd = idx; ! ace_set_txprd(regs, ap, idx); /* * tx_csm is set by the NIC whereas we set tx_ret_csm which --- 2512,2518 ---- return 1; } ! idx = ace_queue_skb(skb, dev, idx); /* * tx_csm is set by the NIC whereas we set tx_ret_csm which *************** *** 3004,3012 **** goto out; } ! /* * Local variables: * compile-command: "gcc -D__SMP__ -D__KERNEL__ -DMODULE -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -fno-strength-reduce -DMODVERSIONS -include ../../include/linux/modversions.h -c -o acenic.o acenic.c" * End: */ --- 3168,3437 ---- goto out; } ! /* * Local variables: * compile-command: "gcc -D__SMP__ -D__KERNEL__ -DMODULE -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -fno-strength-reduce -DMODVERSIONS -include ../../include/linux/modversions.h -c -o acenic.o acenic.c" * End: */ + + + /* + * Polling Extension + * + * tx_csm: NIC's tx consumer pointer. updated when NIC transmit a pkt. + * + * tx_ret_csm: driver's tx consumer pointer. the pkts between tx_ret_csm and + * tx_csm are transmitted packets that need to be cleaned up. + * + * TxPrd: tx producer register in shared memory, read by NIC. this is kept in + * sync with tx_prd. + * + * tx_prd: driver's tx producer pointer (i.e. the next free buf on tx ring). + * driver will queue pkts onto tx ring, starting from tx_prd until the idx on + * tx ring hits tx_ret_csm - 2. that is when the driver believes the tx ring + * is full. + * + * rx_ret_prd: NIC's rx producer pointer. when a pkt arrives at NIC, this + * producer is incremented. + * + * cur_rx: the next rx buffer the driver should read. the difference between + * cur_rx and rx_ret_prd are newly arrived pkts. + * + * rx processing with Tigon uses a return ring. there are three rx rings (std, + * mini, and jumbo) and one rx return ring. the rx_ret_prd pointer points to + * the return ring. on the return ring, a descriptor tells you which of the + * three rx rings contain the last pkt. there is a counter for each of the + * three rings. when a pkt is pulled off of a ring, the corresponding counter + * is incremented. this is important, because the number recorded by each + * counter is the number of buffer refilled onto each ring. + */ + + // #define SELECTIVE_INTR 1 + + static int + ace_tx_queue(struct device *dev, struct sk_buff *skb) + { + struct ace_private *ap = dev->priv; + u32 idx; + + /* polling extension: we have to use tbusy to prevent ace_start_xmit from + * colliding with ace_tx_queue */ + if (test_and_set_bit(0, (void*)&dev->tbusy)) + return 1; + + idx = ap->tx_prd; + + /* hard_start_xmit and tx_clean collision detected */ + if ((idx + 1) % TX_RING_ENTRIES == ap->tx_ret_csm) { + ap->tx_full = 1; + return 1; + } + + idx = ace_queue_skb(skb, dev, idx); + + if ((idx + 2) % TX_RING_ENTRIES == ap->tx_ret_csm) { + ap->tx_full = 1; + } else { + clear_bit(0, (void*)&dev->tbusy); + } + + dev->trans_start = jiffies; + return 0; + } + + static int + ace_tx_start(struct device *dev) + { + /* sorry pal, dunno how to do this... */ + return 1; + } + + static struct sk_buff* + ace_tx_clean(struct device *dev) + { + struct ace_private *ap = dev->priv; + u32 txcsm = *ap->tx_csm; + + if (txcsm != ap->tx_ret_csm) { + ace_service_tx(dev, 1); + + /* only clear the tbusy bit if tx_full is set, so we don't + * accidentally allow two start_xmit or tx_queue to go on w/o + * synchronization. if tx_full is set, then neither start_xmit nor + * tx_queue is executing, or one is executing but the producer pointer + * has already been updated. + */ + if (ap->tx_full) { + clear_bit(0, (void*)&ap->tx_full); + clear_bit(0, (void*)&dev->tbusy); + } + ap->tx_ret_csm = txcsm; + wmb(); + } + #if 0 + if (ap->tx_prd < ap->tx_ret_csm) + return ap->tx_prd + TX_RING_ENTRIES - ap->tx_ret_csm; + else + return ap->tx_prd - ap->tx_ret_csm; + #endif + return 0; + } + + static int + ace_rx_refill(struct device *dev, struct sk_buff **skb_list) + { + u32 evtcsm, evtprd; + struct ace_private *ap = dev->priv; + struct ace_regs *regs = ap->regs; + + /* this seems like what we wanted: while it does clear the bh_pending bit + * for ace, but bh_pending is only set when rx rings need to be filled, so + * we should be fine. + * + * ace_bh return type is void, so we always return 0, even if ace_bh says + * there are no more memory to refill the rings... I guess that is really + * a fatalistic condition anyways... + */ + ace_bh(dev); + + #ifndef SELECTIVE_INTR + /* handle events... since we can't selectively mask out rx and tx + * interrupts... + */ + evtcsm = readl(®s->EvtCsm); + evtprd = *ap->evt_prd; + + if (evtcsm != evtprd) { + evtcsm = ace_handle_event(dev, evtcsm, evtprd); + writel(evtcsm, ®s->EvtCsm); + } + #endif + return 0; + } + + static struct sk_buff * + ace_rx_poll(struct device *dev, int *want) + { + struct ace_private *ap = dev->priv; + u32 idx, rxretprd; + int mini_count = 0, std_count = 0; + struct sk_buff *skb_head, *skb_last; + int got = 0; + + skb_head = skb_last = NULL; + + rxretprd = *ap->rx_ret_prd; + idx = ap->cur_rx; + + while (idx != rxretprd && got < *want) { + struct sk_buff *skb; + + skb = ace_rx_get_skb(dev, idx, &mini_count, &std_count); + if (skb == 0L) goto error; + + if (got == 0) { + skb_head = skb; + skb_last = skb; + skb_last->next = NULL; + skb_last->prev = NULL; + } else { + skb_last->next = skb; + skb->prev = skb_last; + skb->next = NULL; + skb_last = skb; + } + got++; + + idx = (idx + 1) % RX_RETURN_RING_ENTRIES; + } + + atomic_sub(std_count, &ap->cur_rx_bufs); + if (!ACE_IS_TIGON_I(ap)) + atomic_sub(mini_count, &ap->cur_mini_bufs); + + out: + /* + * According to the documentation RxRetCsm is obsolete with + * the 12.3.x Firmware - my Tigon I NICs seem to disagree! + */ + if (ACE_IS_TIGON_I(ap)) { + struct ace_regs *regs = ap->regs; + writel(idx, ®s->RxRetCsm); + } + ap->cur_rx = idx; + + *want = got; + return skb_head; + + error: + idx = rxretprd; + goto out; + } + + static int + ace_poll_on(struct device *dev) + { + if (!dev->polling) { + struct ace_private *ap; + struct ace_regs *regs; + struct net_device *d = (struct net_device*)dev; + u32 stat; + + ap = d->priv; + regs = ap->regs; + + #ifdef SELECTIVE_INTR + /* okay, for some reason, below code segment did not turn off rx and + * tx interrupts... so we need to mask interrupts completely and + * depend on rx_refill to manage events. + */ + stat = readl(®s->ModeStat); + stat |= (ACE_DONT_RUPT_SENDS | ACE_DONT_RUPT_RECVS); + writel(stat, ®s->ModeStat); + #else + stat = readl(®s->HostCtrl); + stat |= MASK_INTS; + writel(stat, ®s->HostCtrl); + #endif + dev->polling = 2; + } + return 0; + } + + static int + ace_poll_off(struct device *dev) + { + if (dev->polling) { + struct ace_private *ap; + struct ace_regs *regs; + struct net_device *d = (struct net_device*)dev; + u32 stat; + + ap = d->priv; + regs = ap->regs; + + #ifdef SELECTIVE_INTR + stat = readl(®s->ModeStat); + stat &= ~(ACE_DONT_RUPT_SENDS | ACE_DONT_RUPT_RECVS); + writel(stat, ®s->ModeStat); + #else + stat = readl(®s->HostCtrl); + stat &= ~MASK_INTS; + writel(stat, ®s->HostCtrl); + #endif + dev->polling = 0; + + printk("ace polling interrupt summary: %ld %ld\n", + ace_poll_intr, ace_poll_evtintr); + ace_poll_intr = ace_poll_evtintr = 0; + } + return 0; + } + + static int ace_tx_eob(struct device *dev) + { + return 0; + } + + diff -r -c --exclude=*.o --exclude=*.flags ../linux-2.2.19-pure/drivers/net/acenic.h ./drivers/net/acenic.h *** ../linux-2.2.19-pure/drivers/net/acenic.h Sun Mar 25 08:31:20 2001 --- ./drivers/net/acenic.h Sat May 4 23:13:01 2002 *************** *** 251,256 **** --- 251,259 ---- #define ACE_WARN 0x08 #define ACE_BYTE_SWAP_DMA 0x10 #define ACE_NO_JUMBO_FRAG 0x200 + #define ACE_DONT_RUPT_EVENTS 0x1000 + #define ACE_DONT_RUPT_SENDS 0x2000 + #define ACE_DONT_RUPT_RECVS 0x4000 #define ACE_FATAL 0x40000000 diff -r -c --exclude=*.o --exclude=*.flags ../linux-2.2.19-pure/drivers/net/eepro100.c ./drivers/net/eepro100.c *** ../linux-2.2.19-pure/drivers/net/eepro100.c Sun Mar 25 08:37:34 2001 --- ./drivers/net/eepro100.c Sat May 4 23:13:01 2002 *************** *** 49,62 **** static const char *version = "eepro100.c:v1.09j-t 9/29/99 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/eepro100.html\n" "eepro100.c: $Revision: 1.20.2.10 $ 2000/05/31 Modified by Andrey V. Savochkin and others\n" ! "eepro100.c: VA Linux custom, Dragan Stancevic 2000/11/15\n"; /* A few user-configurable values that apply to all boards. First set is undocumented and spelled per Intel recommendations. */ ! static int congenb = 0; /* Enable congestion control in the DP83840. */ ! static int txfifo = 0; /* Tx FIFO threshold in 4 byte units, 0-15 */ ! static int rxfifo = 0xF; /* Rx FIFO threshold, default 32 bytes. */ /* Tx/Rx DMA burst length, 0-127, 0 == no preemption, tx==128 -> disabled. */ static int txdmacount = 128; static int rxdmacount = 0; --- 49,69 ---- static const char *version = "eepro100.c:v1.09j-t 9/29/99 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/eepro100.html\n" "eepro100.c: $Revision: 1.20.2.10 $ 2000/05/31 Modified by Andrey V. Savochkin and others\n" ! "eepro100.c: VA Linux custom, Dragan Stancevic 2000/11/15\n" ! "eepro100.c: MIT click polling extensions\n"; ! /* A few user-configurable values that apply to all boards. First set is undocumented and spelled per Intel recommendations. */ ! /* ! * benjie: i am setting txfifo to 8 and rxfifo to 8 as they are in the ! * 2.4.9 linux kernel ! */ ! static int congenb = 0; /* Enable congestion control in the DP83840. */ ! static int txfifo = 8; /* Tx FIFO threshold in 4 byte units, 0-15 */ ! static int rxfifo = 8; /* Rx FIFO threshold, default 32 bytes. */ ! /* Tx/Rx DMA burst length, 0-127, 0 == no preemption, tx==128 -> disabled. */ static int txdmacount = 128; static int rxdmacount = 0; *************** *** 587,592 **** --- 594,609 ---- static void set_rx_mode(struct net_device *dev); static void speedo_show_state(struct net_device *dev); + /* device polling stuff */ + static int speedo_tx_queue(struct net_device *dev, struct sk_buff *skb); + static int speedo_tx_eob(struct net_device *dev); + static int speedo_tx_start(struct net_device *dev); + static int speedo_rx_refill(struct net_device *dev, struct sk_buff **); + static struct sk_buff *speedo_tx_clean(struct net_device *dev); + static struct sk_buff *speedo_rx_poll(struct net_device *dev, int *want); + static int speedo_poll_on(struct net_device *dev); + static int speedo_poll_off(struct net_device *dev); + #ifdef honor_default_port *************** *** 891,896 **** --- 908,924 ---- dev->set_multicast_list = &set_rx_mode; dev->do_ioctl = &speedo_ioctl; + /* Click: polling support */ + dev->polling = 0; + dev->poll_on = &speedo_poll_on; + dev->poll_off = &speedo_poll_off; + dev->rx_poll = &speedo_rx_poll; + dev->rx_refill = &speedo_rx_refill; + dev->tx_queue = &speedo_tx_queue; + dev->tx_clean = &speedo_tx_clean; + dev->tx_start = &speedo_tx_start; + dev->tx_eob = &speedo_tx_eob; + return dev; } *************** *** 1111,1117 **** ioaddr + SCBPointer); /* We are not ACK-ing FCP and ER in the interrupt handler yet so they should remain masked --Dragan */ ! outw(CUStart | SCBMaskEarlyRx | SCBMaskFlowCtl, ioaddr + SCBCmd); } /* Media monitoring and control. */ --- 1139,1146 ---- ioaddr + SCBPointer); /* We are not ACK-ing FCP and ER in the interrupt handler yet so they should remain masked --Dragan */ ! outw(CUStart | SCBMaskEarlyRx | SCBMaskFlowCtl | ! (dev->polling ? SCBMaskAll : 0), ioaddr + SCBCmd); } /* Media monitoring and control. */ *************** *** 1318,1324 **** dev->name); outl(virt_to_bus(&sp->tx_ring[sp->dirty_tx % TX_RING_SIZE]), ioaddr + SCBPointer); ! outw(CUStart, ioaddr + SCBCmd); reset_mii(dev); } else { #else --- 1347,1354 ---- dev->name); outl(virt_to_bus(&sp->tx_ring[sp->dirty_tx % TX_RING_SIZE]), ioaddr + SCBPointer); ! outw(CUStart | (dev->polling ? SCBMaskAll : 0), ! ioaddr + SCBCmd); reset_mii(dev); } else { #else *************** *** 1365,1370 **** --- 1395,1408 ---- long ioaddr = dev->base_addr; int entry; + #if 0 + if (dev->polling > 0) { + printk(KERN_ERR "%s: start_xmit while polling\n", + dev->name); + return 1; + } + #endif + #if ! defined(HAS_NETIF_QUEUE) if (test_bit(0, (void*)&dev->tbusy) != 0) { int tickssofar = jiffies - dev->trans_start; *************** *** 1377,1383 **** command atomic. --SAW */ spin_lock_irqsave(&sp->lock, flags); wait_for_cmd_done(ioaddr + SCBCmd); ! outw(SCBTriggerIntr, ioaddr + SCBCmd); spin_unlock_irqrestore(&sp->lock, flags); return 1; } --- 1415,1422 ---- command atomic. --SAW */ spin_lock_irqsave(&sp->lock, flags); wait_for_cmd_done(ioaddr + SCBCmd); ! outw(SCBTriggerIntr | ((dev->polling>0) ? SCBMaskAll : 0), ! ioaddr + SCBCmd); spin_unlock_irqrestore(&sp->lock, flags); return 1; } *************** *** 1388,1394 **** { /* Prevent interrupts from changing the Tx ring from underneath us. */ unsigned long flags; - spin_lock_irqsave(&sp->lock, flags); /* Check if there are enough space. */ --- 1427,1432 ---- *************** *** 1436,1442 **** } dev->trans_start = jiffies; - return 0; } --- 1474,1479 ---- *************** *** 1445,1450 **** --- 1482,1493 ---- unsigned int dirty_tx; struct speedo_private *sp = (struct speedo_private *)dev->priv; + if (dev->polling) { + printk(KERN_ERR "%s: speedo_tx_buffer_gc while polling\n", + dev->name); + return; + } + dirty_tx = sp->dirty_tx; while ((int)(sp->cur_tx - dirty_tx) > 0) { int entry = dirty_tx % TX_RING_SIZE; *************** *** 1510,1515 **** --- 1553,1561 ---- } #endif + if (dev->polling) + printk(KERN_ERR "%s: interrupt while polling\n", dev->name); + ioaddr = dev->base_addr; sp = (struct speedo_private *)dev->priv; *************** *** 1539,1548 **** break; /* Always check if all rx buffers are allocated. --SAW */ ! speedo_refill_rx_buffers(dev, 0); ! if ((status & 0x5000) || /* Packet received, or Rx error. */ ! (sp->rx_ring_state&(RrNoMem|RrPostponed)) == RrPostponed) /* Need to gather the postponed packet. */ speedo_rx(dev); --- 1585,1596 ---- break; /* Always check if all rx buffers are allocated. --SAW */ ! if (!dev->polling) ! speedo_refill_rx_buffers(dev, 0); ! if (!dev->polling && ! ((status & 0x5000) || /* Packet received, or Rx error. */ ! (sp->rx_ring_state&(RrNoMem|RrPostponed)) == RrPostponed)) /* Need to gather the postponed packet. */ speedo_rx(dev); *************** *** 1612,1618 **** } /* User interrupt, Command/Tx unit interrupt or CU not active. */ ! if (status & 0xA400) { spin_lock(&sp->lock); speedo_tx_buffer_gc(dev); if (sp->tx_full --- 1660,1666 ---- } /* User interrupt, Command/Tx unit interrupt or CU not active. */ ! if (!dev->polling && (status & 0xA400)) { spin_lock(&sp->lock); speedo_tx_buffer_gc(dev); if (sp->tx_full *************** *** 1721,1726 **** --- 1769,1780 ---- { struct speedo_private *sp = (struct speedo_private *)dev->priv; + if (dev->polling) { + printk(KERN_ERR "%s: speedo_refill_rx_buffers called " + "while polling\n", dev->name); + return; + } + /* Refill the RX ring. */ while ((int)(sp->cur_rx - sp->dirty_rx) > 0 && speedo_refill_rx_buf(dev, force) != -1); *************** *** 1735,1740 **** --- 1789,1800 ---- int rx_work_limit = sp->dirty_rx + RX_RING_SIZE - sp->cur_rx; int alloc_ok = 1; + if (dev->polling) { + printk(KERN_ERR "%s: in speedo_rx() while polling\n", + dev->name); + return 0; + } + if (speedo_debug > 4) printk(KERN_DEBUG " In speedo_rx().\n"); /* If we own the next entry, it's a new packet. Send it up. */ *************** *** 2218,2220 **** --- 2278,2649 ---- * tab-width: 4 * End: */ + + /* + * Click: Polling extensions. Most of this code has been copied + * from various routines above with slight modifications. + */ + + static int speedo_rx_refill(struct net_device *dev, struct sk_buff **skbs) { + struct speedo_private *sp = (struct speedo_private *)dev->priv; + struct sk_buff *skb_list; + int dirty_rx = sp->dirty_rx; + + /* If the list is empty, return the number of skb's we want */ + if (skbs == 0) + return sp->cur_rx - sp->dirty_rx; + + skb_list = *skbs; + + /* + * Refill the RX ring with supplied skb's. Unlike + * speedo_refill_rx_buf routine, we don't have to + * worry about failed allocations. + */ + while ((int)(sp->cur_rx - sp->dirty_rx) > 0 && skb_list) { + int entry; + struct RxFD *rxf; + struct sk_buff *skb; + + entry = sp->dirty_rx % RX_RING_SIZE; + if (sp->rx_skbuff[entry] == NULL) { + skb = skb_list; + skb_list = skb_list->next; + skb->prev = skb->next = NULL; + skb->list = NULL; + + sp->rx_skbuff[entry] = skb; + sp->rx_ringp[entry] = (struct RxFD *) skb->tail; + + skb->dev = dev; + skb_reserve(skb, sizeof(struct RxFD)); + + rxf = sp->rx_ringp[entry]; + rxf->rx_buf_addr = (uint32_t)0xffffffff; + } else { + rxf = sp->rx_ringp[entry]; + } + speedo_rx_link(dev, entry, rxf); + sp->dirty_rx++; + } + + /* + * Check if the RU is stopped -- restart it, if so. + */ + if ((inw(dev->base_addr + SCBStatus) & 0x003c) == 0x0008) { + wait_for_cmd_done(dev->base_addr + SCBCmd); + + /* + * If the RU stopped, it's because there aren't + * any DMA buffers left, so the first DMA buffer + * we've just refilled is where we should start + * receiving. + */ + outl(virt_to_bus(sp->rx_ringp[dirty_rx % RX_RING_SIZE]), + dev->base_addr + SCBPointer); + outb(RxStart, dev->base_addr + SCBCmd); + } + + /* + * Clear error flags on the RX ring, write back the remaining + * skb's that we haven't used, and return the number of dirty + * buffers remaining. + */ + sp->rx_ring_state &= ~(RrNoMem|RrOOMReported); + *skbs = skb_list; + return sp->cur_rx - sp->dirty_rx; + } + + static struct sk_buff *speedo_rx_poll(struct net_device *dev, int *want) { + struct speedo_private *sp = (struct speedo_private *)dev->priv; + int entry = sp->cur_rx % RX_RING_SIZE; + int rx_work_limit = sp->dirty_rx + RX_RING_SIZE - sp->cur_rx; + struct sk_buff *skb_head, *skb_last; + int got = 0; + + skb_head = skb_last = NULL; + + /* If we own the next entry, it's a new packet. Send it up. */ + while (sp->rx_ringp[entry] != NULL) { + int status = le32_to_cpu(sp->rx_ringp[entry]->status); + int pkt_len = le32_to_cpu(sp->rx_ringp[entry]->count) & 0x3fff; + + /* If the packet hasn't been received, bail out. */ + if ((status & RxComplete) == 0) + break; + + /* If we've already received too many packets, bail out. */ + if (got == *want || --rx_work_limit < 0) + break; + + /* Check for a rare out-of-memory case: the current buffer is + the last buffer allocated in the RX ring. --SAW */ + if (sp->last_rxf == sp->rx_ringp[entry]) { + /* + * Postpone the packet. It'll be reaped next time + * when this packet is no longer the last packet + * in the ring. + */ + sp->rx_ring_state |= RrPostponed; + break; + } + + if ((status & (RxErrTooBig|RxOK|0x0f90)) != RxOK) { + if (status & RxErrTooBig) { + printk(KERN_ERR "%s: Ethernet frame overran " + "the Rx buffer, status %8.8x!\n", + dev->name, status); + } else if (! (status & RxOK)) { + /* + * There was a fatal error. This *should* + * be impossible. + */ + sp->stats.rx_errors++; + printk(KERN_ERR "%s: Anomalous event in " + "speedo_rx_poll(), status %8.8x.\n", + dev->name, status); + } + } else { + struct sk_buff *skb = sp->rx_skbuff[entry]; + + if (skb == NULL) { + printk(KERN_ERR "%s: Inconsistent Rx " + "descriptor chain.\n", dev->name); + break; + } + + /* Remove skbuff from RX ring */ + sp->rx_skbuff[entry] = NULL; + sp->rx_ringp[entry] = NULL; + skb_put(skb, pkt_len); + + skb->protocol = eth_type_trans(skb, dev); + sp->stats.rx_packets++; + sp->stats.rx_bytes += pkt_len; + + /* Append the skb to the received list */ + if (got == 0) { + skb_head = skb_last = skb; + skb->next = skb->prev = NULL; + } else { + skb_last->next = skb; + skb->prev = skb_last; + skb->next = NULL; + skb_last = skb; + } + + got++; + } + + entry = (++sp->cur_rx) % RX_RING_SIZE; + sp->rx_ring_state &= ~RrPostponed; + } + + if (got == 0 && (inw(dev->base_addr + SCBStatus) & 0x003c) == 0x0008) { + wait_for_cmd_done(dev->base_addr + SCBCmd); + + outl(virt_to_bus(sp->rx_ringp[sp->cur_rx % RX_RING_SIZE]), + dev->base_addr + SCBPointer); + outb(RxStart, dev->base_addr + SCBCmd); + } + + sp->last_rx_time = jiffies; + *want = got; + return skb_head; + } + + static int speedo_tx_queue(struct net_device *dev, struct sk_buff *skb) { + struct speedo_private *sp = (struct speedo_private *)dev->priv; + long ioaddr = dev->base_addr; + int entry; + unsigned flags; + + #if ! defined(HAS_NETIF_QUEUE) + if (test_bit(0, (void*)&dev->tbusy) != 0) { + int tickssofar = jiffies - dev->trans_start; + if (tickssofar < TX_TIMEOUT - 2) + return 1; + if (tickssofar < TX_TIMEOUT) { + /* Reap sent packets from the full Tx queue. */ + unsigned long flags; + /* Take a spinlock to make wait_for_cmd_done and + * sending the command atomic. --SAW + */ + spin_lock_irqsave(&sp->lock, flags); + wait_for_cmd_done(ioaddr + SCBCmd); + outw(SCBTriggerIntr | (dev->polling ? SCBMaskAll : 0), + ioaddr + SCBCmd); + spin_unlock_irqrestore(&sp->lock, flags); + return 1; + } + speedo_tx_timeout(dev); + return 1; + } + #endif + + spin_lock_irqsave(&sp->lock, flags); + + /* Check if there are enough space. */ + if ((int)(sp->cur_tx - sp->dirty_tx) >= TX_QUEUE_LIMIT) { + printk(KERN_ERR "%s: incorrect tbusy state, fixed.\n", + dev->name); + netif_stop_queue(dev); + spin_unlock_irqrestore(&sp->lock, flags); + sp->tx_full = 1; + return 1; + } + + /* Calculate the Tx descriptor entry. */ + entry = sp->cur_tx++ % TX_RING_SIZE; + + sp->tx_skbuff[entry] = skb; + sp->tx_ring[entry].link = + virt_to_le32desc(&sp->tx_ring[sp->cur_tx % TX_RING_SIZE]); + sp->tx_ring[entry].tx_desc_addr = + virt_to_le32desc(&sp->tx_ring[entry].tx_buf_addr0); + + /* The data region is always in one buffer descriptor. */ + sp->tx_ring[entry].count = cpu_to_le32(sp->tx_threshold); + sp->tx_ring[entry].tx_buf_addr0 = virt_to_le32desc(skb->data); + sp->tx_ring[entry].tx_buf_size0 = cpu_to_le32(skb->len); + + sp->tx_ring[entry].status = + cpu_to_le32(CmdSuspend | CmdTx | CmdTxFlex); + + /* Trigger the command unit resume. */ + clear_suspend(sp->last_cmd); + sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry]; + + /* Leave room for set_rx_mode(). If there is no more space than + * reserved for multicast filter mark the ring as full. + */ + if ((int)(sp->cur_tx - sp->dirty_tx) >= TX_QUEUE_LIMIT) { + netif_stop_queue(dev); + sp->tx_full = 1; + } + + spin_unlock_irqrestore(&sp->lock, flags); + return 0; + } + + static int speedo_tx_eob(struct net_device *dev) + { + /* Start the DMA going again */ + #if 1 + #if 0 // benjie + wait_for_cmd_done(ioaddr + SCBCmd); + #endif + outb(CUResume, dev->base_addr + SCBCmd); + #endif + return 0; + } + + static int speedo_tx_start(struct net_device *dev) { + printk("hard tx_start\n"); + /* must have been suspended before the last queued DMA ring, so + * this mindless CUResume is probably okay */ + outb(CUResume, dev->base_addr + SCBCmd); + dev->trans_start = jiffies; + return 0; + } + + static struct sk_buff *speedo_tx_clean(struct net_device *dev) { + unsigned int dirty_tx; + struct speedo_private *sp = (struct speedo_private *)dev->priv; + struct sk_buff *skb_head, *skb_last; + + skb_head = skb_last = NULL; + + dirty_tx = sp->dirty_tx; + while ((int)(sp->cur_tx - dirty_tx) > 0) { + int entry = dirty_tx % TX_RING_SIZE; + int status = le32_to_cpu(sp->tx_ring[entry].status); + + if ((status & StatusComplete) == 0) + break; /* It still hasn't been processed. */ + + if (status & TxUnderrun) + if (sp->tx_threshold < 0x01e08000) { + if (speedo_debug > 2) + printk(KERN_DEBUG "%s: TX underrun, " + "threshold adjusted.\n", + dev->name); + sp->tx_threshold += 0x00040000; + } + + /* Put the original skb on the return list. */ + if (sp->tx_skbuff[entry]) { + struct sk_buff *skb = sp->tx_skbuff[entry]; + + sp->stats.tx_packets++; /* Count only user packets. */ + sp->stats.tx_bytes += sp->tx_skbuff[entry]->len; + sp->tx_skbuff[entry] = 0; + + if (skb_head == NULL) { + skb_head = skb_last = skb; + skb->next = skb->prev = NULL; + } else { + skb_last->next = skb; + skb->prev = skb_last; + skb->next = NULL; + skb_last = skb; + } + } + dirty_tx++; + } + + if (speedo_debug && (int)(sp->cur_tx - dirty_tx) > TX_RING_SIZE) { + printk(KERN_ERR "out-of-sync dirty pointer, %d vs. %d," + " full=%d.\n", + dirty_tx, sp->cur_tx, sp->tx_full); + dirty_tx += TX_RING_SIZE; + } + + while (sp->mc_setup_head != NULL + && (int)(dirty_tx - sp->mc_setup_head->tx - 1) > 0) { + struct speedo_mc_block *t; + if (speedo_debug > 1) + printk(KERN_DEBUG "%s: freeing mc frame.\n", dev->name); + t = sp->mc_setup_head->next; + kfree(sp->mc_setup_head); + sp->mc_setup_head = t; + } + if (sp->mc_setup_head == NULL) + sp->mc_setup_tail = NULL; + + sp->dirty_tx = dirty_tx; + + if (sp->tx_full && (int)(sp->cur_tx - sp->dirty_tx) < TX_QUEUE_UNFULL) { + /* The ring is no longer full. */ + sp->tx_full = 0; + netif_wake_queue(dev); /* Attention: under a spinlock. --SAW */ + } + return skb_head; + } + + static int speedo_poll_on(struct net_device *dev) { + long ioaddr = dev->base_addr; + + if (dev->polling == 0) { + /* Mask all interrupts */ + outw(SCBMaskAll, ioaddr + SCBCmd); + + dev->polling = 2; + } + + return 0; + } + + static int speedo_poll_off(struct net_device *dev) { + long ioaddr = dev->base_addr; + + if (dev->polling > 0) { + /* Enable interrupts */ + outw(0, ioaddr + SCBCmd); + + dev->polling = 0; + } + + return 0; + } + diff -r -c --exclude=*.o --exclude=*.flags ../linux-2.2.19-pure/drivers/net/tulip.c ./drivers/net/tulip.c *** ../linux-2.2.19-pure/drivers/net/tulip.c Sun Mar 25 08:37:35 2001 --- ./drivers/net/tulip.c Sat May 4 23:13:01 2002 *************** *** 495,509 **** }; struct tulip_private { ! char devname[8]; /* Used only for kernel debugging. */ ! const char *product_name; ! struct device *next_module; struct tulip_rx_desc rx_ring[RX_RING_SIZE]; struct tulip_tx_desc tx_ring[TX_RING_SIZE]; /* The saved address of a sent-in-place packet/buffer, for skfree(). */ struct sk_buff* tx_skbuff[TX_RING_SIZE]; ! /* The addresses of receive-in-place skbuffs. */ ! struct sk_buff* rx_skbuff[RX_RING_SIZE]; char *rx_buffs; /* Address of temporary Rx buffers. */ u16 setup_frame[96]; /* Pseudo-Tx frame to init address table. */ int chip_id; --- 495,515 ---- }; struct tulip_private { ! unsigned int cur_rx, dirty_rx; ! unsigned int rx_reserved[6]; struct tulip_rx_desc rx_ring[RX_RING_SIZE]; + /* The addresses of receive-in-place skbuffs. */ + struct sk_buff* rx_skbuff[RX_RING_SIZE]; + + unsigned int cur_tx, dirty_tx; + unsigned int tx_reserved[6]; struct tulip_tx_desc tx_ring[TX_RING_SIZE]; /* The saved address of a sent-in-place packet/buffer, for skfree(). */ struct sk_buff* tx_skbuff[TX_RING_SIZE]; ! ! char devname[8]; /* Used only for kernel debugging. */ ! const char *product_name; ! struct device *next_module; char *rx_buffs; /* Address of temporary Rx buffers. */ u16 setup_frame[96]; /* Pseudo-Tx frame to init address table. */ int chip_id; *************** *** 512,519 **** struct net_device_stats stats; struct timer_list timer; /* Media selection timer. */ int interrupt; /* In-interrupt flag. */ - unsigned int cur_rx, cur_tx; /* The next free ring entry */ - unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ unsigned int tx_full:1; /* The Tx queue is full. */ unsigned int full_duplex:1; /* Full-duplex operation requested. */ unsigned int full_duplex_lock:1; --- 518,523 ---- *************** *** 566,571 **** --- 570,585 ---- #endif static void set_rx_mode(struct device *dev); + /* device polling stuff */ + static int tulip_tx_queue(struct device *dev, struct sk_buff *skb); + static int tulip_tx_eob(struct device *dev); + static int tulip_tx_start(struct device *dev); + static int tulip_rx_refill(struct device *dev, struct sk_buff **); + static struct sk_buff *tulip_tx_clean(struct device *dev); + static struct sk_buff *tulip_rx_poll(struct device *dev, int *want); + static int tulip_poll_on(struct device *dev); + static int tulip_poll_off(struct device *dev); + /* A list of all installed Tulip devices. */ *************** *** 813,818 **** --- 827,843 ---- dev->base_addr = ioaddr; dev->irq = irq; + + /* Click - polling extensions */ + dev->polling = 0; + dev->rx_poll = tulip_rx_poll; + dev->rx_refill = tulip_rx_refill; + dev->tx_clean = tulip_tx_clean; + dev->tx_queue = tulip_tx_queue; + dev->tx_start = tulip_tx_start; + dev->tx_eob = tulip_tx_eob; + dev->poll_off = tulip_poll_off; + dev->poll_on = tulip_poll_on; tp->pci_bus = pci_bus; tp->pci_devfn = pci_devfn; *************** *** 2539,2560 **** tp->tx_ring[i-1].buffer2 = virt_to_le32desc(&tp->tx_ring[0]); } static int tulip_start_xmit(struct sk_buff *skb, struct device *dev) { struct tulip_private *tp = (struct tulip_private *)dev->priv; int entry; u32 flag; - /* Block a timer-based transmit from overlapping. This could better be - done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { ! if (jiffies - dev->trans_start < TX_TIMEOUT) ! return 1; ! tulip_tx_timeout(dev); return 1; } ! /* Caution: the write order is important here, set the field with the ownership bits last. */ --- 2564,2599 ---- tp->tx_ring[i-1].buffer2 = virt_to_le32desc(&tp->tx_ring[0]); } + /* start the tulip transmit process by doing an outb */ + static int + tulip_tx_start(struct device *dev) + { + if (dev->polling <= 0) + /* Trigger an immediate transmit demand. */ + outl(0, dev->base_addr + CSR1); + dev->trans_start = jiffies; + return 0; + } + static int tulip_start_xmit(struct sk_buff *skb, struct device *dev) { struct tulip_private *tp = (struct tulip_private *)dev->priv; int entry; u32 flag; + + #if 0 + if (dev->polling > 0) { + printk("tulip_start_xmit when interrupt is off\n"); + } + #endif if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { ! if (jiffies - dev->trans_start >= TX_TIMEOUT) ! tulip_tx_timeout(dev); return 1; } ! /* Caution: the write order is important here, set the field with the ownership bits last. */ *************** *** 2578,2593 **** flag = 0xe0000000 | DESC_RING_WRAP; tp->tx_ring[entry].length = cpu_to_le32(skb->len | flag); tp->tx_ring[entry].status = cpu_to_le32(DescOwned); tp->cur_tx++; ! if ( ! tp->tx_full) ! clear_bit(0, (void*)&dev->tbusy); ! dev->trans_start = jiffies; ! /* Trigger an immediate transmit demand. */ ! outl(0, dev->base_addr + CSR1); ! return 0; } /* The interrupt handler does all of the Rx thread work and cleans up --- 2617,2648 ---- flag = 0xe0000000 | DESC_RING_WRAP; tp->tx_ring[entry].length = cpu_to_le32(skb->len | flag); + /* new linux memory barrier thingie */ + wmb(); tp->tx_ring[entry].status = cpu_to_le32(DescOwned); tp->cur_tx++; ! ! tulip_tx_start(dev); ! ! if (!tp->tx_full) ! clear_bit(0, (void*)&dev->tbusy); ! ! return 0; ! } ! /* polling extension - intr stats */ ! void (*tulip_interrupt_hook)(struct device *, unsigned); ! static __inline__ unsigned long long ! tulip_get_cycles(void) ! { ! unsigned long low, high; ! unsigned long long x; ! __asm__ __volatile__("rdtsc":"=a" (low), "=d" (high)); ! x = high; ! x <<= 32; ! x |= low; ! return(x); } /* The interrupt handler does all of the Rx thread work and cleans up *************** *** 2599,2611 **** long ioaddr = dev->base_addr; int csr5; int entry; - int missed; int rx = 0; int tx = 0; int oi = 0; int maxrx = RX_RING_SIZE; int maxtx = TX_RING_SIZE; int maxoi = TX_RING_SIZE; #if defined(__i386__) && defined(SMP_CHECK) if (test_and_set_bit(0, (void*)&dev->interrupt)) { --- 2654,2666 ---- long ioaddr = dev->base_addr; int csr5; int entry; int rx = 0; int tx = 0; int oi = 0; int maxrx = RX_RING_SIZE; int maxtx = TX_RING_SIZE; int maxoi = TX_RING_SIZE; + int first_time = 1; #if defined(__i386__) && defined(SMP_CHECK) if (test_and_set_bit(0, (void*)&dev->interrupt)) { *************** *** 2627,2648 **** do { csr5 = inl(ioaddr + CSR5); /* Acknowledge all of the current interrupt sources ASAP. */ outl(csr5 & 0x0001ffff, ioaddr + CSR5); if (tulip_debug > 4) printk(KERN_DEBUG "%s: interrupt csr5=%#8.8x new csr5=%#8.8x.\n", dev->name, csr5, inl(dev->base_addr + CSR5)); ! if ((csr5 & (NormalIntr|AbnormalIntr)) == 0) ! break; ! ! if (csr5 & (RxIntr | RxNoBuf)) { ! rx += tulip_rx(dev); ! tulip_refill_rx(dev); } ! if (csr5 & (TxNoBuf | TxDied | TxIntr | TimerInt)) { unsigned int dirty_tx; for (dirty_tx = tp->dirty_tx; tp->cur_tx - dirty_tx > 0; --- 2682,2720 ---- do { csr5 = inl(ioaddr + CSR5); + + if ((csr5 & (NormalIntr|AbnormalIntr)) == 0) { + if (dev->polling > 0) goto out; + if (first_time) goto out; + else break; + } + first_time = 0; + /* Acknowledge all of the current interrupt sources ASAP. */ outl(csr5 & 0x0001ffff, ioaddr + CSR5); + /* Notify tulip_interrupt_hook. */ + if (tulip_interrupt_hook) + tulip_interrupt_hook(dev, CSR5); + + if (dev->polling > 0) { + if ((csr5 & (TxDied|TimerInt|AbnormalIntr))==0) goto out; + } + if (tulip_debug > 4) printk(KERN_DEBUG "%s: interrupt csr5=%#8.8x new csr5=%#8.8x.\n", dev->name, csr5, inl(dev->base_addr + CSR5)); ! if ((csr5 & (RxIntr | RxNoBuf)) && !dev->polling) { ! rx += tulip_rx(dev); ! tulip_refill_rx(dev); } ! if ((csr5 & (TxNoBuf | TxDied | TxIntr | TimerInt)) ! && !dev->polling) { ! /* part of following code duplicated at the end ! * in tulip_tx_clean for the polling driver, changes ! * here should propagate to there as well */ unsigned int dirty_tx; for (dirty_tx = tp->dirty_tx; tp->cur_tx - dirty_tx > 0; *************** *** 2707,2720 **** } tp->dirty_tx = dirty_tx; ! if (csr5 & TxDied) { ! if (tulip_debug > 2) ! printk(KERN_WARNING "%s: The transmitter stopped." ! " CSR5 is %x, CSR6 %x, new CSR6 %x.\n", ! dev->name, csr5, inl(ioaddr + CSR6), tp->csr6); ! outl(tp->csr6 | 0x0002, ioaddr + CSR6); ! outl(tp->csr6 | 0x2002, ioaddr + CSR6); ! } } /* Log errors. */ --- 2779,2793 ---- } tp->dirty_tx = dirty_tx; ! } ! ! if (csr5 & TxDied) { ! if (tulip_debug > 2) ! printk(KERN_WARNING "%s: The transmitter stopped." ! " CSR5 is %x, CSR6 %x, new CSR6 %x.\n", ! dev->name, csr5, inl(ioaddr + CSR6), tp->csr6); ! outl(tp->csr6 | 0x0002, ioaddr + CSR6); ! outl(tp->csr6 | 0x2002, ioaddr + CSR6); } /* Log errors. */ *************** *** 2733,2740 **** outl(0, ioaddr + CSR1); } if (csr5 & RxDied) { /* Missed a Rx frame. */ tp->stats.rx_errors++; ! tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; outl(tp->csr6 | 0x2002, ioaddr + CSR6); } if (csr5 & (TPLnkPass | TPLnkFail | 0x08000000)) { --- 2806,2816 ---- outl(0, ioaddr + CSR1); } if (csr5 & RxDied) { /* Missed a Rx frame. */ + unsigned csr8status = inl(ioaddr+CSR8); + unsigned fifostatus = csr8status>>17; tp->stats.rx_errors++; ! tp->stats.rx_missed_errors += csr8status&0xffff; ! tp->stats.rx_fifo_errors += fifostatus&0x7ff; outl(tp->csr6 | 0x2002, ioaddr + CSR6); } if (csr5 & (TPLnkPass | TPLnkFail | 0x08000000)) { *************** *** 2774,2780 **** } } while (1); ! tulip_refill_rx(dev); /* check if we card is in suspend mode */ entry = tp->dirty_rx % RX_RING_SIZE; --- 2850,2858 ---- } } while (1); ! if (!dev->polling) { ! tulip_refill_rx(dev); ! } /* check if we card is in suspend mode */ entry = tp->dirty_rx % RX_RING_SIZE; *************** *** 2792,2805 **** --- 2870,2886 ---- } } + #if 0 if ((missed = inl(ioaddr + CSR8) & 0x1ffff)) { tp->stats.rx_dropped += missed & 0x10000 ? 0x10000 : missed; } + #endif if (tulip_debug > 4) printk(KERN_DEBUG "%s: exiting interrupt, csr5=%#4.4x.\n", dev->name, inl(ioaddr + CSR5)); + out: #if defined(__i386__) clear_bit(0, (void*)&dev->interrupt); #else *************** *** 2945,2952 **** if (tp->chip_id == DC21040) outl(0x00000004, ioaddr + CSR13); ! if (inl(ioaddr + CSR6) != 0xffffffff) ! tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; del_timer(&tp->timer); --- 3026,3037 ---- if (tp->chip_id == DC21040) outl(0x00000004, ioaddr + CSR13); ! if (inl(ioaddr + CSR6) != 0xffffffff) { ! unsigned csr8status = inl(ioaddr+CSR8); ! unsigned fifostatus = csr8status>>17; ! tp->stats.rx_missed_errors += csr8status&0xffff; ! tp->stats.rx_fifo_errors += fifostatus&0x7ff; ! } del_timer(&tp->timer); *************** *** 2989,2997 **** struct tulip_private *tp = (struct tulip_private *)dev->priv; long ioaddr = dev->base_addr; ! if (dev->start) ! tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; ! return &tp->stats; } --- 3074,3087 ---- struct tulip_private *tp = (struct tulip_private *)dev->priv; long ioaddr = dev->base_addr; ! if (dev->start) { ! unsigned csr8status = inl(ioaddr+CSR8); ! unsigned fifostatus = csr8status>>17; ! unsigned missed = csr8status & 0x1ffff; ! tp->stats.rx_dropped += missed & 0x10000 ? 0x10000 : missed; ! tp->stats.rx_missed_errors += csr8status&0xffff; ! tp->stats.rx_fifo_errors += fifostatus&0x7ff; ! } return &tp->stats; } *************** *** 3190,3196 **** /* Same setup recently queued, we need not add it. */ } else { unsigned long flags; ! unsigned int entry; save_flags(flags); cli(); entry = tp->cur_tx++ % TX_RING_SIZE; --- 3280,3286 ---- /* Same setup recently queued, we need not add it. */ } else { unsigned long flags; ! unsigned int entry, dummy = 0; save_flags(flags); cli(); entry = tp->cur_tx++ % TX_RING_SIZE; *************** *** 3201,3207 **** --- 3291,3302 ---- tp->tx_ring[entry].length = (entry == TX_RING_SIZE-1) ? cpu_to_le32(DESC_RING_WRAP) : 0; tp->tx_ring[entry].buffer1 = 0; + #if 1 + /* race with chip, set DescOwned later */ + dummy = entry; + #else tp->tx_ring[entry].status = cpu_to_le32(DescOwned); + #endif entry = tp->cur_tx++ % TX_RING_SIZE; } *************** *** 3216,3221 **** --- 3311,3320 ---- set_bit(0, (void*)&dev->tbusy); tp->tx_full = 1; } + #if 1 + if (dummy >= 0) + tp->tx_ring[dummy].status = cpu_to_le32(DescOwned); + #endif restore_flags(flags); /* Trigger an immediate transmit demand. */ outl(0, ioaddr + CSR1); *************** *** 3360,3362 **** --- 3459,3739 ---- * tab-width: 4 * End: */ + + + /* + * Polling Extension + * + * Most of the following functions are verbatim, or very similar to, code from + * the interrupt routines. They are cleaned up and tuned for polling. + * + * Very minimal synchronization occurs: most polling functions are suppose to + * be used in polling mode, under which case the interrupt handler is + * disallowed from touching the rx and tx ring. Callers of polling functions + * are expected to synchronize calls to these functions themselves. + * + * dev->tbusy was used by Linux's original tulip driver to synchronize the + * send pkt routine (tulip_start_xmit) and timer based send. I am using it + * also to synchronize tx queueing. + */ + + /* demand polling */ + #define DEMAND_POLLTX 1 + + int + tulip_rx_refill(struct device *dev, struct sk_buff **skbs) + { + struct tulip_private *tp = (struct tulip_private *)dev->priv; + struct sk_buff *skb_list; + + if (skbs == 0) + return tp->cur_rx - tp->dirty_rx; + + skb_list = *skbs; + /* Refill the Rx ring buffers. */ + for (; (tp->cur_rx-tp->dirty_rx) > 0 && skb_list != 0L; tp->dirty_rx++) { + int entry = tp->dirty_rx % RX_RING_SIZE; + if (tp->rx_skbuff[entry] == NULL) { + struct sk_buff *skb = skb_list; + skb_list = skb_list->next; + skb->prev = NULL; + skb->next = NULL; + skb->list = NULL; + tp->rx_skbuff[entry] = skb; + skb->dev = dev; /* mark as being used by this device. */ + tp->rx_ring[entry].buffer1 = virt_to_le32desc(skb->tail); + } + tp->rx_ring[entry].status = cpu_to_le32(DescOwned); + } + if (skb_list == 0) + *skbs = 0; + else + *skbs = skb_list; + return tp->cur_rx - tp->dirty_rx; + } + + static struct sk_buff * + tulip_rx_poll(struct device *dev, int *want) + { + struct tulip_private *tp = (struct tulip_private *)dev->priv; + int rx_work_limit = tp->dirty_rx + RX_RING_SIZE - tp->cur_rx; + int entry = tp->cur_rx % RX_RING_SIZE; + struct sk_buff *skb_head, *skb_last; + int got = 0; + + skb_head = skb_last = NULL; + + while (! (tp->rx_ring[entry].status & cpu_to_le32(DescOwned))) { + s32 status = le32_to_cpu(tp->rx_ring[entry].status); + if (--rx_work_limit < 0 || got == *want) break; + + if ((status & 0x38008300) != 0x0300) { + if ((status & 0x38000300) != 0x0300) { + /* Ignore earlier buffers. */ + if ((status & 0xffff) != 0x7fff) { + if (tulip_debug > 1) + printk(KERN_WARNING "%s: Oversized Ethernet frame " + "spanned " "multiple buffers, status %8.8x!\n", + dev->name, status); + tp->stats.rx_length_errors++; + } + } else if (status & RxDescFatalErr) { + /* There was a fatal error */ + if (tulip_debug > 2) + printk(KERN_DEBUG "%s: Receive error, Rx status %8.8x.\n", + dev->name, status); + tp->stats.rx_errors++; /* end of a packet.*/ + if (status & 0x0890) tp->stats.rx_length_errors++; + if (status & 0x0004) tp->stats.rx_frame_errors++; + if (status & 0x0002) tp->stats.rx_crc_errors++; + if (status & 0x0001) tp->stats.rx_fifo_errors++; + } + } else { + /* Omit the four octet CRC from the length. */ + short pkt_len = ((status >> 16) & 0x7ff) - 4; + struct sk_buff *skb = tp->rx_skbuff[entry]; + int p; + tp->rx_skbuff[entry] = NULL; + + skb_put(skb, pkt_len); + skb->protocol = eth_type_trans(skb, dev); + tp->stats.rx_packets++; + tp->stats.rx_bytes += pkt_len; + + if (got == 0) { + skb_head = skb; + skb_last = skb; + skb_last->next = NULL; + skb_last->prev = NULL; + } else { + skb_last->next = skb; + skb->prev = skb_last; + skb->next = NULL; + skb_last = skb; + } + got++; + } + entry = (++tp->cur_rx) % RX_RING_SIZE; + } + dev->last_rx = jiffies; + *want = got; + return skb_head; + } + + + static int + tulip_poll_on(struct device *dev) + { + long ioaddr = dev->base_addr; + int csr7; + #ifdef DEMAND_POLLTX + int csr0; + #endif + if (!dev->polling) { + csr7 = inl(ioaddr + CSR7) & ~(NormalIntr|RxNoBuf|RxIntr|TxIntr|TxNoBuf); + outl(csr7, ioaddr+CSR7); + #ifdef DEMAND_POLLTX + csr0 = (inl(ioaddr + CSR0) & ~(7<<17)) | (4<<17); + outl(csr0, ioaddr+CSR0); + #endif + dev->polling = 2; + } + return 0; + } + + static int + tulip_poll_off(struct device *dev) + { + long ioaddr = dev->base_addr; + int csr7; + #ifdef DEMAND_POLLTX + int csr0; + #endif + if (dev->polling > 0) { + #ifdef DEMAND_POLLTX + csr0 = inl(ioaddr + CSR0) & ~(7<<17); + outl(csr0, ioaddr+CSR0); + #endif + csr7 = inl(ioaddr + CSR7) | (NormalIntr|RxNoBuf|RxIntr|TxIntr|TxNoBuf); + outl(csr7, ioaddr+CSR7); + dev->polling = 0; + } + return 0; + } + + static int + tulip_tx_queue(struct device *dev, struct sk_buff *skb) + { + struct tulip_private *tp = (struct tulip_private *)dev->priv; + int entry; + u32 flag; + + if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { + /* printk("tulip_tx_queue: reject because tbusy\n"); */ + return 1; + } + + /* Caution: the write order is important here, set the base address + * with the "ownership" bits last. */ + + /* Calculate the next Tx descriptor entry. */ + entry = tp->cur_tx % TX_RING_SIZE; + + tp->tx_skbuff[entry] = skb; + tp->tx_ring[entry].buffer1 = virt_to_le32desc(skb->data); + + flag = 0x60000000; /* No interrupt */ + + if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE - 2) + tp->tx_full = 0; + else + /* Leave room for set_rx_mode() to fill entries. */ + tp->tx_full = 1; + + if (entry == TX_RING_SIZE-1) + flag = 0xe0000000 | DESC_RING_WRAP; + + tp->tx_ring[entry].length = cpu_to_le32(skb->len | flag); + /* new linux memory barrier thingie */ + wmb(); + /* Pass ownership to the chip. */ + tp->tx_ring[entry].status = cpu_to_le32(DescOwned); + tp->cur_tx++; + + #ifndef DEMAND_POLLTX + outl(0, dev->base_addr + CSR1); + dev->trans_start = jiffies; + #endif + if (!tp->tx_full) + clear_bit(0, (void*)&dev->tbusy); + + return 0; + } + + /* clean up tx dma ring */ + static struct sk_buff* + tulip_tx_clean(struct device *dev) + { + struct sk_buff *skb_head, *skb_last; + struct tulip_private *tp; + unsigned int dirty_tx; + tp = (struct tulip_private *)dev->priv; + skb_head = skb_last = 0; + + for (dirty_tx = tp->dirty_tx; tp->cur_tx - dirty_tx > 0; dirty_tx++) { + int entry = dirty_tx % TX_RING_SIZE; + int status = le32_to_cpu(tp->tx_ring[entry].status); + struct sk_buff *skb; + + if (status < 0) break; /* It still hasn't been Txed */ + + /* Check for Rx filter setup frames. */ + if (tp->tx_skbuff[entry] == NULL) continue; + + if (status & 0x8000) { + /* There was an major error, log it. */ + tp->stats.tx_errors++; + if (status & 0x4104) tp->stats.tx_aborted_errors++; + if (status & 0x0C00) tp->stats.tx_carrier_errors++; + if (status & 0x0200) tp->stats.tx_window_errors++; + if (status & 0x0002) tp->stats.tx_fifo_errors++; + if ((status & 0x0080) && tp->full_duplex == 0) + tp->stats.tx_heartbeat_errors++; + } else { + tp->stats.tx_bytes += tp->tx_ring[entry].length & 0x7ff; + tp->stats.collisions += (status >> 3) & 15; + tp->stats.tx_packets++; + } + + skb = tp->tx_skbuff[entry]; + tp->tx_skbuff[entry] = 0; + + if (skb_head == 0) { + skb_head = skb; + skb_last = skb; + skb_last->next = NULL; + skb_last->prev = NULL; + } else { + skb_last->next = skb; + skb->prev = skb_last; + skb->next = NULL; + skb_last = skb; + } + } + + if (tp->tx_full && dev->tbusy && tp->cur_tx-dirty_tx < TX_RING_SIZE-2) { + /* The ring is no longer full, clear tbusy. */ + tp->tx_full = 0; + clear_bit(0, (void*)&dev->tbusy); + } + + tp->dirty_tx = dirty_tx; + // ret = tp->cur_tx - tp->dirty_tx; + return skb_head; + } + + static int tulip_tx_eob(struct device *dev) + { + return 0; + } + diff -r -c --exclude=*.o --exclude=*.flags ../linux-2.2.19-pure/fs/proc/inode.c ./fs/proc/inode.c *** ../linux-2.2.19-pure/fs/proc/inode.c Sun Mar 25 08:30:58 2001 --- ./fs/proc/inode.c Sat May 4 23:13:01 2002 *************** *** 265,270 **** --- 265,275 ---- if (inode->i_sb != sb) printk("proc_get_inode: inode fubar\n"); + /* Click change: don't double-increment de's use count if the inode + * existed already */ + if (inode->u.generic_ip == (void *) de) + de_put(de); + inode->u.generic_ip = (void *) de; if (de) { if (de->mode) { diff -r -c --exclude=*.o --exclude=*.flags ../linux-2.2.19-pure/fs/super.c ./fs/super.c *** ../linux-2.2.19-pure/fs/super.c Sun Mar 25 08:30:58 2001 --- ./fs/super.c Sat May 4 23:17:23 2002 *************** *** 565,570 **** --- 565,571 ---- s->s_dev = dev; s->s_flags = flags; s->s_dirt = 0; + s->s_type = type; sema_init(&s->s_vfs_rename_sem,1); sema_init(&s->s_nfsd_free_path_sem,1); /* N.B. Should lock superblock now ... */ *************** *** 572,578 **** goto out_fail; s->s_dev = dev; /* N.B. why do this again?? */ s->s_rd_only = 0; - s->s_type = type; out: return s; --- 573,578 ---- diff -r -c --exclude=*.o --exclude=*.flags ../linux-2.2.19-pure/include/linux/netdevice.h ./include/linux/netdevice.h *** ../linux-2.2.19-pure/include/linux/netdevice.h Sun Mar 25 08:31:03 2001 --- ./include/linux/netdevice.h Sat May 4 23:13:01 2002 *************** *** 324,329 **** --- 324,370 ---- /* this will get initialized at each interface type init routine */ struct divert_blk *divert; #endif /* CONFIG_NET_DIVERT */ + + /* Click polling support */ + /* + * polling is < 0 if the device does not support polling, == 0 if the + * device supports polling but interrupts are on, and > 0 if polling + * is on. + */ + int polling; + int (*poll_on)(struct device *); + int (*poll_off)(struct device *); + /* + * rx_poll returns to caller a linked list of sk_buff objects received + * by the device. on call, the want argument specifies the number of + * packets wanted. on return, the want argument specifies the number + * of packets actually returned. + */ + struct sk_buff * (*rx_poll)(struct device*, int *want); + /* + * refill rx dma ring using the given sk_buff list. returns 0 if + * successful, or if there are more entries need to be cleaned, + * returns the number of dirty entries. the ptr to the sk_buff list is + * updated by the driver to point to any unused skbs. + */ + int (*rx_refill)(struct device*, struct sk_buff**); + /* + * place sk_buff on the transmit ring. returns 0 if successful, 1 + * otherwise + */ + int (*tx_queue)(struct device *, struct sk_buff *); + /* + * clean tx dma ring. returns the list of skb objects cleaned + */ + struct sk_buff* (*tx_clean)(struct device *); + /* + * start transmission. returns 0 if successful, 1 otherwise + */ + int (*tx_start)(struct device *); + /* + * tell device the end of a batch of packets + */ + int (*tx_eob)(struct device *); }; *************** *** 361,366 **** --- 402,410 ---- extern int unregister_netdevice(struct device *dev); extern int register_netdevice_notifier(struct notifier_block *nb); extern int unregister_netdevice_notifier(struct notifier_block *nb); + extern int register_net_in(struct notifier_block *nb); /* Click */ + extern int unregister_net_in(struct notifier_block *nb); /* Click */ + extern void ptype_dispatch(struct sk_buff *skb, unsigned short type); /* Click */ extern int dev_new_index(void); extern struct device *dev_get_by_index(int ifindex); extern int dev_restart(struct device *dev); diff -r -c --exclude=*.o --exclude=*.flags ../linux-2.2.19-pure/include/linux/signal.h ./include/linux/signal.h *** ../linux-2.2.19-pure/include/linux/signal.h Sun Mar 25 08:31:03 2001 --- ./include/linux/signal.h Sat May 4 23:13:01 2002 *************** *** 189,195 **** memset(&set->sig[1], 0, sizeof(long)*(_NSIG_WORDS-1)); break; case 2: set->sig[1] = 0; ! case 1: } } --- 189,195 ---- memset(&set->sig[1], 0, sizeof(long)*(_NSIG_WORDS-1)); break; case 2: set->sig[1] = 0; ! case 1: ; } } *************** *** 201,207 **** memset(&set->sig[1], -1, sizeof(long)*(_NSIG_WORDS-1)); break; case 2: set->sig[1] = -1; ! case 1: } } --- 201,207 ---- memset(&set->sig[1], -1, sizeof(long)*(_NSIG_WORDS-1)); break; case 2: set->sig[1] = -1; ! case 1: ; } } diff -r -c --exclude=*.o --exclude=*.flags ../linux-2.2.19-pure/include/linux/skbuff.h ./include/linux/skbuff.h *** ../linux-2.2.19-pure/include/linux/skbuff.h Sun Mar 25 08:31:03 2001 --- ./include/linux/skbuff.h Sat May 4 23:13:01 2002 *************** *** 36,49 **** for using debugging */ }; struct sk_buff { struct sk_buff * next; /* Next buffer in list */ struct sk_buff * prev; /* Previous buffer in list */ struct sk_buff_head * list; /* List we are on */ ! struct sock *sk; /* Socket we are owned by */ ! struct timeval stamp; /* Time we arrived */ struct device *dev; /* Device we arrived on/are leaving by */ ! /* Transport layer header */ union { --- 36,65 ---- for using debugging */ }; + /* Click: overload sk_buff.pkt_type to contain information about whether + a packet is clean. Clean packets have the following fields zero: + dst, destructor, pkt_bridged, prev, list, sk, security, priority. */ + #define PACKET_CLEAN 128 /* Is packet clean? */ + #define PACKET_TYPE_MASK 127 /* Actual packet type */ + + /* Click: change sk_buff structure so all fields used for router are grouped + * together on one cache line, we hope */ struct sk_buff { struct sk_buff * next; /* Next buffer in list */ struct sk_buff * prev; /* Previous buffer in list */ struct sk_buff_head * list; /* List we are on */ ! unsigned int len; /* Length of actual data */ ! unsigned char *data; /* Data head pointer */ ! unsigned char *tail; /* Tail pointer */ struct device *dev; /* Device we arrived on/are leaving by */ ! unsigned char is_clone, /* We are a clone */ ! cloned, /* head may be cloned (check refcnt to be sure). */ ! pkt_type, /* Packet class */ ! ip_summed; /* Driver fed us an IP checksum */ ! atomic_t users; /* User count - see datagram.c,tcp.c */ ! unsigned int truesize; /* Buffer size */ ! unsigned char *head; /* Head of buffer */ ! unsigned char *end; /* End pointer */ /* Transport layer header */ union { *************** *** 72,101 **** struct ethhdr *ethernet; unsigned char *raw; } mac; struct dst_entry *dst; ! char cb[48]; ! ! unsigned int len; /* Length of actual data */ ! unsigned int csum; /* Checksum */ volatile char used; /* Data moved to user and not MSG_PEEK */ ! unsigned char is_clone, /* We are a clone */ ! cloned, /* head may be cloned (check refcnt to be sure). */ ! pkt_type, /* Packet class */ ! pkt_bridged, /* Tracker for bridging */ ! ip_summed; /* Driver fed us an IP checksum */ __u32 priority; /* Packet queueing priority */ - atomic_t users; /* User count - see datagram.c,tcp.c */ unsigned short protocol; /* Packet protocol from driver. */ unsigned short security; /* Security level of packet */ - unsigned int truesize; /* Buffer size */ - - unsigned char *head; /* Head of buffer */ - unsigned char *data; /* Data head pointer */ - unsigned char *tail; /* Tail pointer */ - unsigned char *end; /* End pointer */ - void (*destructor)(struct sk_buff *); /* Destruct function */ #ifdef CONFIG_IP_FIREWALL __u32 fwmark; /* Label made by fwchains, used by pktsched */ #endif --- 88,107 ---- struct ethhdr *ethernet; unsigned char *raw; } mac; + + char cb[48]; struct dst_entry *dst; ! void (*destructor)(struct sk_buff *); /* Destruct function */ ! struct sock *sk; /* Socket we are owned by */ ! struct timeval stamp; /* Time we arrived */ volatile char used; /* Data moved to user and not MSG_PEEK */ ! unsigned char pkt_bridged; /* Tracker for bridging */ ! unsigned int csum; /* Checksum */ __u32 priority; /* Packet queueing priority */ unsigned short protocol; /* Packet protocol from driver. */ unsigned short security; /* Security level of packet */ #ifdef CONFIG_IP_FIREWALL __u32 fwmark; /* Label made by fwchains, used by pktsched */ #endif *************** *** 145,150 **** --- 151,158 ---- extern struct sk_buff * skb_peek_copy(struct sk_buff_head *list); extern struct sk_buff * alloc_skb(unsigned int size, int priority); extern struct sk_buff * dev_alloc_skb(unsigned int size); + extern void skb_recycled_init(struct sk_buff *buf); + extern struct sk_buff * skb_recycle(struct sk_buff *buf); extern void kfree_skbmem(struct sk_buff *skb); extern struct sk_buff * skb_clone(struct sk_buff *skb, int priority); extern struct sk_buff * skb_copy(struct sk_buff *skb, int priority); *************** *** 491,497 **** return skb->data; } ! extern __inline__ char *__skb_pull(struct sk_buff *skb, unsigned int len) { skb->len-=len; return skb->data+=len; --- 499,505 ---- return skb->data; } ! extern __inline__ unsigned char *__skb_pull(struct sk_buff *skb, unsigned int len) { skb->len-=len; return skb->data+=len; *************** *** 551,560 **** extern __inline__ struct sk_buff *dev_alloc_skb(unsigned int length) { struct sk_buff *skb; ! skb = alloc_skb(length+16, GFP_ATOMIC); if (skb) ! skb_reserve(skb,16); return skb; } --- 559,573 ---- extern __inline__ struct sk_buff *dev_alloc_skb(unsigned int length) { struct sk_buff *skb; + #if 0 + #define SKB_RESERVE_LENGTH 32 + #else + #define SKB_RESERVE_LENGTH 16 + #endif ! skb = alloc_skb(length+SKB_RESERVE_LENGTH, GFP_ATOMIC); if (skb) ! skb_reserve(skb,SKB_RESERVE_LENGTH); return skb; } diff -r -c --exclude=*.o --exclude=*.flags ../linux-2.2.19-pure/include/net/neighbour.h ./include/net/neighbour.h *** ../linux-2.2.19-pure/include/net/neighbour.h Sun Mar 25 08:31:08 2001 --- ./include/net/neighbour.h Sat May 4 23:13:01 2002 *************** *** 50,55 **** --- 50,56 ---- #define NUD_VALID (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE|NUD_PROBE|NUD_STALE|NUD_DELAY) #define NUD_CONNECTED (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE) + struct neighbour; struct neigh_parms { struct neigh_parms *next; diff -r -c --exclude=*.o --exclude=*.flags ../linux-2.2.19-pure/kernel/ksyms.c ./kernel/ksyms.c *** ../linux-2.2.19-pure/kernel/ksyms.c Sun Mar 25 08:31:02 2001 --- ./kernel/ksyms.c Sat May 4 23:17:42 2002 *************** *** 342,347 **** --- 342,348 ---- /* process management */ EXPORT_SYMBOL(__wake_up); + EXPORT_SYMBOL(wake_up_process); EXPORT_SYMBOL(sleep_on); EXPORT_SYMBOL(sleep_on_timeout); EXPORT_SYMBOL(interruptible_sleep_on); *************** *** 415,420 **** --- 416,425 ---- EXPORT_SYMBOL(__down_trylock); EXPORT_SYMBOL(__up); EXPORT_SYMBOL(brw_page); + EXPORT_SYMBOL(inuse_filps); + EXPORT_SYMBOL(super_blocks); + extern spinlock_t inode_lock; + EXPORT_SYMBOL(inode_lock); /* all busmice */ EXPORT_SYMBOL(add_mouse_randomness); diff -r -c --exclude=*.o --exclude=*.flags ../linux-2.2.19-pure/net/core/dev.c ./net/core/dev.c *** ../linux-2.2.19-pure/net/core/dev.c Sun Mar 25 08:37:41 2001 --- ./net/core/dev.c Sat May 4 23:13:01 2002 *************** *** 154,159 **** --- 154,162 ---- static struct notifier_block *netdev_chain=NULL; + /* input packet handlers -- might steal packets from net_bh(). for Click. */ + static struct notifier_block *net_in_chain = 0; + /* * Device drivers call our routines to queue packets here. We empty the * queue in the bottom half handler. *************** *** 335,340 **** --- 338,345 ---- kfree(dev); return NULL; } + /* by default, no polling support */ + dev->poll_on = dev->poll_off = 0; return dev; } *************** *** 516,521 **** --- 521,542 ---- } /* + * Allow Click modules to ask to intercept input packets. + * Must add these to ../netsyms.c + */ + int + register_net_in(struct notifier_block *nb) + { + return(notifier_chain_register(&net_in_chain, nb)); + } + + int + unregister_net_in(struct notifier_block *nb) + { + return(notifier_chain_unregister(&net_in_chain, nb)); + } + + /* * Support routine. Sends outgoing frames to any network * taps currently in use. */ *************** *** 852,857 **** --- 873,958 ---- } #endif /* CONFIG_NET_DIVERT */ + + /* + * Hand a packet to the ordinary Linux protocol stack. + * Broke this out from net_bh() so that Click can call it. + * Always frees the skb one way or another. + * + * skb->pkt_type needs to be set to PACKET_{BROADCAST,MULTICAST,OTHERHOST} + * maybe skb->mac.raw must point to ether header. + * skb->protocol must be set to a htons(ETHERTYPE_?). + * skb->data must point to the ethernet payload (e.g. the IP header). + * skb->nh.raw must point to the ethernet payload also. + */ + void + ptype_dispatch(struct sk_buff *skb, unsigned short type) + { + struct packet_type *ptype; + struct packet_type *pt_prev; + + /* + * We got a packet ID. Now loop over the "known protocols" + * list. There are two lists. The ptype_all list of taps (normally empty) + * and the main protocol list which is hashed perfectly for normal protocols. + */ + + pt_prev = NULL; + for (ptype = ptype_all; ptype!=NULL; ptype=ptype->next) + { + if (!ptype->dev || ptype->dev == skb->dev) { + if(pt_prev) + { + struct sk_buff *skb2=skb_clone(skb, GFP_ATOMIC); + if(skb2) + pt_prev->func(skb2,skb->dev, pt_prev); + } + pt_prev=ptype; + } + } + + for (ptype = ptype_base[ntohs(type)&15]; ptype != NULL; ptype = ptype->next) + { + if (ptype->type == type && (!ptype->dev || ptype->dev==skb->dev)) + { + /* + * We already have a match queued. Deliver + * to it and then remember the new match + */ + if(pt_prev) + { + struct sk_buff *skb2; + + skb2=skb_clone(skb, GFP_ATOMIC); + + /* + * Kick the protocol handler. This should be fast + * and efficient code. + */ + + if(skb2) + pt_prev->func(skb2, skb->dev, pt_prev); + } + /* Remember the current last to do */ + pt_prev=ptype; + } + } /* End of protocol list loop */ + + /* + * Is there a last item to send to ? + */ + + if(pt_prev) + pt_prev->func(skb, skb->dev, pt_prev); + /* + * Has an unknown packet has been received ? + */ + + else { + kfree_skb(skb); + } + } + /* * When we are called the queue is ready to grab, the interrupts are * on and hardware can interrupt and queue to the receive queue as we *************** *** 862,869 **** void net_bh(void) { - struct packet_type *ptype; - struct packet_type *pt_prev; unsigned short type; unsigned long start_time = jiffies; #ifdef CONFIG_CPU_IS_SLOW --- 963,968 ---- *************** *** 979,1044 **** handle_bridge(skb, type); #endif ! /* ! * We got a packet ID. Now loop over the "known protocols" ! * list. There are two lists. The ptype_all list of taps (normally empty) ! * and the main protocol list which is hashed perfectly for normal protocols. ! */ ! ! pt_prev = NULL; ! for (ptype = ptype_all; ptype!=NULL; ptype=ptype->next) ! { ! if (!ptype->dev || ptype->dev == skb->dev) { ! if(pt_prev) ! { ! struct sk_buff *skb2=skb_clone(skb, GFP_ATOMIC); ! if(skb2) ! pt_prev->func(skb2,skb->dev, pt_prev); ! } ! pt_prev=ptype; ! } ! } ! ! for (ptype = ptype_base[ntohs(type)&15]; ptype != NULL; ptype = ptype->next) ! { ! if (ptype->type == type && (!ptype->dev || ptype->dev==skb->dev)) ! { ! /* ! * We already have a match queued. Deliver ! * to it and then remember the new match ! */ ! if(pt_prev) ! { ! struct sk_buff *skb2; - skb2=skb_clone(skb, GFP_ATOMIC); ! /* ! * Kick the protocol handler. This should be fast ! * and efficient code. ! */ - if(skb2) - pt_prev->func(skb2, skb->dev, pt_prev); - } - /* Remember the current last to do */ - pt_prev=ptype; - } - } /* End of protocol list loop */ - /* - * Is there a last item to send to ? - */ - if(pt_prev) - pt_prev->func(skb, skb->dev, pt_prev); - /* - * Has an unknown packet has been received ? - */ - - else { - kfree_skb(skb); - } } /* End of queue loop */ /* --- 1078,1096 ---- handle_bridge(skb, type); #endif ! /* does Click want to steal this packet? */ ! if(notifier_call_chain(&net_in_chain, skb_queue_len(&backlog), skb) & NOTIFY_STOP_MASK) ! continue; ! /* ! * Ordinary Linux dispatch based on packet type. ! * Moved into a function so Click can call it. ! */ ! ptype_dispatch(skb, type); } /* End of queue loop */ /* diff -r -c --exclude=*.o --exclude=*.flags ../linux-2.2.19-pure/net/core/skbuff.c ./net/core/skbuff.c *** ../linux-2.2.19-pure/net/core/skbuff.c Sun Mar 25 08:31:12 2001 --- ./net/core/skbuff.c Sat May 4 23:13:01 2002 *************** *** 257,262 **** --- 257,300 ---- return n; } + + /* Click: attempts to recycle a sk_buff. if it can be recycled, return it + * without reinitializing any bits */ + struct sk_buff *skb_recycle(struct sk_buff *skb) + { + if (atomic_dec_and_test(&skb->users)) { + + dst_release(skb->dst); + if (skb->destructor) + skb->destructor(skb); + skb_headerinit(skb, NULL, 0); + + if (!skb->cloned || atomic_dec_and_test(skb_datarefp(skb))) { + /* Load the data pointers. */ + skb->data = skb->head; + skb->tail = skb->data; + /* end and truesize should have never changed */ + /* skb->end = skb->data + skb->truesize; */ + + /* set up other state */ + skb->len = 0; + skb->is_clone = 0; + skb->cloned = 0; + + atomic_set(&skb->users, 1); + atomic_set(skb_datarefp(skb), 1); + + return skb; + } + + kmem_cache_free(skbuff_head_cache, skb); + atomic_dec(&net_skbcount); + } + + return 0; + } + + /* * This is slower, and copies the whole data area */ diff -r -c --exclude=*.o --exclude=*.flags ../linux-2.2.19-pure/net/netsyms.c ./net/netsyms.c *** ../linux-2.2.19-pure/net/netsyms.c Sun Mar 25 08:37:41 2001 --- ./net/netsyms.c Sat May 4 23:13:01 2002 *************** *** 464,469 **** --- 464,476 ---- EXPORT_SYMBOL(unregister_inetaddr_notifier); #endif + /* Click */ + EXPORT_SYMBOL(register_net_in); + EXPORT_SYMBOL(unregister_net_in); + EXPORT_SYMBOL(ptype_dispatch); + struct inet_protocol *inet_get_protocol(unsigned char prot); + EXPORT_SYMBOL(inet_get_protocol); + /* support for loadable net drivers */ #ifdef CONFIG_NET EXPORT_SYMBOL(loopback_dev); *************** *** 487,492 **** --- 494,500 ---- EXPORT_SYMBOL(eth_copy_and_sum); EXPORT_SYMBOL(alloc_skb); EXPORT_SYMBOL(__kfree_skb); + EXPORT_SYMBOL(skb_recycle); EXPORT_SYMBOL(skb_clone); EXPORT_SYMBOL(skb_copy); EXPORT_SYMBOL(netif_rx);