diff options
author | Tom Gundersen <teg@jklm.no> | 2014-04-14 17:20:51 +0200 |
---|---|---|
committer | Tom Gundersen <teg@jklm.no> | 2014-04-14 17:53:21 +0200 |
commit | a88f77c406ea56160c1e5a4b39eab1de134abe40 (patch) | |
tree | d1da4d5a0fae4a11dfa5429e65c03b09109a55a6 /src/libsystemd/sd-rtnl/rtnl-message.c | |
parent | 1403f45ab61d6f2026a3a7a06b52a536c1b7a3b3 (diff) |
sd-rtnl: socket_read - use a read buffer
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.
Diffstat (limited to 'src/libsystemd/sd-rtnl/rtnl-message.c')
-rw-r--r-- | src/libsystemd/sd-rtnl/rtnl-message.c | 57 |
1 files changed, 27 insertions, 30 deletions
diff --git a/src/libsystemd/sd-rtnl/rtnl-message.c b/src/libsystemd/sd-rtnl/rtnl-message.c index 3a24cb885f..a93cb0c8e4 100644 --- a/src/libsystemd/sd-rtnl/rtnl-message.c +++ b/src/libsystemd/sd-rtnl/rtnl-message.c @@ -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; |