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