[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