netpoll结构定义如下:
struct netpoll { struct net_device *dev; char dev_name[IFNAMSIZ]; const char *name; void (*rx_hook)(struct netpoll *, int, char *, int); __be32 local_ip, remote_ip; u16 local_port, remote_port; u8 remote_mac[ETH_ALEN]; };dev成员存储的是绑定的网络设备实例,netpoll实例只能通过特定的网络设备接收和发送数据包。该设备在注册netpoll实例时设置。
struct netpoll_info { atomic_t refcnt; int rx_flags; spinlock_t rx_lock; struct netpoll *rx_np; /* netpoll that registered an rx_hook */ struct sk_buff_head arp_tx; /* list of arp requests to reply to */ struct sk_buff_head txq; struct delayed_work tx_work; };refcnt是引用计数。每个netpoll_info实例被多个netpoll实例引用,每次引用时都对该成员加1.
static int poll_one_napi(struct netpoll_info *npinfo, struct napi_struct *napi, int budget) { int work; /* net_rx_action's ->poll() invocations and our's are * synchronized by this test which is only made while * holding the napi->poll_lock. */ if (!test_bit(NAPI_STATE_SCHED, &napi->state)) return budget; npinfo->rx_flags |= NETPOLL_RX_DROP; atomic_inc(&trapped); set_bit(NAPI_STATE_NPSVC, &napi->state); work = napi->poll(napi, budget); trace_napi_poll(napi); clear_bit(NAPI_STATE_NPSVC, &napi->state); atomic_dec(&trapped); npinfo->rx_flags &= ~NETPOLL_RX_DROP; return budget - work; }poll_one_api()是由poll_napi()调用的,如果当前CPU和接收数据包的CPU不是一个CPU,并且此时网卡被放置到轮询列表,即设置了NAPI_STATE_SCHED,才会去执行接收操作。所以netpoll在调度接收网卡的数据包过程中会trap数据包(trapped不为0),这种情况下ARP包会被接收。如果trapped为0,即不trap数据包,并且是ARP数据包,则会传递到上层协议栈。不过,在__netpoll_rx()中返回之前,trapped此时不为0,会丢弃ARP包。
static void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb) { ...... /* don't get messages out of order, and no recursion */ if (skb_queue_len(&npinfo->txq) == 0 && !netpoll_owner_active(dev)) { struct netdev_queue *txq; unsigned long flags; txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb)); local_irq_save(flags); /* try until next clock tick */ for (tries = jiffies_to_usecs(1)/USEC_PER_POLL; tries > 0; --tries) { if (__netif_tx_trylock(txq)) { if (!netif_tx_queue_stopped(txq)) { status = ops->ndo_start_xmit(skb, dev); if (status == NETDEV_TX_OK) txq_trans_update(txq); } __netif_tx_unlock(txq); if (status == NETDEV_TX_OK) break; } /* tickle device maybe there is some cleanup */ netpoll_poll(np); udelay(USEC_PER_POLL); } WARN_ONCE(!irqs_disabled(), "netpoll_send_skb(): %s enabled interrupts in poll (%pF)\n", dev->name, ops->ndo_start_xmit); local_irq_restore(flags); } if (status != NETDEV_TX_OK) { skb_queue_tail(&npinfo->txq, skb); schedule_delayed_work(&npinfo->tx_work,0); } }从上面的代码我们可以看到,只有在(skb_queue_len(&npinfo->txq) == 0 && !netpoll_owner_active(dev))为真时才会尝试,发送数据包,否则直接缓存到txq队列中。
static int netpoll_owner_active(struct net_device *dev) { struct napi_struct *napi; list_for_each_entry(napi, &dev->napi_list, dev_list) { if (napi->poll_owner == smp_processor_id()) return 1; } return 0; }poll_owner是在接收数据包的软中断处理函数net_rx_action()中设置的,保存的是当前处理软中断的CPU的ID。如果netpoll实例绑定的网卡没有在接收数据包,也就是网卡没有放到设备轮询列表上,此时会直接返回0.如果此时网卡被放到轮询列表上,但是接收数据包的CPU不是当前的CPU,也会返回0。如果此时绑定的网卡正在接收数据包,并且是当前CPU,才会返回1,这时netpoll在发送SKB包时,会直接将数据包放到txq队列中,等待tx_work工作队列发送。
void netpoll_poll(struct netpoll *np) { struct net_device *dev = np->dev; const struct net_device_ops *ops; if (!dev || !netif_running(dev)) return; ops = dev->netdev_ops; if (!ops->ndo_poll_controller) return; /* Process pending work on NIC */ ops->ndo_poll_controller(dev); poll_napi(dev); /* * 处理arp_tx队列中的ARP报文 */ service_arp_queue(dev->npinfo); zap_completion_queue(); }模拟中断的接口是ndo_poll_controller,如果网卡不支持,则直接返回。模拟中断后,网卡设备会被放到轮询列表上,在poll_api()中会检查接收数据包的CPU和当前CPU是否是同一个CPU,如果不是,则会调用poll_one_napi()去使用网络设备的poll接口来接收数据包,否则直接返回,避免在UP上出现递归的情况。如果可以接收数据包,则trapped会加1,此时netpoll会trap数据包,该网卡上不是netpoll想要的数据包都会被直接丢掉,也只有在这段时间netpoll才可以接收ARP报文。所以我们看到,处理netpoll接收到的ARP包的接口,只在netpoll_poll()中调用,也只有在此时才有必要去处理接收到的ARP包。