From 89343e3cefd1f435c8db94469fa232bda1711aa8 Mon Sep 17 00:00:00 2001 From: "akw27@labyrinth.cl.cam.ac.uk" Date: Tue, 21 Jan 2003 14:38:45 +0000 Subject: [PATCH 01/10] bitkeeper revision 1.15.1.8 (3e2d5b75k3SSnLdVzMd7kREO_EvMLw) Added macros and counters to page table flushes. macros and the counter are in a new header file -- flushtlb.h --- .rootkeys | 1 + xen-2.4.16/common/domain.c | 7 ++-- xen-2.4.16/common/memory.c | 5 ++- xen-2.4.16/include/asm-i386/flushtlb.h | 48 ++++++++++++++++++++++++++ xen-2.4.16/include/asm-i386/page.h | 42 +++++++++++----------- 5 files changed, 75 insertions(+), 28 deletions(-) create mode 100644 xen-2.4.16/include/asm-i386/flushtlb.h diff --git a/.rootkeys b/.rootkeys index 64c5e5a0a9..84ae52496a 100644 --- a/.rootkeys +++ b/.rootkeys @@ -134,6 +134,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 diff --git a/xen-2.4.16/common/domain.c b/xen-2.4.16/common/domain.c index 31ae0b9726..ea14a5ea21 100644 --- a/xen-2.4.16/common/domain.c +++ b/xen-2.4.16/common/domain.c @@ -11,6 +11,7 @@ #include #include #include +#include rwlock_t tasklist_lock __cacheline_aligned = RW_LOCK_UNLOCKED; @@ -581,8 +582,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); @@ -657,8 +657,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/memory.c b/xen-2.4.16/common/memory.c index 226772ebce..836e27abac 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 @@ -766,9 +767,7 @@ int do_process_page_updates(page_update_request_t *updates, 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/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 b0d68a324a..da50b59fd7 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) \ From 091f818b46daa9b41e359d2f79de286bb0a276a7 Mon Sep 17 00:00:00 2001 From: "akw27@plucky.localdomain" Date: Thu, 23 Jan 2003 11:38:00 +0000 Subject: [PATCH 02/10] bitkeeper revision 1.15.1.9 (3e2fd418NdZYqL3KPr6URqd77DwnJQ) Add shadow ring in the RX direction to protect buffers from guest tampering once they have been passed to the hypervisor. This is under the umbrella of eventual zero-copy network code. --- BitKeeper/etc/logging_ok | 1 + xen-2.4.16/common/network.c | 30 ++++++++++++- xen-2.4.16/include/hypervisor-ifs/network.h | 17 +++++-- xen-2.4.16/include/xeno/vif.h | 37 ++++++++++++++- xen-2.4.16/net/dev.c | 50 +++++++++++++++++---- 5 files changed, 120 insertions(+), 15 deletions(-) diff --git a/BitKeeper/etc/logging_ok b/BitKeeper/etc/logging_ok index 9164d8ac24..0088bed9fb 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 iap10@labyrinth.cl.cam.ac.uk kaf24@labyrinth.cl.cam.ac.uk kaf24@plym.cl.cam.ac.uk diff --git a/xen-2.4.16/common/network.c b/xen-2.4.16/common/network.c index 64c32a704f..df4cf04262 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 = 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]); } diff --git a/xen-2.4.16/include/hypervisor-ifs/network.h b/xen-2.4.16/include/hypervisor-ifs/network.h index a0cfc9fa2c..42bff71827 100644 --- a/xen-2.4.16/include/hypervisor-ifs/network.h +++ b/xen-2.4.16/include/hypervisor-ifs/network.h @@ -15,13 +15,15 @@ #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 @@ -111,4 +113,11 @@ 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. + #endif diff --git a/xen-2.4.16/include/xeno/vif.h b/xen-2.4.16/include/xeno/vif.h index 1bef565c32..a83ae515a3 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; // trying to add shadow pointers only as I need to. +} 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. @@ -40,3 +68,8 @@ 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); void add_default_net_rule(int vif_id, u32 ipaddr); + +/* status fields per-descriptor: + */ + + diff --git a/xen-2.4.16/net/dev.c b/xen-2.4.16/net/dev.c index b99a2e91b4..9d02fb3f28 100644 --- a/xen-2.4.16/net/dev.c +++ b/xen-2.4.16/net/dev.c @@ -879,9 +879,10 @@ 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; + /* 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 @@ -899,6 +900,7 @@ void flush_rx_queue(void) 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 ) { @@ -923,12 +925,13 @@ void flush_rx_queue(void) i = net_ring->rx_cons; if ( i != net_ring->rx_prod ) { - if ( !copy_from_user(&rx, net_ring->rx_ring+i, sizeof(rx)) ) + 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; + copy_to_user((void *)rx->addr, skb->mac.raw, rx->size); + copy_to_user(net_ring->rx_ring+i, rx, sizeof(rx)); } net_ring->rx_cons = (i+1) & (RX_RING_SIZE-1); if ( net_ring->rx_cons == net_ring->rx_event ) @@ -1923,7 +1926,8 @@ int __init net_dev_init(void) 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; @@ -1933,6 +1937,10 @@ long do_net_update(void) { current_vif = current->net_vif_list[j]; net_ring = current_vif->net_ring; + + /* 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)) ) @@ -1997,6 +2005,32 @@ long do_net_update(void) } } net_ring->tx_cons = i; + + /* Next, pull any new RX descriptors across to the shadow ring. + * Note that in the next revision, these will reference PTEs and the + * code here will have to validate reference and flush counts, copy the + * descriptor, change the ownership to dom0 and invalidate the client's + * version of the page. + */ + + shadow_ring = current_vif->shadow_ring; + + for (i = shadow_ring->rx_prod; i != net_ring->rx_prod; i = TX_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; + } else { + shadow_ring->rx_ring[i].status = RING_STATUS_OK; + } + } + + shadow_ring->rx_prod = net_ring->rx_prod; } return 0; From 8fd2e874a3f6a71977cd47a89e1edc88968a18c3 Mon Sep 17 00:00:00 2001 From: "akw27@boulderdash.cl.cam.ac.uk" Date: Wed, 29 Jan 2003 08:59:21 +0000 Subject: [PATCH 03/10] bitkeeper revision 1.15.3.1 (3e3797e9gMcow8ShTTrYuWSeSSe-BA) protect free_list with a spin lock. --- xen-2.4.16/common/domain.c | 5 +++++ xen-2.4.16/common/memory.c | 4 ++++ xen-2.4.16/include/xeno/mm.h | 2 ++ 3 files changed, 11 insertions(+) diff --git a/xen-2.4.16/common/domain.c b/xen-2.4.16/common/domain.c index 1128cdfa15..fc71bad3ef 100644 --- a/xen-2.4.16/common/domain.c +++ b/xen-2.4.16/common/domain.c @@ -334,10 +334,13 @@ static unsigned int alloc_new_dom_mem(struct task_struct *p, unsigned int kbytes struct pfn_info *pf, *pf_head; 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; @@ -369,6 +372,8 @@ static unsigned int alloc_new_dom_mem(struct task_struct *p, unsigned int kbytes free_pfns--; } + + spin_unlock_irqrestore(&free_list_lock, flags); p->tot_pages = req_pages; diff --git a/xen-2.4.16/common/memory.c b/xen-2.4.16/common/memory.c index 2e6697a0b8..8cbed41416 100644 --- a/xen-2.4.16/common/memory.c +++ b/xen-2.4.16/common/memory.c @@ -206,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]; @@ -219,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)); @@ -232,6 +234,7 @@ void __init init_frametable(unsigned long nr_pages) memset(frame_table, 0, frame_table_size); /* Put all domain-allocatable memory on a free list. */ + spin_lock_irqsave(&free_list_lock, flags); INIT_LIST_HEAD(&free_list); for( page_index = (MAX_MONITOR_ADDRESS + frame_table_size) >> PAGE_SHIFT; page_index < nr_pages; @@ -240,6 +243,7 @@ void __init init_frametable(unsigned long nr_pages) pf = list_entry(&frame_table[page_index].list, struct pfn_info, list); list_add_tail(&pf->list, &free_list); } + spin_unlock_irqrestore(&free_list_lock, flags); } diff --git a/xen-2.4.16/include/xeno/mm.h b/xen-2.4.16/include/xeno/mm.h index 30071e76cf..3d014f650b 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 :((( */ @@ -110,6 +111,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); From 2d49de48f01d151bb58e01e1a120090965a90e50 Mon Sep 17 00:00:00 2001 From: "akw27@boulderdash.cl.cam.ac.uk" Date: Wed, 29 Jan 2003 11:17:07 +0000 Subject: [PATCH 04/10] bitkeeper revision 1.15.3.2 (3e37b8332YRktwAjVLsh2PyFFW2XNw) RX data is now moved in a domain-memory page, but still copied at the end. --- xen-2.4.16/drivers/net/tulip/interrupt.c | 5 +- xen-2.4.16/include/asm-i386/pci.h | 14 ++- xen-2.4.16/include/xeno/skbuff.h | 21 +++-- xen-2.4.16/net/dev.c | 44 ++++++++++ xen-2.4.16/net/eth.c | 70 ++++++++------- xen-2.4.16/net/skbuff.c | 105 ++++++++++++++++++++++- 6 files changed, 220 insertions(+), 39 deletions(-) diff --git a/xen-2.4.16/drivers/net/tulip/interrupt.c b/xen-2.4.16/drivers/net/tulip/interrupt.c index c92b12ea92..8e88f2f457 100644 --- a/xen-2.4.16/drivers/net/tulip/interrupt.c +++ b/xen-2.4.16/drivers/net/tulip/interrupt.c @@ -170,8 +170,9 @@ static int tulip_rx(struct net_device *dev) #endif /* Check if the packet is long enough to accept without copying to a minimally-sized skbuff. */ - if (pkt_len < tulip_rx_copybreak - && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { + //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/include/asm-i386/pci.h b/xen-2.4.16/include/asm-i386/pci.h index 9ab9c282fe..43fab42762 100644 --- a/xen-2.4.16/include/asm-i386/pci.h +++ b/xen-2.4.16/include/asm-i386/pci.h @@ -75,7 +75,19 @@ 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); + + if ((unsigned long) ptr > PAGE_OFFSET) + return virt_to_bus(ptr); + + /* If an address that is not in hypervisor VM is passed to this + * function (ie > PAGE_OFFSET) we assume that the passer knows + * what they are doing, and have passed a physical address that + * should not be converted here. This is a little hackish, but + * is being added to allow references to domain memory in order + * to support zero-copy network code. + */ + + return (dma_addr_t) ptr; } /* Unmap a single streaming mode DMA translation. The dma_addr and size 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/net/dev.c b/xen-2.4.16/net/dev.c index 9d02fb3f28..749d50aa3f 100644 --- a/xen-2.4.16/net/dev.c +++ b/xen-2.4.16/net/dev.c @@ -30,6 +30,7 @@ #include #include +#include #define BUG_TRAP ASSERT #define notifier_call_chain(_a,_b,_c) ((void)0) @@ -695,6 +696,21 @@ int netif_rx(struct sk_buff *skb) if (skb->stamp.tv_sec == 0) get_fast_time(&skb->stamp); + /* Attempt to handle zero-copy packets here: */ + if (skb->skb_type == SKB_ZERO_COPY) + { + 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,16); // need to ensure that all the drivers and not just tulip do this. + skb->mac.raw = skb->data; + skb->data += ETH_HLEN; + } + /* The code is rearranged so that the path is the most short when CPU is congested, but is still operating. */ @@ -747,10 +763,18 @@ drop: netdev_rx_stat[this_cpu].dropped++; local_irq_restore(flags); + if (skb->skb_type == SKB_ZERO_COPY) + unmap_domain_mem(skb->head); + kfree_skb(skb); return NET_RX_DROP; found: + if (skb->skb_type == SKB_ZERO_COPY) { + unmap_domain_mem(skb->head); + //skb->head = (u8 *)((skb->pf - frame_table) << PAGE_SHIFT); + skb->head = skb->data = skb->tail = (void *)0xdeadbeef; + } hyp_event_notify(cpu_mask); local_irq_restore(flags); return 0; @@ -930,8 +954,28 @@ void flush_rx_queue(void) 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; + } } net_ring->rx_cons = (i+1) & (RX_RING_SIZE-1); if ( net_ring->rx_cons == net_ring->rx_event ) 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..abd2c73ed1 100644 --- a/xen-2.4.16/net/skbuff.c +++ b/xen-2.4.16/net/skbuff.c @@ -149,6 +149,102 @@ static __inline__ void skb_head_to_pool(struct sk_buff *skb) kmem_cache_free(skbuff_head_cache, skb); } +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; + 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_tail(&pf->list, &free_list); + free_pfns++; + + spin_unlock_irqrestore(&free_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); + data = alloc_skb_data_page(skb); + 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->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 +309,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); @@ -295,7 +392,13 @@ 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 { + printk("skb_release_data called with unknown skb type!\n"); + } } } From 51e139273eb48213ee4e146739c2237a8bc1ba45 Mon Sep 17 00:00:00 2001 From: "akw27@boulderdash.cl.cam.ac.uk" Date: Thu, 30 Jan 2003 12:15:49 +0000 Subject: [PATCH 05/10] bitkeeper revision 1.15.3.3 (3e3917752EttVE6JZH0loCHxmqGWrQ) GuestOS modifications to allocate SKB data from pre-allocated pages. Zero copy RX-path stuff. --- .rootkeys | 2 + xen-2.4.16/include/hypervisor-ifs/network.h | 4 +- .../include/linux/skbuff.h | 1185 ++++++++++++++ xenolinux-2.4.16-sparse/net/core/skbuff.c | 1366 +++++++++++++++++ 4 files changed, 2555 insertions(+), 2 deletions(-) create mode 100644 xenolinux-2.4.16-sparse/include/linux/skbuff.h create mode 100644 xenolinux-2.4.16-sparse/net/core/skbuff.c diff --git a/.rootkeys b/.rootkeys index 206a24d82b..39a3380aad 100644 --- a/.rootkeys +++ b/.rootkeys @@ -399,9 +399,11 @@ 3ddb79ba2qYtIQAT_-vCFkkZUXu_UQ xenolinux-2.4.16-sparse/include/asm-xeno/user.h 3ddb79bbqhb9X9qWOz5Bv4wOzrkITg xenolinux-2.4.16-sparse/include/asm-xeno/vga.h 3ddb79bbA52x94o6uwDYsbzrH2hjzA xenolinux-2.4.16-sparse/include/asm-xeno/xor.h +3e37c39fVCSGQENtY6g7muaq_THliw xenolinux-2.4.16-sparse/include/linux/skbuff.h 3ddb79bb_7YG4U75ZmEic9YXWTW7Vw xenolinux-2.4.16-sparse/include/linux/sunrpc/debug.h 3ddb79bcxkVPfWlZ1PQKvDrfArzOVw xenolinux-2.4.16-sparse/kernel/panic.c 3ddb79bbP31im-mx2NbfthSeqty1Dg xenolinux-2.4.16-sparse/mk 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/xen-2.4.16/include/hypervisor-ifs/network.h b/xen-2.4.16/include/hypervisor-ifs/network.h index 42bff71827..182fd655fc 100644 --- a/xen-2.4.16/include/hypervisor-ifs/network.h +++ b/xen-2.4.16/include/hypervisor-ifs/network.h @@ -26,8 +26,8 @@ typedef struct rx_entry_st { 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. 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..2d0a94dce2 --- /dev/null +++ b/xenolinux-2.4.16-sparse/include/linux/skbuff.h @@ -0,0 +1,1185 @@ +/* + * 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..e6891de566 --- /dev/null +++ b/xenolinux-2.4.16-sparse/net/core/skbuff.c @@ -0,0 +1,1366 @@ +/* + * 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 + +/* 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)) + { + pmd = pmd_offset(pgd, np->virt_addr); + if (!pmd_none(*pmd)) + { + ptep = pte_offset(pmd, np->virt_addr); + np->ppte = (unsigned long)ptep; // neet to virt_to_phys this? + } + } + + 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_tail(&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; + 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); + } + } +} + +/* + * 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 Date: Wed, 5 Feb 2003 13:40:29 +0000 Subject: [PATCH 06/10] bitkeeper revision 1.15.1.13 (3e41144dWc5GH88F3idrXT41kpovhQ) Zero copy RX path is working with guest-allocated page pool. --- xen-2.4.16/common/domain.c | 4 +- xen-2.4.16/common/event.c | 4 +- xen-2.4.16/common/network.c | 2 +- xen-2.4.16/include/hypervisor-ifs/network.h | 1 + xen-2.4.16/include/xeno/vif.h | 2 +- xen-2.4.16/net/dev.c | 247 ++++++++++++++++-- xen-2.4.16/net/skbuff.c | 13 +- .../arch/xeno/drivers/network/network.c | 12 +- xenolinux-2.4.16-sparse/include/asm-xeno/io.h | 18 +- xenolinux-2.4.16-sparse/net/core/skbuff.c | 30 ++- 10 files changed, 276 insertions(+), 57 deletions(-) diff --git a/xen-2.4.16/common/domain.c b/xen-2.4.16/common/domain.c index fc71bad3ef..520a9a8511 100644 --- a/xen-2.4.16/common/domain.c +++ b/xen-2.4.16/common/domain.c @@ -402,8 +402,8 @@ extern module_t *mod; extern unsigned char *cmdline; int setup_guestos(struct task_struct *p, dom0_newdomain_t *params) { -#define L2_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_USER|_PAGE_ACCESSED) -#define L1_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_USER|_PAGE_ACCESSED|_PAGE_DIRTY) +#define L1_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_USER|_PAGE_ACCESSED) +#define L2_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_USER|_PAGE_ACCESSED|_PAGE_DIRTY) #define ALLOC_FRAME_FROM_DOMAIN() (alloc_address -= PAGE_SIZE) char *src, *dst; int i, dom = p->domain; diff --git a/xen-2.4.16/common/event.c b/xen-2.4.16/common/event.c index 6a81c63f8b..3e7c416c29 100644 --- a/xen-2.4.16/common/event.c +++ b/xen-2.4.16/common/event.c @@ -14,13 +14,13 @@ typedef void (*hyp_event_callback_fn_t)(void); extern void schedule(void); -extern void flush_rx_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 }; diff --git a/xen-2.4.16/common/network.c b/xen-2.4.16/common/network.c index df4cf04262..91d1f66315 100644 --- a/xen-2.4.16/common/network.c +++ b/xen-2.4.16/common/network.c @@ -78,7 +78,7 @@ net_vif_t *create_net_vif(int domain) if ((shadow_ring->tx_ring == NULL) || (shadow_ring->rx_ring == NULL)) goto fail; - shadow_ring->rx_prod = 0; + shadow_ring->rx_prod = shadow_ring->rx_cons = shadow_ring->rx_idx = 0; // fill in the new vif struct. diff --git a/xen-2.4.16/include/hypervisor-ifs/network.h b/xen-2.4.16/include/hypervisor-ifs/network.h index 182fd655fc..f3f13dc77b 100644 --- a/xen-2.4.16/include/hypervisor-ifs/network.h +++ b/xen-2.4.16/include/hypervisor-ifs/network.h @@ -119,5 +119,6 @@ int add_net_rule(net_rule_t *rule); #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/vif.h b/xen-2.4.16/include/xeno/vif.h index a83ae515a3..761188be74 100644 --- a/xen-2.4.16/include/xeno/vif.h +++ b/xen-2.4.16/include/xeno/vif.h @@ -42,7 +42,7 @@ typedef struct rx_shadow_entry_st { typedef struct net_shadow_ring_st { tx_shadow_entry_t *tx_ring; rx_shadow_entry_t *rx_ring; - unsigned int rx_prod; // trying to add shadow pointers only as I need to. + unsigned int rx_prod, rx_cons, rx_idx; } net_shadow_ring_t; typedef struct net_vif_st { diff --git a/xen-2.4.16/net/dev.c b/xen-2.4.16/net/dev.c index 749d50aa3f..fa7f6e197d 100644 --- a/xen-2.4.16/net/dev.c +++ b/xen-2.4.16/net/dev.c @@ -31,6 +31,7 @@ #include #include +#include #define BUG_TRAP ASSERT #define notifier_call_chain(_a,_b,_c) ((void)0) @@ -39,6 +40,12 @@ #define rtnl_unlock() ((void)0) #define dst_init() ((void)0) +// 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; /* @@ -48,11 +55,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 @@ -662,7 +669,83 @@ 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; + if (skb->skb_type != SKB_ZERO_COPY) + return; + + /* + * 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; + + if (rx->flush_count == tlb_flush_count[smp_processor_id()]) + flush_tlb_all(); + + g_pte = map_domain_mem(rx->addr); + + g_pfn = frame_table + (*g_pte >> PAGE_SHIFT); + h_pfn = skb->pf; + + //flip and/or set relevant pf_info fields. + 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; + //point guest pte at the new page: + *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 @@ -687,12 +770,15 @@ 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; + local_irq_save(flags); + if (skb->stamp.tv_sec == 0) get_fast_time(&skb->stamp); @@ -709,14 +795,13 @@ int netif_rx(struct sk_buff *skb) skb_reserve(skb,16); // need to ensure that all the drivers and not just tulip do this. 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++; @@ -749,7 +834,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; @@ -761,28 +846,24 @@ int netif_rx(struct sk_buff *skb) drop: netdev_rx_stat[this_cpu].dropped++; - local_irq_restore(flags); - if (skb->skb_type == SKB_ZERO_COPY) unmap_domain_mem(skb->head); - kfree_skb(skb); + local_irq_restore(flags); return NET_RX_DROP; found: if (skb->skb_type == SKB_ZERO_COPY) { unmap_domain_mem(skb->head); - //skb->head = (u8 *)((skb->pf - frame_table) << PAGE_SHIFT); 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; @@ -897,7 +978,35 @@ 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 ( net_ring->rx_cons == net_ring->rx_event ) + set_bit(_EVENT_NET_RX_FOR_VIF(nvif), &s->events); + + } + } +} + void flush_rx_queue(void) { struct sk_buff *skb; @@ -906,6 +1015,8 @@ void flush_rx_queue(void) net_shadow_ring_t *shadow_ring; unsigned int i, nvif; 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 @@ -918,7 +1029,6 @@ 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++) @@ -928,6 +1038,8 @@ void flush_rx_queue(void) 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 @@ -938,6 +1050,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 ) @@ -946,9 +1068,15 @@ 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 ) { + net_ring->rx_ring[i].status = shadow_ring->rx_ring[i].status; if ( shadow_ring->rx_ring[i].status == RING_STATUS_OK) { rx = shadow_ring->rx_ring+i; @@ -959,7 +1087,7 @@ void flush_rx_queue(void) * replaced with a page swizzle. */ - if (skb->skb_type == SKB_ZERO_COPY) + /*if (skb->skb_type == SKB_ZERO_COPY) { skb->head = (u8 *)map_domain_mem(((skb->pf - frame_table) << PAGE_SHIFT)); skb->data = skb->head; @@ -975,7 +1103,49 @@ void flush_rx_queue(void) { 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 ) @@ -1963,10 +2133,7 @@ int __init net_dev_init(void) * 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)) + long do_net_update(void) { shared_info_t *shared = current->shared_info; @@ -1976,7 +2143,12 @@ long do_net_update(void) 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]; @@ -2034,6 +2206,7 @@ long do_net_update(void) net_get_target_vif(skb); if ( skb->dst_vif > VIF_PHYSICAL_INTERFACE ) { +printk("LOCAL DELIVERY!\n"); (void)netif_rx(skb); } else if ( skb->dst_vif == VIF_PHYSICAL_INTERFACE ) @@ -2051,32 +2224,48 @@ long do_net_update(void) net_ring->tx_cons = i; /* Next, pull any new RX descriptors across to the shadow ring. - * Note that in the next revision, these will reference PTEs and the - * code here will have to validate reference and flush counts, copy the - * descriptor, change the ownership to dom0 and invalidate the client's - * version of the page. */ shadow_ring = current_vif->shadow_ring; - for (i = shadow_ring->rx_prod; i != net_ring->rx_prod; i = TX_RING_INC(i)) + 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. + /* 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_OK; + + if (!(page->flags & PGT_l1_page_table) + || !((page->flags & PG_domain_mask) == current->domain)) + shadow_ring->rx_ring[i].status = RING_STATUS_BAD_PAGE; + + + g_pte = map_domain_mem(rx->addr); + + if (!(*g_pte & _PAGE_PRESENT)) + shadow_ring->rx_ring[i].status = RING_STATUS_BAD_PAGE; + page = (*g_pte >> PAGE_SHIFT) + frame_table; + if (page->tot_count != 1) + shadow_ring->rx_ring[i].status = RING_STATUS_BAD_PAGE; + + *g_pte &= ~_PAGE_PRESENT; + 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/skbuff.c b/xen-2.4.16/net/skbuff.c index abd2c73ed1..467e161090 100644 --- a/xen-2.4.16/net/skbuff.c +++ b/xen-2.4.16/net/skbuff.c @@ -180,8 +180,8 @@ static inline void dealloc_skb_data_page(struct sk_buff *skb) pf = skb->pf; spin_lock_irqsave(&free_list_lock, flags); - - list_add_tail(&pf->list, &free_list); + + list_add(&pf->list, &free_list); free_pfns++; spin_unlock_irqrestore(&free_list_lock, flags); @@ -213,6 +213,7 @@ struct sk_buff *alloc_zc_skb(unsigned int size,int gfp_mask) /* 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; @@ -237,6 +238,7 @@ struct sk_buff *alloc_zc_skb(unsigned int size,int gfp_mask) atomic_set(&(skb_shinfo(skb)->dataref), 1); skb_shinfo(skb)->nr_frags = 0; skb_shinfo(skb)->frag_list = NULL; + return skb; nodata: @@ -381,6 +383,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) { @@ -394,10 +397,9 @@ static void skb_release_data(struct sk_buff *skb) if (skb->skb_type == SKB_NORMAL) { kfree(skb->head); - } else if (skb->skb_type == SKB_ZERO_COPY) { - dealloc_skb_data_page(skb); + } else if (skb->skb_type == SKB_ZERO_COPY) { dealloc_skb_data_page(skb); } else { - printk("skb_release_data called with unknown skb type!\n"); + BUG(); //skb_release_data called with unknown skb type! } } } @@ -436,6 +438,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..9c40ad0066 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 @@ -192,9 +192,9 @@ static void network_alloc_rx_buffers(struct net_device *dev) skb = dev_alloc_skb(RX_BUF_SIZE); if ( skb == NULL ) break; skb->dev = dev; - skb_reserve(skb, 2); /* word align the IP header */ + //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 = (unsigned long)skb->net_page->ppte; //data; np->net_ring->rx_ring[i].size = RX_BUF_SIZE - 16; /* arbitrary */ } @@ -276,10 +276,18 @@ static void network_rx_int(int irq, void *dev_id, struct pt_regs *ptregs) 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]; + 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; 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..0f097342ba 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,22 @@ static inline void * phys_to_virt(unsigned long address) return __va(address); } +/* + * Change virtual addresses to machine addresses and vv. + * These are equally trivial. + */ + +static inline unsigned long virt_to_mach(volatile void * address) +{ + return __pa(address) + (unsigned long) start_info.phys_base; +} + +static inline void *mach_to_virt(unsigned long address) +{ + return __va(address) - (unsigned long) start_info.phys_base; +} + + /* * Change "struct page" to physical address. */ diff --git a/xenolinux-2.4.16-sparse/net/core/skbuff.c b/xenolinux-2.4.16-sparse/net/core/skbuff.c index e6891de566..ec76f00de0 100644 --- a/xenolinux-2.4.16-sparse/net/core/skbuff.c +++ b/xenolinux-2.4.16-sparse/net/core/skbuff.c @@ -59,7 +59,7 @@ #include #include #include - +#include #include #include @@ -246,19 +246,17 @@ void init_net_pages(unsigned long order_pages) { 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)) - { - pmd = pmd_offset(pgd, np->virt_addr); - if (!pmd_none(*pmd)) - { - ptep = pte_offset(pmd, np->virt_addr); - np->ppte = (unsigned long)ptep; // neet to virt_to_phys this? - } - } + 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 = (unsigned long)virt_to_mach(ptep); list_add_tail(&np->list, &net_page_list); } @@ -297,10 +295,11 @@ void free_net_page(struct net_page_info *np) spin_lock_irqsave(&net_page_list_lock, flags); - list_add_tail(&np->list, &net_page_list); + 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) @@ -427,12 +426,14 @@ static void skb_clone_fraglist(struct sk_buff *skb) static void skb_release_data(struct sk_buff *skb) { - if (!skb->cloned || + 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++) + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) +{ put_page(skb_shinfo(skb)->frags[i].page); +} } if (skb_shinfo(skb)->frag_list) @@ -445,6 +446,7 @@ static void skb_release_data(struct sk_buff *skb) free_net_page(skb->net_page); } } + } /* From cb4878ee12aa24be7a1bb8c1132cc8b6e55c0c44 Mon Sep 17 00:00:00 2001 From: "akw27@boulderdash.cl.cam.ac.uk" Date: Thu, 6 Feb 2003 16:31:29 +0000 Subject: [PATCH 07/10] bitkeeper revision 1.22.1.9 (3e428de1nQwf1QUfwsp1nTiJP4ByhQ) Zero-copy receive path now works over discontiguous memory with no guest-side pool. --- .rootkeys | 2 - xen-2.4.16/arch/i386/ioremap.c | 4 +- xen-2.4.16/common/domain.c | 4 +- xen-2.4.16/drivers/net/3c509.c | 1068 -------- xen-2.4.16/drivers/net/3c59x.c | 10 +- xen-2.4.16/drivers/net/8139cp.c | 8 +- xen-2.4.16/drivers/net/8139too.c | 2324 ----------------- xen-2.4.16/drivers/net/eepro100.c | 7 +- xen-2.4.16/drivers/net/pcnet32.c | 2 +- xen-2.4.16/drivers/net/tulip/interrupt.c | 6 +- xen-2.4.16/drivers/net/tulip/tulip_core.c | 5 +- xen-2.4.16/include/xeno/mm.h | 4 +- xen-2.4.16/net/dev.c | 60 +- xen-2.4.16/net/skbuff.c | 2 +- .../arch/xeno/drivers/network/network.c | 65 +- xenolinux-2.4.16-sparse/include/asm-xeno/io.h | 11 - .../include/linux/skbuff.h | 5 +- xenolinux-2.4.16-sparse/net/core/skbuff.c | 37 +- 18 files changed, 161 insertions(+), 3463 deletions(-) delete mode 100644 xen-2.4.16/drivers/net/3c509.c delete mode 100644 xen-2.4.16/drivers/net/8139too.c diff --git a/.rootkeys b/.rootkeys index eef1617a47..702b1b035c 100644 --- a/.rootkeys +++ b/.rootkeys @@ -67,10 +67,8 @@ 3ddb79bdDWFwINnKn29RlFDwGJhjYg xen-2.4.16/drivers/ide/ide-taskfile.c 3ddb79bdkDY1bSOYkToP1Cc49VdBxg xen-2.4.16/drivers/ide/ide.c 3ddb79bdPyAvT_WZTAFhaX0jp-yXSw xen-2.4.16/drivers/ide/ide_modes.h -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 3ddb79c0GejJrp1U6W4G6dYi-RiH4A xen-2.4.16/drivers/net/eepro100.c diff --git a/xen-2.4.16/arch/i386/ioremap.c b/xen-2.4.16/arch/i386/ioremap.c index 16d677b79d..4ed7ba438d 100644 --- a/xen-2.4.16/arch/i386/ioremap.c +++ b/xen-2.4.16/arch/i386/ioremap.c @@ -15,8 +15,8 @@ static unsigned long remap_base = 0; -#define L2_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED) -#define L1_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED|_PAGE_DIRTY) +#define L1_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED) +#define L2_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED|_PAGE_DIRTY) #define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) diff --git a/xen-2.4.16/common/domain.c b/xen-2.4.16/common/domain.c index f0017d1b0e..8e7f2587bb 100644 --- a/xen-2.4.16/common/domain.c +++ b/xen-2.4.16/common/domain.c @@ -15,8 +15,8 @@ #include #include -#define L2_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_USER|_PAGE_ACCESSED) -#define L1_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_USER|_PAGE_ACCESSED|_PAGE_DIRTY) +#define L1_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_USER|_PAGE_ACCESSED) +#define L2_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_USER|_PAGE_ACCESSED|_PAGE_DIRTY) extern int nr_mods; extern module_t *mod; 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/tulip/interrupt.c b/xen-2.4.16/drivers/net/tulip/interrupt.c index 8e88f2f457..2cf54c6120 100644 --- a/xen-2.4.16/drivers/net/tulip/interrupt.c +++ b/xen-2.4.16/drivers/net/tulip/interrupt.c @@ -170,9 +170,9 @@ static int tulip_rx(struct net_device *dev) #endif /* Check if the packet is long enough to accept without copying to a minimally-sized skbuff. */ - //if (pkt_len < tulip_rx_copybreak - // && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { - if (0) { + 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/xeno/mm.h b/xen-2.4.16/include/xeno/mm.h index 6aaf6971ac..0ebb58c6f7 100644 --- a/xen-2.4.16/include/xeno/mm.h +++ b/xen-2.4.16/include/xeno/mm.h @@ -89,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? */ @@ -98,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) @@ -120,6 +121,7 @@ void init_frametable(unsigned long nr_pages); * will use it to store a "physical" frame number to give the appearance of * contiguous (or near contiguous) physical memory. */ +#undef machine_to_phys_mapping #define machine_to_phys_mapping ((unsigned long *)RDWR_MPT_VIRT_START) /* Part of the domain API. */ diff --git a/xen-2.4.16/net/dev.c b/xen-2.4.16/net/dev.c index fa7f6e197d..0544645686 100644 --- a/xen-2.4.16/net/dev.c +++ b/xen-2.4.16/net/dev.c @@ -714,8 +714,7 @@ void deliver_packet(struct sk_buff *skb, net_vif_t *vif) if ( (skb->len + ETH_HLEN) < rx->size ) rx->size = skb->len + ETH_HLEN; - if (rx->flush_count == tlb_flush_count[smp_processor_id()]) - flush_tlb_all(); + g_pte = map_domain_mem(rx->addr); @@ -723,15 +722,21 @@ void deliver_packet(struct sk_buff *skb, net_vif_t *vif) h_pfn = skb->pf; //flip and/or set relevant pf_info fields. - 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->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; + h_pfn->tot_count = h_pfn->type_count = 1; 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; + 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: + +//printk("newmpfn: %lx, old mpfn: %lx, old:(%lx) new:(%lx)\n", h_pfn - frame_table, *g_pte >> PAGE_SHIFT, machine_to_phys_mapping[h_pfn - frame_table], machine_to_phys_mapping[*g_pte >> PAGE_SHIFT]); + + 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; @@ -1000,6 +1005,9 @@ void update_shared_ring(void) 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_FOR_VIF(nvif), &s->events); @@ -1124,8 +1132,8 @@ void flush_rx_queue(void) 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->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; @@ -2244,23 +2252,39 @@ printk("LOCAL DELIVERY!\n"); pfn = rx->addr >> PAGE_SHIFT; page = frame_table + pfn; - shadow_ring->rx_ring[i].status = RING_STATUS_OK; + shadow_ring->rx_ring[i].status = RING_STATUS_BAD_PAGE; - if (!(page->flags & PGT_l1_page_table) - || !((page->flags & PG_domain_mask) == current->domain)) - 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)) - shadow_ring->rx_ring[i].status = RING_STATUS_BAD_PAGE; - page = (*g_pte >> PAGE_SHIFT) + frame_table; - if (page->tot_count != 1) - shadow_ring->rx_ring[i].status = RING_STATUS_BAD_PAGE; + { +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); } } diff --git a/xen-2.4.16/net/skbuff.c b/xen-2.4.16/net/skbuff.c index 467e161090..3bb298aaf3 100644 --- a/xen-2.4.16/net/skbuff.c +++ b/xen-2.4.16/net/skbuff.c @@ -163,7 +163,7 @@ static inline u8 *alloc_skb_data_page(struct sk_buff *skb) 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); + //pf->next = pf->prev = (pf - frame_table); free_pfns--; spin_unlock_irqrestore(&free_list_lock, flags); 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 9c40ad0066..64f33c10ff 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,6 +21,7 @@ #include #include +#include #include #define NET_TX_IRQ _EVENT_NET_TX @@ -48,6 +49,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,7 +75,7 @@ struct net_private static int network_open(struct net_device *dev) { struct net_private *np = dev->priv; - int error; + int error = 0; char *rxlabel, *txlabel; // This is inevitably not the right way to allocate a couple of static strings. @@ -179,13 +182,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 +243,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->net_page->ppte; //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; @@ -273,6 +323,8 @@ 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) ) { @@ -283,7 +335,14 @@ static void network_rx_int(int irq, void *dev_id, struct pt_regs *ptregs) 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++; 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 0f097342ba..1afc7e4d68 100644 --- a/xenolinux-2.4.16-sparse/include/asm-xeno/io.h +++ b/xenolinux-2.4.16-sparse/include/asm-xeno/io.h @@ -78,17 +78,6 @@ static inline void * phys_to_virt(unsigned long address) * These are equally trivial. */ -static inline unsigned long virt_to_mach(volatile void * address) -{ - return __pa(address) + (unsigned long) start_info.phys_base; -} - -static inline void *mach_to_virt(unsigned long address) -{ - return __va(address) - (unsigned long) start_info.phys_base; -} - - /* * 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 index 2d0a94dce2..54963090f6 100644 --- a/xenolinux-2.4.16-sparse/include/linux/skbuff.h +++ b/xenolinux-2.4.16-sparse/include/linux/skbuff.h @@ -43,7 +43,8 @@ #define SKB_ZERO_COPY 1 #define NUM_NET_PAGES 9 // about 1Meg of buffers. (2^9) -struct net_page_info { + +/*struct net_page_info { struct list_head list; unsigned long virt_addr; unsigned long ppte; @@ -54,7 +55,7 @@ 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 */ diff --git a/xenolinux-2.4.16-sparse/net/core/skbuff.c b/xenolinux-2.4.16-sparse/net/core/skbuff.c index ec76f00de0..45332f1ffd 100644 --- a/xenolinux-2.4.16-sparse/net/core/skbuff.c +++ b/xenolinux-2.4.16-sparse/net/core/skbuff.c @@ -64,12 +64,13 @@ #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; @@ -229,7 +230,7 @@ nohead: } /* begin zc code additions: */ - +/* void init_net_pages(unsigned long order_pages) { int i; @@ -248,16 +249,16 @@ void init_net_pages(unsigned long order_pages) 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(); + //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 = (unsigned long)virt_to_mach(ptep); + //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; @@ -267,6 +268,7 @@ void init_net_pages(unsigned long order_pages) struct net_page_info *get_net_page(void) { + struct list_head *list_ptr; struct net_page_info *np; unsigned long flags; @@ -301,7 +303,7 @@ void free_net_page(struct net_page_info *np) spin_unlock_irqrestore(&net_page_list_lock, flags); } - +*/ struct sk_buff *alloc_zc_skb(unsigned int size,int gfp_mask) { struct sk_buff *skb; @@ -332,12 +334,13 @@ struct sk_buff *alloc_zc_skb(unsigned int size,int gfp_mask) printk("alloc_zc_skb called with unruly size.\n"); size = PAGE_SIZE; } - skb->net_page = get_net_page(); + /*skb->net_page = get_net_page(); if (skb->net_page == NULL) { goto nodata; } - data = (u8 *)skb->net_page->virt_addr; + 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 */ @@ -443,7 +446,9 @@ static void skb_release_data(struct sk_buff *skb) { kfree(skb->head); } else {// SKB_ZERO_COPY - free_net_page(skb->net_page); + //free_net_page(skb->net_page); +//printk(KERN_ALERT "<%p>\n", phys_to_machine(virt_to_phys(skb->head))); + free_page((void *)skb->head); } } @@ -559,7 +564,7 @@ struct sk_buff *skb_clone(struct sk_buff *skb, int gfp_mask) C(tc_index); #endif C(skb_type); - C(net_page); + //C(net_page); atomic_inc(&(skb_shinfo(skb)->dataref)); skb->cloned = 1; #ifdef CONFIG_NETFILTER @@ -1361,7 +1366,7 @@ void __init skb_init(void) if (!skbuff_head_cache) panic("cannot create skbuff cache"); - init_net_pages(NUM_NET_PAGES); + //init_net_pages(NUM_NET_PAGES); for (i=0; i Date: Tue, 11 Feb 2003 13:04:09 +0000 Subject: [PATCH 08/10] bitkeeper revision 1.22.1.10 (3e48f4c9XYI43AfrJ_7BMyjM2NofzA) Changes to support zero-copy tx... incremental updates to synch with main repository. --- xen-2.4.16/common/kernel.c | 55 ++-- xen-2.4.16/common/network.c | 55 ++-- xen-2.4.16/include/xeno/vif.h | 3 +- xen-2.4.16/net/dev.c | 257 +++++++++++++----- .../arch/xeno/drivers/network/network.c | 17 +- 5 files changed, 263 insertions(+), 124 deletions(-) diff --git a/xen-2.4.16/common/kernel.c b/xen-2.4.16/common/kernel.c index fe67b0f14b..477877883e 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 @@ -297,11 +298,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"); @@ -353,6 +354,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; @@ -403,17 +405,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; @@ -442,6 +451,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); @@ -477,23 +493,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/network.c b/xen-2.4.16/common/network.c index 91d1f66315..3afc59b54f 100644 --- a/xen-2.4.16/common/network.c +++ b/xen-2.4.16/common/network.c @@ -343,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/include/xeno/vif.h b/xen-2.4.16/include/xeno/vif.h index 761188be74..7d2878dabf 100644 --- a/xen-2.4.16/include/xeno/vif.h +++ b/xen-2.4.16/include/xeno/vif.h @@ -66,9 +66,10 @@ 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 0544645686..5663ce6984 100644 --- a/xen-2.4.16/net/dev.c +++ b/xen-2.4.16/net/dev.c @@ -501,7 +501,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) { @@ -673,12 +673,11 @@ 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; + unsigned long *g_pte; //tmp struct pfn_info *g_pfn, *h_pfn; unsigned int i; //, nvif; - if (skb->skb_type != SKB_ZERO_COPY) - return; + /* * Write the virtual MAC address into the destination field @@ -724,10 +723,11 @@ void deliver_packet(struct sk_buff *skb, net_vif_t *vif) //flip and/or set relevant pf_info fields. //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; + //tmp = g_pfn->flags; g_pfn->flags = h_pfn->flags; h_pfn->flags = tmp; 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; + h_pfn->flags = g_pfn->flags & (~PG_type_mask); +//if (h_pfn->flags & PG_domain_mask) printk("deliver packet to dom %lu\n", (h_pfn->flags & PG_domain_mask)); if (*g_pte & _PAGE_RW) h_pfn->flags |= PGT_writeable_page; g_pfn->flags = 0; //point guest pte at the new page: @@ -783,25 +783,28 @@ int netif_rx(struct sk_buff *skb) net_vif_t *vif; local_irq_save(flags); - - if (skb->stamp.tv_sec == 0) - get_fast_time(&skb->stamp); - /* Attempt to handle zero-copy packets here: */ - if (skb->skb_type == SKB_ZERO_COPY) - { - 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. - */ + if (skb->skb_type != SKB_ZERO_COPY) + BUG(); - skb->data = skb->head; - skb_reserve(skb,16); // need to ensure that all the drivers and not just tulip do this. - skb->mac.raw = skb->data; - skb->data += ETH_HLEN; - skb->nh.raw = skb->data; - } + if (skb->stamp.tv_sec == 0) + get_fast_time(&skb->stamp); + + if ( (skb->data - skb->head) != (18 + ETH_HLEN) ) + 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. @@ -812,9 +815,11 @@ int netif_rx(struct sk_buff *skb) 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 ) { @@ -851,17 +856,14 @@ int netif_rx(struct sk_buff *skb) drop: netdev_rx_stat[this_cpu].dropped++; - if (skb->skb_type == SKB_ZERO_COPY) - unmap_domain_mem(skb->head); + unmap_domain_mem(skb->head); kfree_skb(skb); local_irq_restore(flags); return NET_RX_DROP; found: - if (skb->skb_type == SKB_ZERO_COPY) { - unmap_domain_mem(skb->head); - skb->head = skb->data = skb->tail = (void *)0xdeadbeef; - } + 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); @@ -990,7 +992,7 @@ void update_shared_ring(void) 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++) { @@ -1004,7 +1006,7 @@ void update_shared_ring(void) 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(); @@ -2134,13 +2136,59 @@ 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 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) { @@ -2161,6 +2209,9 @@ long do_net_update(void) { 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. */ @@ -2170,63 +2221,123 @@ long do_net_update(void) 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 ) + { + printk("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); - 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; - } + // 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. - 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 ) + skb = dev_alloc_skb(tx.size); + if (skb == NULL) { -printk("LOCAL DELIVERY!\n"); - (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; + if (copy_to_user(skb->data, g_data, tx.size)) { - skb_push(skb, skb->dev->hard_header_len); - dev_queue_xmit(skb); - } - else + unmap_domain_mem(g_data); + continue; + } + + (void)netif_rx(skb); // why is there a void here? It's from the old code. + + 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) { +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; 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 64f33c10ff..6b2c1d0484 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 @@ -241,7 +241,7 @@ static void network_alloc_rx_buffers(struct net_device *dev) skb = dev_alloc_skb(RX_BUF_SIZE); if ( skb == NULL ) break; skb->dev = dev; - //skb_reserve(skb, 2); /* word align the IP header */ + skb_reserve(skb, 2); /* word align the IP header */ np->rx_skb_ring[i] = skb; np->net_ring->rx_ring[i].addr = get_ppte(skb->head); np->net_ring->rx_ring[i].size = RX_BUF_SIZE - 16; /* arbitrary */ @@ -269,6 +269,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) { @@ -281,10 +289,13 @@ 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; 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); From 26ebb5b27fe8a6773fe3660bd2ce84823637a3f9 Mon Sep 17 00:00:00 2001 From: "akw27@boulderdash.cl.cam.ac.uk" Date: Fri, 14 Feb 2003 13:15:39 +0000 Subject: [PATCH 09/10] bitkeeper revision 1.22.1.13 (3e4cebfb6eonEmBxQR609x0DlVAf5Q) updates to enable inter-domain communication. (this requires that the vif's be mapped appropriately by dom0) but now works correctly. Secondly, guest now dev_alloc_skbs the original way. --- xen-2.4.16/drivers/net/tg3.c | 22 ++++++----- xen-2.4.16/drivers/net/tg3.h | 2 +- xen-2.4.16/include/asm-i386/pci.h | 13 +------ xen-2.4.16/net/dev.c | 37 +++++++++---------- xen-2.4.16/net/skbuff.c | 11 ++++++ .../arch/xeno/drivers/network/network.c | 2 +- .../include/linux/skbuff.h | 4 +- 7 files changed, 46 insertions(+), 45 deletions(-) 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/include/asm-i386/pci.h b/xen-2.4.16/include/asm-i386/pci.h index 65b286213d..b5007e38a1 100644 --- a/xen-2.4.16/include/asm-i386/pci.h +++ b/xen-2.4.16/include/asm-i386/pci.h @@ -76,18 +76,7 @@ static inline dma_addr_t pci_map_single(struct pci_dev *hwdev, void *ptr, BUG(); flush_write_buffers(); - if ((unsigned long) ptr > PAGE_OFFSET) - return virt_to_bus(ptr); - - /* If an address that is not in hypervisor VM is passed to this - * function (ie > PAGE_OFFSET) we assume that the passer knows - * what they are doing, and have passed a physical address that - * should not be converted here. This is a little hackish, but - * is being added to allow references to domain memory in order - * to support zero-copy network code. - */ - - return (dma_addr_t) ptr; + return virt_to_bus(ptr); } /* Unmap a single streaming mode DMA translation. The dma_addr and size diff --git a/xen-2.4.16/net/dev.c b/xen-2.4.16/net/dev.c index 5663ce6984..39bd6699af 100644 --- a/xen-2.4.16/net/dev.c +++ b/xen-2.4.16/net/dev.c @@ -712,28 +712,20 @@ void deliver_packet(struct sk_buff *skb, net_vif_t *vif) 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; - //flip and/or set relevant pf_info fields. - //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 = h_pfn->type_count = 1; g_pfn->tot_count = g_pfn->type_count = 0; h_pfn->flags = g_pfn->flags & (~PG_type_mask); -//if (h_pfn->flags & PG_domain_mask) printk("deliver packet to dom %lu\n", (h_pfn->flags & PG_domain_mask)); + if (*g_pte & _PAGE_RW) h_pfn->flags |= PGT_writeable_page; g_pfn->flags = 0; + //point guest pte at the new page: - -//printk("newmpfn: %lx, old mpfn: %lx, old:(%lx) new:(%lx)\n", h_pfn - frame_table, *g_pte >> PAGE_SHIFT, machine_to_phys_mapping[h_pfn - frame_table], machine_to_phys_mapping[*g_pte >> PAGE_SHIFT]); - machine_to_phys_mapping[h_pfn - frame_table] = machine_to_phys_mapping[g_pfn - frame_table]; @@ -791,7 +783,8 @@ int netif_rx(struct sk_buff *skb) get_fast_time(&skb->stamp); if ( (skb->data - skb->head) != (18 + ETH_HLEN) ) - BUG(); + 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)); @@ -818,11 +811,12 @@ int netif_rx(struct sk_buff *skb) if ( skb->dst_vif == VIF_UNKNOWN_INTERFACE ) 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 (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; } @@ -2258,7 +2252,7 @@ long do_net_update(void) { // 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) { @@ -2269,13 +2263,16 @@ long do_net_update(void) skb->src_vif = current_vif->id; skb->dst_vif = target; skb->protocol = protocol; - if (copy_to_user(skb->data, g_data, tx.size)) - { - unmap_domain_mem(g_data); - continue; - } + + 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); // why is there a void here? It's from the old code. + (void)netif_rx(skb); unmap_domain_mem(g_data); } diff --git a/xen-2.4.16/net/skbuff.c b/xen-2.4.16/net/skbuff.c index 3bb298aaf3..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,8 @@ 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; @@ -169,6 +172,7 @@ static inline u8 *alloc_skb_data_page(struct sk_buff *skb) 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); } @@ -185,6 +189,8 @@ static inline void dealloc_skb_data_page(struct sk_buff *skb) 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) @@ -217,6 +223,11 @@ struct sk_buff *alloc_zc_skb(unsigned int size,int gfp_mask) 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); 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 6b2c1d0484..eef4f2dae6 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 @@ -38,7 +38,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); diff --git a/xenolinux-2.4.16-sparse/include/linux/skbuff.h b/xenolinux-2.4.16-sparse/include/linux/skbuff.h index 54963090f6..fb2e5f15d8 100644 --- a/xenolinux-2.4.16-sparse/include/linux/skbuff.h +++ b/xenolinux-2.4.16-sparse/include/linux/skbuff.h @@ -1066,8 +1066,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_zc_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; From 77cf55f0b8964f4d82d7119bfe59db8fddf4b320 Mon Sep 17 00:00:00 2001 From: "akw27@boulderdash.cl.cam.ac.uk" Date: Fri, 14 Feb 2003 14:22:22 +0000 Subject: [PATCH 10/10] bitkeeper revision 1.22.1.14 (3e4cfb9e8kov7wBkpF_IkW4uJSFJYw) Extraneous interrupt removal. --- .../include/hypervisor-ifs/hypervisor-if.h | 11 ------ xen-2.4.16/net/dev.c | 14 +++++-- .../arch/xeno/drivers/network/network.c | 37 +++++++++---------- 3 files changed, 27 insertions(+), 35 deletions(-) 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 a0cfc7ae59..2d6b84e83e 100644 --- a/xen-2.4.16/include/hypervisor-ifs/hypervisor-if.h +++ b/xen-2.4.16/include/hypervisor-ifs/hypervisor-if.h @@ -108,13 +108,6 @@ typedef struct #define EVENT_DIE 0x08 /* OS is about to be killed. Clean up please! */ #define EVENT_NET_TX 0x10 /* packets for transmission. */ #define EVENT_NET_RX 0x20 /* empty buffers for receive. */ -#define EVENT_NET2_TX 0x40 /* packets for transmission. */ -#define EVENT_NET2_RX 0x80 /* 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 @@ -123,11 +116,7 @@ typedef struct #define _EVENT_DIE 3 #define _EVENT_NET_TX 4 #define _EVENT_NET_RX 5 -#define _EVENT_NET2_TX 6 -#define _EVENT_NET2_RX 7 -#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/net/dev.c b/xen-2.4.16/net/dev.c index 39bd6699af..0e19d7a0a6 100644 --- a/xen-2.4.16/net/dev.c +++ b/xen-2.4.16/net/dev.c @@ -40,6 +40,12 @@ #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)) @@ -1005,7 +1011,7 @@ void update_shared_ring(void) __flush_tlb(); 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); } } @@ -1153,7 +1159,7 @@ void flush_rx_queue(void) } 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); } @@ -2222,14 +2228,14 @@ long do_net_update(void) if ( ((tx.addr & ~PAGE_MASK) + tx.size) >= PAGE_SIZE ) { - printk("tx.addr: %lx, size: %lu, end: %lu\n", tx.addr, tx.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); /* 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 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 eef4f2dae6..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 @@ -27,9 +27,6 @@ #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) @@ -76,15 +73,7 @@ static int network_open(struct net_device *dev) { struct net_private *np = dev->priv; int error = 0; - char *rxlabel, *txlabel; - // 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)); @@ -116,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", @@ -125,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; } @@ -144,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); @@ -293,6 +280,16 @@ static int network_start_xmit(struct sk_buff *skb, struct net_device *dev) //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)phys_to_machine(virt_to_phys(skb->data)); @@ -387,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);