Revert "resamplers: Optimize trivial resampler"
[profile/ivi/pulseaudio-panda.git] / src / pulsecore / shm.c
1 /***
2     This file is part of PulseAudio.
3
4     Copyright 2006 Lennart Poettering
5     Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6
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.
11
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.
16
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 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
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>
37
38 #ifdef HAVE_SYS_MMAN_H
39 #include <sys/mman.h>
40 #endif
41
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
46
47 #include <pulse/xmalloc.h>
48 #include <pulse/gccmacro.h>
49
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>
56
57 #include "shm.h"
58
59 #if defined(__linux__) && !defined(MADV_REMOVE)
60 #define MADV_REMOVE 9
61 #endif
62
63 /* 1 GiB at max */
64 #define MAX_SHM_SIZE (PA_ALIGN(1024*1024*1024))
65
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
74
75 #define SHM_MARKER ((int) 0xbeefcafe)
76
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 independent
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;
90
91 #define SHM_MARKER_SIZE PA_ALIGN(sizeof(struct shm_marker))
92
93 #ifdef HAVE_SHM_OPEN
94 static char *segment_name(char *fn, size_t l, unsigned id) {
95     pa_snprintf(fn, l, "/pulse-shm-%u", id);
96     return fn;
97 }
98 #endif
99
100 int pa_shm_create_rw(pa_shm *m, size_t size, pa_bool_t shared, mode_t mode) {
101 #ifdef HAVE_SHM_OPEN
102     char fn[32];
103     int fd = -1;
104 #endif
105
106     pa_assert(m);
107     pa_assert(size > 0);
108     pa_assert(size <= MAX_SHM_SIZE);
109     pa_assert(mode >= 0600);
110
111     /* Each time we create a new SHM area, let's first drop all stale
112      * ones */
113     pa_shm_cleanup();
114
115     /* Round up to make it page aligned */
116     size = PA_PAGE_ALIGN(size);
117
118     if (!shared) {
119         m->id = 0;
120         m->size = size;
121
122 #ifdef MAP_ANONYMOUS
123         if ((m->ptr = mmap(NULL, m->size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, (off_t) 0)) == MAP_FAILED) {
124             pa_log("mmap() failed: %s", pa_cstrerror(errno));
125             goto fail;
126         }
127 #elif defined(HAVE_POSIX_MEMALIGN)
128         {
129             int r;
130
131             if ((r = posix_memalign(&m->ptr, PA_PAGE_SIZE, size)) < 0) {
132                 pa_log("posix_memalign() failed: %s", pa_cstrerror(r));
133                 goto fail;
134             }
135         }
136 #else
137         m->ptr = pa_xmalloc(m->size);
138 #endif
139
140         m->do_unlink = FALSE;
141
142     } else {
143 #ifdef HAVE_SHM_OPEN
144         struct shm_marker *marker;
145
146         pa_random(&m->id, sizeof(m->id));
147         segment_name(fn, sizeof(fn), m->id);
148
149         if ((fd = shm_open(fn, O_RDWR|O_CREAT|O_EXCL, mode & 0444)) < 0) {
150             pa_log("shm_open() failed: %s", pa_cstrerror(errno));
151             goto fail;
152         }
153
154         m->size = size + SHM_MARKER_SIZE;
155
156         if (ftruncate(fd, (off_t) m->size) < 0) {
157             pa_log("ftruncate() failed: %s", pa_cstrerror(errno));
158             goto fail;
159         }
160
161 #ifndef MAP_NORESERVE
162 #define MAP_NORESERVE 0
163 #endif
164
165         if ((m->ptr = mmap(NULL, PA_PAGE_ALIGN(m->size), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_NORESERVE, fd, (off_t) 0)) == MAP_FAILED) {
166             pa_log("mmap() failed: %s", pa_cstrerror(errno));
167             goto fail;
168         }
169
170         /* We store our PID at the end of the shm block, so that we
171          * can check for dead shm segments later */
172         marker = (struct shm_marker*) ((uint8_t*) m->ptr + m->size - SHM_MARKER_SIZE);
173         pa_atomic_store(&marker->pid, (int) getpid());
174         pa_atomic_store(&marker->marker, SHM_MARKER);
175
176         pa_assert_se(pa_close(fd) == 0);
177         m->do_unlink = TRUE;
178 #else
179         goto fail;
180 #endif
181     }
182
183     m->shared = shared;
184
185     return 0;
186
187 fail:
188
189 #ifdef HAVE_SHM_OPEN
190     if (fd >= 0) {
191         shm_unlink(fn);
192         pa_close(fd);
193     }
194 #endif
195
196     return -1;
197 }
198
199 void pa_shm_free(pa_shm *m) {
200     pa_assert(m);
201     pa_assert(m->ptr);
202     pa_assert(m->size > 0);
203
204 #ifdef MAP_FAILED
205     pa_assert(m->ptr != MAP_FAILED);
206 #endif
207
208     if (!m->shared) {
209 #ifdef MAP_ANONYMOUS
210         if (munmap(m->ptr, m->size) < 0)
211             pa_log("munmap() failed: %s", pa_cstrerror(errno));
212 #elif defined(HAVE_POSIX_MEMALIGN)
213         free(m->ptr);
214 #else
215         pa_xfree(m->ptr);
216 #endif
217     } else {
218 #ifdef HAVE_SHM_OPEN
219         if (munmap(m->ptr, PA_PAGE_ALIGN(m->size)) < 0)
220             pa_log("munmap() failed: %s", pa_cstrerror(errno));
221
222         if (m->do_unlink) {
223             char fn[32];
224
225             segment_name(fn, sizeof(fn), m->id);
226
227             if (shm_unlink(fn) < 0)
228                 pa_log(" shm_unlink(%s) failed: %s", fn, pa_cstrerror(errno));
229         }
230 #else
231         /* We shouldn't be here without shm support */
232         pa_assert_not_reached();
233 #endif
234     }
235
236     pa_zero(*m);
237 }
238
239 void pa_shm_punch(pa_shm *m, size_t offset, size_t size) {
240     void *ptr;
241     size_t o;
242
243     pa_assert(m);
244     pa_assert(m->ptr);
245     pa_assert(m->size > 0);
246     pa_assert(offset+size <= m->size);
247
248 #ifdef MAP_FAILED
249     pa_assert(m->ptr != MAP_FAILED);
250 #endif
251
252     /* You're welcome to implement this as NOOP on systems that don't
253      * support it */
254
255     /* Align the pointer up to multiples of the page size */
256     ptr = (uint8_t*) m->ptr + offset;
257     o = (size_t) ((uint8_t*) ptr - (uint8_t*) PA_PAGE_ALIGN_PTR(ptr));
258
259     if (o > 0) {
260         size_t delta = PA_PAGE_SIZE - o;
261         ptr = (uint8_t*) ptr + delta;
262         size -= delta;
263     }
264
265     /* Align the size down to multiples of page size */
266     size = (size / PA_PAGE_SIZE) * PA_PAGE_SIZE;
267
268 #ifdef MADV_REMOVE
269     if (madvise(ptr, size, MADV_REMOVE) >= 0)
270         return;
271 #endif
272
273 #ifdef MADV_FREE
274     if (madvise(ptr, size, MADV_FREE) >= 0)
275         return;
276 #endif
277
278 #ifdef MADV_DONTNEED
279     madvise(ptr, size, MADV_DONTNEED);
280 #elif defined(POSIX_MADV_DONTNEED)
281     posix_madvise(ptr, size, POSIX_MADV_DONTNEED);
282 #endif
283 }
284
285 #ifdef HAVE_SHM_OPEN
286
287 int pa_shm_attach_ro(pa_shm *m, unsigned id) {
288     char fn[32];
289     int fd = -1;
290     struct stat st;
291
292     pa_assert(m);
293
294     segment_name(fn, sizeof(fn), m->id = id);
295
296     if ((fd = shm_open(fn, O_RDONLY, 0)) < 0) {
297         if (errno != EACCES && errno != ENOENT)
298             pa_log("shm_open() failed: %s", pa_cstrerror(errno));
299         goto fail;
300     }
301
302     if (fstat(fd, &st) < 0) {
303         pa_log("fstat() failed: %s", pa_cstrerror(errno));
304         goto fail;
305     }
306
307     if (st.st_size <= 0 ||
308         st.st_size > (off_t) (MAX_SHM_SIZE+SHM_MARKER_SIZE) ||
309         PA_ALIGN((size_t) st.st_size) != (size_t) st.st_size) {
310         pa_log("Invalid shared memory segment size");
311         goto fail;
312     }
313
314     m->size = (size_t) st.st_size;
315
316     if ((m->ptr = mmap(NULL, PA_PAGE_ALIGN(m->size), PROT_READ, MAP_SHARED, fd, (off_t) 0)) == MAP_FAILED) {
317         pa_log("mmap() failed: %s", pa_cstrerror(errno));
318         goto fail;
319     }
320
321     m->do_unlink = FALSE;
322     m->shared = TRUE;
323
324     pa_assert_se(pa_close(fd) == 0);
325
326     return 0;
327
328 fail:
329     if (fd >= 0)
330         pa_close(fd);
331
332     return -1;
333 }
334
335 #else /* HAVE_SHM_OPEN */
336
337 int pa_shm_attach_ro(pa_shm *m, unsigned id) {
338     return -1;
339 }
340
341 #endif /* HAVE_SHM_OPEN */
342
343 int pa_shm_cleanup(void) {
344
345 #ifdef HAVE_SHM_OPEN
346 #ifdef SHM_PATH
347     DIR *d;
348     struct dirent *de;
349
350     if (!(d = opendir(SHM_PATH))) {
351         pa_log_warn("Failed to read "SHM_PATH": %s", pa_cstrerror(errno));
352         return -1;
353     }
354
355     while ((de = readdir(d))) {
356         pa_shm seg;
357         unsigned id;
358         pid_t pid;
359         char fn[128];
360         struct shm_marker *m;
361
362         if (strncmp(de->d_name, "pulse-shm-", 10))
363             continue;
364
365         if (pa_atou(de->d_name + 10, &id) < 0)
366             continue;
367
368         if (pa_shm_attach_ro(&seg, id) < 0)
369             continue;
370
371         if (seg.size < SHM_MARKER_SIZE) {
372             pa_shm_free(&seg);
373             continue;
374         }
375
376         m = (struct shm_marker*) ((uint8_t*) seg.ptr + seg.size - SHM_MARKER_SIZE);
377
378         if (pa_atomic_load(&m->marker) != SHM_MARKER) {
379             pa_shm_free(&seg);
380             continue;
381         }
382
383         if (!(pid = (pid_t) pa_atomic_load(&m->pid))) {
384             pa_shm_free(&seg);
385             continue;
386         }
387
388         if (kill(pid, 0) == 0 || errno != ESRCH) {
389             pa_shm_free(&seg);
390             continue;
391         }
392
393         pa_shm_free(&seg);
394
395         /* Ok, the owner of this shms segment is dead, so, let's remove the segment */
396         segment_name(fn, sizeof(fn), id);
397
398         if (shm_unlink(fn) < 0 && errno != EACCES && errno != ENOENT)
399             pa_log_warn("Failed to remove SHM segment %s: %s\n", fn, pa_cstrerror(errno));
400     }
401
402     closedir(d);
403 #endif /* SHM_PATH */
404 #endif /* HAVE_SHM_OPEN */
405
406     return 0;
407 }