]>
code.delx.au - pulseaudio/blob - src/pulsecore/shm.c
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/"
73 #define SHM_PATH "/tmp"
80 #define SHM_MARKER ((int) 0xbeefcafe)
82 /* We now put this SHM marker at the end of each segment. It's
83 * optional, to not require a reboot when upgrading, though. Note that
84 * on multiarch systems 32bit and 64bit processes might access this
85 * region simultaneously. The header fields need to be independent
86 * from the process' word with */
88 pa_atomic_t marker
; /* 0xbeefcafe */
96 #define SHM_MARKER_SIZE PA_ALIGN(sizeof(struct shm_marker))
99 static char *segment_name(char *fn
, size_t l
, unsigned id
) {
100 pa_snprintf(fn
, l
, "/pulse-shm-%u", id
);
105 int pa_shm_create_rw(pa_shm
*m
, size_t size
, bool shared
, mode_t mode
) {
113 pa_assert(size
<= MAX_SHM_SIZE
);
114 pa_assert(!(mode
& ~0777));
115 pa_assert(mode
>= 0600);
117 /* Each time we create a new SHM area, let's first drop all stale
121 /* Round up to make it page aligned */
122 size
= PA_PAGE_ALIGN(size
);
129 if ((m
->ptr
= mmap(NULL
, m
->size
, PROT_READ
|PROT_WRITE
, MAP_ANONYMOUS
|MAP_PRIVATE
, -1, (off_t
) 0)) == MAP_FAILED
) {
130 pa_log("mmap() failed: %s", pa_cstrerror(errno
));
133 #elif defined(HAVE_POSIX_MEMALIGN)
137 if ((r
= posix_memalign(&m
->ptr
, PA_PAGE_SIZE
, size
)) < 0) {
138 pa_log("posix_memalign() failed: %s", pa_cstrerror(r
));
143 m
->ptr
= pa_xmalloc(m
->size
);
146 m
->do_unlink
= false;
150 struct shm_marker
*marker
;
152 pa_random(&m
->id
, sizeof(m
->id
));
153 segment_name(fn
, sizeof(fn
), m
->id
);
155 if ((fd
= shm_open(fn
, O_RDWR
|O_CREAT
|O_EXCL
, mode
)) < 0) {
156 pa_log("shm_open() failed: %s", pa_cstrerror(errno
));
160 m
->size
= size
+ SHM_MARKER_SIZE
;
162 if (ftruncate(fd
, (off_t
) m
->size
) < 0) {
163 pa_log("ftruncate() failed: %s", pa_cstrerror(errno
));
167 #ifndef MAP_NORESERVE
168 #define MAP_NORESERVE 0
171 if ((m
->ptr
= mmap(NULL
, PA_PAGE_ALIGN(m
->size
), PROT_READ
|PROT_WRITE
, MAP_SHARED
|MAP_NORESERVE
, fd
, (off_t
) 0)) == MAP_FAILED
) {
172 pa_log("mmap() failed: %s", pa_cstrerror(errno
));
176 /* We store our PID at the end of the shm block, so that we
177 * can check for dead shm segments later */
178 marker
= (struct shm_marker
*) ((uint8_t*) m
->ptr
+ m
->size
- SHM_MARKER_SIZE
);
179 pa_atomic_store(&marker
->pid
, (int) getpid());
180 pa_atomic_store(&marker
->marker
, SHM_MARKER
);
182 pa_assert_se(pa_close(fd
) == 0);
205 void pa_shm_free(pa_shm
*m
) {
208 pa_assert(m
->size
> 0);
211 pa_assert(m
->ptr
!= MAP_FAILED
);
216 if (munmap(m
->ptr
, m
->size
) < 0)
217 pa_log("munmap() failed: %s", pa_cstrerror(errno
));
218 #elif defined(HAVE_POSIX_MEMALIGN)
225 if (munmap(m
->ptr
, PA_PAGE_ALIGN(m
->size
)) < 0)
226 pa_log("munmap() failed: %s", pa_cstrerror(errno
));
231 segment_name(fn
, sizeof(fn
), m
->id
);
233 if (shm_unlink(fn
) < 0)
234 pa_log(" shm_unlink(%s) failed: %s", fn
, pa_cstrerror(errno
));
237 /* We shouldn't be here without shm support */
238 pa_assert_not_reached();
245 void pa_shm_punch(pa_shm
*m
, size_t offset
, size_t size
) {
251 pa_assert(m
->size
> 0);
252 pa_assert(offset
+size
<= m
->size
);
255 pa_assert(m
->ptr
!= MAP_FAILED
);
258 /* You're welcome to implement this as NOOP on systems that don't
261 /* Align the pointer up to multiples of the page size */
262 ptr
= (uint8_t*) m
->ptr
+ offset
;
263 o
= (size_t) ((uint8_t*) ptr
- (uint8_t*) PA_PAGE_ALIGN_PTR(ptr
));
266 size_t delta
= PA_PAGE_SIZE
- o
;
267 ptr
= (uint8_t*) ptr
+ delta
;
271 /* Align the size down to multiples of page size */
272 size
= (size
/ PA_PAGE_SIZE
) * PA_PAGE_SIZE
;
275 if (madvise(ptr
, size
, MADV_REMOVE
) >= 0)
280 if (madvise(ptr
, size
, MADV_FREE
) >= 0)
285 madvise(ptr
, size
, MADV_DONTNEED
);
286 #elif defined(POSIX_MADV_DONTNEED)
287 posix_madvise(ptr
, size
, POSIX_MADV_DONTNEED
);
293 int pa_shm_attach_ro(pa_shm
*m
, unsigned id
) {
300 segment_name(fn
, sizeof(fn
), m
->id
= id
);
302 if ((fd
= shm_open(fn
, O_RDONLY
, 0)) < 0) {
303 if (errno
!= EACCES
&& errno
!= ENOENT
)
304 pa_log("shm_open() failed: %s", pa_cstrerror(errno
));
308 if (fstat(fd
, &st
) < 0) {
309 pa_log("fstat() failed: %s", pa_cstrerror(errno
));
313 if (st
.st_size
<= 0 ||
314 st
.st_size
> (off_t
) (MAX_SHM_SIZE
+SHM_MARKER_SIZE
) ||
315 PA_ALIGN((size_t) st
.st_size
) != (size_t) st
.st_size
) {
316 pa_log("Invalid shared memory segment size");
320 m
->size
= (size_t) st
.st_size
;
322 if ((m
->ptr
= mmap(NULL
, PA_PAGE_ALIGN(m
->size
), PROT_READ
, MAP_SHARED
, fd
, (off_t
) 0)) == MAP_FAILED
) {
323 pa_log("mmap() failed: %s", pa_cstrerror(errno
));
327 m
->do_unlink
= false;
330 pa_assert_se(pa_close(fd
) == 0);
341 #else /* HAVE_SHM_OPEN */
343 int pa_shm_attach_ro(pa_shm
*m
, unsigned id
) {
347 #endif /* HAVE_SHM_OPEN */
349 int pa_shm_cleanup(void) {
356 if (!(d
= opendir(SHM_PATH
))) {
357 pa_log_warn("Failed to read "SHM_PATH
": %s", pa_cstrerror(errno
));
361 while ((de
= readdir(d
))) {
366 struct shm_marker
*m
;
369 if (strncmp(de
->d_name
, ".SHMDpulse-shm-", SHM_ID_LEN
))
371 if (strncmp(de
->d_name
, "pulse-shm-", SHM_ID_LEN
))
375 if (pa_atou(de
->d_name
+ SHM_ID_LEN
, &id
) < 0)
378 if (pa_shm_attach_ro(&seg
, id
) < 0)
381 if (seg
.size
< SHM_MARKER_SIZE
) {
386 m
= (struct shm_marker
*) ((uint8_t*) seg
.ptr
+ seg
.size
- SHM_MARKER_SIZE
);
388 if (pa_atomic_load(&m
->marker
) != SHM_MARKER
) {
393 if (!(pid
= (pid_t
) pa_atomic_load(&m
->pid
))) {
398 if (kill(pid
, 0) == 0 || errno
!= ESRCH
) {
405 /* Ok, the owner of this shms segment is dead, so, let's remove the segment */
406 segment_name(fn
, sizeof(fn
), id
);
408 if (shm_unlink(fn
) < 0 && errno
!= EACCES
&& errno
!= ENOENT
)
409 pa_log_warn("Failed to remove SHM segment %s: %s\n", fn
, pa_cstrerror(errno
));
413 #endif /* SHM_PATH */
414 #endif /* HAVE_SHM_OPEN */