remap: fix counters for mmx and sse remap
[pulseaudio-mirror.git] / src / pulsecore / shm.c
blobfbf777a4687e87d7f2dbd1847bee7b3942b4c048
1 /***
2 This file is part of PulseAudio.
4 Copyright 2006 Lennart Poettering
5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as
9 published by the Free Software Foundation; either version 2.1 of the
10 License, or (at your option) any later version.
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public
18 License along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 USA.
21 ***/
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <fcntl.h>
30 #include <stdio.h>
31 #include <errno.h>
32 #include <string.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 #include <dirent.h>
36 #include <signal.h>
38 #ifdef HAVE_SYS_MMAN_H
39 #include <sys/mman.h>
40 #endif
42 /* This is deprecated on glibc but is still used by FreeBSD */
43 #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
44 # define MAP_ANONYMOUS MAP_ANON
45 #endif
47 #include <pulse/xmalloc.h>
48 #include <pulse/gccmacro.h>
50 #include <pulsecore/core-error.h>
51 #include <pulsecore/log.h>
52 #include <pulsecore/random.h>
53 #include <pulsecore/core-util.h>
54 #include <pulsecore/macro.h>
55 #include <pulsecore/atomic.h>
57 #include "shm.h"
59 #if defined(__linux__) && !defined(MADV_REMOVE)
60 #define MADV_REMOVE 9
61 #endif
63 /* 1 GiB at max */
64 #define MAX_SHM_SIZE (PA_ALIGN(1024*1024*1024))
66 #ifdef __linux__
67 /* On Linux we know that the shared memory blocks are files in
68 * /dev/shm. We can use that information to list all blocks and
69 * cleanup unused ones */
70 #define SHM_PATH "/dev/shm/"
71 #else
72 #undef SHM_PATH
73 #endif
75 #define SHM_MARKER ((int) 0xbeefcafe)
77 /* We now put this SHM marker at the end of each segment. It's
78 * optional, to not require a reboot when upgrading, though. Note that
79 * on multiarch systems 32bit and 64bit processes might access this
80 * region simultaneously. The header fields need to be independant
81 * from the process' word with */
82 struct shm_marker {
83 pa_atomic_t marker; /* 0xbeefcafe */
84 pa_atomic_t pid;
85 uint64_t _reserved1;
86 uint64_t _reserved2;
87 uint64_t _reserved3;
88 uint64_t _reserved4;
89 } PA_GCC_PACKED;
91 #define SHM_MARKER_SIZE PA_ALIGN(sizeof(struct shm_marker))
93 static char *segment_name(char *fn, size_t l, unsigned id) {
94 pa_snprintf(fn, l, "/pulse-shm-%u", id);
95 return fn;
98 int pa_shm_create_rw(pa_shm *m, size_t size, pa_bool_t shared, mode_t mode) {
99 char fn[32];
100 int fd = -1;
102 pa_assert(m);
103 pa_assert(size > 0);
104 pa_assert(size <= MAX_SHM_SIZE);
105 pa_assert(mode >= 0600);
107 /* Each time we create a new SHM area, let's first drop all stale
108 * ones */
109 pa_shm_cleanup();
111 /* Round up to make it page aligned */
112 size = PA_PAGE_ALIGN(size);
114 if (!shared) {
115 m->id = 0;
116 m->size = size;
118 #ifdef MAP_ANONYMOUS
119 if ((m->ptr = mmap(NULL, m->size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, (off_t) 0)) == MAP_FAILED) {
120 pa_log("mmap() failed: %s", pa_cstrerror(errno));
121 goto fail;
123 #elif defined(HAVE_POSIX_MEMALIGN)
125 int r;
127 if ((r = posix_memalign(&m->ptr, PA_PAGE_SIZE, size)) < 0) {
128 pa_log("posix_memalign() failed: %s", pa_cstrerror(r));
129 goto fail;
132 #else
133 m->ptr = pa_xmalloc(m->size);
134 #endif
136 m->do_unlink = FALSE;
138 } else {
139 #ifdef HAVE_SHM_OPEN
140 struct shm_marker *marker;
142 pa_random(&m->id, sizeof(m->id));
143 segment_name(fn, sizeof(fn), m->id);
145 if ((fd = shm_open(fn, O_RDWR|O_CREAT|O_EXCL, mode & 0444)) < 0) {
146 pa_log("shm_open() failed: %s", pa_cstrerror(errno));
147 goto fail;
150 m->size = size + SHM_MARKER_SIZE;
152 if (ftruncate(fd, (off_t) m->size) < 0) {
153 pa_log("ftruncate() failed: %s", pa_cstrerror(errno));
154 goto fail;
157 if ((m->ptr = mmap(NULL, PA_PAGE_ALIGN(m->size), PROT_READ|PROT_WRITE, MAP_SHARED, fd, (off_t) 0)) == MAP_FAILED) {
158 pa_log("mmap() failed: %s", pa_cstrerror(errno));
159 goto fail;
162 /* We store our PID at the end of the shm block, so that we
163 * can check for dead shm segments later */
164 marker = (struct shm_marker*) ((uint8_t*) m->ptr + m->size - SHM_MARKER_SIZE);
165 pa_atomic_store(&marker->pid, (int) getpid());
166 pa_atomic_store(&marker->marker, SHM_MARKER);
168 pa_assert_se(pa_close(fd) == 0);
169 m->do_unlink = TRUE;
170 #else
171 return -1;
172 #endif
175 m->shared = shared;
177 return 0;
179 fail:
181 #ifdef HAVE_SHM_OPEN
182 if (fd >= 0) {
183 shm_unlink(fn);
184 pa_close(fd);
186 #endif
188 return -1;
191 void pa_shm_free(pa_shm *m) {
192 pa_assert(m);
193 pa_assert(m->ptr);
194 pa_assert(m->size > 0);
196 #ifdef MAP_FAILED
197 pa_assert(m->ptr != MAP_FAILED);
198 #endif
200 if (!m->shared) {
201 #ifdef MAP_ANONYMOUS
202 if (munmap(m->ptr, m->size) < 0)
203 pa_log("munmap() failed: %s", pa_cstrerror(errno));
204 #elif defined(HAVE_POSIX_MEMALIGN)
205 free(m->ptr);
206 #else
207 pa_xfree(m->ptr);
208 #endif
209 } else {
210 #ifdef HAVE_SHM_OPEN
211 if (munmap(m->ptr, PA_PAGE_ALIGN(m->size)) < 0)
212 pa_log("munmap() failed: %s", pa_cstrerror(errno));
214 if (m->do_unlink) {
215 char fn[32];
217 segment_name(fn, sizeof(fn), m->id);
219 if (shm_unlink(fn) < 0)
220 pa_log(" shm_unlink(%s) failed: %s", fn, pa_cstrerror(errno));
222 #else
223 /* We shouldn't be here without shm support */
224 pa_assert_not_reached();
225 #endif
228 pa_zero(*m);
231 void pa_shm_punch(pa_shm *m, size_t offset, size_t size) {
232 void *ptr;
233 size_t o;
235 pa_assert(m);
236 pa_assert(m->ptr);
237 pa_assert(m->size > 0);
238 pa_assert(offset+size <= m->size);
240 #ifdef MAP_FAILED
241 pa_assert(m->ptr != MAP_FAILED);
242 #endif
244 /* You're welcome to implement this as NOOP on systems that don't
245 * support it */
247 /* Align the pointer up to multiples of the page size */
248 ptr = (uint8_t*) m->ptr + offset;
249 o = (size_t) ((uint8_t*) ptr - (uint8_t*) PA_PAGE_ALIGN_PTR(ptr));
251 if (o > 0) {
252 size_t delta = PA_PAGE_SIZE - o;
253 ptr = (uint8_t*) ptr + delta;
254 size -= delta;
257 /* Align the size down to multiples of page size */
258 size = (size / PA_PAGE_SIZE) * PA_PAGE_SIZE;
260 #ifdef MADV_REMOVE
261 if (madvise(ptr, size, MADV_REMOVE) >= 0)
262 return;
263 #endif
265 #ifdef MADV_FREE
266 if (madvise(ptr, size, MADV_FREE) >= 0)
267 return;
268 #endif
270 #ifdef MADV_DONTNEED
271 madvise(ptr, size, MADV_DONTNEED);
272 #elif defined(POSIX_MADV_DONTNEED)
273 posix_madvise(ptr, size, POSIX_MADV_DONTNEED);
274 #endif
277 #ifdef HAVE_SHM_OPEN
279 int pa_shm_attach_ro(pa_shm *m, unsigned id) {
280 char fn[32];
281 int fd = -1;
282 struct stat st;
284 pa_assert(m);
286 segment_name(fn, sizeof(fn), m->id = id);
288 if ((fd = shm_open(fn, O_RDONLY, 0)) < 0) {
289 if (errno != EACCES)
290 pa_log("shm_open() failed: %s", pa_cstrerror(errno));
291 goto fail;
294 if (fstat(fd, &st) < 0) {
295 pa_log("fstat() failed: %s", pa_cstrerror(errno));
296 goto fail;
299 if (st.st_size <= 0 ||
300 st.st_size > (off_t) (MAX_SHM_SIZE+SHM_MARKER_SIZE) ||
301 PA_ALIGN((size_t) st.st_size) != (size_t) st.st_size) {
302 pa_log("Invalid shared memory segment size");
303 goto fail;
306 m->size = (size_t) st.st_size;
308 if ((m->ptr = mmap(NULL, PA_PAGE_ALIGN(m->size), PROT_READ, MAP_SHARED, fd, (off_t) 0)) == MAP_FAILED) {
309 pa_log("mmap() failed: %s", pa_cstrerror(errno));
310 goto fail;
313 m->do_unlink = FALSE;
314 m->shared = TRUE;
316 pa_assert_se(pa_close(fd) == 0);
318 return 0;
320 fail:
321 if (fd >= 0)
322 pa_close(fd);
324 return -1;
327 #else /* HAVE_SHM_OPEN */
329 int pa_shm_attach_ro(pa_shm *m, unsigned id) {
330 return -1;
333 #endif /* HAVE_SHM_OPEN */
335 int pa_shm_cleanup(void) {
337 #ifdef HAVE_SHM_OPEN
338 #ifdef SHM_PATH
339 DIR *d;
340 struct dirent *de;
342 if (!(d = opendir(SHM_PATH))) {
343 pa_log_warn("Failed to read "SHM_PATH": %s", pa_cstrerror(errno));
344 return -1;
347 while ((de = readdir(d))) {
348 pa_shm seg;
349 unsigned id;
350 pid_t pid;
351 char fn[128];
352 struct shm_marker *m;
354 if (strncmp(de->d_name, "pulse-shm-", 10))
355 continue;
357 if (pa_atou(de->d_name + 10, &id) < 0)
358 continue;
360 if (pa_shm_attach_ro(&seg, id) < 0)
361 continue;
363 if (seg.size < SHM_MARKER_SIZE) {
364 pa_shm_free(&seg);
365 continue;
368 m = (struct shm_marker*) ((uint8_t*) seg.ptr + seg.size - SHM_MARKER_SIZE);
370 if (pa_atomic_load(&m->marker) != SHM_MARKER) {
371 pa_shm_free(&seg);
372 continue;
375 if (!(pid = (pid_t) pa_atomic_load(&m->pid))) {
376 pa_shm_free(&seg);
377 continue;
380 if (kill(pid, 0) == 0 || errno != ESRCH) {
381 pa_shm_free(&seg);
382 continue;
385 pa_shm_free(&seg);
387 /* Ok, the owner of this shms segment is dead, so, let's remove the segment */
388 segment_name(fn, sizeof(fn), id);
390 if (shm_unlink(fn) < 0 && errno != EACCES && errno != ENOENT)
391 pa_log_warn("Failed to remove SHM segment %s: %s\n", fn, pa_cstrerror(errno));
394 closedir(d);
395 #endif /* SHM_PATH */
396 #endif /* HAVE_SHM_OPEN */
398 return 0;