From 141958656f8a83a4f5703bd4890389dd1f180e0a Mon Sep 17 00:00:00 2001 From: Ophestra Date: Mon, 30 Mar 2026 01:48:20 +0900 Subject: [PATCH] internal/uevent: handle state divergence This requires the caller to arrange for a coldboot to happen, some time after this error is encountered, and to resume event processing. Signed-off-by: Ophestra --- internal/uevent/uevent.go | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/internal/uevent/uevent.go b/internal/uevent/uevent.go index e0779866..47091f59 100644 --- a/internal/uevent/uevent.go +++ b/internal/uevent/uevent.go @@ -18,6 +18,13 @@ type ( Recoverable interface{ recoverable() } // Nontrivial is satisfied by errors preferring a JSON encoding. Nontrivial interface{ nontrivial() } + + // NeedsColdboot is satisfied by errors indicating divergence of local state + // from the kernel, usually from lost uevent data. + NeedsColdboot interface { + Recoverable + coldboot() + } ) // Conn represents a NETLINK_KOBJECT_UEVENT socket. @@ -58,6 +65,18 @@ var ( ErrBadSocket = errors.New("unexpected socket address") ) +// ReceiveBufferError indicates one or more [Message] being lost due to the +// socket receive buffer filling up. This is usually caused by epoll waking the +// receiving program up too late. +type ReceiveBufferError struct{ _ [0]*ReceiveBufferError } + +var _ NeedsColdboot = ReceiveBufferError{} + +func (ReceiveBufferError) recoverable() {} +func (ReceiveBufferError) coldboot() {} +func (ReceiveBufferError) Unwrap() error { return syscall.ENOBUFS } +func (e ReceiveBufferError) Error() string { return syscall.ENOBUFS.Error() } + // BadPortError is returned by [Conn.Consume] upon receiving a message that did // not come from the kernel. type BadPortError syscall.SockaddrNetlink @@ -84,6 +103,9 @@ func (c *Conn) Consume(ctx context.Context, events chan<- *Message) error { for { data, _, from, err := c.conn.Recvmsg(ctx, 0) if err != nil { + if errors.Is(err, syscall.ENOBUFS) { + return ReceiveBufferError{} + } return err }