diff --git a/.gitignore b/.gitignore index d53e75a43e0..2dd1d715dd5 100644 --- a/.gitignore +++ b/.gitignore @@ -88,6 +88,7 @@ /test-bus-memfd /test-bus-signature /test-bus-server +/test-bus-zero-copy /test-calendarspec /test-catalog /test-cgroup diff --git a/Makefile.am b/Makefile.am index eb85c8dadad..fa626c5a2fd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1758,7 +1758,8 @@ tests += \ test-bus-server \ test-bus-match \ test-bus-kernel \ - test-bus-memfd + test-bus-memfd \ + test-bus-zero-copy noinst_PROGRAMS += \ busctl @@ -1840,6 +1841,16 @@ test_bus_memfd_LDADD = \ libsystemd-shared.la \ libsystemd-bus.la +test_bus_zero_copy_SOURCES = \ + src/libsystemd-bus/test-bus-zero-copy.c + +test_bus_zero_copy_CFLAGS = \ + $(AM_CFLAGS) + +test_bus_zero_copy_LDADD = \ + libsystemd-shared.la \ + libsystemd-bus.la + busctl_SOURCES = \ src/libsystemd-bus/busctl.c diff --git a/src/libsystemd-bus/bus-kernel.c b/src/libsystemd-bus/bus-kernel.c index 81469365f0f..54a5e1691cc 100644 --- a/src/libsystemd-bus/bus-kernel.c +++ b/src/libsystemd-bus/bus-kernel.c @@ -486,10 +486,11 @@ static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k, sd_bus_mess part->data = UINT64_TO_PTR(d->vec.address); part->size = d->vec.size; } else { - part->data = (uint8_t*) UINT64_TO_PTR(d->vec.address) + (begin_body - idx); + part->data = d->vec.address != 0 ? (uint8_t*) UINT64_TO_PTR(d->vec.address) + (begin_body - idx) : NULL; part->size = d->vec.size - (begin_body - idx); } + part->is_zero = d->vec.address == 0; part->sealed = true; } diff --git a/src/libsystemd-bus/bus-message.c b/src/libsystemd-bus/bus-message.c index c1e1c468b81..747b44ac942 100644 --- a/src/libsystemd-bus/bus-message.c +++ b/src/libsystemd-bus/bus-message.c @@ -69,7 +69,9 @@ static void message_free_part(sd_bus_message *m, struct bus_body_part *part) { close_nointr_nofail(part->memfd); } - } else if (part->free_this) + } else if (part->munmap_this) + munmap(part->data, part->mapped); + else if (part->free_this) free(part->data); if (part != &m->body) @@ -1119,8 +1121,13 @@ static void part_zero(struct bus_body_part *part, size_t sz) { assert(sz > 0); assert(sz < 8); - part->data = NULL; + /* All other fields can be left in their defaults */ + assert(!part->data); + assert(part->memfd < 0); + part->size = sz; + part->is_zero = true; + part->sealed = true; } static int part_make_space( @@ -1151,8 +1158,8 @@ static int part_make_space( return -errno; } - if (sz > part->mapped) { - size_t psz = PAGE_ALIGN(sz); + if (!part->data || sz > part->mapped) { + size_t psz = PAGE_ALIGN(sz > 0 ? sz : 1); if (part->mapped <= 0) n = mmap(NULL, psz, PROT_READ|PROT_WRITE, MAP_SHARED, part->memfd, 0); @@ -1166,6 +1173,7 @@ static int part_make_space( part->mapped = psz; part->data = n; + part->munmap_this = true; } } else { n = realloc(part->data, sz); @@ -1185,8 +1193,22 @@ static int part_make_space( return 0; } -static void *message_extend_body(sd_bus_message *m, size_t align, size_t sz) { +static void message_extend_containers(sd_bus_message *m, size_t expand) { struct bus_container *c; + + assert(m); + + if (expand <= 0) + return; + + /* Update counters */ + for (c = m->containers; c < m->containers + m->n_containers; c++) + if (c->array_size) + *c->array_size += expand; + +} + +static void *message_extend_body(sd_bus_message *m, size_t align, size_t sz) { struct bus_body_part *part = NULL; size_t start_body, end_body, padding, start_part, end_part, added; bool add_new_part; @@ -1234,6 +1256,7 @@ static void *message_extend_body(sd_bus_message *m, size_t align, size_t sz) { if (r < 0) return NULL; } else { + struct bus_container *c; void *op; size_t os; @@ -1260,12 +1283,8 @@ static void *message_extend_body(sd_bus_message *m, size_t align, size_t sz) { m->error.message = (const char*) adjust_pointer(m->error.message, op, os, part->data); } - /* Update counters */ - for (c = m->containers; c < m->containers + m->n_containers; c++) - if (c->array_size) - *c->array_size += added; - m->header->body_size = end_body; + message_extend_containers(m, added); return p; } @@ -2099,6 +2118,101 @@ int sd_bus_message_append_array(sd_bus_message *m, char type, const void *ptr, s return 0; } +int sd_bus_message_append_array_memfd(sd_bus_message *m, char type, sd_memfd *memfd) { + _cleanup_close_ int copy_fd = -1; + struct bus_body_part *part; + ssize_t align, sz; + uint64_t size; + void *a; + int r; + + if (!m) + return -EINVAL; + if (!memfd) + return -EINVAL; + if (m->sealed) + return -EPERM; + if (!bus_type_is_trivial(type)) + return -EINVAL; + if (m->poisoned) + return -ESTALE; + + r = sd_memfd_set_sealed(memfd, true); + if (r < 0) + return r; + + copy_fd = sd_memfd_dup_fd(memfd); + if (copy_fd < 0) + return copy_fd; + + r = sd_memfd_get_size(memfd, &size); + if (r < 0) + return r; + + align = bus_type_get_alignment(type); + sz = bus_type_get_size(type); + + assert_se(align > 0); + assert_se(sz > 0); + + if (size % sz != 0) + return -EINVAL; + + if (size > (size_t) (uint32_t) -1) + return -EINVAL; + + r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, CHAR_TO_STR(type)); + if (r < 0) + return r; + + a = message_extend_body(m, align, 0); + if (!a) + return -ENOMEM; + + part = message_append_part(m); + if (!part) + return -ENOMEM; + + part->memfd = copy_fd; + part->sealed = true; + part->size = size; + copy_fd = -1; + + message_extend_containers(m, size); + m->header->body_size += size; + + return sd_bus_message_close_container(m); +} + +static int body_part_map_for_read(struct bus_body_part *part) { + void *p; + size_t psz; + + assert_se(part); + + if (part->data) + return 0; + + if (part->size <= 0) + return 0; + + psz = PAGE_ALIGN(part->size); + + if (part->memfd >= 0) + p = mmap(NULL, psz, PROT_READ, MAP_SHARED, part->memfd, 0); + else if (part->is_zero) + p = mmap(NULL, psz, PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + else + return -EINVAL; + + if (p == MAP_FAILED) + return -errno; + + part->mapped = psz; + part->data = p; + return 0; +} + static int buffer_peek(const void *p, uint32_t sz, size_t *rindex, size_t align, size_t nbytes, void **r) { size_t k, start, end; @@ -2139,6 +2253,8 @@ static bool message_end_of_array(sd_bus_message *m, size_t index) { static struct bus_body_part* find_part(sd_bus_message *m, size_t index, size_t sz, void **p) { struct bus_body_part *part; size_t begin; + int r; + assert(m); if (m->cached_rindex_part && index >= m->cached_rindex_part_begin) { @@ -2154,8 +2270,13 @@ static struct bus_body_part* find_part(sd_bus_message *m, size_t index, size_t s return NULL; if (index + sz <= begin + part->size) { + + r = body_part_map_for_read(part); + if (r < 0) + return NULL; + if (p) - *p = part->data ? (uint8_t*) part->data + index - begin : NULL; + *p = (uint8_t*) part->data + index - begin; m->cached_rindex_part = part; m->cached_rindex_part_begin = begin; @@ -2163,6 +2284,7 @@ static struct bus_body_part* find_part(sd_bus_message *m, size_t index, size_t s return part; } + begin += part->size; part = part->next; } @@ -3638,7 +3760,8 @@ int bus_message_dump(sd_bus_message *m) { "\treply_serial=%u\n" "\terror.name=%s\n" "\terror.message=%s\n" - "\tsealed=%s\n", + "\tsealed=%s\n" + "\tn_body_parts=%u\n", m, m->n_ref, m->header->endian, @@ -3657,7 +3780,8 @@ int bus_message_dump(sd_bus_message *m) { m->reply_serial, strna(m->error.name), strna(m->error.message), - yes_no(m->sealed)); + yes_no(m->sealed), + m->n_body_parts); if (m->pid != 0) printf("\tpid=%lu\n", (unsigned long) m->pid); diff --git a/src/libsystemd-bus/bus-message.h b/src/libsystemd-bus/bus-message.h index 2517514bac1..f9c8fc9c083 100644 --- a/src/libsystemd-bus/bus-message.h +++ b/src/libsystemd-bus/bus-message.h @@ -33,9 +33,10 @@ struct bus_container { char enclosing; - char *signature; unsigned index, saved_index; + char *signature; + uint32_t *array_size; size_t before, begin; }; @@ -51,13 +52,15 @@ struct bus_header { } _packed_; struct bus_body_part { + struct bus_body_part *next; void *data; size_t size; size_t mapped; int memfd; bool free_this:1; + bool munmap_this:1; bool sealed:1; - struct bus_body_part *next; + bool is_zero:1; }; struct sd_bus_message { diff --git a/src/libsystemd-bus/sd-memfd.c b/src/libsystemd-bus/sd-memfd.c index 2712a26f660..bd14da3a70b 100644 --- a/src/libsystemd-bus/sd-memfd.c +++ b/src/libsystemd-bus/sd-memfd.c @@ -205,3 +205,27 @@ int sd_memfd_set_size(sd_memfd *m, uint64_t sz) { return r; } + +int sd_memfd_new_and_map(sd_memfd **m, size_t sz, void **p) { + sd_memfd *n; + int r; + + r = sd_memfd_new(&n); + if (r < 0) + return r; + + r = sd_memfd_set_size(n, sz); + if (r < 0) { + sd_memfd_free(n); + return r; + } + + r = sd_memfd_map(n, 0, sz, p); + if (r < 0) { + sd_memfd_free(n); + return r; + } + + *m = n; + return 0; +} diff --git a/src/libsystemd-bus/test-bus-zero-copy.c b/src/libsystemd-bus/test-bus-zero-copy.c new file mode 100644 index 00000000000..024a0bfddf9 --- /dev/null +++ b/src/libsystemd-bus/test-bus-zero-copy.c @@ -0,0 +1,112 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "util.h" +#include "log.h" + +#include "sd-bus.h" +#include "sd-memfd.h" +#include "bus-message.h" +#include "bus-error.h" +#include "bus-kernel.h" + +int main(int argc, char *argv[]) { + _cleanup_free_ char *bus_name = NULL, *address = NULL; + void *p; + sd_bus *a, *b; + int r, bus_ref; + sd_bus_message *m; + sd_memfd *f; + + log_set_max_level(LOG_DEBUG); + + bus_ref = bus_kernel_create("deine-mutter", &bus_name); + if (bus_ref == -ENOENT) + return EXIT_TEST_SKIP; + + assert_se(bus_ref >= 0); + + address = strappend("kernel:path=", bus_name); + assert_se(address); + + r = sd_bus_new(&a); + assert_se(r >= 0); + + r = sd_bus_new(&b); + assert_se(r >= 0); + + r = sd_bus_set_address(a, address); + assert_se(r >= 0); + + r = sd_bus_set_address(b, address); + assert_se(r >= 0); + + r = sd_bus_start(a); + assert_se(r >= 0); + + r = sd_bus_start(b); + assert_se(r >= 0); + + r = sd_bus_message_new_method_call(b, ":1.1", "/a/path", "an.inter.face", "AMethod", &m); + assert_se(r >= 0); + + r = sd_bus_message_open_container(m, 'r', "ayay"); + assert_se(r >= 0); + + r = sd_bus_message_append_array_space(m, 'y', 32, &p); + assert_se(r >= 0); + + memset(p, 'L', 32); + + r = sd_memfd_new_and_map(&f, 32, &p); + assert_se(r >= 0); + + memset(p, 'P', 32); + munmap(p, 32); + + r = sd_memfd_set_size(f, 32); + assert_se(r >= 0); + + r = sd_bus_message_append_array_memfd(m, 'y', f); + assert_se(r >= 0); + + r = sd_bus_message_close_container(m); + assert_se(r >= 0); + + r = bus_message_seal(m, 55); + assert_se(r >= 0); + + bus_message_dump(m); + + r = sd_bus_send(b, m, NULL); + assert_se(r >= 0); + + sd_bus_message_unref(m); + + sd_bus_unref(a); + sd_bus_unref(b); + sd_memfd_free(f); + + return 0; +} diff --git a/src/systemd/sd-bus.h b/src/systemd/sd-bus.h index 28c85369926..4c3c26d6110 100644 --- a/src/systemd/sd-bus.h +++ b/src/systemd/sd-bus.h @@ -27,6 +27,7 @@ #include #include "sd-bus-protocol.h" +#include "sd-memfd.h" #ifdef __cplusplus extern "C" { @@ -160,7 +161,9 @@ int sd_bus_message_append(sd_bus_message *m, const char *types, ...); int sd_bus_message_append_basic(sd_bus_message *m, char type, const void *p); int sd_bus_message_append_array(sd_bus_message *m, char type, const void *ptr, size_t size); int sd_bus_message_append_array_space(sd_bus_message *m, char type, size_t size, void **ptr); +int sd_bus_message_append_array_memfd(sd_bus_message *m, char type, sd_memfd *memfd); int sd_bus_message_append_string_space(sd_bus_message *m, size_t size, char **s); +int sd_bus_message_append_string_memfd(sd_bus_message *m, sd_memfd* memfd); int sd_bus_message_open_container(sd_bus_message *m, char type, const char *contents); int sd_bus_message_close_container(sd_bus_message *m); diff --git a/src/systemd/sd-memfd.h b/src/systemd/sd-memfd.h index 8594a3b61fb..ee140e48d3f 100644 --- a/src/systemd/sd-memfd.h +++ b/src/systemd/sd-memfd.h @@ -35,6 +35,8 @@ typedef struct sd_memfd sd_memfd; int sd_memfd_new(sd_memfd **m); int sd_memfd_make(int fd, sd_memfd **m); +int sd_memfd_new_and_map(sd_memfd **m, size_t sz, void **p); + void sd_memfd_free(sd_memfd *m); int sd_memfd_get_fd(sd_memfd *m);