chiark / gitweb /
bus: reduce calls to KDBUS_CMD_MEMFD_SIZE_SET ioctl
[elogind.git] / src / libsystemd-bus / bus-message.c
index 5e355127b0cb119f460738c1c9c346a5a3114b97..d6888738e1063989594f2bc762146551f16a3925 100644 (file)
@@ -35,6 +35,7 @@
 #include "bus-type.h"
 #include "bus-signature.h"
 #include "bus-gvariant.h"
+#include "bus-util.h"
 
 static int message_append_basic(sd_bus_message *m, char type, const void *p, const void **stored);
 
@@ -64,7 +65,7 @@ static void message_free_part(sd_bus_message *m, struct bus_body_part *part) {
                  * can't be sealed yet. */
 
                 if (!part->sealed)
-                        bus_kernel_push_memfd(m->bus, part->memfd, part->data, part->mapped);
+                        bus_kernel_push_memfd(m->bus, part->memfd, part->data, part->mapped, part->allocated);
                 else {
                         if (part->mapped > 0)
                                 assert_se(munmap(part->data, part->mapped) == 0);
@@ -113,7 +114,7 @@ static void message_reset_containers(sd_bus_message *m) {
         free(m->containers);
         m->containers = NULL;
 
-        m->n_containers = 0;
+        m->n_containers = m->containers_allocated = 0;
         m->root_container.index = 0;
 }
 
@@ -161,7 +162,9 @@ static void *message_extend_fields(sd_bus_message *m, size_t align, size_t sz, b
         size_t old_size, new_size, start;
 
         assert(m);
-        assert_return(!m->poisoned, NULL);
+
+        if (m->poisoned)
+                return NULL;
 
         old_size = sizeof(struct bus_header) + m->header->fields_size;
         start = ALIGN_TO(old_size, align);
@@ -789,7 +792,9 @@ _public_ sd_bus_message* sd_bus_message_ref(sd_bus_message *m) {
 }
 
 _public_ sd_bus_message* sd_bus_message_unref(sd_bus_message *m) {
-        assert_return(m, NULL);
+
+        if (!m)
+                return NULL;
 
         assert(m->n_ref > 0);
         m->n_ref--;
@@ -987,7 +992,9 @@ struct bus_body_part *message_append_part(sd_bus_message *m) {
         struct bus_body_part *part;
 
         assert(m);
-        assert_return(!m->poisoned, NULL);
+
+        if (m->poisoned)
+                return NULL;
 
         if (m->n_body_parts <= 0) {
                 part = &m->body;
@@ -1042,20 +1049,27 @@ static int part_make_space(
                 return -ENOMEM;
 
         if (!part->data && part->memfd < 0)
-                part->memfd = bus_kernel_pop_memfd(m->bus, &part->data, &part->mapped);
+                part->memfd = bus_kernel_pop_memfd(m->bus, &part->data, &part->mapped, &part->allocated);
 
         if (part->memfd >= 0) {
-                uint64_t u = sz;
 
-                r = ioctl(part->memfd, KDBUS_CMD_MEMFD_SIZE_SET, &u);
-                if (r < 0) {
-                        m->poisoned = true;
-                        return -errno;
+                if (part->allocated == 0 || sz > part->allocated) {
+                        uint64_t new_allocated;
+
+                        new_allocated = PAGE_ALIGN(sz > 0 ? 2 * sz : 1);
+                        r = ioctl(part->memfd, KDBUS_CMD_MEMFD_SIZE_SET, &new_allocated);
+                        if (r < 0) {
+                                m->poisoned = true;
+                                return -errno;
+                        }
+
+                        part->allocated = new_allocated;
                 }
 
                 if (!part->data || sz > part->mapped) {
-                        size_t psz = PAGE_ALIGN(sz > 0 ? sz : 1);
+                        size_t psz;
 
+                        psz = PAGE_ALIGN(sz > 0 ? sz : 1);
                         if (part->mapped <= 0)
                                 n = mmap(NULL, psz, PROT_READ|PROT_WRITE, MAP_SHARED, part->memfd, 0);
                         else
@@ -1072,14 +1086,20 @@ static int part_make_space(
 
                 part->munmap_this = true;
         } else {
-                n = realloc(part->data, MAX(sz, 1u));
-                if (!n) {
-                        m->poisoned = true;
-                        return -ENOMEM;
-                }
+                if (part->allocated == 0 || sz > part->allocated) {
+                        size_t new_allocated;
+
+                        new_allocated = sz > 0 ? 2 * sz : 64;
+                        n = realloc(part->data, new_allocated);
+                        if (!n) {
+                                m->poisoned = true;
+                                return -ENOMEM;
+                        }
 
-                part->data = n;
-                part->free_this = true;
+                        part->data = n;
+                        part->allocated = new_allocated;
+                        part->free_this = true;
+                }
         }
 
         if (q)
@@ -1103,7 +1123,7 @@ static int message_add_offset(sd_bus_message *m, size_t offset) {
         if (!c->need_offsets)
                 return 0;
 
-        if (!GREEDY_REALLOC(c->offsets, c->n_offsets_allocated, c->n_offsets + 1))
+        if (!GREEDY_REALLOC(c->offsets, c->offsets_allocated, c->n_offsets + 1))
                 return -ENOMEM;
 
         c->offsets[c->n_offsets++] = offset;
@@ -1134,7 +1154,9 @@ static void *message_extend_body(sd_bus_message *m, size_t align, size_t sz, boo
         assert(m);
         assert(align > 0);
         assert(!m->sealed);
-        assert_return(!m->poisoned, NULL);
+
+        if (m->poisoned)
+                return NULL;
 
         start_body = ALIGN_TO((size_t) m->header->body_size, align);
         end_body = start_body + sz;
@@ -1835,14 +1857,11 @@ _public_ int sd_bus_message_open_container(
         assert_return(!m->poisoned, -ESTALE);
 
         /* Make sure we have space for one more container */
-        w = realloc(m->containers, sizeof(struct bus_container) * (m->n_containers + 1));
-        if (!w) {
+        if (!GREEDY_REALLOC(m->containers, m->containers_allocated, m->n_containers + 1)) {
                 m->poisoned = true;
                 return -ENOMEM;
         }
 
-        m->containers = w;
-
         c = message_get_container(m);
 
         signature = strdup(contents);
@@ -1873,14 +1892,14 @@ _public_ int sd_bus_message_open_container(
         }
 
         /* OK, let's fill it in */
-        w += m->n_containers++;
+        w = m->containers + m->n_containers++;
         w->enclosing = type;
         w->signature = signature;
         w->index = 0;
         w->array_size = array_size;
         w->before = before;
         w->begin = begin;
-        w->n_offsets = w->n_offsets_allocated = 0;
+        w->n_offsets = w->offsets_allocated = 0;
         w->offsets = NULL;
         w->need_offsets = need_offsets;
 
@@ -2687,7 +2706,7 @@ static int bus_message_close_header(sd_bus_message *m) {
         return 0;
 }
 
-int bus_message_seal(sd_bus_message *m, uint64_t serial) {
+int bus_message_seal(sd_bus_message *m, uint64_t serial, usec_t timeout) {
         struct bus_body_part *part;
         size_t l, a;
         unsigned i;
@@ -2734,6 +2753,7 @@ int bus_message_seal(sd_bus_message *m, uint64_t serial) {
                 return r;
 
         m->header->serial = serial;
+        m->timeout = m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED ? 0 : timeout;
 
         /* Add padding at the end of the fields part, since we know
          * the body needs to start at an 8 byte alignment. We made
@@ -2750,8 +2770,19 @@ int bus_message_seal(sd_bus_message *m, uint64_t serial) {
         if (m->destination && m->bus && m->bus->use_memfd) {
                 MESSAGE_FOREACH_PART(part, i, m)
                         if (part->memfd >= 0 && !part->sealed && (part->size > MEMFD_MIN_SIZE || m->bus->use_memfd < 0)) {
+                                uint64_t sz;
+
+                                /* Try to seal it if that makes
+                                 * sense. First, unmap our own map to
+                                 * make sure we don't keep it busy. */
                                 bus_body_part_unmap(part);
 
+                                /* Then, sync up real memfd size */
+                                sz = part->size;
+                                if (ioctl(part->memfd, KDBUS_CMD_MEMFD_SIZE_SET, &sz) < 0)
+                                        return -errno;
+
+                                /* Finally, try to seal */
                                 if (ioctl(part->memfd, KDBUS_CMD_MEMFD_SEAL_SET, 1) >= 0)
                                         part->sealed = true;
                         }
@@ -3845,10 +3876,8 @@ _public_ int sd_bus_message_enter_container(sd_bus_message *m,
         if (m->n_containers >= BUS_CONTAINER_DEPTH)
                 return -EBADMSG;
 
-        w = realloc(m->containers, sizeof(struct bus_container) * (m->n_containers + 1));
-        if (!w)
+        if (!GREEDY_REALLOC(m->containers, m->containers_allocated, m->n_containers + 1))
                 return -ENOMEM;
-        m->containers = w;
 
         if (message_end_of_signature(m))
                 return -ENXIO;
@@ -3883,7 +3912,7 @@ _public_ int sd_bus_message_enter_container(sd_bus_message *m,
         }
 
         /* OK, let's fill it in */
-        w += m->n_containers++;
+        w = m->containers + m->n_containers++;
         w->enclosing = type;
         w->signature = signature;
         w->index = 0;
@@ -4143,7 +4172,7 @@ _public_ int sd_bus_message_rewind(sd_bus_message *m, int complete) {
         }
 
         c->offset_index = 0;
-        c->item_size = c->n_offsets > 0 ? c->offsets[0] : c->end;
+        c->item_size = (c->n_offsets > 0 ? c->offsets[0] : c->end) - c->begin;
 
         return !isempty(c->signature);
 }
@@ -5403,3 +5432,87 @@ _public_ sd_bus *sd_bus_message_get_bus(sd_bus_message *m) {
 
         return m->bus;
 }
+
+int bus_message_remarshal(sd_bus *bus, sd_bus_message **m) {
+        _cleanup_bus_message_unref_ sd_bus_message *n = NULL;
+        usec_t timeout;
+        int r;
+
+        assert(bus);
+        assert(m);
+        assert(*m);
+
+        switch ((*m)->header->type) {
+
+        case SD_BUS_MESSAGE_SIGNAL:
+                r = sd_bus_message_new_signal(bus, (*m)->path, (*m)->interface, (*m)->member, &n);
+                if (r < 0)
+                        return r;
+
+                break;
+
+        case SD_BUS_MESSAGE_METHOD_CALL:
+                r = sd_bus_message_new_method_call(bus, (*m)->destination, (*m)->path, (*m)->interface, (*m)->member, &n);
+                if (r < 0)
+                        return r;
+
+                break;
+
+        case SD_BUS_MESSAGE_METHOD_RETURN:
+        case SD_BUS_MESSAGE_METHOD_ERROR:
+
+                n = message_new(bus, (*m)->header->type);
+                if (!n)
+                        return -ENOMEM;
+
+                n->reply_serial = (*m)->reply_serial;
+                r = message_append_field_uint32(n, BUS_MESSAGE_HEADER_REPLY_SERIAL, n->reply_serial);
+                if (r < 0)
+                        return r;
+
+                if ((*m)->header->type == SD_BUS_MESSAGE_METHOD_ERROR && (*m)->error.name) {
+                        r = message_append_field_string(n, BUS_MESSAGE_HEADER_ERROR_NAME, SD_BUS_TYPE_STRING, (*m)->error.name, &n->error.message);
+                        if (r < 0)
+                                return r;
+
+                        n->error._need_free = -1;
+                }
+
+                break;
+
+        default:
+                return -EINVAL;
+        }
+
+        if ((*m)->destination && !n->destination) {
+                r = message_append_field_string(n, BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, (*m)->destination, &n->destination);
+                if (r < 0)
+                        return r;
+        }
+
+        if ((*m)->sender && !n->sender) {
+                r = message_append_field_string(n, BUS_MESSAGE_HEADER_SENDER, SD_BUS_TYPE_STRING, (*m)->sender, &n->sender);
+                if (r < 0)
+                        return r;
+        }
+
+        n->header->flags |= (*m)->header->flags & (BUS_MESSAGE_NO_REPLY_EXPECTED|BUS_MESSAGE_NO_AUTO_START);
+
+        r = sd_bus_message_copy(n, *m, true);
+        if (r < 0)
+                return r;
+
+        timeout = (*m)->timeout;
+        if (timeout == 0 && !((*m)->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED))
+                timeout = BUS_DEFAULT_TIMEOUT;
+
+        r = bus_message_seal(n, (*m)->header->serial, timeout);
+        if (r < 0)
+                return r;
+
+        sd_bus_message_unref(*m);
+        *m = n;
+        n = NULL;
+
+        return 0;
+}