4 This file is part of PulseAudio.
6 Copyright 2006 Lennart Poettering
7 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
9 PulseAudio is free software; you can redistribute it and/or modify
10 it under the terms of the GNU Lesser General Public License as
11 published by the Free Software Foundation; either version 2.1 of the
12 License, or (at your option) any later version.
14 PulseAudio is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
19 You should have received a copy of the GNU Lesser General Public
20 License along with PulseAudio; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
36 #include <sys/types.h>
40 #ifdef HAVE_SYS_MMAN_H
44 #include <pulse/xmalloc.h>
45 #include <pulse/gccmacro.h>
47 #include <pulsecore/core-error.h>
48 #include <pulsecore/log.h>
49 #include <pulsecore/random.h>
50 #include <pulsecore/core-util.h>
51 #include <pulsecore/macro.h>
52 #include <pulsecore/atomic.h>
56 #if defined(__linux__) && !defined(MADV_REMOVE)
60 #define MAX_SHM_SIZE (PA_ALIGN(1024*1024*64))
63 /* On Linux we know that the shared memory blocks are files in
64 * /dev/shm. We can use that information to list all blocks and
65 * cleanup unused ones */
66 #define SHM_PATH "/dev/shm/"
71 #define SHM_MARKER ((int) 0xbeefcafe)
73 /* We now put this SHM marker at the end of each segment. It's
74 * optional, to not require a reboot when upgrading, though */
75 struct shm_marker PA_GCC_PACKED {
76 pa_atomic_t marker; /* 0xbeefcafe */
78 uint64_t *_reserverd1;
79 uint64_t *_reserverd2;
80 uint64_t *_reserverd3;
81 uint64_t *_reserverd4;
84 static char *segment_name(char *fn, size_t l, unsigned id) {
85 pa_snprintf(fn, l, "/pulse-shm-%u", id);
89 int pa_shm_create_rw(pa_shm *m, size_t size, pa_bool_t shared, mode_t mode) {
95 pa_assert(size <= MAX_SHM_SIZE);
96 pa_assert(mode >= 0600);
98 /* Each time we create a new SHM area, let's first drop all stale
102 /* Round up to make it aligned */
103 size = PA_ALIGN(size);
110 if ((m->ptr = mmap(NULL, m->size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0)) == MAP_FAILED) {
111 pa_log("mmap() failed: %s", pa_cstrerror(errno));
114 #elif defined(HAVE_POSIX_MEMALIGN)
118 if ((r = posix_memalign(&m->ptr, PA_PAGE_SIZE, size)) < 0) {
119 pa_log("posix_memalign() failed: %s", pa_cstrerror(r));
124 m->ptr = pa_xmalloc(m->size);
127 m->do_unlink = FALSE;
131 struct shm_marker *marker;
133 pa_random(&m->id, sizeof(m->id));
134 segment_name(fn, sizeof(fn), m->id);
136 if ((fd = shm_open(fn, O_RDWR|O_CREAT|O_EXCL, mode & 0444)) < 0) {
137 pa_log("shm_open() failed: %s", pa_cstrerror(errno));
141 m->size = size + PA_ALIGN(sizeof(struct shm_marker));
143 if (ftruncate(fd, m->size) < 0) {
144 pa_log("ftruncate() failed: %s", pa_cstrerror(errno));
148 if ((m->ptr = mmap(NULL, m->size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
149 pa_log("mmap() failed: %s", pa_cstrerror(errno));
153 /* We store our PID at the end of the shm block, so that we
154 * can check for dead shm segments later */
155 marker = (struct shm_marker*) ((uint8_t*) m->ptr + m->size - PA_ALIGN(sizeof(struct shm_marker)));
156 pa_atomic_store(&marker->pid, (int) getpid());
157 pa_atomic_store(&marker->marker, SHM_MARKER);
159 pa_assert_se(close(fd) == 0);
182 void pa_shm_free(pa_shm *m) {
185 pa_assert(m->size > 0);
188 pa_assert(m->ptr != MAP_FAILED);
193 if (munmap(m->ptr, m->size) < 0)
194 pa_log("munmap() failed: %s", pa_cstrerror(errno));
195 #elif defined(HAVE_POSIX_MEMALIGN)
202 if (munmap(m->ptr, m->size) < 0)
203 pa_log("munmap() failed: %s", pa_cstrerror(errno));
208 segment_name(fn, sizeof(fn), m->id);
210 if (shm_unlink(fn) < 0)
211 pa_log(" shm_unlink(%s) failed: %s", fn, pa_cstrerror(errno));
214 /* We shouldn't be here without shm support */
215 pa_assert_not_reached();
219 memset(m, 0, sizeof(*m));
222 void pa_shm_punch(pa_shm *m, size_t offset, size_t size) {
228 pa_assert(m->size > 0);
229 pa_assert(offset+size <= m->size);
232 pa_assert(m->ptr != MAP_FAILED);
235 /* You're welcome to implement this as NOOP on systems that don't
238 /* Align this to multiples of the page size */
239 ptr = (uint8_t*) m->ptr + offset;
240 o = (uint8_t*) ptr - (uint8_t*) PA_PAGE_ALIGN_PTR(ptr);
244 ptr = (uint8_t*) ptr + (ps - o);
249 if (madvise(ptr, size, MADV_REMOVE) >= 0)
254 if (madvise(ptr, size, MADV_FREE) >= 0)
259 pa_assert_se(madvise(ptr, size, MADV_DONTNEED) == 0);
260 #elif defined(POSIX_MADV_DONTNEED)
261 pa_assert_se(posix_madvise(ptr, size, POSIX_MADV_DONTNEED) == 0);
267 int pa_shm_attach_ro(pa_shm *m, unsigned id) {
274 segment_name(fn, sizeof(fn), m->id = id);
276 if ((fd = shm_open(fn, O_RDONLY, 0)) < 0) {
278 pa_log("shm_open() failed: %s", pa_cstrerror(errno));
282 if (fstat(fd, &st) < 0) {
283 pa_log("fstat() failed: %s", pa_cstrerror(errno));
287 if (st.st_size <= 0 ||
288 st.st_size > (off_t) (MAX_SHM_SIZE+PA_ALIGN(sizeof(struct shm_marker))) ||
289 PA_ALIGN((size_t) st.st_size) != (size_t) st.st_size) {
290 pa_log("Invalid shared memory segment size");
294 m->size = st.st_size;
296 if ((m->ptr = mmap(NULL, m->size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
297 pa_log("mmap() failed: %s", pa_cstrerror(errno));
304 pa_assert_se(pa_close(fd) == 0);
315 #else /* HAVE_SHM_OPEN */
317 int pa_shm_attach_ro(pa_shm *m, unsigned id) {
321 #endif /* HAVE_SHM_OPEN */
323 int pa_shm_cleanup(void) {
330 if (!(d = opendir(SHM_PATH))) {
331 pa_log_warn("Failed to read "SHM_PATH": %s", pa_cstrerror(errno));
335 while ((de = readdir(d))) {
340 struct shm_marker *m;
342 if (strncmp(de->d_name, "pulse-shm-", 10))
345 if (pa_atou(de->d_name + 10, &id) < 0)
348 if (pa_shm_attach_ro(&seg, id) < 0)
351 if (seg.size < PA_ALIGN(sizeof(struct shm_marker))) {
356 m = (struct shm_marker*) ((uint8_t*) seg.ptr + seg.size - PA_ALIGN(sizeof(struct shm_marker)));
358 if (pa_atomic_load(&m->marker) != SHM_MARKER) {
363 if (!(pid = (pid_t) pa_atomic_load(&m->pid))) {
368 if (kill(pid, 0) == 0 || errno != ESRCH) {
375 /* Ok, the owner of this shms segment is dead, so, let's remove the segment */
376 segment_name(fn, sizeof(fn), id);
378 if (shm_unlink(fn) < 0 && errno != EACCES && errno != ENOENT)
379 pa_log_warn("Failed to remove SHM segment %s: %s\n", fn, pa_cstrerror(errno));
383 #endif /* SHM_PATH */
384 #endif /* HAVE_SHM_OPEN */