]>
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 */
73 struct shm_marker PA_GCC_PACKED
{
74 pa_atomic_t marker
; /* 0xbeefcafe */
76 uint64_t *_reserverd1
;
77 uint64_t *_reserverd2
;
78 uint64_t *_reserverd3
;
79 uint64_t *_reserverd4
;
82 static char *segment_name(char *fn
, size_t l
, unsigned id
) {
83 pa_snprintf(fn
, l
, "/pulse-shm-%u", id
);
87 int pa_shm_create_rw(pa_shm
*m
, size_t size
, pa_bool_t shared
, mode_t mode
) {
93 pa_assert(size
<= MAX_SHM_SIZE
);
94 pa_assert(mode
>= 0600);
96 /* Each time we create a new SHM area, let's first drop all stale
100 /* Round up to make it aligned */
101 size
= PA_ALIGN(size
);
108 if ((m
->ptr
= mmap(NULL
, m
->size
, PROT_READ
|PROT_WRITE
, MAP_ANONYMOUS
|MAP_PRIVATE
, -1, 0)) == MAP_FAILED
) {
109 pa_log("mmap() failed: %s", pa_cstrerror(errno
));
112 #elif defined(HAVE_POSIX_MEMALIGN)
116 if ((r
= posix_memalign(&m
->ptr
, PA_PAGE_SIZE
, size
)) < 0) {
117 pa_log("posix_memalign() failed: %s", pa_cstrerror(r
));
122 m
->ptr
= pa_xmalloc(m
->size
);
125 m
->do_unlink
= FALSE
;
129 struct shm_marker
*marker
;
131 pa_random(&m
->id
, sizeof(m
->id
));
132 segment_name(fn
, sizeof(fn
), m
->id
);
134 if ((fd
= shm_open(fn
, O_RDWR
|O_CREAT
|O_EXCL
, mode
& 0444)) < 0) {
135 pa_log("shm_open() failed: %s", pa_cstrerror(errno
));
139 m
->size
= size
+ PA_ALIGN(sizeof(struct shm_marker
));
141 if (ftruncate(fd
, m
->size
) < 0) {
142 pa_log("ftruncate() failed: %s", pa_cstrerror(errno
));
146 if ((m
->ptr
= mmap(NULL
, m
->size
, PROT_READ
|PROT_WRITE
, MAP_SHARED
, fd
, 0)) == MAP_FAILED
) {
147 pa_log("mmap() failed: %s", pa_cstrerror(errno
));
151 /* We store our PID at the end of the shm block, so that we
152 * can check for dead shm segments later */
153 marker
= (struct shm_marker
*) ((uint8_t*) m
->ptr
+ m
->size
- PA_ALIGN(sizeof(struct shm_marker
)));
154 pa_atomic_store(&marker
->pid
, (int) getpid());
155 pa_atomic_store(&marker
->marker
, SHM_MARKER
);
157 pa_assert_se(close(fd
) == 0);
180 void pa_shm_free(pa_shm
*m
) {
183 pa_assert(m
->size
> 0);
186 pa_assert(m
->ptr
!= MAP_FAILED
);
191 if (munmap(m
->ptr
, m
->size
) < 0)
192 pa_log("munmap() failed: %s", pa_cstrerror(errno
));
193 #elif defined(HAVE_POSIX_MEMALIGN)
200 if (munmap(m
->ptr
, m
->size
) < 0)
201 pa_log("munmap() failed: %s", pa_cstrerror(errno
));
206 segment_name(fn
, sizeof(fn
), m
->id
);
208 if (shm_unlink(fn
) < 0)
209 pa_log(" shm_unlink(%s) failed: %s", fn
, pa_cstrerror(errno
));
212 /* We shouldn't be here without shm support */
213 pa_assert_not_reached();
217 memset(m
, 0, sizeof(*m
));
220 void pa_shm_punch(pa_shm
*m
, size_t offset
, size_t size
) {
226 pa_assert(m
->size
> 0);
227 pa_assert(offset
+size
<= m
->size
);
230 pa_assert(m
->ptr
!= MAP_FAILED
);
233 /* You're welcome to implement this as NOOP on systems that don't
236 /* Align this to multiples of the page size */
237 ptr
= (uint8_t*) m
->ptr
+ offset
;
238 o
= (uint8_t*) ptr
- (uint8_t*) PA_PAGE_ALIGN_PTR(ptr
);
242 ptr
= (uint8_t*) ptr
+ (ps
- o
);
247 if (madvise(ptr
, size
, MADV_REMOVE
) >= 0)
252 if (madvise(ptr
, size
, MADV_FREE
) >= 0)
257 pa_assert_se(madvise(ptr
, size
, MADV_DONTNEED
) == 0);
258 #elif defined(POSIX_MADV_DONTNEED)
259 pa_assert_se(posix_madvise(ptr
, size
, POSIX_MADV_DONTNEED
) == 0);
265 int pa_shm_attach_ro(pa_shm
*m
, unsigned id
) {
272 segment_name(fn
, sizeof(fn
), m
->id
= id
);
274 if ((fd
= shm_open(fn
, O_RDONLY
, 0)) < 0) {
276 pa_log("shm_open() failed: %s", pa_cstrerror(errno
));
280 if (fstat(fd
, &st
) < 0) {
281 pa_log("fstat() failed: %s", pa_cstrerror(errno
));
285 if (st
.st_size
<= 0 ||
286 st
.st_size
> (off_t
) (MAX_SHM_SIZE
+PA_ALIGN(sizeof(struct shm_marker
))) ||
287 PA_ALIGN((size_t) st
.st_size
) != (size_t) st
.st_size
) {
288 pa_log("Invalid shared memory segment size");
292 m
->size
= st
.st_size
;
294 if ((m
->ptr
= mmap(NULL
, m
->size
, PROT_READ
, MAP_SHARED
, fd
, 0)) == MAP_FAILED
) {
295 pa_log("mmap() failed: %s", pa_cstrerror(errno
));
302 pa_assert_se(pa_close(fd
) == 0);
313 #else /* HAVE_SHM_OPEN */
315 int pa_shm_attach_ro(pa_shm
*m
, unsigned id
) {
319 #endif /* HAVE_SHM_OPEN */
321 int pa_shm_cleanup(void) {
328 if (!(d
= opendir(SHM_PATH
))) {
329 pa_log_warn("Failed to read "SHM_PATH
": %s", pa_cstrerror(errno
));
333 while ((de
= readdir(d
))) {
338 struct shm_marker
*m
;
340 if (strncmp(de
->d_name
, "pulse-shm-", 10))
343 if (pa_atou(de
->d_name
+ 10, &id
) < 0)
346 if (pa_shm_attach_ro(&seg
, id
) < 0)
349 if (seg
.size
< PA_ALIGN(sizeof(struct shm_marker
))) {
354 m
= (struct shm_marker
*) ((uint8_t*) seg
.ptr
+ seg
.size
- PA_ALIGN(sizeof(struct shm_marker
)));
356 if (pa_atomic_load(&m
->marker
) != SHM_MARKER
) {
361 if (!(pid
= (pid_t
) pa_atomic_load(&m
->pid
))) {
366 if (kill(pid
, 0) == 0 || errno
!= ESRCH
) {
373 /* Ok, the owner of this shms segment is dead, so, let's remove the segment */
374 segment_name(fn
, sizeof(fn
), id
);
376 if (shm_unlink(fn
) < 0 && errno
!= EACCES
&& errno
!= ENOENT
)
377 pa_log_warn("Failed to remove SHM segment %s: %s\n", fn
, pa_cstrerror(errno
));
381 #endif /* SHM_PATH */
382 #endif /* HAVE_SHM_OPEN */