网络设备NAPI能力

来源:互联网 发布:钱龙分析软件 编辑:程序博客网 时间:2024/06/08 13:46


void netif_napi_add(struct net_device *dev, struct napi_struct *napi,
   int (*poll)(struct napi_struct *, int), int weight)
{
INIT_LIST_HEAD(&napi->poll_list);
hrtimer_init(&napi->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
napi->timer.function = napi_watchdog;
napi->gro_count = 0;
napi->gro_list = NULL;
napi->skb = NULL;
napi->poll = poll;
if (weight > NAPI_POLL_WEIGHT)
pr_err_once("netif_napi_add() called with weight %d on device %s\n",
   weight, dev->name);
napi->weight = weight;
list_add(&napi->dev_list, &dev->napi_list);
napi->dev = dev;
#ifdef CONFIG_NETPOLL
spin_lock_init(&napi->poll_lock);
napi->poll_owner = -1;
#endif
set_bit(NAPI_STATE_SCHED, &napi->state);
napi_hash_add(napi);
}
EXPORT_SYMBOL(netif_napi_add);




static int napi_poll(struct napi_struct *n, struct list_head *repoll)
{
void *have;
int work, weight;


list_del_init(&n->poll_list);


have = netpoll_poll_lock(n);


weight = n->weight;


/* This NAPI_STATE_SCHED test is for avoiding a race
* with netpoll's poll_napi().  Only the entity which
* obtains the lock and sees NAPI_STATE_SCHED set will
* actually make the ->poll() call.  Therefore we avoid
* accidentally calling ->poll() when NAPI is not scheduled.
*/
work = 0;
if (test_bit(NAPI_STATE_SCHED, &n->state)) {
work = n->poll(n, weight);
trace_napi_poll(n);
}


WARN_ON_ONCE(work > weight);


if (likely(work < weight))
goto out_unlock;


/* Drivers must not modify the NAPI state if they
* consume the entire weight.  In such cases this code
* still "owns" the NAPI instance and therefore can
* move the instance around on the list at-will.
*/
if (unlikely(napi_disable_pending(n))) {
napi_complete(n);
goto out_unlock;
}


if (n->gro_list) {
/* flush too old packets
* If HZ < 1000, flush all packets.
*/
napi_gro_flush(n, HZ >= 1000);
}


/* Some drivers may have called napi_schedule
* prior to exhausting their budget.
*/
if (unlikely(!list_empty(&n->poll_list))) {
pr_warn_once("%s: Budget exhausted after napi rescheduled\n",
    n->dev ? n->dev->name : "backlog");
goto out_unlock;
}


list_add_tail(&n->poll_list, repoll);


out_unlock:
netpoll_poll_unlock(have);


return work;
}




static void net_rx_action(struct softirq_action *h)
{
struct softnet_data *sd = this_cpu_ptr(&softnet_data);
unsigned long time_limit = jiffies + 2;
int budget = netdev_budget;
LIST_HEAD(list);
LIST_HEAD(repoll);


local_irq_disable();
list_splice_init(&sd->poll_list, &list);
local_irq_enable();


for (;;) {
struct napi_struct *n;


if (list_empty(&list)) {
if (!sd_has_rps_ipi_waiting(sd) && list_empty(&repoll))
return;
break;
}


n = list_first_entry(&list, struct napi_struct, poll_list);
budget -= napi_poll(n, &repoll);


/* If softirq window is exhausted then punt.
* Allow this to run for 2 jiffies since which will allow
* an average latency of 1.5/HZ.
*/
if (unlikely(budget <= 0 ||
    time_after_eq(jiffies, time_limit))) {
sd->time_squeeze++;
break;
}
}


__kfree_skb_flush();
local_irq_disable();


list_splice_tail_init(&sd->poll_list, &list);
list_splice_tail(&repoll, &list);
list_splice(&list, &sd->poll_list);
if (!list_empty(&sd->poll_list))
__raise_softirq_irqoff(NET_RX_SOFTIRQ);


net_rps_action_and_irq_enable(sd);
}



0 0