📄 hamachi.c
字号:
#define csum_add(it, val) \do { \ it += (u16) (val); \ if (it & 0xffff0000) { \ it &= 0xffff; \ ++it; \ } \} while (0) /* printk("add %04x --> %04x\n", val, it); \ *//* uh->len already network format, do not swap */#define pseudo_csum_udp(sum,ih,uh) do { \ sum = 0; \ csum_add(sum, (ih)->saddr >> 16); \ csum_add(sum, (ih)->saddr & 0xffff); \ csum_add(sum, (ih)->daddr >> 16); \ csum_add(sum, (ih)->daddr & 0xffff); \ csum_add(sum, __constant_htons(IPPROTO_UDP)); \ csum_add(sum, (uh)->len); \} while (0)/* swap len */#define pseudo_csum_tcp(sum,ih,len) do { \ sum = 0; \ csum_add(sum, (ih)->saddr >> 16); \ csum_add(sum, (ih)->saddr & 0xffff); \ csum_add(sum, (ih)->daddr >> 16); \ csum_add(sum, (ih)->daddr & 0xffff); \ csum_add(sum, __constant_htons(IPPROTO_TCP)); \ csum_add(sum, htons(len)); \} while (0)#endifstatic int hamachi_start_xmit(struct sk_buff *skb, struct net_device *dev){ struct hamachi_private *hmp = (struct hamachi_private *)dev->priv; unsigned entry; u16 status; /* Ok, now make sure that the queue has space before trying to add another skbuff. if we return non-zero the scheduler should interpret this as a queue full and requeue the buffer for later. */ if (hmp->tx_full) { /* We should NEVER reach this point -KDU */ printk(KERN_WARNING "%s: Hamachi transmit queue full at slot %d.\n",dev->name, hmp->cur_tx); /* Wake the potentially-idle transmit channel. */ /* If we don't need to read status, DON'T -KDU */ status=readw(dev->base_addr + TxStatus); if( !(status & 0x0001) || (status & 0x0002)) writew(0x0001, dev->base_addr + TxCmd); return 1; } /* Caution: the write order is important here, set the field with the "ownership" bits last. */ /* Calculate the next Tx descriptor entry. */ entry = hmp->cur_tx % TX_RING_SIZE; hmp->tx_skbuff[entry] = skb;#ifdef TX_CHECKSUM { /* tack on checksum tag */ u32 tagval = 0; struct ethhdr *eh = (struct ethhdr *)skb->data; if (eh->h_proto == __constant_htons(ETH_P_IP)) { struct iphdr *ih = (struct iphdr *)((char *)eh + ETH_HLEN); if (ih->protocol == IPPROTO_UDP) { struct udphdr *uh = (struct udphdr *)((char *)ih + ih->ihl*4); u32 offset = ((unsigned char *)uh + 6) - skb->data; u32 pseudo; pseudo_csum_udp(pseudo, ih, uh); pseudo = htons(pseudo); printk("udp cksum was %04x, sending pseudo %04x\n", uh->check, pseudo); uh->check = 0; /* zero out uh->check before card calc */ /* * start at 14 (skip ethhdr), store at offset (uh->check), * use pseudo value given. */ tagval = (14 << 24) | (offset << 16) | pseudo; } else if (ih->protocol == IPPROTO_TCP) { printk("tcp, no auto cksum\n"); } } *(u32 *)skb_push(skb, 8) = tagval; }#endif hmp->tx_ring[entry].addr = virt_to_desc(skb->data); /* Hmmmm, could probably put a DescIntr on these, but the way the driver is currently coded makes Tx interrupts unnecessary since the clearing of the Tx ring is handled by the start_xmit routine. This organization helps mitigate the interrupts a bit and probably renders the max_tx_latency param useless. Update: Putting a DescIntr bit on all of the descriptors and mitigating interrupt frequency with the tx_min_pkt parameter. -KDU */ if (entry >= TX_RING_SIZE-1) /* Wrap ring */ hmp->tx_ring[entry].status_n_length = cpu_to_le32(DescOwn|DescEndPacket|DescEndRing|DescIntr | skb->len); else hmp->tx_ring[entry].status_n_length = cpu_to_le32(DescOwn|DescEndPacket|DescIntr | skb->len); hmp->cur_tx++; /* Non-x86 Todo: explicitly flush cache lines here. */ /* Wake the potentially-idle transmit channel. */ /* If we don't need to read status, DON'T -KDU */ status=readw(dev->base_addr + TxStatus); if( !(status & 0x0001) || (status & 0x0002)) writew(0x0001, dev->base_addr + TxCmd); /* Immediately before returning, let's clear as many entries as we can. */ hamachi_tx(dev); /* We should kick the bottom half here, since we are not accepting * interrupts with every packet. i.e. realize that Gigabit ethernet * can transmit faster than ordinary machines can load packets; * hence, any packet that got put off because we were in the transmit * routine should IMMEDIATELY get a chance to be re-queued. -KDU */ if ((hmp->cur_tx - hmp->dirty_tx) < (TX_RING_SIZE - 4)) netif_wake_queue(dev); /* Typical path */ else { hmp->tx_full = 1; netif_stop_queue(dev); } dev->trans_start = jiffies; if (hamachi_debug > 4) { printk(KERN_DEBUG "%s: Hamachi transmit frame #%d queued in slot %d.\n", dev->name, hmp->cur_tx, entry); } return 0;}/* The interrupt handler does all of the Rx thread work and cleans up after the Tx thread. */static void hamachi_interrupt(int irq, void *dev_instance, struct pt_regs *rgs){ struct net_device *dev = (struct net_device *)dev_instance; struct hamachi_private *hmp; long ioaddr, boguscnt = max_interrupt_work;#ifndef final_version /* Can never occur. */ if (dev == NULL) { printk (KERN_ERR "hamachi_interrupt(): irq %d for unknown device.\n", irq); return; }#endif ioaddr = dev->base_addr; hmp = (struct hamachi_private *)dev->priv; spin_lock(&hmp->lock); do { u32 intr_status = readl(ioaddr + InterruptClear); if (hamachi_debug > 4) printk(KERN_DEBUG "%s: Hamachi interrupt, status %4.4x.\n", dev->name, intr_status); if (intr_status == 0) break; if (intr_status & IntrRxDone) hamachi_rx(dev); if (intr_status & IntrTxDone){ /* This code should RARELY need to execute. After all, this is * a gigabit link, it should consume packets as fast as we put * them in AND we clear the Tx ring in hamachi_start_xmit(). */ if (hmp->tx_full){ for (; hmp->cur_tx - hmp->dirty_tx > 0; hmp->dirty_tx++){ int entry = hmp->dirty_tx % TX_RING_SIZE; if (hmp->tx_ring[entry].status_n_length & cpu_to_le32(DescOwn)) break; /* Free the original skb. */ if (hmp->tx_skbuff[entry]){ dev_kfree_skb_irq(hmp->tx_skbuff[entry]); hmp->tx_skbuff[entry] = 0; } hmp->tx_ring[entry].status_n_length = 0; if (entry >= TX_RING_SIZE-1) hmp->tx_ring[TX_RING_SIZE-1].status_n_length |= cpu_to_le32(DescEndRing); hmp->stats.tx_packets++; } if (hmp->cur_tx - hmp->dirty_tx < TX_RING_SIZE - 4){ /* The ring is no longer full */ hmp->tx_full = 0; netif_wake_queue(dev); } } else { netif_wake_queue(dev); } } /* Abnormal error summary/uncommon events handlers. */ if (intr_status & (IntrTxPCIFault | IntrTxPCIErr | IntrRxPCIFault | IntrRxPCIErr | LinkChange | NegotiationChange | StatsMax)) hamachi_error(dev, intr_status); if (--boguscnt < 0) { printk(KERN_WARNING "%s: Too much work at interrupt, status=0x%4.4x.\n", dev->name, intr_status); break; } } while (1); if (hamachi_debug > 3) printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n", dev->name, readl(ioaddr + IntrStatus));#ifndef final_version /* Code that should never be run! Perhaps remove after testing.. */ { static int stopit = 10; if (dev->start == 0 && --stopit < 0) { printk(KERN_ERR "%s: Emergency stop, looping startup interrupt.\n", dev->name); free_irq(irq, dev); } }#endif spin_unlock(&hmp->lock);}#ifdef TX_CHECKSUM/* * Copied from eth_type_trans(), with reduced header, since we don't * get it on RX, only on TX. */static unsigned short hamachi_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-8); /* artificially enlarged on tx */ eth= skb->mac.ethernet; 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. */ else if(dev->flags&(IFF_PROMISC/*|IFF_ALLMULTI*/)) { if(memcmp(eth->h_dest,dev->dev_addr, ETH_ALEN)) skb->pkt_type=PACKET_OTHERHOST; } if (ntohs(eth->h_proto) >= 1536) return eth->h_proto; 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) return htons(ETH_P_802_3); /* * Real 802.2 LLC */ return htons(ETH_P_802_2);}#endif /* TX_CHECKSUM *//* This routine is logically part of the interrupt handler, but seperated for clarity and better register allocation. */static int hamachi_rx(struct net_device *dev){ struct hamachi_private *hmp = (struct hamachi_private *)dev->priv; int entry = hmp->cur_rx % RX_RING_SIZE; int boguscnt = (hmp->dirty_rx + RX_RING_SIZE) - hmp->cur_rx; if (hamachi_debug > 4) { printk(KERN_DEBUG " In hamachi_rx(), entry %d status %4.4x.\n", entry, hmp->rx_ring[entry].status_n_length); } /* If EOP is set on the next entry, it's a new packet. Send it up. */ while ( ! (hmp->rx_head_desc->status_n_length & cpu_to_le32(DescOwn))) { struct hamachi_desc *desc = hmp->rx_head_desc; u32 desc_status = le32_to_cpu(desc->status_n_length); u16 data_size = desc_status; /* Implicit truncate */ u8 *buf_addr = le32desc_to_virt(desc->addr); s32 frame_status = le32_to_cpu(get_unaligned((s32*)&(buf_addr[data_size - 12]))); if (hamachi_debug > 4) printk(KERN_DEBUG " hamachi_rx() status was %8.8x.\n", frame_status); if (--boguscnt < 0) break; if ( ! (desc_status & DescEndPacket)) { printk(KERN_WARNING "%s: Oversized Ethernet frame spanned " "multiple buffers, entry %#x length %d status %4.4x!\n", dev->name, hmp->cur_rx, data_size, desc_status); printk(KERN_WARNING "%s: Oversized Ethernet frame %p vs %p.\n", dev->name, desc, &hmp->rx_ring[hmp->cur_rx % RX_RING_SIZE]); printk(KERN_WARNING "%s: Oversized Ethernet frame -- next status %x/%x last status %x.\n", dev->name, hmp->rx_ring[(hmp->cur_rx+1) % RX_RING_SIZE].status_n_length & 0xffff0000, hmp->rx_ring[(hmp->cur_rx+1) % RX_RING_SIZE].status_n_length & 0x0000ffff, hmp->rx_ring[(hmp->cur_rx-1) % RX_RING_SIZE].status_n_length); hmp->stats.rx_length_errors++; } /* else Omit for prototype errata??? */ if (frame_status & 0x00380000) { /* There was an error. */ if (hamachi_debug > 2) printk(KERN_DEBUG " hamachi_rx() Rx error was %8.8x.\n", frame_status); hmp->stats.rx_errors++; if (frame_status & 0x00600000) hmp->stats.rx_length_errors++; if (frame_status & 0x00080000) hmp->stats.rx_frame_errors++; if (frame_status & 0x00100000) hmp->stats.rx_crc_errors++; if (frame_status < 0) hmp->stats.rx_dropped++; } else { struct sk_buff *skb; /* Omit CRC */ u16 pkt_len = (frame_status & 0x07ff) - 4; #ifdef RX_CHECKSUM u32 pfck = *(u32 *) &buf_addr[data_size - 8];#endif#ifndef final_version if (hamachi_debug > 4) printk(KERN_DEBUG " hamachi_rx() normal Rx pkt length %d" " of %d, bogus_cnt %d.\n", pkt_len, data_size, boguscnt); if (hamachi_debug > 5) printk(KERN_DEBUG"%s: rx status %8.8x %8.8x %8.8x %8.8x %8.8x.\n", dev->name, *(s32*)&(buf_addr[data_size - 20]), *(s32*)&(buf_addr[data_size - 16]), *(s32*)&(buf_addr[data_size - 12]), *(s32*)&(buf_addr[data_size - 8]), *(s32*)&(buf_addr[data_size - 4]));#endif /* Check if the packet is long enough to accept without copying to a minimally-sized skbuff. */ if (pkt_len < rx_copybreak && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {#ifdef RX_CHECKSUM printk(KERN_ERR "%s: rx_copybreak non-zero " "not good with RX_CHECKSUM\n", dev->name);#endif skb->dev = dev;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -