diff --git a/.rootkeys b/.rootkeys index fbeff2f687..f502859972 100644 --- a/.rootkeys +++ b/.rootkeys @@ -77,10 +77,8 @@ 3ddb79bdkDY1bSOYkToP1Cc49VdBxg xen-2.4.16/drivers/ide/ide.c 3ddb79bdPyAvT_WZTAFhaX0jp-yXSw xen-2.4.16/drivers/ide/ide_modes.h 3e4a8d401aSwOzCScQXR3lsmNlAwUQ xen-2.4.16/drivers/ide/piix.c -3ddb79bfogeJNHTIepPjd8fy1TyoTw xen-2.4.16/drivers/net/3c509.c 3ddb79bfMlOcWUwjtg6oMYhGySHDDw xen-2.4.16/drivers/net/3c59x.c 3ddb79bfl_DWxZQFKiJ2BXrSedV4lg xen-2.4.16/drivers/net/8139cp.c -3ddb79bfLVGtyXNJS4NQg-lP21rndA xen-2.4.16/drivers/net/8139too.c 3ddb79c0tWiE8xIFHszxipeVCGKTSA xen-2.4.16/drivers/net/Makefile 3ddb79bfU-H1Hms4BuJEPPydjXUEaQ xen-2.4.16/drivers/net/Space.c 3e4540ccS4bfbx9rLiLElP0F1OVwZA xen-2.4.16/drivers/net/e1000/LICENSE @@ -160,6 +158,7 @@ 3e20b82fl1jmQiKdLy7fxMcutfpjWA xen-2.4.16/include/asm-i386/domain_page.h 3ddb79c2O729EttZTYu1c8LcsUO_GQ xen-2.4.16/include/asm-i386/elf.h 3ddb79c3NU8Zy40OTrq3D-i30Y3t4A xen-2.4.16/include/asm-i386/fixmap.h +3e2d29944GI24gf7vOP_7x8EyuqxeA xen-2.4.16/include/asm-i386/flushtlb.h 3ddb79c39o75zPP0T1aQQ4mNrCAN2w xen-2.4.16/include/asm-i386/hardirq.h 3ddb79c3BFEIwXR4IsWbwp4BoL4DkA xen-2.4.16/include/asm-i386/hdreg.h 3ddb79c3TMDjkxVndKFKnGiwY0HzDg xen-2.4.16/include/asm-i386/i387.h @@ -442,6 +441,7 @@ 3ddb79bbA52x94o6uwDYsbzrH2hjzA xenolinux-2.4.16-sparse/include/asm-xeno/xor.h 3e4a8cb7ON8EclY3NN3YPXyMT941hA xenolinux-2.4.16-sparse/include/linux/blk.h 3e4a8cb7GJrKD0z7EF0VZOhdEa01Mw xenolinux-2.4.16-sparse/include/linux/major.h +3e37c39fVCSGQENtY6g7muaq_THliw xenolinux-2.4.16-sparse/include/linux/skbuff.h 3ddb79bb_7YG4U75ZmEic9YXWTW7Vw xenolinux-2.4.16-sparse/include/linux/sunrpc/debug.h 3e4a8cb7j05wwb1uPZgY16s68o7qAw xenolinux-2.4.16-sparse/init/main.c 3ddb79bcxkVPfWlZ1PQKvDrfArzOVw xenolinux-2.4.16-sparse/kernel/panic.c @@ -449,3 +449,4 @@ 3e15d52e0_j129JPvo7xfYGndVFpwQ xenolinux-2.4.16-sparse/mm/memory.c 3e15d535DLvpzTrLRUIerB69LpJD1g xenolinux-2.4.16-sparse/mm/mremap.c 3e15d531m1Y1_W8ki64AFOU_ua4C4w xenolinux-2.4.16-sparse/mm/swapfile.c +3e37c312QFuzIxXsuAgO6IRt3Tp96Q xenolinux-2.4.16-sparse/net/core/skbuff.c diff --git a/BitKeeper/etc/logging_ok b/BitKeeper/etc/logging_ok index cca59e5d2c..f4f9934995 100644 --- a/BitKeeper/etc/logging_ok +++ b/BitKeeper/etc/logging_ok @@ -1,5 +1,6 @@ akw27@boulderdash.cl.cam.ac.uk akw27@labyrinth.cl.cam.ac.uk +akw27@plucky.localdomain bd240@boulderdash.cl.cam.ac.uk iap10@labyrinth.cl.cam.ac.uk kaf24@labyrinth.cl.cam.ac.uk diff --git a/xen-2.4.16/common/domain.c b/xen-2.4.16/common/domain.c index 70887bfd8d..cb24e0cdce 100644 --- a/xen-2.4.16/common/domain.c +++ b/xen-2.4.16/common/domain.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -175,10 +176,13 @@ unsigned int alloc_new_dom_mem(struct task_struct *p, unsigned int kbytes) struct pfn_info *pf; unsigned int alloc_pfns; unsigned int req_pages; + unsigned long flags; /* how many pages do we need to alloc? */ req_pages = kbytes >> (PAGE_SHIFT - 10); + spin_lock_irqsave(&free_list_lock, flags); + /* is there enough mem to serve the request? */ if ( req_pages > free_pfns ) return -1; @@ -194,6 +198,8 @@ unsigned int alloc_new_dom_mem(struct task_struct *p, unsigned int kbytes) list_add_tail(&pf->list, &p->pg_head); free_pfns--; } + + spin_unlock_irqrestore(&free_list_lock, flags); p->tot_pages = req_pages; @@ -350,6 +356,7 @@ static unsigned long alloc_page_from_domain(unsigned long * cur_addr, */ int setup_guestos(struct task_struct *p, dom0_newdomain_t *params) { + struct list_head *list_ent; char *src, *dst; int i, dom = p->domain; @@ -517,8 +524,7 @@ int setup_guestos(struct task_struct *p, dom0_newdomain_t *params) /* Install the new page tables. */ __cli(); - __asm__ __volatile__ ( - "mov %%eax,%%cr3" : : "a" (pagetable_val(p->mm.pagetable))); + __write_cr3_counted(pagetable_val(p->mm.pagetable)); /* Copy the guest OS image. */ src = (char *)__va(mod[0].mod_start + 12); @@ -594,8 +600,7 @@ int setup_guestos(struct task_struct *p, dom0_newdomain_t *params) } /* Reinstate the caller's page tables. */ - __asm__ __volatile__ ( - "mov %%eax,%%cr3" : : "a" (pagetable_val(current->mm.pagetable))); + __write_cr3_counted(pagetable_val(current->mm.pagetable)); __sti(); new_thread(p, diff --git a/xen-2.4.16/common/event.c b/xen-2.4.16/common/event.c index 4514d02eb3..4cc2c70287 100644 --- a/xen-2.4.16/common/event.c +++ b/xen-2.4.16/common/event.c @@ -14,14 +14,14 @@ typedef void (*hyp_event_callback_fn_t)(void); extern void schedule(void); -extern void flush_rx_queue(void); extern void flush_blk_queue(void); +extern void update_shared_ring(void); /* Ordering must match definitions of _HYP_EVENT_* in xeno/sched.h */ static hyp_event_callback_fn_t event_call_fn[] = { schedule, - flush_rx_queue, + update_shared_ring, kill_domain, flush_blk_queue }; diff --git a/xen-2.4.16/common/kernel.c b/xen-2.4.16/common/kernel.c index 002f99e4c5..9e3a6846fa 100644 --- a/xen-2.4.16/common/kernel.c +++ b/xen-2.4.16/common/kernel.c @@ -14,6 +14,7 @@ #include #include #include +#include /* VGA text definitions. */ #define COLUMNS 80 @@ -301,11 +302,11 @@ void panic(const char *fmt, ...) char buf[1024], *p; unsigned long flags; extern void machine_restart(char *); - + va_start(args, fmt); (void)vsnprintf(buf, sizeof(buf), fmt, args); va_end(args); - + /* Spit out multiline message in one go. */ spin_lock_irqsave(&console_lock, flags); __putstr("\n****************************************\n"); @@ -357,6 +358,7 @@ unsigned short compute_cksum(unsigned short *buf, int count) /* XXX SMH: below is rather vile; pulled in to allow network console */ extern int netif_rx(struct sk_buff *); +extern struct net_device *the_dev; typedef struct my_udphdr { __u16 source; @@ -407,17 +409,24 @@ int console_export(char *str, int len) struct my_udphdr *udph = NULL; struct my_ethhdr *ethh = NULL; int hdr_size = sizeof(struct my_iphdr) + sizeof(struct my_udphdr); - - // Prepare console packet - console_packet = alloc_skb(sizeof(struct my_ethhdr) + hdr_size + len, - GFP_KERNEL); + u8 *skb_data; + + // Prepare console packet - the grim + 20 in the alloc is for headroom. + console_packet = dev_alloc_skb(sizeof(struct my_ethhdr) + hdr_size + len + 20); + if (!console_packet) return 0; +//console_packet->security = 9; // hack to trace these packets. + console_packet->dev = the_dev; + skb_data = map_domain_mem((unsigned long)console_packet->head); + skb_reserve(console_packet, 2); // ip header alignment. +//printk("Eth is: %d\n", console_packet->data - console_packet->head); + ethh = (struct my_ethhdr *) skb_data + (console_packet->data - console_packet->head); skb_reserve(console_packet, sizeof(struct my_ethhdr)); - ethh = (struct my_ethhdr *)console_packet->head; skb_put(console_packet, hdr_size + len); - iph = (struct my_iphdr *)console_packet->data; - udph = (struct my_udphdr *)(iph + 1); - memcpy((char *)(udph + 1), str, len); +//printk("IP is: %d\n", console_packet->data - console_packet->head); + iph = (struct my_iphdr *)skb_data + (console_packet->data - console_packet->head); + udph = (struct my_udphdr *)(iph + 1); + memcpy((char *)(udph + 1), str, len); // Build IP header iph->version = 4; @@ -446,6 +455,13 @@ int console_export(char *str, int len) memcpy(ethh->h_dest, "000000", 6); ethh->h_proto = htons(ETH_P_IP); console_packet->mac.ethernet= (struct ethhdr *)ethh; + + // Make the packet appear to come off the external NIC so that the + // tables code doesn't get too confused. + console_packet->src_vif = VIF_PHYSICAL_INTERFACE; + console_packet->dst_vif = 0; + + unmap_domain_mem(skb_data); // Pass the packet to netif_rx (void)netif_rx(console_packet); @@ -481,23 +497,14 @@ long do_console_write(char *str, int count) if ( !safe_str[i] ) break; putchar(prev = safe_str[i]); - - if ( prev == '\n' ) - { - exported_str[j]='\0'; - console_export(exported_str, j-1); - j=0; - } - - } - if ( prev != '\n' ) - { - putchar('\n'); - exported_str[j]='\0'; - console_export(exported_str, j-1); } + if ( prev != '\n' ) putchar('\n'); + spin_unlock_irqrestore(&console_lock, flags); + exported_str[j]='\0'; + console_export(exported_str, j-1); + return(0); } diff --git a/xen-2.4.16/common/memory.c b/xen-2.4.16/common/memory.c index 8c75f08dbb..0c534ad3d1 100644 --- a/xen-2.4.16/common/memory.c +++ b/xen-2.4.16/common/memory.c @@ -171,6 +171,7 @@ #include #include #include +#include #include #include #include @@ -205,6 +206,7 @@ unsigned long frame_table_size; unsigned long max_page; struct list_head free_list; +spinlock_t free_list_lock = SPIN_LOCK_UNLOCKED; unsigned int free_pfns; static int tlb_flush[NR_CPUS]; @@ -218,6 +220,7 @@ void __init init_frametable(unsigned long nr_pages) { struct pfn_info *pf; unsigned long page_index; + unsigned long flags; memset(tlb_flush, 0, sizeof(tlb_flush)); @@ -230,6 +233,7 @@ void __init init_frametable(unsigned long nr_pages) free_pfns = 0; /* Put all domain-allocatable memory on a free list. */ + spin_lock_irqsave(&free_list_lock, flags); INIT_LIST_HEAD(&free_list); for( page_index = (__pa(frame_table) + frame_table_size) >> PAGE_SHIFT; page_index < nr_pages; @@ -239,6 +243,7 @@ void __init init_frametable(unsigned long nr_pages) list_add_tail(&pf->list, &free_list); free_pfns++; } + spin_unlock_irqrestore(&free_list_lock, flags); } @@ -697,7 +702,6 @@ static int do_extended_command(unsigned long ptr, unsigned long val) return err; } - int do_process_page_updates(page_update_request_t *ureqs, int count) { page_update_request_t req; @@ -807,11 +811,10 @@ int do_process_page_updates(page_update_request_t *ureqs, int count) if ( tlb_flush[smp_processor_id()] ) { tlb_flush[smp_processor_id()] = 0; - __asm__ __volatile__ ( - "movl %%eax,%%cr3" : : - "a" (pagetable_val(current->mm.pagetable))); + __write_cr3_counted(pagetable_val(current->mm.pagetable)); } return(0); } + diff --git a/xen-2.4.16/common/network.c b/xen-2.4.16/common/network.c index 64c32a704f..3afc59b54f 100644 --- a/xen-2.4.16/common/network.c +++ b/xen-2.4.16/common/network.c @@ -49,6 +49,7 @@ net_vif_t *create_net_vif(int domain) { net_vif_t *new_vif; net_ring_t *new_ring; + net_shadow_ring_t *shadow_ring; struct task_struct *dom_task; if ( !(dom_task = find_domain_by_id(domain)) ) @@ -64,7 +65,27 @@ net_vif_t *create_net_vif(int domain) new_ring = dom_task->net_ring_base + dom_task->num_net_vifs; memset(new_ring, 0, sizeof(net_ring_t)); + // allocate the shadow ring. + // maybe these should be kmem_cache instead of kmalloc? + + shadow_ring = kmalloc(sizeof(net_shadow_ring_t), GFP_KERNEL); + if (shadow_ring == NULL) goto fail; + + shadow_ring->tx_ring = kmalloc(TX_RING_SIZE + * sizeof(tx_shadow_entry_t), GFP_KERNEL); + shadow_ring->rx_ring = kmalloc(RX_RING_SIZE + * sizeof(rx_shadow_entry_t), GFP_KERNEL); + if ((shadow_ring->tx_ring == NULL) || (shadow_ring->rx_ring == NULL)) + goto fail; + + shadow_ring->rx_prod = shadow_ring->rx_cons = shadow_ring->rx_idx = 0; + + // fill in the new vif struct. + new_vif->net_ring = new_ring; + new_vif->shadow_ring = shadow_ring; + + skb_queue_head_init(&new_vif->skb_list); new_vif->domain = domain; @@ -77,6 +98,10 @@ net_vif_t *create_net_vif(int domain) dom_task->num_net_vifs++; return new_vif; + +fail: + printk("VIF allocation failed!\n"); + return NULL; } /* delete_net_vif - Delete the last vif in the given domain. @@ -101,7 +126,10 @@ void destroy_net_vif(struct task_struct *p) write_lock(&sys_vif_lock); sys_vif_list[p->net_vif_list[i]->id] = NULL; // system vif list not gc'ed write_unlock(&sys_vif_lock); - + + kfree(p->net_vif_list[i]->shadow_ring->tx_ring); + kfree(p->net_vif_list[i]->shadow_ring->rx_ring); + kfree(p->net_vif_list[i]->shadow_ring); kmem_cache_free(net_vif_cache, p->net_vif_list[i]); } @@ -315,47 +343,56 @@ int net_find_rule(u8 nproto, u8 tproto, u32 src_addr, u32 dst_addr, u16 src_port * list. */ -int net_get_target_vif(struct sk_buff *skb) +#define net_get_target_vif(skb) __net_get_target_vif(skb->data, skb->len, skb->src_vif) + +int __net_get_target_vif(u8 *data, unsigned int len, int src_vif) { int target = VIF_DROP; - skb->h.raw = skb->nh.raw = skb->data; - if ( skb->len < 2 ) goto drop; - switch ( ntohs(skb->mac.ethernet->h_proto) ) + u8 *h_raw, *nh_raw; + + if ( len < 2 ) goto drop; + + nh_raw = data + ETH_HLEN; + switch ( ntohs(*(unsigned short *)(data + 12)) ) { case ETH_P_ARP: - if ( skb->len < 28 ) goto drop; - target = net_find_rule((u8)ETH_P_ARP, 0, ntohl(*(u32 *)(skb->nh.raw + 14)), - ntohl(*(u32 *)(skb->nh.raw + 24)), 0, 0, - skb->src_vif); +//printk("ARP!\n"); + if ( len < 28 ) goto drop; + target = net_find_rule((u8)ETH_P_ARP, 0, ntohl(*(u32 *)(nh_raw + 14)), + ntohl(*(u32 *)(nh_raw + 24)), 0, 0, + src_vif); break; case ETH_P_IP: - if ( skb->len < 20 ) goto drop; - skb->h.raw += ((*(unsigned char *)(skb->nh.raw)) & 0x0f) * 4; - switch ( *(unsigned char *)(skb->nh.raw + 9) ) +//printk("IP\n"); + if ( len < 20 ) goto drop; + h_raw = data + ((*(unsigned char *)(nh_raw)) & 0x0f) * 4; + switch ( *(unsigned char *)(nh_raw + 9) ) { - case IPPROTO_TCP: case IPPROTO_UDP: - target = net_find_rule((u8)ETH_P_IP, *(u8 *)(skb->nh.raw + 9), - ntohl(*(u32 *)(skb->nh.raw + 12)), - ntohl(*(u32 *)(skb->nh.raw + 16)), - ntohs(*(u16 *)(skb->h.raw)), - ntohs(*(u16 *)(skb->h.raw + 2)), - skb->src_vif); +//printk("UDP!\n"); + case IPPROTO_TCP: + target = net_find_rule((u8)ETH_P_IP, *(u8 *)(nh_raw + 9), + ntohl(*(u32 *)(nh_raw + 12)), + ntohl(*(u32 *)(nh_raw + 16)), + ntohs(*(u16 *)(h_raw)), + ntohs(*(u16 *)(h_raw + 2)), + src_vif); break; default: // ip-based protocol where we don't have ports. - target = net_find_rule((u8)ETH_P_IP, *(u8 *)(skb->nh.raw + 9), - ntohl(*(u32 *)(skb->nh.raw + 12)), - ntohl(*(u32 *)(skb->nh.raw + 16)), +//printk("Other IP!\n"); + target = net_find_rule((u8)ETH_P_IP, *(u8 *)(data + 9), + ntohl(*(u32 *)(nh_raw + 12)), + ntohl(*(u32 *)(nh_raw + 16)), 0, 0, - skb->src_vif); + src_vif); } break; } - skb->dst_vif=target; return target; drop: +//printk("Drop case!\n"); return VIF_DROP; } diff --git a/xen-2.4.16/drivers/net/3c509.c b/xen-2.4.16/drivers/net/3c509.c deleted file mode 100644 index 8729f39f8b..0000000000 --- a/xen-2.4.16/drivers/net/3c509.c +++ /dev/null @@ -1,1068 +0,0 @@ -/* 3c509.c: A 3c509 EtherLink3 ethernet driver for linux. */ -/* - Written 1993-2000 by Donald Becker. - - Copyright 1994-2000 by Donald Becker. - Copyright 1993 United States Government as represented by the - Director, National Security Agency. This software may be used and - distributed according to the terms of the GNU General Public License, - incorporated herein by reference. - - This driver is for the 3Com EtherLinkIII series. - - The author may be reached as becker@scyld.com, or C/O - Scyld Computing Corporation - 410 Severn Ave., Suite 210 - Annapolis MD 21403 - - Known limitations: - Because of the way 3c509 ISA detection works it's difficult to predict - a priori which of several ISA-mode cards will be detected first. - - This driver does not use predictive interrupt mode, resulting in higher - packet latency but lower overhead. If interrupts are disabled for an - unusually long time it could also result in missed packets, but in - practice this rarely happens. - - - FIXES: - Alan Cox: Removed the 'Unexpected interrupt' bug. - Michael Meskes: Upgraded to Donald Becker's version 1.07. - Alan Cox: Increased the eeprom delay. Regardless of - what the docs say some people definitely - get problems with lower (but in card spec) - delays - v1.10 4/21/97 Fixed module code so that multiple cards may be detected, - other cleanups. -djb - Andrea Arcangeli: Upgraded to Donald Becker's version 1.12. - Rick Payne: Fixed SMP race condition - v1.13 9/8/97 Made 'max_interrupt_work' an insmod-settable variable -djb - v1.14 10/15/97 Avoided waiting..discard message for fast machines -djb - v1.15 1/31/98 Faster recovery for Tx errors. -djb - v1.16 2/3/98 Different ID port handling to avoid sound cards. -djb - v1.18 12Mar2001 Andrew Morton - - Avoid bogus detect of 3c590's (Andrzej Krzysztofowicz) - - Reviewed against 1.18 from scyld.com -*/ - -/* A few values that may be tweaked. */ - -/* Time in jiffies before concluding the transmitter is hung. */ -#define TX_TIMEOUT (400*HZ/1000) -/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ -static int max_interrupt_work = 10; - -#include -#include - -//#include -//#include -#include -//#include -#include -#include -#include -//#include -#include -#include -#include -#include -#include -#include -#include /* for udelay() */ -#include - -#include -#include -#include - -static char versionA[] __initdata = "3c509.c:1.18 12Mar2001 becker@scyld.com\n"; -static char versionB[] __initdata = "http://www.scyld.com/network/3c509.html\n"; - -#ifdef EL3_DEBUG -static int el3_debug = EL3_DEBUG; -#else -static int el3_debug = 2; -#endif - -/* To minimize the size of the driver source I only define operating - constants if they are used several times. You'll need the manual - anyway if you want to understand driver details. */ -/* Offsets from base I/O address. */ -#define EL3_DATA 0x00 -#define EL3_CMD 0x0e -#define EL3_STATUS 0x0e -#define EEPROM_READ 0x80 - -#define EL3_IO_EXTENT 16 - -#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD) - - -/* The top five bits written to EL3_CMD are a command, the lower - 11 bits are the parameter, if applicable. */ -enum c509cmd { - TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11, - RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11, - TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11, - FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11, - SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11, - SetTxThreshold = 18<<11, SetTxStart = 19<<11, StatsEnable = 21<<11, - StatsDisable = 22<<11, StopCoax = 23<<11,}; - -enum c509status { - IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004, - TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020, - IntReq = 0x0040, StatsFull = 0x0080, CmdBusy = 0x1000, }; - -/* The SetRxFilter command accepts the following classes: */ -enum RxFilter { - RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 }; - -/* Register window 1 offsets, the window used in normal operation. */ -#define TX_FIFO 0x00 -#define RX_FIFO 0x00 -#define RX_STATUS 0x08 -#define TX_STATUS 0x0B -#define TX_FREE 0x0C /* Remaining free bytes in Tx buffer. */ - -#define WN0_IRQ 0x08 /* Window 0: Set IRQ line in bits 12-15. */ -#define WN4_MEDIA 0x0A /* Window 4: Various transcvr/media bits. */ -#define MEDIA_TP 0x00C0 /* Enable link beat and jabber for 10baseT. */ - -/* - * Must be a power of two (we use a binary and in the - * circular queue) - */ -#define SKB_QUEUE_SIZE 64 - -struct el3_private { - struct net_device_stats stats; - struct net_device *next_dev; - spinlock_t lock; - /* skb send-queue */ - int head, size; - struct sk_buff *queue[SKB_QUEUE_SIZE]; - char mca_slot; -}; -static int id_port __initdata = 0x110; /* Start with 0x110 to avoid new sound cards.*/ -static struct net_device *el3_root_dev; - -static ushort id_read_eeprom(int index); -static ushort read_eeprom(int ioaddr, int index); -static int el3_open(struct net_device *dev); -static int el3_start_xmit(struct sk_buff *skb, struct net_device *dev); -static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs); -static void update_stats(struct net_device *dev); -static struct net_device_stats *el3_get_stats(struct net_device *dev); -static int el3_rx(struct net_device *dev); -static int el3_close(struct net_device *dev); -static void set_multicast_list(struct net_device *dev); -static void el3_tx_timeout (struct net_device *dev); - -#ifdef CONFIG_MCA -struct el3_mca_adapters_struct { - char* name; - int id; -}; - -static struct el3_mca_adapters_struct el3_mca_adapters[] __initdata = { - { "3Com 3c529 EtherLink III (10base2)", 0x627c }, - { "3Com 3c529 EtherLink III (10baseT)", 0x627d }, - { "3Com 3c529 EtherLink III (test mode)", 0x62db }, - { "3Com 3c529 EtherLink III (TP or coax)", 0x62f6 }, - { "3Com 3c529 EtherLink III (TP)", 0x62f7 }, - { NULL, 0 }, -}; -#endif /* CONFIG_MCA */ - -#if defined(CONFIG_ISAPNP) || defined(CONFIG_ISAPNP_MODULE) -static struct isapnp_device_id el3_isapnp_adapters[] __initdata = { - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, - ISAPNP_VENDOR('T', 'C', 'M'), ISAPNP_FUNCTION(0x5090), - (long) "3Com Etherlink III (TP)" }, - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, - ISAPNP_VENDOR('T', 'C', 'M'), ISAPNP_FUNCTION(0x5091), - (long) "3Com Etherlink III" }, - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, - ISAPNP_VENDOR('T', 'C', 'M'), ISAPNP_FUNCTION(0x5094), - (long) "3Com Etherlink III (combo)" }, - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, - ISAPNP_VENDOR('T', 'C', 'M'), ISAPNP_FUNCTION(0x5095), - (long) "3Com Etherlink III (TPO)" }, - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, - ISAPNP_VENDOR('T', 'C', 'M'), ISAPNP_FUNCTION(0x5098), - (long) "3Com Etherlink III (TPC)" }, - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, - ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_FUNCTION(0x80f7), - (long) "3Com Etherlink III compatible" }, - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, - ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_FUNCTION(0x80f8), - (long) "3Com Etherlink III compatible" }, - { } /* terminate list */ -}; - -MODULE_DEVICE_TABLE(isapnp, el3_isapnp_adapters); -MODULE_LICENSE("GPL"); - - -static u16 el3_isapnp_phys_addr[8][3]; -static int nopnp; -#endif /* CONFIG_ISAPNP || CONFIG_ISAPNP_MODULE */ - -int __init el3_probe(struct net_device *dev) -{ - struct el3_private *lp; - short lrs_state = 0xff, i; - int ioaddr, irq, if_port; - u16 phys_addr[3]; - static int current_tag; - int mca_slot = -1; -#if defined(CONFIG_ISAPNP) || defined(CONFIG_ISAPNP_MODULE) - static int pnp_cards; -#endif /* CONFIG_ISAPNP || CONFIG_ISAPNP_MODULE */ - - if (dev) SET_MODULE_OWNER(dev); - - /* First check all slots of the EISA bus. The next slot address to - probe is kept in 'eisa_addr' to support multiple probe() calls. */ - if (EISA_bus) { - static int eisa_addr = 0x1000; - while (eisa_addr < 0x9000) { - int device_id; - - ioaddr = eisa_addr; - eisa_addr += 0x1000; - - /* Check the standard EISA ID register for an encoded '3Com'. */ - if (inw(ioaddr + 0xC80) != 0x6d50) - continue; - - /* Avoid conflict with 3c590, 3c592, 3c597, etc */ - device_id = (inb(ioaddr + 0xC82)<<8) + inb(ioaddr + 0xC83); - if ((device_id & 0xFF00) == 0x5900) { - continue; - } - - /* Change the register set to the configuration window 0. */ - outw(SelectWindow | 0, ioaddr + 0xC80 + EL3_CMD); - - irq = inw(ioaddr + WN0_IRQ) >> 12; - if_port = inw(ioaddr + 6)>>14; - for (i = 0; i < 3; i++) - phys_addr[i] = htons(read_eeprom(ioaddr, i)); - - /* Restore the "Product ID" to the EEPROM read register. */ - read_eeprom(ioaddr, 3); - - /* Was the EISA code an add-on hack? Nahhhhh... */ - goto found; - } - } - -#ifdef CONFIG_MCA - /* Based on Erik Nygren's (nygren@mit.edu) 3c529 patch, heavily - * modified by Chris Beauregard (cpbeaure@csclub.uwaterloo.ca) - * to support standard MCA probing. - * - * redone for multi-card detection by ZP Gu (zpg@castle.net) - * now works as a module - */ - - if( MCA_bus ) { - int slot, j; - u_char pos4, pos5; - - for( j = 0; el3_mca_adapters[j].name != NULL; j ++ ) { - slot = 0; - while( slot != MCA_NOTFOUND ) { - slot = mca_find_unused_adapter( - el3_mca_adapters[j].id, slot ); - if( slot == MCA_NOTFOUND ) break; - - /* if we get this far, an adapter has been - * detected and is enabled - */ - - pos4 = mca_read_stored_pos( slot, 4 ); - pos5 = mca_read_stored_pos( slot, 5 ); - - ioaddr = ((short)((pos4&0xfc)|0x02)) << 8; - irq = pos5 & 0x0f; - - /* probing for a card at a particular IO/IRQ */ - if(dev && ((dev->irq >= 1 && dev->irq != irq) || - (dev->base_addr >= 1 && dev->base_addr != ioaddr))) { - slot++; /* probing next slot */ - continue; - } - - printk("3c509: found %s at slot %d\n", - el3_mca_adapters[j].name, slot + 1 ); - - /* claim the slot */ - mca_set_adapter_name(slot, el3_mca_adapters[j].name); - mca_set_adapter_procfn(slot, NULL, NULL); - mca_mark_as_used(slot); - - if_port = pos4 & 0x03; - if (el3_debug > 2) { - printk("3c529: irq %d ioaddr 0x%x ifport %d\n", irq, ioaddr, if_port); - } - EL3WINDOW(0); - for (i = 0; i < 3; i++) { - phys_addr[i] = htons(read_eeprom(ioaddr, i)); - } - - mca_slot = slot; - - goto found; - } - } - /* if we get here, we didn't find an MCA adapter */ - return -ENODEV; - } -#endif /* CONFIG_MCA */ - -#if defined(CONFIG_ISAPNP) || defined(CONFIG_ISAPNP_MODULE) - if (nopnp == 1) - goto no_pnp; - - for (i=0; el3_isapnp_adapters[i].vendor != 0; i++) { - struct pci_dev *idev = NULL; - int j; - while ((idev = isapnp_find_dev(NULL, - el3_isapnp_adapters[i].vendor, - el3_isapnp_adapters[i].function, - idev))) { - idev->prepare(idev); - /* Deactivation is needed if the driver was called - with "nopnp=1" before, does not harm if not. */ - idev->deactivate(idev); - idev->activate(idev); - if (!idev->resource[0].start || check_region(idev->resource[0].start, EL3_IO_EXTENT)) - continue; - ioaddr = idev->resource[0].start; - if (!request_region(ioaddr, EL3_IO_EXTENT, "3c509 PnP")) - return -EBUSY; - irq = idev->irq_resource[0].start; - if (el3_debug > 3) - printk ("ISAPnP reports %s at i/o 0x%x, irq %d\n", - (char*) el3_isapnp_adapters[i].driver_data, ioaddr, irq); - EL3WINDOW(0); - for (j = 0; j < 3; j++) - el3_isapnp_phys_addr[pnp_cards][j] = - phys_addr[j] = - htons(read_eeprom(ioaddr, j)); - if_port = read_eeprom(ioaddr, 8) >> 14; - pnp_cards++; - goto found; - } - } -no_pnp: -#endif /* CONFIG_ISAPNP || CONFIG_ISAPNP_MODULE */ - - /* Select an open I/O location at 0x1*0 to do contention select. */ - for ( ; id_port < 0x200; id_port += 0x10) { - if (check_region(id_port, 1)) - continue; - outb(0x00, id_port); - outb(0xff, id_port); - if (inb(id_port) & 0x01) - break; - } - if (id_port >= 0x200) { - /* Rare -- do we really need a warning? */ - printk(" WARNING: No I/O port available for 3c509 activation.\n"); - return -ENODEV; - } - /* Next check for all ISA bus boards by sending the ID sequence to the - ID_PORT. We find cards past the first by setting the 'current_tag' - on cards as they are found. Cards with their tag set will not - respond to subsequent ID sequences. */ - - outb(0x00, id_port); - outb(0x00, id_port); - for(i = 0; i < 255; i++) { - outb(lrs_state, id_port); - lrs_state <<= 1; - lrs_state = lrs_state & 0x100 ? lrs_state ^ 0xcf : lrs_state; - } - - /* For the first probe, clear all board's tag registers. */ - if (current_tag == 0) - outb(0xd0, id_port); - else /* Otherwise kill off already-found boards. */ - outb(0xd8, id_port); - - if (id_read_eeprom(7) != 0x6d50) { - return -ENODEV; - } - - /* Read in EEPROM data, which does contention-select. - Only the lowest address board will stay "on-line". - 3Com got the byte order backwards. */ - for (i = 0; i < 3; i++) { - phys_addr[i] = htons(id_read_eeprom(i)); - } - -#if defined(CONFIG_ISAPNP) || defined(CONFIG_ISAPNP_MODULE) - if (nopnp == 0) { - /* The ISA PnP 3c509 cards respond to the ID sequence. - This check is needed in order not to register them twice. */ - for (i = 0; i < pnp_cards; i++) { - if (phys_addr[0] == el3_isapnp_phys_addr[i][0] && - phys_addr[1] == el3_isapnp_phys_addr[i][1] && - phys_addr[2] == el3_isapnp_phys_addr[i][2]) - { - if (el3_debug > 3) - printk("3c509 with address %02x %02x %02x %02x %02x %02x was found by ISAPnP\n", - phys_addr[0] & 0xff, phys_addr[0] >> 8, - phys_addr[1] & 0xff, phys_addr[1] >> 8, - phys_addr[2] & 0xff, phys_addr[2] >> 8); - /* Set the adaptor tag so that the next card can be found. */ - outb(0xd0 + ++current_tag, id_port); - goto no_pnp; - } - } - } -#endif /* CONFIG_ISAPNP || CONFIG_ISAPNP_MODULE */ - - { - unsigned int iobase = id_read_eeprom(8); - if_port = iobase >> 14; - ioaddr = 0x200 + ((iobase & 0x1f) << 4); - } - irq = id_read_eeprom(9) >> 12; - - if (dev) { /* Set passed-in IRQ or I/O Addr. */ - if (dev->irq > 1 && dev->irq < 16) - irq = dev->irq; - - if (dev->base_addr) { - if (dev->mem_end == 0x3c509 /* Magic key */ - && dev->base_addr >= 0x200 && dev->base_addr <= 0x3e0) - ioaddr = dev->base_addr & 0x3f0; - else if (dev->base_addr != ioaddr) - return -ENODEV; - } - } - - if (!request_region(ioaddr, EL3_IO_EXTENT, "3c509")) - return -EBUSY; - - /* Set the adaptor tag so that the next card can be found. */ - outb(0xd0 + ++current_tag, id_port); - - /* Activate the adaptor at the EEPROM location. */ - outb((ioaddr >> 4) | 0xe0, id_port); - - EL3WINDOW(0); - if (inw(ioaddr) != 0x6d50) { - release_region(ioaddr, EL3_IO_EXTENT); - return -ENODEV; - } - - /* Free the interrupt so that some other card can use it. */ - outw(0x0f00, ioaddr + WN0_IRQ); - found: - if (dev == NULL) { - dev = init_etherdev(dev, sizeof(struct el3_private)); - if (dev == NULL) { - release_region(ioaddr, EL3_IO_EXTENT); - return -ENOMEM; - } - SET_MODULE_OWNER(dev); - } - memcpy(dev->dev_addr, phys_addr, sizeof(phys_addr)); - dev->base_addr = ioaddr; - dev->irq = irq; - dev->if_port = (dev->mem_start & 0x1f) ? dev->mem_start & 3 : if_port; - - { - const char *if_names[] = {"10baseT", "AUI", "undefined", "BNC"}; - printk("%s: 3c5x9 at %#3.3lx, %s port, address ", - dev->name, dev->base_addr, if_names[dev->if_port]); - } - - /* Read in the station address. */ - for (i = 0; i < 6; i++) - printk(" %2.2x", dev->dev_addr[i]); - printk(", IRQ %d.\n", dev->irq); - - /* Make up a EL3-specific-data structure. */ - if (dev->priv == NULL) - dev->priv = kmalloc(sizeof(struct el3_private), GFP_KERNEL); - if (dev->priv == NULL) - return -ENOMEM; - memset(dev->priv, 0, sizeof(struct el3_private)); - - lp = dev->priv; - lp->mca_slot = mca_slot; - lp->next_dev = el3_root_dev; - spin_lock_init(&lp->lock); - el3_root_dev = dev; - - if (el3_debug > 0) - printk(KERN_INFO "%s" KERN_INFO "%s", versionA, versionB); - - /* The EL3-specific entries in the device structure. */ - dev->open = &el3_open; - dev->hard_start_xmit = &el3_start_xmit; - dev->stop = &el3_close; - dev->get_stats = &el3_get_stats; - dev->set_multicast_list = &set_multicast_list; - dev->tx_timeout = el3_tx_timeout; - dev->watchdog_timeo = TX_TIMEOUT; - - /* Fill in the generic fields of the device structure. */ - ether_setup(dev); - return 0; -} - -/* Read a word from the EEPROM using the regular EEPROM access register. - Assume that we are in register window zero. - */ -static ushort __init read_eeprom(int ioaddr, int index) -{ - outw(EEPROM_READ + index, ioaddr + 10); - /* Pause for at least 162 us. for the read to take place. */ - udelay (500); - return inw(ioaddr + 12); -} - -/* Read a word from the EEPROM when in the ISA ID probe state. */ -static ushort __init id_read_eeprom(int index) -{ - int bit, word = 0; - - /* Issue read command, and pause for at least 162 us. for it to complete. - Assume extra-fast 16Mhz bus. */ - outb(EEPROM_READ + index, id_port); - - /* Pause for at least 162 us. for the read to take place. */ - udelay (500); - - for (bit = 15; bit >= 0; bit--) - word = (word << 1) + (inb(id_port) & 0x01); - - if (el3_debug > 3) - printk(" 3c509 EEPROM word %d %#4.4x.\n", index, word); - - return word; -} - - -static int -el3_open(struct net_device *dev) -{ - int ioaddr = dev->base_addr; - int i; - - outw(TxReset, ioaddr + EL3_CMD); - outw(RxReset, ioaddr + EL3_CMD); - outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD); - - i = request_irq(dev->irq, &el3_interrupt, 0, dev->name, dev); - if (i) return i; - - EL3WINDOW(0); - if (el3_debug > 3) - printk("%s: Opening, IRQ %d status@%x %4.4x.\n", dev->name, - dev->irq, ioaddr + EL3_STATUS, inw(ioaddr + EL3_STATUS)); - - /* Activate board: this is probably unnecessary. */ - outw(0x0001, ioaddr + 4); - - /* Set the IRQ line. */ - outw((dev->irq << 12) | 0x0f00, ioaddr + WN0_IRQ); - - /* Set the station address in window 2 each time opened. */ - EL3WINDOW(2); - - for (i = 0; i < 6; i++) - outb(dev->dev_addr[i], ioaddr + i); - - if (dev->if_port == 3) - /* Start the thinnet transceiver. We should really wait 50ms...*/ - outw(StartCoax, ioaddr + EL3_CMD); - else if (dev->if_port == 0) { - /* 10baseT interface, enabled link beat and jabber check. */ - EL3WINDOW(4); - outw(inw(ioaddr + WN4_MEDIA) | MEDIA_TP, ioaddr + WN4_MEDIA); - } - - /* Switch to the stats window, and clear all stats by reading. */ - outw(StatsDisable, ioaddr + EL3_CMD); - EL3WINDOW(6); - for (i = 0; i < 9; i++) - inb(ioaddr + i); - inw(ioaddr + 10); - inw(ioaddr + 12); - - /* Switch to register set 1 for normal use. */ - EL3WINDOW(1); - - /* Accept b-case and phys addr only. */ - outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD); - outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */ - - netif_start_queue(dev); - - outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */ - outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */ - /* Allow status bits to be seen. */ - outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD); - /* Ack all pending events, and set active indicator mask. */ - outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, - ioaddr + EL3_CMD); - outw(SetIntrEnb | IntLatch|TxAvailable|TxComplete|RxComplete|StatsFull, - ioaddr + EL3_CMD); - - if (el3_debug > 3) - printk("%s: Opened 3c509 IRQ %d status %4.4x.\n", - dev->name, dev->irq, inw(ioaddr + EL3_STATUS)); - - return 0; -} - -static void -el3_tx_timeout (struct net_device *dev) -{ - struct el3_private *lp = (struct el3_private *)dev->priv; - int ioaddr = dev->base_addr; - - /* Transmitter timeout, serious problems. */ - printk("%s: transmit timed out, Tx_status %2.2x status %4.4x " - "Tx FIFO room %d.\n", - dev->name, inb(ioaddr + TX_STATUS), inw(ioaddr + EL3_STATUS), - inw(ioaddr + TX_FREE)); - lp->stats.tx_errors++; - dev->trans_start = jiffies; - /* Issue TX_RESET and TX_START commands. */ - outw(TxReset, ioaddr + EL3_CMD); - outw(TxEnable, ioaddr + EL3_CMD); - netif_wake_queue(dev); -} - - -static int -el3_start_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct el3_private *lp = (struct el3_private *)dev->priv; - int ioaddr = dev->base_addr; - unsigned long flags; - - netif_stop_queue (dev); - - lp->stats.tx_bytes += skb->len; - - if (el3_debug > 4) { - printk("%s: el3_start_xmit(length = %u) called, status %4.4x.\n", - dev->name, skb->len, inw(ioaddr + EL3_STATUS)); - } -#if 0 -#ifndef final_version - { /* Error-checking code, delete someday. */ - ushort status = inw(ioaddr + EL3_STATUS); - if (status & 0x0001 /* IRQ line active, missed one. */ - && inw(ioaddr + EL3_STATUS) & 1) { /* Make sure. */ - printk("%s: Missed interrupt, status then %04x now %04x" - " Tx %2.2x Rx %4.4x.\n", dev->name, status, - inw(ioaddr + EL3_STATUS), inb(ioaddr + TX_STATUS), - inw(ioaddr + RX_STATUS)); - /* Fake interrupt trigger by masking, acknowledge interrupts. */ - outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD); - outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, - ioaddr + EL3_CMD); - outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD); - } - } -#endif -#endif - /* - * We lock the driver against other processors. Note - * we don't need to lock versus the IRQ as we suspended - * that. This means that we lose the ability to take - * an RX during a TX upload. That sucks a bit with SMP - * on an original 3c509 (2K buffer) - * - * Using disable_irq stops us crapping on other - * time sensitive devices. - */ - - spin_lock_irqsave(&lp->lock, flags); - - /* Put out the doubleword header... */ - outw(skb->len, ioaddr + TX_FIFO); - outw(0x00, ioaddr + TX_FIFO); - /* ... and the packet rounded to a doubleword. */ -#ifdef __powerpc__ - outsl_unswapped(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2); -#else - outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2); -#endif - - dev->trans_start = jiffies; - if (inw(ioaddr + TX_FREE) > 1536) - netif_start_queue(dev); - else - /* Interrupt us when the FIFO has room for max-sized packet. */ - outw(SetTxThreshold + 1536, ioaddr + EL3_CMD); - - spin_unlock_irqrestore(&lp->lock, flags); - - dev_kfree_skb (skb); - - /* Clear the Tx status stack. */ - { - short tx_status; - int i = 4; - - while (--i > 0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) { - if (tx_status & 0x38) lp->stats.tx_aborted_errors++; - if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD); - if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD); - outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */ - } - } - return 0; -} - -/* The EL3 interrupt handler. */ -static void -el3_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct net_device *dev = (struct net_device *)dev_id; - struct el3_private *lp; - int ioaddr, status; - int i = max_interrupt_work; - - if (dev == NULL) { - printk ("el3_interrupt(): irq %d for unknown device.\n", irq); - return; - } - - lp = (struct el3_private *)dev->priv; - spin_lock(&lp->lock); - - ioaddr = dev->base_addr; - - if (el3_debug > 4) { - status = inw(ioaddr + EL3_STATUS); - printk("%s: interrupt, status %4.4x.\n", dev->name, status); - } - - while ((status = inw(ioaddr + EL3_STATUS)) & - (IntLatch | RxComplete | StatsFull)) { - - if (status & RxComplete) - el3_rx(dev); - - if (status & TxAvailable) { - if (el3_debug > 5) - printk(" TX room bit was handled.\n"); - /* There's room in the FIFO for a full-sized packet. */ - outw(AckIntr | TxAvailable, ioaddr + EL3_CMD); - netif_wake_queue (dev); - } - if (status & (AdapterFailure | RxEarly | StatsFull | TxComplete)) { - /* Handle all uncommon interrupts. */ - if (status & StatsFull) /* Empty statistics. */ - update_stats(dev); - if (status & RxEarly) { /* Rx early is unused. */ - el3_rx(dev); - outw(AckIntr | RxEarly, ioaddr + EL3_CMD); - } - if (status & TxComplete) { /* Really Tx error. */ - struct el3_private *lp = (struct el3_private *)dev->priv; - short tx_status; - int i = 4; - - while (--i>0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) { - if (tx_status & 0x38) lp->stats.tx_aborted_errors++; - if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD); - if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD); - outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */ - } - } - if (status & AdapterFailure) { - /* Adapter failure requires Rx reset and reinit. */ - outw(RxReset, ioaddr + EL3_CMD); - /* Set the Rx filter to the current state. */ - outw(SetRxFilter | RxStation | RxBroadcast - | (dev->flags & IFF_ALLMULTI ? RxMulticast : 0) - | (dev->flags & IFF_PROMISC ? RxProm : 0), - ioaddr + EL3_CMD); - outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */ - outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD); - } - } - - if (--i < 0) { - printk("%s: Infinite loop in interrupt, status %4.4x.\n", - dev->name, status); - /* Clear all interrupts. */ - outw(AckIntr | 0xFF, ioaddr + EL3_CMD); - break; - } - /* Acknowledge the IRQ. */ - outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD); /* Ack IRQ */ - } - - if (el3_debug > 4) { - printk("%s: exiting interrupt, status %4.4x.\n", dev->name, - inw(ioaddr + EL3_STATUS)); - } - spin_unlock(&lp->lock); - return; -} - - -static struct net_device_stats * -el3_get_stats(struct net_device *dev) -{ - struct el3_private *lp = (struct el3_private *)dev->priv; - unsigned long flags; - - /* - * This is fast enough not to bother with disable IRQ - * stuff. - */ - - spin_lock_irqsave(&lp->lock, flags); - update_stats(dev); - spin_unlock_irqrestore(&lp->lock, flags); - return &lp->stats; -} - -/* Update statistics. We change to register window 6, so this should be run - single-threaded if the device is active. This is expected to be a rare - operation, and it's simpler for the rest of the driver to assume that - window 1 is always valid rather than use a special window-state variable. - */ -static void update_stats(struct net_device *dev) -{ - struct el3_private *lp = (struct el3_private *)dev->priv; - int ioaddr = dev->base_addr; - - if (el3_debug > 5) - printk(" Updating the statistics.\n"); - /* Turn off statistics updates while reading. */ - outw(StatsDisable, ioaddr + EL3_CMD); - /* Switch to the stats window, and read everything. */ - EL3WINDOW(6); - lp->stats.tx_carrier_errors += inb(ioaddr + 0); - lp->stats.tx_heartbeat_errors += inb(ioaddr + 1); - /* Multiple collisions. */ inb(ioaddr + 2); - lp->stats.collisions += inb(ioaddr + 3); - lp->stats.tx_window_errors += inb(ioaddr + 4); - lp->stats.rx_fifo_errors += inb(ioaddr + 5); - lp->stats.tx_packets += inb(ioaddr + 6); - /* Rx packets */ inb(ioaddr + 7); - /* Tx deferrals */ inb(ioaddr + 8); - inw(ioaddr + 10); /* Total Rx and Tx octets. */ - inw(ioaddr + 12); - - /* Back to window 1, and turn statistics back on. */ - EL3WINDOW(1); - outw(StatsEnable, ioaddr + EL3_CMD); - return; -} - -static int -el3_rx(struct net_device *dev) -{ - struct el3_private *lp = (struct el3_private *)dev->priv; - int ioaddr = dev->base_addr; - short rx_status; - - if (el3_debug > 5) - printk(" In rx_packet(), status %4.4x, rx_status %4.4x.\n", - inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS)); - while ((rx_status = inw(ioaddr + RX_STATUS)) > 0) { - if (rx_status & 0x4000) { /* Error, update stats. */ - short error = rx_status & 0x3800; - - outw(RxDiscard, ioaddr + EL3_CMD); - lp->stats.rx_errors++; - switch (error) { - case 0x0000: lp->stats.rx_over_errors++; break; - case 0x0800: lp->stats.rx_length_errors++; break; - case 0x1000: lp->stats.rx_frame_errors++; break; - case 0x1800: lp->stats.rx_length_errors++; break; - case 0x2000: lp->stats.rx_frame_errors++; break; - case 0x2800: lp->stats.rx_crc_errors++; break; - } - } else { - short pkt_len = rx_status & 0x7ff; - struct sk_buff *skb; - - skb = dev_alloc_skb(pkt_len+5); - lp->stats.rx_bytes += pkt_len; - if (el3_debug > 4) - printk("Receiving packet size %d status %4.4x.\n", - pkt_len, rx_status); - if (skb != NULL) { - skb->dev = dev; - skb_reserve(skb, 2); /* Align IP on 16 byte */ - - /* 'skb->data' points to the start of sk_buff data area. */ -#ifdef __powerpc__ - insl_unswapped(ioaddr+RX_FIFO, skb_put(skb,pkt_len), - (pkt_len + 3) >> 2); -#else - insl(ioaddr + RX_FIFO, skb_put(skb,pkt_len), - (pkt_len + 3) >> 2); -#endif - - outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */ - skb->protocol = eth_type_trans(skb,dev); - netif_rx(skb); - dev->last_rx = jiffies; - lp->stats.rx_packets++; - continue; - } - outw(RxDiscard, ioaddr + EL3_CMD); - lp->stats.rx_dropped++; - if (el3_debug) - printk("%s: Couldn't allocate a sk_buff of size %d.\n", - dev->name, pkt_len); - } - inw(ioaddr + EL3_STATUS); /* Delay. */ - while (inw(ioaddr + EL3_STATUS) & 0x1000) - printk(KERN_DEBUG " Waiting for 3c509 to discard packet, status %x.\n", - inw(ioaddr + EL3_STATUS) ); - } - - return 0; -} - -/* - * Set or clear the multicast filter for this adaptor. - */ -static void -set_multicast_list(struct net_device *dev) -{ - unsigned long flags; - struct el3_private *lp = (struct el3_private *)dev->priv; - int ioaddr = dev->base_addr; - - if (el3_debug > 1) { - static int old; - if (old != dev->mc_count) { - old = dev->mc_count; - printk("%s: Setting Rx mode to %d addresses.\n", dev->name, dev->mc_count); - } - } - spin_lock_irqsave(&lp->lock, flags); - if (dev->flags&IFF_PROMISC) { - outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm, - ioaddr + EL3_CMD); - } - else if (dev->mc_count || (dev->flags&IFF_ALLMULTI)) { - outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast, ioaddr + EL3_CMD); - } - else - outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD); - spin_unlock_irqrestore(&lp->lock, flags); -} - -static int -el3_close(struct net_device *dev) -{ - int ioaddr = dev->base_addr; - - if (el3_debug > 2) - printk("%s: Shutting down ethercard.\n", dev->name); - - netif_stop_queue(dev); - - /* Turn off statistics ASAP. We update lp->stats below. */ - outw(StatsDisable, ioaddr + EL3_CMD); - - /* Disable the receiver and transmitter. */ - outw(RxDisable, ioaddr + EL3_CMD); - outw(TxDisable, ioaddr + EL3_CMD); - - if (dev->if_port == 3) - /* Turn off thinnet power. Green! */ - outw(StopCoax, ioaddr + EL3_CMD); - else if (dev->if_port == 0) { - /* Disable link beat and jabber, if_port may change ere next open(). */ - EL3WINDOW(4); - outw(inw(ioaddr + WN4_MEDIA) & ~MEDIA_TP, ioaddr + WN4_MEDIA); - } - - free_irq(dev->irq, dev); - /* Switching back to window 0 disables the IRQ. */ - EL3WINDOW(0); - /* But we explicitly zero the IRQ line select anyway. */ - outw(0x0f00, ioaddr + WN0_IRQ); - - update_stats(dev); - return 0; -} - -/*#ifdef MODULE*/ -/* Parameters that may be passed into the module. */ -static int debug = -1; -static int irq[] = {-1, -1, -1, -1, -1, -1, -1, -1}; -static int xcvr[] = {-1, -1, -1, -1, -1, -1, -1, -1}; - -MODULE_PARM(debug,"i"); -MODULE_PARM(irq,"1-8i"); -MODULE_PARM(xcvr,"1-8i"); -MODULE_PARM(max_interrupt_work, "i"); -MODULE_PARM_DESC(debug, "EtherLink III debug level (0-6)"); -MODULE_PARM_DESC(irq, "EtherLink III IRQ number(s) (assigned)"); -MODULE_PARM_DESC(xcvr,"EtherLink III tranceiver(s) (0=internal, 1=external)"); -MODULE_PARM_DESC(max_interrupt_work, "EtherLink III maximum events handled per interrupt"); -#ifdef CONFIG_ISAPNP -MODULE_PARM(nopnp, "i"); -MODULE_PARM_DESC(nopnp, "EtherLink III disable ISA PnP support (0-1)"); -#endif /* CONFIG_ISAPNP */ - -int -init_module(void) -{ - int el3_cards = 0; - - if (debug >= 0) - el3_debug = debug; - - el3_root_dev = NULL; - while (el3_probe(0) == 0) { - if (irq[el3_cards] > 1) - el3_root_dev->irq = irq[el3_cards]; - if (xcvr[el3_cards] >= 0) - el3_root_dev->if_port = xcvr[el3_cards]; - el3_cards++; - } - - return el3_cards ? 0 : -ENODEV; -} - -void -cleanup_module(void) -{ - struct net_device *next_dev; - - /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ - while (el3_root_dev) { - struct el3_private *lp = (struct el3_private *)el3_root_dev->priv; -#ifdef CONFIG_MCA - if(lp->mca_slot!=-1) - mca_mark_as_unused(lp->mca_slot); -#endif - next_dev = lp->next_dev; - unregister_netdev(el3_root_dev); - release_region(el3_root_dev->base_addr, EL3_IO_EXTENT); - kfree(el3_root_dev); - el3_root_dev = next_dev; - } -} -/*#endif*/ - -module_init(init_module); -module_exit(cleanup_module); diff --git a/xen-2.4.16/drivers/net/3c59x.c b/xen-2.4.16/drivers/net/3c59x.c index af5d1ad5ca..9478920e5b 100644 --- a/xen-2.4.16/drivers/net/3c59x.c +++ b/xen-2.4.16/drivers/net/3c59x.c @@ -188,13 +188,15 @@ /* "Knobs" that adjust features and parameters. */ /* Set the copy breakpoint for the copy-only-tiny-frames scheme. Setting to > 1512 effectively disables this feature. */ -#ifndef __arm__ +/*#ifndef __arm__ static const int rx_copybreak = 200; -#else +#else*/ /* ARM systems perform better by disregarding the bus-master transfer capability of these cards. -- rmk */ -static const int rx_copybreak = 1513; -#endif +/*static const int rx_copybreak = 1513; +#endif*/ +static const int rx_copybreak = 0; /* Xen doesn't copybreak in drivers. */ + /* Allow setting MTU to a larger size, bypassing the normal ethernet setup. */ static const int mtu = 1500; /* Maximum events (Rx packets, etc.) to handle at each interrupt. */ diff --git a/xen-2.4.16/drivers/net/8139cp.c b/xen-2.4.16/drivers/net/8139cp.c index c1e2f19d8a..592a03237f 100644 --- a/xen-2.4.16/drivers/net/8139cp.c +++ b/xen-2.4.16/drivers/net/8139cp.c @@ -78,13 +78,17 @@ MODULE_PARM (multicast_filter_limit, "i"); MODULE_PARM_DESC (multicast_filter_limit, "8139cp maximum number of filtered multicast addresses"); /* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */ -#if defined(__alpha__) || defined(__arm__) || defined(__hppa__) \ +/*#if defined(__alpha__) || defined(__arm__) || defined(__hppa__) \ || defined(__sparc_) || defined(__ia64__) \ || defined(__sh__) || defined(__mips__) static int rx_copybreak = 1518; #else static int rx_copybreak = 100; -#endif +#endif*/ + +/* Xen doesn't do rx_copybreak in drivers. */ +static int rx_copybreak = 0; + MODULE_PARM (rx_copybreak, "i"); MODULE_PARM_DESC (rx_copybreak, "8139cp Breakpoint at which Rx packets are copied"); diff --git a/xen-2.4.16/drivers/net/8139too.c b/xen-2.4.16/drivers/net/8139too.c deleted file mode 100644 index d1f0a04623..0000000000 --- a/xen-2.4.16/drivers/net/8139too.c +++ /dev/null @@ -1,2324 +0,0 @@ - -#define CONFIG_8139TOO_8129 - -/* - - 8139too.c: A RealTek RTL-8139 Fast Ethernet driver for Linux. - - Maintained by Jeff Garzik - Copyright 2000,2001 Jeff Garzik - - Much code comes from Donald Becker's rtl8139.c driver, - versions 1.13 and older. This driver was originally based - on rtl8139.c version 1.07. Header of rtl8139.c version 1.13: - - ---------- - - Written 1997-2001 by Donald Becker. - This software may be used and distributed according to the - terms of the GNU General Public License (GPL), incorporated - herein by reference. Drivers based on or derived from this - code fall under the GPL and must retain the authorship, - copyright and license notice. This file is not a complete - program and may only be used when the entire operating - system is licensed under the GPL. - - This driver is for boards based on the RTL8129 and RTL8139 - PCI ethernet chips. - - The author may be reached as becker@scyld.com, or C/O Scyld - Computing Corporation 410 Severn Ave., Suite 210 Annapolis - MD 21403 - - Support and updates available at - http://www.scyld.com/network/rtl8139.html - - Twister-tuning table provided by Kinston - . - - ---------- - - This software may be used and distributed according to the terms - of the GNU General Public License, incorporated herein by reference. - - Contributors: - - Donald Becker - he wrote the original driver, kudos to him! - (but please don't e-mail him for support, this isn't his driver) - - Tigran Aivazian - bug fixes, skbuff free cleanup - - Martin Mares - suggestions for PCI cleanup - - David S. Miller - PCI DMA and softnet updates - - Ernst Gill - fixes ported from BSD driver - - Daniel Kobras - identified specific locations of - posted MMIO write bugginess - - Gerard Sharp - bug fix, testing and feedback - - David Ford - Rx ring wrap fix - - Dan DeMaggio - swapped RTL8139 cards with me, and allowed me - to find and fix a crucial bug on older chipsets. - - Donald Becker/Chris Butterworth/Marcus Westergren - - Noticed various Rx packet size-related buglets. - - Santiago Garcia Mantinan - testing and feedback - - Jens David - 2.2.x kernel backports - - Martin Dennett - incredibly helpful insight on undocumented - features of the 8139 chips - - Jean-Jacques Michel - bug fix - - Tobias Ringström - Rx interrupt status checking suggestion - - Andrew Morton - Clear blocked signals, avoid - buffer overrun setting current->comm. - - Kalle Olavi Niemitalo - Wake-on-LAN ioctls - - Robert Kuebel - Save kernel thread from dying on any signal. - - Submitting bug reports: - - "rtl8139-diag -mmmaaavvveefN" output - enable RTL8139_DEBUG below, and look at 'dmesg' or kernel log - - See 8139too.txt for more details. - -*/ - -#define DRV_NAME "8139too" -#define DRV_VERSION "0.9.22" - - -#include -#include -//#include -//#include -#include -#include -#include -#include -#include -//#include -#include -#include -#include -//#include -#include -#include - -#define RTL8139_DRIVER_NAME DRV_NAME " Fast Ethernet driver " DRV_VERSION -#define PFX DRV_NAME ": " - - -/* enable PIO instead of MMIO, if CONFIG_8139TOO_PIO is selected */ -#ifdef CONFIG_8139TOO_PIO -#define USE_IO_OPS 1 -#endif - -/* define to 1 to enable copious debugging info */ -#undef RTL8139_DEBUG - -/* define to 1 to disable lightweight runtime debugging checks */ -#undef RTL8139_NDEBUG - - -#ifdef RTL8139_DEBUG -/* note: prints function name for you */ -# define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args) -#else -# define DPRINTK(fmt, args...) -#endif - -#ifdef RTL8139_NDEBUG -# define assert(expr) do {} while (0) -#else -# define assert(expr) \ - if(!(expr)) { \ - printk( "Assertion failed! %s,%s,%s,line=%d\n", \ - #expr,__FILE__,__FUNCTION__,__LINE__); \ - } -#endif - - -/* A few user-configurable values. */ -/* media options */ -#define MAX_UNITS 8 -static int media[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; -static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; - -/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ -static int max_interrupt_work = 20; - -/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). - The RTL chips use a 64 element hash table based on the Ethernet CRC. */ -static int multicast_filter_limit = 32; - -/* Size of the in-memory receive ring. */ -#define RX_BUF_LEN_IDX 2 /* 0==8K, 1==16K, 2==32K, 3==64K */ -#define RX_BUF_LEN (8192 << RX_BUF_LEN_IDX) -#define RX_BUF_PAD 16 -#define RX_BUF_WRAP_PAD 2048 /* spare padding to handle lack of packet wrap */ -#define RX_BUF_TOT_LEN (RX_BUF_LEN + RX_BUF_PAD + RX_BUF_WRAP_PAD) - -/* Number of Tx descriptor registers. */ -#define NUM_TX_DESC 4 - -/* max supported ethernet frame size -- must be at least (dev->mtu+14+4).*/ -#define MAX_ETH_FRAME_SIZE 1536 - -/* Size of the Tx bounce buffers -- must be at least (dev->mtu+14+4). */ -#define TX_BUF_SIZE MAX_ETH_FRAME_SIZE -#define TX_BUF_TOT_LEN (TX_BUF_SIZE * NUM_TX_DESC) - -/* PCI Tuning Parameters - Threshold is bytes transferred to chip before transmission starts. */ -#define TX_FIFO_THRESH 256 /* In bytes, rounded down to 32 byte units. */ - -/* The following settings are log_2(bytes)-4: 0 == 16 bytes .. 6==1024, 7==end of packet. */ -#define RX_FIFO_THRESH 7 /* Rx buffer level before first PCI xfer. */ -#define RX_DMA_BURST 7 /* Maximum PCI burst, '6' is 1024 */ -#define TX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */ -#define TX_RETRY 8 /* 0-15. retries = 16 + (TX_RETRY * 16) */ - -/* Operational parameters that usually are not changed. */ -/* Time in jiffies before concluding the transmitter is hung. */ -#define TX_TIMEOUT (6*HZ) - - -enum { - HAS_MII_XCVR = 0x010000, - HAS_CHIP_XCVR = 0x020000, - HAS_LNK_CHNG = 0x040000, -}; - -#define RTL_MIN_IO_SIZE 0x80 -#define RTL8139B_IO_SIZE 256 - -#define RTL8129_CAPS HAS_MII_XCVR -#define RTL8139_CAPS HAS_CHIP_XCVR|HAS_LNK_CHNG - -typedef enum { - RTL8139 = 0, - RTL8139_CB, - SMC1211TX, - /*MPX5030,*/ - DELTA8139, - ADDTRON8139, - DFE538TX, - DFE690TXD, - RTL8129, -} board_t; - - -/* indexed by board_t, above */ -static struct { - const char *name; - u32 hw_flags; -} board_info[] __devinitdata = { - { "RealTek RTL8139 Fast Ethernet", RTL8139_CAPS }, - { "RealTek RTL8139B PCI/CardBus", RTL8139_CAPS }, - { "SMC1211TX EZCard 10/100 (RealTek RTL8139)", RTL8139_CAPS }, -/* { MPX5030, "Accton MPX5030 (RealTek RTL8139)", RTL8139_CAPS },*/ - { "Delta Electronics 8139 10/100BaseTX", RTL8139_CAPS }, - { "Addtron Technolgy 8139 10/100BaseTX", RTL8139_CAPS }, - { "D-Link DFE-538TX (RealTek RTL8139)", RTL8139_CAPS }, - { "D-Link DFE-690TXD (RealTek RTL8139)", RTL8139_CAPS }, - { "RealTek RTL8129", RTL8129_CAPS }, -}; - - -static struct pci_device_id rtl8139_pci_tbl[] __devinitdata = { - {0x10ec, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x10ec, 0x8138, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139_CB }, - {0x1113, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, SMC1211TX }, -/* {0x1113, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MPX5030 },*/ - {0x1500, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DELTA8139 }, - {0x4033, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ADDTRON8139 }, - {0x1186, 0x1300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DFE538TX }, - {0x1186, 0x1340, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DFE690TXD }, - -#ifdef CONFIG_8139TOO_8129 - {0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8129 }, -#endif - - /* some crazy cards report invalid vendor ids like - * 0x0001 here. The other ids are valid and constant, - * so we simply don't match on the main vendor id. - */ - {PCI_ANY_ID, 0x8139, 0x10ec, 0x8139, 0, 0, RTL8139 }, - {PCI_ANY_ID, 0x8139, 0x1186, 0x1300, 0, 0, DFE538TX }, - - {0,} -}; -MODULE_DEVICE_TABLE (pci, rtl8139_pci_tbl); - - -/* The rest of these values should never change. */ - -/* Symbolic offsets to registers. */ -enum RTL8139_registers { - MAC0 = 0, /* Ethernet hardware address. */ - MAR0 = 8, /* Multicast filter. */ - TxStatus0 = 0x10, /* Transmit status (Four 32bit registers). */ - TxAddr0 = 0x20, /* Tx descriptors (also four 32bit). */ - RxBuf = 0x30, - ChipCmd = 0x37, - RxBufPtr = 0x38, - RxBufAddr = 0x3A, - IntrMask = 0x3C, - IntrStatus = 0x3E, - TxConfig = 0x40, - ChipVersion = 0x43, - RxConfig = 0x44, - Timer = 0x48, /* A general-purpose counter. */ - RxMissed = 0x4C, /* 24 bits valid, write clears. */ - Cfg9346 = 0x50, - Config0 = 0x51, - Config1 = 0x52, - FlashReg = 0x54, - MediaStatus = 0x58, - Config3 = 0x59, - Config4 = 0x5A, /* absent on RTL-8139A */ - HltClk = 0x5B, - MultiIntr = 0x5C, - TxSummary = 0x60, - BasicModeCtrl = 0x62, - BasicModeStatus = 0x64, - NWayAdvert = 0x66, - NWayLPAR = 0x68, - NWayExpansion = 0x6A, - /* Undocumented registers, but required for proper operation. */ - FIFOTMS = 0x70, /* FIFO Control and test. */ - CSCR = 0x74, /* Chip Status and Configuration Register. */ - PARA78 = 0x78, - PARA7c = 0x7c, /* Magic transceiver parameter register. */ - Config5 = 0xD8, /* absent on RTL-8139A */ -}; - -enum ClearBitMasks { - MultiIntrClear = 0xF000, - ChipCmdClear = 0xE2, - Config1Clear = (1<<7)|(1<<6)|(1<<3)|(1<<2)|(1<<1), -}; - -enum ChipCmdBits { - CmdReset = 0x10, - CmdRxEnb = 0x08, - CmdTxEnb = 0x04, - RxBufEmpty = 0x01, -}; - -/* Interrupt register bits, using my own meaningful names. */ -enum IntrStatusBits { - PCIErr = 0x8000, - PCSTimeout = 0x4000, - RxFIFOOver = 0x40, - RxUnderrun = 0x20, - RxOverflow = 0x10, - TxErr = 0x08, - TxOK = 0x04, - RxErr = 0x02, - RxOK = 0x01, - - RxAckBits = RxFIFOOver | RxOverflow | RxOK, -}; - -enum TxStatusBits { - TxHostOwns = 0x2000, - TxUnderrun = 0x4000, - TxStatOK = 0x8000, - TxOutOfWindow = 0x20000000, - TxAborted = 0x40000000, - TxCarrierLost = 0x80000000, -}; -enum RxStatusBits { - RxMulticast = 0x8000, - RxPhysical = 0x4000, - RxBroadcast = 0x2000, - RxBadSymbol = 0x0020, - RxRunt = 0x0010, - RxTooLong = 0x0008, - RxCRCErr = 0x0004, - RxBadAlign = 0x0002, - RxStatusOK = 0x0001, -}; - -/* Bits in RxConfig. */ -enum rx_mode_bits { - AcceptErr = 0x20, - AcceptRunt = 0x10, - AcceptBroadcast = 0x08, - AcceptMulticast = 0x04, - AcceptMyPhys = 0x02, - AcceptAllPhys = 0x01, -}; - -/* Bits in TxConfig. */ -enum tx_config_bits { - TxIFG1 = (1 << 25), /* Interframe Gap Time */ - TxIFG0 = (1 << 24), /* Enabling these bits violates IEEE 802.3 */ - TxLoopBack = (1 << 18) | (1 << 17), /* enable loopback test mode */ - TxCRC = (1 << 16), /* DISABLE appending CRC to end of Tx packets */ - TxClearAbt = (1 << 0), /* Clear abort (WO) */ - TxDMAShift = 8, /* DMA burst value (0-7) is shifted this many bits */ - TxRetryShift = 4, /* TXRR value (0-15) is shifted this many bits */ - - TxVersionMask = 0x7C800000, /* mask out version bits 30-26, 23 */ -}; - -/* Bits in Config1 */ -enum Config1Bits { - Cfg1_PM_Enable = 0x01, - Cfg1_VPD_Enable = 0x02, - Cfg1_PIO = 0x04, - Cfg1_MMIO = 0x08, - LWAKE = 0x10, /* not on 8139, 8139A */ - Cfg1_Driver_Load = 0x20, - Cfg1_LED0 = 0x40, - Cfg1_LED1 = 0x80, - SLEEP = (1 << 1), /* only on 8139, 8139A */ - PWRDN = (1 << 0), /* only on 8139, 8139A */ -}; - -/* Bits in Config3 */ -enum Config3Bits { - Cfg3_FBtBEn = (1 << 0), /* 1 = Fast Back to Back */ - Cfg3_FuncRegEn = (1 << 1), /* 1 = enable CardBus Function registers */ - Cfg3_CLKRUN_En = (1 << 2), /* 1 = enable CLKRUN */ - Cfg3_CardB_En = (1 << 3), /* 1 = enable CardBus registers */ - Cfg3_LinkUp = (1 << 4), /* 1 = wake up on link up */ - Cfg3_Magic = (1 << 5), /* 1 = wake up on Magic Packet (tm) */ - Cfg3_PARM_En = (1 << 6), /* 0 = software can set twister parameters */ - Cfg3_GNTSel = (1 << 7), /* 1 = delay 1 clock from PCI GNT signal */ -}; - -/* Bits in Config4 */ -enum Config4Bits { - LWPTN = (1 << 2), /* not on 8139, 8139A */ -}; - -/* Bits in Config5 */ -enum Config5Bits { - Cfg5_PME_STS = (1 << 0), /* 1 = PCI reset resets PME_Status */ - Cfg5_LANWake = (1 << 1), /* 1 = enable LANWake signal */ - Cfg5_LDPS = (1 << 2), /* 0 = save power when link is down */ - Cfg5_FIFOAddrPtr = (1 << 3), /* Realtek internal SRAM testing */ - Cfg5_UWF = (1 << 4), /* 1 = accept unicast wakeup frame */ - Cfg5_MWF = (1 << 5), /* 1 = accept multicast wakeup frame */ - Cfg5_BWF = (1 << 6), /* 1 = accept broadcast wakeup frame */ -}; - -enum RxConfigBits { - /* rx fifo threshold */ - RxCfgFIFOShift = 13, - RxCfgFIFONone = (7 << RxCfgFIFOShift), - - /* Max DMA burst */ - RxCfgDMAShift = 8, - RxCfgDMAUnlimited = (7 << RxCfgDMAShift), - - /* rx ring buffer length */ - RxCfgRcv8K = 0, - RxCfgRcv16K = (1 << 11), - RxCfgRcv32K = (1 << 12), - RxCfgRcv64K = (1 << 11) | (1 << 12), - - /* Disable packet wrap at end of Rx buffer */ - RxNoWrap = (1 << 7), -}; - - -/* Twister tuning parameters from RealTek. - Completely undocumented, but required to tune bad links. */ -enum CSCRBits { - CSCR_LinkOKBit = 0x0400, - CSCR_LinkChangeBit = 0x0800, - CSCR_LinkStatusBits = 0x0f000, - CSCR_LinkDownOffCmd = 0x003c0, - CSCR_LinkDownCmd = 0x0f3c0, -}; - - -enum Cfg9346Bits { - Cfg9346_Lock = 0x00, - Cfg9346_Unlock = 0xC0, -}; - - -#define PARA78_default 0x78fa8388 -#define PARA7c_default 0xcb38de43 /* param[0][3] */ -#define PARA7c_xxx 0xcb38de43 -static const unsigned long param[4][4] = { - {0xcb39de43, 0xcb39ce43, 0xfb38de03, 0xcb38de43}, - {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83}, - {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83}, - {0xbb39de43, 0xbb39ce43, 0xbb39ce83, 0xbb39ce83} -}; - -typedef enum { - CH_8139 = 0, - CH_8139_K, - CH_8139A, - CH_8139B, - CH_8130, - CH_8139C, -} chip_t; - -enum chip_flags { - HasHltClk = (1 << 0), - HasLWake = (1 << 1), -}; - - -/* directly indexed by chip_t, above */ -const static struct { - const char *name; - u8 version; /* from RTL8139C docs */ - u32 RxConfigMask; /* should clear the bits supported by this chip */ - u32 flags; -} rtl_chip_info[] = { - { "RTL-8139", - 0x40, - 0xf0fe0040, /* XXX copied from RTL8139A, verify */ - HasHltClk, - }, - - { "RTL-8139 rev K", - 0x60, - 0xf0fe0040, - HasHltClk, - }, - - { "RTL-8139A", - 0x70, - 0xf0fe0040, - HasHltClk, /* XXX undocumented? */ - }, - - { "RTL-8139B", - 0x78, - 0xf0fc0040, - HasLWake, - }, - - { "RTL-8130", - 0x7C, - 0xf0fe0040, /* XXX copied from RTL8139A, verify */ - HasLWake, - }, - - { "RTL-8139C", - 0x74, - 0xf0fc0040, /* XXX copied from RTL8139B, verify */ - HasLWake, - }, - -}; - -struct rtl_extra_stats { - unsigned long early_rx; - unsigned long tx_buf_mapped; - unsigned long tx_timeouts; -}; - -struct rtl8139_private { - void *mmio_addr; - int drv_flags; - struct pci_dev *pci_dev; - struct net_device_stats stats; - unsigned char *rx_ring; - unsigned int cur_rx; /* Index into the Rx buffer of next Rx pkt. */ - unsigned int tx_flag; - unsigned long cur_tx; - unsigned long dirty_tx; - unsigned char *tx_buf[NUM_TX_DESC]; /* Tx bounce buffers */ - unsigned char *tx_bufs; /* Tx bounce buffer region. */ - dma_addr_t rx_ring_dma; - dma_addr_t tx_bufs_dma; - signed char phys[4]; /* MII device addresses. */ - char twistie, twist_row, twist_col; /* Twister tune state. */ - unsigned int full_duplex:1; /* Full-duplex operation requested. */ - unsigned int duplex_lock:1; - unsigned int default_port:4; /* Last dev->if_port value. */ - unsigned int media2:4; /* Secondary monitored media port. */ - unsigned int medialock:1; /* Don't sense media type. */ - unsigned int mediasense:1; /* Media sensing in progress. */ - spinlock_t lock; - chip_t chipset; - u32 rx_config; - struct rtl_extra_stats xstats; - int time_to_die; -}; - -MODULE_AUTHOR ("Jeff Garzik "); -MODULE_DESCRIPTION ("RealTek RTL-8139 Fast Ethernet driver"); -MODULE_LICENSE("GPL"); - -MODULE_PARM (multicast_filter_limit, "i"); -MODULE_PARM (max_interrupt_work, "i"); -MODULE_PARM (media, "1-" __MODULE_STRING(MAX_UNITS) "i"); -MODULE_PARM (full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); -MODULE_PARM_DESC (multicast_filter_limit, "8139too maximum number of filtered multicast addresses"); -MODULE_PARM_DESC (max_interrupt_work, "8139too maximum events handled per interrupt"); -MODULE_PARM_DESC (media, "8139too: Bits 4+9: force full duplex, bit 5: 100Mbps"); -MODULE_PARM_DESC (full_duplex, "8139too: Force full duplex for board(s) (1)"); - -static int read_eeprom (void *ioaddr, int location, int addr_len); -static int rtl8139_open (struct net_device *dev); -static int mdio_read (struct net_device *dev, int phy_id, int location); -static void mdio_write (struct net_device *dev, int phy_id, int location, - int val); -static void rtl8139_tx_timeout (struct net_device *dev); -static void rtl8139_init_ring (struct net_device *dev); -static int rtl8139_start_xmit (struct sk_buff *skb, - struct net_device *dev); -static void rtl8139_interrupt (int irq, void *dev_instance, - struct pt_regs *regs); -static int rtl8139_close (struct net_device *dev); -static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd); -static struct net_device_stats *rtl8139_get_stats (struct net_device *dev); -static inline u32 ether_crc (int length, unsigned char *data); -static void rtl8139_set_rx_mode (struct net_device *dev); -static void __set_rx_mode (struct net_device *dev); -static void rtl8139_hw_start (struct net_device *dev); - -#ifdef USE_IO_OPS - -#define RTL_R8(reg) inb (((unsigned long)ioaddr) + (reg)) -#define RTL_R16(reg) inw (((unsigned long)ioaddr) + (reg)) -#define RTL_R32(reg) ((unsigned long) inl (((unsigned long)ioaddr) + (reg))) -#define RTL_W8(reg, val8) outb ((val8), ((unsigned long)ioaddr) + (reg)) -#define RTL_W16(reg, val16) outw ((val16), ((unsigned long)ioaddr) + (reg)) -#define RTL_W32(reg, val32) outl ((val32), ((unsigned long)ioaddr) + (reg)) -#define RTL_W8_F RTL_W8 -#define RTL_W16_F RTL_W16 -#define RTL_W32_F RTL_W32 -#undef readb -#undef readw -#undef readl -#undef writeb -#undef writew -#undef writel -#define readb(addr) inb((unsigned long)(addr)) -#define readw(addr) inw((unsigned long)(addr)) -#define readl(addr) inl((unsigned long)(addr)) -#define writeb(val,addr) outb((val),(unsigned long)(addr)) -#define writew(val,addr) outw((val),(unsigned long)(addr)) -#define writel(val,addr) outl((val),(unsigned long)(addr)) - -#else - -/* write MMIO register, with flush */ -/* Flush avoids rtl8139 bug w/ posted MMIO writes */ -#define RTL_W8_F(reg, val8) do { writeb ((val8), ioaddr + (reg)); readb (ioaddr + (reg)); } while (0) -#define RTL_W16_F(reg, val16) do { writew ((val16), ioaddr + (reg)); readw (ioaddr + (reg)); } while (0) -#define RTL_W32_F(reg, val32) do { writel ((val32), ioaddr + (reg)); readl (ioaddr + (reg)); } while (0) - - -#define MMIO_FLUSH_AUDIT_COMPLETE 1 -#if MMIO_FLUSH_AUDIT_COMPLETE - -/* write MMIO register */ -#define RTL_W8(reg, val8) writeb ((val8), ioaddr + (reg)) -#define RTL_W16(reg, val16) writew ((val16), ioaddr + (reg)) -#define RTL_W32(reg, val32) writel ((val32), ioaddr + (reg)) - -#else - -/* write MMIO register, then flush */ -#define RTL_W8 RTL_W8_F -#define RTL_W16 RTL_W16_F -#define RTL_W32 RTL_W32_F - -#endif /* MMIO_FLUSH_AUDIT_COMPLETE */ - -/* read MMIO register */ -#define RTL_R8(reg) readb (ioaddr + (reg)) -#define RTL_R16(reg) readw (ioaddr + (reg)) -#define RTL_R32(reg) ((unsigned long) readl (ioaddr + (reg))) - -#endif /* USE_IO_OPS */ - - -static const u16 rtl8139_intr_mask = - PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver | - TxErr | TxOK | RxErr | RxOK; - -static const unsigned int rtl8139_rx_config = - RxCfgRcv32K | RxNoWrap | - (RX_FIFO_THRESH << RxCfgFIFOShift) | - (RX_DMA_BURST << RxCfgDMAShift); - -static const unsigned int rtl8139_tx_config = - (TX_DMA_BURST << TxDMAShift) | (TX_RETRY << TxRetryShift); - -static void __rtl8139_cleanup_dev (struct net_device *dev) -{ - struct rtl8139_private *tp; - struct pci_dev *pdev; - - assert (dev != NULL); - assert (dev->priv != NULL); - - tp = dev->priv; - assert (tp->pci_dev != NULL); - pdev = tp->pci_dev; - -#ifndef USE_IO_OPS - if (tp->mmio_addr) - iounmap (tp->mmio_addr); -#endif /* !USE_IO_OPS */ - - /* it's ok to call this even if we have no regions to free */ - pci_release_regions (pdev); - -#ifndef RTL8139_NDEBUG - /* poison memory before freeing */ - memset (dev, 0xBC, - sizeof (struct net_device) + - sizeof (struct rtl8139_private)); -#endif /* RTL8139_NDEBUG */ - - kfree (dev); - - pci_set_drvdata (pdev, NULL); -} - - -static void rtl8139_chip_reset (void *ioaddr) -{ - int i; - - /* Soft reset the chip. */ - RTL_W8 (ChipCmd, CmdReset); - - /* Check that the chip has finished the reset. */ - for (i = 1000; i > 0; i--) { - barrier(); - if ((RTL_R8 (ChipCmd) & CmdReset) == 0) - break; - udelay (10); - } -} - - -static int __devinit rtl8139_init_board (struct pci_dev *pdev, - struct net_device **dev_out) -{ - void *ioaddr; - struct net_device *dev; - struct rtl8139_private *tp; - u8 tmp8; - int rc; - unsigned int i; - u32 pio_start, pio_end, pio_flags, pio_len; - unsigned long mmio_start, mmio_end, mmio_flags, mmio_len; - u32 tmp; - - assert (pdev != NULL); - - *dev_out = NULL; - - /* dev and dev->priv zeroed in alloc_etherdev */ - dev = alloc_etherdev (sizeof (*tp)); - if (dev == NULL) { - printk (KERN_ERR PFX "%s: Unable to alloc new net device\n", pdev->slot_name); - return -ENOMEM; - } - SET_MODULE_OWNER(dev); - tp = dev->priv; - tp->pci_dev = pdev; - - /* enable device (incl. PCI PM wakeup and hotplug setup) */ - rc = pci_enable_device (pdev); - if (rc) - goto err_out; - - pio_start = pci_resource_start (pdev, 0); - pio_end = pci_resource_end (pdev, 0); - pio_flags = pci_resource_flags (pdev, 0); - pio_len = pci_resource_len (pdev, 0); - - mmio_start = pci_resource_start (pdev, 1); - mmio_end = pci_resource_end (pdev, 1); - mmio_flags = pci_resource_flags (pdev, 1); - mmio_len = pci_resource_len (pdev, 1); - - /* set this immediately, we need to know before - * we talk to the chip directly */ - DPRINTK("PIO region size == 0x%02X\n", pio_len); - DPRINTK("MMIO region size == 0x%02lX\n", mmio_len); - -#ifdef USE_IO_OPS - /* make sure PCI base addr 0 is PIO */ - if (!(pio_flags & IORESOURCE_IO)) { - printk (KERN_ERR PFX "%s: region #0 not a PIO resource, aborting\n", pdev->slot_name); - rc = -ENODEV; - goto err_out; - } - /* check for weird/broken PCI region reporting */ - if (pio_len < RTL_MIN_IO_SIZE) { - printk (KERN_ERR PFX "%s: Invalid PCI I/O region size(s), aborting\n", pdev->slot_name); - rc = -ENODEV; - goto err_out; - } -#else - /* make sure PCI base addr 1 is MMIO */ - if (!(mmio_flags & IORESOURCE_MEM)) { - printk (KERN_ERR PFX "%s: region #1 not an MMIO resource, aborting\n", pdev->slot_name); - rc = -ENODEV; - goto err_out; - } - if (mmio_len < RTL_MIN_IO_SIZE) { - printk (KERN_ERR PFX "%s: Invalid PCI mem region size(s), aborting\n", pdev->slot_name); - rc = -ENODEV; - goto err_out; - } -#endif - - rc = pci_request_regions (pdev, "8139too"); - if (rc) - goto err_out; - - /* enable PCI bus-mastering */ - pci_set_master (pdev); - -#ifdef USE_IO_OPS - ioaddr = (void *) pio_start; - dev->base_addr = pio_start; - tp->mmio_addr = ioaddr; -#else - /* ioremap MMIO region */ - ioaddr = ioremap (mmio_start, mmio_len); - if (ioaddr == NULL) { - printk (KERN_ERR PFX "%s: cannot remap MMIO, aborting\n", pdev->slot_name); - rc = -EIO; - goto err_out; - } - dev->base_addr = (long) ioaddr; - tp->mmio_addr = ioaddr; -#endif /* USE_IO_OPS */ - - /* Bring old chips out of low-power mode. */ - RTL_W8 (HltClk, 'R'); - - /* check for missing/broken hardware */ - if (RTL_R32 (TxConfig) == 0xFFFFFFFF) { - printk (KERN_ERR PFX "%s: Chip not responding, ignoring board\n", - pdev->slot_name); - rc = -EIO; - goto err_out; - } - - /* identify chip attached to board */ - tmp = RTL_R8 (ChipVersion); - for (i = 0; i < ARRAY_SIZE (rtl_chip_info); i++) - if (tmp == rtl_chip_info[i].version) { - tp->chipset = i; - goto match; - } - - /* if unknown chip, assume array element #0, original RTL-8139 in this case */ - printk (KERN_DEBUG PFX "%s: unknown chip version, assuming RTL-8139\n", - pdev->slot_name); - printk (KERN_DEBUG PFX "%s: TxConfig = 0x%lx\n", pdev->slot_name, RTL_R32 (TxConfig)); - tp->chipset = 0; - -match: - DPRINTK ("chipset id (%d) == index %d, '%s'\n", - tmp, - tp->chipset, - rtl_chip_info[tp->chipset].name); - - if (tp->chipset >= CH_8139B) { - u8 new_tmp8 = tmp8 = RTL_R8 (Config1); - DPRINTK("PCI PM wakeup\n"); - if ((rtl_chip_info[tp->chipset].flags & HasLWake) && - (tmp8 & LWAKE)) - new_tmp8 &= ~LWAKE; - new_tmp8 |= Cfg1_PM_Enable; - if (new_tmp8 != tmp8) { - RTL_W8 (Cfg9346, Cfg9346_Unlock); - RTL_W8 (Config1, tmp8); - RTL_W8 (Cfg9346, Cfg9346_Lock); - } - if (rtl_chip_info[tp->chipset].flags & HasLWake) { - tmp8 = RTL_R8 (Config4); - if (tmp8 & LWPTN) - RTL_W8 (Config4, tmp8 & ~LWPTN); - } - } else { - DPRINTK("Old chip wakeup\n"); - tmp8 = RTL_R8 (Config1); - tmp8 &= ~(SLEEP | PWRDN); - RTL_W8 (Config1, tmp8); - } - - rtl8139_chip_reset (ioaddr); - - *dev_out = dev; - return 0; - -err_out: - __rtl8139_cleanup_dev (dev); - return rc; -} - - -static int __devinit rtl8139_init_one (struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - struct net_device *dev = NULL; - struct rtl8139_private *tp; - int i, addr_len, option; - void *ioaddr; - static int board_idx = -1; - u8 pci_rev; - - assert (pdev != NULL); - assert (ent != NULL); - - board_idx++; - - /* when we're built into the kernel, the driver version message - * is only printed if at least one 8139 board has been found - */ -#ifndef MODULE - { - static int printed_version; - if (!printed_version++) - printk (KERN_INFO RTL8139_DRIVER_NAME "\n"); - } -#endif - - pci_read_config_byte(pdev, PCI_REVISION_ID, &pci_rev); - - if (pdev->vendor == PCI_VENDOR_ID_REALTEK && - pdev->device == PCI_DEVICE_ID_REALTEK_8139 && pci_rev >= 0x20) { - printk(KERN_INFO PFX "pci dev %s (id %04x:%04x rev %02x) is an enhanced 8139C+ chip\n", - pdev->slot_name, pdev->vendor, pdev->device, pci_rev); - printk(KERN_INFO PFX "Ensure the \"8139cp\" driver is installed!\n"); - return -ENODEV; /* force use of better driver */ - } - - i = rtl8139_init_board (pdev, &dev); - if (i < 0) - return i; - - tp = dev->priv; - ioaddr = tp->mmio_addr; - - assert (ioaddr != NULL); - assert (dev != NULL); - assert (tp != NULL); - - addr_len = read_eeprom (ioaddr, 0, 8) == 0x8129 ? 8 : 6; - for (i = 0; i < 3; i++) - ((u16 *) (dev->dev_addr))[i] = - le16_to_cpu (read_eeprom (ioaddr, i + 7, addr_len)); - - /* The Rtl8139-specific entries in the device structure. */ - dev->open = rtl8139_open; - dev->hard_start_xmit = rtl8139_start_xmit; - dev->stop = rtl8139_close; - dev->get_stats = rtl8139_get_stats; - dev->set_multicast_list = rtl8139_set_rx_mode; - dev->do_ioctl = netdev_ioctl; - dev->tx_timeout = rtl8139_tx_timeout; - dev->watchdog_timeo = TX_TIMEOUT; - dev->features |= NETIF_F_SG; - - dev->irq = pdev->irq; - - /* dev->priv/tp zeroed and aligned in init_etherdev */ - tp = dev->priv; - - /* note: tp->chipset set in rtl8139_init_board */ - tp->drv_flags = board_info[ent->driver_data].hw_flags; - tp->mmio_addr = ioaddr; - spin_lock_init (&tp->lock); - - /* dev is fully set up and ready to use now */ - DPRINTK("about to register device named %s (%p)...\n", dev->name, dev); - i = register_netdev (dev); - if (i) goto err_out; - - pci_set_drvdata (pdev, dev); - - printk (KERN_INFO "%s: %s at 0x%lx, " - "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, " - "IRQ %d\n", - dev->name, - board_info[ent->driver_data].name, - dev->base_addr, - dev->dev_addr[0], dev->dev_addr[1], - dev->dev_addr[2], dev->dev_addr[3], - dev->dev_addr[4], dev->dev_addr[5], - dev->irq); - - printk (KERN_DEBUG "%s: Identified 8139 chip type '%s'\n", - dev->name, rtl_chip_info[tp->chipset].name); - - /* Find the connected MII xcvrs. - Doing this in open() would allow detecting external xcvrs later, but - takes too much time. */ -#ifdef CONFIG_8139TOO_8129 - if (tp->drv_flags & HAS_MII_XCVR) { - int phy, phy_idx = 0; - for (phy = 0; phy < 32 && phy_idx < sizeof(tp->phys); phy++) { - int mii_status = mdio_read(dev, phy, 1); - if (mii_status != 0xffff && mii_status != 0x0000) { - u16 advertising = mdio_read(dev, phy, 4); - tp->phys[phy_idx++] = phy; - printk(KERN_INFO "%s: MII transceiver %d status 0x%4.4x " - "advertising %4.4x.\n", - dev->name, phy, mii_status, advertising); - } - } - if (phy_idx == 0) { - printk(KERN_INFO "%s: No MII transceivers found! Assuming SYM " - "transceiver.\n", - dev->name); - tp->phys[0] = 32; - } - } else -#endif - tp->phys[0] = 32; - - /* The lower four bits are the media type. */ - option = (board_idx >= MAX_UNITS) ? 0 : media[board_idx]; - if (option > 0) { - tp->full_duplex = (option & 0x210) ? 1 : 0; - tp->default_port = option & 0xFF; - if (tp->default_port) - tp->medialock = 1; - } - if (board_idx < MAX_UNITS && full_duplex[board_idx] > 0) - tp->full_duplex = full_duplex[board_idx]; - if (tp->full_duplex) { - printk(KERN_INFO "%s: Media type forced to Full Duplex.\n", dev->name); - /* Changing the MII-advertised media because might prevent - re-connection. */ - tp->duplex_lock = 1; - } - if (tp->default_port) { - printk(KERN_INFO " Forcing %dMbps %s-duplex operation.\n", - (option & 0x20 ? 100 : 10), - (option & 0x10 ? "full" : "half")); - mdio_write(dev, tp->phys[0], 0, - ((option & 0x20) ? 0x2000 : 0) | /* 100Mbps? */ - ((option & 0x10) ? 0x0100 : 0)); /* Full duplex? */ - } - - /* Put the chip into low-power mode. */ - if (rtl_chip_info[tp->chipset].flags & HasHltClk) - RTL_W8 (HltClk, 'H'); /* 'R' would leave the clock running. */ - - return 0; - -err_out: - __rtl8139_cleanup_dev (dev); - return i; -} - - -static void __devexit rtl8139_remove_one (struct pci_dev *pdev) -{ - struct net_device *dev = pci_get_drvdata (pdev); - struct rtl8139_private *np; - - assert (dev != NULL); - np = dev->priv; - assert (np != NULL); - - unregister_netdev (dev); - - __rtl8139_cleanup_dev (dev); -} - - -/* Serial EEPROM section. */ - -/* EEPROM_Ctrl bits. */ -#define EE_SHIFT_CLK 0x04 /* EEPROM shift clock. */ -#define EE_CS 0x08 /* EEPROM chip select. */ -#define EE_DATA_WRITE 0x02 /* EEPROM chip data in. */ -#define EE_WRITE_0 0x00 -#define EE_WRITE_1 0x02 -#define EE_DATA_READ 0x01 /* EEPROM chip data out. */ -#define EE_ENB (0x80 | EE_CS) - -/* Delay between EEPROM clock transitions. - No extra delay is needed with 33Mhz PCI, but 66Mhz may change this. - */ - -#define eeprom_delay() readl(ee_addr) - -/* The EEPROM commands include the alway-set leading bit. */ -#define EE_WRITE_CMD (5) -#define EE_READ_CMD (6) -#define EE_ERASE_CMD (7) - -static int __devinit read_eeprom (void *ioaddr, int location, int addr_len) -{ - int i; - unsigned retval = 0; - void *ee_addr = ioaddr + Cfg9346; - int read_cmd = location | (EE_READ_CMD << addr_len); - - writeb (EE_ENB & ~EE_CS, ee_addr); - writeb (EE_ENB, ee_addr); - eeprom_delay (); - - /* Shift the read command bits out. */ - for (i = 4 + addr_len; i >= 0; i--) { - int dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; - writeb (EE_ENB | dataval, ee_addr); - eeprom_delay (); - writeb (EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); - eeprom_delay (); - } - writeb (EE_ENB, ee_addr); - eeprom_delay (); - - for (i = 16; i > 0; i--) { - writeb (EE_ENB | EE_SHIFT_CLK, ee_addr); - eeprom_delay (); - retval = - (retval << 1) | ((readb (ee_addr) & EE_DATA_READ) ? 1 : - 0); - writeb (EE_ENB, ee_addr); - eeprom_delay (); - } - - /* Terminate the EEPROM access. */ - writeb (~EE_CS, ee_addr); - eeprom_delay (); - - return retval; -} - -/* MII serial management: mostly bogus for now. */ -/* Read and write the MII management registers using software-generated - serial MDIO protocol. - The maximum data clock rate is 2.5 Mhz. The minimum timing is usually - met by back-to-back PCI I/O cycles, but we insert a delay to avoid - "overclocking" issues. */ -#define MDIO_DIR 0x80 -#define MDIO_DATA_OUT 0x04 -#define MDIO_DATA_IN 0x02 -#define MDIO_CLK 0x01 -#define MDIO_WRITE0 (MDIO_DIR) -#define MDIO_WRITE1 (MDIO_DIR | MDIO_DATA_OUT) - -#define mdio_delay(mdio_addr) readb(mdio_addr) - - -static char mii_2_8139_map[8] = { - BasicModeCtrl, - BasicModeStatus, - 0, - 0, - NWayAdvert, - NWayLPAR, - NWayExpansion, - 0 -}; - - -#ifdef CONFIG_8139TOO_8129 -/* Syncronize the MII management interface by shifting 32 one bits out. */ -static void mdio_sync (void *mdio_addr) -{ - int i; - - for (i = 32; i >= 0; i--) { - writeb (MDIO_WRITE1, mdio_addr); - mdio_delay (mdio_addr); - writeb (MDIO_WRITE1 | MDIO_CLK, mdio_addr); - mdio_delay (mdio_addr); - } -} -#endif - -static int mdio_read (struct net_device *dev, int phy_id, int location) -{ - struct rtl8139_private *tp = dev->priv; - int retval = 0; -#ifdef CONFIG_8139TOO_8129 - void *mdio_addr = tp->mmio_addr + Config4; - int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location; - int i; -#endif - - if (phy_id > 31) { /* Really a 8139. Use internal registers. */ - return location < 8 && mii_2_8139_map[location] ? - readw (tp->mmio_addr + mii_2_8139_map[location]) : 0; - } - -#ifdef CONFIG_8139TOO_8129 - mdio_sync (mdio_addr); - /* Shift the read command bits out. */ - for (i = 15; i >= 0; i--) { - int dataval = (mii_cmd & (1 << i)) ? MDIO_DATA_OUT : 0; - - writeb (MDIO_DIR | dataval, mdio_addr); - mdio_delay (mdio_addr); - writeb (MDIO_DIR | dataval | MDIO_CLK, mdio_addr); - mdio_delay (mdio_addr); - } - - /* Read the two transition, 16 data, and wire-idle bits. */ - for (i = 19; i > 0; i--) { - writeb (0, mdio_addr); - mdio_delay (mdio_addr); - retval = (retval << 1) | ((readb (mdio_addr) & MDIO_DATA_IN) ? 1 : 0); - writeb (MDIO_CLK, mdio_addr); - mdio_delay (mdio_addr); - } -#endif - - return (retval >> 1) & 0xffff; -} - - -static void mdio_write (struct net_device *dev, int phy_id, int location, - int value) -{ - struct rtl8139_private *tp = dev->priv; -#ifdef CONFIG_8139TOO_8129 - void *mdio_addr = tp->mmio_addr + Config4; - int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location << 18) | value; - int i; -#endif - - if (phy_id > 31) { /* Really a 8139. Use internal registers. */ - void *ioaddr = tp->mmio_addr; - if (location == 0) { - RTL_W8 (Cfg9346, Cfg9346_Unlock); - RTL_W16 (BasicModeCtrl, value); - RTL_W8 (Cfg9346, Cfg9346_Lock); - } else if (location < 8 && mii_2_8139_map[location]) - RTL_W16 (mii_2_8139_map[location], value); - return; - } - -#ifdef CONFIG_8139TOO_8129 - mdio_sync (mdio_addr); - - /* Shift the command bits out. */ - for (i = 31; i >= 0; i--) { - int dataval = - (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; - writeb (dataval, mdio_addr); - mdio_delay (mdio_addr); - writeb (dataval | MDIO_CLK, mdio_addr); - mdio_delay (mdio_addr); - } - /* Clear out extra bits. */ - for (i = 2; i > 0; i--) { - writeb (0, mdio_addr); - mdio_delay (mdio_addr); - writeb (MDIO_CLK, mdio_addr); - mdio_delay (mdio_addr); - } -#endif -} - - -static int rtl8139_open (struct net_device *dev) -{ - struct rtl8139_private *tp = dev->priv; - int retval; -#ifdef RTL8139_DEBUG - void *ioaddr = tp->mmio_addr; -#endif - - retval = request_irq (dev->irq, rtl8139_interrupt, SA_SHIRQ, dev->name, dev); - if (retval) - return retval; - - tp->tx_bufs = pci_alloc_consistent(tp->pci_dev, TX_BUF_TOT_LEN, - &tp->tx_bufs_dma); - tp->rx_ring = pci_alloc_consistent(tp->pci_dev, RX_BUF_TOT_LEN, - &tp->rx_ring_dma); - if (tp->tx_bufs == NULL || tp->rx_ring == NULL) { - free_irq(dev->irq, dev); - - if (tp->tx_bufs) - pci_free_consistent(tp->pci_dev, TX_BUF_TOT_LEN, - tp->tx_bufs, tp->tx_bufs_dma); - if (tp->rx_ring) - pci_free_consistent(tp->pci_dev, RX_BUF_TOT_LEN, - tp->rx_ring, tp->rx_ring_dma); - - return -ENOMEM; - - } - - tp->full_duplex = tp->duplex_lock; - tp->tx_flag = (TX_FIFO_THRESH << 11) & 0x003f0000; - tp->twistie = 1; - tp->time_to_die = 0; - - rtl8139_init_ring (dev); - rtl8139_hw_start (dev); - - DPRINTK ("%s: rtl8139_open() ioaddr %#lx IRQ %d" - " GP Pins %2.2x %s-duplex.\n", - dev->name, pci_resource_start (tp->pci_dev, 1), - dev->irq, RTL_R8 (MediaStatus), - tp->full_duplex ? "full" : "half"); - - return 0; -} - - -static void rtl_check_media (struct net_device *dev) -{ - struct rtl8139_private *tp = dev->priv; - - if (tp->phys[0] >= 0) { - u16 mii_reg5 = mdio_read(dev, tp->phys[0], 5); - if (mii_reg5 == 0xffff) - ; /* Not there */ - else if ((mii_reg5 & 0x0100) == 0x0100 - || (mii_reg5 & 0x00C0) == 0x0040) - tp->full_duplex = 1; - - printk (KERN_INFO"%s: Setting %s%s-duplex based on" - " auto-negotiated partner ability %4.4x.\n", - dev->name, mii_reg5 == 0 ? "" : - (mii_reg5 & 0x0180) ? "100mbps " : "10mbps ", - tp->full_duplex ? "full" : "half", mii_reg5); - } -} - -/* Start the hardware at open or resume. */ -static void rtl8139_hw_start (struct net_device *dev) -{ - struct rtl8139_private *tp = dev->priv; - void *ioaddr = tp->mmio_addr; - u32 i; - u8 tmp; - - /* Bring old chips out of low-power mode. */ - if (rtl_chip_info[tp->chipset].flags & HasHltClk) - RTL_W8 (HltClk, 'R'); - - rtl8139_chip_reset (ioaddr); - - /* unlock Config[01234] and BMCR register writes */ - RTL_W8_F (Cfg9346, Cfg9346_Unlock); - /* Restore our idea of the MAC address. */ - RTL_W32_F (MAC0 + 0, cpu_to_le32 (*(u32 *) (dev->dev_addr + 0))); - RTL_W32_F (MAC0 + 4, cpu_to_le32 (*(u32 *) (dev->dev_addr + 4))); - - /* Must enable Tx/Rx before setting transfer thresholds! */ - RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb); - - tp->rx_config = rtl8139_rx_config | AcceptBroadcast | AcceptMyPhys; - RTL_W32 (RxConfig, tp->rx_config); - - /* Check this value: the documentation for IFG contradicts ifself. */ - RTL_W32 (TxConfig, rtl8139_tx_config); - - tp->cur_rx = 0; - - rtl_check_media (dev); - - if (tp->chipset >= CH_8139B) { - /* Disable magic packet scanning, which is enabled - * when PM is enabled in Config1. It can be reenabled - * via ETHTOOL_SWOL if desired. */ - RTL_W8 (Config3, RTL_R8 (Config3) & ~Cfg3_Magic); - } - - DPRINTK("init buffer addresses\n"); - - /* Lock Config[01234] and BMCR register writes */ - RTL_W8 (Cfg9346, Cfg9346_Lock); - - /* init Rx ring buffer DMA address */ - RTL_W32_F (RxBuf, tp->rx_ring_dma); - - /* init Tx buffer DMA addresses */ - for (i = 0; i < NUM_TX_DESC; i++) - RTL_W32_F (TxAddr0 + (i * 4), tp->tx_bufs_dma + (tp->tx_buf[i] - tp->tx_bufs)); - - RTL_W32 (RxMissed, 0); - - rtl8139_set_rx_mode (dev); - - /* no early-rx interrupts */ - RTL_W16 (MultiIntr, RTL_R16 (MultiIntr) & MultiIntrClear); - - /* make sure RxTx has started */ - tmp = RTL_R8 (ChipCmd); - if ((!(tmp & CmdRxEnb)) || (!(tmp & CmdTxEnb))) - RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb); - - /* Enable all known interrupts by setting the interrupt mask. */ - RTL_W16 (IntrMask, rtl8139_intr_mask); - - netif_start_queue (dev); -} - - -/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ -static void rtl8139_init_ring (struct net_device *dev) -{ - struct rtl8139_private *tp = dev->priv; - int i; - - tp->cur_rx = 0; - tp->cur_tx = 0; - tp->dirty_tx = 0; - - for (i = 0; i < NUM_TX_DESC; i++) - tp->tx_buf[i] = &tp->tx_bufs[i * TX_BUF_SIZE]; -} - - -static void rtl8139_tx_clear (struct rtl8139_private *tp) -{ - tp->cur_tx = 0; - tp->dirty_tx = 0; - - /* XXX account for unsent Tx packets in tp->stats.tx_dropped */ -} - - -static void rtl8139_tx_timeout (struct net_device *dev) -{ - struct rtl8139_private *tp = dev->priv; - void *ioaddr = tp->mmio_addr; - int i; - u8 tmp8; - unsigned long flags; - - DPRINTK ("%s: Transmit timeout, status %2.2x %4.4x " - "media %2.2x.\n", dev->name, - RTL_R8 (ChipCmd), - RTL_R16 (IntrStatus), - RTL_R8 (MediaStatus)); - - tp->xstats.tx_timeouts++; - - /* disable Tx ASAP, if not already */ - tmp8 = RTL_R8 (ChipCmd); - if (tmp8 & CmdTxEnb) - RTL_W8 (ChipCmd, CmdRxEnb); - - /* Disable interrupts by clearing the interrupt mask. */ - RTL_W16 (IntrMask, 0x0000); - - /* Emit info to figure out what went wrong. */ - printk (KERN_DEBUG "%s: Tx queue start entry %ld dirty entry %ld.\n", - dev->name, tp->cur_tx, tp->dirty_tx); - for (i = 0; i < NUM_TX_DESC; i++) - printk (KERN_DEBUG "%s: Tx descriptor %d is %8.8lx.%s\n", - dev->name, i, RTL_R32 (TxStatus0 + (i * 4)), - i == tp->dirty_tx % NUM_TX_DESC ? - " (queue head)" : ""); - - /* Stop a shared interrupt from scavenging while we are. */ - spin_lock_irqsave (&tp->lock, flags); - rtl8139_tx_clear (tp); - spin_unlock_irqrestore (&tp->lock, flags); - - /* ...and finally, reset everything */ - rtl8139_hw_start (dev); - - netif_wake_queue (dev); -} - - -static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev) -{ - struct rtl8139_private *tp = dev->priv; - void *ioaddr = tp->mmio_addr; - unsigned int entry; - - /* Calculate the next Tx descriptor entry. */ - entry = tp->cur_tx % NUM_TX_DESC; - - if (likely(skb->len < TX_BUF_SIZE)) { - skb_copy_bits(skb, 0, tp->tx_buf[entry], skb->len); - dev_kfree_skb(skb); - } else { - dev_kfree_skb(skb); - tp->stats.tx_dropped++; - return 0; - } - - /* Note: the chip doesn't have auto-pad! */ - spin_lock_irq(&tp->lock); - RTL_W32_F (TxStatus0 + (entry * sizeof (u32)), - tp->tx_flag | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN)); - - dev->trans_start = jiffies; - - tp->cur_tx++; - wmb(); - - if ((tp->cur_tx - NUM_TX_DESC) == tp->dirty_tx) - netif_stop_queue (dev); - spin_unlock_irq(&tp->lock); - - DPRINTK ("%s: Queued Tx packet at %p size %u to slot %d.\n", - dev->name, skb->data, skb->len, entry); - - return 0; -} - - -static void rtl8139_tx_interrupt (struct net_device *dev, - struct rtl8139_private *tp, - void *ioaddr) -{ - unsigned long dirty_tx, tx_left; - - assert (dev != NULL); - assert (tp != NULL); - assert (ioaddr != NULL); - - dirty_tx = tp->dirty_tx; - tx_left = tp->cur_tx - dirty_tx; - while (tx_left > 0) { - int entry = dirty_tx % NUM_TX_DESC; - int txstatus; - - txstatus = RTL_R32 (TxStatus0 + (entry * sizeof (u32))); - - if (!(txstatus & (TxStatOK | TxUnderrun | TxAborted))) - break; /* It still hasn't been Txed */ - - /* Note: TxCarrierLost is always asserted at 100mbps. */ - if (txstatus & (TxOutOfWindow | TxAborted)) { - /* There was an major error, log it. */ - DPRINTK ("%s: Transmit error, Tx status %8.8x.\n", - dev->name, txstatus); - tp->stats.tx_errors++; - if (txstatus & TxAborted) { - tp->stats.tx_aborted_errors++; - RTL_W32 (TxConfig, TxClearAbt); - RTL_W16 (IntrStatus, TxErr); - wmb(); - } - if (txstatus & TxCarrierLost) - tp->stats.tx_carrier_errors++; - if (txstatus & TxOutOfWindow) - tp->stats.tx_window_errors++; -#ifdef ETHER_STATS - if ((txstatus & 0x0f000000) == 0x0f000000) - tp->stats.collisions16++; -#endif - } else { - if (txstatus & TxUnderrun) { - /* Add 64 to the Tx FIFO threshold. */ - if (tp->tx_flag < 0x00300000) - tp->tx_flag += 0x00020000; - tp->stats.tx_fifo_errors++; - } - tp->stats.collisions += (txstatus >> 24) & 15; - tp->stats.tx_bytes += txstatus & 0x7ff; - tp->stats.tx_packets++; - } - - dirty_tx++; - tx_left--; - } - -#ifndef RTL8139_NDEBUG - if (tp->cur_tx - dirty_tx > NUM_TX_DESC) { - printk (KERN_ERR "%s: Out-of-sync dirty pointer, %ld vs. %ld.\n", - dev->name, dirty_tx, tp->cur_tx); - dirty_tx += NUM_TX_DESC; - } -#endif /* RTL8139_NDEBUG */ - - /* only wake the queue if we did work, and the queue is stopped */ - if (tp->dirty_tx != dirty_tx) { - tp->dirty_tx = dirty_tx; - mb(); - if (netif_queue_stopped (dev)) - netif_wake_queue (dev); - } -} - - -/* TODO: clean this up! Rx reset need not be this intensive */ -static void rtl8139_rx_err (u32 rx_status, struct net_device *dev, - struct rtl8139_private *tp, void *ioaddr) -{ - u8 tmp8; - int tmp_work; - - DPRINTK ("%s: Ethernet frame had errors, status %8.8x.\n", - dev->name, rx_status); - if (rx_status & RxTooLong) { - DPRINTK ("%s: Oversized Ethernet frame, status %4.4x!\n", - dev->name, rx_status); - /* A.C.: The chip hangs here. */ - } - tp->stats.rx_errors++; - if (rx_status & (RxBadSymbol | RxBadAlign)) - tp->stats.rx_frame_errors++; - if (rx_status & (RxRunt | RxTooLong)) - tp->stats.rx_length_errors++; - if (rx_status & RxCRCErr) - tp->stats.rx_crc_errors++; - - /* Reset the receiver, based on RealTek recommendation. (Bug?) */ - - /* disable receive */ - RTL_W8_F (ChipCmd, CmdTxEnb); - tmp_work = 200; - while (--tmp_work > 0) { - udelay(1); - tmp8 = RTL_R8 (ChipCmd); - if (!(tmp8 & CmdRxEnb)) - break; - } - if (tmp_work <= 0) - printk (KERN_WARNING PFX "rx stop wait too long\n"); - /* restart receive */ - tmp_work = 200; - while (--tmp_work > 0) { - RTL_W8_F (ChipCmd, CmdRxEnb | CmdTxEnb); - udelay(1); - tmp8 = RTL_R8 (ChipCmd); - if ((tmp8 & CmdRxEnb) && (tmp8 & CmdTxEnb)) - break; - } - if (tmp_work <= 0) - printk (KERN_WARNING PFX "tx/rx enable wait too long\n"); - - /* and reinitialize all rx related registers */ - RTL_W8_F (Cfg9346, Cfg9346_Unlock); - /* Must enable Tx/Rx before setting transfer thresholds! */ - RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb); - - tp->rx_config = rtl8139_rx_config | AcceptBroadcast | AcceptMyPhys; - RTL_W32 (RxConfig, tp->rx_config); - tp->cur_rx = 0; - - DPRINTK("init buffer addresses\n"); - - /* Lock Config[01234] and BMCR register writes */ - RTL_W8 (Cfg9346, Cfg9346_Lock); - - /* init Rx ring buffer DMA address */ - RTL_W32_F (RxBuf, tp->rx_ring_dma); - - /* A.C.: Reset the multicast list. */ - __set_rx_mode (dev); -} - -static void rtl8139_rx_interrupt (struct net_device *dev, - struct rtl8139_private *tp, void *ioaddr) -{ - unsigned char *rx_ring; - u16 cur_rx; - - assert (dev != NULL); - assert (tp != NULL); - assert (ioaddr != NULL); - - rx_ring = tp->rx_ring; - cur_rx = tp->cur_rx; - - DPRINTK ("%s: In rtl8139_rx(), current %4.4x BufAddr %4.4x," - " free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx, - RTL_R16 (RxBufAddr), - RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd)); - - while ((RTL_R8 (ChipCmd) & RxBufEmpty) == 0) { - int ring_offset = cur_rx % RX_BUF_LEN; - u32 rx_status; - unsigned int rx_size; - unsigned int pkt_size; - struct sk_buff *skb; - - rmb(); - - /* read size+status of next frame from DMA ring buffer */ - rx_status = le32_to_cpu (*(u32 *) (rx_ring + ring_offset)); - rx_size = rx_status >> 16; - pkt_size = rx_size - 4; - - DPRINTK ("%s: rtl8139_rx() status %4.4x, size %4.4x," - " cur %4.4x.\n", dev->name, rx_status, - rx_size, cur_rx); -#if RTL8139_DEBUG > 2 - { - int i; - DPRINTK ("%s: Frame contents ", dev->name); - for (i = 0; i < 70; i++) - printk (" %2.2x", - rx_ring[ring_offset + i]); - printk (".\n"); - } -#endif - - /* Packet copy from FIFO still in progress. - * Theoretically, this should never happen - * since EarlyRx is disabled. - */ - if (rx_size == 0xfff0) { - tp->xstats.early_rx++; - break; - } - - /* If Rx err or invalid rx_size/rx_status received - * (which happens if we get lost in the ring), - * Rx process gets reset, so we abort any further - * Rx processing. - */ - if ((rx_size > (MAX_ETH_FRAME_SIZE+4)) || - (rx_size < 8) || - (!(rx_status & RxStatusOK))) { - rtl8139_rx_err (rx_status, dev, tp, ioaddr); - return; - } - - /* Malloc up new buffer, compatible with net-2e. */ - /* Omit the four octet CRC from the length. */ - - /* TODO: consider allocating skb's outside of - * interrupt context, both to speed interrupt processing, - * and also to reduce the chances of having to - * drop packets here under memory pressure. - */ - - skb = dev_alloc_skb (pkt_size + 2); - if (skb) { - skb->dev = dev; - skb_reserve (skb, 2); /* 16 byte align the IP fields. */ - - eth_copy_and_sum (skb, &rx_ring[ring_offset + 4], pkt_size, 0); - skb_put (skb, pkt_size); - - skb->protocol = eth_type_trans (skb, dev); - netif_rx (skb); - dev->last_rx = jiffies; - tp->stats.rx_bytes += pkt_size; - tp->stats.rx_packets++; - } else { - printk (KERN_WARNING - "%s: Memory squeeze, dropping packet.\n", - dev->name); - tp->stats.rx_dropped++; - } - - cur_rx = (cur_rx + rx_size + 4 + 3) & ~3; - RTL_W16 (RxBufPtr, cur_rx - 16); - - if (RTL_R16 (IntrStatus) & RxAckBits) - RTL_W16_F (IntrStatus, RxAckBits); - } - - DPRINTK ("%s: Done rtl8139_rx(), current %4.4x BufAddr %4.4x," - " free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx, - RTL_R16 (RxBufAddr), - RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd)); - - tp->cur_rx = cur_rx; -} - - -static void rtl8139_weird_interrupt (struct net_device *dev, - struct rtl8139_private *tp, - void *ioaddr, - int status, int link_changed) -{ - DPRINTK ("%s: Abnormal interrupt, status %8.8x.\n", - dev->name, status); - - assert (dev != NULL); - assert (tp != NULL); - assert (ioaddr != NULL); - - /* Update the error count. */ - tp->stats.rx_missed_errors += RTL_R32 (RxMissed); - RTL_W32 (RxMissed, 0); - - if ((status & RxUnderrun) && link_changed && - (tp->drv_flags & HAS_LNK_CHNG)) { - /* Really link-change on new chips. */ - int lpar = RTL_R16 (NWayLPAR); - int duplex = (lpar & 0x0100) || (lpar & 0x01C0) == 0x0040 - || tp->duplex_lock; - if (tp->full_duplex != duplex) { - tp->full_duplex = duplex; -#if 0 - RTL_W8 (Cfg9346, Cfg9346_Unlock); - RTL_W8 (Config1, tp->full_duplex ? 0x60 : 0x20); - RTL_W8 (Cfg9346, Cfg9346_Lock); -#endif - } - status &= ~RxUnderrun; - } - - /* XXX along with rtl8139_rx_err, are we double-counting errors? */ - if (status & - (RxUnderrun | RxOverflow | RxErr | RxFIFOOver)) - tp->stats.rx_errors++; - - if (status & PCSTimeout) - tp->stats.rx_length_errors++; - if (status & (RxUnderrun | RxFIFOOver)) - tp->stats.rx_fifo_errors++; - if (status & PCIErr) { - u16 pci_cmd_status; - pci_read_config_word (tp->pci_dev, PCI_STATUS, &pci_cmd_status); - pci_write_config_word (tp->pci_dev, PCI_STATUS, pci_cmd_status); - - printk (KERN_ERR "%s: PCI Bus error %4.4x.\n", - dev->name, pci_cmd_status); - } -} - - -/* The interrupt handler does all of the Rx thread work and cleans up - after the Tx thread. */ -static void rtl8139_interrupt (int irq, void *dev_instance, - struct pt_regs *regs) -{ - struct net_device *dev = (struct net_device *) dev_instance; - struct rtl8139_private *tp = dev->priv; - int boguscnt = max_interrupt_work; - void *ioaddr = tp->mmio_addr; - int ackstat, status; - int link_changed = 0; /* avoid bogus "uninit" warning */ - - spin_lock (&tp->lock); - - do { - status = RTL_R16 (IntrStatus); - - /* h/w no longer present (hotplug?) or major error, bail */ - if (status == 0xFFFF) - break; - - if ((status & - (PCIErr | PCSTimeout | RxUnderrun | RxOverflow | - RxFIFOOver | TxErr | TxOK | RxErr | RxOK)) == 0) - break; - - /* Acknowledge all of the current interrupt sources ASAP, but - an first get an additional status bit from CSCR. */ - if (status & RxUnderrun) - link_changed = RTL_R16 (CSCR) & CSCR_LinkChangeBit; - - /* The chip takes special action when we clear RxAckBits, - * so we clear them later in rtl8139_rx_interrupt - */ - ackstat = status & ~(RxAckBits | TxErr); - RTL_W16 (IntrStatus, ackstat); - - DPRINTK ("%s: interrupt status=%#4.4x ackstat=%#4.4x new intstat=%#4.4x.\n", - dev->name, ackstat, status, RTL_R16 (IntrStatus)); - - if (netif_running (dev) && (status & RxAckBits)) - rtl8139_rx_interrupt (dev, tp, ioaddr); - - /* Check uncommon events with one test. */ - if (status & (PCIErr | PCSTimeout | RxUnderrun | RxOverflow | - RxFIFOOver | RxErr)) - rtl8139_weird_interrupt (dev, tp, ioaddr, - status, link_changed); - - if (netif_running (dev) && (status & (TxOK | TxErr))) { - rtl8139_tx_interrupt (dev, tp, ioaddr); - if (status & TxErr) - RTL_W16 (IntrStatus, TxErr); - } - - boguscnt--; - } while (boguscnt > 0); - - if (boguscnt <= 0) { - printk (KERN_WARNING "%s: Too much work at interrupt, " - "IntrStatus=0x%4.4x.\n", dev->name, status); - - /* Clear all interrupt sources. */ - RTL_W16 (IntrStatus, 0xffff); - } - - spin_unlock (&tp->lock); - - DPRINTK ("%s: exiting interrupt, intr_status=%#4.4x.\n", - dev->name, RTL_R16 (IntrStatus)); -} - - -static int rtl8139_close (struct net_device *dev) -{ - struct rtl8139_private *tp = dev->priv; - void *ioaddr = tp->mmio_addr; - unsigned long flags; - - netif_stop_queue (dev); - - DPRINTK ("%s: Shutting down ethercard, status was 0x%4.4x.\n", - dev->name, RTL_R16 (IntrStatus)); - - spin_lock_irqsave (&tp->lock, flags); - - /* Stop the chip's Tx and Rx DMA processes. */ - RTL_W8 (ChipCmd, 0); - - /* Disable interrupts by clearing the interrupt mask. */ - RTL_W16 (IntrMask, 0); - - /* Update the error counts. */ - tp->stats.rx_missed_errors += RTL_R32 (RxMissed); - RTL_W32 (RxMissed, 0); - - spin_unlock_irqrestore (&tp->lock, flags); - - synchronize_irq (); - free_irq (dev->irq, dev); - - rtl8139_tx_clear (tp); - - pci_free_consistent(tp->pci_dev, RX_BUF_TOT_LEN, - tp->rx_ring, tp->rx_ring_dma); - pci_free_consistent(tp->pci_dev, TX_BUF_TOT_LEN, - tp->tx_bufs, tp->tx_bufs_dma); - tp->rx_ring = NULL; - tp->tx_bufs = NULL; - - /* Green! Put the chip in low-power mode. */ - RTL_W8 (Cfg9346, Cfg9346_Unlock); - - if (rtl_chip_info[tp->chipset].flags & HasHltClk) - RTL_W8 (HltClk, 'H'); /* 'R' would leave the clock running. */ - - return 0; -} - - -/* Get the ethtool settings. Assumes that eset points to kernel - memory, *eset has been initialized as {ETHTOOL_GSET}, and other - threads or interrupts aren't messing with the 8139. */ -static void netdev_get_eset (struct net_device *dev, struct ethtool_cmd *eset) -{ - struct rtl8139_private *np = dev->priv; - void *ioaddr = np->mmio_addr; - u16 advert; - - eset->supported = SUPPORTED_10baseT_Half - | SUPPORTED_10baseT_Full - | SUPPORTED_100baseT_Half - | SUPPORTED_100baseT_Full - | SUPPORTED_Autoneg - | SUPPORTED_TP; - - eset->advertising = ADVERTISED_TP | ADVERTISED_Autoneg; - advert = mdio_read (dev, np->phys[0], 4); - if (advert & 0x0020) - eset->advertising |= ADVERTISED_10baseT_Half; - if (advert & 0x0040) - eset->advertising |= ADVERTISED_10baseT_Full; - if (advert & 0x0080) - eset->advertising |= ADVERTISED_100baseT_Half; - if (advert & 0x0100) - eset->advertising |= ADVERTISED_100baseT_Full; - - eset->speed = (RTL_R8 (MediaStatus) & 0x08) ? 10 : 100; - /* (KON)FIXME: np->full_duplex is set or reset by the thread, - which means this always shows half duplex if the interface - isn't up yet, even if it has already autonegotiated. */ - eset->duplex = np->full_duplex ? DUPLEX_FULL : DUPLEX_HALF; - eset->port = PORT_TP; - /* (KON)FIXME: Is np->phys[0] correct? starfire.c uses that. */ - eset->phy_address = np->phys[0]; - eset->transceiver = XCVR_INTERNAL; - eset->autoneg = (mdio_read (dev, np->phys[0], 0) & 0x1000) != 0; - eset->maxtxpkt = 1; - eset->maxrxpkt = 1; -} - - -/* Get the ethtool Wake-on-LAN settings. Assumes that wol points to - kernel memory, *wol has been initialized as {ETHTOOL_GWOL}, and - other threads or interrupts aren't messing with the 8139. */ -static void netdev_get_wol (struct net_device *dev, struct ethtool_wolinfo *wol) -{ - struct rtl8139_private *np = dev->priv; - void *ioaddr = np->mmio_addr; - - if (rtl_chip_info[np->chipset].flags & HasLWake) { - u8 cfg3 = RTL_R8 (Config3); - u8 cfg5 = RTL_R8 (Config5); - - wol->supported = WAKE_PHY | WAKE_MAGIC - | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST; - - wol->wolopts = 0; - if (cfg3 & Cfg3_LinkUp) - wol->wolopts |= WAKE_PHY; - if (cfg3 & Cfg3_Magic) - wol->wolopts |= WAKE_MAGIC; - /* (KON)FIXME: See how netdev_set_wol() handles the - following constants. */ - if (cfg5 & Cfg5_UWF) - wol->wolopts |= WAKE_UCAST; - if (cfg5 & Cfg5_MWF) - wol->wolopts |= WAKE_MCAST; - if (cfg5 & Cfg5_BWF) - wol->wolopts |= WAKE_BCAST; - } -} - - -/* Set the ethtool Wake-on-LAN settings. Return 0 or -errno. Assumes - that wol points to kernel memory and other threads or interrupts - aren't messing with the 8139. */ -static int netdev_set_wol (struct net_device *dev, - const struct ethtool_wolinfo *wol) -{ - struct rtl8139_private *np = dev->priv; - void *ioaddr = np->mmio_addr; - u32 support; - u8 cfg3, cfg5; - - support = ((rtl_chip_info[np->chipset].flags & HasLWake) - ? (WAKE_PHY | WAKE_MAGIC - | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST) - : 0); - if (wol->wolopts & ~support) - return -EINVAL; - - cfg3 = RTL_R8 (Config3) & ~(Cfg3_LinkUp | Cfg3_Magic); - if (wol->wolopts & WAKE_PHY) - cfg3 |= Cfg3_LinkUp; - if (wol->wolopts & WAKE_MAGIC) - cfg3 |= Cfg3_Magic; - RTL_W8 (Cfg9346, Cfg9346_Unlock); - RTL_W8 (Config3, cfg3); - RTL_W8 (Cfg9346, Cfg9346_Lock); - - cfg5 = RTL_R8 (Config5) & ~(Cfg5_UWF | Cfg5_MWF | Cfg5_BWF); - /* (KON)FIXME: These are untested. We may have to set the - CRC0, Wakeup0 and LSBCRC0 registers too, but I have no - documentation. */ - if (wol->wolopts & WAKE_UCAST) - cfg5 |= Cfg5_UWF; - if (wol->wolopts & WAKE_MCAST) - cfg5 |= Cfg5_MWF; - if (wol->wolopts & WAKE_BCAST) - cfg5 |= Cfg5_BWF; - RTL_W8 (Config5, cfg5); /* need not unlock via Cfg9346 */ - - return 0; -} - - -static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr) -{ - struct rtl8139_private *np = dev->priv; - u32 ethcmd; - - /* dev_ioctl() in ../../net/core/dev.c has already checked - capable(CAP_NET_ADMIN), so don't bother with that here. */ - - if (copy_from_user (ðcmd, useraddr, sizeof (ethcmd))) - return -EFAULT; - - switch (ethcmd) { - case ETHTOOL_GSET: - { - struct ethtool_cmd eset = { ETHTOOL_GSET }; - spin_lock_irq (&np->lock); - netdev_get_eset (dev, &eset); - spin_unlock_irq (&np->lock); - if (copy_to_user (useraddr, &eset, sizeof (eset))) - return -EFAULT; - return 0; - } - - /* TODO: ETHTOOL_SSET */ - - case ETHTOOL_GDRVINFO: - { - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - strcpy (info.driver, DRV_NAME); - strcpy (info.version, DRV_VERSION); - strcpy (info.bus_info, np->pci_dev->slot_name); - if (copy_to_user (useraddr, &info, sizeof (info))) - return -EFAULT; - return 0; - } - - case ETHTOOL_GWOL: - { - struct ethtool_wolinfo wol = { ETHTOOL_GWOL }; - spin_lock_irq (&np->lock); - netdev_get_wol (dev, &wol); - spin_unlock_irq (&np->lock); - if (copy_to_user (useraddr, &wol, sizeof (wol))) - return -EFAULT; - return 0; - } - - case ETHTOOL_SWOL: - { - struct ethtool_wolinfo wol; - int rc; - if (copy_from_user (&wol, useraddr, sizeof (wol))) - return -EFAULT; - spin_lock_irq (&np->lock); - rc = netdev_set_wol (dev, &wol); - spin_unlock_irq (&np->lock); - return rc; - } - - default: - break; - } - - return -EOPNOTSUPP; -} - - -static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct rtl8139_private *tp = dev->priv; - struct mii_ioctl_data *data = (struct mii_ioctl_data *)&rq->ifr_data; - int rc = 0; - int phy = tp->phys[0] & 0x3f; - - if (cmd != SIOCETHTOOL) { - /* With SIOCETHTOOL, this would corrupt the pointer. */ - data->phy_id &= 0x1f; - data->reg_num &= 0x1f; - } - - switch (cmd) { - case SIOCETHTOOL: - return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); - - case SIOCGMIIPHY: /* Get the address of the PHY in use. */ - case SIOCDEVPRIVATE: /* binary compat, remove in 2.5 */ - data->phy_id = phy; - /* Fall Through */ - - case SIOCGMIIREG: /* Read the specified MII register. */ - case SIOCDEVPRIVATE+1: /* binary compat, remove in 2.5 */ - data->val_out = mdio_read (dev, data->phy_id, data->reg_num); - break; - - case SIOCSMIIREG: /* Write the specified MII register */ - case SIOCDEVPRIVATE+2: /* binary compat, remove in 2.5 */ - if (!capable (CAP_NET_ADMIN)) { - rc = -EPERM; - break; - } - - if (data->phy_id == phy) { - u16 value = data->val_in; - switch (data->reg_num) { - case 0: - /* Check for autonegotiation on or reset. */ - tp->medialock = (value & 0x9000) ? 0 : 1; - if (tp->medialock) - tp->full_duplex = (value & 0x0100) ? 1 : 0; - break; - case 4: /* tp->advertising = value; */ break; - } - } - mdio_write(dev, data->phy_id, data->reg_num, data->val_in); - break; - - default: - rc = -EOPNOTSUPP; - break; - } - - return rc; -} - - -static struct net_device_stats *rtl8139_get_stats (struct net_device *dev) -{ - struct rtl8139_private *tp = dev->priv; - void *ioaddr = tp->mmio_addr; - unsigned long flags; - - if (netif_running(dev)) { - spin_lock_irqsave (&tp->lock, flags); - tp->stats.rx_missed_errors += RTL_R32 (RxMissed); - RTL_W32 (RxMissed, 0); - spin_unlock_irqrestore (&tp->lock, flags); - } - - return &tp->stats; -} - -/* Set or clear the multicast filter for this adaptor. - This routine is not state sensitive and need not be SMP locked. */ - -static unsigned const ethernet_polynomial = 0x04c11db7U; -static inline u32 ether_crc (int length, unsigned char *data) -{ - int crc = -1; - - while (--length >= 0) { - unsigned char current_octet = *data++; - int bit; - for (bit = 0; bit < 8; bit++, current_octet >>= 1) - crc = (crc << 1) ^ ((crc < 0) ^ (current_octet & 1) ? - ethernet_polynomial : 0); - } - - return crc; -} - - -static void __set_rx_mode (struct net_device *dev) -{ - struct rtl8139_private *tp = dev->priv; - void *ioaddr = tp->mmio_addr; - u32 mc_filter[2]; /* Multicast hash filter */ - int i, rx_mode; - u32 tmp; - - DPRINTK ("%s: rtl8139_set_rx_mode(%4.4x) done -- Rx config %8.8lx.\n", - dev->name, dev->flags, RTL_R32 (RxConfig)); - - /* Note: do not reorder, GCC is clever about common statements. */ - if (dev->flags & IFF_PROMISC) { - /* Unconditionally log net taps. */ - printk (KERN_NOTICE "%s: Promiscuous mode enabled.\n", - dev->name); - rx_mode = - AcceptBroadcast | AcceptMulticast | AcceptMyPhys | - AcceptAllPhys; - mc_filter[1] = mc_filter[0] = 0xffffffff; - } else if ((dev->mc_count > multicast_filter_limit) - || (dev->flags & IFF_ALLMULTI)) { - /* Too many to filter perfectly -- accept all multicasts. */ - rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; - mc_filter[1] = mc_filter[0] = 0xffffffff; - } else { - struct dev_mc_list *mclist; - rx_mode = AcceptBroadcast | AcceptMyPhys; - mc_filter[1] = mc_filter[0] = 0; - for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; - i++, mclist = mclist->next) { - int bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26; - - mc_filter[bit_nr >> 5] |= cpu_to_le32(1 << (bit_nr & 31)); - rx_mode |= AcceptMulticast; - } - } - - /* We can safely update without stopping the chip. */ - tmp = rtl8139_rx_config | rx_mode; - if (tp->rx_config != tmp) { - RTL_W32_F (RxConfig, tmp); - tp->rx_config = tmp; - } - RTL_W32_F (MAR0 + 0, mc_filter[0]); - RTL_W32_F (MAR0 + 4, mc_filter[1]); -} - -static void rtl8139_set_rx_mode (struct net_device *dev) -{ - unsigned long flags; - struct rtl8139_private *tp = dev->priv; - - spin_lock_irqsave (&tp->lock, flags); - __set_rx_mode(dev); - spin_unlock_irqrestore (&tp->lock, flags); -} - -#ifdef CONFIG_PM - -static int rtl8139_suspend (struct pci_dev *pdev, u32 state) -{ - struct net_device *dev = pci_get_drvdata (pdev); - struct rtl8139_private *tp = dev->priv; - void *ioaddr = tp->mmio_addr; - unsigned long flags; - - if (!netif_running (dev)) - return 0; - - netif_device_detach (dev); - - spin_lock_irqsave (&tp->lock, flags); - - /* Disable interrupts, stop Tx and Rx. */ - RTL_W16 (IntrMask, 0); - RTL_W8 (ChipCmd, 0); - - /* Update the error counts. */ - tp->stats.rx_missed_errors += RTL_R32 (RxMissed); - RTL_W32 (RxMissed, 0); - - spin_unlock_irqrestore (&tp->lock, flags); - return 0; -} - - -static int rtl8139_resume (struct pci_dev *pdev) -{ - struct net_device *dev = pci_get_drvdata (pdev); - - if (!netif_running (dev)) - return 0; - netif_device_attach (dev); - rtl8139_hw_start (dev); - return 0; -} - -#endif /* CONFIG_PM */ - - -static struct pci_driver rtl8139_pci_driver = { - name: DRV_NAME, - id_table: rtl8139_pci_tbl, - probe: rtl8139_init_one, - remove: __devexit_p(rtl8139_remove_one), -#ifdef CONFIG_PM - suspend: rtl8139_suspend, - resume: rtl8139_resume, -#endif /* CONFIG_PM */ -}; - - -static int __init rtl8139_init_module (void) -{ - /* when we're a module, we always print a version message, - * even if no 8139 board is found. - */ -#ifdef MODULE - printk (KERN_INFO RTL8139_DRIVER_NAME "\n"); -#endif - - return pci_module_init (&rtl8139_pci_driver); -} - - -static void __exit rtl8139_cleanup_module (void) -{ - pci_unregister_driver (&rtl8139_pci_driver); -} - - -module_init(rtl8139_init_module); -module_exit(rtl8139_cleanup_module); diff --git a/xen-2.4.16/drivers/net/eepro100.c b/xen-2.4.16/drivers/net/eepro100.c index bce354fac1..eb47eb65cb 100644 --- a/xen-2.4.16/drivers/net/eepro100.c +++ b/xen-2.4.16/drivers/net/eepro100.c @@ -43,12 +43,15 @@ static int rxdmacount /* = 0 */; /* 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 static int rx_copybreak = 200; -#endif +#endif*/ + +/* Xen doesn't do rx_copybreak in drivers. */ +static int rx_copybreak = 0; /* Maximum events (Rx packets, etc.) to handle at each interrupt. */ static int max_interrupt_work = 20; diff --git a/xen-2.4.16/drivers/net/pcnet32.c b/xen-2.4.16/drivers/net/pcnet32.c index 08ace71a04..ce6c8341a2 100644 --- a/xen-2.4.16/drivers/net/pcnet32.c +++ b/xen-2.4.16/drivers/net/pcnet32.c @@ -66,7 +66,7 @@ static int tx_start = 1; /* Mapping -- 0:20, 1:64, 2:128, 3:~220 (depends on chi static struct net_device *pcnet32_dev; static const int max_interrupt_work = 80; -static const int rx_copybreak = 200; +static const int rx_copybreak = 0; /* 200; Xen doesn't do in-driver copybreak. */ #define PORT_AUI 0x00 #define PORT_10BT 0x01 diff --git a/xen-2.4.16/drivers/net/tg3.c b/xen-2.4.16/drivers/net/tg3.c index 060154fa2b..41f680904b 100644 --- a/xen-2.4.16/drivers/net/tg3.c +++ b/xen-2.4.16/drivers/net/tg3.c @@ -4114,14 +4114,16 @@ static int tg3_reset_hw(struct tg3 *tp) udelay(10); } - tw32(HOSTCC_RXCOL_TICKS, 0); - tw32(HOSTCC_RXMAX_FRAMES, 1); - tw32(HOSTCC_RXCOAL_TICK_INT, 0); - tw32(HOSTCC_RXCOAL_MAXF_INT, 1); - tw32(HOSTCC_TXCOL_TICKS, LOW_TXCOL_TICKS); - tw32(HOSTCC_TXMAX_FRAMES, LOW_RXMAX_FRAMES); - tw32(HOSTCC_TXCOAL_TICK_INT, 0); - tw32(HOSTCC_TXCOAL_MAXF_INT, 0); + // akw: I have set these all back to default coalescing values. + + tw32(HOSTCC_RXCOL_TICKS, DEFAULT_RXCOL_TICKS); //0); + tw32(HOSTCC_RXMAX_FRAMES, DEFAULT_RXMAX_FRAMES); //1); + tw32(HOSTCC_RXCOAL_TICK_INT, DEFAULT_RXCOAL_TICK_INT); //, 0); + tw32(HOSTCC_RXCOAL_MAXF_INT, DEFAULT_RXCOAL_MAXF_INT); //, 1); + tw32(HOSTCC_TXCOL_TICKS, DEFAULT_TXCOL_TICKS); //, LOW_TXCOL_TICKS); + tw32(HOSTCC_TXMAX_FRAMES, DEFAULT_TXMAX_FRAMES); //, LOW_RXMAX_FRAMES); + tw32(HOSTCC_TXCOAL_TICK_INT, DEFAULT_TXCOAL_TICK_INT); //, 0); + tw32(HOSTCC_TXCOAL_MAXF_INT, DEFAULT_TXCOAL_MAXF_INT); //, 0); tw32(HOSTCC_STAT_COAL_TICKS, DEFAULT_STAT_COAL_TICKS); @@ -6185,9 +6187,11 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) } tp->rx_offset = 2; + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701 && (tp->tg3_flags & TG3_FLAG_PCIX_MODE) != 0) - tp->rx_offset = 0; + printk("WARNING: This card may not support unaligned receive pointers.\n"); + //tp->rx_offset = 0; /* By default, disable wake-on-lan. User can change this * using ETHTOOL_SWOL. diff --git a/xen-2.4.16/drivers/net/tg3.h b/xen-2.4.16/drivers/net/tg3.h index 349687c3fa..d816322d98 100644 --- a/xen-2.4.16/drivers/net/tg3.h +++ b/xen-2.4.16/drivers/net/tg3.h @@ -21,7 +21,7 @@ #define TG3_BDINFO_NIC_ADDR 0xcUL /* 32-bit */ #define TG3_BDINFO_SIZE 0x10UL -#define RX_COPY_THRESHOLD 256 +#define RX_COPY_THRESHOLD 0 //256 #define RX_STD_MAX_SIZE 1536 #define RX_JUMBO_MAX_SIZE 0xdeadbeef /* XXX */ diff --git a/xen-2.4.16/drivers/net/tulip/interrupt.c b/xen-2.4.16/drivers/net/tulip/interrupt.c index c92b12ea92..2cf54c6120 100644 --- a/xen-2.4.16/drivers/net/tulip/interrupt.c +++ b/xen-2.4.16/drivers/net/tulip/interrupt.c @@ -172,6 +172,7 @@ static int tulip_rx(struct net_device *dev) to a minimally-sized skbuff. */ if (pkt_len < tulip_rx_copybreak && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { + //if (0) { skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ pci_dma_sync_single(tp->pdev, diff --git a/xen-2.4.16/drivers/net/tulip/tulip_core.c b/xen-2.4.16/drivers/net/tulip/tulip_core.c index 601046c8ea..f2a8c5f331 100644 --- a/xen-2.4.16/drivers/net/tulip/tulip_core.c +++ b/xen-2.4.16/drivers/net/tulip/tulip_core.c @@ -60,13 +60,16 @@ const char * const medianame[32] = { }; /* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */ -#if defined(__alpha__) || defined(__arm__) || defined(__hppa__) \ +/*#if defined(__alpha__) || defined(__arm__) || defined(__hppa__) \ || defined(__sparc_) || defined(__ia64__) \ || defined(__sh__) || defined(__mips__) static int rx_copybreak = 1518; #else static int rx_copybreak = 100; #endif +*/ +/* Xen doesn't do rx_copybreak in drivers. */ +static int rx_copybreak = 0; /* Set the bus performance register. diff --git a/xen-2.4.16/include/asm-i386/flushtlb.h b/xen-2.4.16/include/asm-i386/flushtlb.h new file mode 100644 index 0000000000..306839c6a4 --- /dev/null +++ b/xen-2.4.16/include/asm-i386/flushtlb.h @@ -0,0 +1,48 @@ +/****************************************************************************** + * flushtlb.h + * + * TLB flush macros that count flushes. Counting is used to enforce + * zero-copy safety, particularily for the network code. + * + * akw - Jan 21, 2003 + */ + +#ifndef __FLUSHTLB_H +#define __FLUSHTLB_H + +#include + +unsigned long tlb_flush_count[NR_CPUS]; +//#if 0 +#define __read_cr3(__var) \ + do { \ + __asm__ __volatile ( \ + "movl %%cr3, %0;" \ + : "=r" (__var)); \ + } while (0) +//#endif + +#define __write_cr3_counted(__pa) \ + do { \ + __asm__ __volatile__ ( \ + "movl %0, %%cr3;" \ + :: "r" (__pa) \ + : "memory"); \ + tlb_flush_count[smp_processor_id()]++; \ + } while (0) + +//#endif +#define __flush_tlb_counted() \ + do { \ + unsigned int tmpreg; \ + \ + __asm__ __volatile__( \ + "movl %%cr3, %0; # flush TLB \n" \ + "movl %0, %%cr3; " \ + : "=r" (tmpreg) \ + :: "memory"); \ + tlb_flush_count[smp_processor_id()]++; \ + } while (0) + +#endif + diff --git a/xen-2.4.16/include/asm-i386/page.h b/xen-2.4.16/include/asm-i386/page.h index 5b2d1fabc5..2929d7e6b7 100644 --- a/xen-2.4.16/include/asm-i386/page.h +++ b/xen-2.4.16/include/asm-i386/page.h @@ -91,36 +91,36 @@ typedef struct { unsigned long pt_lo; } pagetable_t; #include #include #include +#include extern l2_pgentry_t idle0_pg_table[ENTRIES_PER_L2_PAGETABLE]; extern l2_pgentry_t *idle_pg_table[NR_CPUS]; extern void paging_init(void); -#define __flush_tlb() \ - do { \ - unsigned int tmpreg; \ - \ - __asm__ __volatile__( \ - "movl %%cr3, %0; # flush TLB \n" \ - "movl %0, %%cr3; \n" \ - : "=r" (tmpreg) \ - :: "memory"); \ - } while (0) +#define __flush_tlb() __flush_tlb_counted() /* Flush global pages as well. */ + +#define __pge_off() \ + do { \ + __asm__ __volatile__( \ + "movl %0, %%cr4; # turn off PGE " \ + :: "r" (mmu_cr4_features & ~X86_CR4_PGE)); \ + } while (0) + +#define __pge_on() \ + do { \ + __asm__ __volatile__( \ + "movl %0, %%cr4; # turn off PGE " \ + :: "r" (mmu_cr4_features)); \ + } while (0) + + #define __flush_tlb_all() \ do { \ - unsigned int tmpreg; \ - \ - __asm__ __volatile__( \ - "movl %1, %%cr4; # turn off PGE \n" \ - "movl %%cr3, %0; # flush TLB \n" \ - "movl %0, %%cr3; \n" \ - "movl %2, %%cr4; # turn PGE back on \n" \ - : "=&r" (tmpreg) \ - : "r" (mmu_cr4_features & ~X86_CR4_PGE), \ - "r" (mmu_cr4_features) \ - : "memory"); \ + __pge_off(); \ + __flush_tlb_counted(); \ + __pge_on(); \ } while (0) #define __flush_tlb_one(__addr) \ diff --git a/xen-2.4.16/include/asm-i386/pci.h b/xen-2.4.16/include/asm-i386/pci.h index 33cc37b2cd..b5007e38a1 100644 --- a/xen-2.4.16/include/asm-i386/pci.h +++ b/xen-2.4.16/include/asm-i386/pci.h @@ -75,6 +75,7 @@ static inline dma_addr_t pci_map_single(struct pci_dev *hwdev, void *ptr, if (direction == PCI_DMA_NONE) BUG(); flush_write_buffers(); + return virt_to_bus(ptr); } diff --git a/xen-2.4.16/include/hypervisor-ifs/hypervisor-if.h b/xen-2.4.16/include/hypervisor-ifs/hypervisor-if.h index 9a1be50b15..cf3edb8cfb 100644 --- a/xen-2.4.16/include/hypervisor-ifs/hypervisor-if.h +++ b/xen-2.4.16/include/hypervisor-ifs/hypervisor-if.h @@ -111,11 +111,6 @@ typedef struct #define EVENT_NET_TX 0x20 /* packets for transmission. */ #define EVENT_NET_RX 0x40 /* empty buffers for receive. */ -/* should these macros and the ones below test for range violation? */ -#define EVENT_NET_TX_FOR_VIF(x) (EVENT_NET_TX << (2 * x)) -#define EVENT_NET_RX_FOR_VIF(x) (EVENT_NET_RX << (2 * x)) - - /* Bit offsets, as opposed to the above masks. */ #define _EVENT_BLK_TX 0 #define _EVENT_BLK_RX 1 @@ -125,9 +120,6 @@ typedef struct #define _EVENT_NET_RX 5 #define _EVENT_DEBUG 6 -#define _EVENT_NET_TX_FOR_VIF(x) (_EVENT_NET_TX + (2 * x)) -#define _EVENT_NET_RX_FOR_VIF(x) (_EVENT_NET_RX + (2 * x)) - /* * NB. We expect that this struct is smaller than a page. diff --git a/xen-2.4.16/include/hypervisor-ifs/network.h b/xen-2.4.16/include/hypervisor-ifs/network.h index a0cfc9fa2c..f3f13dc77b 100644 --- a/xen-2.4.16/include/hypervisor-ifs/network.h +++ b/xen-2.4.16/include/hypervisor-ifs/network.h @@ -15,17 +15,19 @@ #include typedef struct tx_entry_st { - unsigned long addr; /* virtual address */ - unsigned long size; /* in bytes */ + unsigned long addr; /* virtual address */ + unsigned long size; /* in bytes */ + int status; /* per descriptor status. */ } tx_entry_t; typedef struct rx_entry_st { - unsigned long addr; /* virtual address */ - unsigned long size; /* in bytes */ + unsigned long addr; /* virtual address */ + unsigned long size; /* in bytes */ + int status; /* per descriptor status. */ } rx_entry_t; -#define TX_RING_SIZE 1024 -#define RX_RING_SIZE 1024 +#define TX_RING_SIZE 256 +#define RX_RING_SIZE 256 typedef struct net_ring_st { /* * Guest OS places packets into ring at tx_prod. @@ -111,4 +113,12 @@ typedef struct net_rule_ent_st /* Drop a new rule down to the network tables. */ int add_net_rule(net_rule_t *rule); + +/* Descriptor status values: + */ + +#define RING_STATUS_OK 0 // Everything is gravy. +#define RING_STATUS_ERR_CFU -1 // Copy from user problems. +#define RING_STATUS_BAD_PAGE -2 // What they gave us was pure evil. + #endif diff --git a/xen-2.4.16/include/xeno/mm.h b/xen-2.4.16/include/xeno/mm.h index ec85a0a7f9..0187e21bb4 100644 --- a/xen-2.4.16/include/xeno/mm.h +++ b/xen-2.4.16/include/xeno/mm.h @@ -7,6 +7,7 @@ #include #include #include +#include /* XXX KAF: These may die eventually, but so many refs in slab.c :((( */ @@ -88,7 +89,7 @@ typedef struct pfn_info { * references exist of teh current type. A change in type can only occur * when type_count == 0. */ -#define PG_type_mask (7<<24) /* bits 24-26 */ +#define PG_type_mask (15<<24) /* bits 24-27 */ #define PGT_none (0<<24) /* no special uses of this page */ #define PGT_l1_page_table (1<<24) /* using this page as an L1 page table? */ #define PGT_l2_page_table (2<<24) /* using this page as an L2 page table? */ @@ -97,6 +98,7 @@ typedef struct pfn_info { #define PGT_gdt_page (5<<24) /* using this page in a GDT? */ #define PGT_ldt_page (6<<24) /* using this page in an LDT? */ #define PGT_writeable_page (7<<24) /* has writable mappings of this page? */ +#define PGT_net_rx_buf (8<<24) /* this page has been pirated by the net code. */ #define PageSlab(page) test_bit(PG_slab, &(page)->flags) #define PageSetSlab(page) set_bit(PG_slab, &(page)->flags) @@ -108,6 +110,7 @@ typedef struct pfn_info { extern frame_table_t * frame_table; extern unsigned long frame_table_size; extern struct list_head free_list; +extern spinlock_t free_list_lock; extern unsigned int free_pfns; extern unsigned long max_page; void init_frametable(unsigned long nr_pages); diff --git a/xen-2.4.16/include/xeno/skbuff.h b/xen-2.4.16/include/xeno/skbuff.h index f9c38c12bf..dd6257b370 100644 --- a/xen-2.4.16/include/xeno/skbuff.h +++ b/xen-2.4.16/include/xeno/skbuff.h @@ -34,6 +34,10 @@ #define VIF_DROP -3 #define VIF_ANY_INTERFACE -4 +//skb_type values: +#define SKB_NORMAL 0 +#define SKB_ZERO_COPY 1 + #define HAVE_ALLOC_SKB /* For the drivers to know */ #define HAVE_ALIGNABLE_SKB /* Ditto 8) */ #define SLAB_SKB /* Slabified skbuffs */ @@ -187,7 +191,7 @@ struct sk_buff { unsigned int data_len; unsigned int csum; /* Checksum */ unsigned char __unused, /* Dead field, may be reused */ - cloned, /* head may be cloned (check refcnt to be sure). */ + cloned, /* head may be cloned (check refcnt to be sure) */ pkt_type, /* Packet class */ ip_summed; /* Driver fed us an IP checksum */ __u32 priority; /* Packet queueing priority */ @@ -203,8 +207,12 @@ struct sk_buff { void (*destructor)(struct sk_buff *); /* Destruct function */ - int src_vif; /* vif we came from */ - int dst_vif; /* vif we are bound for */ + unsigned int skb_type; /* SKB_NORMAL or SKB_ZERO_COPY */ + struct pfn_info *pf; /* record of physical pf address for freeing */ + int src_vif; /* vif we came from */ + int dst_vif; /* vif we are bound for */ + struct skb_shared_info shinfo; /* shared info is no longer shared in Xen. */ + @@ -244,6 +252,7 @@ struct sk_buff { extern void __kfree_skb(struct sk_buff *skb); extern struct sk_buff * alloc_skb(unsigned int size, int priority); +extern struct sk_buff * alloc_zc_skb(unsigned int size, int priority); 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(const struct sk_buff *skb, int priority); @@ -259,7 +268,8 @@ extern void skb_over_panic(struct sk_buff *skb, int len, void *here); extern void skb_under_panic(struct sk_buff *skb, int len, void *here); /* Internal */ -#define skb_shinfo(SKB) ((struct skb_shared_info *)((SKB)->end)) +//#define skb_shinfo(SKB) ((struct skb_shared_info *)((SKB)->end)) +#define skb_shinfo(SKB) ((struct skb_shared_info *)(&(SKB)->shinfo)) /** * skb_queue_empty - check if a queue is empty @@ -1045,7 +1055,8 @@ static inline struct sk_buff *__dev_alloc_skb(unsigned int length, { struct sk_buff *skb; - skb = alloc_skb(length+16, gfp_mask); + //skb = alloc_skb(length+16, gfp_mask); + skb = alloc_zc_skb(length+16, gfp_mask); if (skb) skb_reserve(skb,16); return skb; diff --git a/xen-2.4.16/include/xeno/vif.h b/xen-2.4.16/include/xeno/vif.h index 1bef565c32..7d2878dabf 100644 --- a/xen-2.4.16/include/xeno/vif.h +++ b/xen-2.4.16/include/xeno/vif.h @@ -18,9 +18,37 @@ #include #include +/* + * shadow ring structures are used to protect the descriptors from + * tampering after they have been passed to the hypervisor. + * + * TX_RING_SIZE and RX_RING_SIZE are defined in the shared network.h. + */ + +typedef struct tx_shadow_entry_st { + unsigned long addr; + unsigned long size; + int status; + unsigned long flush_count; +} tx_shadow_entry_t; + +typedef struct rx_shadow_entry_st { + unsigned long addr; + unsigned long size; + int status; + unsigned long flush_count; +} rx_shadow_entry_t; + +typedef struct net_shadow_ring_st { + tx_shadow_entry_t *tx_ring; + rx_shadow_entry_t *rx_ring; + unsigned int rx_prod, rx_cons, rx_idx; +} net_shadow_ring_t; + typedef struct net_vif_st { - net_ring_t *net_ring; - int id; + net_ring_t *net_ring; + net_shadow_ring_t *shadow_ring; + int id; struct sk_buff_head skb_list; unsigned int domain; // rules table goes here in next revision. @@ -38,5 +66,11 @@ extern net_vif_t *sys_vif_list[]; net_vif_t *create_net_vif(int domain); void destroy_net_vif(struct task_struct *p); void add_default_net_rule(int vif_id, u32 ipaddr); -int net_get_target_vif(struct sk_buff *skb); +int __net_get_target_vif(u8 *data, unsigned int len, int src_vif); void add_default_net_rule(int vif_id, u32 ipaddr); + +#define net_get_target_vif(skb) __net_get_target_vif(skb->data, skb->len, skb->src_vif) +/* status fields per-descriptor: + */ + + diff --git a/xen-2.4.16/net/dev.c b/xen-2.4.16/net/dev.c index b99a2e91b4..0e19d7a0a6 100644 --- a/xen-2.4.16/net/dev.c +++ b/xen-2.4.16/net/dev.c @@ -30,6 +30,8 @@ #include #include +#include +#include #define BUG_TRAP ASSERT #define notifier_call_chain(_a,_b,_c) ((void)0) @@ -38,6 +40,18 @@ #define rtnl_unlock() ((void)0) #define dst_init() ((void)0) +#if 1 +#define DPRINTK(_f, _a...) printk(_f , ## _a) +#else +#define DPRINTK(_f, _a...) ((void)0) +#endif + +// Ring defines: +#define TX_RING_INC(_i) (((_i)+1) & (TX_RING_SIZE-1)) +#define RX_RING_INC(_i) (((_i)+1) & (RX_RING_SIZE-1)) +#define TX_RING_ADD(_i,_j) (((_i)+(_j)) & (TX_RING_SIZE-1)) +#define RX_RING_ADD(_i,_j) (((_i)+(_j)) & (RX_RING_SIZE-1)) + struct net_device *the_dev = NULL; /* @@ -47,11 +61,11 @@ struct net_device *the_dev = NULL; struct softnet_data softnet_data[NR_CPUS] __cacheline_aligned; -/***************************************************************************************** +/********************************************************************************* Device Interface Subroutines -******************************************************************************************/ +**********************************************************************************/ /** * __dev_get_by_name - find a device by its name @@ -493,7 +507,7 @@ int dev_queue_xmit(struct sk_buff *skb) { struct net_device *dev = skb->dev; struct Qdisc *q; - +if (!(dev->features&NETIF_F_SG)) printk("NIC doesn't do SG!!!\n"); if (skb_shinfo(skb)->frag_list && !(dev->features&NETIF_F_FRAGLIST) && skb_linearize(skb, GFP_ATOMIC) != 0) { @@ -661,7 +675,80 @@ static void get_sample_stats(int cpu) softnet_data[cpu].avg_blog = avg_blog; } +void deliver_packet(struct sk_buff *skb, net_vif_t *vif) +{ + net_shadow_ring_t *shadow_ring; + rx_shadow_entry_t *rx; + unsigned long *g_pte; //tmp + struct pfn_info *g_pfn, *h_pfn; + unsigned int i; //, nvif; + + + /* + * Write the virtual MAC address into the destination field + * of the ethernet packet. Furthermore, do the same for ARP + * reply packets. This is easy because the virtual MAC address + * is always 00-[nn]-00-00-00-00, where the second sixteen bits + * of the MAC are the vif's id. This is to differentiate between + * vifs on guests that have more than one. + * + * In zero copy, the data pointers for the packet have to have been + * mapped in by the caller. + */ + + memset(skb->mac.ethernet->h_dest, 0, ETH_ALEN); +// *(unsigned int *)(skb->mac.ethernet->h_dest + 1) = nvif; + if ( ntohs(skb->mac.ethernet->h_proto) == ETH_P_ARP ) + { + memset(skb->nh.raw + 18, 0, ETH_ALEN); +// *(unsigned int *)(skb->nh.raw + 18 + 1) = nvif; + } + shadow_ring = vif->shadow_ring; + + //Advance to next good buffer. + for (i = shadow_ring->rx_cons; + (i != shadow_ring->rx_prod) + && ( shadow_ring->rx_ring[i].status != RING_STATUS_OK ); + i = RX_RING_INC(i)); + + if (( i != shadow_ring->rx_prod ) && + ( shadow_ring->rx_ring[i].status == RING_STATUS_OK )) + { + rx = shadow_ring->rx_ring+i; + if ( (skb->len + ETH_HLEN) < rx->size ) + rx->size = skb->len + ETH_HLEN; + + g_pte = map_domain_mem(rx->addr); + + g_pfn = frame_table + (*g_pte >> PAGE_SHIFT); + h_pfn = skb->pf; + + h_pfn->tot_count = h_pfn->type_count = 1; + g_pfn->tot_count = g_pfn->type_count = 0; + h_pfn->flags = g_pfn->flags & (~PG_type_mask); + + if (*g_pte & _PAGE_RW) h_pfn->flags |= PGT_writeable_page; + g_pfn->flags = 0; + + //point guest pte at the new page: + machine_to_phys_mapping[h_pfn - frame_table] + = machine_to_phys_mapping[g_pfn - frame_table]; + + *g_pte = (*g_pte & ~PAGE_MASK) + | (((h_pfn - frame_table) << PAGE_SHIFT) & PAGE_MASK); + *g_pte |= _PAGE_PRESENT; + + unmap_domain_mem(g_pte); + skb->pf = g_pfn; // return the guest pfn to be put on the free list + + shadow_ring->rx_cons = RX_RING_INC(i); + } +} + +/* Deliver skb to an old protocol, which is not threaded well + or which do not understand shared skbs. + */ /** * netif_rx - post buffer to the network code * @skb: buffer to post @@ -686,32 +773,56 @@ int netif_rx(struct sk_buff *skb) #ifdef CONFIG_SMP unsigned long cpu_mask; #endif + struct task_struct *p; int this_cpu = smp_processor_id(); struct softnet_data *queue; unsigned long flags; net_vif_t *vif; - if (skb->stamp.tv_sec == 0) - get_fast_time(&skb->stamp); + local_irq_save(flags); + if (skb->skb_type != SKB_ZERO_COPY) + BUG(); + + if (skb->stamp.tv_sec == 0) + get_fast_time(&skb->stamp); + + if ( (skb->data - skb->head) != (18 + ETH_HLEN) ) + printk("headroom was %lu!\n", (unsigned long)skb->data - (unsigned long)skb->head); + // BUG(); + + skb->head = (u8 *)map_domain_mem(((skb->pf - frame_table) << PAGE_SHIFT)); + + /* remapping this address really screws up all the skb pointers. We need + * to map them all here sufficiently to get the packet demultiplexed. + */ + + skb->data = skb->head; + skb_reserve(skb,18); // 18 is the 16 from dev_alloc_skb plus 2 for # + // IP header alignment. + skb->mac.raw = skb->data; + skb->data += ETH_HLEN; + skb->nh.raw = skb->data; + /* The code is rearranged so that the path is the most short when CPU is congested, but is still operating. */ queue = &softnet_data[this_cpu]; - - local_irq_save(flags); netdev_rx_stat[this_cpu].total++; if ( skb->src_vif == VIF_UNKNOWN_INTERFACE ) skb->src_vif = VIF_PHYSICAL_INTERFACE; - + if ( skb->dst_vif == VIF_UNKNOWN_INTERFACE ) - net_get_target_vif(skb); + skb->dst_vif = __net_get_target_vif(skb->mac.raw, skb->len, skb->src_vif); +//if (skb->dst_vif == VIF_DROP) +//printk("netif_rx target: %d (sec: %u)\n", skb->dst_vif, skb->security); if ( (vif = sys_vif_list[skb->dst_vif]) == NULL ) { +//printk("No such vif! (%d).\n", skb->dst_vif); // the target vif does not exist. goto drop; } @@ -733,7 +844,7 @@ int netif_rx(struct sk_buff *skb) do { if ( p->domain != vif->domain ) continue; if ( vif->skb_list.qlen > 100 ) break; - skb_queue_tail(&vif->skb_list, skb); + deliver_packet(skb, vif); cpu_mask = mark_hyp_event(p, _HYP_EVENT_NET_RX); read_unlock(&tasklist_lock); goto found; @@ -745,20 +856,21 @@ int netif_rx(struct sk_buff *skb) drop: netdev_rx_stat[this_cpu].dropped++; - local_irq_restore(flags); - + unmap_domain_mem(skb->head); kfree_skb(skb); + local_irq_restore(flags); return NET_RX_DROP; found: + unmap_domain_mem(skb->head); + skb->head = skb->data = skb->tail = (void *)0xdeadbeef; + kfree_skb(skb); hyp_event_notify(cpu_mask); local_irq_restore(flags); return 0; } -/* Deliver skb to an old protocol, which is not threaded well - or which do not understand shared skbs. - */ + static int deliver_to_old_ones(struct packet_type *pt, struct sk_buff *skb, int last) { static spinlock_t net_bh_lock = SPIN_LOCK_UNLOCKED; @@ -873,15 +985,49 @@ static inline void handle_diverter(struct sk_buff *skb) } #endif /* CONFIG_NET_DIVERT */ +void update_shared_ring(void) +{ + rx_shadow_entry_t *rx; + shared_info_t *s = current->shared_info; + net_ring_t *net_ring; + net_shadow_ring_t *shadow_ring; + unsigned int nvif; + + clear_bit(_HYP_EVENT_NET_RX, ¤t->hyp_events); + for (nvif = 0; nvif < current->num_net_vifs; nvif++) + { + net_ring = current->net_vif_list[nvif]->net_ring; + shadow_ring = current->net_vif_list[nvif]->shadow_ring; + while ((shadow_ring->rx_idx != shadow_ring->rx_cons) + && (net_ring->rx_cons != net_ring->rx_prod)) + { + rx = shadow_ring->rx_ring+shadow_ring->rx_idx; + copy_to_user(net_ring->rx_ring + net_ring->rx_cons, rx, sizeof(rx_entry_t)); + shadow_ring->rx_idx = RX_RING_INC(shadow_ring->rx_idx); + net_ring->rx_cons = RX_RING_INC(net_ring->rx_cons); + + if (rx->flush_count == tlb_flush_count[smp_processor_id()]) + __flush_tlb(); + + if ( net_ring->rx_cons == net_ring->rx_event ) + set_bit(_EVENT_NET_RX, &s->events); + + } + } +} + void flush_rx_queue(void) { struct sk_buff *skb; shared_info_t *s = current->shared_info; net_ring_t *net_ring; + net_shadow_ring_t *shadow_ring; unsigned int i, nvif; - rx_entry_t rx; - + rx_shadow_entry_t *rx; + unsigned long *g_pte, tmp; + struct pfn_info *g_pfn, *h_pfn; + /* I have changed this to batch flush all vifs for a guest * at once, whenever this is called. Since the guest is about to be * scheduled and issued an RX interrupt for one nic, it might as well @@ -893,15 +1039,17 @@ void flush_rx_queue(void) * loop can be replaced with a translation to the specific NET * interrupt to serve. --akw */ - clear_bit(_HYP_EVENT_NET_RX, ¤t->hyp_events); for (nvif = 0; nvif < current->num_net_vifs; nvif++) { net_ring = current->net_vif_list[nvif]->net_ring; + shadow_ring = current->net_vif_list[nvif]->shadow_ring; while ( (skb = skb_dequeue(¤t->net_vif_list[nvif]->skb_list)) != NULL ) { + //temporary hack to stop processing non-zc skbs. + if (skb->skb_type == SKB_NORMAL) continue; /* * Write the virtual MAC address into the destination field * of the ethernet packet. Furthermore, do the same for ARP @@ -912,6 +1060,16 @@ void flush_rx_queue(void) * second sixteen bits, which are the per-host vif id. * (so eth0 should be 00-00-..., eth1 is 00-01-...) */ + + if (skb->skb_type == SKB_ZERO_COPY) + { + skb->head = (u8 *)map_domain_mem(((skb->pf - frame_table) << PAGE_SHIFT)); + skb->data = skb->head; + skb_reserve(skb,16); + skb->mac.raw = skb->data; + skb->data += ETH_HLEN; + } + memset(skb->mac.ethernet->h_dest, 0, ETH_ALEN); *(unsigned int *)(skb->mac.ethernet->h_dest + 1) = nvif; if ( ntohs(skb->mac.ethernet->h_proto) == ETH_P_ARP ) @@ -920,19 +1078,88 @@ void flush_rx_queue(void) *(unsigned int *)(skb->nh.raw + 18 + 1) = nvif; } + if (skb->skb_type == SKB_ZERO_COPY) + { + unmap_domain_mem(skb->head); + } + i = net_ring->rx_cons; if ( i != net_ring->rx_prod ) { - if ( !copy_from_user(&rx, net_ring->rx_ring+i, sizeof(rx)) ) + net_ring->rx_ring[i].status = shadow_ring->rx_ring[i].status; + if ( shadow_ring->rx_ring[i].status == RING_STATUS_OK) { - if ( (skb->len + ETH_HLEN) < rx.size ) - rx.size = skb->len + ETH_HLEN; - copy_to_user((void *)rx.addr, skb->mac.raw, rx.size); - copy_to_user(net_ring->rx_ring+i, &rx, sizeof(rx)); + rx = shadow_ring->rx_ring+i; + if ( (skb->len + ETH_HLEN) < rx->size ) + rx->size = skb->len + ETH_HLEN; + + /* remap the packet again. This is very temporary and will shortly be + * replaced with a page swizzle. + */ + + /*if (skb->skb_type == SKB_ZERO_COPY) + { + skb->head = (u8 *)map_domain_mem(((skb->pf - frame_table) << PAGE_SHIFT)); + skb->data = skb->head; + skb_reserve(skb,16); + skb->mac.raw = skb->data; + skb->data += ETH_HLEN; + } + + copy_to_user((void *)rx->addr, skb->mac.raw, rx->size); + copy_to_user(net_ring->rx_ring+i, rx, sizeof(rx)); + + if (skb->skb_type == SKB_ZERO_COPY) + { + unmap_domain_mem(skb->head); + skb->head = skb->data = skb->tail = (void *)0xdeadbeef; + }*/ + + //presumably I don't need to rewalk the guest page table + //here. + if (skb->skb_type == SKB_ZERO_COPY) + { + // g_pfn is the frame FROM the guest being given up + // h_pfn is the frame FROM the hypervisor, passing up. + + if (rx->flush_count == tlb_flush_count[smp_processor_id()]) + { + flush_tlb_all(); + } + + g_pte = map_domain_mem(rx->addr); + + //g_pfn = frame_table + (rx->addr >> PAGE_SHIFT); + g_pfn = frame_table + (*g_pte >> PAGE_SHIFT); + h_pfn = skb->pf; + + + //tmp = g_pfn->next; g_pfn->next = h_pfn->next; h_pfn->next = tmp; + //tmp = g_pfn->prev; g_pfn->prev = h_pfn->prev; h_pfn->prev = tmp; + tmp = g_pfn->flags; g_pfn->flags = h_pfn->flags; h_pfn->flags = tmp; + + h_pfn->tot_count = 1; + h_pfn->type_count = g_pfn->type_count; + g_pfn->tot_count = g_pfn->type_count = 0; + + h_pfn->flags = current->domain | PGT_l1_page_table; + g_pfn->flags = PGT_l1_page_table; + + + *g_pte = (*g_pte & ~PAGE_MASK) | (((h_pfn - frame_table) << PAGE_SHIFT) & PAGE_MASK); + + *g_pte |= _PAGE_PRESENT; + unmap_domain_mem(g_pte); + + skb->pf = g_pfn; // return the guest pfn to be put on the free list + } else { + BUG(); //got a non-zero copy skb. which is not good. + } + } net_ring->rx_cons = (i+1) & (RX_RING_SIZE-1); if ( net_ring->rx_cons == net_ring->rx_event ) - set_bit(_EVENT_NET_RX_FOR_VIF(nvif), &s->events); + set_bit(_EVENT_NET_RX, &s->events); } kfree_skb(skb); } @@ -1909,96 +2136,274 @@ int __init net_dev_init(void) return 0; } +inline int init_tx_header(u8 *data, unsigned int len, struct net_device *dev) +{ + memcpy(data + ETH_ALEN, dev->dev_addr, ETH_ALEN); + + switch ( ntohs(*(unsigned short *)(data + 12)) ) + { + case ETH_P_ARP: + if ( len < 42 ) break; + memcpy(data + 22, dev->dev_addr, 6); + return ETH_P_ARP; + case ETH_P_IP: + return ETH_P_IP; + } + return 0; +} +/* + * tx_skb_release + * + * skb destructor function that is attached to zero-copy tx skbs before + * they are passed to the device driver for transmission. The destructor + * is responsible for unlinking the fragment pointer to the skb data that + * is in guest memory, and decrementing the tot_count on the packet pages + * pfn_info. + */ + +void tx_skb_release(struct sk_buff *skb) +{ + int i; + + for (i= 0; i < skb_shinfo(skb)->nr_frags; i++) + skb_shinfo(skb)->frags[i].page->tot_count--; + + skb_shinfo(skb)->nr_frags = 0; +} + /* * do_net_update: * * Called from guest OS to notify updates to its transmit and/or receive * descriptor rings. */ -#define TX_RING_INC(_i) (((_i)+1) & (TX_RING_SIZE-1)) -#define RX_RING_INC(_i) (((_i)+1) & (RX_RING_SIZE-1)) -#define TX_RING_ADD(_i,_j) (((_i)+(_j)) & (TX_RING_SIZE-1)) -#define RX_RING_ADD(_i,_j) (((_i)+(_j)) & (RX_RING_SIZE-1)) +#define PKT_PROT_LEN (ETH_HLEN + 8) + +void print_range2(u8 *start, unsigned int len) +{ + int i=0; + while (i++ < len) + { + printk("%x:",start[i]); + } + printk("\n"); +} + long do_net_update(void) { shared_info_t *shared = current->shared_info; - net_ring_t *net_ring = current->net_ring_base; + net_ring_t *net_ring; + net_shadow_ring_t *shadow_ring; net_vif_t *current_vif; unsigned int i, j; struct sk_buff *skb; tx_entry_t tx; - + rx_shadow_entry_t *rx; + unsigned long pfn; + struct pfn_info *page; + unsigned long *g_pte; + + for ( j = 0; j < current->num_net_vifs; j++) { current_vif = current->net_vif_list[j]; net_ring = current_vif->net_ring; + int target; + u8 *g_data; + unsigned short protocol; + + /* First, we send out pending TX descriptors if they exist on this ring. + */ + for ( i = net_ring->tx_cons; i != net_ring->tx_prod; i = TX_RING_INC(i) ) { if ( copy_from_user(&tx, net_ring->tx_ring+i, sizeof(tx)) ) continue; + if ( tx.size < PKT_PROT_LEN ) continue; // This should be reasonable. + + // Packets must not cross page boundaries. For now, this is a + // kernel panic, later it may become a continue -- silent fail. + + if ( ((tx.addr & ~PAGE_MASK) + tx.size) >= PAGE_SIZE ) + { + DPRINTK("tx.addr: %lx, size: %lu, end: %lu\n", tx.addr, tx.size, + (tx.addr &~PAGE_MASK) + tx.size); + continue; + //BUG(); + } + if ( TX_RING_INC(i) == net_ring->tx_event ) - set_bit(_EVENT_NET_TX_FOR_VIF(j), &shared->events); + set_bit(_EVENT_NET_TX, &shared->events); - skb = alloc_skb(tx.size, GFP_KERNEL); - if ( skb == NULL ) continue; - skb_put(skb, tx.size); - if ( copy_from_user(skb->data, (void *)tx.addr, tx.size) ) - { - kfree_skb(skb); - continue; - } - skb->dev = the_dev; + /* Map the skb in from the guest, and get it's delivery target. + * We need this to know whether the packet is to be sent locally + * or remotely. + */ + + g_data = map_domain_mem(tx.addr); - if ( skb->len < 16 ) +//print_range2(g_data, PKT_PROT_LEN); + protocol = __constant_htons(init_tx_header(g_data, tx.size, the_dev)); + if ( protocol == 0 ) { - kfree_skb(skb); + unmap_domain_mem(g_data); continue; } - memcpy(skb->data + ETH_ALEN, skb->dev->dev_addr, ETH_ALEN); - - switch ( ntohs(*(unsigned short *)(skb->data + 12)) ) + target = __net_get_target_vif(g_data, tx.size, current_vif->id); +//printk("Send to target: %d\n", target); + if (target > VIF_PHYSICAL_INTERFACE ) { - case ETH_P_ARP: - skb->protocol = __constant_htons(ETH_P_ARP); - if ( skb->len < 42 ) break; - memcpy(skb->data + 22, skb->dev->dev_addr, 6); - break; - case ETH_P_IP: - skb->protocol = __constant_htons(ETH_P_IP); - break; - default: - kfree_skb(skb); - skb = NULL; - break; - } - - if ( skb != NULL ) - { - skb->protocol = eth_type_trans(skb, skb->dev); - skb->src_vif = current_vif->id; - net_get_target_vif(skb); - if ( skb->dst_vif > VIF_PHYSICAL_INTERFACE ) + // Local delivery: Allocate an skb off the domain free list + // fil it, and pass it to netif_rx as if it came off the NIC. +//printk("LOCAL! (%d) \n", target); + skb = dev_alloc_skb(tx.size); + if (skb == NULL) { - (void)netif_rx(skb); + unmap_domain_mem(g_data); + continue; } - else if ( skb->dst_vif == VIF_PHYSICAL_INTERFACE ) + + skb->src_vif = current_vif->id; + skb->dst_vif = target; + skb->protocol = protocol; + + skb->head = (u8 *)map_domain_mem(((skb->pf - frame_table) << PAGE_SHIFT)); + skb->data = skb->head + 16; + skb_reserve(skb,2); + memcpy(skb->data, g_data, tx.size); + skb->len = tx.size; + unmap_domain_mem(skb->head); + skb->data += ETH_HLEN; // so the assertion in netif_RX doesn't freak out. + + (void)netif_rx(skb); + + unmap_domain_mem(g_data); + } + else if ( target == VIF_PHYSICAL_INTERFACE ) + { + // External delivery: Allocate a small skb to hold protected header info + // and copy the eth header and IP address fields into that. + // Set a frag link to the remaining data, and we will scatter-gather + // in the device driver to send the two bits later. + + /*unmap_domain_mem(g_data);*/ + + skb = alloc_skb(PKT_PROT_LEN, GFP_KERNEL); // Eth header + two IP addrs. + if (skb == NULL) { - skb_push(skb, skb->dev->hard_header_len); - dev_queue_xmit(skb); - } - else +printk("Alloc skb failed!\n"); + continue; + } + + skb_put(skb, PKT_PROT_LEN); + /*if ( copy_from_user(skb->data, (void *)tx.addr, PKT_PROT_LEN) ) { +printk("Copy from user failed!\n"); kfree_skb(skb); + continue; } + */ + memcpy(skb->data, g_data, PKT_PROT_LEN); + unmap_domain_mem(g_data); +//print_range2(g_data, PKT_PROT_LEN); + skb->dev = the_dev; + skb->src_vif = current_vif->id; + skb->dst_vif = target; + skb->protocol = protocol; // These next two lines abbreviate the call + // to eth_type_trans as we already have our + // protocol. + //skb_pull(skb, skb->dev->hard_header_len); + skb->mac.raw=skb->data; + // set tot_count++ in the guest data pfn. + page = (tx.addr >> PAGE_SHIFT) + frame_table; + page->tot_count++; + + // place the remainder of the packet (which is in guest memory) into an + // skb frag. + skb_shinfo(skb)->frags[0].page = page; + skb_shinfo(skb)->frags[0].size = tx.size - PKT_PROT_LEN; + skb_shinfo(skb)->frags[0].page_offset + = (tx.addr & ~PAGE_MASK) + PKT_PROT_LEN; + skb_shinfo(skb)->nr_frags = 1; + skb->data_len = tx.size - skb->len; + skb->len = tx.size; + + // assign a destructor to the skb that will unlink and dec the tot_count + skb->destructor = &tx_skb_release; + //skb_push(skb, skb->dev->hard_header_len); +//printk("calling dev_queue_xmit!\n"); + dev_queue_xmit(skb); + } + else + { + unmap_domain_mem(g_data); } } net_ring->tx_cons = i; - } + /* Next, pull any new RX descriptors across to the shadow ring. + */ + + shadow_ring = current_vif->shadow_ring; + + for (i = shadow_ring->rx_prod; i != net_ring->rx_prod; i = RX_RING_INC(i)) + { + /* This copy assumes that rx_shadow_entry_t is an extension of + * rx_net_entry_t extra fields must be tacked on to the end. + */ + if ( copy_from_user( shadow_ring->rx_ring+i, net_ring->rx_ring+i, + sizeof (rx_entry_t) ) ) + { + shadow_ring->rx_ring[i].status = RING_STATUS_ERR_CFU; + continue; + } else { + + rx = shadow_ring->rx_ring + i; + pfn = rx->addr >> PAGE_SHIFT; + page = frame_table + pfn; + + shadow_ring->rx_ring[i].status = RING_STATUS_BAD_PAGE; + + if ( page->flags != (PGT_l1_page_table | current->domain) ) + { +BUG(); + continue; + } + + + g_pte = map_domain_mem(rx->addr); + + if (!(*g_pte & _PAGE_PRESENT)) + { +BUG(); + unmap_domain_mem(g_pte); + continue; + } + + page = (*g_pte >> PAGE_SHIFT) + frame_table; + + if (page->tot_count != 1) + { +printk("!\n"); + unmap_domain_mem(g_pte); + continue; + } + + // The pte they passed was good, so we take it away from them. + shadow_ring->rx_ring[i].status = RING_STATUS_OK; + *g_pte &= ~_PAGE_PRESENT; + page->flags = (page->flags & ~PG_type_mask) | PGT_net_rx_buf; + rx->flush_count = tlb_flush_count[smp_processor_id()]; + + unmap_domain_mem(g_pte); + } + } + shadow_ring->rx_prod = net_ring->rx_prod; + } return 0; } diff --git a/xen-2.4.16/net/eth.c b/xen-2.4.16/net/eth.c index d982eef39b..5238de022e 100644 --- a/xen-2.4.16/net/eth.c +++ b/xen-2.4.16/net/eth.c @@ -161,52 +161,62 @@ unsigned short eth_type_trans(struct sk_buff *skb, struct net_device *dev) struct ethhdr *eth; unsigned char *rawp; - skb->mac.raw=skb->data; - skb_pull(skb,dev->hard_header_len); - eth= skb->mac.ethernet; + if (skb->skb_type == SKB_ZERO_COPY) + { + skb_pull(skb,dev->hard_header_len); + skb->mac.raw= (void *)0xdeadbeef; + return htons(ETH_P_802_2); + + } else { // SKB_NORMAL + + skb->mac.raw=skb->data; + skb_pull(skb,dev->hard_header_len); + eth= skb->mac.ethernet; - if(*eth->h_dest&1) - { - if(memcmp(eth->h_dest,dev->broadcast, ETH_ALEN)==0) + if(*eth->h_dest&1) + { + if(memcmp(eth->h_dest,dev->broadcast, ETH_ALEN)==0) skb->pkt_type=PACKET_BROADCAST; else skb->pkt_type=PACKET_MULTICAST; - } + } - /* - * This ALLMULTI check should be redundant by 1.4 - * so don't forget to remove it. - * - * Seems, you forgot to remove it. All silly devices - * seems to set IFF_PROMISC. - */ + /* + * This ALLMULTI check should be redundant by 1.4 + * so don't forget to remove it. + * + * Seems, you forgot to remove it. All silly devices + * seems to set IFF_PROMISC. + */ - else if(1 /*dev->flags&IFF_PROMISC*/) - { + else if(1 /*dev->flags&IFF_PROMISC*/) + { if(memcmp(eth->h_dest,dev->dev_addr, ETH_ALEN)) skb->pkt_type=PACKET_OTHERHOST; - } + } - if (ntohs(eth->h_proto) >= 1536) + if (ntohs(eth->h_proto) >= 1536) return eth->h_proto; - rawp = skb->data; + rawp = skb->data; - /* - * This is a magic hack to spot IPX packets. Older Novell breaks - * the protocol design and runs IPX over 802.3 without an 802.2 LLC - * layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This - * won't work for fault tolerant netware but does for the rest. - */ - if (*(unsigned short *)rawp == 0xFFFF) + /* + * This is a magic hack to spot IPX packets. Older Novell breaks + * the protocol design and runs IPX over 802.3 without an 802.2 LLC + * layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This + * won't work for fault tolerant netware but does for the rest. + */ + if (*(unsigned short *)rawp == 0xFFFF) return htons(ETH_P_802_3); - /* - * Real 802.2 LLC - */ - return htons(ETH_P_802_2); + /* + * Real 802.2 LLC + */ + return htons(ETH_P_802_2); + } } + int eth_header_parse(struct sk_buff *skb, unsigned char *haddr) { struct ethhdr *eth = skb->mac.ethernet; diff --git a/xen-2.4.16/net/skbuff.c b/xen-2.4.16/net/skbuff.c index 07896fda78..2df98d791e 100644 --- a/xen-2.4.16/net/skbuff.c +++ b/xen-2.4.16/net/skbuff.c @@ -63,6 +63,7 @@ #include #include +#include #define BUG_TRAP ASSERT @@ -149,6 +150,114 @@ static __inline__ void skb_head_to_pool(struct sk_buff *skb) kmem_cache_free(skbuff_head_cache, skb); } +//static unsigned long skbpagesout=0, skbpagesin=0; + +static inline u8 *alloc_skb_data_page(struct sk_buff *skb) +{ + struct list_head *list_ptr; + struct pfn_info *pf; + unsigned long flags; + + spin_lock_irqsave(&free_list_lock, flags); + + if (!free_pfns) return NULL; + + list_ptr = free_list.next; + pf = list_entry(list_ptr, struct pfn_info, list); + pf->flags = 0; // owned by dom0 + list_del(&pf->list); + //pf->next = pf->prev = (pf - frame_table); + free_pfns--; + + spin_unlock_irqrestore(&free_list_lock, flags); + + skb->pf = pf; +//if (skbpagesout++ % 100 == 0) printk("XEN-: skb allocs: %lu\n", skbpagesout); + return (u8 *)((pf - frame_table) << PAGE_SHIFT); +} + +static inline void dealloc_skb_data_page(struct sk_buff *skb) +{ + struct pfn_info *pf; + unsigned long flags; + + pf = skb->pf; + + spin_lock_irqsave(&free_list_lock, flags); + + list_add(&pf->list, &free_list); + free_pfns++; + + spin_unlock_irqrestore(&free_list_lock, flags); + +//if (skbpagesin++ % 100 == 0) printk("XEN-: skb allocs: %lu\n", skbpagesin); +} + +struct sk_buff *alloc_zc_skb(unsigned int size,int gfp_mask) +{ + struct sk_buff *skb; + u8 *data; + + if (in_interrupt() && (gfp_mask & __GFP_WAIT)) { + static int count = 0; + if (++count < 5) { + printk(KERN_ERR "alloc_skb called nonatomically " + "from interrupt %p\n", NET_CALLER(size)); + BUG(); + } + gfp_mask &= ~__GFP_WAIT; + } + + /* Get the HEAD */ + skb = skb_head_from_pool(); + if (skb == NULL) { + skb = kmem_cache_alloc(skbuff_head_cache, gfp_mask & ~__GFP_DMA); + if (skb == NULL) + goto nohead; + } + + /* Get the DATA. Size must match skb_add_mtu(). */ + size = SKB_DATA_ALIGN(size); + data = alloc_skb_data_page(skb); + + if (data == NULL) + goto nodata; + + // This is so that pci_map_single does the right thing in the driver. + // If you want to ever use this pointer otherwise, you need to regenerate it + // based on skb->pf. + data = phys_to_virt((unsigned long)data); + + /* XXX: does not include slab overhead */ + skb->truesize = size + sizeof(struct sk_buff); + + /* Load the data pointers. */ + skb->head = data; + skb->data = data; + skb->tail = data; + skb->end = data + size; + + /* Set up other state */ + skb->len = 0; + skb->cloned = 0; + skb->data_len = 0; + skb->src_vif = VIF_UNKNOWN_INTERFACE; + skb->dst_vif = VIF_UNKNOWN_INTERFACE; + skb->skb_type = SKB_ZERO_COPY; + + atomic_set(&skb->users, 1); + atomic_set(&(skb_shinfo(skb)->dataref), 1); + skb_shinfo(skb)->nr_frags = 0; + skb_shinfo(skb)->frag_list = NULL; + + return skb; + +nodata: + skb_head_to_pool(skb); +nohead: + return NULL; +} + /* Allocate a new skbuff. We do this ourselves so we can fill in a few * 'private' fields and also do memory statistics to find all the @@ -213,6 +322,7 @@ struct sk_buff *alloc_skb(unsigned int size,int gfp_mask) skb->data_len = 0; skb->src_vif = VIF_UNKNOWN_INTERFACE; skb->dst_vif = VIF_UNKNOWN_INTERFACE; + skb->skb_type = SKB_NORMAL; atomic_set(&skb->users, 1); atomic_set(&(skb_shinfo(skb)->dataref), 1); @@ -284,6 +394,7 @@ static void skb_clone_fraglist(struct sk_buff *skb) static void skb_release_data(struct sk_buff *skb) { + if (!skb->cloned || atomic_dec_and_test(&(skb_shinfo(skb)->dataref))) { if (skb_shinfo(skb)->nr_frags) { @@ -295,7 +406,12 @@ static void skb_release_data(struct sk_buff *skb) if (skb_shinfo(skb)->frag_list) skb_drop_fraglist(skb); - kfree(skb->head); + if (skb->skb_type == SKB_NORMAL) { + kfree(skb->head); + } else if (skb->skb_type == SKB_ZERO_COPY) { dealloc_skb_data_page(skb); + } else { + BUG(); //skb_release_data called with unknown skb type! + } } } @@ -333,6 +449,7 @@ void __kfree_skb(struct sk_buff *skb) } skb->destructor(skb); } + #ifdef CONFIG_NETFILTER nf_conntrack_put(skb->nfct); #endif diff --git a/xenolinux-2.4.16-sparse/arch/xeno/drivers/network/network.c b/xenolinux-2.4.16-sparse/arch/xeno/drivers/network/network.c index b018e29e49..87b1a97fdc 100644 --- a/xenolinux-2.4.16-sparse/arch/xeno/drivers/network/network.c +++ b/xenolinux-2.4.16-sparse/arch/xeno/drivers/network/network.c @@ -21,14 +21,12 @@ #include #include +#include #include #define NET_TX_IRQ _EVENT_NET_TX #define NET_RX_IRQ _EVENT_NET_RX -#define NET_TX_IRQ_FOR_VIF(x) _EVENT_NET_TX_FOR_VIF(x) -#define NET_RX_IRQ_FOR_VIF(x) _EVENT_NET_RX_FOR_VIF(x) - #define TX_MAX_ENTRIES (TX_RING_SIZE - 2) #define RX_MAX_ENTRIES (RX_RING_SIZE - 2) @@ -37,7 +35,7 @@ #define TX_RING_ADD(_i,_j) (((_i)+(_j)) & (TX_RING_SIZE-1)) #define RX_RING_ADD(_i,_j) (((_i)+(_j)) & (RX_RING_SIZE-1)) -#define RX_BUF_SIZE 1600 /* Ethernet MTU + plenty of slack! */ +#define RX_BUF_SIZE 2049 /* (was 1600) Ethernet MTU + plenty of slack! */ static void network_rx_int(int irq, void *dev_id, struct pt_regs *ptregs); static void network_tx_int(int irq, void *dev_id, struct pt_regs *ptregs); @@ -48,6 +46,8 @@ static void cleanup_module(void); static struct list_head dev_list; +static unsigned int net_countx; + /* * RX RING: RX_IDX <= rx_cons <= rx_prod * TX RING: TX_IDX <= tx_cons <= tx_prod @@ -72,16 +72,8 @@ struct net_private static int network_open(struct net_device *dev) { struct net_private *np = dev->priv; - int error; - char *rxlabel, *txlabel; + int error = 0; - // This is inevitably not the right way to allocate a couple of static strings. - rxlabel = kmalloc(sizeof("net-rx- "), GFP_KERNEL); - txlabel = kmalloc(sizeof("net-tx- "), GFP_KERNEL); - if ((rxlabel == NULL) || (txlabel == NULL)) goto fail; - sprintf(rxlabel, "net-rx-%d", np->id); - sprintf(txlabel, "net-tx-%d", np->id); - np->rx_idx = np->tx_idx = np->tx_full = 0; memset(&np->stats, 0, sizeof(np->stats)); @@ -113,8 +105,8 @@ static int network_open(struct net_device *dev) network_alloc_rx_buffers(dev); - error = request_irq(NET_RX_IRQ_FOR_VIF(np->id), network_rx_int, 0, - rxlabel, dev); + error = request_irq(NET_RX_IRQ, network_rx_int, 0, + "net-rx", dev); if ( error ) { printk(KERN_WARNING "%s: Could not allocate receive interrupt\n", @@ -122,13 +114,13 @@ static int network_open(struct net_device *dev) goto fail; } - error = request_irq(NET_TX_IRQ_FOR_VIF(np->id), network_tx_int, 0, - txlabel, dev); + error = request_irq(NET_TX_IRQ, network_tx_int, 0, + "net-tx", dev); if ( error ) { printk(KERN_WARNING "%s: Could not allocate transmit interrupt\n", dev->name); - free_irq(NET_RX_IRQ_FOR_VIF(np->id), dev); + free_irq(NET_RX_IRQ, dev); goto fail; } @@ -141,8 +133,6 @@ static int network_open(struct net_device *dev) return 0; fail: - if ( rxlabel ) kfree(rxlabel); - if ( txlabel ) kfree(txlabel); if ( np->net_ring->rx_ring ) kfree(np->net_ring->rx_ring); if ( np->net_ring->tx_ring ) kfree(np->net_ring->tx_ring); if ( np->rx_skb_ring ) kfree(np->rx_skb_ring); @@ -179,13 +169,59 @@ static void network_tx_buf_gc(struct net_device *dev) spin_unlock_irqrestore(&np->tx_lock, flags); } +inline unsigned long get_ppte(unsigned long addr) +{ + unsigned long ppte = 0xdeadbeef; + pgd_t *pgd; pmd_t *pmd; pte_t *ptep; + pgd = pgd_offset_k(addr); + if (pgd_none(*pgd) || pgd_bad(*pgd)) BUG(); + + pmd = pmd_offset(pgd, addr); + if (pmd_none(*pmd)) BUG(); + if (pmd_bad(*pmd)) BUG(); + + ptep = pte_offset(pmd, addr); + ppte = (unsigned long)phys_to_machine(virt_to_phys(ptep)); + + return ppte; +} +/* +static void validate_free_list(void) +{ + unsigned long addr, ppfn, mpfn, mpfn2, flags; + struct list_head *i; + struct net_page_info *np; + + printk(KERN_ALERT "Walking free pages:\n"); + + spin_lock_irqsave(&net_page_list_lock, flags); + + list_for_each(i, &net_page_list) + { + np = list_entry(i, struct net_page_info, list); + addr = np->virt_addr; + ppfn = virt_to_phys(addr) >> PAGE_SHIFT; + mpfn = get_ppte(addr); + mpfn2 = phys_to_machine_mapping[ppfn]; + + mpfn = (*(unsigned long *)phys_to_virt(machine_to_phys(mpfn))) >> PAGE_SHIFT; + if (mpfn != mpfn2) printk(KERN_ALERT "mpfn %lu != %lu\n", mpfn, mpfn2); + + if (machine_to_phys_mapping[mpfn] != ppfn) printk(KERN_ALERT "ppfn %lu != %lu\n", machine_to_phys_mapping[mpfn], ppfn); + } + + spin_unlock_irqrestore(&net_page_list_lock, flags); + +} +*/ static void network_alloc_rx_buffers(struct net_device *dev) { unsigned int i; struct net_private *np = dev->priv; struct sk_buff *skb; unsigned int end = RX_RING_ADD(np->rx_idx, RX_MAX_ENTRIES); + for ( i = np->net_ring->rx_prod; i != end; i = RX_RING_INC(i) ) { @@ -194,8 +230,9 @@ static void network_alloc_rx_buffers(struct net_device *dev) skb->dev = dev; skb_reserve(skb, 2); /* word align the IP header */ np->rx_skb_ring[i] = skb; - np->net_ring->rx_ring[i].addr = (unsigned long)skb->data; + np->net_ring->rx_ring[i].addr = get_ppte(skb->head); np->net_ring->rx_ring[i].size = RX_BUF_SIZE - 16; /* arbitrary */ +//printk(KERN_ALERT "[%p]\n", phys_to_machine(virt_to_phys(skb->page_ptr))); } np->net_ring->rx_prod = i; @@ -219,6 +256,14 @@ static void network_free_rx_buffers(struct net_device *dev) } } +void print_range(u8 *start, unsigned int len) +{ + int i = 0; + + while (i++ < len) + printk("%x:", start[i]); + printk("\n"); +} static int network_start_xmit(struct sk_buff *skb, struct net_device *dev) { @@ -231,10 +276,23 @@ static int network_start_xmit(struct sk_buff *skb, struct net_device *dev) netif_stop_queue(dev); return -ENOBUFS; } - +//print_range(skb->data, ETH_HLEN + 8); +//print_range(skb->data + ETH_HLEN + 8, 20); +//printk("skb->len is %u in guestOS (expected fraglen: %u).\n", skb->len, skb->len - (ETH_HLEN + 8)); i = np->net_ring->tx_prod; + + if ( (((unsigned long)skb->data & ~PAGE_MASK) + skb->len) >= PAGE_SIZE ) + { + struct sk_buff *new_skb = alloc_skb(RX_BUF_SIZE, GFP_KERNEL); + skb_put(new_skb, skb->len); + memcpy(new_skb->data, skb->data, skb->len); + kfree_skb(skb); + skb = new_skb; + } + np->tx_skb_ring[i] = skb; - np->net_ring->tx_ring[i].addr = (unsigned long)skb->data; + np->net_ring->tx_ring[i].addr + = (unsigned long)phys_to_machine(virt_to_phys(skb->data)); np->net_ring->tx_ring[i].size = skb->len; np->net_ring->tx_prod = TX_RING_INC(i); atomic_inc(&np->tx_entries); @@ -273,13 +331,30 @@ static void network_rx_int(int irq, void *dev_id, struct pt_regs *ptregs) struct net_private *np = dev->priv; struct sk_buff *skb; + /*if (net_countx++ % 100 == 0) validate_free_list();*/ + again: for ( i = np->rx_idx; i != np->net_ring->rx_cons; i = RX_RING_INC(i) ) { + if (np->net_ring->rx_ring[i].status != RING_STATUS_OK) + { + printk("bad buffer on RX ring!(%d)\n", + np->net_ring->rx_ring[i].status); + continue; + } skb = np->rx_skb_ring[i]; + +//printk(KERN_ALERT "[%u]: ptmm[%lx] old:(%lx) new:(%lx)\n", i , virt_to_phys(skb->head) >> PAGE_SHIFT, phys_to_machine_mapping[virt_to_phys(skb->head) >> PAGE_SHIFT], (*(unsigned long *)phys_to_virt(machine_to_phys(np->net_ring->rx_ring[i].addr))) >> PAGE_SHIFT); + + phys_to_machine_mapping[virt_to_phys(skb->head) >> PAGE_SHIFT] = + (*(unsigned long *)phys_to_virt( + machine_to_phys(np->net_ring->rx_ring[i].addr)) + ) >> PAGE_SHIFT; + skb_put(skb, np->net_ring->rx_ring[i].size); skb->protocol = eth_type_trans(skb, dev); np->stats.rx_packets++; + np->stats.rx_bytes += np->net_ring->rx_ring[i].size; netif_rx(skb); dev->last_rx = jiffies; @@ -309,8 +384,8 @@ int network_close(struct net_device *dev) struct net_private *np = dev->priv; netif_stop_queue(dev); - free_irq(NET_RX_IRQ_FOR_VIF(np->id), dev); - free_irq(NET_TX_IRQ_FOR_VIF(np->id), dev); + free_irq(NET_RX_IRQ, dev); + free_irq(NET_TX_IRQ, dev); network_free_rx_buffers(dev); kfree(np->net_ring->rx_ring); kfree(np->net_ring->tx_ring); diff --git a/xenolinux-2.4.16-sparse/include/asm-xeno/io.h b/xenolinux-2.4.16-sparse/include/asm-xeno/io.h index 250b64fac8..1afc7e4d68 100644 --- a/xenolinux-2.4.16-sparse/include/asm-xeno/io.h +++ b/xenolinux-2.4.16-sparse/include/asm-xeno/io.h @@ -2,7 +2,7 @@ #define _ASM_IO_H #include - +#include /* * This file contains the definitions for the x86 IO instructions * inb/inw/inl/outb/outw/outl and the "string versions" of the same @@ -73,6 +73,11 @@ static inline void * phys_to_virt(unsigned long address) return __va(address); } +/* + * Change virtual addresses to machine addresses and vv. + * These are equally trivial. + */ + /* * Change "struct page" to physical address. */ diff --git a/xenolinux-2.4.16-sparse/include/linux/skbuff.h b/xenolinux-2.4.16-sparse/include/linux/skbuff.h new file mode 100644 index 0000000000..fb2e5f15d8 --- /dev/null +++ b/xenolinux-2.4.16-sparse/include/linux/skbuff.h @@ -0,0 +1,1186 @@ +/* + * Definitions for the 'struct sk_buff' memory handlers. + * + * Authors: + * Alan Cox, + * Florian La Roche, + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_SKBUFF_H +#define _LINUX_SKBUFF_H + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* Zero Copy additions: + * + * (1) there are now two types of skb, as indicated by the skb_type field. + * this is because, at least for the time being, there are two seperate types + * of memory that may be allocated to skb->data. + * + * (2) until discontiguous memory is fully supported, there will be a free list of pages + * to be used by the net RX code. This list will be allocated in the driver init code + * but is declared here because the socket free code needs to return pages to it. + */ + +// for skb->skb_type: + +#define SKB_NORMAL 0 +#define SKB_ZERO_COPY 1 + +#define NUM_NET_PAGES 9 // about 1Meg of buffers. (2^9) + +/*struct net_page_info { + struct list_head list; + unsigned long virt_addr; + unsigned long ppte; +}; + +extern char *net_page_chunk; +extern struct net_page_info *net_page_table; +extern struct list_head net_page_list; +extern spinlock_t net_page_list_lock; +extern unsigned int net_pages; +*/ +/* End zero copy additions */ + +#define HAVE_ALLOC_SKB /* For the drivers to know */ +#define HAVE_ALIGNABLE_SKB /* Ditto 8) */ +#define SLAB_SKB /* Slabified skbuffs */ + +#define CHECKSUM_NONE 0 +#define CHECKSUM_HW 1 +#define CHECKSUM_UNNECESSARY 2 + +#define SKB_DATA_ALIGN(X) (((X) + (SMP_CACHE_BYTES-1)) & ~(SMP_CACHE_BYTES-1)) +#define SKB_MAX_ORDER(X,ORDER) (((PAGE_SIZE<<(ORDER)) - (X) - sizeof(struct skb_shared_info))&~(SMP_CACHE_BYTES-1)) +#define SKB_MAX_HEAD(X) (SKB_MAX_ORDER((X),0)) +#define SKB_MAX_ALLOC (SKB_MAX_ORDER(0,2)) + +/* A. Checksumming of received packets by device. + * + * NONE: device failed to checksum this packet. + * skb->csum is undefined. + * + * UNNECESSARY: device parsed packet and wouldbe verified checksum. + * skb->csum is undefined. + * It is bad option, but, unfortunately, many of vendors do this. + * Apparently with secret goal to sell you new device, when you + * will add new protocol to your host. F.e. IPv6. 8) + * + * HW: the most generic way. Device supplied checksum of _all_ + * the packet as seen by netif_rx in skb->csum. + * NOTE: Even if device supports only some protocols, but + * is able to produce some skb->csum, it MUST use HW, + * not UNNECESSARY. + * + * B. Checksumming on output. + * + * NONE: skb is checksummed by protocol or csum is not required. + * + * HW: device is required to csum packet as seen by hard_start_xmit + * from skb->h.raw to the end and to record the checksum + * at skb->h.raw+skb->csum. + * + * Device must show its capabilities in dev->features, set + * at device setup time. + * NETIF_F_HW_CSUM - it is clever device, it is able to checksum + * everything. + * NETIF_F_NO_CSUM - loopback or reliable single hop media. + * NETIF_F_IP_CSUM - device is dumb. It is able to csum only + * TCP/UDP over IPv4. Sigh. Vendors like this + * way by an unknown reason. Though, see comment above + * about CHECKSUM_UNNECESSARY. 8) + * + * Any questions? No questions, good. --ANK + */ + +#ifdef __i386__ +#define NET_CALLER(arg) (*(((void**)&arg)-1)) +#else +#define NET_CALLER(arg) __builtin_return_address(0) +#endif + +#ifdef CONFIG_NETFILTER +struct nf_conntrack { + atomic_t use; + void (*destroy)(struct nf_conntrack *); +}; + +struct nf_ct_info { + struct nf_conntrack *master; +}; +#endif + +struct sk_buff_head { + /* These two members must be first. */ + struct sk_buff * next; + struct sk_buff * prev; + + __u32 qlen; + spinlock_t lock; +}; + +struct sk_buff; + +#define MAX_SKB_FRAGS 6 + +typedef struct skb_frag_struct skb_frag_t; + +struct skb_frag_struct +{ + struct page *page; + __u16 page_offset; + __u16 size; +}; + +/* This data is invariant across clones and lives at + * the end of the header data, ie. at skb->end. + */ +struct skb_shared_info { + atomic_t dataref; + unsigned int nr_frags; + struct sk_buff *frag_list; + skb_frag_t frags[MAX_SKB_FRAGS]; +}; + +struct sk_buff { + /* These two members must be first. */ + 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 net_device *dev; /* Device we arrived on/are leaving by */ + + /* Transport layer header */ + union + { + struct tcphdr *th; + struct udphdr *uh; + struct icmphdr *icmph; + struct igmphdr *igmph; + struct iphdr *ipiph; + struct spxhdr *spxh; + unsigned char *raw; + } h; + + /* Network layer header */ + union + { + struct iphdr *iph; + struct ipv6hdr *ipv6h; + struct arphdr *arph; + struct ipxhdr *ipxh; + unsigned char *raw; + } nh; + + /* Link layer header */ + union + { + struct ethhdr *ethernet; + unsigned char *raw; + } mac; + + struct dst_entry *dst; + + /* + * This is the control buffer. It is free to use for every + * layer. Please put your private variables there. If you + * want to keep them across layers you have to do a skb_clone() + * first. This is owned by whoever has the skb queued ATM. + */ + char cb[48]; + + unsigned int len; /* Length of actual data */ + unsigned int data_len; + unsigned int csum; /* Checksum */ + unsigned char __unused, /* Dead field, may be reused */ + cloned, /* head may be cloned (check refcnt to be sure). */ + pkt_type, /* Packet class */ + 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_NETFILTER + /* Can be used for communication between hooks. */ + unsigned long nfmark; + /* Cache info */ + __u32 nfcache; + /* Associated connection, if any */ + struct nf_ct_info *nfct; +#ifdef CONFIG_NETFILTER_DEBUG + unsigned int nf_debug; +#endif +#endif /*CONFIG_NETFILTER*/ + +#if defined(CONFIG_HIPPI) + union{ + __u32 ifield; + } private; +#endif + +#ifdef CONFIG_NET_SCHED + __u32 tc_index; /* traffic control index */ +#endif + unsigned int skb_type; /* for zero copy handling. */ + struct net_page_info *net_page; +}; + +#define SK_WMEM_MAX 65535 +#define SK_RMEM_MAX 65535 + +#ifdef __KERNEL__ +/* + * Handling routines are only of interest to the kernel + */ +#include + +#include + +extern void __kfree_skb(struct sk_buff *skb); +extern struct sk_buff * alloc_skb(unsigned int size, int priority); +extern struct sk_buff * alloc_zc_skb(unsigned int size, int priority); +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(const struct sk_buff *skb, int priority); +extern struct sk_buff * pskb_copy(struct sk_buff *skb, int gfp_mask); +extern int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, int gfp_mask); +extern struct sk_buff * skb_realloc_headroom(struct sk_buff *skb, unsigned int headroom); +extern struct sk_buff * skb_copy_expand(const struct sk_buff *skb, + int newheadroom, + int newtailroom, + int priority); +#define dev_kfree_skb(a) kfree_skb(a) +extern void skb_over_panic(struct sk_buff *skb, int len, void *here); +extern void skb_under_panic(struct sk_buff *skb, int len, void *here); + +/* Internal */ +#define skb_shinfo(SKB) ((struct skb_shared_info *)((SKB)->end)) + +/** + * skb_queue_empty - check if a queue is empty + * @list: queue head + * + * Returns true if the queue is empty, false otherwise. + */ + +static inline int skb_queue_empty(struct sk_buff_head *list) +{ + return (list->next == (struct sk_buff *) list); +} + +/** + * skb_get - reference buffer + * @skb: buffer to reference + * + * Makes another reference to a socket buffer and returns a pointer + * to the buffer. + */ + +static inline struct sk_buff *skb_get(struct sk_buff *skb) +{ + atomic_inc(&skb->users); + return skb; +} + +/* + * If users==1, we are the only owner and are can avoid redundant + * atomic change. + */ + +/** + * kfree_skb - free an sk_buff + * @skb: buffer to free + * + * Drop a reference to the buffer and free it if the usage count has + * hit zero. + */ + +static inline void kfree_skb(struct sk_buff *skb) +{ + if (atomic_read(&skb->users) == 1 || atomic_dec_and_test(&skb->users)) + __kfree_skb(skb); +} + +/* Use this if you didn't touch the skb state [for fast switching] */ +static inline void kfree_skb_fast(struct sk_buff *skb) +{ + if (atomic_read(&skb->users) == 1 || atomic_dec_and_test(&skb->users)) + kfree_skbmem(skb); +} + +/** + * skb_cloned - is the buffer a clone + * @skb: buffer to check + * + * Returns true if the buffer was generated with skb_clone() and is + * one of multiple shared copies of the buffer. Cloned buffers are + * shared data so must not be written to under normal circumstances. + */ + +static inline int skb_cloned(struct sk_buff *skb) +{ + return skb->cloned && atomic_read(&skb_shinfo(skb)->dataref) != 1; +} + +/** + * skb_shared - is the buffer shared + * @skb: buffer to check + * + * Returns true if more than one person has a reference to this + * buffer. + */ + +static inline int skb_shared(struct sk_buff *skb) +{ + return (atomic_read(&skb->users) != 1); +} + +/** + * skb_share_check - check if buffer is shared and if so clone it + * @skb: buffer to check + * @pri: priority for memory allocation + * + * If the buffer is shared the buffer is cloned and the old copy + * drops a reference. A new clone with a single reference is returned. + * If the buffer is not shared the original buffer is returned. When + * being called from interrupt status or with spinlocks held pri must + * be GFP_ATOMIC. + * + * NULL is returned on a memory allocation failure. + */ + +static inline struct sk_buff *skb_share_check(struct sk_buff *skb, int pri) +{ + if (skb_shared(skb)) { + struct sk_buff *nskb; + nskb = skb_clone(skb, pri); + kfree_skb(skb); + return nskb; + } + return skb; +} + + +/* + * Copy shared buffers into a new sk_buff. We effectively do COW on + * packets to handle cases where we have a local reader and forward + * and a couple of other messy ones. The normal one is tcpdumping + * a packet thats being forwarded. + */ + +/** + * skb_unshare - make a copy of a shared buffer + * @skb: buffer to check + * @pri: priority for memory allocation + * + * If the socket buffer is a clone then this function creates a new + * copy of the data, drops a reference count on the old copy and returns + * the new copy with the reference count at 1. If the buffer is not a clone + * the original buffer is returned. When called with a spinlock held or + * from interrupt state @pri must be %GFP_ATOMIC + * + * %NULL is returned on a memory allocation failure. + */ + +static inline struct sk_buff *skb_unshare(struct sk_buff *skb, int pri) +{ + struct sk_buff *nskb; + if(!skb_cloned(skb)) + return skb; + nskb=skb_copy(skb, pri); + kfree_skb(skb); /* Free our shared copy */ + return nskb; +} + +/** + * skb_peek + * @list_: list to peek at + * + * Peek an &sk_buff. Unlike most other operations you _MUST_ + * be careful with this one. A peek leaves the buffer on the + * list and someone else may run off with it. You must hold + * the appropriate locks or have a private queue to do this. + * + * Returns %NULL for an empty list or a pointer to the head element. + * The reference count is not incremented and the reference is therefore + * volatile. Use with caution. + */ + +static inline struct sk_buff *skb_peek(struct sk_buff_head *list_) +{ + struct sk_buff *list = ((struct sk_buff *)list_)->next; + if (list == (struct sk_buff *)list_) + list = NULL; + return list; +} + +/** + * skb_peek_tail + * @list_: list to peek at + * + * Peek an &sk_buff. Unlike most other operations you _MUST_ + * be careful with this one. A peek leaves the buffer on the + * list and someone else may run off with it. You must hold + * the appropriate locks or have a private queue to do this. + * + * Returns %NULL for an empty list or a pointer to the tail element. + * The reference count is not incremented and the reference is therefore + * volatile. Use with caution. + */ + +static inline struct sk_buff *skb_peek_tail(struct sk_buff_head *list_) +{ + struct sk_buff *list = ((struct sk_buff *)list_)->prev; + if (list == (struct sk_buff *)list_) + list = NULL; + return list; +} + +/** + * skb_queue_len - get queue length + * @list_: list to measure + * + * Return the length of an &sk_buff queue. + */ + +static inline __u32 skb_queue_len(struct sk_buff_head *list_) +{ + return(list_->qlen); +} + +static inline void skb_queue_head_init(struct sk_buff_head *list) +{ + spin_lock_init(&list->lock); + list->prev = (struct sk_buff *)list; + list->next = (struct sk_buff *)list; + list->qlen = 0; +} + +/* + * Insert an sk_buff at the start of a list. + * + * The "__skb_xxxx()" functions are the non-atomic ones that + * can only be called with interrupts disabled. + */ + +/** + * __skb_queue_head - queue a buffer at the list head + * @list: list to use + * @newsk: buffer to queue + * + * Queue a buffer at the start of a list. This function takes no locks + * and you must therefore hold required locks before calling it. + * + * A buffer cannot be placed on two lists at the same time. + */ + +static inline void __skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk) +{ + struct sk_buff *prev, *next; + + newsk->list = list; + list->qlen++; + prev = (struct sk_buff *)list; + next = prev->next; + newsk->next = next; + newsk->prev = prev; + next->prev = newsk; + prev->next = newsk; +} + + +/** + * skb_queue_head - queue a buffer at the list head + * @list: list to use + * @newsk: buffer to queue + * + * Queue a buffer at the start of the list. This function takes the + * list lock and can be used safely with other locking &sk_buff functions + * safely. + * + * A buffer cannot be placed on two lists at the same time. + */ + +static inline void skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk) +{ + unsigned long flags; + + spin_lock_irqsave(&list->lock, flags); + __skb_queue_head(list, newsk); + spin_unlock_irqrestore(&list->lock, flags); +} + +/** + * __skb_queue_tail - queue a buffer at the list tail + * @list: list to use + * @newsk: buffer to queue + * + * Queue a buffer at the end of a list. This function takes no locks + * and you must therefore hold required locks before calling it. + * + * A buffer cannot be placed on two lists at the same time. + */ + + +static inline void __skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk) +{ + struct sk_buff *prev, *next; + + newsk->list = list; + list->qlen++; + next = (struct sk_buff *)list; + prev = next->prev; + newsk->next = next; + newsk->prev = prev; + next->prev = newsk; + prev->next = newsk; +} + +/** + * skb_queue_tail - queue a buffer at the list tail + * @list: list to use + * @newsk: buffer to queue + * + * Queue a buffer at the tail of the list. This function takes the + * list lock and can be used safely with other locking &sk_buff functions + * safely. + * + * A buffer cannot be placed on two lists at the same time. + */ + +static inline void skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk) +{ + unsigned long flags; + + spin_lock_irqsave(&list->lock, flags); + __skb_queue_tail(list, newsk); + spin_unlock_irqrestore(&list->lock, flags); +} + +/** + * __skb_dequeue - remove from the head of the queue + * @list: list to dequeue from + * + * Remove the head of the list. This function does not take any locks + * so must be used with appropriate locks held only. The head item is + * returned or %NULL if the list is empty. + */ + +static inline struct sk_buff *__skb_dequeue(struct sk_buff_head *list) +{ + struct sk_buff *next, *prev, *result; + + prev = (struct sk_buff *) list; + next = prev->next; + result = NULL; + if (next != prev) { + result = next; + next = next->next; + list->qlen--; + next->prev = prev; + prev->next = next; + result->next = NULL; + result->prev = NULL; + result->list = NULL; + } + return result; +} + +/** + * skb_dequeue - remove from the head of the queue + * @list: list to dequeue from + * + * Remove the head of the list. The list lock is taken so the function + * may be used safely with other locking list functions. The head item is + * returned or %NULL if the list is empty. + */ + +static inline struct sk_buff *skb_dequeue(struct sk_buff_head *list) +{ + long flags; + struct sk_buff *result; + + spin_lock_irqsave(&list->lock, flags); + result = __skb_dequeue(list); + spin_unlock_irqrestore(&list->lock, flags); + return result; +} + +/* + * Insert a packet on a list. + */ + +static inline void __skb_insert(struct sk_buff *newsk, + struct sk_buff * prev, struct sk_buff *next, + struct sk_buff_head * list) +{ + newsk->next = next; + newsk->prev = prev; + next->prev = newsk; + prev->next = newsk; + newsk->list = list; + list->qlen++; +} + +/** + * skb_insert - insert a buffer + * @old: buffer to insert before + * @newsk: buffer to insert + * + * Place a packet before a given packet in a list. The list locks are taken + * and this function is atomic with respect to other list locked calls + * A buffer cannot be placed on two lists at the same time. + */ + +static inline void skb_insert(struct sk_buff *old, struct sk_buff *newsk) +{ + unsigned long flags; + + spin_lock_irqsave(&old->list->lock, flags); + __skb_insert(newsk, old->prev, old, old->list); + spin_unlock_irqrestore(&old->list->lock, flags); +} + +/* + * Place a packet after a given packet in a list. + */ + +static inline void __skb_append(struct sk_buff *old, struct sk_buff *newsk) +{ + __skb_insert(newsk, old, old->next, old->list); +} + +/** + * skb_append - append a buffer + * @old: buffer to insert after + * @newsk: buffer to insert + * + * Place a packet after a given packet in a list. The list locks are taken + * and this function is atomic with respect to other list locked calls. + * A buffer cannot be placed on two lists at the same time. + */ + + +static inline void skb_append(struct sk_buff *old, struct sk_buff *newsk) +{ + unsigned long flags; + + spin_lock_irqsave(&old->list->lock, flags); + __skb_append(old, newsk); + spin_unlock_irqrestore(&old->list->lock, flags); +} + +/* + * remove sk_buff from list. _Must_ be called atomically, and with + * the list known.. + */ + +static inline void __skb_unlink(struct sk_buff *skb, struct sk_buff_head *list) +{ + struct sk_buff * next, * prev; + + list->qlen--; + next = skb->next; + prev = skb->prev; + skb->next = NULL; + skb->prev = NULL; + skb->list = NULL; + next->prev = prev; + prev->next = next; +} + +/** + * skb_unlink - remove a buffer from a list + * @skb: buffer to remove + * + * Place a packet after a given packet in a list. The list locks are taken + * and this function is atomic with respect to other list locked calls + * + * Works even without knowing the list it is sitting on, which can be + * handy at times. It also means that THE LIST MUST EXIST when you + * unlink. Thus a list must have its contents unlinked before it is + * destroyed. + */ + +static inline void skb_unlink(struct sk_buff *skb) +{ + struct sk_buff_head *list = skb->list; + + if(list) { + unsigned long flags; + + spin_lock_irqsave(&list->lock, flags); + if(skb->list == list) + __skb_unlink(skb, skb->list); + spin_unlock_irqrestore(&list->lock, flags); + } +} + +/* XXX: more streamlined implementation */ + +/** + * __skb_dequeue_tail - remove from the tail of the queue + * @list: list to dequeue from + * + * Remove the tail of the list. This function does not take any locks + * so must be used with appropriate locks held only. The tail item is + * returned or %NULL if the list is empty. + */ + +static inline struct sk_buff *__skb_dequeue_tail(struct sk_buff_head *list) +{ + struct sk_buff *skb = skb_peek_tail(list); + if (skb) + __skb_unlink(skb, list); + return skb; +} + +/** + * skb_dequeue - remove from the head of the queue + * @list: list to dequeue from + * + * Remove the head of the list. The list lock is taken so the function + * may be used safely with other locking list functions. The tail item is + * returned or %NULL if the list is empty. + */ + +static inline struct sk_buff *skb_dequeue_tail(struct sk_buff_head *list) +{ + long flags; + struct sk_buff *result; + + spin_lock_irqsave(&list->lock, flags); + result = __skb_dequeue_tail(list); + spin_unlock_irqrestore(&list->lock, flags); + return result; +} + +static inline int skb_is_nonlinear(const struct sk_buff *skb) +{ + return skb->data_len; +} + +static inline int skb_headlen(const struct sk_buff *skb) +{ + return skb->len - skb->data_len; +} + +#define SKB_PAGE_ASSERT(skb) do { if (skb_shinfo(skb)->nr_frags) BUG(); } while (0) +#define SKB_FRAG_ASSERT(skb) do { if (skb_shinfo(skb)->frag_list) BUG(); } while (0) +#define SKB_LINEAR_ASSERT(skb) do { if (skb_is_nonlinear(skb)) BUG(); } while (0) + +/* + * Add data to an sk_buff + */ + +static inline unsigned char *__skb_put(struct sk_buff *skb, unsigned int len) +{ + unsigned char *tmp=skb->tail; + SKB_LINEAR_ASSERT(skb); + skb->tail+=len; + skb->len+=len; + return tmp; +} + +/** + * skb_put - add data to a buffer + * @skb: buffer to use + * @len: amount of data to add + * + * This function extends the used data area of the buffer. If this would + * exceed the total buffer size the kernel will panic. A pointer to the + * first byte of the extra data is returned. + */ + +static inline unsigned char *skb_put(struct sk_buff *skb, unsigned int len) +{ + unsigned char *tmp=skb->tail; + SKB_LINEAR_ASSERT(skb); + skb->tail+=len; + skb->len+=len; + if(skb->tail>skb->end) { + skb_over_panic(skb, len, current_text_addr()); + } + return tmp; +} + +static inline unsigned char *__skb_push(struct sk_buff *skb, unsigned int len) +{ + skb->data-=len; + skb->len+=len; + return skb->data; +} + +/** + * skb_push - add data to the start of a buffer + * @skb: buffer to use + * @len: amount of data to add + * + * This function extends the used data area of the buffer at the buffer + * start. If this would exceed the total buffer headroom the kernel will + * panic. A pointer to the first byte of the extra data is returned. + */ + +static inline unsigned char *skb_push(struct sk_buff *skb, unsigned int len) +{ + skb->data-=len; + skb->len+=len; + if(skb->datahead) { + skb_under_panic(skb, len, current_text_addr()); + } + return skb->data; +} + +static inline char *__skb_pull(struct sk_buff *skb, unsigned int len) +{ + skb->len-=len; + if (skb->len < skb->data_len) + BUG(); + return skb->data+=len; +} + +/** + * skb_pull - remove data from the start of a buffer + * @skb: buffer to use + * @len: amount of data to remove + * + * This function removes data from the start of a buffer, returning + * the memory to the headroom. A pointer to the next data in the buffer + * is returned. Once the data has been pulled future pushes will overwrite + * the old data. + */ + +static inline unsigned char * skb_pull(struct sk_buff *skb, unsigned int len) +{ + if (len > skb->len) + return NULL; + return __skb_pull(skb,len); +} + +extern unsigned char * __pskb_pull_tail(struct sk_buff *skb, int delta); + +static inline char *__pskb_pull(struct sk_buff *skb, unsigned int len) +{ + if (len > skb_headlen(skb) && + __pskb_pull_tail(skb, len-skb_headlen(skb)) == NULL) + return NULL; + skb->len -= len; + return skb->data += len; +} + +static inline unsigned char * pskb_pull(struct sk_buff *skb, unsigned int len) +{ + if (len > skb->len) + return NULL; + return __pskb_pull(skb,len); +} + +static inline int pskb_may_pull(struct sk_buff *skb, unsigned int len) +{ + if (len <= skb_headlen(skb)) + return 1; + if (len > skb->len) + return 0; + return (__pskb_pull_tail(skb, len-skb_headlen(skb)) != NULL); +} + +/** + * skb_headroom - bytes at buffer head + * @skb: buffer to check + * + * Return the number of bytes of free space at the head of an &sk_buff. + */ + +static inline int skb_headroom(const struct sk_buff *skb) +{ + return skb->data-skb->head; +} + +/** + * skb_tailroom - bytes at buffer end + * @skb: buffer to check + * + * Return the number of bytes of free space at the tail of an sk_buff + */ + +static inline int skb_tailroom(const struct sk_buff *skb) +{ + return skb_is_nonlinear(skb) ? 0 : skb->end-skb->tail; +} + +/** + * skb_reserve - adjust headroom + * @skb: buffer to alter + * @len: bytes to move + * + * Increase the headroom of an empty &sk_buff by reducing the tail + * room. This is only allowed for an empty buffer. + */ + +static inline void skb_reserve(struct sk_buff *skb, unsigned int len) +{ + skb->data+=len; + skb->tail+=len; +} + +extern int ___pskb_trim(struct sk_buff *skb, unsigned int len, int realloc); + +static inline void __skb_trim(struct sk_buff *skb, unsigned int len) +{ + if (!skb->data_len) { + skb->len = len; + skb->tail = skb->data+len; + } else { + ___pskb_trim(skb, len, 0); + } +} + +/** + * skb_trim - remove end from a buffer + * @skb: buffer to alter + * @len: new length + * + * Cut the length of a buffer down by removing data from the tail. If + * the buffer is already under the length specified it is not modified. + */ + +static inline void skb_trim(struct sk_buff *skb, unsigned int len) +{ + if (skb->len > len) { + __skb_trim(skb, len); + } +} + + +static inline int __pskb_trim(struct sk_buff *skb, unsigned int len) +{ + if (!skb->data_len) { + skb->len = len; + skb->tail = skb->data+len; + return 0; + } else { + return ___pskb_trim(skb, len, 1); + } +} + +static inline int pskb_trim(struct sk_buff *skb, unsigned int len) +{ + if (len < skb->len) + return __pskb_trim(skb, len); + return 0; +} + +/** + * skb_orphan - orphan a buffer + * @skb: buffer to orphan + * + * If a buffer currently has an owner then we call the owner's + * destructor function and make the @skb unowned. The buffer continues + * to exist but is no longer charged to its former owner. + */ + + +static inline void skb_orphan(struct sk_buff *skb) +{ + if (skb->destructor) + skb->destructor(skb); + skb->destructor = NULL; + skb->sk = NULL; +} + +/** + * skb_purge - empty a list + * @list: list to empty + * + * Delete all buffers on an &sk_buff list. Each buffer is removed from + * the list and one reference dropped. This function takes the list + * lock and is atomic with respect to other list locking functions. + */ + + +static inline void skb_queue_purge(struct sk_buff_head *list) +{ + struct sk_buff *skb; + while ((skb=skb_dequeue(list))!=NULL) + kfree_skb(skb); +} + +/** + * __skb_purge - empty a list + * @list: list to empty + * + * Delete all buffers on an &sk_buff list. Each buffer is removed from + * the list and one reference dropped. This function does not take the + * list lock and the caller must hold the relevant locks to use it. + */ + + +static inline void __skb_queue_purge(struct sk_buff_head *list) +{ + struct sk_buff *skb; + while ((skb=__skb_dequeue(list))!=NULL) + kfree_skb(skb); +} + +/** + * __dev_alloc_skb - allocate an skbuff for sending + * @length: length to allocate + * @gfp_mask: get_free_pages mask, passed to alloc_skb + * + * Allocate a new &sk_buff and assign it a usage count of one. The + * buffer has unspecified headroom built in. Users should allocate + * the headroom they think they need without accounting for the + * built in space. The built in space is used for optimisations. + * + * %NULL is returned in there is no free memory. + */ + +static inline struct sk_buff *__dev_alloc_skb(unsigned int length, + int gfp_mask) +{ + struct sk_buff *skb; + + skb = alloc_skb(length+16, gfp_mask); + //skb = alloc_zc_skb(length+16, gfp_mask); + if (skb) + skb_reserve(skb,16); + return skb; +} + +/** + * dev_alloc_skb - allocate an skbuff for sending + * @length: length to allocate + * + * Allocate a new &sk_buff and assign it a usage count of one. The + * buffer has unspecified headroom built in. Users should allocate + * the headroom they think they need without accounting for the + * built in space. The built in space is used for optimisations. + * + * %NULL is returned in there is no free memory. Although this function + * allocates memory it can be called from an interrupt. + */ + +static inline struct sk_buff *dev_alloc_skb(unsigned int length) +{ + return __dev_alloc_skb(length, GFP_ATOMIC); +} + +/** + * skb_cow - copy header of skb when it is required + * @skb: buffer to cow + * @headroom: needed headroom + * + * If the skb passed lacks sufficient headroom or its data part + * is shared, data is reallocated. If reallocation fails, an error + * is returned and original skb is not changed. + * + * The result is skb with writable area skb->head...skb->tail + * and at least @headroom of space at head. + */ + +static inline int +skb_cow(struct sk_buff *skb, unsigned int headroom) +{ + int delta = (headroom > 16 ? headroom : 16) - skb_headroom(skb); + + if (delta < 0) + delta = 0; + + if (delta || skb_cloned(skb)) + return pskb_expand_head(skb, (delta+15)&~15, 0, GFP_ATOMIC); + return 0; +} + +/** + * skb_linearize - convert paged skb to linear one + * @skb: buffer to linarize + * @gfp: allocation mode + * + * If there is no free memory -ENOMEM is returned, otherwise zero + * is returned and the old skb data released. */ +int skb_linearize(struct sk_buff *skb, int gfp); + +static inline void *kmap_skb_frag(const skb_frag_t *frag) +{ +#ifdef CONFIG_HIGHMEM + if (in_irq()) + BUG(); + + local_bh_disable(); +#endif + return kmap_atomic(frag->page, KM_SKB_DATA_SOFTIRQ); +} + +static inline void kunmap_skb_frag(void *vaddr) +{ + kunmap_atomic(vaddr, KM_SKB_DATA_SOFTIRQ); +#ifdef CONFIG_HIGHMEM + local_bh_enable(); +#endif +} + +#define skb_queue_walk(queue, skb) \ + for (skb = (queue)->next; \ + (skb != (struct sk_buff *)(queue)); \ + skb=skb->next) + + +extern struct sk_buff * skb_recv_datagram(struct sock *sk,unsigned flags,int noblock, int *err); +extern unsigned int datagram_poll(struct file *file, struct socket *sock, struct poll_table_struct *wait); +extern int skb_copy_datagram(const struct sk_buff *from, int offset, char *to,int size); +extern int skb_copy_datagram_iovec(const struct sk_buff *from, int offset, struct iovec *to,int size); +extern int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset, u8 *to, int len, unsigned int *csump); +extern int skb_copy_and_csum_datagram_iovec(const struct sk_buff *skb, int hlen, struct iovec *iov); +extern void skb_free_datagram(struct sock * sk, struct sk_buff *skb); + +extern unsigned int skb_checksum(const struct sk_buff *skb, int offset, int len, unsigned int csum); +extern int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len); +extern unsigned int skb_copy_and_csum_bits(const struct sk_buff *skb, int offset, u8 *to, int len, unsigned int csum); +extern void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to); + +extern void skb_init(void); +extern void skb_add_mtu(int mtu); + +#ifdef CONFIG_NETFILTER +static inline void +nf_conntrack_put(struct nf_ct_info *nfct) +{ + if (nfct && atomic_dec_and_test(&nfct->master->use)) + nfct->master->destroy(nfct->master); +} +static inline void +nf_conntrack_get(struct nf_ct_info *nfct) +{ + if (nfct) + atomic_inc(&nfct->master->use); +} +#endif + +#endif /* __KERNEL__ */ +#endif /* _LINUX_SKBUFF_H */ diff --git a/xenolinux-2.4.16-sparse/net/core/skbuff.c b/xenolinux-2.4.16-sparse/net/core/skbuff.c new file mode 100644 index 0000000000..45332f1ffd --- /dev/null +++ b/xenolinux-2.4.16-sparse/net/core/skbuff.c @@ -0,0 +1,1373 @@ +/* + * Routines having to do with the 'struct sk_buff' memory handlers. + * + * Authors: Alan Cox + * Florian La Roche + * + * Version: $Id: skbuff.c,v 1.89 2001/08/06 13:25:02 davem Exp $ + * + * Fixes: + * Alan Cox : Fixed the worst of the load balancer bugs. + * Dave Platt : Interrupt stacking fix. + * Richard Kooijman : Timestamp fixes. + * Alan Cox : Changed buffer format. + * Alan Cox : destructor hook for AF_UNIX etc. + * Linus Torvalds : Better skb_clone. + * Alan Cox : Added skb_copy. + * Alan Cox : Added all the changed routines Linus + * only put in the headers + * Ray VanTassle : Fixed --skb->lock in free + * Alan Cox : skb_copy copy arp field + * Andi Kleen : slabified it. + * + * NOTE: + * The __skb_ routines should be called with interrupts + * disabled, or you better be *real* sure that the operation is atomic + * with respect to whatever list is being frobbed (e.g. via lock_sock() + * or via disabling bottom half handlers, etc). + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +/* + * The functions in this file will not compile correctly with gcc 2.4.x + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* zc globals: */ +/* +char *net_page_chunk; +struct net_page_info *net_page_table; +struct list_head net_page_list; +spinlock_t net_page_list_lock = SPIN_LOCK_UNLOCKED; +unsigned int net_pages; +*/ + + +int sysctl_hot_list_len = 128; + +static kmem_cache_t *skbuff_head_cache; + +static union { + struct sk_buff_head list; + char pad[SMP_CACHE_BYTES]; +} skb_head_pool[NR_CPUS]; + +/* + * Keep out-of-line to prevent kernel bloat. + * __builtin_return_address is not used because it is not always + * reliable. + */ + +/** + * skb_over_panic - private function + * @skb: buffer + * @sz: size + * @here: address + * + * Out of line support code for skb_put(). Not user callable. + */ + +void skb_over_panic(struct sk_buff *skb, int sz, void *here) +{ + printk("skput:over: %p:%d put:%d dev:%s", + here, skb->len, sz, skb->dev ? skb->dev->name : ""); + BUG(); +} + +/** + * skb_under_panic - private function + * @skb: buffer + * @sz: size + * @here: address + * + * Out of line support code for skb_push(). Not user callable. + */ + + +void skb_under_panic(struct sk_buff *skb, int sz, void *here) +{ + printk("skput:under: %p:%d put:%d dev:%s", + here, skb->len, sz, skb->dev ? skb->dev->name : ""); + BUG(); +} + +static __inline__ struct sk_buff *skb_head_from_pool(void) +{ + struct sk_buff_head *list = &skb_head_pool[smp_processor_id()].list; + + if (skb_queue_len(list)) { + struct sk_buff *skb; + unsigned long flags; + + local_irq_save(flags); + skb = __skb_dequeue(list); + local_irq_restore(flags); + return skb; + } + return NULL; +} + +static __inline__ void skb_head_to_pool(struct sk_buff *skb) +{ + struct sk_buff_head *list = &skb_head_pool[smp_processor_id()].list; + + if (skb_queue_len(list) < sysctl_hot_list_len) { + unsigned long flags; + + local_irq_save(flags); + __skb_queue_head(list, skb); + local_irq_restore(flags); + + return; + } + kmem_cache_free(skbuff_head_cache, skb); +} + + +/* Allocate a new skbuff. We do this ourselves so we can fill in a few + * 'private' fields and also do memory statistics to find all the + * [BEEP] leaks. + * + */ + +/** + * alloc_skb - allocate a network buffer + * @size: size to allocate + * @gfp_mask: allocation mask + * + * Allocate a new &sk_buff. The returned buffer has no headroom and a + * tail room of size bytes. The object has a reference count of one. + * The return is the buffer. On a failure the return is %NULL. + * + * Buffers may only be allocated from interrupts using a @gfp_mask of + * %GFP_ATOMIC. + */ + +struct sk_buff *alloc_skb(unsigned int size,int gfp_mask) +{ + struct sk_buff *skb; + u8 *data; + + if (in_interrupt() && (gfp_mask & __GFP_WAIT)) { + static int count = 0; + if (++count < 5) { + printk(KERN_ERR "alloc_skb called nonatomically " + "from interrupt %p\n", NET_CALLER(size)); + BUG(); + } + gfp_mask &= ~__GFP_WAIT; + } + + /* Get the HEAD */ + skb = skb_head_from_pool(); + if (skb == NULL) { + skb = kmem_cache_alloc(skbuff_head_cache, gfp_mask & ~__GFP_DMA); + if (skb == NULL) + goto nohead; + } + + /* Get the DATA. Size must match skb_add_mtu(). */ + size = SKB_DATA_ALIGN(size); + data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask); + if (data == NULL) + goto nodata; + + /* XXX: does not include slab overhead */ + skb->truesize = size + sizeof(struct sk_buff); + + /* Load the data pointers. */ + skb->head = data; + skb->data = data; + skb->tail = data; + skb->end = data + size; + + /* Set up other state */ + skb->len = 0; + skb->cloned = 0; + skb->data_len = 0; + skb->skb_type = SKB_NORMAL; + + atomic_set(&skb->users, 1); + atomic_set(&(skb_shinfo(skb)->dataref), 1); + skb_shinfo(skb)->nr_frags = 0; + skb_shinfo(skb)->frag_list = NULL; + return skb; + +nodata: + skb_head_to_pool(skb); +nohead: + return NULL; +} + +/* begin zc code additions: */ +/* +void init_net_pages(unsigned long order_pages) +{ + int i; + struct net_page_info *np; + pgd_t *pgd; pmd_t *pmd; pte_t *ptep; + unsigned long nr_pages = 1 << order_pages; + + net_page_chunk = (char *)__get_free_pages(GFP_KERNEL, order_pages); + net_page_table = kmalloc(nr_pages * sizeof(struct net_page_info), GFP_KERNEL); + + INIT_LIST_HEAD(&net_page_list); + + for (i = 0; i < nr_pages; i++) + { + np = net_page_table + i; + np->virt_addr = (unsigned long)net_page_chunk + (i * PAGE_SIZE); + + // now fill the pte pointer: + //np->ppte = 0xdeadbeef; + //pgd = pgd_offset_k(np->virt_addr); + //if (pgd_none(*pgd) || pgd_bad(*pgd)) BUG(); + + //if (pmd_none(*pmd)) BUG(); + //if (pmd_bad(*pmd)) BUG(); + + //ptep = pte_offset(pmd, np->virt_addr); + //np->ppte = phys_to_machine(virt_to_phys(ptep)); + + list_add_tail(&np->list, &net_page_list); + } + net_pages = nr_pages; + + +} + +struct net_page_info *get_net_page(void) +{ + + struct list_head *list_ptr; + struct net_page_info *np; + unsigned long flags; + + if (!net_pages) + { + return NULL; + } + spin_lock_irqsave(&net_page_list_lock, flags); + + list_ptr = net_page_list.next; + np = list_entry(list_ptr, struct net_page_info, list); + list_del(&np->list); + net_pages--; + + spin_unlock_irqrestore(&net_page_list_lock, flags); + + return np; +} + +void free_net_page(struct net_page_info *np) +{ + unsigned long flags; + + if (np == NULL) return; + + spin_lock_irqsave(&net_page_list_lock, flags); + + list_add(&np->list, &net_page_list); + net_pages++; + + spin_unlock_irqrestore(&net_page_list_lock, flags); + +} +*/ +struct sk_buff *alloc_zc_skb(unsigned int size,int gfp_mask) +{ + struct sk_buff *skb; + u8 *data; + + if (in_interrupt() && (gfp_mask & __GFP_WAIT)) { + static int count = 0; + if (++count < 5) { + printk(KERN_ERR "alloc_skb called nonatomically " + "from interrupt %p\n", NET_CALLER(size)); + BUG(); + } + gfp_mask &= ~__GFP_WAIT; + } + + /* Get the HEAD */ + skb = skb_head_from_pool(); + if (skb == NULL) { + skb = kmem_cache_alloc(skbuff_head_cache, gfp_mask & ~__GFP_DMA); + if (skb == NULL) + goto nohead; + } + + /* Get the DATA. Size must match skb_add_mtu(). */ + size = SKB_DATA_ALIGN(size); + if (size > PAGE_SIZE) + { + printk("alloc_zc_skb called with unruly size.\n"); + size = PAGE_SIZE; + } + /*skb->net_page = get_net_page(); + if (skb->net_page == NULL) + { + goto nodata; + } + data = (u8 *)skb->net_page->virt_addr;*/ + data = (char *)__get_free_page(gfp_mask); + if (data == NULL) + goto nodata; + /* XXX: does not include slab overhead */ + skb->truesize = size + sizeof(struct sk_buff); + + /* Load the data pointers. */ + skb->head = data; + skb->data = data; + skb->tail = data; + skb->end = data + size; + + /* Set up other state */ + skb->len = 0; + skb->cloned = 0; + skb->data_len = 0; + skb->skb_type = SKB_ZERO_COPY; + + atomic_set(&skb->users, 1); + atomic_set(&(skb_shinfo(skb)->dataref), 1); + skb_shinfo(skb)->nr_frags = 0; + skb_shinfo(skb)->frag_list = NULL; + return skb; + +nodata: + skb_head_to_pool(skb); +nohead: + return NULL; +} + +/* end zc code additions: */ + +/* + * Slab constructor for a skb head. + */ +static inline void skb_headerinit(void *p, kmem_cache_t *cache, + unsigned long flags) +{ + struct sk_buff *skb = p; + + skb->next = NULL; + skb->prev = NULL; + skb->list = NULL; + skb->sk = NULL; + skb->stamp.tv_sec=0; /* No idea about time */ + skb->dev = NULL; + skb->dst = NULL; + memset(skb->cb, 0, sizeof(skb->cb)); + skb->pkt_type = PACKET_HOST; /* Default type */ + skb->ip_summed = 0; + skb->priority = 0; + skb->security = 0; /* By default packets are insecure */ + skb->destructor = NULL; + +#ifdef CONFIG_NETFILTER + skb->nfmark = skb->nfcache = 0; + skb->nfct = NULL; +#ifdef CONFIG_NETFILTER_DEBUG + skb->nf_debug = 0; +#endif +#endif +#ifdef CONFIG_NET_SCHED + skb->tc_index = 0; +#endif +} + +static void skb_drop_fraglist(struct sk_buff *skb) +{ + struct sk_buff *list = skb_shinfo(skb)->frag_list; + + skb_shinfo(skb)->frag_list = NULL; + + do { + struct sk_buff *this = list; + list = list->next; + kfree_skb(this); + } while (list); +} + +static void skb_clone_fraglist(struct sk_buff *skb) +{ + struct sk_buff *list; + + for (list = skb_shinfo(skb)->frag_list; list; list=list->next) + skb_get(list); +} + +static void skb_release_data(struct sk_buff *skb) +{ + if (!skb->cloned || + atomic_dec_and_test(&(skb_shinfo(skb)->dataref))) { + if (skb_shinfo(skb)->nr_frags) { + int i; + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) +{ + put_page(skb_shinfo(skb)->frags[i].page); +} + } + + if (skb_shinfo(skb)->frag_list) + skb_drop_fraglist(skb); + + if (skb->skb_type == SKB_NORMAL) + { + kfree(skb->head); + } else {// SKB_ZERO_COPY + //free_net_page(skb->net_page); +//printk(KERN_ALERT "<%p>\n", phys_to_machine(virt_to_phys(skb->head))); + free_page((void *)skb->head); + } + } + +} + +/* + * Free an skbuff by memory without cleaning the state. + */ +void kfree_skbmem(struct sk_buff *skb) +{ + skb_release_data(skb); + skb_head_to_pool(skb); +} + +/** + * __kfree_skb - private function + * @skb: buffer + * + * Free an sk_buff. Release anything attached to the buffer. + * Clean the state. This is an internal helper function. Users should + * always call kfree_skb + */ + +void __kfree_skb(struct sk_buff *skb) +{ + if (skb->list) { + printk(KERN_WARNING "Warning: kfree_skb passed an skb still " + "on a list (from %p).\n", NET_CALLER(skb)); + BUG(); + } + + dst_release(skb->dst); + if(skb->destructor) { + if (in_irq()) { + printk(KERN_WARNING "Warning: kfree_skb on hard IRQ %p\n", + NET_CALLER(skb)); + } + skb->destructor(skb); + } +#ifdef CONFIG_NETFILTER + nf_conntrack_put(skb->nfct); +#endif + skb_headerinit(skb, NULL, 0); /* clean state */ + kfree_skbmem(skb); +} + +/** + * skb_clone - duplicate an sk_buff + * @skb: buffer to clone + * @gfp_mask: allocation priority + * + * Duplicate an &sk_buff. The new one is not owned by a socket. Both + * copies share the same packet data but not structure. The new + * buffer has a reference count of 1. If the allocation fails the + * function returns %NULL otherwise the new buffer is returned. + * + * If this function is called from an interrupt gfp_mask() must be + * %GFP_ATOMIC. + */ + +struct sk_buff *skb_clone(struct sk_buff *skb, int gfp_mask) +{ + struct sk_buff *n; + + n = skb_head_from_pool(); + if (!n) { + n = kmem_cache_alloc(skbuff_head_cache, gfp_mask); + if (!n) + return NULL; + } + +#define C(x) n->x = skb->x + + n->next = n->prev = NULL; + n->list = NULL; + n->sk = NULL; + C(stamp); + C(dev); + C(h); + C(nh); + C(mac); + C(dst); + dst_clone(n->dst); + memcpy(n->cb, skb->cb, sizeof(skb->cb)); + C(len); + C(data_len); + C(csum); + n->cloned = 1; + C(pkt_type); + C(ip_summed); + C(priority); + atomic_set(&n->users, 1); + C(protocol); + C(security); + C(truesize); + C(head); + C(data); + C(tail); + C(end); + n->destructor = NULL; +#ifdef CONFIG_NETFILTER + C(nfmark); + C(nfcache); + C(nfct); +#ifdef CONFIG_NETFILTER_DEBUG + C(nf_debug); +#endif +#endif /*CONFIG_NETFILTER*/ +#if defined(CONFIG_HIPPI) + C(private); +#endif +#ifdef CONFIG_NET_SCHED + C(tc_index); +#endif + C(skb_type); + //C(net_page); + atomic_inc(&(skb_shinfo(skb)->dataref)); + skb->cloned = 1; +#ifdef CONFIG_NETFILTER + nf_conntrack_get(skb->nfct); +#endif + return n; +} + +static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old) +{ + /* + * Shift between the two data areas in bytes + */ + unsigned long offset = new->data - old->data; + + new->list=NULL; + new->sk=NULL; + new->dev=old->dev; + new->priority=old->priority; + new->protocol=old->protocol; + new->dst=dst_clone(old->dst); + new->h.raw=old->h.raw+offset; + new->nh.raw=old->nh.raw+offset; + new->mac.raw=old->mac.raw+offset; + memcpy(new->cb, old->cb, sizeof(old->cb)); + atomic_set(&new->users, 1); + new->pkt_type=old->pkt_type; + new->stamp=old->stamp; + new->destructor = NULL; + new->security=old->security; +#ifdef CONFIG_NETFILTER + new->nfmark=old->nfmark; + new->nfcache=old->nfcache; + new->nfct=old->nfct; + nf_conntrack_get(new->nfct); +#ifdef CONFIG_NETFILTER_DEBUG + new->nf_debug=old->nf_debug; +#endif +#endif +#ifdef CONFIG_NET_SCHED + new->tc_index = old->tc_index; +#endif +} + +/** + * skb_copy - create private copy of an sk_buff + * @skb: buffer to copy + * @gfp_mask: allocation priority + * + * Make a copy of both an &sk_buff and its data. This is used when the + * caller wishes to modify the data and needs a private copy of the + * data to alter. Returns %NULL on failure or the pointer to the buffer + * on success. The returned buffer has a reference count of 1. + * + * As by-product this function converts non-linear &sk_buff to linear + * one, so that &sk_buff becomes completely private and caller is allowed + * to modify all the data of returned buffer. This means that this + * function is not recommended for use in circumstances when only + * header is going to be modified. Use pskb_copy() instead. + */ + +struct sk_buff *skb_copy(const struct sk_buff *skb, int gfp_mask) +{ + struct sk_buff *n; + int headerlen = skb->data-skb->head; + + /* + * Allocate the copy buffer + */ + n=alloc_skb(skb->end - skb->head + skb->data_len, gfp_mask); + if(n==NULL) + return NULL; + + /* Set the data pointer */ + skb_reserve(n,headerlen); + /* Set the tail pointer and length */ + skb_put(n,skb->len); + n->csum = skb->csum; + n->ip_summed = skb->ip_summed; + + if (skb_copy_bits(skb, -headerlen, n->head, headerlen+skb->len)) + BUG(); + + copy_skb_header(n, skb); + + return n; +} + +/* Keep head the same: replace data */ +int skb_linearize(struct sk_buff *skb, int gfp_mask) +{ + unsigned int size; + u8 *data; + long offset; + int headerlen = skb->data - skb->head; + int expand = (skb->tail+skb->data_len) - skb->end; + + if (skb_shared(skb)) + BUG(); + + if (expand <= 0) + expand = 0; + + size = (skb->end - skb->head + expand); + size = SKB_DATA_ALIGN(size); + data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask); + if (data == NULL) + return -ENOMEM; + + /* Copy entire thing */ + if (skb_copy_bits(skb, -headerlen, data, headerlen+skb->len)) + BUG(); + + /* Offset between the two in bytes */ + offset = data - skb->head; + + /* Free old data. */ + skb_release_data(skb); + + skb->head = data; + skb->end = data + size; + + /* Set up new pointers */ + skb->h.raw += offset; + skb->nh.raw += offset; + skb->mac.raw += offset; + skb->tail += offset; + skb->data += offset; + + /* Set up shinfo */ + atomic_set(&(skb_shinfo(skb)->dataref), 1); + skb_shinfo(skb)->nr_frags = 0; + skb_shinfo(skb)->frag_list = NULL; + + /* We are no longer a clone, even if we were. */ + skb->cloned = 0; + + skb->tail += skb->data_len; + skb->data_len = 0; + return 0; +} + + +/** + * pskb_copy - create copy of an sk_buff with private head. + * @skb: buffer to copy + * @gfp_mask: allocation priority + * + * Make a copy of both an &sk_buff and part of its data, located + * in header. Fragmented data remain shared. This is used when + * the caller wishes to modify only header of &sk_buff and needs + * private copy of the header to alter. Returns %NULL on failure + * or the pointer to the buffer on success. + * The returned buffer has a reference count of 1. + */ + +struct sk_buff *pskb_copy(struct sk_buff *skb, int gfp_mask) +{ + struct sk_buff *n; + + /* + * Allocate the copy buffer + */ + n=alloc_skb(skb->end - skb->head, gfp_mask); + if(n==NULL) + return NULL; + + /* Set the data pointer */ + skb_reserve(n,skb->data-skb->head); + /* Set the tail pointer and length */ + skb_put(n,skb_headlen(skb)); + /* Copy the bytes */ + memcpy(n->data, skb->data, n->len); + n->csum = skb->csum; + n->ip_summed = skb->ip_summed; + + n->data_len = skb->data_len; + n->len = skb->len; + + if (skb_shinfo(skb)->nr_frags) { + int i; + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + skb_shinfo(n)->frags[i] = skb_shinfo(skb)->frags[i]; + get_page(skb_shinfo(n)->frags[i].page); + } + skb_shinfo(n)->nr_frags = i; + } + + if (skb_shinfo(skb)->frag_list) { + skb_shinfo(n)->frag_list = skb_shinfo(skb)->frag_list; + skb_clone_fraglist(n); + } + + copy_skb_header(n, skb); + + return n; +} + +/** + * pskb_expand_head - reallocate header of &sk_buff + * @skb: buffer to reallocate + * @nhead: room to add at head + * @ntail: room to add at tail + * @gfp_mask: allocation priority + * + * Expands (or creates identical copy, if &nhead and &ntail are zero) + * header of skb. &sk_buff itself is not changed. &sk_buff MUST have + * reference count of 1. Returns zero in the case of success or error, + * if expansion failed. In the last case, &sk_buff is not changed. + * + * All the pointers pointing into skb header may change and must be + * reloaded after call to this function. + */ + +int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, int gfp_mask) +{ + int i; + u8 *data; + int size = nhead + (skb->end - skb->head) + ntail; + long off; + + if (skb_shared(skb)) + BUG(); + + size = SKB_DATA_ALIGN(size); + + data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask); + if (data == NULL) + goto nodata; + + /* Copy only real data... and, alas, header. This should be + * optimized for the cases when header is void. */ + memcpy(data+nhead, skb->head, skb->tail-skb->head); + memcpy(data+size, skb->end, sizeof(struct skb_shared_info)); + + for (i=0; inr_frags; i++) + get_page(skb_shinfo(skb)->frags[i].page); + + if (skb_shinfo(skb)->frag_list) + skb_clone_fraglist(skb); + + skb_release_data(skb); + + off = (data+nhead) - skb->head; + + skb->head = data; + skb->end = data+size; + + skb->data += off; + skb->tail += off; + skb->mac.raw += off; + skb->h.raw += off; + skb->nh.raw += off; + skb->cloned = 0; + atomic_set(&skb_shinfo(skb)->dataref, 1); + return 0; + +nodata: + return -ENOMEM; +} + +/* Make private copy of skb with writable head and some headroom */ + +struct sk_buff * +skb_realloc_headroom(struct sk_buff *skb, unsigned int headroom) +{ + struct sk_buff *skb2; + int delta = headroom - skb_headroom(skb); + + if (delta <= 0) + return pskb_copy(skb, GFP_ATOMIC); + + skb2 = skb_clone(skb, GFP_ATOMIC); + if (skb2 == NULL || + !pskb_expand_head(skb2, SKB_DATA_ALIGN(delta), 0, GFP_ATOMIC)) + return skb2; + + kfree_skb(skb2); + return NULL; +} + + +/** + * skb_copy_expand - copy and expand sk_buff + * @skb: buffer to copy + * @newheadroom: new free bytes at head + * @newtailroom: new free bytes at tail + * @gfp_mask: allocation priority + * + * Make a copy of both an &sk_buff and its data and while doing so + * allocate additional space. + * + * This is used when the caller wishes to modify the data and needs a + * private copy of the data to alter as well as more space for new fields. + * Returns %NULL on failure or the pointer to the buffer + * on success. The returned buffer has a reference count of 1. + * + * You must pass %GFP_ATOMIC as the allocation priority if this function + * is called from an interrupt. + */ + + +struct sk_buff *skb_copy_expand(const struct sk_buff *skb, + int newheadroom, + int newtailroom, + int gfp_mask) +{ + struct sk_buff *n; + + /* + * Allocate the copy buffer + */ + + n=alloc_skb(newheadroom + skb->len + newtailroom, + gfp_mask); + if(n==NULL) + return NULL; + + skb_reserve(n,newheadroom); + + /* Set the tail pointer and length */ + skb_put(n,skb->len); + + /* Copy the data only. */ + if (skb_copy_bits(skb, 0, n->data, skb->len)) + BUG(); + + copy_skb_header(n, skb); + return n; +} + +/* Trims skb to length len. It can change skb pointers, if "realloc" is 1. + * If realloc==0 and trimming is impossible without change of data, + * it is BUG(). + */ + +int ___pskb_trim(struct sk_buff *skb, unsigned int len, int realloc) +{ + int offset = skb_headlen(skb); + int nfrags = skb_shinfo(skb)->nr_frags; + int i; + + for (i=0; ifrags[i].size; + if (end > len) { + if (skb_cloned(skb)) { + if (!realloc) + BUG(); + if (!pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) + return -ENOMEM; + } + if (len <= offset) { + put_page(skb_shinfo(skb)->frags[i].page); + skb_shinfo(skb)->nr_frags--; + } else { + skb_shinfo(skb)->frags[i].size = len-offset; + } + } + offset = end; + } + + if (offset < len) { + skb->data_len -= skb->len - len; + skb->len = len; + } else { + if (len <= skb_headlen(skb)) { + skb->len = len; + skb->data_len = 0; + skb->tail = skb->data + len; + if (skb_shinfo(skb)->frag_list && !skb_cloned(skb)) + skb_drop_fraglist(skb); + } else { + skb->data_len -= skb->len - len; + skb->len = len; + } + } + + return 0; +} + +/** + * __pskb_pull_tail - advance tail of skb header + * @skb: buffer to reallocate + * @delta: number of bytes to advance tail + * + * The function makes a sense only on a fragmented &sk_buff, + * it expands header moving its tail forward and copying necessary + * data from fragmented part. + * + * &sk_buff MUST have reference count of 1. + * + * Returns %NULL (and &sk_buff does not change) if pull failed + * or value of new tail of skb in the case of success. + * + * All the pointers pointing into skb header may change and must be + * reloaded after call to this function. + */ + +/* Moves tail of skb head forward, copying data from fragmented part, + * when it is necessary. + * 1. It may fail due to malloc failure. + * 2. It may change skb pointers. + * + * It is pretty complicated. Luckily, it is called only in exceptional cases. + */ +unsigned char * __pskb_pull_tail(struct sk_buff *skb, int delta) +{ + int i, k, eat; + + /* If skb has not enough free space at tail, get new one + * plus 128 bytes for future expansions. If we have enough + * room at tail, reallocate without expansion only if skb is cloned. + */ + eat = (skb->tail+delta) - skb->end; + + if (eat > 0 || skb_cloned(skb)) { + if (pskb_expand_head(skb, 0, eat>0 ? eat+128 : 0, GFP_ATOMIC)) + return NULL; + } + + if (skb_copy_bits(skb, skb_headlen(skb), skb->tail, delta)) + BUG(); + + /* Optimization: no fragments, no reasons to preestimate + * size of pulled pages. Superb. + */ + if (skb_shinfo(skb)->frag_list == NULL) + goto pull_pages; + + /* Estimate size of pulled pages. */ + eat = delta; + for (i=0; inr_frags; i++) { + if (skb_shinfo(skb)->frags[i].size >= eat) + goto pull_pages; + eat -= skb_shinfo(skb)->frags[i].size; + } + + /* If we need update frag list, we are in troubles. + * Certainly, it possible to add an offset to skb data, + * but taking into account that pulling is expected to + * be very rare operation, it is worth to fight against + * further bloating skb head and crucify ourselves here instead. + * Pure masohism, indeed. 8)8) + */ + if (eat) { + struct sk_buff *list = skb_shinfo(skb)->frag_list; + struct sk_buff *clone = NULL; + struct sk_buff *insp = NULL; + + do { + if (list == NULL) + BUG(); + + if (list->len <= eat) { + /* Eaten as whole. */ + eat -= list->len; + list = list->next; + insp = list; + } else { + /* Eaten partially. */ + + if (skb_shared(list)) { + /* Sucks! We need to fork list. :-( */ + clone = skb_clone(list, GFP_ATOMIC); + if (clone == NULL) + return NULL; + insp = list->next; + list = clone; + } else { + /* This may be pulled without + * problems. */ + insp = list; + } + if (pskb_pull(list, eat) == NULL) { + if (clone) + kfree_skb(clone); + return NULL; + } + break; + } + } while (eat); + + /* Free pulled out fragments. */ + while ((list = skb_shinfo(skb)->frag_list) != insp) { + skb_shinfo(skb)->frag_list = list->next; + kfree_skb(list); + } + /* And insert new clone at head. */ + if (clone) { + clone->next = list; + skb_shinfo(skb)->frag_list = clone; + } + } + /* Success! Now we may commit changes to skb data. */ + +pull_pages: + eat = delta; + k = 0; + for (i=0; inr_frags; i++) { + if (skb_shinfo(skb)->frags[i].size <= eat) { + put_page(skb_shinfo(skb)->frags[i].page); + eat -= skb_shinfo(skb)->frags[i].size; + } else { + skb_shinfo(skb)->frags[k] = skb_shinfo(skb)->frags[i]; + if (eat) { + skb_shinfo(skb)->frags[k].page_offset += eat; + skb_shinfo(skb)->frags[k].size -= eat; + eat = 0; + } + k++; + } + } + skb_shinfo(skb)->nr_frags = k; + + skb->tail += delta; + skb->data_len -= delta; + + return skb->tail; +} + +/* Copy some data bits from skb to kernel buffer. */ + +int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len) +{ + int i, copy; + int start = skb->len - skb->data_len; + + if (offset > (int)skb->len-len) + goto fault; + + /* Copy header. */ + if ((copy = start-offset) > 0) { + if (copy > len) + copy = len; + memcpy(to, skb->data + offset, copy); + if ((len -= copy) == 0) + return 0; + offset += copy; + to += copy; + } + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + int end; + + BUG_TRAP(start <= offset+len); + + end = start + skb_shinfo(skb)->frags[i].size; + if ((copy = end-offset) > 0) { + u8 *vaddr; + + if (copy > len) + copy = len; + + vaddr = kmap_skb_frag(&skb_shinfo(skb)->frags[i]); + memcpy(to, vaddr+skb_shinfo(skb)->frags[i].page_offset+ + offset-start, copy); + kunmap_skb_frag(vaddr); + + if ((len -= copy) == 0) + return 0; + offset += copy; + to += copy; + } + start = end; + } + + if (skb_shinfo(skb)->frag_list) { + struct sk_buff *list; + + for (list = skb_shinfo(skb)->frag_list; list; list=list->next) { + int end; + + BUG_TRAP(start <= offset+len); + + end = start + list->len; + if ((copy = end-offset) > 0) { + if (copy > len) + copy = len; + if (skb_copy_bits(list, offset-start, to, copy)) + goto fault; + if ((len -= copy) == 0) + return 0; + offset += copy; + to += copy; + } + start = end; + } + } + if (len == 0) + return 0; + +fault: + return -EFAULT; +} + +/* Checksum skb data. */ + +unsigned int skb_checksum(const struct sk_buff *skb, int offset, int len, unsigned int csum) +{ + int i, copy; + int start = skb->len - skb->data_len; + int pos = 0; + + /* Checksum header. */ + if ((copy = start-offset) > 0) { + if (copy > len) + copy = len; + csum = csum_partial(skb->data+offset, copy, csum); + if ((len -= copy) == 0) + return csum; + offset += copy; + pos = copy; + } + + for (i=0; inr_frags; i++) { + int end; + + BUG_TRAP(start <= offset+len); + + end = start + skb_shinfo(skb)->frags[i].size; + if ((copy = end-offset) > 0) { + unsigned int csum2; + u8 *vaddr; + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + + if (copy > len) + copy = len; + vaddr = kmap_skb_frag(frag); + csum2 = csum_partial(vaddr + frag->page_offset + + offset-start, copy, 0); + kunmap_skb_frag(vaddr); + csum = csum_block_add(csum, csum2, pos); + if (!(len -= copy)) + return csum; + offset += copy; + pos += copy; + } + start = end; + } + + if (skb_shinfo(skb)->frag_list) { + struct sk_buff *list; + + for (list = skb_shinfo(skb)->frag_list; list; list=list->next) { + int end; + + BUG_TRAP(start <= offset+len); + + end = start + list->len; + if ((copy = end-offset) > 0) { + unsigned int csum2; + if (copy > len) + copy = len; + csum2 = skb_checksum(list, offset-start, copy, 0); + csum = csum_block_add(csum, csum2, pos); + if ((len -= copy) == 0) + return csum; + offset += copy; + pos += copy; + } + start = end; + } + } + if (len == 0) + return csum; + + BUG(); + return csum; +} + +/* Both of above in one bottle. */ + +unsigned int skb_copy_and_csum_bits(const struct sk_buff *skb, int offset, u8 *to, int len, unsigned int csum) +{ + int i, copy; + int start = skb->len - skb->data_len; + int pos = 0; + + /* Copy header. */ + if ((copy = start-offset) > 0) { + if (copy > len) + copy = len; + csum = csum_partial_copy_nocheck(skb->data+offset, to, copy, csum); + if ((len -= copy) == 0) + return csum; + offset += copy; + to += copy; + pos = copy; + } + + for (i=0; inr_frags; i++) { + int end; + + BUG_TRAP(start <= offset+len); + + end = start + skb_shinfo(skb)->frags[i].size; + if ((copy = end-offset) > 0) { + unsigned int csum2; + u8 *vaddr; + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + + if (copy > len) + copy = len; + vaddr = kmap_skb_frag(frag); + csum2 = csum_partial_copy_nocheck(vaddr + frag->page_offset + + offset-start, to, copy, 0); + kunmap_skb_frag(vaddr); + csum = csum_block_add(csum, csum2, pos); + if (!(len -= copy)) + return csum; + offset += copy; + to += copy; + pos += copy; + } + start = end; + } + + if (skb_shinfo(skb)->frag_list) { + struct sk_buff *list; + + for (list = skb_shinfo(skb)->frag_list; list; list=list->next) { + unsigned int csum2; + int end; + + BUG_TRAP(start <= offset+len); + + end = start + list->len; + if ((copy = end-offset) > 0) { + if (copy > len) + copy = len; + csum2 = skb_copy_and_csum_bits(list, offset-start, to, copy, 0); + csum = csum_block_add(csum, csum2, pos); + if ((len -= copy) == 0) + return csum; + offset += copy; + to += copy; + pos += copy; + } + start = end; + } + } + if (len == 0) + return csum; + + BUG(); + return csum; +} + +void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to) +{ + unsigned int csum; + long csstart; + + if (skb->ip_summed == CHECKSUM_HW) + csstart = skb->h.raw - skb->data; + else + csstart = skb->len - skb->data_len; + + if (csstart > skb->len - skb->data_len) + BUG(); + + memcpy(to, skb->data, csstart); + + csum = 0; + if (csstart != skb->len) + csum = skb_copy_and_csum_bits(skb, csstart, to+csstart, + skb->len-csstart, 0); + + if (skb->ip_summed == CHECKSUM_HW) { + long csstuff = csstart + skb->csum; + + *((unsigned short *)(to + csstuff)) = csum_fold(csum); + } +} + +#if 0 +/* + * Tune the memory allocator for a new MTU size. + */ +void skb_add_mtu(int mtu) +{ + /* Must match allocation in alloc_skb */ + mtu = SKB_DATA_ALIGN(mtu) + sizeof(struct skb_shared_info); + + kmem_add_cache_size(mtu); +} +#endif + +void __init skb_init(void) +{ + int i; + + skbuff_head_cache = kmem_cache_create("skbuff_head_cache", + sizeof(struct sk_buff), + 0, + SLAB_HWCACHE_ALIGN, + skb_headerinit, NULL); + if (!skbuff_head_cache) + panic("cannot create skbuff cache"); + + //init_net_pages(NUM_NET_PAGES); + + for (i=0; i