-
Notifications
You must be signed in to change notification settings - Fork 176
Notes on ADQ
The fact that an application like Pelikan cache can benefit from ADQ [1] without code changes, and very minimal launch time commandline prefix, feels a bit mysterious to me. The core question is, "How does a specific application thread start busy-polling a pre-configured RX queue?" With a bunch of references provided by Intel, I did some digging with Juncheng Yang. I think we have a reasonable understanding at this point.
The first clue came from looking at Intel's patch submitted to Memcached in which a socket option SO_INCOMING_NAPI_ID
was mentioned [2]. Searching for this string reveals its corresponding field in the sock
structure, sk_napi_id
. The use of per-socket identifier made me think that the application thread was never aware of the specific queue number on the adapter, but instead, was somehow associated with the queue via this napi_id
. Most likely, this identifier was set by the network adapter, and copied into the sock
object.
The busy poll support was added to epoll in 2017. The LWN article on this topic listed the key patches. Reading the busy poll related files such as this revealed that indeed the NIC is where napi_id
is set (line 117) and then copied (line 125) Linux epoll
's behavior when busy poll is enabled is well explained by Sridhar Samudrala in his patch, and you should read it. Note that busy polling is opportunistic- the first time a napi_id
is associated with a thread that owns the epoll fd
is when that thread starts looking into it (via calling epoll_wait
), and when there's no socket or traffic present for the same napi_id
, the thread gets disassociated from it and stops busy polling.
So to tie all these together, once a queue is reserved by ADQ, TCP flows that fit the criteria will be tagged with napi_id
in their socket metadata. When a thread starts monitoring the socket using standard APIs such as epoll_add
, the thread becomes associated with a particular napi_id
, representing an RX queue. As long as there are open sockets with active traffic, the same thread will do busy polling against all sockets with the same napi_id
, allowing low-latency processing of corresponding packets.