]>
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 #include <pulse/xmalloc.h>
43 #include <pulse/gccmacro.h>
45 #include <pulsecore/core-error.h>
46 #include <pulsecore/log.h>
47 #include <pulsecore/random.h>
48 #include <pulsecore/core-util.h>
49 #include <pulsecore/macro.h>
50 #include <pulsecore/atomic.h>
54 #if defined(__linux__) && !defined(MADV_REMOVE)
58 #define MAX_SHM_SIZE (PA_ALIGN(1024*1024*64))
61 /* On Linux we know that the shared memory blocks are files in
62 * /dev/shm. We can use that information to list all blocks and
63 * cleanup unused ones */
64 #define SHM_PATH "/dev/shm/"
69 #define SHM_MARKER ((int) 0xbeefcafe)
71 /* We now put this SHM marker at the end of each segment. It's
72 * optional, to not require a reboot when upgrading, though. Note that
73 * on multiarch systems 32bit and 64bit processes might access this
74 * region simultaneously. The header fields need to be independant
75 * from the process' word with */
77 pa_atomic_t marker
; /* 0xbeefcafe */
85 #define SHM_MARKER_SIZE PA_ALIGN(sizeof(struct shm_marker))
87 static char *segment_name(char *fn
, size_t l
, unsigned id
) {
88 pa_snprintf(fn
, l
, "/pulse-shm-%u", id
);
92 int pa_shm_create_rw(pa_shm
*m
, size_t size
, pa_bool_t shared
, mode_t mode
) {
98 pa_assert(size
<= MAX_SHM_SIZE
);
99 pa_assert(mode
>= 0600);
101 /* Each time we create a new SHM area, let's first drop all stale
105 /* Round up to make it page aligned */
106 size
= PA_PAGE_ALIGN(size
);
113 if ((m
->ptr
= mmap(NULL
, m
->size
, PROT_READ
|PROT_WRITE
, MAP_ANONYMOUS
|MAP_PRIVATE
, -1, (off_t
) 0)) == MAP_FAILED
) {
114 pa_log("mmap() failed: %s", pa_cstrerror(errno
));
117 #elif defined(HAVE_POSIX_MEMALIGN)
121 if ((r
= posix_memalign(&m
->ptr
, PA_PAGE_SIZE
, size
)) < 0) {
122 pa_log("posix_memalign() failed: %s", pa_cstrerror(r
));
127 m
->ptr
= pa_xmalloc(m
->size
);
130 m
->do_unlink
= FALSE
;
134 struct shm_marker
*marker
;
136 pa_random(&m
->id
, sizeof(m
->id
));
137 segment_name(fn
, sizeof(fn
), m
->id
);
139 if ((fd
= shm_open(fn
, O_RDWR
|O_CREAT
|O_EXCL
, mode
& 0444)) < 0) {
140 pa_log("shm_open() failed: %s", pa_cstrerror(errno
));
144 m
->size
= size
+ SHM_MARKER_SIZE
;
146 if (ftruncate(fd
, (off_t
) m
->size
) < 0) {
147 pa_log("ftruncate() failed: %s", pa_cstrerror(errno
));
151 if ((m
->ptr
= mmap(NULL
, PA_PAGE_ALIGN(m
->size
), PROT_READ
|PROT_WRITE
, MAP_SHARED
, fd
, (off_t
) 0)) == MAP_FAILED
) {
152 pa_log("mmap() failed: %s", pa_cstrerror(errno
));
156 /* We store our PID at the end of the shm block, so that we
157 * can check for dead shm segments later */
158 marker
= (struct shm_marker
*) ((uint8_t*) m
->ptr
+ m
->size
- SHM_MARKER_SIZE
);
159 pa_atomic_store(&marker
->pid
, (int) getpid());
160 pa_atomic_store(&marker
->marker
, SHM_MARKER
);
162 pa_assert_se(pa_close(fd
) == 0);
185 void pa_shm_free(pa_shm
*m
) {
188 pa_assert(m
->size
> 0);
191 pa_assert(m
->ptr
!= MAP_FAILED
);
196 if (munmap(m
->ptr
, m
->size
) < 0)
197 pa_log("munmap() failed: %s", pa_cstrerror(errno
));
198 #elif defined(HAVE_POSIX_MEMALIGN)
205 if (munmap(m
->ptr
, PA_PAGE_ALIGN(m
->size
)) < 0)
206 pa_log("munmap() failed: %s", pa_cstrerror(errno
));
211 segment_name(fn
, sizeof(fn
), m
->id
);
213 if (shm_unlink(fn
) < 0)
214 pa_log(" shm_unlink(%s) failed: %s", fn
, pa_cstrerror(errno
));
217 /* We shouldn't be here without shm support */
218 pa_assert_not_reached();
225 void pa_shm_punch(pa_shm
*m
, size_t offset
, size_t size
) {
231 pa_assert(m
->size
> 0);
232 pa_assert(offset
+size
<= m
->size
);
235 pa_assert(m
->ptr
!= MAP_FAILED
);
238 /* You're welcome to implement this as NOOP on systems that don't
241 /* Align the pointer up to multiples of the page size */
242 ptr
= (uint8_t*) m
->ptr
+ offset
;
243 o
= (size_t) ((uint8_t*) ptr
- (uint8_t*) PA_PAGE_ALIGN_PTR(ptr
));
246 size_t delta
= PA_PAGE_SIZE
- o
;
247 ptr
= (uint8_t*) ptr
+ delta
;
251 /* Align the size down to multiples of page size */
252 size
= (size
/ PA_PAGE_SIZE
) * PA_PAGE_SIZE
;
255 if (madvise(ptr
, size
, MADV_REMOVE
) >= 0)
260 if (madvise(ptr
, size
, MADV_FREE
) >= 0)
265 madvise(ptr
, size
, MADV_DONTNEED
);
266 #elif defined(POSIX_MADV_DONTNEED)
267 posix_madvise(ptr
, size
, POSIX_MADV_DONTNEED
);
273 int pa_shm_attach_ro(pa_shm
*m
, unsigned id
) {
280 segment_name(fn
, sizeof(fn
), m
->id
= id
);
282 if ((fd
= shm_open(fn
, O_RDONLY
, 0)) < 0) {
284 pa_log("shm_open() failed: %s", pa_cstrerror(errno
));
288 if (fstat(fd
, &st
) < 0) {
289 pa_log("fstat() failed: %s", pa_cstrerror(errno
));
293 if (st
.st_size
<= 0 ||
294 st
.st_size
> (off_t
) (MAX_SHM_SIZE
+SHM_MARKER_SIZE
) ||
295 PA_ALIGN((size_t) st
.st_size
) != (size_t) st
.st_size
) {
296 pa_log("Invalid shared memory segment size");
300 m
->size
= (size_t) st
.st_size
;
302 if ((m
->ptr
= mmap(NULL
, PA_PAGE_ALIGN(m
->size
), PROT_READ
, MAP_SHARED
, fd
, (off_t
) 0)) == MAP_FAILED
) {
303 pa_log("mmap() failed: %s", pa_cstrerror(errno
));
307 m
->do_unlink
= FALSE
;
310 pa_assert_se(pa_close(fd
) == 0);
321 #else /* HAVE_SHM_OPEN */
323 int pa_shm_attach_ro(pa_shm
*m
, unsigned id
) {
327 #endif /* HAVE_SHM_OPEN */
329 int pa_shm_cleanup(void) {
336 if (!(d
= opendir(SHM_PATH
))) {
337 pa_log_warn("Failed to read "SHM_PATH
": %s", pa_cstrerror(errno
));
341 while ((de
= readdir(d
))) {
346 struct shm_marker
*m
;
348 if (strncmp(de
->d_name
, "pulse-shm-", 10))
351 if (pa_atou(de
->d_name
+ 10, &id
) < 0)
354 if (pa_shm_attach_ro(&seg
, id
) < 0)
357 if (seg
.size
< SHM_MARKER_SIZE
) {
362 m
= (struct shm_marker
*) ((uint8_t*) seg
.ptr
+ seg
.size
- SHM_MARKER_SIZE
);
364 if (pa_atomic_load(&m
->marker
) != SHM_MARKER
) {
369 if (!(pid
= (pid_t
) pa_atomic_load(&m
->pid
))) {
374 if (kill(pid
, 0) == 0 || errno
!= ESRCH
) {
381 /* Ok, the owner of this shms segment is dead, so, let's remove the segment */
382 segment_name(fn
, sizeof(fn
), id
);
384 if (shm_unlink(fn
) < 0 && errno
!= EACCES
&& errno
!= ENOENT
)
385 pa_log_warn("Failed to remove SHM segment %s: %s\n", fn
, pa_cstrerror(errno
));
389 #endif /* SHM_PATH */
390 #endif /* HAVE_SHM_OPEN */