153a5a1b3Sopenharmony_ci/*** 253a5a1b3Sopenharmony_ci This file is part of PulseAudio. 353a5a1b3Sopenharmony_ci 453a5a1b3Sopenharmony_ci Copyright 2006 Lennart Poettering 553a5a1b3Sopenharmony_ci Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB 653a5a1b3Sopenharmony_ci 753a5a1b3Sopenharmony_ci PulseAudio is free software; you can redistribute it and/or modify 853a5a1b3Sopenharmony_ci it under the terms of the GNU Lesser General Public License as 953a5a1b3Sopenharmony_ci published by the Free Software Foundation; either version 2.1 of the 1053a5a1b3Sopenharmony_ci License, or (at your option) any later version. 1153a5a1b3Sopenharmony_ci 1253a5a1b3Sopenharmony_ci PulseAudio is distributed in the hope that it will be useful, but 1353a5a1b3Sopenharmony_ci WITHOUT ANY WARRANTY; without even the implied warranty of 1453a5a1b3Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1553a5a1b3Sopenharmony_ci Lesser General Public License for more details. 1653a5a1b3Sopenharmony_ci 1753a5a1b3Sopenharmony_ci You should have received a copy of the GNU Lesser General Public 1853a5a1b3Sopenharmony_ci License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 1953a5a1b3Sopenharmony_ci***/ 2053a5a1b3Sopenharmony_ci 2153a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H 2253a5a1b3Sopenharmony_ci#include <config.h> 2353a5a1b3Sopenharmony_ci#endif 2453a5a1b3Sopenharmony_ci 2553a5a1b3Sopenharmony_ci#ifndef LOG_TAG 2653a5a1b3Sopenharmony_ci#define LOG_TAG "Shm" 2753a5a1b3Sopenharmony_ci#endif 2853a5a1b3Sopenharmony_ci 2953a5a1b3Sopenharmony_ci#include <stdlib.h> 3053a5a1b3Sopenharmony_ci#include <unistd.h> 3153a5a1b3Sopenharmony_ci#include <fcntl.h> 3253a5a1b3Sopenharmony_ci#include <stdio.h> 3353a5a1b3Sopenharmony_ci#include <errno.h> 3453a5a1b3Sopenharmony_ci#include <string.h> 3553a5a1b3Sopenharmony_ci#include <sys/stat.h> 3653a5a1b3Sopenharmony_ci#include <sys/types.h> 3753a5a1b3Sopenharmony_ci#include <dirent.h> 3853a5a1b3Sopenharmony_ci#include <signal.h> 3953a5a1b3Sopenharmony_ci 4053a5a1b3Sopenharmony_ci#ifdef HAVE_SYS_MMAN_H 4153a5a1b3Sopenharmony_ci#include <sys/mman.h> 4253a5a1b3Sopenharmony_ci#endif 4353a5a1b3Sopenharmony_ci 4453a5a1b3Sopenharmony_ci/* This is deprecated on glibc but is still used by FreeBSD */ 4553a5a1b3Sopenharmony_ci#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) 4653a5a1b3Sopenharmony_ci# define MAP_ANONYMOUS MAP_ANON 4753a5a1b3Sopenharmony_ci#endif 4853a5a1b3Sopenharmony_ci 4953a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h> 5053a5a1b3Sopenharmony_ci#include <pulse/gccmacro.h> 5153a5a1b3Sopenharmony_ci 5253a5a1b3Sopenharmony_ci#include <pulsecore/memfd-wrappers.h> 5353a5a1b3Sopenharmony_ci#include <pulsecore/core-error.h> 5453a5a1b3Sopenharmony_ci#include <pulsecore/log.h> 5553a5a1b3Sopenharmony_ci#include <pulsecore/random.h> 5653a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h> 5753a5a1b3Sopenharmony_ci#include <pulsecore/macro.h> 5853a5a1b3Sopenharmony_ci#include <pulsecore/atomic.h> 5953a5a1b3Sopenharmony_ci#include <pulsecore/mem.h> 6053a5a1b3Sopenharmony_ci 6153a5a1b3Sopenharmony_ci#include "shm.h" 6253a5a1b3Sopenharmony_ci 6353a5a1b3Sopenharmony_ci#if defined(__linux__) && !defined(MADV_REMOVE) 6453a5a1b3Sopenharmony_ci#define MADV_REMOVE 9 6553a5a1b3Sopenharmony_ci#endif 6653a5a1b3Sopenharmony_ci 6753a5a1b3Sopenharmony_ci/* 1 GiB at max */ 6853a5a1b3Sopenharmony_ci#define MAX_SHM_SIZE (PA_ALIGN(1024*1024*1024)) 6953a5a1b3Sopenharmony_ci 7053a5a1b3Sopenharmony_ci#ifdef __linux__ 7153a5a1b3Sopenharmony_ci/* On Linux we know that the shared memory blocks are files in 7253a5a1b3Sopenharmony_ci * /dev/shm. We can use that information to list all blocks and 7353a5a1b3Sopenharmony_ci * cleanup unused ones */ 7453a5a1b3Sopenharmony_ci#define SHM_PATH "/dev/shm/" 7553a5a1b3Sopenharmony_ci#define SHM_ID_LEN 10 7653a5a1b3Sopenharmony_ci#elif defined(__sun) 7753a5a1b3Sopenharmony_ci#define SHM_PATH "/tmp" 7853a5a1b3Sopenharmony_ci#define SHM_ID_LEN 15 7953a5a1b3Sopenharmony_ci#else 8053a5a1b3Sopenharmony_ci#undef SHM_PATH 8153a5a1b3Sopenharmony_ci#undef SHM_ID_LEN 8253a5a1b3Sopenharmony_ci#endif 8353a5a1b3Sopenharmony_ci 8453a5a1b3Sopenharmony_ci#define SHM_MARKER ((int) 0xbeefcafe) 8553a5a1b3Sopenharmony_ci 8653a5a1b3Sopenharmony_ci/* We now put this SHM marker at the end of each segment. It's 8753a5a1b3Sopenharmony_ci * optional, to not require a reboot when upgrading, though. Note that 8853a5a1b3Sopenharmony_ci * on multiarch systems 32bit and 64bit processes might access this 8953a5a1b3Sopenharmony_ci * region simultaneously. The header fields need to be independent 9053a5a1b3Sopenharmony_ci * from the process' word with */ 9153a5a1b3Sopenharmony_cistruct shm_marker { 9253a5a1b3Sopenharmony_ci pa_atomic_t marker; /* 0xbeefcafe */ 9353a5a1b3Sopenharmony_ci pa_atomic_t pid; 9453a5a1b3Sopenharmony_ci uint64_t _reserved1; 9553a5a1b3Sopenharmony_ci uint64_t _reserved2; 9653a5a1b3Sopenharmony_ci uint64_t _reserved3; 9753a5a1b3Sopenharmony_ci uint64_t _reserved4; 9853a5a1b3Sopenharmony_ci}; 9953a5a1b3Sopenharmony_ci 10053a5a1b3Sopenharmony_ci// Ensure struct is appropriately packed 10153a5a1b3Sopenharmony_cistatic_assert(sizeof(struct shm_marker) == 8 * 5, "`struct shm_marker` is not tightly packed"); 10253a5a1b3Sopenharmony_ci 10353a5a1b3Sopenharmony_cistatic inline size_t shm_marker_size(pa_mem_type_t type) { 10453a5a1b3Sopenharmony_ci if (type == PA_MEM_TYPE_SHARED_POSIX) 10553a5a1b3Sopenharmony_ci return PA_ALIGN(sizeof(struct shm_marker)); 10653a5a1b3Sopenharmony_ci 10753a5a1b3Sopenharmony_ci return 0; 10853a5a1b3Sopenharmony_ci} 10953a5a1b3Sopenharmony_ci 11053a5a1b3Sopenharmony_ci#ifdef HAVE_SHM_OPEN 11153a5a1b3Sopenharmony_cistatic char *segment_name(char *fn, size_t l, unsigned id) { 11253a5a1b3Sopenharmony_ci pa_snprintf(fn, l, "/pulse-shm-%u", id); 11353a5a1b3Sopenharmony_ci return fn; 11453a5a1b3Sopenharmony_ci} 11553a5a1b3Sopenharmony_ci#endif 11653a5a1b3Sopenharmony_ci 11753a5a1b3Sopenharmony_cistatic int privatemem_create(pa_shm *m, size_t size) { 11853a5a1b3Sopenharmony_ci pa_assert(m); 11953a5a1b3Sopenharmony_ci pa_assert(size > 0); 12053a5a1b3Sopenharmony_ci 12153a5a1b3Sopenharmony_ci m->type = PA_MEM_TYPE_PRIVATE; 12253a5a1b3Sopenharmony_ci m->id = 0; 12353a5a1b3Sopenharmony_ci m->size = size; 12453a5a1b3Sopenharmony_ci m->do_unlink = false; 12553a5a1b3Sopenharmony_ci m->fd = -1; 12653a5a1b3Sopenharmony_ci 12753a5a1b3Sopenharmony_ci#ifdef MAP_ANONYMOUS 12853a5a1b3Sopenharmony_ci if ((m->ptr = mmap(NULL, m->size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, (off_t) 0)) == MAP_FAILED) { 12953a5a1b3Sopenharmony_ci pa_log_error("mmap() failed: %s", pa_cstrerror(errno)); 13053a5a1b3Sopenharmony_ci return -1; 13153a5a1b3Sopenharmony_ci } 13253a5a1b3Sopenharmony_ci#elif defined(HAVE_POSIX_MEMALIGN) 13353a5a1b3Sopenharmony_ci { 13453a5a1b3Sopenharmony_ci int r; 13553a5a1b3Sopenharmony_ci 13653a5a1b3Sopenharmony_ci if ((r = posix_memalign(&m->ptr, pa_page_size(), size)) < 0) { 13753a5a1b3Sopenharmony_ci pa_log_error("posix_memalign() failed: %s", pa_cstrerror(r)); 13853a5a1b3Sopenharmony_ci return r; 13953a5a1b3Sopenharmony_ci } 14053a5a1b3Sopenharmony_ci } 14153a5a1b3Sopenharmony_ci#else 14253a5a1b3Sopenharmony_ci m->ptr = pa_xmalloc(m->size); 14353a5a1b3Sopenharmony_ci#endif 14453a5a1b3Sopenharmony_ci 14553a5a1b3Sopenharmony_ci return 0; 14653a5a1b3Sopenharmony_ci} 14753a5a1b3Sopenharmony_ci 14853a5a1b3Sopenharmony_cistatic int sharedmem_create(pa_shm *m, pa_mem_type_t type, size_t size, mode_t mode) { 14953a5a1b3Sopenharmony_ci#if defined(HAVE_SHM_OPEN) || defined(HAVE_MEMFD) 15053a5a1b3Sopenharmony_ci char fn[32]; 15153a5a1b3Sopenharmony_ci int fd = -1; 15253a5a1b3Sopenharmony_ci struct shm_marker *marker; 15353a5a1b3Sopenharmony_ci bool do_unlink = false; 15453a5a1b3Sopenharmony_ci 15553a5a1b3Sopenharmony_ci /* Each time we create a new SHM area, let's first drop all stale 15653a5a1b3Sopenharmony_ci * ones */ 15753a5a1b3Sopenharmony_ci pa_shm_cleanup(); 15853a5a1b3Sopenharmony_ci 15953a5a1b3Sopenharmony_ci pa_random(&m->id, sizeof(m->id)); 16053a5a1b3Sopenharmony_ci 16153a5a1b3Sopenharmony_ci switch (type) { 16253a5a1b3Sopenharmony_ci#ifdef HAVE_SHM_OPEN 16353a5a1b3Sopenharmony_ci case PA_MEM_TYPE_SHARED_POSIX: 16453a5a1b3Sopenharmony_ci segment_name(fn, sizeof(fn), m->id); 16553a5a1b3Sopenharmony_ci fd = shm_open(fn, O_RDWR|O_CREAT|O_EXCL, mode); 16653a5a1b3Sopenharmony_ci do_unlink = true; 16753a5a1b3Sopenharmony_ci break; 16853a5a1b3Sopenharmony_ci#endif 16953a5a1b3Sopenharmony_ci#ifdef HAVE_MEMFD 17053a5a1b3Sopenharmony_ci case PA_MEM_TYPE_SHARED_MEMFD: 17153a5a1b3Sopenharmony_ci fd = memfd_create("pulseaudio", MFD_ALLOW_SEALING); 17253a5a1b3Sopenharmony_ci break; 17353a5a1b3Sopenharmony_ci#endif 17453a5a1b3Sopenharmony_ci default: 17553a5a1b3Sopenharmony_ci goto fail; 17653a5a1b3Sopenharmony_ci } 17753a5a1b3Sopenharmony_ci 17853a5a1b3Sopenharmony_ci if (fd < 0) { 17953a5a1b3Sopenharmony_ci pa_log_error("%s open() failed: %s", pa_mem_type_to_string(type), pa_cstrerror(errno)); 18053a5a1b3Sopenharmony_ci goto fail; 18153a5a1b3Sopenharmony_ci } 18253a5a1b3Sopenharmony_ci 18353a5a1b3Sopenharmony_ci m->type = type; 18453a5a1b3Sopenharmony_ci m->size = size + shm_marker_size(type); 18553a5a1b3Sopenharmony_ci m->do_unlink = do_unlink; 18653a5a1b3Sopenharmony_ci 18753a5a1b3Sopenharmony_ci if (ftruncate(fd, (off_t) m->size) < 0) { 18853a5a1b3Sopenharmony_ci pa_log_error("ftruncate() failed: %s", pa_cstrerror(errno)); 18953a5a1b3Sopenharmony_ci goto fail; 19053a5a1b3Sopenharmony_ci } 19153a5a1b3Sopenharmony_ci 19253a5a1b3Sopenharmony_ci#ifndef MAP_NORESERVE 19353a5a1b3Sopenharmony_ci#define MAP_NORESERVE 0 19453a5a1b3Sopenharmony_ci#endif 19553a5a1b3Sopenharmony_ci 19653a5a1b3Sopenharmony_ci if ((m->ptr = mmap(NULL, PA_PAGE_ALIGN(m->size), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_NORESERVE, fd, (off_t) 0)) == MAP_FAILED) { 19753a5a1b3Sopenharmony_ci pa_log_error("mmap() failed: %s", pa_cstrerror(errno)); 19853a5a1b3Sopenharmony_ci goto fail; 19953a5a1b3Sopenharmony_ci } 20053a5a1b3Sopenharmony_ci 20153a5a1b3Sopenharmony_ci if (type == PA_MEM_TYPE_SHARED_POSIX) { 20253a5a1b3Sopenharmony_ci /* We store our PID at the end of the shm block, so that we 20353a5a1b3Sopenharmony_ci * can check for dead shm segments later */ 20453a5a1b3Sopenharmony_ci marker = (struct shm_marker*) ((uint8_t*) m->ptr + m->size - shm_marker_size(type)); 20553a5a1b3Sopenharmony_ci pa_atomic_store(&marker->pid, (int) getpid()); 20653a5a1b3Sopenharmony_ci pa_atomic_store(&marker->marker, SHM_MARKER); 20753a5a1b3Sopenharmony_ci } 20853a5a1b3Sopenharmony_ci 20953a5a1b3Sopenharmony_ci /* For memfds, we keep the fd open until we pass it 21053a5a1b3Sopenharmony_ci * to the other PA endpoint over unix domain socket. */ 21153a5a1b3Sopenharmony_ci if (type != PA_MEM_TYPE_SHARED_MEMFD) { 21253a5a1b3Sopenharmony_ci pa_assert_se(pa_close(fd) == 0); 21353a5a1b3Sopenharmony_ci m->fd = -1; 21453a5a1b3Sopenharmony_ci } 21553a5a1b3Sopenharmony_ci#ifdef HAVE_MEMFD 21653a5a1b3Sopenharmony_ci else 21753a5a1b3Sopenharmony_ci m->fd = fd; 21853a5a1b3Sopenharmony_ci#endif 21953a5a1b3Sopenharmony_ci 22053a5a1b3Sopenharmony_ci return 0; 22153a5a1b3Sopenharmony_ci 22253a5a1b3Sopenharmony_cifail: 22353a5a1b3Sopenharmony_ci if (fd >= 0) { 22453a5a1b3Sopenharmony_ci#ifdef HAVE_SHM_OPEN 22553a5a1b3Sopenharmony_ci if (type == PA_MEM_TYPE_SHARED_POSIX) 22653a5a1b3Sopenharmony_ci shm_unlink(fn); 22753a5a1b3Sopenharmony_ci#endif 22853a5a1b3Sopenharmony_ci pa_close(fd); 22953a5a1b3Sopenharmony_ci } 23053a5a1b3Sopenharmony_ci#endif /* defined(HAVE_SHM_OPEN) || defined(HAVE_MEMFD) */ 23153a5a1b3Sopenharmony_ci 23253a5a1b3Sopenharmony_ci return -1; 23353a5a1b3Sopenharmony_ci} 23453a5a1b3Sopenharmony_ci 23553a5a1b3Sopenharmony_ciint pa_shm_create_rw(pa_shm *m, pa_mem_type_t type, size_t size, mode_t mode) { 23653a5a1b3Sopenharmony_ci pa_assert(m); 23753a5a1b3Sopenharmony_ci pa_assert(size > 0); 23853a5a1b3Sopenharmony_ci pa_assert(size <= MAX_SHM_SIZE); 23953a5a1b3Sopenharmony_ci pa_assert(!(mode & ~0777)); 24053a5a1b3Sopenharmony_ci pa_assert(mode >= 0600); 24153a5a1b3Sopenharmony_ci 24253a5a1b3Sopenharmony_ci /* Round up to make it page aligned */ 24353a5a1b3Sopenharmony_ci size = PA_PAGE_ALIGN(size); 24453a5a1b3Sopenharmony_ci 24553a5a1b3Sopenharmony_ci if (type == PA_MEM_TYPE_PRIVATE) 24653a5a1b3Sopenharmony_ci return privatemem_create(m, size); 24753a5a1b3Sopenharmony_ci 24853a5a1b3Sopenharmony_ci return sharedmem_create(m, type, size, mode); 24953a5a1b3Sopenharmony_ci} 25053a5a1b3Sopenharmony_ci 25153a5a1b3Sopenharmony_cistatic void privatemem_free(pa_shm *m) { 25253a5a1b3Sopenharmony_ci pa_assert(m); 25353a5a1b3Sopenharmony_ci pa_assert(m->ptr); 25453a5a1b3Sopenharmony_ci pa_assert(m->size > 0); 25553a5a1b3Sopenharmony_ci 25653a5a1b3Sopenharmony_ci#ifdef MAP_ANONYMOUS 25753a5a1b3Sopenharmony_ci if (munmap(m->ptr, m->size) < 0) 25853a5a1b3Sopenharmony_ci pa_log_error("munmap() failed: %s", pa_cstrerror(errno)); 25953a5a1b3Sopenharmony_ci#elif defined(HAVE_POSIX_MEMALIGN) 26053a5a1b3Sopenharmony_ci free(m->ptr); 26153a5a1b3Sopenharmony_ci#else 26253a5a1b3Sopenharmony_ci pa_xfree(m->ptr); 26353a5a1b3Sopenharmony_ci#endif 26453a5a1b3Sopenharmony_ci} 26553a5a1b3Sopenharmony_ci 26653a5a1b3Sopenharmony_civoid pa_shm_free(pa_shm *m) { 26753a5a1b3Sopenharmony_ci pa_assert(m); 26853a5a1b3Sopenharmony_ci pa_assert(m->ptr); 26953a5a1b3Sopenharmony_ci pa_assert(m->size > 0); 27053a5a1b3Sopenharmony_ci 27153a5a1b3Sopenharmony_ci#ifdef MAP_FAILED 27253a5a1b3Sopenharmony_ci pa_assert(m->ptr != MAP_FAILED); 27353a5a1b3Sopenharmony_ci#endif 27453a5a1b3Sopenharmony_ci 27553a5a1b3Sopenharmony_ci if (m->type == PA_MEM_TYPE_PRIVATE) { 27653a5a1b3Sopenharmony_ci privatemem_free(m); 27753a5a1b3Sopenharmony_ci goto finish; 27853a5a1b3Sopenharmony_ci } 27953a5a1b3Sopenharmony_ci 28053a5a1b3Sopenharmony_ci pa_log_info("mem type: %d", m->type); 28153a5a1b3Sopenharmony_ci 28253a5a1b3Sopenharmony_ci#if defined(HAVE_SHM_OPEN) || defined(HAVE_MEMFD) 28353a5a1b3Sopenharmony_ci if (munmap(m->ptr, PA_PAGE_ALIGN(m->size)) < 0) 28453a5a1b3Sopenharmony_ci pa_log_error("munmap() failed: %s", pa_cstrerror(errno)); 28553a5a1b3Sopenharmony_ci 28653a5a1b3Sopenharmony_ci#ifdef HAVE_SHM_OPEN 28753a5a1b3Sopenharmony_ci if (m->type == PA_MEM_TYPE_SHARED_POSIX && m->do_unlink) { 28853a5a1b3Sopenharmony_ci char fn[32]; 28953a5a1b3Sopenharmony_ci 29053a5a1b3Sopenharmony_ci segment_name(fn, sizeof(fn), m->id); 29153a5a1b3Sopenharmony_ci if (shm_unlink(fn) < 0) 29253a5a1b3Sopenharmony_ci pa_log_error(" shm_unlink(%s) failed: %s", fn, pa_cstrerror(errno)); 29353a5a1b3Sopenharmony_ci } 29453a5a1b3Sopenharmony_ci#endif 29553a5a1b3Sopenharmony_ci#ifdef HAVE_MEMFD 29653a5a1b3Sopenharmony_ci if (m->type == PA_MEM_TYPE_SHARED_MEMFD && m->fd != -1) 29753a5a1b3Sopenharmony_ci pa_assert_se(pa_close(m->fd) == 0); 29853a5a1b3Sopenharmony_ci#endif 29953a5a1b3Sopenharmony_ci 30053a5a1b3Sopenharmony_ci#else 30153a5a1b3Sopenharmony_ci /* We shouldn't be here without shm or memfd support */ 30253a5a1b3Sopenharmony_ci pa_log_error("remove pa_assert_not_reached call"); 30353a5a1b3Sopenharmony_ci#endif 30453a5a1b3Sopenharmony_ci 30553a5a1b3Sopenharmony_cifinish: 30653a5a1b3Sopenharmony_ci pa_zero(*m); 30753a5a1b3Sopenharmony_ci} 30853a5a1b3Sopenharmony_ci 30953a5a1b3Sopenharmony_civoid pa_shm_punch(pa_shm *m, size_t offset, size_t size) { 31053a5a1b3Sopenharmony_ci void *ptr; 31153a5a1b3Sopenharmony_ci size_t o; 31253a5a1b3Sopenharmony_ci const size_t page_size = pa_page_size(); 31353a5a1b3Sopenharmony_ci 31453a5a1b3Sopenharmony_ci pa_assert(m); 31553a5a1b3Sopenharmony_ci pa_assert(m->ptr); 31653a5a1b3Sopenharmony_ci pa_assert(m->size > 0); 31753a5a1b3Sopenharmony_ci pa_assert(offset+size <= m->size); 31853a5a1b3Sopenharmony_ci 31953a5a1b3Sopenharmony_ci#ifdef MAP_FAILED 32053a5a1b3Sopenharmony_ci pa_assert(m->ptr != MAP_FAILED); 32153a5a1b3Sopenharmony_ci#endif 32253a5a1b3Sopenharmony_ci 32353a5a1b3Sopenharmony_ci /* You're welcome to implement this as NOOP on systems that don't 32453a5a1b3Sopenharmony_ci * support it */ 32553a5a1b3Sopenharmony_ci 32653a5a1b3Sopenharmony_ci /* Align the pointer up to multiples of the page size */ 32753a5a1b3Sopenharmony_ci ptr = (uint8_t*) m->ptr + offset; 32853a5a1b3Sopenharmony_ci o = (size_t) ((uint8_t*) ptr - (uint8_t*) PA_PAGE_ALIGN_PTR(ptr)); 32953a5a1b3Sopenharmony_ci 33053a5a1b3Sopenharmony_ci if (o > 0) { 33153a5a1b3Sopenharmony_ci size_t delta = page_size - o; 33253a5a1b3Sopenharmony_ci ptr = (uint8_t*) ptr + delta; 33353a5a1b3Sopenharmony_ci size -= delta; 33453a5a1b3Sopenharmony_ci } 33553a5a1b3Sopenharmony_ci 33653a5a1b3Sopenharmony_ci /* Align the size down to multiples of page size */ 33753a5a1b3Sopenharmony_ci size = (size / page_size) * page_size; 33853a5a1b3Sopenharmony_ci 33953a5a1b3Sopenharmony_ci#ifdef MADV_REMOVE 34053a5a1b3Sopenharmony_ci if (madvise(ptr, size, MADV_REMOVE) >= 0) 34153a5a1b3Sopenharmony_ci return; 34253a5a1b3Sopenharmony_ci#endif 34353a5a1b3Sopenharmony_ci 34453a5a1b3Sopenharmony_ci#ifdef MADV_FREE 34553a5a1b3Sopenharmony_ci if (madvise(ptr, size, MADV_FREE) >= 0) 34653a5a1b3Sopenharmony_ci return; 34753a5a1b3Sopenharmony_ci#endif 34853a5a1b3Sopenharmony_ci 34953a5a1b3Sopenharmony_ci#ifdef MADV_DONTNEED 35053a5a1b3Sopenharmony_ci madvise(ptr, size, MADV_DONTNEED); 35153a5a1b3Sopenharmony_ci#elif defined(POSIX_MADV_DONTNEED) 35253a5a1b3Sopenharmony_ci posix_madvise(ptr, size, POSIX_MADV_DONTNEED); 35353a5a1b3Sopenharmony_ci#endif 35453a5a1b3Sopenharmony_ci} 35553a5a1b3Sopenharmony_ci 35653a5a1b3Sopenharmony_cistatic int shm_attach(pa_shm *m, pa_mem_type_t type, unsigned id, int memfd_fd, bool writable, bool for_cleanup) { 35753a5a1b3Sopenharmony_ci#if defined(HAVE_SHM_OPEN) || defined(HAVE_MEMFD) 35853a5a1b3Sopenharmony_ci char fn[32]; 35953a5a1b3Sopenharmony_ci int fd = -1; 36053a5a1b3Sopenharmony_ci int prot; 36153a5a1b3Sopenharmony_ci struct stat st; 36253a5a1b3Sopenharmony_ci 36353a5a1b3Sopenharmony_ci pa_assert(m); 36453a5a1b3Sopenharmony_ci 36553a5a1b3Sopenharmony_ci switch (type) { 36653a5a1b3Sopenharmony_ci#ifdef HAVE_SHM_OPEN 36753a5a1b3Sopenharmony_ci case PA_MEM_TYPE_SHARED_POSIX: 36853a5a1b3Sopenharmony_ci pa_assert(memfd_fd == -1); 36953a5a1b3Sopenharmony_ci segment_name(fn, sizeof(fn), id); 37053a5a1b3Sopenharmony_ci if ((fd = shm_open(fn, writable ? O_RDWR : O_RDONLY, 0)) < 0) { 37153a5a1b3Sopenharmony_ci if ((errno != EACCES && errno != ENOENT) || !for_cleanup) 37253a5a1b3Sopenharmony_ci pa_log_error("shm_open() failed: %s", pa_cstrerror(errno)); 37353a5a1b3Sopenharmony_ci goto fail; 37453a5a1b3Sopenharmony_ci } 37553a5a1b3Sopenharmony_ci break; 37653a5a1b3Sopenharmony_ci#endif 37753a5a1b3Sopenharmony_ci#ifdef HAVE_MEMFD 37853a5a1b3Sopenharmony_ci case PA_MEM_TYPE_SHARED_MEMFD: 37953a5a1b3Sopenharmony_ci pa_assert(memfd_fd != -1); 38053a5a1b3Sopenharmony_ci fd = memfd_fd; 38153a5a1b3Sopenharmony_ci break; 38253a5a1b3Sopenharmony_ci#endif 38353a5a1b3Sopenharmony_ci default: 38453a5a1b3Sopenharmony_ci goto fail; 38553a5a1b3Sopenharmony_ci } 38653a5a1b3Sopenharmony_ci 38753a5a1b3Sopenharmony_ci if (fstat(fd, &st) < 0) { 38853a5a1b3Sopenharmony_ci pa_log_error("fstat() failed: %s", pa_cstrerror(errno)); 38953a5a1b3Sopenharmony_ci goto fail; 39053a5a1b3Sopenharmony_ci } 39153a5a1b3Sopenharmony_ci 39253a5a1b3Sopenharmony_ci if (st.st_size <= 0 || 39353a5a1b3Sopenharmony_ci st.st_size > (off_t) MAX_SHM_SIZE + (off_t) shm_marker_size(type) || 39453a5a1b3Sopenharmony_ci PA_ALIGN((size_t) st.st_size) != (size_t) st.st_size) { 39553a5a1b3Sopenharmony_ci pa_log_error("Invalid shared memory segment size"); 39653a5a1b3Sopenharmony_ci goto fail; 39753a5a1b3Sopenharmony_ci } 39853a5a1b3Sopenharmony_ci 39953a5a1b3Sopenharmony_ci prot = writable ? PROT_READ | PROT_WRITE : PROT_READ; 40053a5a1b3Sopenharmony_ci if ((m->ptr = mmap(NULL, PA_PAGE_ALIGN(st.st_size), prot, MAP_SHARED, fd, (off_t) 0)) == MAP_FAILED) { 40153a5a1b3Sopenharmony_ci pa_log_error("mmap() failed: %s", pa_cstrerror(errno)); 40253a5a1b3Sopenharmony_ci goto fail; 40353a5a1b3Sopenharmony_ci } 40453a5a1b3Sopenharmony_ci 40553a5a1b3Sopenharmony_ci /* In case of attaching to memfd areas, _the caller_ maintains 40653a5a1b3Sopenharmony_ci * ownership of the passed fd and has the sole responsibility 40753a5a1b3Sopenharmony_ci * of closing it down.. For other types, we're the code path 40853a5a1b3Sopenharmony_ci * which created the fd in the first place and we're thus the 40953a5a1b3Sopenharmony_ci * ones responsible for closing it down */ 41053a5a1b3Sopenharmony_ci if (type != PA_MEM_TYPE_SHARED_MEMFD) 41153a5a1b3Sopenharmony_ci pa_assert_se(pa_close(fd) == 0); 41253a5a1b3Sopenharmony_ci 41353a5a1b3Sopenharmony_ci pa_log_info("shm_attach set mem type %d", type); 41453a5a1b3Sopenharmony_ci m->type = type; 41553a5a1b3Sopenharmony_ci m->id = id; 41653a5a1b3Sopenharmony_ci m->size = (size_t) st.st_size; 41753a5a1b3Sopenharmony_ci m->do_unlink = false; 41853a5a1b3Sopenharmony_ci m->fd = -1; 41953a5a1b3Sopenharmony_ci 42053a5a1b3Sopenharmony_ci return 0; 42153a5a1b3Sopenharmony_ci 42253a5a1b3Sopenharmony_cifail: 42353a5a1b3Sopenharmony_ci /* In case of memfds, caller maintains fd ownership */ 42453a5a1b3Sopenharmony_ci if (fd >= 0 && type != PA_MEM_TYPE_SHARED_MEMFD) 42553a5a1b3Sopenharmony_ci pa_close(fd); 42653a5a1b3Sopenharmony_ci 42753a5a1b3Sopenharmony_ci#endif /* defined(HAVE_SHM_OPEN) || defined(HAVE_MEMFD) */ 42853a5a1b3Sopenharmony_ci 42953a5a1b3Sopenharmony_ci return -1; 43053a5a1b3Sopenharmony_ci} 43153a5a1b3Sopenharmony_ci 43253a5a1b3Sopenharmony_ci/* Caller owns passed @memfd_fd and must close it down when appropriate. */ 43353a5a1b3Sopenharmony_ciint pa_shm_attach(pa_shm *m, pa_mem_type_t type, unsigned id, int memfd_fd, bool writable) { 43453a5a1b3Sopenharmony_ci return shm_attach(m, type, id, memfd_fd, writable, false); 43553a5a1b3Sopenharmony_ci} 43653a5a1b3Sopenharmony_ci 43753a5a1b3Sopenharmony_ciint pa_shm_cleanup(void) { 43853a5a1b3Sopenharmony_ci pa_log_info("start pa_shm_cleanup"); 43953a5a1b3Sopenharmony_ci#ifdef HAVE_SHM_OPEN 44053a5a1b3Sopenharmony_ci#ifdef SHM_PATH 44153a5a1b3Sopenharmony_ci DIR *d; 44253a5a1b3Sopenharmony_ci struct dirent *de; 44353a5a1b3Sopenharmony_ci 44453a5a1b3Sopenharmony_ci if (!(d = opendir(SHM_PATH))) { 44553a5a1b3Sopenharmony_ci pa_log_warn("Failed to read "SHM_PATH": %s", pa_cstrerror(errno)); 44653a5a1b3Sopenharmony_ci return -1; 44753a5a1b3Sopenharmony_ci } 44853a5a1b3Sopenharmony_ci 44953a5a1b3Sopenharmony_ci while ((de = readdir(d))) { 45053a5a1b3Sopenharmony_ci pa_shm seg; 45153a5a1b3Sopenharmony_ci unsigned id; 45253a5a1b3Sopenharmony_ci pid_t pid; 45353a5a1b3Sopenharmony_ci char fn[128]; 45453a5a1b3Sopenharmony_ci struct shm_marker *m; 45553a5a1b3Sopenharmony_ci 45653a5a1b3Sopenharmony_ci#if defined(__sun) 45753a5a1b3Sopenharmony_ci if (strncmp(de->d_name, ".SHMDpulse-shm-", SHM_ID_LEN)) 45853a5a1b3Sopenharmony_ci#else 45953a5a1b3Sopenharmony_ci if (strncmp(de->d_name, "pulse-shm-", SHM_ID_LEN)) 46053a5a1b3Sopenharmony_ci#endif 46153a5a1b3Sopenharmony_ci continue; 46253a5a1b3Sopenharmony_ci 46353a5a1b3Sopenharmony_ci if (pa_atou(de->d_name + SHM_ID_LEN, &id) < 0) 46453a5a1b3Sopenharmony_ci continue; 46553a5a1b3Sopenharmony_ci 46653a5a1b3Sopenharmony_ci if (shm_attach(&seg, PA_MEM_TYPE_SHARED_POSIX, id, -1, false, true) < 0) 46753a5a1b3Sopenharmony_ci continue; 46853a5a1b3Sopenharmony_ci 46953a5a1b3Sopenharmony_ci if (seg.size < shm_marker_size(seg.type)) { 47053a5a1b3Sopenharmony_ci pa_shm_free(&seg); 47153a5a1b3Sopenharmony_ci continue; 47253a5a1b3Sopenharmony_ci } 47353a5a1b3Sopenharmony_ci 47453a5a1b3Sopenharmony_ci m = (struct shm_marker*) ((uint8_t*) seg.ptr + seg.size - shm_marker_size(seg.type)); 47553a5a1b3Sopenharmony_ci 47653a5a1b3Sopenharmony_ci if (pa_atomic_load(&m->marker) != SHM_MARKER) { 47753a5a1b3Sopenharmony_ci pa_shm_free(&seg); 47853a5a1b3Sopenharmony_ci continue; 47953a5a1b3Sopenharmony_ci } 48053a5a1b3Sopenharmony_ci 48153a5a1b3Sopenharmony_ci if (!(pid = (pid_t) pa_atomic_load(&m->pid))) { 48253a5a1b3Sopenharmony_ci pa_shm_free(&seg); 48353a5a1b3Sopenharmony_ci continue; 48453a5a1b3Sopenharmony_ci } 48553a5a1b3Sopenharmony_ci 48653a5a1b3Sopenharmony_ci if (kill(pid, 0) == 0 || errno != ESRCH) { 48753a5a1b3Sopenharmony_ci pa_shm_free(&seg); 48853a5a1b3Sopenharmony_ci continue; 48953a5a1b3Sopenharmony_ci } 49053a5a1b3Sopenharmony_ci 49153a5a1b3Sopenharmony_ci pa_shm_free(&seg); 49253a5a1b3Sopenharmony_ci 49353a5a1b3Sopenharmony_ci /* Ok, the owner of this shms segment is dead, so, let's remove the segment */ 49453a5a1b3Sopenharmony_ci segment_name(fn, sizeof(fn), id); 49553a5a1b3Sopenharmony_ci 49653a5a1b3Sopenharmony_ci if (shm_unlink(fn) < 0 && errno != EACCES && errno != ENOENT) 49753a5a1b3Sopenharmony_ci pa_log_warn("Failed to remove SHM segment %s: %s", fn, pa_cstrerror(errno)); 49853a5a1b3Sopenharmony_ci } 49953a5a1b3Sopenharmony_ci 50053a5a1b3Sopenharmony_ci closedir(d); 50153a5a1b3Sopenharmony_ci#endif /* SHM_PATH */ 50253a5a1b3Sopenharmony_ci#endif /* HAVE_SHM_OPEN */ 50353a5a1b3Sopenharmony_ci 50453a5a1b3Sopenharmony_ci return 0; 50553a5a1b3Sopenharmony_ci} 506