]>
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)
63 #define MAX_SHM_SIZE (PA_ALIGN(1024*1024*64))
66 /* On Linux we know that the shared memory blocks are files in
67 * /dev/shm. We can use that information to list all blocks and
68 * cleanup unused ones */
69 #define SHM_PATH "/dev/shm/"
74 #define SHM_MARKER ((int) 0xbeefcafe)
76 /* We now put this SHM marker at the end of each segment. It's
77 * optional, to not require a reboot when upgrading, though. Note that
78 * on multiarch systems 32bit and 64bit processes might access this
79 * region simultaneously. The header fields need to be independant
80 * from the process' word with */
82 pa_atomic_t marker
; /* 0xbeefcafe */
90 #define SHM_MARKER_SIZE PA_ALIGN(sizeof(struct shm_marker))
92 static char *segment_name(char *fn
, size_t l
, unsigned id
) {
93 pa_snprintf(fn
, l
, "/pulse-shm-%u", id
);
97 int pa_shm_create_rw(pa_shm
*m
, size_t size
, pa_bool_t shared
, mode_t mode
) {
103 pa_assert(size
<= MAX_SHM_SIZE
);
104 pa_assert(mode
>= 0600);
106 /* Each time we create a new SHM area, let's first drop all stale
110 /* Round up to make it page aligned */
111 size
= PA_PAGE_ALIGN(size
);
118 if ((m
->ptr
= mmap(NULL
, m
->size
, PROT_READ
|PROT_WRITE
, MAP_ANONYMOUS
|MAP_PRIVATE
, -1, (off_t
) 0)) == MAP_FAILED
) {
119 pa_log("mmap() failed: %s", pa_cstrerror(errno
));
122 #elif defined(HAVE_POSIX_MEMALIGN)
126 if ((r
= posix_memalign(&m
->ptr
, PA_PAGE_SIZE
, size
)) < 0) {
127 pa_log("posix_memalign() failed: %s", pa_cstrerror(r
));
132 m
->ptr
= pa_xmalloc(m
->size
);
135 m
->do_unlink
= FALSE
;
139 struct shm_marker
*marker
;
141 pa_random(&m
->id
, sizeof(m
->id
));
142 segment_name(fn
, sizeof(fn
), m
->id
);
144 if ((fd
= shm_open(fn
, O_RDWR
|O_CREAT
|O_EXCL
, mode
& 0444)) < 0) {
145 pa_log("shm_open() failed: %s", pa_cstrerror(errno
));
149 m
->size
= size
+ SHM_MARKER_SIZE
;
151 if (ftruncate(fd
, (off_t
) m
->size
) < 0) {
152 pa_log("ftruncate() failed: %s", pa_cstrerror(errno
));
156 if ((m
->ptr
= mmap(NULL
, PA_PAGE_ALIGN(m
->size
), PROT_READ
|PROT_WRITE
, MAP_SHARED
, fd
, (off_t
) 0)) == MAP_FAILED
) {
157 pa_log("mmap() failed: %s", pa_cstrerror(errno
));
161 /* We store our PID at the end of the shm block, so that we
162 * can check for dead shm segments later */
163 marker
= (struct shm_marker
*) ((uint8_t*) m
->ptr
+ m
->size
- SHM_MARKER_SIZE
);
164 pa_atomic_store(&marker
->pid
, (int) getpid());
165 pa_atomic_store(&marker
->marker
, SHM_MARKER
);
167 pa_assert_se(pa_close(fd
) == 0);
190 void pa_shm_free(pa_shm
*m
) {
193 pa_assert(m
->size
> 0);
196 pa_assert(m
->ptr
!= MAP_FAILED
);
201 if (munmap(m
->ptr
, m
->size
) < 0)
202 pa_log("munmap() failed: %s", pa_cstrerror(errno
));
203 #elif defined(HAVE_POSIX_MEMALIGN)
210 if (munmap(m
->ptr
, PA_PAGE_ALIGN(m
->size
)) < 0)
211 pa_log("munmap() failed: %s", pa_cstrerror(errno
));
216 segment_name(fn
, sizeof(fn
), m
->id
);
218 if (shm_unlink(fn
) < 0)
219 pa_log(" shm_unlink(%s) failed: %s", fn
, pa_cstrerror(errno
));
222 /* We shouldn't be here without shm support */
223 pa_assert_not_reached();
230 void pa_shm_punch(pa_shm
*m
, size_t offset
, size_t size
) {
236 pa_assert(m
->size
> 0);
237 pa_assert(offset
+size
<= m
->size
);
240 pa_assert(m
->ptr
!= MAP_FAILED
);
243 /* You're welcome to implement this as NOOP on systems that don't
246 /* Align the pointer up to multiples of the page size */
247 ptr
= (uint8_t*) m
->ptr
+ offset
;
248 o
= (size_t) ((uint8_t*) ptr
- (uint8_t*) PA_PAGE_ALIGN_PTR(ptr
));
251 size_t delta
= PA_PAGE_SIZE
- o
;
252 ptr
= (uint8_t*) ptr
+ delta
;
256 /* Align the size down to multiples of page size */
257 size
= (size
/ PA_PAGE_SIZE
) * PA_PAGE_SIZE
;
260 if (madvise(ptr
, size
, MADV_REMOVE
) >= 0)
265 if (madvise(ptr
, size
, MADV_FREE
) >= 0)
270 madvise(ptr
, size
, MADV_DONTNEED
);
271 #elif defined(POSIX_MADV_DONTNEED)
272 posix_madvise(ptr
, size
, POSIX_MADV_DONTNEED
);
278 int pa_shm_attach_ro(pa_shm
*m
, unsigned id
) {
285 segment_name(fn
, sizeof(fn
), m
->id
= id
);
287 if ((fd
= shm_open(fn
, O_RDONLY
, 0)) < 0) {
289 pa_log("shm_open() failed: %s", pa_cstrerror(errno
));
293 if (fstat(fd
, &st
) < 0) {
294 pa_log("fstat() failed: %s", pa_cstrerror(errno
));
298 if (st
.st_size
<= 0 ||
299 st
.st_size
> (off_t
) (MAX_SHM_SIZE
+SHM_MARKER_SIZE
) ||
300 PA_ALIGN((size_t) st
.st_size
) != (size_t) st
.st_size
) {
301 pa_log("Invalid shared memory segment size");
305 m
->size
= (size_t) st
.st_size
;
307 if ((m
->ptr
= mmap(NULL
, PA_PAGE_ALIGN(m
->size
), PROT_READ
, MAP_SHARED
, fd
, (off_t
) 0)) == MAP_FAILED
) {
308 pa_log("mmap() failed: %s", pa_cstrerror(errno
));
312 m
->do_unlink
= FALSE
;
315 pa_assert_se(pa_close(fd
) == 0);
326 #else /* HAVE_SHM_OPEN */
328 int pa_shm_attach_ro(pa_shm
*m
, unsigned id
) {
332 #endif /* HAVE_SHM_OPEN */
334 int pa_shm_cleanup(void) {
341 if (!(d
= opendir(SHM_PATH
))) {
342 pa_log_warn("Failed to read "SHM_PATH
": %s", pa_cstrerror(errno
));
346 while ((de
= readdir(d
))) {
351 struct shm_marker
*m
;
353 if (strncmp(de
->d_name
, "pulse-shm-", 10))
356 if (pa_atou(de
->d_name
+ 10, &id
) < 0)
359 if (pa_shm_attach_ro(&seg
, id
) < 0)
362 if (seg
.size
< SHM_MARKER_SIZE
) {
367 m
= (struct shm_marker
*) ((uint8_t*) seg
.ptr
+ seg
.size
- SHM_MARKER_SIZE
);
369 if (pa_atomic_load(&m
->marker
) != SHM_MARKER
) {
374 if (!(pid
= (pid_t
) pa_atomic_load(&m
->pid
))) {
379 if (kill(pid
, 0) == 0 || errno
!= ESRCH
) {
386 /* Ok, the owner of this shms segment is dead, so, let's remove the segment */
387 segment_name(fn
, sizeof(fn
), id
);
389 if (shm_unlink(fn
) < 0 && errno
!= EACCES
&& errno
!= ENOENT
)
390 pa_log_warn("Failed to remove SHM segment %s: %s\n", fn
, pa_cstrerror(errno
));
394 #endif /* SHM_PATH */
395 #endif /* HAVE_SHM_OPEN */