Each virtual interface uses two ``descriptor rings'', one for
transmit, the other for receive. Each descriptor identifies a block
of contiguous machine memory allocated to the domain.
The transmit ring carries packets to transmit from the guest to the
backend domain. The return path of the transmit ring carries messages
indicating that the contents have been physically transmitted and the
backend no longer requires the associated pages of memory.
To receive packets, the guest places descriptors of unused pages on
the receive ring. The backend will return received packets by
exchanging these pages in the domain's memory with new pages
containing the received data, and passing back descriptors regarding
the new packets on the ring. This zero-copy approach allows the
backend to maintain a pool of free pages to receive packets into, and
then deliver them to appropriate domains after examining their
headers.
If a domain does not keep its receive ring stocked with empty buffers
then packets destined to it may be dropped. This provides some
defence against receive livelock problems because an overloaded domain
will cease to receive further data. Similarly, on the transmit path,
it provides the application with feedback on the rate at which packets
are able to leave the system.
Flow control on rings is achieved by including a pair of producer
indexes on the shared ring page. Each side will maintain a private
consumer index indicating the next outstanding message. In this
manner, the domains cooperate to divide the ring into two message
lists, one in each direction. Notification is decoupled from the
immediate placement of new messages on the ring; the event channel
will be used to generate notification when either a certain
number of outstanding messages are queued, or a specified number
of nanoseconds have elapsed since the oldest message was placed on the
ring.