RE: [PATCH] netlink: introduce netlink poll to resolve fast return issue

From: Jong eon Park
Date: Fri Nov 10 2023 - 15:51:51 EST




On Tuesday, Nov 7, 2023 at 08:48 Jakub Kicinski wrote:
> Why does the wake up happen in the first place?
> I don't see anything special in the netlink code, so I'm assuming it's
> because datagram_poll() returns EPOLLERR.
>
> The man page says:
>
> EPOLLERR
> Error condition happened on the associated file
> descriptor. This event is also reported for the write end
> of a pipe when the read end has been closed.
>
> epoll_wait(2) will always report for this event; it is not
> necessary to set it in events when calling epoll_ctl().
>
> To me that sounds like EPOLLERR is always implicitly enabled, and should
> be handled by the application. IOW it's an pure application bug.
>
> Are you aware of any precedent for sockets adding in EPOLLOUT when
> EPOLLERR is set?

In my case, the first wake-up was by POLLIN, not POLLERR.
Please consider the below scenario.

------------CPU1 (kernel)---------- --------------CPU2 (app)--------------
...
a driver delivers uevent. poll was waiting for schedule.
a driver delivers uevent.
a driver delivers uevent.
...
1) netlink_broadcast_deliver fails.
(sk_rmem_alloc > sk_rcvbuf)
getting schedule and poll
returns,
and the app calls recv.
(rcv queue is empied)
2)

netlink_overrun is called.
(NETLINK_S_CONGESTED flag is set,
ENOBUF is written in sk_err and,
wake up poll.)
finishing its job and call poll
again.
poll returns POLLERR.

(the app doesn't have POLLERR
handler,)
it calls poll, but getting
POLLERR.
it calls poll, but getting
POLLERR.
it calls poll, but getting
POLLERR.
...


Interestingly, in this issue, even though netlink overrun frequently
happened and caused POLLERRs, the user was managing it well through
POLLIN and 'recv' function without a specific POLLERR handler.
However, in the current situation, rcv queue is already empty and
NETLINK_S_CONGESTED flag prevents any more incoming packets. This makes
it impossible for the user to call 'recv'.

This "congested" situation is a bit ambiguous. The queue is empty, yet
'congested' remains. This means kernel can no longer deliver uevents
despite the empty queue, and it lead to the persistent 'congested' status.

The reason for the difference in netlink lies in the NETLINK_S_CONGESTED
flag. If it were UDP, upon seeing the empty queue, it might have kept
pushing the received packets into the queue (making possible to call
'recv').

BRs,
JE Park.