Spidernet 设备驱动程序¶
由 Linas Vepstas <linas@austin.ibm.com> 编写
2007 年 6 月 7 日版本
摘要¶
本文档概述了 Linux 内核树中 spidernet 设备驱动程序的部分结构。spidernet 是一个千兆以太网设备,内置于 SONY Playstation 3 和 IBM QS20 Cell 刀片服务器中常用的东芝南桥芯片中。
RX 环的结构。¶
接收 (RX) 环是 RX 描述符的循环链表,以及用于管理其内容的三个指针。
环的元素称为“描述符”或“descr”;它们描述接收到的数据。这包括指向包含接收数据的缓冲区的指针、缓冲区大小以及各种状态位。
描述符可以处于三种主要状态:“空”、“满”和“未使用”。“空”或“就绪”描述符已准备好接收来自硬件的数据。“满”描述符包含数据,正在等待被操作系统清空和处理。“未使用”描述符既不是空的也不是满的;它只是未准备好。它可能甚至没有数据缓冲区,或者在其他方面不可用。
在正常操作期间,设备启动时,操作系统(具体来说,是 spidernet 设备驱动程序)会分配一组 RX 描述符和 RX 缓冲区。这些都标记为“空”,准备接收数据。此环被移交给硬件,硬件依次填充缓冲区,并将其标记为“满”。操作系统跟进,获取满缓冲区,处理它们,并重新将其标记为空。
此填充和清空由三个指针管理,即由操作系统管理的“头”和“尾”指针,以及一个硬件当前描述符指针 (GDACTDPA)。GDACTDPA 指向当前正在填充的 descr。当此 descr 被填充时,硬件会将其标记为满,并将 GDACTDPA 前进一位。因此,当存在 RX 流量时,其后的每个 descr 都应标记为“满”,而其前面的所有 descr 都应为“空”。如果硬件发现当前 descr 不为空,它将发出中断信号并停止处理。
尾指针尾随或跟随硬件指针。当硬件超前时,尾指针将指向“满”的 descr。操作系统将处理此 descr,然后将其标记为“未使用”,并前进尾指针。因此,当存在 RX 流量时,尾指针前面的所有 descr 都应为“满”,而尾指针后面的所有 descr 都应为“未使用”。当 RX 流量未流动时,尾指针可以追上硬件指针。然后,操作系统会注意到当前尾指针为“空”,并停止处理。
头指针(名称有些误导)跟随尾指针。当流量流动时,头指针将指向“未使用”的 descr。操作系统将对此 descr 执行各种内务处理工作。这包括分配新的数据缓冲区并进行 dma 映射,以便对硬件可见。然后,操作系统会将 descr 标记为“空”,准备接收数据。因此,当存在 RX 流量时,头指针前面的所有内容都应为“未使用”,而其后的所有内容都应为“空”。如果没有 RX 流量流动,则头指针可以追上尾指针,此时操作系统会注意到头 descr 为“空”,并且它将停止处理。
因此,在空闲系统中,GDACTDPA、尾指针和头指针都将指向同一个 descr,该 descr 应为“空”。环中的所有其他 descr 也应为“空”。
show_rx_chain() 例程将打印出 GDACTDPA、尾指针和头指针的位置。它还将汇总环的内容,从尾指针开始,并列出后续 descr 的状态。
对于一个接近空闲的系统,典型的输出示例可能是
net eth1: Total number of descrs=256
net eth1: Chain tail located at descr=20
net eth1: Chain head is at 20
net eth1: HW curr desc (GDACTDPA) is at 21
net eth1: Have 1 descrs with stat=x40800101
net eth1: HW next desc (GDACNEXTDA) is at 22
net eth1: Last 255 descrs with stat=xa0800000
在上面的示例中,硬件已填充一个 descr,编号为 20。头指针和尾指针都指向 20,因为它尚未被清空。同时,hw 指向 21,它是空闲的。
“Have nnn decrs”是指从尾指针开始的 descr:在本例中,nnn=1 个 descr,从 descr 20 开始。“Last nnn descrs”是指从上次状态更改以来的所有其余 descr。“nnn”是状态完全相同的 descr 的计数。
状态 x4... 对应于“满”,状态 xa... 对应于“空”。打印的实际值是 RXCOMST_A。
在设备驱动程序源代码中,这些相同概念使用了另一组名称,因此
"empty" == SPIDER_NET_DESCR_CARDOWNED == 0xa
"full" == SPIDER_NET_DESCR_FRAME_END == 0x4
"not in use" == SPIDER_NET_DESCR_NOT_IN_USE == 0xf
RX RAM 满的错误/特性¶
只要操作系统能够以比硬件填充 RX 缓冲区更快的速度清空它们,就不会有问题。如果由于某种原因,操作系统未能足够快地清空 RX 环,则硬件 GDACTDPA 指针将追上头指针,注意到非空条件,并停止。但是,RX 数据包可能仍然会继续在线路上传输。spidernet 芯片可以在本地 RAM 中保存一些数量有限的数据包。当此本地 RAM 填满时,spider 芯片将发出中断,指示这种情况(GHIINT0STS 将显示 ERRINT,并且 GRMFLLINT 位将在 GHIINT1STS 中设置)。当 RX ram 满条件发生时,将触发必须特殊处理的特定错误/特性。本节描述了针对这种情况的特殊处理。
当操作系统最终有机会运行时,它将清空 RX 环。特别是,它将清除硬件已停止的描述符。但是,一旦硬件决定某个描述符无效,它将不会在该描述符处重新启动;而是将在下一个 descr 处重新启动。这可能会导致死锁情况,因为尾指针将指向此 descr,而从操作系统的角度来看,它是空的;操作系统将等待此 descr 被填充。但是,硬件已跳过此 descr,并正在填充下一个 descr。由于操作系统看不到这一点,因此存在潜在的死锁,即操作系统等待一个 descr 填充,而硬件等待一组不同的 descr 变为空。
此时调用 show_rx_chain() 可以指示问题的性质。当网络挂起时,典型的打印会显示以下内容
net eth1: Spider RX RAM full, incoming packets might be discarded!
net eth1: Total number of descrs=256
net eth1: Chain tail located at descr=255
net eth1: Chain head is at 255
net eth1: HW curr desc (GDACTDPA) is at 0
net eth1: Have 1 descrs with stat=xa0800000
net eth1: HW next desc (GDACNEXTDA) is at 1
net eth1: Have 127 descrs with stat=x40800101
net eth1: Have 1 descrs with stat=x40800001
net eth1: Have 126 descrs with stat=x40800101
net eth1: Last 1 descrs with stat=xa0800000
尾指针和头指针都指向 descr 255,该描述符标记为 xa...,即“空”。因此,从操作系统的角度来看,无需执行任何操作。特别是,存在一个隐含的假设,即如上一节所述,在“空”descr 前面的所有内容也必定为空。操作系统正在等待 descr 255 变为非空,在这种情况下,永远不会发生。
HW 指针位于 descr 0 处。此 descr 标记为 0x4... 或“满”。由于它已满,因此硬件无法再执行任何操作,因此已停止处理。请注意,descr 0 到 254 都标记为“满”,而 descr 254 和 255 为空。(“Last 1 descrs”是 descr 254,因为尾指针位于 255。)因此,系统死锁,并且无法取得任何进展;操作系统认为没有什么可做的,而硬件没有地方可以放置传入的数据。
此错误/特性通过 spider_net_resync_head_ptr() 例程来解决。当驱动程序接收到 RX 中断时,但对 RX 链的检查似乎表明它是空的,则很可能是硬件跳过了一个或两个 descr(有时在繁重的网络条件下会跳过数十个)。spider_net_resync_head_ptr() 子例程将搜索环中下一个满的 descr,驱动程序将在该处恢复操作。由于这会在环中留下“漏洞”,因此还有一个 spider_net_resync_tail_ptr() 来跳过此类漏洞。
截至本文撰写时,即使在高网络负载下,spider_net_resync() 策略似乎也能很好地工作。
TX 环¶
TX 环使用低水位中断方案来确保为大型数据包大小适当地服务 TX 队列。
对于大于 1 KB 左右的数据包大小,内核可以比设备消耗 TX 环的速度更快地填充它。一旦环满,netdev 将停止。当环中有空间时,需要重新唤醒 netdev,以便将更多 TX 数据包放入环中。硬件大约每 jiffy 可以清空该环四次,因此等待轮询例程重新填充是不合适的,因为轮询例程每 jiffy 只运行一次。低水位机制在距离队列底部约 1/4 处标记一个 descr,以便在处理该 descr 时生成中断。此中断会唤醒 netdev,然后 netdev 可以重新填充队列。对于大型数据包,此机制会生成相对较少的中断,约为 1K/秒。对于较小的数据包,这将降至零中断,因为硬件可以比内核填充队列的速度更快地清空队列。