[pulseaudio-discuss] [PATCH 07/11] pulsecore: Introduce memfd support

Ahmed S. Darwish darwish.07 at gmail.com
Sun Sep 20 14:33:58 PDT 2015


Memfd is a simple memory sharing mechanism, added by the systemd/kdbus
developers, to share pages between processes in an anonymous, no global
registry needed, no mount-point required, relatively secure, manner.

We plan to use POSIX SHM only in legacy mode and exclusively use memfds
on the way forward.

Signed-off-by: Ahmed S. Darwish <darwish.07 at gmail.com>
---
 configure.ac                   |  19 ++++++++
 src/Makefile.am                |   6 +++
 src/pulsecore/memblock.c       |   8 ++-
 src/pulsecore/memfd-wrappers.h |  72 +++++++++++++++++++++++++++
 src/pulsecore/memfd.c          | 108 +++++++++++++++++++++++++++++++++++++++++
 src/pulsecore/memfd.h          |  68 ++++++++++++++++++++++++++
 6 files changed, 279 insertions(+), 2 deletions(-)
 create mode 100644 src/pulsecore/memfd-wrappers.h
 create mode 100644 src/pulsecore/memfd.c
 create mode 100644 src/pulsecore/memfd.h

diff --git a/configure.ac b/configure.ac
index 151e2b1..cee1bb4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -595,6 +595,23 @@ AC_DEFINE(HAVE_DLADDR, [1], [Have dladdr?])
 
 AM_ICONV
 
