]>
code.delx.au - pulseaudio/blob - src/pulsecore/shm.c
4 This file is part of PulseAudio.
6 Copyright 2006 Lennart Poettering
7 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
9 PulseAudio is free software; you can redistribute it and/or modify
10 it under the terms of the GNU Lesser General Public License as
11 published by the Free Software Foundation; either version 2.1 of the
12 License, or (at your option) any later version.
14 PulseAudio is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
19 You should have received a copy of the GNU Lesser General Public
20 License along with PulseAudio; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
36 #include <sys/types.h>
40 #ifdef HAVE_SYS_MMAN_H
44 #include <pulse/xmalloc.h>
46 #include <pulsecore/core-error.h>
47 #include <pulsecore/log.h>
48 #include <pulsecore/random.h>
49 #include <pulsecore/core-util.h>
50 #include <pulsecore/macro.h>
51 #include <pulsecore/atomic.h>
55 #if defined(__linux__) && !defined(MADV_REMOVE)
59 #define MAX_SHM_SIZE (PA_ALIGN(1024*1024*20))
62 /* On Linux we know that the shared memory blocks are files in
63 * /dev/shm. We can use that information to list all blocks and
64 * cleanup unused ones */
65 #define SHM_PATH "/dev/shm/"
70 #define SHM_MARKER ((int) 0xbeefcafe)
72 /* We now put this SHM marker at the end of each segment. It's optional to not require a reboot when upgrading, though */
74 pa_atomic_t marker
; /* 0xbeefcafe */
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
, int 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
);
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 || st
.st_size
> MAX_SHM_SIZE
+PA_ALIGN(sizeof(struct shm_marker
)) || PA_ALIGN(st
.st_size
) != st
.st_size
) {
286 pa_log("Invalid shared memory segment size");
290 m
->size
= st
.st_size
;
292 if ((m
->ptr
= mmap(NULL
, m
->size
, PROT_READ
, MAP_SHARED
, fd
, 0)) == MAP_FAILED
) {
293 pa_log("mmap() failed: %s", pa_cstrerror(errno
));
300 pa_assert_se(pa_close(fd
) == 0);
311 #else /* HAVE_SHM_OPEN */
313 int pa_shm_attach_ro(pa_shm
*m
, unsigned id
) {
317 #endif /* HAVE_SHM_OPEN */
319 int pa_shm_cleanup(void) {
325 if (!(d
= opendir(SHM_PATH
))) {
326 pa_log_warn("Failed to read "SHM_PATH
": %s", pa_cstrerror(errno
));
330 while ((de
= readdir(d
))) {
335 struct shm_marker
*m
;
337 if (strncmp(de
->d_name
, "pulse-shm-", 10))
340 if (pa_atou(de
->d_name
+ 10, &id
) < 0)
343 if (pa_shm_attach_ro(&seg
, id
) < 0)
346 if (seg
.size
< PA_ALIGN(sizeof(struct shm_marker
))) {
351 m
= (struct shm_marker
*) ((uint8_t*) seg
.ptr
+ seg
.size
- PA_ALIGN(sizeof(struct shm_marker
)));
353 if (pa_atomic_load(&m
->marker
) != SHM_MARKER
) {
358 if (!(pid
= (pid_t
) pa_atomic_load(&m
->pid
))) {
363 if (kill(pid
, 0) == 0 || errno
!= ESRCH
) {
370 /* Ok, the owner of this shms segment is dead, so, let's remove the segment */
371 segment_name(fn
, sizeof(fn
), id
);
373 if (shm_unlink(fn
) < 0 && errno
!= EACCES
)
374 pa_log_warn("Failed to remove SHM segment %s: %s\n", fn
, pa_cstrerror(errno
));