]> code.delx.au - pulseaudio/blob - src/pulsecore/shm.c
merge glitch-free branch back into trunk
[pulseaudio] / src / pulsecore / shm.c
1 /* $Id$ */
2
3 /***
4 This file is part of PulseAudio.
5
6 Copyright 2006 Lennart Poettering
7 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
8
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.
13
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.
18
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
22 USA.
23 ***/
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <fcntl.h>
32 #include <stdio.h>
33 #include <errno.h>
34 #include <string.h>
35 #include <sys/stat.h>
36 #include <sys/types.h>
37 #include <dirent.h>
38 #include <signal.h>
39
40 #ifdef HAVE_SYS_MMAN_H
41 #include <sys/mman.h>
42 #endif
43
44 #include <pulse/xmalloc.h>
45 #include <pulse/gccmacro.h>
46
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>
53
54 #include "shm.h"
55
56 #if defined(__linux__) && !defined(MADV_REMOVE)
57 #define MADV_REMOVE 9
58 #endif
59
60 #define MAX_SHM_SIZE (PA_ALIGN(1024*1024*64))
61
62 #ifdef __linux__
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/"
67 #else
68 #undef SHM_PATH
69 #endif
70
71 #define SHM_MARKER ((int) 0xbeefcafe)
72
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 */
77 pa_atomic_t pid;
78 uint64_t *_reserverd1;
79 uint64_t *_reserverd2;
80 uint64_t *_reserverd3;
81 uint64_t *_reserverd4;
82 };
83
84 static char *segment_name(char *fn, size_t l, unsigned id) {
85 pa_snprintf(fn, l, "/pulse-shm-%u", id);
86 return fn;
87 }
88
89 int pa_shm_create_rw(pa_shm *m, size_t size, pa_bool_t shared, mode_t mode) {
90 char fn[32];
91 int fd = -1;
92
93 pa_assert(m);
94 pa_assert(size > 0);
95 pa_assert(size <= MAX_SHM_SIZE);
96 pa_assert(mode >= 0600);
97
98 /* Each time we create a new SHM area, let's first drop all stale
99 * ones */
100 pa_shm_cleanup();
101
102 /* Round up to make it aligned */
103 size = PA_ALIGN(size);
104
105 if (!shared) {
106 m->id = 0;
107 m->size = size;
108
109 #ifdef MAP_ANONYMOUS
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));
112 goto fail;
113 }
114 #elif defined(HAVE_POSIX_MEMALIGN)
115 {
116 int r;
117
118 if ((r = posix_memalign(&m->ptr, PA_PAGE_SIZE, size)) < 0) {
119 pa_log("posix_memalign() failed: %s", pa_cstrerror(r));
120 goto fail;
121 }
122 }
123 #else
124 m->ptr = pa_xmalloc(m->size);
125 #endif
126
127 m->do_unlink = FALSE;
128
129 } else {
130 #ifdef HAVE_SHM_OPEN
131 struct shm_marker *marker;
132
133 pa_random(&m->id, sizeof(m->id));
134 segment_name(fn, sizeof(fn), m->id);
135
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));
138 goto fail;
139 }
140
141 m->size = size + PA_ALIGN(sizeof(struct shm_marker));
142
143 if (ftruncate(fd, m->size) < 0) {
144 pa_log("ftruncate() failed: %s", pa_cstrerror(errno));
145 goto fail;
146 }
147
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));
150 goto fail;
151 }
152
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);
158
159 pa_assert_se(close(fd) == 0);
160 m->do_unlink = TRUE;
161 #else
162 return -1;
163 #endif
164 }
165
166 m->shared = shared;
167
168 return 0;
169
170 fail:
171
172 #ifdef HAVE_SHM_OPEN
173 if (fd >= 0) {
174 shm_unlink(fn);
175 pa_close(fd);
176 }
177 #endif
178
179 return -1;
180 }
181
182 void pa_shm_free(pa_shm *m) {
183 pa_assert(m);
184 pa_assert(m->ptr);
185 pa_assert(m->size > 0);
186
187 #ifdef MAP_FAILED
188 pa_assert(m->ptr != MAP_FAILED);
189 #endif
190
191 if (!m->shared) {
192 #ifdef MAP_ANONYMOUS
193 if (munmap(m->ptr, m->size) < 0)
194 pa_log("munmap() failed: %s", pa_cstrerror(errno));
195 #elif defined(HAVE_POSIX_MEMALIGN)
196 free(m->ptr);
197 #else
198 pa_xfree(m->ptr);
199 #endif
200 } else {
201 #ifdef HAVE_SHM_OPEN
202 if (munmap(m->ptr, m->size) < 0)
203 pa_log("munmap() failed: %s", pa_cstrerror(errno));
204
205 if (m->do_unlink) {
206 char fn[32];
207
208 segment_name(fn, sizeof(fn), m->id);
209
210 if (shm_unlink(fn) < 0)
211 pa_log(" shm_unlink(%s) failed: %s", fn, pa_cstrerror(errno));
212 }
213 #else
214 /* We shouldn't be here without shm support */
215 pa_assert_not_reached();
216 #endif
217 }
218
219 memset(m, 0, sizeof(*m));
220 }
221
222 void pa_shm_punch(pa_shm *m, size_t offset, size_t size) {
223 void *ptr;
224 size_t o, ps;
225
226 pa_assert(m);
227 pa_assert(m->ptr);
228 pa_assert(m->size > 0);
229 pa_assert(offset+size <= m->size);
230
231 #ifdef MAP_FAILED
232 pa_assert(m->ptr != MAP_FAILED);
233 #endif
234
235 /* You're welcome to implement this as NOOP on systems that don't
236 * support it */
237
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);
241
242 if (o > 0) {
243 ps = PA_PAGE_SIZE;
244 ptr = (uint8_t*) ptr + (ps - o);
245 size -= ps - o;
246 }
247
248 #ifdef MADV_REMOVE
249 if (madvise(ptr, size, MADV_REMOVE) >= 0)
250 return;
251 #endif
252
253 #ifdef MADV_FREE
254 if (madvise(ptr, size, MADV_FREE) >= 0)
255 return;
256 #endif
257
258 #ifdef MADV_DONTNEED
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);
262 #endif
263 }
264
265 #ifdef HAVE_SHM_OPEN
266
267 int pa_shm_attach_ro(pa_shm *m, unsigned id) {
268 char fn[32];
269 int fd = -1;
270 struct stat st;
271
272 pa_assert(m);
273
274 segment_name(fn, sizeof(fn), m->id = id);
275
276 if ((fd = shm_open(fn, O_RDONLY, 0)) < 0) {
277 if (errno != EACCES)
278 pa_log("shm_open() failed: %s", pa_cstrerror(errno));
279 goto fail;
280 }
281
282 if (fstat(fd, &st) < 0) {
283 pa_log("fstat() failed: %s", pa_cstrerror(errno));
284 goto fail;
285 }
286
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");
291 goto fail;
292 }
293
294 m->size = st.st_size;
295
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));
298 goto fail;
299 }
300
301 m->do_unlink = 0;
302 m->shared = 1;
303
304 pa_assert_se(pa_close(fd) == 0);
305
306 return 0;
307
308 fail:
309 if (fd >= 0)
310 pa_close(fd);
311
312 return -1;
313 }
314
315 #else /* HAVE_SHM_OPEN */
316
317 int pa_shm_attach_ro(pa_shm *m, unsigned id) {
318 return -1;
319 }
320
321 #endif /* HAVE_SHM_OPEN */
322
323 int pa_shm_cleanup(void) {
324
325 #ifdef HAVE_SHM_OPEN
326 #ifdef SHM_PATH
327 DIR *d;
328 struct dirent *de;
329
330 if (!(d = opendir(SHM_PATH))) {
331 pa_log_warn("Failed to read "SHM_PATH": %s", pa_cstrerror(errno));
332 return -1;
333 }
334
335 while ((de = readdir(d))) {
336 pa_shm seg;
337 unsigned id;
338 pid_t pid;
339 char fn[128];
340 struct shm_marker *m;
341
342 if (strncmp(de->d_name, "pulse-shm-", 10))
343 continue;
344
345 if (pa_atou(de->d_name + 10, &id) < 0)
346 continue;
347
348 if (pa_shm_attach_ro(&seg, id) < 0)
349 continue;
350
351 if (seg.size < PA_ALIGN(sizeof(struct shm_marker))) {
352 pa_shm_free(&seg);
353 continue;
354 }
355
356 m = (struct shm_marker*) ((uint8_t*) seg.ptr + seg.size - PA_ALIGN(sizeof(struct shm_marker)));
357
358 if (pa_atomic_load(&m->marker) != SHM_MARKER) {
359 pa_shm_free(&seg);
360 continue;
361 }
362
363 if (!(pid = (pid_t) pa_atomic_load(&m->pid))) {
364 pa_shm_free(&seg);
365 continue;
366 }
367
368 if (kill(pid, 0) == 0 || errno != ESRCH) {
369 pa_shm_free(&seg);
370 continue;
371 }
372
373 pa_shm_free(&seg);
374
375 /* Ok, the owner of this shms segment is dead, so, let's remove the segment */
376 segment_name(fn, sizeof(fn), id);
377
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));
380 }
381
382 closedir(d);
383 #endif /* SHM_PATH */
384 #endif /* HAVE_SHM_OPEN */
385
386 return 0;
387 }