2.相关技术的描述
当通过数据通道,例如,网络,在两个设备之间传输数据时,每个设备都必须具有适合的网络接口,以允许其通过通道进行通信。通常这些网络是基于以太网技术的。为将要通过网络通信的设备配备网络接口,该网络接口能够支持网络协议的物理和逻辑要求。将网络接口的物理硬件部分称为网络接口卡(NIC),尽管它们不一定采用卡的形式:例如,它们可以采用直接安装在主板上的集成电路(IC)和连接器的形式,或者是与计算机系统的其他部分一起被装配在单个集成电路芯片上的宏单元的形式。
大多数计算机系统包括操作系统(OS),用户级应用程序通过其与网络通信。操作系统的一部分,已知为内核,包括协议栈,这些协议栈用于翻译在应用和指定到NIC的设备驱动器间的命令和数据,并且该设备驱动器直接控制NIC。通过在操作系统的内核中提供这些功能,可以从用户级应用程序中隐藏网络接口卡的复杂性和差异。另外,通过多个应用程序可以安全地共享网络硬件和其它系统资源(例如存储器),并且通过避免故障或者恶意的应用程序来保护系统。
在典型的内核栈系统的操作中,硬件网络接口卡是在网络和内核间的接口。在内核中,设备驱动器层直接与NIC通信,而协议层与系统的应用级通信。
NIC存储指向主机存储器中的缓冲区的指针,用来输入提供给内核的数据和输出应用于网络的数据。它们称为RX数据环和TX数据环。NIC对指示RX缓冲区环上的内核要读取的下一个数据的缓冲区指针进行更新。通过直接存储器存取(DMA)来提供TX数据环,并且NIC对指示已发送的输出数据的缓冲区指针进行更新。NIC可以利用中断向内核发送信号。
通过内核从RX数据环中摘掉输入数据,依次对其处理。通常由内核本身处理带外数据。通过指向应用专用端口的专用的缓冲区队列的指针来添加要到该应用专用端口的数据,该缓冲区队列驻留在内核的私有地址空间中。
在用于数据接收的系统操作时,会发生以下步骤:
1、在系统初始化期间,操作系统设备驱动器创建内核缓冲区,并对NIC的RX环初始化,以指向这些缓冲区。OS还接收到来自配置脚本的OS的IP主机地址的通知。
2、一应用程序希望接收网络数据包并且典型地创建一套接字(a socket),该套接字被绑定到一端口,该套接字是驻留在操作系统内的队列状数据结构。对于给定的网络协议,该端口具有在主机内的唯一的编号,以这样的一种方式,使得可以将寻址到<主机:端口>的网络包传送到正确的端口的队列。
3、一数据包到达网络接口卡(NIC)。该NIC通过主机I/O总线(例如PCI总线),将该包复制到由下一个有效RX DMA环指针值指向的存储器地址。
4、要么没有可用的剩余DMA指针,要么达到预指定超时,NIC断言I/O总线中断,以通知主机已传送了数据。
5、响应于中断,设备驱动器检查所传送的缓冲区,如果该缓冲区包含有效地址信息,例如有效的主机地址,则将指向该缓冲区的指针传递给适当的协议栈(例如TCP/IP)。在某些系统中,设备驱动器能够切换以轮询有限的时段,以试图减少中断次数。
6、协议栈确定是否存在有效目的地端口,如果存在,则执行网络协议处理(生成对所接收到的数据的确认),并使数据包在端口的队列中排队。
7、OS可以指示应用程序(例如通过再调度并设置“选择”位掩码中的位)一数据包已到达所述端口绑定到的网络端点上(通过将该应用程序标记为可运行的并调用一调度程序)。
8、应用程序从OS请求数据,例如通过执行一recv()系统调用(提供缓冲区的地址和大小),同时在OS内核中,将数据从内核缓冲区复制到应用程序的缓冲区。在从系统调用返回时,应用程序可以从应用程序缓冲区访问该数据。
9、在复制后(其通常发生在软中断的环境下),内核将内核缓冲区返回给空闲存储器的OS库。而且,在中断过程中,设备驱动器分配一新缓冲区并向DMA环添加一指针。按此方式,具有一个从空闲库到应用的端口队列的缓冲区的循环,并且周而复始。
10、典型地,内核缓冲区位于物理RAM中,并且永远不会被虚拟存储器(VM)系统调页出去。然而,可以将空闲库共享为所有应用程序的公共资源。
对于数据发送,发生以下步骤:
1、操作系统设备驱动器创建用于进行发送的内核缓冲区,并初始化NIC的TX环。
2、要发送数据的应用程序将该数据存储在应用程序缓冲区中,并且请求通过OS的发送,例如通过执行send()系统调用(提供应用缓冲区的地址和大小)。
3、响应于该send()调用,OS内核将数据从应用程序缓冲区复制到内核缓冲区中,并应用适当的协议栈(例如TCP/IP)。
4、将指向含有该数据的内核缓冲区的指针,置于TX环的下一个空闲空隙中。如果没用可用的空隙,则在内核中将该缓冲区排队,直到NIC例如通过中断指示一空隙已变得可用。
5、当由NIC要处理该空隙时,NIC通过主机I/O总线,利用DMA循环访问由该空隙的内容表示的内核缓冲区,然后发送该数据。
过去已经认识到,发送和接收操作均会包括过多的数据运动。已经提出一些解决方案来减轻由这种数据运动而导致的性能劣化。例如,参见美国专利No.6246683,通过引用将其并入于此。在PCT国际公报No.WO2004/025477A2中,通过引用将其并入于此,进一步认识到发送和接收操作均会包括过多的环境切换,这也会引起重大的费用。在那里描述了用于减少所需环境切换次数的技术。
在那里描述的机制中,使用事件队列来在主机系统和NIC之间对控制信息进行通信。当通过I/O总线将网络接口设备连接到主机系统时,例如通过一PCI总线,需要在处理器与NIC间的控制信息的频繁的通信。典型地,由NIC发出的中断来启动控制通信,这导致了环境切换。另外,该通信往往要求主机系统通过PCI总线从NIC读取控制信息或向NIC写入控制信息,并且这样会导致总线瓶颈。该问题在数据包往往很短的网络连接的环境下尤其严重,使得所需控制工作占总网络处理工作的百分比很大。
在该PCT公报中描述的实施例中,“端口”被认为是绑定到应用程序的操作系统专用实体,具有地址码,并且可以接收消息。寻址到一端口的一个或多个输入消息,形成一消息队列,通过操作系统来处理该消息队列。操作系统先前已经存储一个在该端口和运行在该操作系统上的应用程序之间的捆绑关系。通过操作系统来处理用于一端口的消息队列中的消息,又通过操作系统将这些消息提供给该端口所绑定的应用程序。操作系统可以存储端口到应用程序的多样的捆绑关系,以便通过指定适当的端口,将输入数据施加给适当的应用程序。端口存在于操作系统内,以便无论相应的应用程序的状态如何,都可以接收并安全地处理这些消息。
在操作系统的操作开始时,操作系统创建一队列用来处理带外消息。通过NIC可以写入该队列,并且该队别可以具有一个与其相关联的中断。当一应用程序绑定于一端口时,操作系统创建该端口,并将其与该应用程序相关联。操作系统还创建了一队列(一事件队列),以处理仅用于该端口的带外消息。然后将用于该端口的带外消息队列存储映射到该应用程序的虚拟地址空间,以便不需要内核环境切换就可以使事件退出队列。
事件队列与NIC一起注册,并且在与各队列相关联的NIC上存在控制块(并映射到OS和应用程序中的任一个或这两者的地址空间中)。
图1例示了如在PCT公报中描述的带有控制块的队列。在所描述的实现中,将NIC161通过PCI总线110连接到主机系统中。将事件队列159存储在主机存储器160中,NIC161具有到主机存储器160的访问权。与事件队列159相关联的是读指针(RDPTR)162a和写指针(WRPTR)163a,其指出在该队列中接下来要读取和写入的数据处的点。将指针162a存储在主机存储器160中。将指针163a存储在NIC161中。在NIC和存储器中的与原始指针不同的另一个中存储有指针的映射副本RDPTR’162b和WRPTR’163b。在该系统的操作中:
1、NIC161可以通过比较存储在本地的RDPTR’与WRPTR,来确定用于写入事件队列159中的可用空间。
2、NIC161生成带外数据并将其写入队列159。
3、当已写入数据时,NIC161更新WRPTR和WRTER’,使得在最后一个数据之后将写入下一数据。
4、应用程序通过对如从存储器160访问的RDPTR与WRPTR’进行比较,来确定用于读取的可用空间。
5、应用程序从队列159读取带外数据并对消息进行处理。
6、应用程序对RDPTR和RDPTR’进行更新。
7、如果应用程序要求中断,则该应用程序(或者代表该应用程序的操作系统)设置控制块164的IRQ165a和IRQ165b’位。将控制块存储在主机存储器160中并将其映射到NIC中的相对应的存储器上。如果进行了设置,则NIC还将在以上步骤3中生成中断。事件队列机制通过在应用程序和OS已具有环境的同时不断允许它们对新事件进行轮询,有助于改进性能;通过只在需要时产生中断减少了环境切换。还减少了总线瓶颈,因为主机系统可以从现在在主机存储器中的事件中更频繁地检索控制信息,而不是从NIC直接通过PCI总线检索控制信息。
事件队列的使用并不完全消除I/O总线上的控制通信量。在一个方面,这种通信量实际上被增加。参考图1和上面所附描述,可以了解的是NIC161和主机系统都需要将读和写指针复制到事件队列159中。NIC需要上述二者是由于它从读指针减去写指针(取队列长度的模)为了确定事件队列159中的用于写(重新排队)新事件的可用空间。类似地,主机系统需要上述二者是由于它从写指针减去读指针(取队列长度的模)为了确定用于读的排队等候事件的有效性。然而,事件队列的目的典型地是仅仅由NIC使事件排队,并且仅仅由主机使事件出列。因而,尽管NIC可以容易地保存写指针的当前副本(由于NIC是唯一修改写指针的代理),但是其仍然需要离开主机存储器去获得读指针的当前副本。类似地,尽管主机能够容易地获得读指针的当前副本(由于主机是唯一修改读指针的代理),但是其仍然需要离开NIC去获得写指针的当前副本。来自副本代理的读或者写指针的每次检索都包含通过I/O总线的不需要的处理。
在上述PCT公布中,通过使每个代理都保持相对代理指针的本地副本减少这种处理的数量。不论什么时候NIC更新它的写指针163a,NIC也会更新主机存储器160中的副本163b。类似地,不论什么时候主机系统更新它的读指针162a,它也会更新NIC上的副本162b。在这种方式中每个代理都总是具有双方指针的当前本地副本。
但是当PCT公布的更新机制减少I/O总线上的控制通信量的时候,仍然需要一些通信量。在严重的情形中,为了将本地读和写指针的影像副本更新到副本代理,甚至这个通信量能显著地降低I/O总线上的性能。找到进一步减小或者甚至完全消除这种控制通信量的方式是非常需要的,虽然仍然允许NIC获知事件队列159中的用于写新事件的有效空间,并且仍然允许主机系统获知用于读的排队等候事件的有效性。
概略地描述,这可以由每个代理所完成,所述代理推断所有来自需要以任何方式去穿过I/O总线的其他控制通信量的所需信息。在实施例中,为了关于数据接收队列(RX队列)的事件队列,主机系统将NIC看作是NIC可以将接收数据写入的主机存储器的接收数据缓冲区。为了关于数据传送队列(TX队列)的事件队列,主机系统将NIC看作是准备用于传送的主机存储器中的传送数据缓冲区。除了限制数量的管理事件外,如此设计NIC以致于将不多于预定数量的事件写入到用于每个由主机识别的接收或者传送数据缓冲区的相关事件队列。NIC不需要为了队列深度管理,将读指针的本地副本保存在事件队列中,因为主机不通知NIC比可以被相关事件队列中的当前有效空间容纳的接收或者传送缓冲区更多的缓冲区。因此通过将NIC仅仅看作是限制数量的接收或者传送数据缓存区,主机系统也“授权”NIC将不多于特定数量的事件写入到相关事件队列。无论如何主机需要把这些缓冲区看作和NIC一样,因此,通过授权机制在I/O总线上没有招致明显的附加费用。NIC根据这些授权,知晓它能使多少事件在事件队列中排队,不通过从读指针减去写指针。
在实施例中,主机子系统根据它已经将其看作是NIC的未完成的接收或者传送数据缓冲区,做出它的事件队列中可用空间数量的决定。在实施例中,主机子系统保持用于接收和传送队列的读和写指针,并且其可以简单地使用它们之间的差(取接收或者传送缓冲区环大小的模)作为它的授权事件数量的决定的部分。
主机子系统,对于其局部,不需要为了队列深度管理将写指针保持到事件队列中,因为它在事件被处理后就清除事件队列中的事件。主机子系统知晓它已经通过检索仍然处于其清除状态的事件描述符来处理所有未完成事件。
数据结构,简化实施例
图3和4例示了被并入于图2的计算机系统210中的本发明的简化实施例。在图3的实施例中,仅示出了带有相关联的结构的单个发送队列,在图4的实施例中,仅示出了带有相关联的结构的单个接收队列。在典型实现中包括有发送和接收队列,但是可以单独的在各队列中实现本发明的多个方面。图3和4仅示出了主机存储器子系统222和网络接口卡216,还有与本讨论有关的这些组件内的结构。
首先参照图3,将发送队列存储在主机存储器222中的一系列发送数据缓冲区310中。在主机存储器222中这些发送数据缓冲区可以是不连续的,并且通过发送缓冲区链表312将这些发送数据缓冲区链接起来。主机子系统214将发送数据缓冲区描述符写入发送缓冲区链表312中的由主机存储器222中的缓冲区链表写指针314指向的位置处,并且NIC216从发送缓冲区链表312中的由NIC216上的(与主机存储器222中的缓冲区链表读指针316相对应的)缓冲区链表读指针326指向的位置处读取发送缓冲区描述符。发送缓冲区链表312是“环绕”链表,意思是指针连续地递增,然后链表的末端按环状方式自动环绕到起始。如果链表具有N个条目,例如,可以说读和写指针递增“模N”。还可以说指针“模数递增”,这暗含了链表的长度。
与发送缓冲区链表312和发送数据缓冲区310相关联的还有发送事件队列318,它也是个环绕结构。将事件写入到发送事件队列318中的由NIC216上的发送事件队列写指针332标识的位置处,并由主机子系统214从该发送事件队列中的由主机存储器222中的发送事件队列读指针320标识的位置处读取事件。只要有可能,图2的系统就使用事件而非中断作为硬件状态报告方法。为了改进事件递送潜伏期和整体总线效率,由NIC216将事件写出到主机存储器222中,而不是由主机子系统214从NIC216中的位置读取事件。
这里将指向发送缓冲区链表312中的写指针314和读指针316称为“主机中心”,因为它们表示如由主机子系统214观察到的队列状态。在各种实施例中,主机子系统214可以将这些指针进行比较,以根据在该实施例中对于队列深度管理需要什么,来检测队列溢出状况、队列充满状况、或者队列高或低水位标状况。NIC216还保持有分别指向发送数据队列310中的写指针324和读指针326,如下所述。将NIC上的写指针324和读指针326称为“设备中心”指针,因为它们表示如由NIC设备216观察到的队列状态。按常规方式,主机子系统使用它的主机中心缓冲区链表写指针314向发送缓冲区链表312进行写入,在进行了写入之后使主机中心缓冲区链表写指针314模数递增,并通知NIC更新它的设备中心缓冲区链表写指针324。类似地,NIC使用它的设备中心缓冲区链表读指针326从发送缓冲区链表312进行读取,在进行了读取之后使设备中心缓冲区链表读指针326模数递增,并通知主机子系统214更新它的主机中心缓冲区链表读指针316。由此,尽管尽力使得主机中心缓存区链表读指针与设备中心缓冲区读指针、主机中心缓冲区链表写指针与设备中心缓冲区写指针相同步,但是可能存在其中各对中的两个指针并不完全匹配的某个时间点。重要的是,在该简化实施例中,主机子系统214不保持发送事件队列写指针332的主机中心复本。NIC216确实保持有发送事件队列读指针320的设备中心复本344,但是它并不是用于事件队列深度管理的。相反地,如下更详细地描述的,它是用于管理对主机子系统的中断的生成以激活发送事件处理器。
NIC216进一步包括将设备中心发送事件队列写指针332中的值与设备中心发送事件队列读指针344中的值进行比较的比较器346。当这两个值不相等时,比较器346的输出是有效的。NIC216还包括中断使能寄存器位348和逻辑350,当中断使能寄存器位348和比较器346的输出均有效时,逻辑350触发中断发生器352。以下对中断发生组件的操作进行描述。
NIC216除了已经提及的组件之外还保持有发送FIFO340,NIC216将它从发送数据缓冲区310检索的发送数据写入发送FIFO340中。由物理网络接口(PHY)342将从发送FIFO340输出的数据驱动到网络212上。
网络接口卡216通过I/O总线218(及其它手段)与主机存储器222相通信。优选地,I/O总线218是PCI总线,更优选地PCI的版本是PCI express。在PCI Special Interest Group的“PCI Express Base Specification 1.0a”(April15,2003)中描述了PCI express,通过引用将其并入于此。通过核心逻辑子系统224使用直接存储器存取(DMA)协议、经由I/O总线218对大部分数据(包括来自发送数据缓冲区310的发送数据)进行通信,尽管在主机子系统214与NIC216之间的某些通信可以涉及处理器子系统220。
现在参照图4,类似于发送队列,将接收队列存储在主机存储器222中的一系列接收数据缓冲区410中。通过环绕接收缓冲区链表412将这些接收数据缓冲区链接起来。当主机子系统214希望使得附加缓冲区可用于接受接收数据时,它将新接收数据缓冲区的标识写入接收缓冲区链表412中的由主机存储器222中的主机中心缓冲区链表写指针414指向的位置处。NIC216从接收缓冲区链表412中的由NIC216上的(与主机存储器222中的主机中心缓冲区链表读指针416相对应的)设备中心缓冲区链表读指针426指向的位置处读取接收缓冲区描述符。
与接收缓冲区链表412和接收缓冲区410相关联的还有环绕接收事件队列418。将事件写入到接收事件队列418中的由NIC216上的接收事件队列写指针432标识的位置处,并从该接收事件队列418中的由主机存储器222中的接收事件队列读指针420标识的位置处读取事件。对于发送事件队列读指针和写指针,按常规的方式,主机子系统使用它的主机中心缓冲区链表写指针414向接收缓冲区链表412进行写入,在进行了写入之后使主机中心缓冲区链表写指针414模数递增,并通知NIC更新它的设备中心缓冲区链表写指针424。类似地,NIC使用它的设备中心缓冲区链表读指针426从接收缓冲区链表412进行读取,在进行了读取之后使设备中心缓冲区链表读指针426模数递增,并通知主机子系统214更新它的主机中心缓冲区链表读指针416。重要的是,在该简化实施例中,主机子系统214不保持接收事件队列写指针432的主机中心复本。如在发送端那样,NIC216确实保持有接收事件队列读指针420的设备中心复本444,但是它是用于管理对主机子系统的中断的生成以激活接收事件处理器,而不是用于事件队列深度管理。
NIC216进一步包括将设备中心接收事件队列写指针432中的值与设备中心接收事件队列读指针444中的值进行比较的比较器446。当这两个值不相等时,比较器446的输出是有效的。NIC216还包括中断使能寄存器位448和逻辑450,当中断使能寄存器位448和比较器446的输出均有效时逻辑450触发中断发生器452。
NIC216除了图3的发送设备和上述接收端组件之外,还保持有接收FIFO440,NIC216将它从PHY342接收的数据写入接收FIFO440中。NIC216根据接收缓冲区链表412的当前条目将从接收FIFO440输出的数据写入接收数据缓冲区410中。
发送队列操作,简化实施例
在操作中,许多不同的功能并发地操作。图5、6和7是例示了使用图3的结构为了发送数据而执行的功能的流程图。对于这里的所有流程图,应当理解,可以对这些流程图中的许多步骤进行组合,可以并行地执行或在不影响所实现的功能的情况下按不同的次序来执行这些步骤。此外,尽管这里将这些功能描述为按不同的“模块”来执行,但是应当理解,如果存在的话,实际实现并不一定要按相同的方式来“模块化”。
在图5中,主机发送事件管理模块从更高级软件接收到一指示:发送数据缓冲区310中的一个或更多个新缓冲区等待发送(步骤510)。还可以在轮询循环或定时器到期时定期地激活该模块(步骤512)。典型地,是以外界为基础,什么时候去推发送缓冲区的时间和同时推多少发送缓冲区,对于本发明的理解来说不是重要的。仅仅只要更高级软件已经填满发送缓冲区,就不再推发送缓冲区。
在步骤514,为了保证发送事件队列318不溢出,主机子系统214确定在发送事件队列318中的当前可用空间。主机子系统214将不会排队比按照将要生成的发送完成事件的最大量提供到发送事件队列318中的数据缓冲区多的用于发送的数据缓冲区。通常,通过B*(x-z)-y得出主机子系统排队用来发送的数据缓冲区的最大量,其中x是在发送事件队列318中的条目的总数;z是可能写入事件队列318的其它类型的事件的预定最大量,例如管理事件;y是先前排队的并且仍然未被处理的发送数据缓冲区的数量;以及B是在发送事件队列318中由每个发送完成事件所描述的发送数据缓冲区的最小量。
x的值是在发送事件队列318中的条目的总数,在一个实施例中被预定和固定。在另一个实施例中,主机子系统214可以确定并且更改发送事件队列大小x,依靠诸如多快能在网络212上产生给定的数据速率的事件,以及基于多快主机子系统能够处理接收到的事件的这些因素。
在一个实施例中,z的值是管理事件的预定最大量,将允许NIC216向发送事件队列318写入而不需要被清除。更适宜地,通过在NIC216中的管理事件资源的总数而得出z,假定每个资源可以断言只是一个事件而不被清除;可以丢弃或者保存来自上述资源的附加事件。更一般地,z是Ei(i=1..s,s是管理事件资源的数量)的和,并且Ei(i=1..s)是事件的最大量,每个第i个这样的资源可以在任何给定的点及时地具有未被处理的事件。
B是发送数据缓冲区的最小量,可以通过在发送事件队列318中的每个发送完成事件进行描述,在一个实施例中B是一(B=1)。也就是,每个被发送到NIC216的发送数据缓冲区生成单个相应的发送完成事件。在另一个实施例中,每个发送完成事件总是暗含地描述大于1的发送数据缓冲区的固定数量,例如64;在这种情况下,B是上述固定的数量。在这样一个实施例中,被发送到NIC216的每个设置的B的发送数据缓冲区生成一个相应的发送完成事件。然而在第三实施例中,每个发送完成事件描述一个发送数据缓冲区已完成的变量,而不是大于预定的最大值,也不是小于预定的最小值;在这种情况下,B是最小量。发送完成事件自身指示描述的发送数据缓冲区的数量。即使在第三实施例中,B也可以与1一样小。
在一优选实施例中,通过主机子系统214,B是可编程的,并且其后是固定的。该实施例,与像变量实施例相比,更像“固定”实施例,因为通过发送完成事件所描述的发送数据缓冲区的数量是不变的,并且主机和NIC都知道这是什么。决不会出现变量实施例的复杂性。
y的值是先前排队的并且仍然未被处理的发送数据缓冲区的数量,不是基于发送事件队列读指针和写指针的任何比较来确定,而是基于按照主机子系统先前已经“已许可的”NIC216进行写入的发送事件队列条目的数量的主机子系统214的历史消息,以及依据主机子系统已经“已耗用的”的这些发送事件队列条目的数量的主机子系统214的历史消息来确定。主机“许可”NIC216通过通知NIC216发送数据的行为来写发送事件队列条目。主机通过将发送数据描述符(也可以被认为是DMA命令)推入到发送缓冲区链表312来发布这些指示。这样,先前“已许可的”NIC216进行写入的发送事件队列条目的数量的主机历史消息,是可以通过先前排队用来发送的数据缓冲区的数量来确定。特别地,先前已许可的发送事件队列条目的数量等于1/B乘以先前排队用来发送的发送数据缓冲区的数量。主机子系统已经“已耗用的”的发送事件队列条目的数量的主机历史消息只是1/B乘以将通过在发送事件队列318中实际接收到的发送完成事件所描述的发送数据缓冲区传输的数量,但是只限于在本实施例中,通过每个发送完成事件所描述的发送数据缓冲区的数量是固定的。在这种情况下,主机子系统214确定y作为发送数据缓冲区的数量,该发送数据缓冲区先前排队用来发送,y减去B乘以在发送事件队列318中实际接收到的发送完成事件的数量。在一个实施例中,通过从主机中心发送缓冲区链表写指针314模数递减主机中心发送缓冲区链表读指针316来确定y。
在一个实施例中,通过每个发送完成事件来描述发送数据缓冲区的数量是可变的,主机的主机子系统已经使用的发送事件队列条目的数量的历史消息,不是通过任何固定的实际接收到发送事件队列318中的发送完成事件所描述的发送数据缓冲区传输的数量来决定的。在那样的情况下,使用另一个机制来做出这个决定。
在确定了在发送事件队列318中的当前可用空间的数量后,在步骤516,主机子系统214确定数量“M”,其是待发送数据缓冲区的数量和发送数据缓冲区的最小值两者的较小者,可以通过如在步骤514确定的发送事件队列318中的可用空间的发送完成事件来进行描述。
在步骤518中,确定M大于还是等于某个最小阈值。在一个实施例中,该阈值为1,这意味着只要新发送数据缓冲区准备好进行发送并且在发送事件队列318中存在任何可用空间就许可发送事件队列318的事件。然而,逐个地许可事件会对I/O总线218增加显著的额外开销,因此在更优选的实施例中,选择较大的数作为阈值。如果M小于该阈值,则主机发送事件队列管理模块500仅仅变得不活动,以等待下一激活事件(步骤530)。
如果M大于或等于该最小阈值,那么在步骤520中,主机子系统214“许可”NIC写入M/B个新发送事件到发送事件队列318中。这个许可通过在发送缓冲区链表312中的待发送数据缓冲区描述符的写入而暗含地执行,因而要求在I/O总线218上较少或者没有额外开销。特别地,步骤520包括步骤522,在步骤522中主机子系统214将其主机中心缓冲区链表写指针314更新(模数递增)M个条目。在步骤524中,以先前(在步骤522之前)由主机中心缓冲区链表写指针314指定的条目为起点,主机子系统214将M个待发送数据缓冲区描述符写入发送数据缓冲区链表312中。主机中心缓冲区链表写指针的更新先于M个发送数据缓冲区描述符的队列,为了避免由此在主机子系统214在新发送数据缓冲区描述符排队后变成隐藏而产生的竞争状况,并且NIC在主机214返回更新读指针前检索发送数据缓冲区内容。在步骤526中,主机子系统214将所更新的写指针通知给NIC216,在步骤528中,NIC216更新它自己的设备中心缓冲区链表写指针324。在一个实施例中,将步骤526与528组合成单个步骤,在该步骤中,主机子系统214将所更新的写指针写入设备中心发送缓冲区链表写指针324的存储器映射位置中。
在步骤530中,主机发送事件队列管理模块变得不活动,以等待下一激活事件。可以看出,NIC216暗含地知道来自设备中心发送缓冲区链表读指针326和写指针324间的模数差异的发送事件队列318中的空间的可用性。NIC知道可以将许多发送事件写入发送事件队列318中直到溢出,因为主机子系统214不将缓冲区描述符写入发送缓冲区链表312中,除非在发送事件队列318中的已经可用的空间用来提供任何作为结果的发送完成事件。
图6是例示了在NIC216上发起的用于将数据发送到网络212上的功能的流程图。在TX FIFO低水位标(LWM)状况610上激活NIC发送数据模块600。在通过轮询循环进行选择时或定时器到期时也定期地激活该模块(步骤612)。
在步骤620中,NIC216首先确定在发送缓冲区链表312中并且待检索的发送数据缓冲区描述符的数量。通过从设备中心缓冲区链表写指针324模数减去设备中心缓冲区链表读指针326,来进行该确定。NIC还根据它自己的指向TX FIFO340中的读和写指针知道在它自己的TX FIFO340中的可用空间。在步骤622中,NIC确定M,待发送缓冲区描述符的数量和TX FIFO340中的可用空间中的较小者。因此M是当前可以被复制到TX FIFO340中的发送数据缓冲区描述符的数量。
在步骤626中,NIC216进行到以由设备中心缓冲区链表读指针326表示的条目为起点从发送缓冲区链表312中读取M个缓冲区描述符。在步骤628中,NIC从主机存储器中的发送数据缓冲区310中的由缓冲区描述符标识的缓冲区检索数据。由于通过DMA经由I/O总线218执行从发送数据缓冲区310的数据检索,因此这里有时将发送缓冲区描述符称为DMA描述符或DMA命令。对M个缓冲区描述符本身的检索也是通过DMA经由I/O总线218来执行。
注意,在不同的实施例中,对M个缓冲区描述符的读取可以与对发送数据的读取相交织或按流水线式进行,而不是作为独立的原子步骤(atomic step)来执行。还要注意的是,从由已存在于FIFO340中的缓冲区描述符标识的发送数据缓冲区中检索数据不是等到低水位标或阈值数量的缓冲区进行检索。只是延迟从发送缓冲区链表312中对缓冲区描述符的检索,以进行批处理;并且如果NIC216处理完了待发送数据,也不为了进行批处理而使这些描述符延迟。
在步骤630中,NIC对设备中心缓冲区链表读指针326进行更新。在一实施例中,因为由于完成事件递送的结果,隐式或显式地进行通知,NIC216不对相对应的主机中心缓冲区链表读指针316显式地进行更新。
在图6的实施例中,每个发送完成事件都可以表示多个数据缓冲区完成。除了在某些下述更不常见的情况下以外,由每个发送完成事件表示的数据缓冲区完成的数量是可编程的,因而是固定数量B。因而NIC216“批处理”发送完成事件到一,从而使I/O总线218的使用最优化。另外,在某些常规DMA命令队列执行中,队列空警报的发现和报告不会发生直到控制器尝试检索下一DMA命令和发现队列空,当NIC216认为在发送队列中它已经使用最后的缓冲区描述符时,NIC216改为检测和报告一个警报。这个“检索的最后的缓冲区”状况与最后的批处理发送完成事件组合成单个事件描述符,从而进一步使I/O总线218的使用最优化。图6的接下来的步骤例示了这两个最优化。
特别地,NIC确定在步骤640中它是否认为是通过在步骤628已经检索的在发送缓冲区链标312中的描述符所标识的最后的发送数据缓冲区。由NIC216在步骤626中从发送缓冲区链表读取的缓冲区描述符的数量,等于在步骤620中在发送缓冲区链表中的发送缓冲区描述符的数量,如果该数量是M则这种状况是正确的。如果不是,也就是NIC知道在发送缓冲区链表312中有更多的发送缓冲区描述符,则在该指示下,NIC写入发送事件队列318的任何发送完成事件将不具有规定的tx_desc_q_empty标记。
在步骤642中,NIC对N与B进行比较,N是表示除在先前写入发送事件队列318中的批处理发送完成事件中已报告的那些发送缓冲区以外的已从主机存储器中检索的发送缓冲区的数量的值。如果N<B,则NIC发送数据模块600仅仅变得不活动,以等待下一激活事件(步骤644)。只是累积未被处理的发送缓冲区完成,并且也不报告给主机子系统214,直到下一次写入发送完成事件。另一方面,如果在步骤642中N>=B,则在步骤646中,以由设备中心发送事件队列写指针标识的条目为起点,NIC216将表示B个发送数据缓冲区的整数倍(总共)的批处理发送完成事件写入发送事件队列318中。因此在步骤646中写入的批处理发送完成事件的数量是N/B的整数部分,并且不将任何余数报告给主机子系统214,直到下一次写入这种完成事件时。该发送完成事件描述符格式包括一个标记“tx_desc_q_empty”标记,但是在步骤646中写入的发送完成事件中,这个标记不被放置,因为NIC已经确定(在步骤640中)附加发送缓冲区描述符保存在发送缓冲区链表312中。
在一实施例中,NIC确定N=B,并且只要NIC的设备中心缓冲区链表写指针324是B的整数倍,就写入批处理发送完成事件。
注意,当完成了将数据从主机存储器222传送到发送FIFO340中时,NIC216将发送完成事件写入发送事件队列318中。它不进行等待,直到将数据实际发送到网络212上,这是因为更高级应用软件典型地对发送错误不感兴趣。另一实施例不将“发送完成”通知给主机子系统,直到在将数据发送到网络上的处理中发生了随后步骤,例如网络接口设备完成了将数据发送到网络上。在这里使用的,如果在特定实施例中,对这种下游步骤的通知意味着NIC已经完成了从发送数据缓冲区中检索数据,则将这种通知视为“包括”步骤,即向主机子系统通知:网络接口设备完成了从发送数据缓冲区中检索数据。
返回到步骤640,如果NIC216认为是通过在步骤628中在发送缓冲区链表312中的描述符标识的最后的发送数据缓冲区,则NIC在报告发送完成事件之前不用等待,直到N>=B。而是,以设备中心发送事件队列写指针332为起点,NIC向发送事件队列318写入充足的批处理发送完成事件,以覆盖所有的N个未被处理的缓冲区完成。从而如果N是B的整数倍,则在步骤648中写入N/B个发送完成事件,如果N不是B的整数倍,则在步骤648中写入INT(N/B)+1个发送完成事件。无论在步骤648中写入的发送完成事件的数量是多少,最后的发送完成事件都将具有它自己规定的tx_desc_q_empty标记。
应当理解,N不是B的整数倍时,在步骤648中写入的最后的发送完成事件可以表示少于B个发送缓冲区完成。注意,在一个实施例中,没有专门的通知用于主机知道这些,因为主机已经知道发送缓冲区链表它自己的深度。主机知道每个发送完成事件表示B和保存在发送缓冲区链表中的描述符的数量的较小者。然而,在另一个实施例中,只依靠保存在发送缓冲区链表中的描述符的数量来确定由最后的完成事件描述的缓冲区完成的数量,可能创建竞争状况,例如,如果在步骤626中的NIC216从链表最后更新缓冲区描述符之后,主机将更多的缓冲区描述符在发送缓冲区链表312中排队。为了保护避免这样的竞争状况,发送完成事件格式还包括字段(tx_desc_ptr),在该字段中NIC216复制其设备中心缓冲区链表读指针326。主机可以确定通过来自1/B的小数部分乘以如在发送完成事件报告的设备中心缓冲区链表读指针与主机中心缓冲区链表读指针316的差的最后的完成事件所描述的缓冲区完成的数量。二者择一地,主机可以只是通过如在发送完成事件报告的设备中心缓冲区链表读指针更新它的主机中心缓冲区链表读指针316,而不是使用通过接收的发送完成事件所描述的缓冲区完成的数量的单独计算。
在多个实施例中,除发送描述符队列空状况之外的其它状况还可以导致NIC写入比B个发送缓冲区覆盖少的发送完成事件。一个这样的其它状况,可能是发送队列直接操作的完成。对于每个这样的状况,提供一种机制,使主机子系统能够知道在发送缓冲区链表312中由批处理发送完成事件覆盖哪些描述符。同样在多个实施例中,还可以批处理除了数据发送完成事件以外的其它重复事件。候选例子包括(无限制)事件通知多个结构完成操作的主机子系统,这样的主机命令对缓冲区描述符表1310中条目进行更新,在以下将描述。
如果在步骤646或648中将任何发送完成事件写入发送事件队列318中,则在步骤634中NIC216相应地更新它自己的发送事件队列写指针。在步骤634中对设备中心发送事件队列写指针332的更新可能会导致产生一中断(步骤636)以激活参照图7讨论的主机发送事件处理器。如可以从图3中的逻辑看到的,如果设备中心发送事件队列写指针332先前等于设备中心发送事件队列读指针344,则在步骤634中进行的更新会导致比较器346进行输出以过渡到活动状态。如果在出现该情况时中断使能位348是有效的,则中断发生器352将产生中断。如果在步骤634中进行更新时所述读指针和写指针先前不相等,则不会产生新的中断,因为比较器346输出将已经处于活动状态。注意,在某些情况下对写指针332的更新会导致比较器346过渡到不活动状态,尤其是如果该写指针已绕回并且就要追上读指针。但是这不是逻辑350需要处理的情况,因为,如下所述,将算法设计成使得如果出现了该情况则中断使能位348总是不活动的。
在步骤636之后,NIC发送数据模块600失活(步骤644)。
如上所述,NIC216知道在步骤632的循环可以向发送事件队列318写入,只要需要的发送完成事件的数量能够表示M个发送数据缓冲区的完成,甚至与设备中心发送事件队列读指针344无关。NIC216知道这是因为主机子系统214不再向发送缓冲区链表312写入缓冲区描述符,除非在发送事件队列318中用于发送完成事件的充足数量的空间已经是可用的,以表示所有未被处理的发送数据缓冲区的完成。
同样如上所述,NIC不显式地通知主机子系统214已更新的发送事件队列读指针值,因为主机不再保持有发送事件队列写指针的当前样式。主机不是通过比较在发送事件队列318中的当前读指针和写指针,而是通过比较如先前所述的在发送缓冲区链表312中的主机中心读指针316和写指针314,来确定在发送事件队列318中的可用空间。
图7是例示了主机发送事件处理器模块700的相关功能的流程图。要么在接收到中断时(步骤710)、要么在步骤636(图6)中产生中断时、要么在通过轮询循环定期地进行选择时或者在定时器到期时(步骤712)激活模块700。
初步地,主机发送事件处理器执行一个破损安全机制用来保证主机子系统214和NIC216适当地操作关于发送事件队列318的情况。特别地,在创建时间,在发送事件队列318中的所有的条目初始化到“清空状态”值。在一个实施例中,该清空状态是0,而在另一个实施例中,可以使用清空状态的不同表示。写入事件队列318的时间描述符不同于该清空状态。只要从事件队列318检索事件并且已处理,主机子系统214将向事件队列的那些条目中写入清空状态值。随后,在处理后续事件之前,主机子系统214从刚才处理的条目中重新检索清空状态值,并且检查它是否仍然处于清空状态。如果没有,则NIC216已经或者将要使发送事件队列318溢出。如果主机子系统214和NIC是SW,并且都精确地遵循这里所述的协议,这种溢出将永远不会发生,但是在某些实施例中,这种破损安全机制是需要的。
因此,在步骤714中,主机子系统214首先在事件队列318中的由事件队列读指针320指定的一个当前位置之前的位置处检索事件描述符。这个位置是在发送事件队列318中检索最后的处理事件的位置。在步骤716中,确定事件描述符是否保持在它的清空状态。如果不是,则报告队列溢出状态(步骤718)。
如果重新检索的事件描述符保持在它的清空状态,则在步骤720中,主机子系统214在事件队列中的由发送事件队列读指针320指定的位置处检索事件描述符。如果该新事件处于清空状态(步骤722),则发送事件队列318还不是空的。在步骤726中,确定新事件是否为发送完成事件。在一个实施例中,发送事件队列318不能含有除发送完成事件以外的任何事件,但是在另一个实施例中它可以如此。由此,如果当前事件是除发送完成事件以外的事件,如管理事件,则在步骤728中对其进行处理。
如果当前事件是发送完成事件,则依据该实施例,它可以指示用于固定或者可变数量的发送数据缓冲区的数量递送的完成。在任意一种情况中,在步骤730中,主机子系统214使发送缓冲区链表312的主机中心缓冲区链表读指针316模数递增在当前发送完成事件中表示的缓冲区数量。这是主机子系统214借以知道NIC216已经更新了它自己的设备中心缓冲区链表读指针326的机制。然而,在一实施例中,NIC可以在发送完成事件中显式地指定它的已更新设备中心缓冲区链表读指针326。可以使用后一机制来替代前一机制,或者连同前一机制一起使用后一机制。
而且,在步骤730中,一旦主机已经使主机中心缓冲区链表读指针316递增得超过了特定发送数据缓冲区描述符,它还将所标识的发送缓冲区释放回库中。最终,在更高级软件使用新数据再填充了该缓冲区之后,主机会将数据缓冲区描述符再写入发送缓冲区链表312中以再次使其入队,以通过NIC216进行发送。
在步骤732中,主机子系统214清除发送事件队列318中的由当前发送事件队列读指针标识的位置处的事件描述符,因为先前解释的理由,在步骤734中,主机子系统214使发送事件队列读指针320模数递增。然后该模块返回到步骤720以检索下一事件描述符,以此类推,直到检索一清空的条目然后该模块变得不活动(步骤724)。
如果在步骤722中确定所检索的下一事件描述符是清空的,则发送事件队列318此时不再含有其它待处理事件。在一个实施例中,则主机发送事件处理器700将只变得不活动,以等待下一激活触发(步骤724)。至少为了队列深度管理的目的,不需要主机子系统214通知NIC216已更新的主机中心发送事件队列读指针,因为NIC避免发送队列事件318的溢出,不是通过比较在发送事件队列318中的当前读指针和写指针,而是通过不再许可(通过主机)向发送事件队列318写入比先前所述的由可用空间积累的发送描述符更多的发送事件描述符。
然而,在一个优选实施例中,主机存储器子系统确实如此通知NIC216作为如以下更详细地描述的一种管理事件队列中断的手段。因而,在步骤723中,如果主机中心发送事件队列读指针320已发生了变化,则主机将所更新的指针值写入NIC的设备中心发送事件队列读指针中。然后在步骤724中主机发送事件处理器700变得不活动。
接收队列操作,简化实施例
图8到11是例示了用于使用图4的结构接收数据而执行的功能的流程图。接收队列操作在许多方面与如上所述的发送队列操作是类似的,因此这里将略去已经描述的操作的某些方面。在图8中,主机接收事件管理模块从更高级软件接收如下指示:接收数据缓冲区410中的新数据缓冲区是空的并且可用于接收数据(步骤811)。还响应于主机对以下描述的接收缓冲区链表空事件的接收而激活该模块(步骤810)。还可以在轮询循环或定时器到期时定期地激活该模块(步骤812)。在步骤814中,主机子系统214确定再接收事件队列418中的当前可用空间的数量。与发送事件队列318一样,首先基于外界因素什么时候去推接收缓冲区的时间对于理解本发明来说不是重要的。主机子系统214将推接收缓冲区到接收缓冲区链表达到根据其它因素所请求的程度,但是是有限制的,在这里所述的方式中,使得保证接收事件队列418不会溢出。因而,主机子系统214将不会让比在接收事件队列418中积累的由将要生成的接收完成事件的数量多的用来接收数据的数据缓冲区排队。通常,通过B*(x-z)-y得出主机子系统214排队用来接收数据的数据缓冲区的最大量,其中x是在接收事件队列418中的条目的总数,z是可能写入事件队列418的其它类型的事件的预定最大量,y是先前排队的并且仍然未被处理的接收数据缓冲区的数量;以及B是在接收事件队列418中由每个接收完成事件所描述的接收数据缓冲区的最小量。
在多个实施例中,以上关于发送队列的操作的那些描述,可以同样地解释x、y、z和B的值。它们不需要具有相同的值。特别的关于y,先前排队的并且仍然未被处理的接收数据缓冲区的数量,该值不是基于接收事件队列读指针和写指针的任何比较来确定,而是基于依据主机子系统先前已经许可的NIC216进行写入的接收事件队列条目的数量的主机子系统214的历史消息,以及依据主机子系统已经使用的这些接收事件队列条目的数量的主机子系统的历史消息来确定。先前已许可的NIC216进行写入的接收事件队列条目的数量的主机历史消息,是可以通过先前排队用来接收的接收数据缓冲区来确定。因而,主机子系统214确定y作为先前排队用来接收的数据缓冲区的数量,y减去通过在接收事件队列418中实际接收到的接收完成事件所描述的数据缓冲区的数量。在一个实施例中,通过从主机中心缓冲区链表写指针414模数递减主机中心缓冲区链表读指针416来确定y。
在确定了在接收事件队列418中的当前可用空间的数量后,在步骤816,主机子系统214确定数量“M”,其是用于排队的接收数据的可用数据缓冲区的数量和接收数据缓冲区的最小值两者的较小者,可以通过如在步骤814确定的接收事件队列418中的可用空间的接收完成事件来进行描述。
在步骤818中,确定M大于还是等于某个最小阈值。优选地,该最小阈值为1,但是在其它实施例中选择较大的数量作为该阈值。如果M小于该阈值,则主机接收事件队列管理模块800仅仅变得不活动,以等待下一激活事件(步骤830)。
如果M大于或等于最小阈值,则在步骤820中,主机子系统214“许可”NIC写入M/B个新接收事件到接收事件队列418中。这个许可通过在接收缓冲区链表412中的待接收数据缓冲区描述符的写入而暗含地执行,因而要求在I/O总线218上较少或者没有额外开销。特别地,步骤820包括步骤822,在步骤822中主机子系统214将其主机中心缓冲区链表写指针414更新(模数递增)M个条目。在步骤824中,以先前(在步骤822之前)由主机中心缓冲区链表写指针414指定的条目为起点,主机子系统214将M个可用接收数据缓冲区描述符写入接收缓冲区链表412中。同在发送端一样,主机中心缓冲区链表写指针的更新先于M个接收数据缓冲区描述符的队列,为了避免由此在主机子系统214在新接收数据缓冲区描述符排队后变成隐藏而产生的竞争状况,并且NIC在主机214返回更新读指针前将接收数据写入这些缓冲区。在步骤826中,主机子系统214将所更新的写指针通知给NIC216,并且在步骤828中,NIC216更新它自己的设备中心缓冲区链表写指针424。在一个实施例中,将步骤826与828组合成单个步骤,在该步骤中,主机子系统214将所更新的写指针写入设备中心接收缓冲区链表写指针424的存储器映射位置中。
在步骤830中,主机接收事件队列管理模块变得不活动,以等待下一激活事件。可以看出,NIC216暗含地知道来自设备中心读指针426和写指针424间到接收缓冲区链表412的模数差异的接收事件队列418中的空间的可用性。NIC知道可以将许多接收事件写入接收事件队列418中直到溢出,因为主机子系统214不将缓冲区描述符写入接收缓冲区链表412中,除非在接收事件队列418中的已经可用的空间用来提供任何作为结果的接收完成事件。
图9是例示了当从网络212接收到数据时在NIC216上发起的功能的流程图。将输入数据置于RX FIFO440中,当达到高水位标时,激活NIC接收数据模块900(步骤910)。与发送端不同,NIC216不将固定数量个接收数据缓冲区完成批处理到各接收完成事件中。然而,在一个实施例中,它确实进行等待,以在一数据包完成时断言单个接收完成事件,即使该数据包占据一个以上接收数据缓冲区。
在步骤912中,NIC216检索下一接收数据缓冲区的描述符,并从由设备中心缓冲区链表读指针426指定的接收缓冲区链表412的条目起进行偏移。在步骤914中,NIC216对它的设备中心缓冲区链表读指针进行更新(模数递增)。此时NIC不将新的读指针通知给主机214,因为如下所述将通过递送隐式或显式地进行该通知。
在步骤916中,以所指定的偏移量为起点,NIC216将输入数据包的数据写入由所检索的描述符指定地接收数据缓冲区中。通过DMA继续进行写入,直到达到当前数据缓冲区的末端或达到输入数据包的末端,或这两者。
同在发送端一样,当NIC216认为在接收队列中它已经检索并且已经使用描述符时,NIC216检测并且报告队列空警报。该警报与接收完成事件组合成单一事件描述符,从而进一步使I/O总线218的使用最优化。
特别地,NIC216确定在步骤918中它是否认为是通过在步骤628已经使用的在接收缓冲区链表412中的描述符所标识的最后的接收缓冲区。NIC216可以通过对比其设备中心缓冲区链表读指针426与其设备中心缓冲区链表写指针424来确定这个。如果不是,也就是NIC知道在接收缓冲区链表中有更多的接收缓冲区描述符,则不需要警报,并且在步骤920中,NIC确定是否到达了数据包末端。若否,则NIC接收数据模块900返回到步骤912,以检索下一接收数据缓冲区的描述符。在本实施例中不断言任何事件以表示“接收数据缓冲区已满”。基于在接收缓冲区链表412中以主机中心RX队列读指针为起点的连续标识的多个接收数据缓冲区,主机214将变得知道哪个接收数据缓冲区是满的。
如果步骤920确定已到达数据包末端,则在步骤922中NIC216断言一接收完成事件,以覆盖含有来自数据包的数据的所有接收数据缓冲区。同在发送一样,接收完成事件描述符格式包括接收描述符队列空标记(“rx_desc_q_empty”),但是在步骤922中写入的接收完成事件中,该标记不被放置,因为NIC已经确定(在步骤918中)附加接收缓冲区描述符保存在接收缓冲区链表412中。注意,在本实施例中,即使数据包数据跨过接收数据缓冲区410中的多个缓冲区,也只断言一个接收完成事件。由接收缓冲区链表412中的多个连续条目将多个缓冲区链接在一起。还要注意,如果数据包末端与接收缓冲区的末端不相一致,则保留缓冲区中的剩余空间,而不使用它。
返回到步骤918,如果NIC216认为通过在接收缓冲区链表412中的描述符所标识的最后的接收数据缓冲区已经在步骤912中检索,则NIC不再等候,直到数据包末端在报告接收完成事件之前。相反,在步骤924中,NIC断言一接收完成事件,以覆盖含有来自包的数据的所有接收数据缓冲区。在该接收完成事件中,放置rx_desc_q_empty标记。如果当这个发生时,数据包数据保存在NIC的RX FIFO440中,则该数据包数据丢失。主机子系统214可以通过将在接收完成事件描述符中(总是含有全包的字节计数)的接收数据包字节计数字段与由接收完成事件覆盖的接收数据缓冲区中的字节的数量进行比较,来检测包数据的丢失。同在发送端一样,主机(在一个实施例中)知道由接收完成事件覆盖的接收数据缓冲区是由在接收缓冲区链表412中的描述符标识的、以由主机中心缓冲区链表读指针416指向的条目为起点并在由主机中心缓冲区链表写指针414指向的条目之前终止的那些接收数据缓冲区。在另一实施例中,类似于如上所述的关于发送端一样,通过在接收完成事件描述符格式中包括一附加字段以含有如在步骤914中由NIC216更新的设备中心缓冲区链表读指针426的复制,可以避免竞争状态。然后主机可以将由接收完成事件覆盖的接收数据缓冲区确定为由接收缓冲区链表412中的多个描述符标识的、在如在接收完成事件中报告的主机中心缓冲区链表读指针416与设备中心缓冲区链表读指针之间的那些接收数据缓冲区。
在另一实施例中,NIC支持一个以上网络端口。该实施例中对接收完成事件批处理是更加困难的,因为NIC可以相对于接收缓冲区在接收缓冲区链表412中放置的次序,不连续地填充接收缓冲区。也就是,NIC可以用来自网络端口1的部分数据包来填充由接收缓冲区N标识的缓冲区,则用网络端口2的全部数据包来填充由接收缓冲区N+1标识的缓冲区。此外来自网络1数据包的数据将进入后续数据缓冲区。如果再该实施例中将要批处理接收完成事件,则NIC可以在断言用于来自网络端口1的数据包的完成事件之前断言用于来自网络端口2的数据包的完成事件。如果NIC的用于来自网络端口2的数据包的接收完成事件是数据包括指示最后描述符已使用的它的已更新的接收缓冲区链标读指针,并且如果接收完成事件是表示由接收缓冲区链表412中的多个描述符标识的、以由主机中心缓冲区链表读指针416指向的条目为起点并在由接收完成事件描述符中的设备中心接收缓冲区链表读指针指向的条目终止的所有的数据缓冲区的完成,则主机可能错误地认为由接收描述符N标识的缓冲区和由接收描述符N+1标识的缓冲区含有来自相同数据包的数据。
为了避免这种情形,该实施例确实不批处理接收完成事件。接收完成事件不表示一个以上接收数据缓冲区的完成。该实施例支持标准大小数据包(其中数据包具有相对小的最长长度并且所述多个接收数据缓冲区至少与该最大数据包长度一样大)和“特大”数据包(其中数据包可以更长并且可以跨过一个以上数据缓冲区)。给定的接收队列要么是标准模式的要么是特大模式的。如果该队列是标准模式的,则不存在错误,每个已充满接收数据缓冲区都将含有一数据包末端,因此没有接收完成事件会表示一个以上数据缓冲区的完成,因而不会出现问题。如果队列是特大模式的,则还是该情况,即没有接收完成事件会表示一个以上数据缓冲区的完成,因为对于NIC填充的每个数据缓冲区,它写入一接收完成事件。该接收完成事件格式包括“RX_Jumbo_Cont”位,NIC设置该位以通知主机子系统:主体数据缓冲区不含有数据包末端(即,将存在延续缓冲区)。因而该实施例不对接收完成事件进行批处理。接收完成事件还包括NIC的已更新设备中心接收缓冲区链表读指针426的复本,该指针现在指向来自接收缓冲区链表412的特定描述符,对于该特定描述符的数据缓冲区,所述事件表示完成。接收完成事件格式还表示从其接收了数据包的NIC端口号。
回到图9的实施例,在步骤924和922之后,一旦NIC已经断言一接收完成事件,NIC接收数据模块900就回到不活动状态(步骤926)。
在步骤924和922中,NIC断言含有特定信息的接收完成事件。图10是该步骤的流程图详情。在步骤1010中,以由设备中心接收事件队列写指针标识的条目为起点,NIC216将该接收完成事件写入接收事件队列418中。在步骤1012中,NIC216相应地更新它自己的接收事件队列写指针。如上所述,NIC216知道可以向接收事件队列418写入,只要需要的接收完成事件的数量能够表示M个接收数据缓冲区的完成,甚至与设备中心接收事件队列读指针444无关。NIC216知道这是因为主机子系统214不再向接收缓冲区链表412写入缓冲区描述符,除非在发送事件队列418中用于接收完成事件的充足数量的空间已经是可用的,以表示所有未被处理的接收数据缓冲区的完成。
同样如上所述,NIC不显式地通知主机子系统214已更新的接收事件队列读指针值,因为主机不再保持有接收事件队列写指针的当前样式。主机不是通过比较在接收事件队列418中的当前读指针和写指针,而是通过比较如先前所述的在接收缓冲区链表412中的主机中心读指针416和写指针414,来确定在接收事件队列418中的可用空间。
然而,同在发送端一样,在步骤1012中对设备中心接收事件队列写指针432的更新可能会导致产生一中断(步骤1014)以激活参照图11讨论的主机接收事件处理器。如可以从图4中的逻辑看到的。如果设备中心接收事件队列写指针432先前等于设备中心接收事件队列读指针444,则在步骤634中进行的更新会导致比较器446进行输出以过渡到活动状态。如果在出现该情况时中断使能位448是有效的,则中断发生器452将产生中断。如果在步骤634中进行更新时所述读指针和写指针先前不相等,则不会产生新的中断,因为比较器446输出将已经处于活动状态。同发送端一样,逻辑450需要处理其中对写指针432的更新导致比较器446过渡到不活动状态的情况,因为将算法设计成使得如果出现了该情况则中断使能位448总是不活动的。
图11是例示了主机接收事件处理器模块1100的相关功能的流程图。要么在接收到中断时(步骤1110)、要么在步骤1014(图10)中产生中断时、要么在通过轮询循环定期地进行选择时或者在定时器到期时(步骤1112)激活模块1100。
最初,主机接收事件处理器执行一个与在发送端执行的相同的破损安全机制,用来保证主机子系统214和NIC216适当地操作关于接收事件队列418的情况。从而在创建时间,在接收事件队列418中的所有的条目初始化到清空状态值。在步骤1114中,主机子系统214首先在接收事件队列418中的由事件队列读指针420指定的一个当前位置之前的位置处检索事件描述符,并且在步骤1116中,确定事件描述符是否保持在它的清空状态。如果不是,则报告队列溢出状态(步骤1118)。
如果重新检索的事件描述符保持在它的清空状态,则在步骤1120中,主机子系统214在事件队列中的由接收事件队列读指针420指定的位置处检索事件描述符。如果该新事件不处于清空状态(步骤1122),则接收事件队列418此时含有待处理事件。在步骤1126中,确定该新事件是否为接收完成事件。在一个实施例中,接收事件队列418不能含有除接收完成事件以外的任何事件,但是在另一实施例中它可以如此。由此,如果当前事件是除接收完成事件以外的事件,如管理事件,则在步骤1128中对其进行处理。
如果当前事件是接收完成事件,则在步骤1140中,主机214确定是否设置“接收缓冲区链表空”标记。如果是,则该模块(在步骤1142中)触发主机接收事件队列管理模块800,以用附加接收数据缓冲区补充接收缓冲区链表412。在步骤1144中,主机214进一步确定是否有任何的多种错误字节由接收完成事件描述符表示。如果有,则在步骤1146中,主机214对错误进行处理。注意,事实上可以在步骤1140的接收缓冲区链表空测试前后检测在步骤1144中包括的某些错误字节,某些可能准备暂时绕开接收缓冲区链表412中的接收缓冲区描述符的补充(由步骤1142触发的),并且某些可能绕开在步骤1148中对数据包的处理。对于本发明的理解来说这些错误处理的详情是不重要的。
在步骤1148中,假如没有检测出严重的错误,主机214对重新接收的数据包数据进行处理。这可能需要将由多个连续接收缓冲区链表条目指定的几个接收数据缓冲区顺序地链接起来。主机214根据接收缓冲区链表412中的由主机中心缓冲区链表读指针416指向的缓冲区描述符知道数据包的起始缓冲区和偏移量,并且根据在接收完成事件中标识的接收数据包字节计数或者根据在接收完成事件中可能包括的设备中心缓冲区链表读指针426的复本,知道数据包的终点。在处理了这些缓冲区中的数据包数据之后,主机可以将这些缓冲区释放回库中,以最终将它们再写入接收缓冲区链表412中以由不同的输入数据包数据来再使用。
在步骤1150中,如果更高级软件被如此地设计,则主机子系统214可以使用新的可用接收数据缓冲区的描述符对由主机中心缓冲区链表读指针416指向的接收缓冲区链表412条目进行再编程,并且可以针对如下条目进行同样的处理:所有连续的后续接收缓冲区链表条目,直到但不包括指向下一接收数据包的数据的起点的接收缓冲区链表条目。在步骤1130中,主机子系统214使接收缓冲区链表412的主机中心缓冲区链表读指针416模数递增在当前接收完成事件中表示的缓冲区数量。同在发送端一样,这是主机子系统214借以知道NIC216已经更新了它自己的设备中心缓冲区链表读指针426的机制。然而,在一实施例中,NIC可以在接收完成事件中显式地指定它的已更新设备中心缓冲区链表读指针426。可以使用后一机制来替代前一机制,或者连同前一机制一起使用后一机制。
在步骤1132中,主机子系统214清除接收事件队列418中的由当前接收事件队列读指针标识的位置处的事件描述符,因为先前描述的有关破损安全的理由,在步骤1134中,主机子系统214使接收事件队列读指针420模数递增。然后该模块返回到步骤1120以检索下一事件描述符,以此类推,直到检索一清空的条目然后该模块变得不活动(步骤1124)。
如果在步骤1122中确定所检索的下一事件描述符是清空的,则接收事件队列418此时不再含有其它待处理事件。在一个实施例中,则主机接收事件处理器1100将只变得不活动,以等待下一激活触发(步骤1124)。至少为了队列深度管理的目的,不需要主机子系统214通知NIC216已更新的主机中心接收事件队列读指针,因为NIC避免接收队列事件418的溢出,不是通过比较在接收事件队列418中的当前读指针和写指针,而是通过不再许可(通过主机)向接收事件队列418写入比先前所述的由可用空间积累的接收描述符更多的接收事件描述符。
然而,在一个优选实施例中,主机存储器子系统确实如此通知NIC216作为如以下更详细地描述的一种管理事件队列中断的手段。因而,在步骤1123中,如果主机中心接收事件队列读指针420已发生了变化,则主机将所更新的指针值写入NIC的设备中心接收事件队列读指针中。然后在步骤1124中主机接收事件处理器1100变得不活动。
中断管理
如上所述,尽管在一个实施例中可以利用本发明的通过永远不更新在主机存储器子系统222中或在NIC设备216上的事件队列读指针或写指针的影像复本的方面,根据具体情况而定,在另一个实施例中,NIC216事实上确实保持有用于发送事件队列318和接收事件队列418中的每一个的设备中心事件队列读指针344或444。然而,如下所述,将这些读指针只用于中断管理;NIC216确实不应用它们用于事件队列深度管理。而且,在一个实施例中,仅在主机子系统214的选择下将它们用于中断管理。出于下述原因,主机可以只使用设备中心接收事件队列读指针444,而设备中心发送事件队列读指针344完全不被使用。在这种实施例中,主机永远都不将事件队列读指针更新写入设备中心发送事件队列读指针344中(即,略去图7中的步骤723)。
图15是例示了在主机子系统214与NIC216之间的交互作用的流程图,该交互作用用于对NIC216的中断生成进行管理以激活图11的主机接收事件处理器。将该交互作用设计成使所需中断次数最少化同时还避免竞争状况的概率。图15中的某些步骤重复了其它流程图的一部分的多个步骤,但是将这些步骤集合在图15中会有助于例示它们与中断管理的关系。
在主机接收事件处理器1100中,在处理了它已从接收事件队列418检索的一个或更多个接收事件之后,它可以将一已更新的接收事件队列读指针写入NIC216上的设备中心接收事件队列读指针444中(步骤1123)。在图15中将该步骤示为步骤1508。通过写入这种值,主机将它的观点指示给NIC:接收事件队列418现在是空的。如果NIC216的观点是真实的,则设备中心接收事件队列读指针434和写指针432现在是相等的。在步骤1510中NIC由此(通过比较器446)将这两个值进行比较,如果它们相等,则NIC还通过将中断使能位418设置为它的活动状态来再使能用于接收事件队列418的中断(步骤1512)。响应于对设备中心接收事件队列读指针的更新并且与该更新原子地发生对中断的再使能;对这种更新值的写入构成了单个组合指令,以使用该新值来更新设备中心接收事件队列读指针并再使能中断。在使能了中断的情况下,NIC216上的接收事件队列中断管理模块接着变得不活动(步骤1514),直到NIC将新接收完成事件写入接收事件队列418中(步骤1010)并相应地更新设备中心接收事件队列写指针432(步骤1012)。设备中心接收事件队列读指针444与写指针432此时不相等,然后在步骤1516中,由于从步骤1512起一直使能了中断,因此NIC接收数据模块产生一中断以激活图11的主机接收事件处理器(如在步骤1014中阐述的)。NIC216还通过对中断使能位448进行再设置来原子地禁用(抑制)其它接收事件队列中断生成,从而在不产生其它中断的情况下临时地允许NIC接收数据模块将附加事件写入接收事件队列418中。然后接收事件队列中断管理模块回到步骤1508,等待由另一已更新接收事件队列读指针的主机进行下一写入。当发生该下一写入时,如前所述,NIC再次针对相等性将设备中心读指针与写指针进行比较(步骤1510),并再次在禁用中断的情况下进行等待,直到NIC将另一事件写入接收事件队列418中(步骤1514)。
如果在步骤1510中NIC216确定设备中心事件队列读指针与写指针不相等,则这表示NIC216未共享主机的观点:接收事件队列418现在是空的。这可能会在如下情况下发生:例如,如果NIC处于正在将更多接收完成事件写入接收事件队列418的过程中(NIC接收数据模块的步骤1010),同时在主机接收事件处理器的步骤1122中主机确定下一检索的事件描述符是空的。如果出现了该竞争状态,则NIC216通过在步骤1510中确定所述两个指针不相等(即,设备中心接收事件队列写指针432在设备中心接收事件队列读指针444之前)将检测到该竞争状态。在此情况下NIC将使中断被禁用,并将立即产生另一个中断以激活主机接收事件处理器(步骤1518)。这将给予主机这样一个机会,即,当主机在步骤1122中不正确地确定接收事件队列418是空的时,它可以处理正在处理中的事件。
注意,NIC接收数据模块可以继续将更多个事件写入接收事件队列418中,并且如果在步骤1122中主机接收事件处理器检测它们,则将处理它们。如果在主机下一次向NIC写入已更新的接收事件队列读指针时(步骤1123)这些附加事件的任何一个仍然未被处理,则在步骤1510中将再次检测到该新竞争状态,还产生另一中断;以此类推。
如果在特定实施例中在发送端使用事件队列中断管理模块,则针对发送类似地实现以上针对接收事件队列中断管理模块(图15)描述的机制。然而,如以上指出的,在另一实施例中,仅在接收端而不在发送端使用中断管理模块。这是因为在接收端希望具有低潜伏性,而在发送端潜伏性并不是那么重要。在发送端,对中断的减少可能比发送完成事件的立即接收更重要。在这种实施例中用于中断管理的所有上述硬件可以保留在NIC216上,但是主机发送事件处理器700从不更新设备中心发送事件队列读指针344。因此中断使能位348从不变活动,并且无论写/读指针比较器346的输出如何,中断发生器352都不产生中断。相反,只在定时器到期时或在设备驱动器中通过轮询循环进行选择时激活主机发送事件处理器700(步骤712)。这允许主机子系统针对何时和以何频度轮询发送事件队列418中的新事件进行它自己的软件型判决,而不是由NIC产生的中断来支配。
在许多实施例中,在步骤1512和1516中对中断的使能和禁用不必总体地作为应用于整个外设216的功能。例如,可以通过掩盖机制实现该功能。在该意义下,这里使用的术语禁用和使能与诸如对中断的掩盖和解掩盖、以及对中断的许可和抑制是可互换的。此外,典型地,由于主机事件处理器在将它自己的已更新事件队列读指针写入NIC216之后通常失活(见图11,步骤1124),等待再激活新中断(步骤1110),因此也可以将对事件队列读指针的写入视为中断请求。这里在使能中断与中断请求之间没有区别。
多重队列实施例
本发明在网络接口架构中尤其有用,在网络接口架构中,协议栈的部分位于操作系统内核和传输库中,以由用户级应用直接调用。在2004年4月12日提交的英国专利公报No.GB0408876A0(标题为“User-level Stack”)中描述了这样一种架构的示例,通过引用将其并入于此。在这种架构中,可以支持各种协议栈,每种协议栈都带有它自己的发送和接收数据结构组,并且所有协议栈都由在NIC上的硬件中执行的功能来协助。
图12是其中通过NIC1210上的硬件协助来支持多个协议栈的系统的简化框图。NIC1210不仅包括常规硬件NIC部分1212,而且包括多个“虚”NIC部分(VNIC)1214。对于操作系统来说,NIC1210看起来是具有两个设备驱动器的双功能设备:与常规NIC部分1212相通信的常规设备驱动器1216,和与VNIC1214相通信的用户设备驱动器1218。一般来讲,由内核1220中的常规协议栈(未示出)使用常规设备驱动器1216,由多个用户级协议栈1222中的每一个来使用用户设备驱动器1218。为多个用户级应用(或处理)1224中的每一个创建独立用户专用协议栈1222。用户级应用可以通过呼叫内核1220与网络212相通信,但是优选地它们通过它们的相应用户级协议栈1222与网络212相通信。
图12的系统比图3和4的系统更复杂,但是对于各单个发送或接收队列,操作是类似的。在这两个实施例之间的主要差别源自图12实施例保持多个队列的特性和状态的更大的复杂性。以下对这些差异的有关细节进行描述。
图13是图12的系统为了支持所述多个VNIC1214中的每一个的独立发送队列而使用的各种数据结构的框图。该图表示在主机存储器222中存在哪些结构并且在NIC216上存在哪些结构。所有发送队列的发送数据缓冲区310、发送缓冲区链表312以及发送事件队列318都驻留在主机存储器222中,并由一般化的多个缓冲区组成,在主机存储器222中这些缓冲区可以是彼此不连续并散布开来的。在图13中,将组成发送数据缓冲区310的缓冲区标识为“TX DATA BUF #n”,将组成发送缓冲区链表312的缓冲区标识为“TX QUEUE BUF#n”。将组成发送事件队列318的缓冲区标识为“TX EV QUEUE BUF #n”。
在一个实施例中单个缓冲区可以是4k或8k字节长度的,并通过缓冲区描述符表1310中的多个物理上连续的描述符将这些缓冲区链接在一起,成为逻辑上连续的序列。例如,一个发送队列(发送缓冲区链表312)可能占据主机存储器222中的缓冲区1312、1314以及1316,这些缓冲区是不连续的并且可能是存储器的多个无序区。通过缓冲区描述符表1310中的多个物理上连续的条目1318、1320以及1322将这些缓冲区链接在一起,成为单个逻辑上连续的空间。由主机214对条目1318、1320以及1322进行写入和管理,并将它们视为环绕环。因此,例如,如果主机希望定义具有64k个发送数据缓冲区描述符的条目的发送缓冲区链表312,并且每个缓冲区的大小为4k,那么主机将为该发送缓冲区链表分配缓冲区描述符表1310中的物理上连续的16个条目序列。类似地,一个发送事件队列318可能占据主机存储器222中的缓冲区1326、1328以及1330。这些缓冲区是不连续地并且可能在主机存储器中是无序的,但是通过缓冲区描述附表1310中的多个物理上连续的条目1332、1334以及1336将这些缓冲区链接在一起,成为单个逻辑上连续的环绕空间。通过“缓冲区ID”为缓冲区描述符表1310编索引,此外,其每个条目还标识了主机存储器222中的相对应的缓冲区的基地址等。
为了掌握可能同时与LAN212相通信的许多用户级应用的多个发送缓冲区链表和发送事件队列中的每一个的状态,NIC216包括发送队列描述符表1340和事件队列描述符表1342。每个发送队列(包括其发送数据缓冲区、其发送缓冲区链表以及其发送事件队列)都具有相对应的发送队列ID,将该发送队列ID用作发送队列描述符表1340中的索引。发送队列描述符表1340中的指定条目是用于描述该特定发送队列的状态和其它特性的起点(如由NIC216观察到的)。此外,每个这种条目还标识了:
*该队列是内核队列、用户队列还是另一种队列;
*发送缓冲区链表312的大小(其可以包含的发送数据缓冲区描述符的数量);
*与该发送队列相关联的发送事件队列的ID;
*待作为发送完成事件的一部分返回给事件队列的队列“标签”;
*该发送队列的发送缓冲区链表312中的基缓冲区的缓冲区ID;
*指向该发送队列的发送缓冲区链表312中的设备中心读指针326和写指针324。
为了从主机存储器222中的特定发送队列中检索当前发送数据,NIC216首先使用该特定发送队列的ID在发送队列描述符表1340中查找含有该特定发送队列的发送缓冲区链表312的基缓冲区的缓冲区ID。NIC216还从同一位置获得指向到该发送缓冲区链表312中的当前设备中心缓冲区链表读指针326。然后它使用该基缓冲区ID作为基,使用该设备中心缓冲区链表读指针高阶位作为偏移量,到缓冲区描述符表1310中获得主机存储器222中的含有所述特定发送缓冲区链表312的缓冲区的基地址。然后NIC使用该基地址作为基,使用该设备中心缓冲区链表读指针低阶位乘以每个描述符占用的字节数作为偏移量,它从主机存储器222检索所述特定发送缓冲区链表312中的当前条目。注意,为了减少对主机存储器222的访问,在一个实施例中NIC216将发送缓冲区链表312的一部分高速缓存。
此外,所述特定发送缓冲区链表312的当前条目还含有:
*当前发送数据缓冲区的缓冲区ID;
*到当前发送数据缓冲区中的字节偏移量;以及
*待从当前发送数据缓冲区发送的字节数。
然后NIC216使用当前发送数据缓冲区的缓冲区ID作为到缓冲区描述符表1310中的另一索引,以检索含有当前发送数据的缓冲区的缓冲区描述符。注意,该缓冲区描述符是缓冲区描述符表1310中的单个条目;与含有发送队列或发送事件队列的缓冲区的描述符不同,该缓冲区描述符不是环的一部分。NIC216获得当前发送数据缓冲区在主机存储器222中的物理地址,然后使用该物理地址作为基,并使用与发送缓冲区链表条目的字节偏移量作为偏移量,确定待发送的当前数据在主机存储器222中的物理起始地址。
如前所述,由发送队列ID指定的发送队列描述符表1340条目也含有与所述特定发送队列相关联的发送事件队列的ID。由发送事件队列描述符表1342中的相应条目描述所有应用1224的所有发送事件队列。在发送事件队列描述符表1342中由来自发送队列描述附表1340的发送队列ID标识的条目是用于描述该特定发送事件队列318的状态和其它特性的起点(如由NIC216观察到的)。此外,每个这种条目还都标识了:
*该特定发送事件队列318的大小(这就是在主机的在发送事件队列318中可用空间的数量的计算中使用的x的值);
*组成该特定事件队列318的基缓冲区的缓冲区ID;
*与图3的中断使能位348相对应的char_ev_enable位;
*该特定事件队列318的发送事件队列写指针332;以及
*该特定事件队列318的发送事件队列读指针。
由此为了将一事件写入与特定发送队列相关联的特定事件队列318中,NIC216使用从发送队列描述符表1340中的相应条目获得的发送事件队列ID,在发送事件队列描述附表1342中查找含有所述特定发送队列的发送事件队列318的基缓冲区的缓冲区ID。NIC216还从同一位置获得指向到该发送事件队列318中的当前发送事件队列写指针332。然后它使用该基缓冲区ID作为基,使用发送事件队列写指针高阶位乘以每个描述符占用的字节数作为偏移量,到缓冲区描述符表1310中获得主机存储器222中的含有所述特定发送事件队列318的当前条目的缓冲区的基地址。然后NIC使用该基地址作为基,使用发送事件队列写指针低阶位作为偏移量,以将希望的事件描述符写入主机存储器222中的所述特定发送事件队列318的当前条目中。
注意,如图13所例示,虽然缓冲区描述符表1310所示的每个空隙(例如1332、1334、1318)表示单个描述符,但是主机存储器222中的每个空隙(例如1326、1328、1314)表示信息存储器“页”。一页例如可以是4k或8k字节长,因此如果发送队列中的发送数据缓冲区描述符占据4或8字节,则图13所示的每个空隙1312、1314或1316可以保持512、1k或2k个发送数据缓冲区描述符。
图14是图12的系统为了支持所述多个VNIC1214中的每一个的独立接收队列而使用的各种数据结构的框图。该图类似于发送端的图,因此这里不再对某些类似特征进行描述。
与发送端一样,所有接收队列的接收数据缓冲区410、接收缓冲区链表412以及接收事件队列418都驻留在主机存储器222中,并由一般化的多个缓冲区组成,在主机存储器222中这些缓冲区可以是彼此不连续并散布开来的。在图14中,将组成接收数据缓冲区410的缓冲区标识为“RXDATA BUF#n”,将组成接收缓冲区链表412的缓冲区标识为“TXQUEUE BUF#n”。将组成接收事件队列418的缓冲区标识为“TX EV QUEUE BUF#n”。优选地,将所有协议栈的发送事件队列318与接收事件队列418组合成一个事件队列的总库。即,优选地,将发送事件队列描述符表1342和接收事件描述符表1442实现为仅单个表。
仍然与发送端一样,通过缓冲区描述符表1410中的多个物理上连续的描述符表将单个缓冲区链接在一起,成为逻辑上连续的序列。通过“缓冲区ID”为缓冲区描述符表1410编索引,此外,其每个条目都标识了主机存储器222中的相对应的缓冲区的基地址。
为了掌握可能同时与LAN212相通信的许多用户级应用的多个接收缓冲区链表和接收事件队列中的每一个的状态,与发送队列描述符表1340类似,NIC216包括接收队列描述符表1440和事件队列描述符表1442。每个接收队列(包括其接收数据缓冲区、其接收缓冲区链表以及其接收事件队列)都具有相对应的接收队列ID,将该接收队列ID用作接收队列描述符表1440中的索引。接收队列描述符表1440中的指定条目是用于描述该特定接收队列的状态和其它特性的起点(如由NIC216观察到的)。每个这种条目都大致标识了与以上针对发送队列描述符表1340中的条目描述的有关接收队列的相同的信息。
如前所述,由接收队列ID指定的接收队列描述符表1440条目也含有与所述特定接收队列相关联的接收事件队列的ID。由接收事件队列描述符表1442中的相应条目描述所有应用1224的所有接收事件队列。在接收事件队列描述符表1442中由来自接收队列描述附表1440的接收队列ID标识的条目是用于描述该特定接收事件队列418的状态和其它特性的起点(如由NIC216观察到的)。每个这种条目都大致标识了与以上针对发送队列描述符表1342中的条目描述的有关接收队列的相同的信息。与发送端一样,至少对于事件队列深度管理目的,该信息不必包括任何接收事件队列418的设备中心接收事件队列读指针。
除与发送端的结构相对应的接收端的结构外,接收端还包括过滤表和逻辑块1450。由于NIC216可以支持在用户级应用1224与LAN212上的远程代理之间的多个同时连接,并且由于NIC216支持使用多个发送和接收队列的这些同时连接,因此由NIC216执行的一个功能是将每个输入数据包导引到正确的接收队列。NIC216用以进行该确定的机制对于本发明的理解并不重要,但是需要注意的是,过滤表和逻辑1450保持有在数据包报头信息与目的地接收队列ID之间的对应关系。由此过滤表和逻辑1450使用输入数据包的报头信息来确定正确的目的地接收队列的ID,并使用该接收队列ID作为对接收队列描述符表1440的索引。如以上针对发送端说明的那样,接收队列ID是NIC216用以获得与目的地接收队列有关的所有所需的信息(以正确地转发数据包数据)的起点。
管理事件
如上所述,在这里描述的实施例中将事件用作首要的状态报告方法。事件是从NIC216中的各种资源收集的状态字。如以上针对简化实施例描述的,事件可以产生中断,但是如下所述,在图12到14的实施例中对中断的产生包括额外的间接级。使中断最小化以降低中断潜伏性和CPU开销。
同样如上所述,将发送事件队列描述符表1342和接收事件队列描述符表1442优选地仅实现为单个表。在一个这种实施例中,统一的事件队列支持多达4k个事件队列。事件队列0到3专用于最多4个队列,该最多4个队列用于内核网络接口驱动器(被公知为NET驱动器)的数据包传送,事件队列4专用于第二内核驱动器(被公知为CHAR驱动器),该第二内核驱动器负责总体管理和在所有用户队列与它们相对应的协议栈之间的协调。
事件具有如由事件描述符中的事件代号字段标识的不同类型。事件描述符的其余字段取决于事件类型。已经描述的两种事件类型是发送完成事件和接收完成事件;如前所述,如分别在发送队列描述符表1340或接收队列描述符表1440中设计的那样,将这些事件发送给任何事件队列。其它事件类型专用于其它非IP LAN协议。每个事件队列还具有在NIC216中的相关联的定时器,这些定时器也可以为它们相应的事件队列产生事件。某些事件(包括大多数的管理事件)是严格意义上的CHAR驱动器事件。只将这些事件发送给CHAR驱动器而不发送给任何用户事件队列。还有的事件是全局事件,CHAR驱动器或内核驱动器可以负责处理该全局事件。也可以由CHAR驱动器或NET驱动器来产生事件。CHAR和NET驱动器可以为任何事件队列产生任何希望的类型的事件。
NIC216使用单个事件FIFO(未示出)来缓冲等待被写出到存储器中的事件。事件FIFO的深度很浅,以确保低潜伏递送。当该FIFO已满时,所有代理都得到了背压。另外,作为从不同资源收集的事件,在某些情况下,NIC216能够积累比在单个事件描述符中的更多的事件。如果这样将出现这种情况,例如,由在单个事件描述符格式中不同的标记描述的两个不同的事件。这里提及的事件“接合”的步骤,可以帮助减少待写入主机存储器222中的事件队列的事件描述符的数量。然而,接合的可能性确实不能减少用于提供发送或接收完成事件的事件队列的可用空间数量的主机子系统的计算中的值“z”,因为主机不能基于任何接合而执行。如上所述,主机仍然需要确定z,在依靠基于管理事件资源的数量和每个这样资源可能最后在任何给定点还未被处理的事件的最大量。在另一个实施例中,可能会采用接合的某些量,从而减少z的值。
共享事件队列实施例
在图3、4、13以及14的实施例中,针对每个发送和接收数据队列示出独立的事件队列。然而,在一优选实施例中,主机子系统214可以指定单个事件队列来接收关于若干不同数据队列的事件。分配给单个事件队列的数据队列可以是发送队列、接收队列或这两者。例如,主机子系统可能正在进行充当网络212上的若干束TCP连接的端点的程序线程。通常,该线程针对各连接束具有独立发送队列和独立接收队列,但是通过仅具有用于接收与所有这种发送和接收队列有关的事件的一个事件队列。作为另一示例,一个程序线程可能具有一个以上事件队列,或者几个程序线程可能共享一个事件队列。支持所以这种变化。此外,在多重队列实施例中,同样将诸如1342和1442的发送和接收事件队列描述符表优选地表现为仅单个表。
如上所述,特定数据队列的队列描述符表1340或1440(分别是发送或接收)中的条目包括这样的字段:该字段标识了应当将与所述特定队列有关的事件写入其中的事件队列ID。当NIC216耗用了来自特定数据队列的发送或接收数据缓冲区并希望将一完成事件写入合适的事件队列中时,NIC216从队列描述符表条目中的用于该特定数据队列的该字段检索合适的事件队列ID。队列描述符表中的用于特定数据队列的条目还包括队列“标签”,NIC216会将该队列“标签”包括进来,作为完成事件描述符中的数据。典型地,负责所述特定数据队列的程序线程将向该标签字段写入该线程可以稍后(当接收到完成事件时)使用的代号,以标识该事件所属的特定数据队列ID。
在一共享的事件队列实施例中,当主机发送事件管理模块确定相关事件队列中的用于接收引用特定发送队列的完成事件的当前有效空间的数量时,z的值是可以从其他资源写入到相关事件队列的事件的最大数量,其中其他资源包括引用可以被写入相同相关事件队列的其他数据队列的完成事件。同样地,当接收事件管理模块确定相关事件队列中的用于接收引用特定接收队列的完成事件的当前有效空间的数量时,z的值是可以从其他资源写入到相关事件队列的事件的最大数量,其中其他资源包括引用可以被写入相同相关事件队列的其他数据队列的完成事件。
规定的另一种方式,如果z被认为限制到不是完成事件的事件,则通过B*(x-z)-y得出(如上阐述)主机子系统在任何给定时间排队用来发送或接收的数据缓冲区的最大量,其中,x在相关联的事件队列中的条目的总量,B是可以由在相关联的事件队列中的每个完成事件描述的数据缓冲区的最小量。然而,z的值是现在将要写入事件队列的未完成事件的最大量,y是先前排队并且仍未被处理的数据缓冲区的总量。y的值是y1,y2,...yN的和,其中每个yi表示在与特定事件队列相关联的分别不同的数据队列中先前排队的数据缓冲区之一的数量,并且仍然未被处理。
多重队列实施例中的中断管理
在多重队列实施例中,如以上针对简化实施例描述的那样对NET内核队列的中断进行管理。按硬件而定,要么每个内核队列都具有它自己的中断,要么两个或更多个内核队列共享中断。在后一情况下,共享的中断激活所有的共享驱动器,这些共享驱动器中的每一个快速地确定它是否为负责处理该中断的驱动器。CHAR驱动器也可以与一个或更多个NET队列共享中断。
然而,用户队列不是操作系统的一部分并且不能接收中断。在本发明的一个方面中,可以通过添加一个间接层来实现类似的功能。具体来说,在步骤636(用于发送)和1014(用于接收)中,相应的NIC发送和接收数据模块不直接中断主机中的相应发送和接收事件处理器。相反,它们向CHAR驱动器的事件队列写入“用户事件队列唤醒事件”,其包括(通过事件队列ID来)标识需要激活的事件队列的字段。在简化实施例中,通过中断使能位348、448来许可中断;在多事件实施例中,通过需要激活的用户级事件队列描述符中的char_ev_enable位来许可将用户事件队列唤醒事件写入CHAR驱动器的事件队列中。
CHAR驱动器队列在它忽略它的char_ev_enable位的意义上来说与用户级队列不同,并且与简化实施例的不同之处在于:其事件队列含有引用另一队列的驱动器的唤醒事件而非数据传送完成事件(尽管在一实施例中CHAR驱动器事件队列也可以含有数据传送完成事件)。CHAR驱动器队列与简化实施例地类似之处在于:它仍然包括NIC上的中断使能位。使用该位对中断的使能和禁用实质上与以上参照图3和4阐述的相同。
因此,连同向CHAR驱动器事件队列写入用户事件队列唤醒事件,只有接着使能了CHAR驱动器中断时,NIC216才产生对运行在主机子系统214上的CHAR驱动器的中断。然后立即关闭CHAR驱动器队列的中断使能位,使得被写入CHAR事件队列中的其它用户事件队列唤醒事件不会产生中断(被写入CHAR事件队列中的其它种类的事件也不会产生中断)。换句话说,CHAR事件队列不会产生其它中断,直到CHAR驱动器请求一个中断。然而,NET驱动器队列可能仍然能够产生中断,因为所有中断事件队列都按独立的方式进行操作。如以上参照图3和4阐述的,当主机中的CHAR驱动器相信它已经清空了CHAR驱动器事件队列时,它将它的已更新主机中心CHAR事件队列读指针写回NIC216,NIC216对它与设备中心写指针进行比较,以确定是否再使能CHAR驱动器中断(如果这两个指针相等)或断言新的中断(如果这两个指针不相等)。由此避免了在下述情况下可能出现的竞争状态:如果NIC正在将更多用户事件队列唤醒事件写入CHAR驱动器事件队列的过程中,同时主机中的CHAR驱动器确定CHAR驱动器事件队列中的下一检索的描述符是空的。
当CHAR驱动器事件处理器从CHAR驱动器事件队列获得用户事件队列唤醒事件时,它进行如下操作:激活负责在用户事件队列唤醒事件中标识的事件队列的主机事件处理器。
在用于中断队列(NET驱动器队列和CHAR驱动器队列)与用于非中断队列(用户队列)的中断管理机制之间存在两重性。如上所述,用于中断队列的驱动器使能和禁用(抑制)特定队列的中断,而用于非中断队列的驱动器使能和禁用NIC对特定队列的唤醒事件的写入。这两个处理的细微差异在于,对于中断队列,缺省状态是其中将发生中断的状态;在不希望发生中断的时段中必须抑制这些中断。另一方面,对于非中断队列,缺省状态是其中不写入唤醒事件的状态。必须请求唤醒事件以出现一个唤醒事件。然而,在这两种情况下,在上述实施例中,使能或禁用中断的机制类似于用于使能或禁用唤醒事件的机制:连同对中断/唤醒事件的发出一起禁用/抑制/扣留它们,并且通过向NIC对已更新事件队列读指针的写入来再使能/允许/请求它们。由此,这里在短语“对中断的使能”、“对中断的允许”或“对中断的请求”之间没有区别。类似地,这里在短语“对唤醒事件的使能”、“对唤醒事件的允许”或“对唤醒事件的请求”之间没有区别。即使在其中用于中断队列的机制与用于非中断队列的机制不相同的实施例中,这里在这些术语之中也没有区别。
可以看到,即使用户级驱动器实际上不能接收中断,通过向用于对中断进行协调的CHAR驱动器发送唤醒事件而提供的附加间接层也允许用户级队列及其驱动器按与中断操作系统队列大致相同的方式进行操作。即使在可以接收中断的多个驱动器的实施例中,附加间接层也是有益的,因为它不仅有助于单个地使各事件队列的中断最小化,而且有助于整体地使在所有事件队列上的中断最小化。
外设向中间事件队列写入事件(“参考”事件)、调用中间事件队列处理器以针对不同的(“被参考”)事件队列执行功能的技术并不限于用于激活有意阻断对唤醒事件的等待的参考队列的处理器的唤醒事件。也不限于其中中间队列是中断队列而参考队列不使中断队列的情况。作为示例,在2004年2月3日提交的英国专利公报No.0404696.7(标题为“DualDriver Interface”,通过应用将其并入于此)中描述的一个实施例中,支持多个内核事件队列。当一事件已在队列(参考队列)上保留了一定延长时段时(这表示参考队列停滞住了),外设能够检测到。例如,如果与队列相关联的应用、驱动器或传输库出现了故障,或者已经终止或被排除在计划以外,该队列可能会变得停滞。响应于这种检测,而不是发出待由与参考队列相关联的实体来处理的中断,网络接口驱动器向另一队列(优选地,控制通道的队列)发出事件(可优选地,带有相关联地中断)。该“参考”事件表示停滞队列的性质(即,对停滞队列的表示)和标识。优选地,将与控制通道相关联的实体(优选地,其为诸如所述多个驱动器中的一个的控制实体)布置成通过访问并释放所表示的队列对这种消息进行响应。可以显见对中间队列技术的许多其它使用。取决于所表示的队列变得停滞的原因,可能存在如下情况:当前已释放事件队列的处理器接着可以检索并处理队列上的一个或更多个事件。
避免中断假警报
某些I/O总线(如PCI2.0和PCI-X)支持电平触发中断,在电平触发中断中,外设通过使信号线进入活动状态来产生中断。在使用电平触发中断的系统中,存在可以产生“假警报”中断的边界条件。考虑如下情况:外设通过使中断信号线进入其活动电平来产生其中断,并且只在接收到来自主机的中断确认时才使它返回不活动电平。如果在将该中断确认写入外设之后,在外设能够使中断信号线失活之前主机中断服务例程退出了,则主机可以将持续活动的信号线解释为新中断并再次激活事件队列处理器。如果此时事件队列仍然是空的,则该新的“假警报”会引起不必要的主机的环境切换。
因此,在使用电平触发中断的实施例中,可以如下地使这种假警报的可能性最小化。响应于中断,主机子系统读取NIC上的寄存器,以确定多个源中的哪个源发起了该中断。响应于主机子系统对中断源寄存器的读取,NIC使中断线失活。NIC可以产生其它中断,然后如果在读取了中断源寄存器之后产生了更多事件,但是不会丢失中断。在主机子系统写回它的事件队列读指针时,中断线也不会活动,因此也不会产生假警报中断。
在上述多重队列实施例中,在不需要到NIC的通信以解断言唤醒事件的意义上来说,唤醒事件更类似于边沿触发中断,而不是电平触发中断。NIC在主机事件处理器已使它自己失活之后保持唤醒事件活动,这是没有风险的,因为主机可以撤销该唤醒事件并按协调的方式使它自己失活。因此,根据它的性质,假设正确地设计了事件处理器,则在唤醒事件的环境下不必产生假警报问题。然而,对于CHAR驱动器中断,仍然会产生该问题。在这种情况下,一个实施例使用以上针对CHAR驱动器事件队列描述的假警报防止机制,和以上针对用户事件队列描述的竞争状况检测和纠正机制。在使用消息信令中断(MSI)的实施例中,如在PCI-Express I/O总线上,所有队列只使用上述竞争状况机制。
*****
如这里使用的,对信息项的“标识”不一定要求直接指定该信息项。通过一个或更多个间接层简单地引用实际信息,或者通过标识一个或更多个不同的信息项(这些信息项一起例如通过公知的算法足以确定实际信息项),可以在一字段中“标识”信息。此外,这里使用术语“表示”来意指与“标识”相同的意思。
此外,在这里使用的,指向存储器中的特定位置的“指针”是“标识”存储器中的特定位置的信息。在一个实施例中,该指针包括:第一值,其标识缓冲区描述符表中的条目,而该条目标识主机存储器中的缓冲区的基地址;和第二值,其标识到该缓冲区的偏移量;以及还可以包括其它参数,需要这些参数来唯一地标识存储器中的特定位置。注意,由于如上所述地定义了“标识”,因此标识了队列中的待读取或写入事件的“下一”位置的任何读或写指针,也标识了最后一次读取或最后一次写入位置,反之亦然。
此外,如这里使用的,如果前任信号、事件或值影响了给定信号、事件或值,则给定信号、事件或值对该前任信号、事件或值“有响应”。如果存在插入处理单元、步骤或时段,则给定的信号、事件或值仍然可以对前任信号、事件或值“有响应”。如果插入处理单元或步骤组合了一个以上的信号、事件或值,则将处理单元或步骤的信号输出视为对信号、事件或值输入中的每一个都“有响应”。如果给定信号、事件或值与前任信号、事件或值相同,则该情况仅仅是其中将给定信号、事件或值仍然视为对前任信号、事件或值“有响应”的退化情况。类似地定义给定信号、事件或值对另一信号、事件或值的“依赖性”。
出于示例和说明的目的,提供了本发明优选实施例的上述说明。并不旨在穷举或者将本发明限制为公开的精确形式。显然,本领域的技术人员可以显见许多变型和修改。具体来说,并无限制地,通过引用将在本专利申请的背景部分中通过引用描述、建议或并入的任何和所以变型并入本文对本发明的多个实施例的描述中。对本文描述的实施例的选取和描述的目的是为了很好地阐述本发明的原理及其实际应用,从而使得本领域的技术人员能够针对各种实施例并以适于所预期的具体应用的各种修改来理解本发明。本发明的范围由所附权利要求及其等同物来限定。