+#### Linux memfd_create(2) SHM support ####
+
+AC_ARG_ENABLE([memfd],
+    AS_HELP_STRING([--disable-memfd], [Disable Linux memfd shared memory]))
+
+AS_IF([test "x$enable_memfd" != "xno"],
+    AC_CHECK_DECL(__NR_memfd_create, [HAVE_MEMFD=1], [HAVE_MEMFD=0], [#include <sys/syscall.h>]),
+    [HAVE_MEMFD=0])
+
+AS_IF([test "x$enable_memfd" = "xyes" && test "x$HAVE_MEMFD" = "x0"],
+    [AC_MSG_ERROR([*** Your Linux kernel does not support memfd shared memory.
+                  *** Use linux v3.17 or higher for such a feature.])])
+
+AC_SUBST(HAVE_MEMFD)
+AM_CONDITIONAL([HAVE_MEMFD], [test "x$HAVE_MEMFD" = x1])
+AS_IF([test "x$HAVE_MEMFD" = "x1"], AC_DEFINE([HAVE_MEMFD], 1, [Have memfd shared memory.]))
+
 #### X11 (optional) ####
 
 AC_ARG_ENABLE([x11],
@@ -1517,6 +1534,7 @@ AC_OUTPUT
 
 # ==========================================================================
 
+AS_IF([test "x$HAVE_MEMFD" = "x1"], ENABLE_MEMFD=yes, ENABLE_MEMFD=no)
 AS_IF([test "x$HAVE_X11" = "x1"], ENABLE_X11=yes, ENABLE_X11=no)
 AS_IF([test "x$HAVE_OSS_OUTPUT" = "x1"], ENABLE_OSS_OUTPUT=yes, ENABLE_OSS_OUTPUT=no)
 AS_IF([test "x$HAVE_OSS_WRAPPER" = "x1"], ENABLE_OSS_WRAPPER=yes, ENABLE_OSS_WRAPPER=no)
@@ -1578,6 +1596,7 @@ echo "
     CPPFLAGS:                      ${CPPFLAGS}
     LIBS:                          ${LIBS}
 
+    Enable memfd shared memory:    ${ENABLE_MEMFD}
     Enable X11:                    ${ENABLE_X11}
     Enable OSS Output:             ${ENABLE_OSS_OUTPUT}
     Enable OSS Wrapper:            ${ENABLE_OSS_WRAPPER}
diff --git a/src/Makefile.am b/src/Makefile.am
index 0686c8a..423cd90 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -699,6 +699,7 @@ libpulsecommon_ at PA_MAJORMINOR@_la_SOURCES = \
 		pulsecore/sample-util.c pulsecore/sample-util.h \
 		pulsecore/mem.h pulsecore/mem.c \
 		pulsecore/shm.c pulsecore/shm.h \
+		pulsecore/memfd.h \
 		pulsecore/privatemem.c pulsecore/privatemem.h \
 		pulsecore/bitset.c pulsecore/bitset.h \
 		pulsecore/socket-client.c pulsecore/socket-client.h \
@@ -727,6 +728,11 @@ libpulsecommon_ at PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(LIBJSON_CFLAGS) $(LIBS
 libpulsecommon_ at PA_MAJORMINOR@_la_LDFLAGS = $(AM_LDFLAGS) $(AM_LIBLDFLAGS) -avoid-version
 libpulsecommon_ at PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) $(LIBJSON_LIBS)  $(LIBWRAP_LIBS) $(WINSOCK_LIBS) $(LTLIBICONV) $(LIBSNDFILE_LIBS)
 
+if HAVE_MEMFD
+libpulsecommon_ at PA_MAJORMINOR@_la_SOURCES += \
+		pulsecore/memfd.c pulsecore/memfd-wrappers.h
+endif
+
 if HAVE_X11
 libpulsecommon_ at PA_MAJORMINOR@_la_SOURCES += \
 		pulse/client-conf-x11.c pulse/client-conf-x11.h \
diff --git a/src/pulsecore/memblock.c b/src/pulsecore/memblock.c
index ab117d8..e0e4dfb 100644
--- a/src/pulsecore/memblock.c
+++ b/src/pulsecore/memblock.c
@@ -38,6 +38,7 @@
 
 #include <pulsecore/mem.h>
 #include <pulsecore/shm.h>
+#include <pulsecore/memfd.h>
 #include <pulsecore/privatemem.h>
 #include <pulsecore/log.h>
 #include <pulsecore/hashmap.h>
@@ -152,6 +153,7 @@ struct pa_mempool {
         pa_mem mem;
         union {
             pa_shm shm;
+            pa_memfd memfd;
             pa_privatemem privatemem;
         } per_type;
     };
@@ -791,12 +793,14 @@ pa_mempool *pa_mempool_new(pa_mempool_type_t type, size_t size) {
     pa_mem_size = p->n_blocks * p->block_size;
 
     switch (type) {
+
     case PA_MEMPOOL_SHARED_POSIX:
         if (pa_shm_create(&p->per_type.shm, pa_mem_size, 0700) < 0)
             goto fail;
         break;
     case PA_MEMPOOL_SHARED_MEMFD:
-        pa_assert_not_reached();
+        if (pa_memfd_create(&p->per_type.memfd, pa_mem_size) < 0)
+            goto fail;
         break;
     case PA_MEMPOOL_PRIVATE:
         if (pa_privatemem_create(&p->per_type.privatemem, pa_mem_size) < 0)
@@ -895,7 +899,7 @@ void pa_mempool_free(pa_mempool *p) {
         pa_shm_free(&p->per_type.shm);
         break;
     case PA_MEMPOOL_SHARED_MEMFD:
-        pa_assert_not_reached();
+        pa_memfd_free(&p->per_type.memfd);
         break;
     case PA_MEMPOOL_PRIVATE:
         pa_privatemem_free(&p->per_type.privatemem);
diff --git a/src/pulsecore/memfd-wrappers.h b/src/pulsecore/memfd-wrappers.h
new file mode 100644
index 0000000..ef222ac
--- /dev/null
+++ b/src/pulsecore/memfd-wrappers.h
@@ -0,0 +1,72 @@
+#ifndef SRC_PULSECORE_MEMFD_WRAPPERS_H_
+#define SRC_PULSECORE_MEMFD_WRAPPERS_H_
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2015 Ahmed S. Darwish <darwish.07 at gmail.com>
+
+  PulseAudio 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.
+
+  PulseAudio 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 PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_MEMFD
+
+#include <sys/syscall.h>
+#include <fcntl.h>
+
+/*
+ * No glibc wrappers exist for memfd_create(2), so provide our own.
+ *
+ * Also define memfd fcntl sealing macros. While they are already
+ * defined in the kernel header file <linux/fcntl.h>, that file as
+ * a whole conflicts with the original glibc header <fnctl.h>.
+ */
+
+static inline int memfd_create(const char *name, unsigned int flags) {
+    return syscall(__NR_memfd_create, name, flags);
+}
+
+/* memfd_create(2) flags */
+
+#ifndef MFD_CLOEXEC
+#define MFD_CLOEXEC       0x0001U
+#endif
+
+#ifndef MFD_ALLOW_SEALING
+#define MFD_ALLOW_SEALING 0x0002U
+#endif
+
+/* fcntl() seals-related flags */
+
+#ifndef F_LINUX_SPECIFIC_BASE
+#define F_LINUX_SPECIFIC_BASE 1024
+#endif
+
+#ifndef F_ADD_SEALS
+#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
+#define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10)
+
+#define F_SEAL_SEAL     0x0001  /* prevent further seals from being set */
+#define F_SEAL_SHRINK   0x0002  /* prevent file from shrinking */
+#define F_SEAL_GROW     0x0004  /* prevent file from growing */
+#define F_SEAL_WRITE    0x0008  /* prevent writes */
+#endif
+
+#else
+
+#error "Please include this file only when memfd is supported."
+
+#endif /* HAVE_MEMFD */
+
+#endif /* SRC_PULSECORE_MEMFD_WRAPPERS_H_ */
diff --git a/src/pulsecore/memfd.c b/src/pulsecore/memfd.c
new file mode 100644
index 0000000..429e82e
--- /dev/null
+++ b/src/pulsecore/memfd.c
@@ -0,0 +1,108 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2015 Ahmed S. Darwish <darwish.07 at gmail.com>
+
+  PulseAudio 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.
+
+  PulseAudio 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 PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/mman.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <errno.h>
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/macro.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/memfd-wrappers.h>
+#include <pulsecore/memfd.h>
+
+static void memfd_reset(pa_memfd *memfd) {
+    memfd->ptr = NULL;
+    memfd->size = 0;
+    memfd->fd = -1;
+}
+
+static int __pa_memfd_create(pa_memfd *memfd, int fd, size_t size, bool writable) {
+    int prot;
+
+    memfd->fd = fd;
+    memfd->size = size;
+    prot = writable ? PROT_READ | PROT_WRITE : PROT_READ;
+
+    memfd->ptr= mmap(NULL, size, prot, MAP_SHARED | MAP_NORESERVE, fd, 0);
+    if (memfd->ptr == MAP_FAILED) {
+        pa_log_error("Failed to mmap memfd fd %d: %s", fd, pa_cstrerror(errno));
+        goto fail;
+    }
+
+    return 0;
+
+fail:
+    memfd_reset(memfd);
+    return -1;
+}
+
+int pa_memfd_create(pa_memfd *memfd, size_t size) {
+    int fd;
+    pa_assert(memfd);
+    pa_assert(size > 0);
+    pa_assert(size <= MAX_SHARED_MEM_SIZE);
+
+    if ((fd = memfd_create("memfd", MFD_ALLOW_SEALING)) < 0) {
+        pa_log_error("Failed to create memfd file descriptor: %s", pa_cstrerror(errno));
+        goto fail0;
+    }
+
+    if (ftruncate(fd, size) < 0) {
+        pa_log_error("Failed to truncate memfd fd %d to %ld bytes: %s", fd, size, pa_cstrerror(errno));
+        goto fail1;
+    }
+
+    return __pa_memfd_create(memfd, fd, size, true);
+
+fail1:
+    close(fd);
+fail0:
+    return -1;
+}
+
+int pa_memfd_attach(pa_memfd *memfd, int fd, bool writable) {
+    size_t size;
+    pa_assert(memfd);
+    pa_assert(fd >= 0);
+
+    if (shared_fd_get_file_size(fd, &size) < 0 || size > MAX_SHARED_MEM_SIZE)
+        return -1;
+
+    return __pa_memfd_create(memfd, fd, size, writable);
+}
+
+void pa_memfd_free(pa_memfd *memfd) {
+    pa_assert(memfd);
+    pa_assert(memfd->ptr);
+    pa_assert(memfd->fd >= 0);
+
+    if (munmap(memfd->ptr, memfd->size) < 0)
+        pa_log_error("memfd munmap() failed: %s", pa_cstrerror(errno));
+
+    if (close(memfd->fd) < 0)
+        pa_log_error("memfd close() failed: %s", pa_cstrerror(errno));
+
+    memfd_reset(memfd);
+}
diff --git a/src/pulsecore/memfd.h b/src/pulsecore/memfd.h
new file mode 100644
index 0000000..2c07d8e
--- /dev/null
+++ b/src/pulsecore/memfd.h
@@ -0,0 +1,68 @@
+#ifndef SRC_PULSECORE_MEMFD_H_
+#define SRC_PULSECORE_MEMFD_H_
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2015 Ahmed S. Darwish <darwish.07 at gmail.com>
+
+  PulseAudio 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.
+
+  PulseAudio 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 PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "mem.h"
+
+/* A mempool memory allocation backend using Linux kernel memfds.
+ *
+ * For a rationale on why memfds were created (and why are we thus
+ * using them in PulseAudio), please check:
+ *
+ *   - `memfd_create(2)', David Herrmann blog
+ *      https://dvdhrm.wordpress.com/2014/06/10/memfd_create2/
+ */
+typedef struct pa_memfd {
+    __PA_PARENT_MEM_STRUCT__;
+    int fd;
+} pa_memfd;
+
+#ifdef HAVE_MEMFD
+
+static inline bool pa_memfd_is_supported() {
+    return true;
+}
+
+int pa_memfd_create(pa_memfd *memfd, size_t size);
+int pa_memfd_attach(pa_memfd *memfd, int fd, bool writable);
+void pa_memfd_free(pa_memfd *memfd);
+
+#else
+
+#include <pulsecore/macro.h>
+
+static inline bool PA_UNUSED pa_memfd_is_supported() {
+    return false;
+}
+
+static inline int pa_memfd_create(pa_memfd *memfd, size_t size) {
+    pa_assert_not_reached();
+}
+static inline int pa_memfd_attach(pa_memfd *memfd, int fd, bool writable) {
+    pa_assert_not_reached();
+}
+static inline void pa_memfd_free(pa_memfd *memfd) {
+    pa_assert_not_reached();
+}
+
+#endif
+
+#endif /* SRC_PULSECORE_MEMFD_H_ */

-- 
Darwish
http://darwish.chasingpointers.com


More information about the pulseaudio-discuss mailing list