system-linux: handle hotplug event socket ENOBUFS errors
authorAlin Nastac <alin.nastac@gmail.com>
Thu, 31 Jan 2019 10:20:29 +0000 (11:20 +0100)
committerHans Dedecker <dedeckeh@gmail.com>
Thu, 31 Jan 2019 21:03:00 +0000 (22:03 +0100)
Hotplug events are no longer handled after socket RX queue is
overrun. The issue has been fixed by:
  - setting SO_RCVBUF initially to 65535
  - doubling SO_RCVBUF value each time RX queue gets overrun

Signed-off-by: Alin Nastac <alin.nastac@gmail.com>
system-linux.c

index ea16ed5aede732104bfb17f40f0d0a1c59dca3c8..82e9928c92e711e328c7844aadaeb9a6932a79b6 100644 (file)
@@ -181,6 +181,21 @@ create_event_socket(struct event_socket *ev, int protocol,
        return true;
 }
 
+static bool
+create_hotplug_event_socket(struct event_socket *ev, int protocol,
+                           void (*cb)(struct uloop_fd *u, unsigned int events))
+{
+       if (!create_raw_event_socket(ev, protocol, 1, cb, ULOOP_ERROR_CB))
+               return false;
+
+       /* Increase rx buffer size to 65K on event sockets */
+       ev->bufsize = 65535;
+       if (nl_socket_set_buffer_size(ev->sock, ev->bufsize, 0))
+               return false;
+
+       return true;
+}
+
 static bool
 system_rtn_aton(const char *src, unsigned int *dst)
 {
@@ -249,8 +264,8 @@ int system_init(void)
        if (!create_event_socket(&rtnl_event, NETLINK_ROUTE, cb_rtnl_event))
                return -1;
 
-       if (!create_raw_event_socket(&hotplug_event, NETLINK_KOBJECT_UEVENT, 1,
-                                       handle_hotplug_event, 0))
+       if (!create_hotplug_event_socket(&hotplug_event, NETLINK_KOBJECT_UEVENT,
+                                        handle_hotplug_event))
                return -1;
 
        /* Receive network link events form kernel */
@@ -660,13 +675,39 @@ handle_hotplug_event(struct uloop_fd *u, unsigned int events)
        struct sockaddr_nl nla;
        unsigned char *buf = NULL;
        int size;
+       int err;
+       socklen_t errlen = sizeof(err);
+
+       if (!u->error) {
+               while ((size = nl_recv(ev->sock, &nla, &buf, NULL)) > 0) {
+                       if (nla.nl_pid == 0)
+                               handle_hotplug_msg((char *) buf, size);
+
+                       free(buf);
+               }
+               return;
+       }
 
-       while ((size = nl_recv(ev->sock, &nla, &buf, NULL)) > 0) {
-               if (nla.nl_pid == 0)
-                       handle_hotplug_msg((char *) buf, size);
+       if (getsockopt(u->fd, SOL_SOCKET, SO_ERROR, (void *)&err, &errlen))
+               goto abort;
 
-               free(buf);
+       switch(err) {
+       case ENOBUFS:
+               /* Increase rx buffer size on netlink socket */
+               ev->bufsize *= 2;
+               if (nl_socket_set_buffer_size(ev->sock, ev->bufsize, 0))
+                       goto abort;
+               break;
+
+       default:
+               goto abort;
        }
+       u->error = false;
+       return;
+
+abort:
+       uloop_fd_delete(&ev->uloop);
+       return;
 }
 
 static int system_rtnl_call(struct nl_msg *msg)