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包。