e100_rx_alloc_list函数调用完成的:
static int e100_rx_alloc_list(struct nic *nic) { struct rx *rx; unsigned int i, count = nic->params.rfds.count; nic->rx_to_use = nic->rx_to_clean = NULL; nic->ru_running = RU_UNINITIALIZED; if(!(nic->rxs = kmalloc(sizeof(struct rx) * count, GFP_ATOMIC))) return -ENOMEM; memset(nic->rxs, 0, sizeof(struct rx) * count); for(rx = nic->rxs, i = 0; i < count; rx++, i++) { rx->next = (i + 1 < count) ? rx + 1 : nic->rxs; rx->prev = (i == 0) ? nic->rxs + count - 1 : rx - 1; if(e100_rx_alloc_skb(nic, rx)) { e100_rx_clean_list(nic); return -ENOMEM; } } nic->rx_to_use = nic->rx_to_clean = nic->rxs; nic->ru_running = RU_SUSPENDED; return 0; }
#define RFD_BUF_LEN (sizeof(struct rfd) + VLAN_ETH_FRAME_LEN) static inline int e100_rx_alloc_skb(struct nic *nic, struct rx *rx) { if(!(rx->skb = dev_alloc_skb(RFD_BUF_LEN + NET_IP_ALIGN))) return -ENOMEM; rx->skb->dev = nic->netdev; skb_reserve(rx->skb, NET_IP_ALIGN); memcpy(rx->skb->#, &nic->blank_rfd, sizeof(struct rfd)); rx->dma_addr = pci_map_single(nic->pdev, rx->skb->#, RFD_BUF_LEN, PCI_DMA_BIDIRECTIONAL); if(pci_dma_mapping_error(rx->dma_addr)) { dev_kfree_skb_any(rx->skb); rx->skb = 0; rx->dma_addr = 0; return -ENOMEM; } if(rx->prev->skb) { struct rfd *prev_rfd = (struct rfd *)rx->prev->skb->#; put_unaligned(cpu_to_le32(rx->dma_addr), (u32 *)&prev_rfd->link); wmb(); prev_rfd->command &= ~cpu_to_le16(cb_el); pci_dma_sync_single_for_device(nic->pdev, rx->prev->dma_addr, sizeof(struct rfd), PCI_DMA_TODEVICE); } return 0; }
static int e100_poll(struct net_device *netdev, int *budget) { struct nic *nic = netdev_priv(netdev); unsigned int work_to_do = min(netdev->quota, *budget); unsigned int work_done = 0; int tx_cleaned; e100_rx_clean(nic, &work_done, work_to_do); tx_cleaned = e100_tx_clean(nic); if((!tx_cleaned && (work_done == 0)) || !netif_running(netdev)) { netif_rx_complete(netdev); e100_enable_irq(nic); return 0; } *budget -= work_done; netdev->quota -= work_done; return 1; }
static inline void e100_rx_clean(struct nic *nic, unsigned int *work_done, unsigned int work_to_do) { struct rx *rx; int restart_required = 0; struct rx *rx_to_start = NULL; if(RU_SUSPENDED == nic->ru_running) restart_required = 1; for(rx = nic->rx_to_clean; rx->skb; rx = nic->rx_to_clean = rx->next) { int err = e100_rx_indicate(nic, rx, work_done, work_to_do); if(-EAGAIN == err) { restart_required = 0; break; } else if(-ENO# == err) break; } if(restart_required) rx_to_start = nic->rx_to_clean; for(rx = nic->rx_to_use; !rx->skb; rx = nic->rx_to_use = rx->next) { if(unlikely(e100_rx_alloc_skb(nic, rx))) break; } if(restart_required) { // ack the rnr? writeb(stat_ack_rnr, &nic->csr->scb.stat_ack); e100_start_receiver(nic, rx_to_start); if(work_done) (*work_done)++; } }
static inline int e100_rx_indicate(struct nic *nic, struct rx *rx, unsigned int *work_done, unsigned int work_to_do) { struct sk_buff *skb = rx->skb; struct rfd *rfd = (struct rfd *)skb->#; u16 rfd_status, actual_size; if(unlikely(work_done && *work_done >= work_to_do)) return -EAGAIN; pci_dma_sync_single_for_cpu(nic->pdev, rx->dma_addr, sizeof(struct rfd), PCI_DMA_FROMDEVICE); rfd_status = le16_to_cpu(rfd->status); DPRINTK(RX_STATUS, DEBUG, "status=0x%04X\n", rfd_status); if(unlikely(!(rfd_status & cb_complete))) return -ENO#; actual_size = le16_to_cpu(rfd->actual_size) & 0x3FFF; if(unlikely(actual_size > RFD_BUF_LEN - sizeof(struct rfd))) actual_size = RFD_BUF_LEN - sizeof(struct rfd); pci_unmap_single(nic->pdev, rx->dma_addr, RFD_BUF_LEN, PCI_DMA_FROMDEVICE); if(le16_to_cpu(rfd->command) & cb_el) nic->ru_running = RU_SUSPENDED; skb_reserve(skb, sizeof(struct rfd)); skb_put(skb, actual_size); skb->protocol = eth_type_trans(skb, nic->netdev); if(unlikely(!(rfd_status & cb_ok))) { nic->net_stats.rx_dropped++; dev_kfree_skb_any(skb); } else if(actual_size > nic->netdev->mtu + VLAN_ETH_HLEN) { nic->rx_over_length_errors++; nic->net_stats.rx_dropped++; dev_kfree_skb_any(skb); } else { nic->net_stats.rx_packets++; nic->net_stats.rx_bytes += actual_size; nic->netdev->last_rx = jiffies; netif_receive_skb(skb); if(work_done) (*work_done)++; } rx->skb = NULL; return 0; }