]> code.delx.au - pulseaudio/blob - src/pulsecore/shm.c
Fix detection of shared memory support and proper fallback.
[pulseaudio] / src / pulsecore / shm.c
1 /* $Id$ */
2
3 /***
4 This file is part of PulseAudio.
5
6 PulseAudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as
8 published by the Free Software Foundation; either version 2.1 of the
9 License, or (at your option) any later version.
10
11 PulseAudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with PulseAudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19 USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <assert.h>
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
35 #ifdef HAVE_SYS_MMAN_H
36 #include <sys/mman.h>
37 #endif
38
39 #include <pulsecore/core-error.h>
40 #include <pulsecore/log.h>
41 #include <pulsecore/random.h>
42 #include <pulse/xmalloc.h>
43
44 #include "shm.h"
45
46 #if defined(__linux__) && !defined(MADV_REMOVE)
47 #define MADV_REMOVE 9
48 #endif
49
50 #define MAX_SHM_SIZE (1024*1024*20)
51
52 static char *segment_name(char *fn, size_t l, unsigned id) {
53 snprintf(fn, l, "/pulse-shm-%u", id);
54 return fn;
55 }
56
57 #ifdef HAVE_SHM_OPEN
58
59 int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode) {
60 char fn[32];
61 int fd = -1;
62
63 assert(m);
64 assert(size > 0);
65 assert(size < MAX_SHM_SIZE);
66 assert(mode >= 0600);
67
68 if (!shared) {
69 m->id = 0;
70 m->size = size;
71
72 #ifdef MAP_ANONYMOUS
73 if ((m->ptr = mmap(NULL, m->size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0)) == MAP_FAILED) {
74 pa_log("mmap() failed: %s", pa_cstrerror(errno));
75 goto fail;
76 }
77 #elif defined(HAVE_POSIX_MEMALIGN)
78 {
79 int r;
80
81 if ((r = posix_memalign(&m->ptr, sysconf(_SC_PAGESIZE), size)) < 0) {
82 pa_log("posix_memalign() failed: %s", pa_cstrerror(r));
83 goto fail;
84 }
85 }
86 #else
87 m->ptr = pa_xmalloc(m->size);
88 #endif
89
90 m->do_unlink = 0;
91
92 } else {
93 pa_random(&m->id, sizeof(m->id));
94 segment_name(fn, sizeof(fn), m->id);
95
96 if ((fd = shm_open(fn, O_RDWR|O_CREAT|O_EXCL, mode & 0444)) < 0) {
97 pa_log("shm_open() failed: %s", pa_cstrerror(errno));
98 goto fail;
99 }
100
101 if (ftruncate(fd, m->size = size) < 0) {
102 pa_log("ftruncate() failed: %s", pa_cstrerror(errno));
103 goto fail;
104 }
105
106 if ((m->ptr = mmap(NULL, m->size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
107 pa_log("mmap() failed: %s", pa_cstrerror(errno));
108 goto fail;
109 }
110
111 close(fd);
112 m->do_unlink = 1;
113 }
114
115 m->shared = shared;
116
117 return 0;
118
119 fail:
120
121 if (fd >= 0) {
122 shm_unlink(fn);
123 close(fd);
124 }
125
126 return -1;
127 }
128
129 void pa_shm_free(pa_shm *m) {
130 char fn[32];
131
132 assert(m);
133 assert(m->ptr && m->ptr != MAP_FAILED);
134 assert(m->size > 0);
135
136 #if !defined(MAP_ANONYMOUS) && defined(HAVE_POSIX_MEMALIGN)
137 if (!m->shared)
138 free(m->ptr);
139 else
140 #elif !defined(MAP_ANONYMOUS)
141 if (!m->shared)
142 pa_xfree(m->ptr);
143 else
144 #endif
145
146 if (munmap(m->ptr, m->size) < 0)
147 pa_log("munmap() failed: %s", pa_cstrerror(errno));
148
149 if (m->do_unlink) {
150 segment_name(fn, sizeof(fn), m->id);
151
152 if (shm_unlink(fn) < 0)
153 pa_log(__FILE__":shm_unlink(%s) failed: %s", fn, pa_cstrerror(errno));
154 }
155
156 memset(m, 0, sizeof(*m));
157 }
158
159 void pa_shm_punch(pa_shm *m, size_t offset, size_t size) {
160 void *ptr;
161
162 assert(m);
163 assert(m->ptr && m->ptr != MAP_FAILED);
164 assert(m->size > 0);
165 assert(offset < m->size);
166 assert(offset+size < m->size);
167
168 /* You're welcome to implement this as NOOP on systems that don't
169 * support it */
170
171 ptr = (uint8_t*) m->ptr + offset;
172
173 #ifdef __linux__
174 {
175 /* On Linux ptr must be page aligned */
176 long psz = sysconf(_SC_PAGESIZE);
177 unsigned o;
178
179 o = ((unsigned long) ptr) - ((((unsigned long) ptr)/psz) * psz);
180
181 if (o > 0) {
182 ptr = (uint8_t*) ptr + (psz - o);
183 size -= psz - o;
184 }
185 }
186 #endif
187
188 #ifdef MADV_REMOVE
189 if (madvise(ptr, size, MADV_REMOVE) >= 0)
190 return;
191 #endif
192
193 #ifdef MADV_FREE
194 if (madvise(ptr, size, MADV_FREE) >= 0)
195 return;
196 #endif
197
198 #ifdef MADV_DONTNEED
199 madvise(ptr, size, MADV_DONTNEED);
200 #endif
201 }
202
203 int pa_shm_attach_ro(pa_shm *m, unsigned id) {
204 char fn[32];
205 int fd = -1;
206 struct stat st;
207
208 assert(m);
209
210 segment_name(fn, sizeof(fn), m->id = id);
211
212 if ((fd = shm_open(fn, O_RDONLY, 0)) < 0) {
213 pa_log("shm_open() failed: %s", pa_cstrerror(errno));
214 goto fail;
215 }
216
217 if (fstat(fd, &st) < 0) {
218 pa_log("fstat() failed: %s", pa_cstrerror(errno));
219 goto fail;
220 }
221
222 if (st.st_size <= 0 || st.st_size > MAX_SHM_SIZE) {
223 pa_log("Invalid shared memory segment size");
224 goto fail;
225 }
226
227 m->size = st.st_size;
228
229 if ((m->ptr = mmap(NULL, m->size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
230 pa_log("mmap() failed: %s", pa_cstrerror(errno));
231 goto fail;
232 }
233
234 m->do_unlink = 0;
235 m->shared = 1;
236
237 close(fd);
238
239 return 0;
240
241 fail:
242 if (fd >= 0)
243 close(fd);
244
245 return -1;
246 }
247
248 #else /* HAVE_SHM_OPEN */
249
250 int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode) {
251 return -1;
252 }
253
254 void pa_shm_free(pa_shm *m) {
255 }
256
257 void pa_shm_punch(pa_shm *m, size_t offset, size_t size) {
258 }
259
260 int pa_shm_attach_ro(pa_shm *m, unsigned id) {
261 return -1;
262 }
263
264 #endif /* HAVE_SHM_OPEN */