]> code.delx.au - pulseaudio/blob - src/pulsecore/shm.c
if MAP_ANONYMOUS is not supported use posix_memalign if possible to allocate the...
[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 <sys/mman.h>
31 #include <stdio.h>
32 #include <errno.h>
33 #include <string.h>
34 #include <sys/stat.h>
35
36 #include <pulsecore/core-error.h>
37 #include <pulsecore/log.h>
38 #include <pulsecore/random.h>
39 #include <pulse/xmalloc.h>
40
41 #include "shm.h"
42
43 #if defined(__linux__) && !defined(MADV_REMOVE)
44 #define MADV_REMOVE 9
45 #endif
46
47 #define MAX_SHM_SIZE (1024*1024*20)
48
49 static char *segment_name(char *fn, size_t l, unsigned id) {
50 snprintf(fn, l, "/pulse-shm-%u", id);
51 return fn;
52 }
53
54 int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode) {
55 char fn[32];
56 int fd = -1;
57
58 assert(m);
59 assert(size > 0);
60 assert(size < MAX_SHM_SIZE);
61 assert(mode >= 0600);
62
63 if (!shared) {
64 m->id = 0;
65 m->size = size;
66
67 #ifdef MAP_ANONYMOUS
68 if ((m->ptr = mmap(NULL, m->size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0)) == MAP_FAILED) {
69 pa_log("mmap() failed: %s", pa_cstrerror(errno));
70 goto fail;
71 }
72 #elif defined(HAVE_POSIX_MEMALIGN)
73 {
74 int r;
75
76 if ((r = posix_memalign(&m->ptr, sysconf(_SC_PAGESIZE), size)) < 0) {
77 pa_log("posix_memalign() failed: %s", pa_cstrerror(r));
78 goto fail;
79 }
80 }
81 #else
82 m->ptr = pa_xmalloc(m->size);
83 #endif
84
85 m->do_unlink = 0;
86
87 } else {
88 pa_random(&m->id, sizeof(m->id));
89 segment_name(fn, sizeof(fn), m->id);
90
91 if ((fd = shm_open(fn, O_RDWR|O_CREAT|O_EXCL, mode & 0444)) < 0) {
92 pa_log("shm_open() failed: %s", pa_cstrerror(errno));
93 goto fail;
94 }
95
96 if (ftruncate(fd, m->size = size) < 0) {
97 pa_log("ftruncate() failed: %s", pa_cstrerror(errno));
98 goto fail;
99 }
100
101 if ((m->ptr = mmap(NULL, m->size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
102 pa_log("mmap() failed: %s", pa_cstrerror(errno));
103 goto fail;
104 }
105
106 close(fd);
107 m->do_unlink = 1;
108 }
109
110 m->shared = shared;
111
112 return 0;
113
114 fail:
115
116 if (fd >= 0) {
117 shm_unlink(fn);
118 close(fd);
119 }
120
121 return -1;
122 }
123
124 void pa_shm_free(pa_shm *m) {
125 char fn[32];
126
127 assert(m);
128 assert(m->ptr && m->ptr != MAP_FAILED);
129 assert(m->size > 0);
130
131 #if !defined(MAP_ANONYMOUS) && defined(HAVE_POSIX_MEMALIGN)
132 if (!m->shared)
133 free(m->ptr);
134 else
135 #elif !defined(MAP_ANONYMOUS)
136 if (!m->shared)
137 pa_xfree(m->ptr);
138 else
139 #endif
140
141 if (munmap(m->ptr, m->size) < 0)
142 pa_log("munmap() failed: %s", pa_cstrerror(errno));
143
144 if (m->do_unlink) {
145 segment_name(fn, sizeof(fn), m->id);
146
147 if (shm_unlink(fn) < 0)
148 pa_log(__FILE__":shm_unlink(%s) failed: %s", fn, pa_cstrerror(errno));
149 }
150
151 memset(m, 0, sizeof(*m));
152 }
153
154 void pa_shm_punch(pa_shm *m, size_t offset, size_t size) {
155 void *ptr;
156
157 assert(m);
158 assert(m->ptr && m->ptr != MAP_FAILED);
159 assert(m->size > 0);
160 assert(offset < m->size);
161 assert(offset+size < m->size);
162
163 /* You're welcome to implement this as NOOP on systems that don't
164 * support it */
165
166 ptr = (uint8_t*) m->ptr + offset;
167
168 #ifdef __linux__
169 {
170 /* On Linux ptr must be page aligned */
171 long psz = sysconf(_SC_PAGESIZE);
172 unsigned o;
173
174 o = ((unsigned long) ptr) - ((((unsigned long) ptr)/psz) * psz);
175
176 if (o > 0) {
177 ptr = (uint8_t*) ptr + (psz - o);
178 size -= psz - o;
179 }
180 }
181 #endif
182
183 #ifdef MADV_REMOVE
184 if (madvise(ptr, size, MADV_REMOVE) >= 0)
185 return;
186 #endif
187
188 #ifdef MADV_FREE
189 if (madvise(ptr, size, MADV_FREE) >= 0)
190 return;
191 #endif
192
193 #ifdef MADV_DONTNEED
194 madvise(ptr, size, MADV_DONTNEED);
195 #endif
196 }
197
198 int pa_shm_attach_ro(pa_shm *m, unsigned id) {
199 char fn[32];
200 int fd = -1;
201 struct stat st;
202
203 assert(m);
204
205 segment_name(fn, sizeof(fn), m->id = id);
206
207 if ((fd = shm_open(fn, O_RDONLY, 0)) < 0) {
208 pa_log("shm_open() failed: %s", pa_cstrerror(errno));
209 goto fail;
210 }
211
212 if (fstat(fd, &st) < 0) {
213 pa_log("fstat() failed: %s", pa_cstrerror(errno));
214 goto fail;
215 }
216
217 if (st.st_size <= 0 || st.st_size > MAX_SHM_SIZE) {
218 pa_log("Invalid shared memory segment size");
219 goto fail;
220 }
221
222 m->size = st.st_size;
223
224 if ((m->ptr = mmap(NULL, m->size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
225 pa_log("mmap() failed: %s", pa_cstrerror(errno));
226 goto fail;
227 }
228
229 m->do_unlink = 0;
230 m->shared = 1;
231
232 close(fd);
233
234 return 0;
235
236 fail:
237 if (fd >= 0)
238 close(fd);
239
240 return -1;
241 }