公开学习文档

公开学习文档


__netif_schedule

<h2>概述</h2> <p>在发送网络包时,如果系统态 CPU 用尽了,则会调用 <code>__netif_schedule</code> 触发一个软中断。</p> <h2>分析</h2> <pre><code class="language-c">// file: net/core/dev.c void __netif_schedule(struct Qdisc *q) { if (!test_and_set_bit(__QDISC_STATE_SCHED, &amp;amp;q-&amp;gt;state)) __netif_reschedule(q); } EXPORT_SYMBOL(__netif_schedule); static inline void __netif_reschedule(struct Qdisc *q) { struct softnet_data *sd; unsigned long flags; local_irq_save(flags); sd = &amp;amp;__get_cpu_var(softnet_data); // 熟悉的 sd q-&amp;gt;next_sched = NULL; *sd-&amp;gt;output_queue_tailp = q; sd-&amp;gt;output_queue_tailp = &amp;amp;q-&amp;gt;next_sched; raise_softirq_irqoff(NET_TX_SOFTIRQ); // 软中断 local_irq_restore(flags); }</code></pre> <p>前文已经分析,<code>NET_TX_SOFTIRQ</code> 类型的软中断,其响应函数为 <code>net_tx_action</code>。 需要注意:这以后发送数据占用 CPU 的时间为 si,不会消耗用户进程的系统时间。 继续分析:</p> <pre><code class="language-c">// file: net/core/dev.c static void net_tx_action(struct softirq_action *h) { struct softnet_data *sd = &amp;amp;__get_cpu_var(softnet_data); if (sd-&amp;gt;completion_queue) { struct sk_buff *clist; local_irq_disable(); clist = sd-&amp;gt;completion_queue; sd-&amp;gt;completion_queue = NULL; local_irq_enable(); while (clist) { struct sk_buff *skb = clist; clist = clist-&amp;gt;next; WARN_ON(atomic_read(&amp;amp;skb-&amp;gt;users)); trace_kfree_skb(skb, net_tx_action); __kfree_skb(skb); } } // 如果 output_queue 上有 qdisc // 疑问:与 output_queue_tailp 是什么关系 if (sd-&amp;gt;output_queue) { struct Qdisc *head; local_irq_disable(); head = sd-&amp;gt;output_queue; // 首个 qdisc sd-&amp;gt;output_queue = NULL; sd-&amp;gt;output_queue_tailp = &amp;amp;sd-&amp;gt;output_queue; local_irq_enable(); // 遍历 qdisc 列表 while (head) { struct Qdisc *q = head; // q spinlock_t *root_lock; head = head-&amp;gt;next_sched; // 下一个 root_lock = qdisc_lock(q); if (spin_trylock(root_lock)) { smp_mb__before_clear_bit(); clear_bit(__QDISC_STATE_SCHED, &amp;amp;q-&amp;gt;state); qdisc_run(q); // 发送数据包 spin_unlock(root_lock); } else { if (!test_bit(__QDISC_STATE_DEACTIVATED, &amp;amp;q-&amp;gt;state)) { __netif_reschedule(q); // 同上 } else { smp_mb__before_clear_bit(); clear_bit(__QDISC_STATE_SCHED, &amp;amp;q-&amp;gt;state); } } } } } </code></pre> <p>软中断中,会调用 <code>qdisc_run</code> 来发送数据包,它其实和进程用户态一样,也会调用 <code>__qdisc_run</code>,如下:</p> <pre><code class="language-c">// file: include/net/pkt_sched.h static inline void qdisc_run(struct Qdisc *q) { if (qdisc_run_begin(q)) __qdisc_run(q); }</code></pre>

页面列表

ITEM_HTML