chiark / gitweb /
sd-rtnl: socket_read - use a read buffer
authorTom Gundersen <teg@jklm.no>
Mon, 14 Apr 2014 15:20:51 +0000 (17:20 +0200)
committerTom Gundersen <teg@jklm.no>
Mon, 14 Apr 2014 15:53:21 +0000 (17:53 +0200)
Rather than allocating/freeing memory for each message read, keep a global read buffer
in the rtnl object. Also, rather than using a fixed size, peek at the pending message
header to get the message size and reallocate as necessary.

src/libsystemd/sd-rtnl/rtnl-internal.h
src/libsystemd/sd-rtnl/rtnl-message.c
src/libsystemd/sd-rtnl/sd-rtnl.c

index 2f788d04f6cd08c2402ba0a9d0aa1d5073d45d41..6dd65b2de7f1d4c30908fe49d26dfa3fbec8bc19 100644 (file)
@@ -72,6 +72,9 @@ struct sd_rtnl {
         unsigned wqueue_size;
         size_t wqueue_allocated;
 
+        struct nlmsghdr *rbuffer;
+        size_t rbuffer_allocated;
+
         bool processing:1;
 
         uint32_t serial;
index 3a24cb885f36a5d8b51d118f4ae6e5448fb806ac..a93cb0c8e46459e3d2654ae128811a141f37ed71 100644 (file)
@@ -997,26 +997,6 @@ int sd_rtnl_message_get_errno(sd_rtnl_message *m) {
         return err->error;
 }
 
-static int message_receive_need(sd_rtnl *rtnl, size_t *need) {
-        assert(rtnl);
-        assert(need);
-
-        /* ioctl(rtnl->fd, FIONREAD, &need)
-           Does not appear to work on netlink sockets. libnl uses
-           MSG_PEEK instead. I don't know if that is worth the
-           extra roundtrip.
-
-           For now we simply use the maximum message size the kernel
-           may use (NLMSG_GOODSIZE), and then realloc to the actual
-           size after reading the message (hence avoiding huge memory
-           usage in case many small messages are kept around) */
-        *need = page_size();
-        if (*need > 8192UL)
-                *need = 8192UL;
-
-        return 0;
-}
-
 int rtnl_message_parse(sd_rtnl_message *m,
                        size_t **rta_offset_tb,
                        unsigned short *rta_tb_size,
@@ -1082,7 +1062,6 @@ int socket_write_message(sd_rtnl *nl, sd_rtnl_message *m) {
 int socket_read_message(sd_rtnl *rtnl) {
         _cleanup_rtnl_message_unref_ sd_rtnl_message *first = NULL;
         sd_rtnl_message *previous = NULL;
-        _cleanup_free_ void *buffer = NULL;
         uint8_t cred_buffer[CMSG_SPACE(sizeof(struct ucred))];
         struct iovec iov = {};
         struct msghdr msg = {
@@ -1094,23 +1073,37 @@ int socket_read_message(sd_rtnl *rtnl) {
         struct cmsghdr *cmsg;
         bool auth = false;
         struct nlmsghdr *new_msg;
-        size_t need, len;
+        size_t len;
         int r, ret = 0;
 
         assert(rtnl);
+        assert(rtnl->rbuffer);
 
-        r = message_receive_need(rtnl, &need);
+        iov.iov_base = rtnl->rbuffer;
+        iov.iov_len = rtnl->rbuffer_allocated;
+
+        /* peek at the pending message header to get the message size */
+        r = recvmsg(rtnl->fd, &msg, MSG_PEEK);
         if (r < 0)
-                return r;
+                /* no data */
+                return (errno == EAGAIN) ? 0 : -errno;
+        else if (r == 0)
+                /* connection was closed by the kernel */
+                return -ECONNRESET;
+        else if ((size_t)r < sizeof(struct nlmsghdr))
+                return -EIO;
 
-        buffer = malloc0(need);
-        if (!buffer)
+        /* make room for the pending message */
+        if (!greedy_realloc((void **)&rtnl->rbuffer,
+                            &rtnl->rbuffer_allocated,
+                            rtnl->rbuffer->nlmsg_len,
+                            sizeof(uint8_t)))
                 return -ENOMEM;
 
-        iov.iov_base = buffer;
-        iov.iov_len = need;
+        iov.iov_base = rtnl->rbuffer;
+        iov.iov_len = rtnl->rbuffer_allocated;
 
-        r = recvmsg(rtnl->fd, &msg, 0);
+        r = recvmsg(rtnl->fd, &msg, MSG_TRUNC);
         if (r < 0)
                 /* no data */
                 return (errno == EAGAIN) ? 0 : -errno;
@@ -1120,6 +1113,10 @@ int socket_read_message(sd_rtnl *rtnl) {
         else
                 len = (size_t)r;
 
+        if (len > rtnl->rbuffer_allocated)
+                /* message did not fit in read buffer */
+                return -EIO;
+
         for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
                 if (cmsg->cmsg_level == SOL_SOCKET &&
                     cmsg->cmsg_type == SCM_CREDENTIALS &&
@@ -1138,7 +1135,7 @@ int socket_read_message(sd_rtnl *rtnl) {
                 /* not from the kernel, ignore */
                 return 0;
 
-        for (new_msg = buffer; NLMSG_OK(new_msg, len); new_msg = NLMSG_NEXT(new_msg, len)) {
+        for (new_msg = rtnl->rbuffer; NLMSG_OK(new_msg, len); new_msg = NLMSG_NEXT(new_msg, len)) {
                 _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
                 const NLType *nl_type;
 
index 367f165a1c46aa2d7148beae26d89e44b42c8b6b..e4d436bb175c22d2df7cbf51ad4442294deb6041 100644 (file)
@@ -54,6 +54,12 @@ static int sd_rtnl_new(sd_rtnl **ret) {
         if (!GREEDY_REALLOC(rtnl->wqueue, rtnl->wqueue_allocated, 1))
                 return -ENOMEM;
 
+        /* We guarantee that the read buffer has at least space for
+         * a message header */
+        if (!greedy_realloc((void**)&rtnl->rbuffer, &rtnl->rbuffer_allocated,
+                            sizeof(struct nlmsghdr), sizeof(uint8_t)))
+                return -ENOMEM;
+
         *ret = rtnl;
         rtnl = NULL;
 
@@ -133,6 +139,8 @@ sd_rtnl *sd_rtnl_unref(sd_rtnl *rtnl) {
                         sd_rtnl_message_unref(rtnl->wqueue[i]);
                 free(rtnl->wqueue);
 
+                free(rtnl->rbuffer);
+
                 hashmap_free_free(rtnl->reply_callbacks);
                 prioq_free(rtnl->reply_callbacks_prioq);