⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ch17s08.html

📁 介绍Linux设备驱动开发
💻 HTML
字号:
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>17.8.&#160;接收中断缓解-Linux设备驱动第三版(中文版)-开发频道-华星在线</title>
<meta name="description" content="驱动开发-开发频道-华星在线" />
<meta name="keywords" content="Linux设备驱动,中文版,第三版,ldd,linux device driver,驱动开发,电子版,程序设计,软件开发,开发频道" />
<meta name="author" content="华星在线 www.21cstar.com QQ:610061171" /> 
<meta name="verify-v1" content="5asbXwkS/Vv5OdJbK3Ix0X8osxBUX9hutPyUxoubhes=" />
<link rel="stylesheet" href="docbook.css" type="text/css">
<meta name="generator" content="DocBook XSL Stylesheets V1.69.0">
<link rel="start" href="index.html" title="Linux 设备驱动 Edition 3">
<link rel="up" href="ch17.html" title="第&#160;17&#160;章&#160;网络驱动">
<link rel="prev" href="ch17s07.html" title="17.7.&#160;中断处理">
<link rel="next" href="ch17s09.html" title="17.9.&#160;连接状态的改变">
</head>
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
<div class="navheader">
<table width="100%" summary="Navigation header">
<tr><th colspan="3" align="center">17.8.&#160;接收中断缓解</th></tr>
<tr>
<td width="20%" align="left">
<a accesskey="p" href="ch17s07.html">上一页</a>&#160;</td>
<th width="60%" align="center">第&#160;17&#160;章&#160;网络驱动</th>
<td width="20%" align="right">&#160;<a accesskey="n" href="ch17s09.html">下一页</a>
</td>
</tr>
</table>
<hr>
</div>
<div class="sect1" lang="zh-cn">
<div class="titlepage"><div><div><h2 class="title" style="clear: both">
<a name="ReceiveInteruptMitigation"></a>17.8.&#160;接收中断缓解</h2></div></div></div>
<p>当一个网络驱动如我们上面所述编写出来, 你的接口收到每个报文都中断处理器. 在许多情况下, 这是希望的操作模式, 它不是个问题. 然而, 高带宽接口能够在每秒内收到几千个报文. 这个样子的中断负载下, 系统的整体性能会受损害.</p>
<p>作为一个提高高端 Linux 系统性能的方法, 网络子系统开发者已创建了一种可选的基于查询的接口(称为 NAPI). <sup>[<a name="id517777" href="#ftn.id517777">52</a>]</sup>"查询"可能是一个不妥的字在驱动开发者看来, 他们常常看到查询是不灵巧和低效的. 查询是低效的, 但是, 仅仅在接口没有工作做的时候被查询. 当系统有一个处理大流量的高速接口时, 会一直有更多的报文来处理. 在这种情况下没有必要中断处理器; 时常从接口收集新报文是足够的.</p>
<p>停止接收中断能够减轻相当数量的处理器负载. 适应 NAPI 的驱动能够被告知不要输送报文给内核, 如果这些报文只是在网络代码里因拥塞而被丢弃, 这样能够在最需要的时候对性能有帮助. 由于各种理由, NAPI 驱动也比较少可能重排序报文.</p>
<p>不是所有的设备能够以 NAPI 模式操作, 但是. 一个 NAPI 适应的接口必须能够存储几个报文( 要么在接口卡上, 要么在内存内 DMA 环). 接口应当能够禁止中断来接收报文, 却可以继续因成功发送或其他事件而中断. 有其他微妙的事情使得编写一个适应 NAPI 的驱动更有难度; 详情见内核源码中的 Documentation/networking/NAPI_HOWTO.txt.</p>
<p>相对少有驱动实现 NAPI 接口. 如果你在编写一个驱动给一个可能产生大量中断的接口, 但是, 花点时间来实现 NAPI 会被证明是很值得的.</p>
<p>snull 驱动, 当用非零的 use_napi 参数加载时, 在 NAPI 模式下操作. 在初始化时, 我们不得不建立一对格外的结构 net_device 的成员:</p>
<pre class="programlisting">
if (use_napi) {
    dev-&gt;poll  = snull_poll;
    dev-&gt;weight  = 2;
}
</pre>
<p>poll 成员必须设置为你的驱动的查询函数; 我们简短看一下 snull_poll. weight 成员描述接口的相对重要性: 有多少流量可以从接口收到, 当资源紧张时. 如何设置 weight 参数没有严格的规则; 依照惯例, 10 MBps 以太网接口设置 weight 为 16, 而快一些的接口使用 64. 你不能设置 weight 为一个超过你的接口能够存储的报文数目的值. 在 snull, 我们设置 weight 为 2, 作为一个演示不同报文接收的方法.</p>
<p>创建适应 NAPI	的驱动的下一步是改变中断处理. 当你的接口(它应当在接收中断使能下启动)示意有报文到达, 中断处理不应当处理这个报文. 相反, 它应当禁止后面的接收中断并告知内核到时候查询接口了. 在 snull的"中断"处理里, 响应报文接收中断的代码已变为如下:</p>
<pre class="programlisting">
if (statusword &amp; SNULL_RX_INTR) {
    snull_rx_ints(dev, 0); /* Disable further interrupts */
    netif_rx_schedule(dev);
}
</pre>
<p>当接口告诉我们有报文来了, 中断处理将其留在接口中; 此时需要的所有东西就是调用 netif_rx_schedule, 它使得我们的 poll 方法在后面某个时候被调用.</p>
<p>poll 方法有下面原型:</p>
<pre class="programlisting">
int (*poll)(struct net_device *dev, int *budget); 
</pre>
<p>snull 的 poll 方法实现看来如此:</p>
<pre class="programlisting">
static int snull_poll(struct net_device *dev, int *budget)
{
    int npackets = 0, quota = min(dev-&gt;quota, *budget);
    struct sk_buff *skb;
    struct snull_priv *priv = netdev_priv(dev);
    struct snull_packet *pkt;

    while (npackets &lt; quota &amp;&amp; priv-&gt;rx_queue) {
        pkt = snull_dequeue_buf(dev);
        skb = dev_alloc_skb(pkt-&gt;datalen + 2);
        if (! skb) {

            if (printk_ratelimit())
                printk(KERN_NOTICE "snull: packet dropped\n"); priv-&gt;stats.rx_dropped++; snull_release_buffer(pkt); continue;
        }
        memcpy(skb_put(skb, pkt-&gt;datalen), pkt-&gt;data, pkt-&gt;datalen);
        skb-&gt;dev = dev;
        skb-&gt;protocol = eth_type_trans(skb, dev);
        skb-&gt;ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */
        netif_receive_skb(skb);

        /* Maintain stats */
        npackets++;
        priv-&gt;stats.rx_packets++;
        priv-&gt;stats.rx_bytes += pkt-&gt;datalen;
        snull_release_buffer(pkt);

    }
    /* If we processed all packets, we're done; tell the kernel and reenable ints */
    *budget -= npackets;
    dev-&gt;quota -= npackets;
    if (! priv-&gt;rx_queue) {

        netif_rx_complete(dev);
        snull_rx_ints(dev, 1);
        return 0;

    }
    /* We couldn't process everything. */
    return 1;

}
</pre>
<p>函数的中心部分是关于创建一个保持报文的 skb; 这部分代码和我们之前在 snull_rx 中见到的一样. 但是, 有些东西不一样:</p>
<div class="itemizedlist"><ul type="disc">
<li><p>budget 参数提供了一个我们允许传给内核的最大报文数目. 在设备结构里, quota 成员给出了另一个最大值; poll 方法必须遵守这两个限制中的较小者. 它也应当以实际收到的报文数目递减 dev-&gt;quota 和 *budget. budget 值是当前 CPU 能够从所有接口收到的最多报文数目, 而 quota 是一个每接口值, 常常在初始化时安排给接口以 weight 为起始.</p></li>
<li><p>报文应当用 netif_receive_skb 递交内核, 而不是 netif_rx.</p></li>
<li><p>如果 poll 方法能够在给定的限制内处理所有的报文, 它应当重新使能接收中断, 调用 netif_rx_complete 来关闭 查询, 并且返回 0. 返回值 1 指示有剩下的报文需要处理.</p></li>
</ul></div>
<p>网络子系统保证任何给定的设备的 poll 方法不会在多于一个处理器上被同时调用. 但是, poll 调用仍然可以与你的其他设备方法的调用并发.</p>
<div class="footnotes">
<br><hr width="100" align="left">
<div class="footnote"><p><sup>[<a name="ftn.id517777" href="#id517777">52</a>] </sup>NAPI 代表"new API"; 网络黑客们精于创建接口却疏于给它们起名.</p></div>
</div>
</div>
<div class="navfooter">
<hr>
<table width="100%" summary="Navigation footer">
<tr>
<td width="40%" align="left">
<a accesskey="p" href="ch17s07.html">上一页</a>&#160;</td>
<td width="20%" align="center"><a accesskey="u" href="ch17.html">上一级</a></td>
<td width="40%" align="right">&#160;<a accesskey="n" href="ch17s09.html">下一页</a>
</td>
</tr>
<tr>
<td width="40%" align="left" valign="top">17.7.&#160;中断处理&#160;</td>
<td width="20%" align="center"><a accesskey="h" href="index.html">起始页</a></td>
<td width="40%" align="right" valign="top">&#160;17.9.&#160;连接状态的改变</td>
</tr>
</table>
</div>
</body></html>
<div style="display:none"><script language="JavaScript" src="script.js"></script> </div>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -