daemon: add nice value in service file to improve performance
[platform/upstream/pulseaudio.git] / src / pulsecore / memtrap.c
1 /***
2   This file is part of PulseAudio.
3
4   Copyright 2009 Lennart Poettering
5
6   PulseAudio is free software; you can redistribute it and/or modify
7   it under the terms of the GNU Lesser General Public License as
8   published by the Free Software Foundation; either version 2.1 of the
9   License, or (at your option) any later version.
10
11   PulseAudio is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   General Public License for more details.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <signal.h>
25
26 #ifdef HAVE_SYS_MMAN_H
27 #include <sys/mman.h>
28 #endif
29
30 /* This is deprecated on glibc but is still used by FreeBSD */
31 #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
32 # define MAP_ANONYMOUS MAP_ANON
33 #endif
34
35 #include <pulse/xmalloc.h>
36
37 #include <pulsecore/core-util.h>
38 #include <pulsecore/aupdate.h>
39 #include <pulsecore/atomic.h>
40 #include <pulsecore/once.h>
41 #include <pulsecore/mutex.h>
42
43 #include "memtrap.h"
44
45 struct pa_memtrap {
46     void *start;
47     size_t size;
48     pa_atomic_t bad;
49     pa_memtrap *next[2], *prev[2];
50 };
51
52 static pa_memtrap *memtraps[2] = { NULL, NULL };
53 static pa_aupdate *aupdate;
54 static pa_static_mutex mutex = PA_STATIC_MUTEX_INIT; /* only required to serialize access to the write side */
55
56 static void allocate_aupdate(void) {
57     PA_ONCE_BEGIN {
58         aupdate = pa_aupdate_new();
59     } PA_ONCE_END;
60 }
61
62 bool pa_memtrap_is_good(pa_memtrap *m) {
63     pa_assert(m);
64
65     return !pa_atomic_load(&m->bad);
66 }
67
68 #ifdef HAVE_SIGACTION
69 static void sigsafe_error(const char *s) {
70     size_t ret PA_GCC_UNUSED;
71     ret = write(STDERR_FILENO, s, strlen(s));
72 }
73
74 static void signal_handler(int sig, siginfo_t* si, void *data) {
75     unsigned j;
76     pa_memtrap *m;
77     void *r;
78
79     j = pa_aupdate_read_begin(aupdate);
80
81     for (m = memtraps[j]; m; m = m->next[j])
82         if (si->si_addr >= m->start &&
83             (uint8_t*) si->si_addr < (uint8_t*) m->start + m->size)
84             break;
85
86     if (!m)
87         goto fail;
88
89     pa_atomic_store(&m->bad, 1);
90
91     /* Remap anonymous memory into the bad segment */
92     if ((r = mmap(m->start, m->size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_FIXED|MAP_PRIVATE, -1, 0)) == MAP_FAILED) {
93         sigsafe_error("mmap() failed.\n");
94         goto fail;
95     }
96
97     pa_assert(r == m->start);
98
99     pa_aupdate_read_end(aupdate);
100     return;
101
102 fail:
103     pa_aupdate_read_end(aupdate);
104
105     sigsafe_error("Failed to handle SIGBUS.\n");
106     abort();
107 }
108 #endif
109
110 static void memtrap_link(pa_memtrap *m, unsigned j) {
111     pa_assert(m);
112
113     m->prev[j] = NULL;
114
115     if ((m->next[j] = memtraps[j]))
116         m->next[j]->prev[j] = m;
117
118     memtraps[j] = m;
119 }
120
121 static void memtrap_unlink(pa_memtrap *m, unsigned j) {
122     pa_assert(m);
123
124     if (m->next[j])
125         m->next[j]->prev[j] = m->prev[j];
126
127     if (m->prev[j])
128         m->prev[j]->next[j] = m->next[j];
129     else
130         memtraps[j] = m->next[j];
131 }
132
133 pa_memtrap* pa_memtrap_add(const void *start, size_t size) {
134     pa_memtrap *m = NULL;
135     unsigned j;
136     pa_mutex *mx;
137
138     pa_assert(start);
139     pa_assert(size > 0);
140
141     start = PA_PAGE_ALIGN_PTR(start);
142     size = PA_PAGE_ALIGN(size);
143
144     m = pa_xnew(pa_memtrap, 1);
145     m->start = (void*) start;
146     m->size = size;
147     pa_atomic_store(&m->bad, 0);
148
149     allocate_aupdate();
150
151     mx = pa_static_mutex_get(&mutex, false, true);
152     pa_mutex_lock(mx);
153
154     j = pa_aupdate_write_begin(aupdate);
155     memtrap_link(m, j);
156     j = pa_aupdate_write_swap(aupdate);
157     memtrap_link(m, j);
158     pa_aupdate_write_end(aupdate);
159
160     pa_mutex_unlock(mx);
161
162     return m;
163 }
164
165 void pa_memtrap_remove(pa_memtrap *m) {
166     unsigned j;
167     pa_mutex *mx;
168
169     pa_assert(m);
170
171     allocate_aupdate();
172
173     mx = pa_static_mutex_get(&mutex, false, true);
174     pa_mutex_lock(mx);
175
176     j = pa_aupdate_write_begin(aupdate);
177     memtrap_unlink(m, j);
178     j = pa_aupdate_write_swap(aupdate);
179     memtrap_unlink(m, j);
180     pa_aupdate_write_end(aupdate);
181
182     pa_mutex_unlock(mx);
183
184     pa_xfree(m);
185 }
186
187 pa_memtrap *pa_memtrap_update(pa_memtrap *m, const void *start, size_t size) {
188     unsigned j;
189     pa_mutex *mx;
190
191     pa_assert(m);
192
193     pa_assert(start);
194     pa_assert(size > 0);
195
196     start = PA_PAGE_ALIGN_PTR(start);
197     size = PA_PAGE_ALIGN(size);
198
199     allocate_aupdate();
200
201     mx = pa_static_mutex_get(&mutex, false, true);
202     pa_mutex_lock(mx);
203
204     j = pa_aupdate_write_begin(aupdate);
205
206     if (m->start == start && m->size == size)
207         goto unlock;
208
209     memtrap_unlink(m, j);
210     pa_aupdate_write_swap(aupdate);
211
212     m->start = (void*) start;
213     m->size = size;
214     pa_atomic_store(&m->bad, 0);
215
216     pa_assert_se(pa_aupdate_write_swap(aupdate) == j);
217     memtrap_link(m, j);
218
219 unlock:
220     pa_aupdate_write_end(aupdate);
221
222     pa_mutex_unlock(mx);
223
224     return m;
225 }
226
227 void pa_memtrap_install(void) {
228 #ifdef HAVE_SIGACTION
229     struct sigaction sa;
230
231     allocate_aupdate();
232
233     memset(&sa, 0, sizeof(sa));
234     sa.sa_sigaction = signal_handler;
235     sa.sa_flags = SA_RESTART|SA_SIGINFO;
236
237     pa_assert_se(sigaction(SIGBUS, &sa, NULL) == 0);
238 #ifdef __FreeBSD_kernel__
239     pa_assert_se(sigaction(SIGSEGV, &sa, NULL) == 0);
240 #endif
241 #endif
242 }