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/"
75 #define SHM_MARKER ((int) 0xbeefcafe)
77 /* We now put this SHM marker at the end of each segment. It's
78 * optional, to not require a reboot when upgrading, though. Note that
79 * on multiarch systems 32bit and 64bit processes might access this
80 * region simultaneously. The header fields need to be independant
81 * from the process' word with */
83 pa_atomic_t marker
; /* 0xbeefcafe */
91 #define SHM_MARKER_SIZE PA_ALIGN(sizeof(struct shm_marker))
93 static char *segment_name(char *fn
, size_t l
, unsigned id
) {
94 pa_snprintf(fn
, l
, "/pulse-shm-%u", id
);
98 int pa_shm_create_rw(pa_shm
*m
, size_t size
, pa_bool_t shared
, mode_t mode
) {
104 pa_assert(size
<= MAX_SHM_SIZE
);
105 pa_assert(mode
>= 0600);
107 /* Each time we create a new SHM area, let's first drop all stale
111 /* Round up to make it page aligned */
112 size
= PA_PAGE_ALIGN(size
);
119 if ((m
->ptr
= mmap(NULL
, m
->size
, PROT_READ
|PROT_WRITE
, MAP_ANONYMOUS
|MAP_PRIVATE
, -1, (off_t
) 0)) == MAP_FAILED
) {
120 pa_log("mmap() failed: %s", pa_cstrerror(errno
));
123 #elif defined(HAVE_POSIX_MEMALIGN)
127 if ((r
= posix_memalign(&m
->ptr
, PA_PAGE_SIZE
, size
)) < 0) {
128 pa_log("posix_memalign() failed: %s", pa_cstrerror(r
));
133 m
->ptr
= pa_xmalloc(m
->size
);
136 m
->do_unlink
= FALSE
;
140 struct shm_marker
*marker
;
142 pa_random(&m
->id
, sizeof(m
->id
));
143 segment_name(fn
, sizeof(fn
), m
->id
);
145 if ((fd
= shm_open(fn
, O_RDWR
|O_CREAT
|O_EXCL
, mode
& 0444)) < 0) {
146 pa_log("shm_open() failed: %s", pa_cstrerror(errno
));
150 m
->size
= size
+ SHM_MARKER_SIZE
;
152 if (ftruncate(fd
, (off_t
) m
->size
) < 0) {
153 pa_log("ftruncate() failed: %s", pa_cstrerror(errno
));
157 #ifndef MAP_NORESERVE
158 #define MAP_NORESERVE 0
161 if ((m
->ptr
= mmap(NULL
, PA_PAGE_ALIGN(m
->size
), PROT_READ
|PROT_WRITE
, MAP_SHARED
|MAP_NORESERVE
, fd
, (off_t
) 0)) == MAP_FAILED
) {
162 pa_log("mmap() failed: %s", pa_cstrerror(errno
));
166 /* We store our PID at the end of the shm block, so that we
167 * can check for dead shm segments later */
168 marker
= (struct shm_marker
*) ((uint8_t*) m
->ptr
+ m
->size
- SHM_MARKER_SIZE
);
169 pa_atomic_store(&marker
->pid
, (int) getpid());
170 pa_atomic_store(&marker
->marker
, SHM_MARKER
);
172 pa_assert_se(pa_close(fd
) == 0);
195 void pa_shm_free(pa_shm
*m
) {
198 pa_assert(m
->size
> 0);
201 pa_assert(m
->ptr
!= MAP_FAILED
);
206 if (munmap(m
->ptr
, m
->size
) < 0)
207 pa_log("munmap() failed: %s", pa_cstrerror(errno
));
208 #elif defined(HAVE_POSIX_MEMALIGN)
215 if (munmap(m
->ptr
, PA_PAGE_ALIGN(m
->size
)) < 0)
216 pa_log("munmap() failed: %s", pa_cstrerror(errno
));
221 segment_name(fn
, sizeof(fn
), m
->id
);
223 if (shm_unlink(fn
) < 0)
224 pa_log(" shm_unlink(%s) failed: %s", fn
, pa_cstrerror(errno
));
227 /* We shouldn't be here without shm support */
228 pa_assert_not_reached();
235 void pa_shm_punch(pa_shm
*m
, size_t offset
, size_t size
) {
241 pa_assert(m
->size
> 0);
242 pa_assert(offset
+size
<= m
->size
);
245 pa_assert(m
->ptr
!= MAP_FAILED
);
248 /* You're welcome to implement this as NOOP on systems that don't
251 /* Align the pointer up to multiples of the page size */
252 ptr
= (uint8_t*) m
->ptr
+ offset
;
253 o
= (size_t) ((uint8_t*) ptr
- (uint8_t*) PA_PAGE_ALIGN_PTR(ptr
));
256 size_t delta
= PA_PAGE_SIZE
- o
;
257 ptr
= (uint8_t*) ptr
+ delta
;
261 /* Align the size down to multiples of page size */
262 size
= (size
/ PA_PAGE_SIZE
) * PA_PAGE_SIZE
;
265 if (madvise(ptr
, size
, MADV_REMOVE
) >= 0)
270 if (madvise(ptr
, size
, MADV_FREE
) >= 0)
275 madvise(ptr
, size
, MADV_DONTNEED
);
276 #elif defined(POSIX_MADV_DONTNEED)
277 posix_madvise(ptr
, size
, POSIX_MADV_DONTNEED
);
283 int pa_shm_attach_ro(pa_shm
*m
, unsigned id
) {
290 segment_name(fn
, sizeof(fn
), m
->id
= id
);
292 if ((fd
= shm_open(fn
, O_RDONLY
, 0)) < 0) {
293 if (errno
!= EACCES
&& errno
!= ENOENT
)
294 pa_log("shm_open() failed: %s", pa_cstrerror(errno
));
298 if (fstat(fd
, &st
) < 0) {
299 pa_log("fstat() failed: %s", pa_cstrerror(errno
));
303 if (st
.st_size
<= 0 ||
304 st
.st_size
> (off_t
) (MAX_SHM_SIZE
+SHM_MARKER_SIZE
) ||
305 PA_ALIGN((size_t) st
.st_size
) != (size_t) st
.st_size
) {
306 pa_log("Invalid shared memory segment size");
310 m
->size
= (size_t) st
.st_size
;
312 if ((m
->ptr
= mmap(NULL
, PA_PAGE_ALIGN(m
->size
), PROT_READ
, MAP_SHARED
, fd
, (off_t
) 0)) == MAP_FAILED
) {
313 pa_log("mmap() failed: %s", pa_cstrerror(errno
));
317 m
->do_unlink
= FALSE
;
320 pa_assert_se(pa_close(fd
) == 0);
331 #else /* HAVE_SHM_OPEN */
333 int pa_shm_attach_ro(pa_shm
*m
, unsigned id
) {
337 #endif /* HAVE_SHM_OPEN */
339 int pa_shm_cleanup(void) {
346 if (!(d
= opendir(SHM_PATH
))) {
347 pa_log_warn("Failed to read "SHM_PATH
": %s", pa_cstrerror(errno
));
351 while ((de
= readdir(d
))) {
356 struct shm_marker
*m
;
358 if (strncmp(de
->d_name
, "pulse-shm-", 10))
361 if (pa_atou(de
->d_name
+ 10, &id
) < 0)
364 if (pa_shm_attach_ro(&seg
, id
) < 0)
367 if (seg
.size
< SHM_MARKER_SIZE
) {
372 m
= (struct shm_marker
*) ((uint8_t*) seg
.ptr
+ seg
.size
- SHM_MARKER_SIZE
);
374 if (pa_atomic_load(&m
->marker
) != SHM_MARKER
) {
379 if (!(pid
= (pid_t
) pa_atomic_load(&m
->pid
))) {
384 if (kill(pid
, 0) == 0 || errno
!= ESRCH
) {
391 /* Ok, the owner of this shms segment is dead, so, let's remove the segment */
392 segment_name(fn
, sizeof(fn
), id
);
394 if (shm_unlink(fn
) < 0 && errno
!= EACCES
&& errno
!= ENOENT
)
395 pa_log_warn("Failed to remove SHM segment %s: %s\n", fn
, pa_cstrerror(errno
));
399 #endif /* SHM_PATH */
400 #endif /* HAVE_SHM_OPEN */