2 This file is part of PulseAudio.
4 Copyright 2006 Lennart Poettering
5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
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.
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.
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
34 #include <sys/types.h>
38 #ifdef HAVE_SYS_MMAN_H
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
47 #include <pulse/xmalloc.h>
48 #include <pulse/gccmacro.h>
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>
59 #if defined(__linux__) && !defined(MADV_REMOVE)
64 #define MAX_SHM_SIZE (PA_ALIGN(1024*1024*1024))
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/"
75 #define SHM_MARKER ((int) 0xbeefcafe)
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 independant
81 * from the process' word with */
83 pa_atomic_t marker
; /* 0xbeefcafe */
91 #define SHM_MARKER_SIZE PA_ALIGN(sizeof(struct shm_marker))
94 static char *segment_name(char *fn
, size_t l
, unsigned id
) {
95 pa_snprintf(fn
, l
, "/pulse-shm-%u", id
);
100 int pa_shm_create_rw(pa_shm
*m
, size_t size
, pa_bool_t shared
, mode_t mode
) {
108 pa_assert(size
<= MAX_SHM_SIZE
);
109 pa_assert(mode
>= 0600);
111 /* Each time we create a new SHM area, let's first drop all stale
115 /* Round up to make it page aligned */
116 size
= PA_PAGE_ALIGN(size
);
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
));
127 #elif defined(HAVE_POSIX_MEMALIGN)
131 if ((r
= posix_memalign(&m
->ptr
, PA_PAGE_SIZE
, size
)) < 0) {
132 pa_log("posix_memalign() failed: %s", pa_cstrerror(r
));
137 m
->ptr
= pa_xmalloc(m
->size
);
140 m
->do_unlink
= FALSE
;
144 struct shm_marker
*marker
;
146 pa_random(&m
->id
, sizeof(m
->id
));
147 segment_name(fn
, sizeof(fn
), m
->id
);
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
));
154 m
->size
= size
+ SHM_MARKER_SIZE
;
156 if (ftruncate(fd
, (off_t
) m
->size
) < 0) {
157 pa_log("ftruncate() failed: %s", pa_cstrerror(errno
));
161 #ifndef MAP_NORESERVE
162 #define MAP_NORESERVE 0
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
));
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
);
176 pa_assert_se(pa_close(fd
) == 0);
199 void pa_shm_free(pa_shm
*m
) {
202 pa_assert(m
->size
> 0);
205 pa_assert(m
->ptr
!= MAP_FAILED
);
210 if (munmap(m
->ptr
, m
->size
) < 0)
211 pa_log("munmap() failed: %s", pa_cstrerror(errno
));
212 #elif defined(HAVE_POSIX_MEMALIGN)
219 if (munmap(m
->ptr
, PA_PAGE_ALIGN(m
->size
)) < 0)
220 pa_log("munmap() failed: %s", pa_cstrerror(errno
));
225 segment_name(fn
, sizeof(fn
), m
->id
);
227 if (shm_unlink(fn
) < 0)
228 pa_log(" shm_unlink(%s) failed: %s", fn
, pa_cstrerror(errno
));
231 /* We shouldn't be here without shm support */
232 pa_assert_not_reached();
239 void pa_shm_punch(pa_shm
*m
, size_t offset
, size_t size
) {
245 pa_assert(m
->size
> 0);
246 pa_assert(offset
+size
<= m
->size
);
249 pa_assert(m
->ptr
!= MAP_FAILED
);
252 /* You're welcome to implement this as NOOP on systems that don't
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
));
260 size_t delta
= PA_PAGE_SIZE
- o
;
261 ptr
= (uint8_t*) ptr
+ delta
;
265 /* Align the size down to multiples of page size */
266 size
= (size
/ PA_PAGE_SIZE
) * PA_PAGE_SIZE
;
269 if (madvise(ptr
, size
, MADV_REMOVE
) >= 0)
274 if (madvise(ptr
, size
, MADV_FREE
) >= 0)
279 madvise(ptr
, size
, MADV_DONTNEED
);
280 #elif defined(POSIX_MADV_DONTNEED)
281 posix_madvise(ptr
, size
, POSIX_MADV_DONTNEED
);
287 int pa_shm_attach_ro(pa_shm
*m
, unsigned id
) {
294 segment_name(fn
, sizeof(fn
), m
->id
= id
);
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
));
302 if (fstat(fd
, &st
) < 0) {
303 pa_log("fstat() failed: %s", pa_cstrerror(errno
));
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");
314 m
->size
= (size_t) st
.st_size
;
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
));
321 m
->do_unlink
= FALSE
;
324 pa_assert_se(pa_close(fd
) == 0);
335 #else /* HAVE_SHM_OPEN */
337 int pa_shm_attach_ro(pa_shm
*m
, unsigned id
) {
341 #endif /* HAVE_SHM_OPEN */
343 int pa_shm_cleanup(void) {
350 if (!(d
= opendir(SHM_PATH
))) {
351 pa_log_warn("Failed to read "SHM_PATH
": %s", pa_cstrerror(errno
));
355 while ((de
= readdir(d
))) {
360 struct shm_marker
*m
;
362 if (strncmp(de
->d_name
, "pulse-shm-", 10))
365 if (pa_atou(de
->d_name
+ 10, &id
) < 0)
368 if (pa_shm_attach_ro(&seg
, id
) < 0)
371 if (seg
.size
< SHM_MARKER_SIZE
) {
376 m
= (struct shm_marker
*) ((uint8_t*) seg
.ptr
+ seg
.size
- SHM_MARKER_SIZE
);
378 if (pa_atomic_load(&m
->marker
) != SHM_MARKER
) {
383 if (!(pid
= (pid_t
) pa_atomic_load(&m
->pid
))) {
388 if (kill(pid
, 0) == 0 || errno
!= ESRCH
) {
395 /* Ok, the owner of this shms segment is dead, so, let's remove the segment */
396 segment_name(fn
, sizeof(fn
), id
);
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
));
403 #endif /* SHM_PATH */
404 #endif /* HAVE_SHM_OPEN */