diff options
Diffstat (limited to 'sound/core')
-rw-r--r-- | sound/core/pcm.c | 4 | ||||
-rw-r--r-- | sound/core/pcm_lib.c | 140 |
2 files changed, 121 insertions, 23 deletions
diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 6884ae031f6..df57a0e30bf 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c | |||
@@ -921,6 +921,10 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream) | |||
921 | snd_free_pages((void*)runtime->control, | 921 | snd_free_pages((void*)runtime->control, |
922 | PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control))); | 922 | PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control))); |
923 | kfree(runtime->hw_constraints.rules); | 923 | kfree(runtime->hw_constraints.rules); |
924 | #ifdef CONFIG_SND_PCM_XRUN_DEBUG | ||
925 | if (runtime->hwptr_log) | ||
926 | kfree(runtime->hwptr_log); | ||
927 | #endif | ||
924 | kfree(runtime); | 928 | kfree(runtime); |
925 | substream->runtime = NULL; | 929 | substream->runtime = NULL; |
926 | put_pid(substream->pid); | 930 | put_pid(substream->pid); |
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 9621236b2fe..1990afb8a73 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c | |||
@@ -126,34 +126,34 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram | |||
126 | } | 126 | } |
127 | } | 127 | } |
128 | 128 | ||
129 | static void pcm_debug_name(struct snd_pcm_substream *substream, | ||
130 | char *name, size_t len) | ||
131 | { | ||
132 | snprintf(name, len, "pcmC%dD%d%c:%d", | ||
133 | substream->pcm->card->number, | ||
134 | substream->pcm->device, | ||
135 | substream->stream ? 'c' : 'p', | ||
136 | substream->number); | ||
137 | } | ||
138 | |||
129 | #define XRUN_DEBUG_BASIC (1<<0) | 139 | #define XRUN_DEBUG_BASIC (1<<0) |
130 | #define XRUN_DEBUG_STACK (1<<1) /* dump also stack */ | 140 | #define XRUN_DEBUG_STACK (1<<1) /* dump also stack */ |
131 | #define XRUN_DEBUG_JIFFIESCHECK (1<<2) /* do jiffies check */ | 141 | #define XRUN_DEBUG_JIFFIESCHECK (1<<2) /* do jiffies check */ |
132 | #define XRUN_DEBUG_PERIODUPDATE (1<<3) /* full period update info */ | 142 | #define XRUN_DEBUG_PERIODUPDATE (1<<3) /* full period update info */ |
133 | #define XRUN_DEBUG_HWPTRUPDATE (1<<4) /* full hwptr update info */ | 143 | #define XRUN_DEBUG_HWPTRUPDATE (1<<4) /* full hwptr update info */ |
144 | #define XRUN_DEBUG_LOG (1<<5) /* show last 10 positions on err */ | ||
145 | #define XRUN_DEBUG_LOGONCE (1<<6) /* do above only once */ | ||
134 | 146 | ||
135 | #ifdef CONFIG_SND_PCM_XRUN_DEBUG | 147 | #ifdef CONFIG_SND_PCM_XRUN_DEBUG |
148 | |||
136 | #define xrun_debug(substream, mask) \ | 149 | #define xrun_debug(substream, mask) \ |
137 | ((substream)->pstr->xrun_debug & (mask)) | 150 | ((substream)->pstr->xrun_debug & (mask)) |
138 | #else | ||
139 | #define xrun_debug(substream, mask) 0 | ||
140 | #endif | ||
141 | 151 | ||
142 | #define dump_stack_on_xrun(substream) do { \ | 152 | #define dump_stack_on_xrun(substream) do { \ |
143 | if (xrun_debug(substream, XRUN_DEBUG_STACK)) \ | 153 | if (xrun_debug(substream, XRUN_DEBUG_STACK)) \ |
144 | dump_stack(); \ | 154 | dump_stack(); \ |
145 | } while (0) | 155 | } while (0) |
146 | 156 | ||
147 | static void pcm_debug_name(struct snd_pcm_substream *substream, | ||
148 | char *name, size_t len) | ||
149 | { | ||
150 | snprintf(name, len, "pcmC%dD%d%c:%d", | ||
151 | substream->pcm->card->number, | ||
152 | substream->pcm->device, | ||
153 | substream->stream ? 'c' : 'p', | ||
154 | substream->number); | ||
155 | } | ||
156 | |||
157 | static void xrun(struct snd_pcm_substream *substream) | 157 | static void xrun(struct snd_pcm_substream *substream) |
158 | { | 158 | { |
159 | struct snd_pcm_runtime *runtime = substream->runtime; | 159 | struct snd_pcm_runtime *runtime = substream->runtime; |
@@ -169,6 +169,102 @@ static void xrun(struct snd_pcm_substream *substream) | |||
169 | } | 169 | } |
170 | } | 170 | } |
171 | 171 | ||
172 | #define hw_ptr_error(substream, fmt, args...) \ | ||
173 | do { \ | ||
174 | if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { \ | ||
175 | if (printk_ratelimit()) { \ | ||
176 | snd_printd("PCM: " fmt, ##args); \ | ||
177 | } \ | ||
178 | dump_stack_on_xrun(substream); \ | ||
179 | } \ | ||
180 | } while (0) | ||
181 | |||
182 | #define XRUN_LOG_CNT 10 | ||
183 | |||
184 | struct hwptr_log_entry { | ||
185 | unsigned long jiffies; | ||
186 | snd_pcm_uframes_t pos; | ||
187 | snd_pcm_uframes_t period_size; | ||
188 | snd_pcm_uframes_t buffer_size; | ||
189 | snd_pcm_uframes_t old_hw_ptr; | ||
190 | snd_pcm_uframes_t hw_ptr_base; | ||
191 | snd_pcm_uframes_t hw_ptr_interrupt; | ||
192 | }; | ||
193 | |||
194 | struct snd_pcm_hwptr_log { | ||
195 | unsigned int idx; | ||
196 | unsigned int hit: 1; | ||
197 | struct hwptr_log_entry entries[XRUN_LOG_CNT]; | ||
198 | }; | ||
199 | |||
200 | static void xrun_log(struct snd_pcm_substream *substream, | ||
201 | snd_pcm_uframes_t pos) | ||
202 | { | ||
203 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
204 | struct snd_pcm_hwptr_log *log = runtime->hwptr_log; | ||
205 | struct hwptr_log_entry *entry; | ||
206 | |||
207 | if (log == NULL) { | ||
208 | log = kzalloc(sizeof(*log), GFP_ATOMIC); | ||
209 | if (log == NULL) | ||
210 | return; | ||
211 | runtime->hwptr_log = log; | ||
212 | } else { | ||
213 | if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit) | ||
214 | return; | ||
215 | } | ||
216 | entry = &log->entries[log->idx]; | ||
217 | entry->jiffies = jiffies; | ||
218 | entry->pos = pos; | ||
219 | entry->period_size = runtime->period_size; | ||
220 | entry->buffer_size = runtime->buffer_size;; | ||
221 | entry->old_hw_ptr = runtime->status->hw_ptr; | ||
222 | entry->hw_ptr_base = runtime->hw_ptr_base; | ||
223 | entry->hw_ptr_interrupt = runtime->hw_ptr_interrupt;; | ||
224 | log->idx = (log->idx + 1) % XRUN_LOG_CNT; | ||
225 | } | ||
226 | |||
227 | static void xrun_log_show(struct snd_pcm_substream *substream) | ||
228 | { | ||
229 | struct snd_pcm_hwptr_log *log = substream->runtime->hwptr_log; | ||
230 | struct hwptr_log_entry *entry; | ||
231 | char name[16]; | ||
232 | unsigned int idx; | ||
233 | int cnt; | ||
234 | |||
235 | if (log == NULL) | ||
236 | return; | ||
237 | if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit) | ||
238 | return; | ||
239 | pcm_debug_name(substream, name, sizeof(name)); | ||
240 | for (cnt = 0, idx = log->idx; cnt < XRUN_LOG_CNT; cnt++) { | ||
241 | entry = &log->entries[idx]; | ||
242 | if (entry->period_size == 0) | ||
243 | break; | ||
244 | snd_printd("hwptr log: %s: j=%lu, pos=0x%lx/0x%lx/0x%lx, " | ||
245 | "hwptr=0x%lx, hw_base=0x%lx, hw_intr=0x%lx\n", | ||
246 | name, entry->jiffies, (unsigned long)entry->pos, | ||
247 | (unsigned long)entry->period_size, | ||
248 | (unsigned long)entry->buffer_size, | ||
249 | (unsigned long)entry->old_hw_ptr, | ||
250 | (unsigned long)entry->hw_ptr_base, | ||
251 | (unsigned long)entry->hw_ptr_interrupt); | ||
252 | idx++; | ||
253 | idx %= XRUN_LOG_CNT; | ||
254 | } | ||
255 | log->hit = 1; | ||
256 | } | ||
257 | |||
258 | #else /* ! CONFIG_SND_PCM_XRUN_DEBUG */ | ||
259 | |||
260 | #define xrun_debug(substream, mask) 0 | ||
261 | #define xrun(substream) do { } while (0) | ||
262 | #define hw_ptr_error(substream, fmt, args...) do { } while (0) | ||
263 | #define xrun_log(substream, pos) do { } while (0) | ||
264 | #define xrun_log_show(substream) do { } while (0) | ||
265 | |||
266 | #endif | ||
267 | |||
172 | static snd_pcm_uframes_t | 268 | static snd_pcm_uframes_t |
173 | snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream, | 269 | snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream, |
174 | struct snd_pcm_runtime *runtime) | 270 | struct snd_pcm_runtime *runtime) |
@@ -182,6 +278,7 @@ snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream, | |||
182 | if (printk_ratelimit()) { | 278 | if (printk_ratelimit()) { |
183 | char name[16]; | 279 | char name[16]; |
184 | pcm_debug_name(substream, name, sizeof(name)); | 280 | pcm_debug_name(substream, name, sizeof(name)); |
281 | xrun_log_show(substream); | ||
185 | snd_printd(KERN_ERR "BUG: %s, pos = 0x%lx, " | 282 | snd_printd(KERN_ERR "BUG: %s, pos = 0x%lx, " |
186 | "buffer size = 0x%lx, period size = 0x%lx\n", | 283 | "buffer size = 0x%lx, period size = 0x%lx\n", |
187 | name, pos, runtime->buffer_size, | 284 | name, pos, runtime->buffer_size, |
@@ -190,6 +287,8 @@ snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream, | |||
190 | pos = 0; | 287 | pos = 0; |
191 | } | 288 | } |
192 | pos -= pos % runtime->min_align; | 289 | pos -= pos % runtime->min_align; |
290 | if (xrun_debug(substream, XRUN_DEBUG_LOG)) | ||
291 | xrun_log(substream, pos); | ||
193 | return pos; | 292 | return pos; |
194 | } | 293 | } |
195 | 294 | ||
@@ -220,16 +319,6 @@ static int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream, | |||
220 | return 0; | 319 | return 0; |
221 | } | 320 | } |
222 | 321 | ||
223 | #define hw_ptr_error(substream, fmt, args...) \ | ||
224 | do { \ | ||
225 | if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { \ | ||
226 | if (printk_ratelimit()) { \ | ||
227 | snd_printd("PCM: " fmt, ##args); \ | ||
228 | } \ | ||
229 | dump_stack_on_xrun(substream); \ | ||
230 | } \ | ||
231 | } while (0) | ||
232 | |||
233 | static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) | 322 | static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) |
234 | { | 323 | { |
235 | struct snd_pcm_runtime *runtime = substream->runtime; | 324 | struct snd_pcm_runtime *runtime = substream->runtime; |
@@ -270,6 +359,7 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) | |||
270 | if (runtime->periods == 1 || new_hw_ptr < old_hw_ptr) | 359 | if (runtime->periods == 1 || new_hw_ptr < old_hw_ptr) |
271 | delta += runtime->buffer_size; | 360 | delta += runtime->buffer_size; |
272 | if (delta < 0) { | 361 | if (delta < 0) { |
362 | xrun_log_show(substream); | ||
273 | hw_ptr_error(substream, | 363 | hw_ptr_error(substream, |
274 | "Unexpected hw_pointer value " | 364 | "Unexpected hw_pointer value " |
275 | "(stream=%i, pos=%ld, intr_ptr=%ld)\n", | 365 | "(stream=%i, pos=%ld, intr_ptr=%ld)\n", |
@@ -315,6 +405,7 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) | |||
315 | delta = jdelta / | 405 | delta = jdelta / |
316 | (((runtime->period_size * HZ) / runtime->rate) | 406 | (((runtime->period_size * HZ) / runtime->rate) |
317 | + HZ/100); | 407 | + HZ/100); |
408 | xrun_log_show(substream); | ||
318 | hw_ptr_error(substream, | 409 | hw_ptr_error(substream, |
319 | "hw_ptr skipping! [Q] " | 410 | "hw_ptr skipping! [Q] " |
320 | "(pos=%ld, delta=%ld, period=%ld, " | 411 | "(pos=%ld, delta=%ld, period=%ld, " |
@@ -334,6 +425,7 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) | |||
334 | } | 425 | } |
335 | no_jiffies_check: | 426 | no_jiffies_check: |
336 | if (delta > runtime->period_size + runtime->period_size / 2) { | 427 | if (delta > runtime->period_size + runtime->period_size / 2) { |
428 | xrun_log_show(substream); | ||
337 | hw_ptr_error(substream, | 429 | hw_ptr_error(substream, |
338 | "Lost interrupts? " | 430 | "Lost interrupts? " |
339 | "(stream=%i, delta=%ld, intr_ptr=%ld)\n", | 431 | "(stream=%i, delta=%ld, intr_ptr=%ld)\n", |
@@ -397,6 +489,7 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) | |||
397 | if (delta < 0) { | 489 | if (delta < 0) { |
398 | delta += runtime->buffer_size; | 490 | delta += runtime->buffer_size; |
399 | if (delta < 0) { | 491 | if (delta < 0) { |
492 | xrun_log_show(substream); | ||
400 | hw_ptr_error(substream, | 493 | hw_ptr_error(substream, |
401 | "Unexpected hw_pointer value [2] " | 494 | "Unexpected hw_pointer value [2] " |
402 | "(stream=%i, pos=%ld, old_ptr=%ld, jdelta=%li)\n", | 495 | "(stream=%i, pos=%ld, old_ptr=%ld, jdelta=%li)\n", |
@@ -416,6 +509,7 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) | |||
416 | goto no_jiffies_check; | 509 | goto no_jiffies_check; |
417 | delta -= runtime->delay; | 510 | delta -= runtime->delay; |
418 | if (((delta * HZ) / runtime->rate) > jdelta + HZ/100) { | 511 | if (((delta * HZ) / runtime->rate) > jdelta + HZ/100) { |
512 | xrun_log_show(substream); | ||
419 | hw_ptr_error(substream, | 513 | hw_ptr_error(substream, |
420 | "hw_ptr skipping! " | 514 | "hw_ptr skipping! " |
421 | "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu)\n", | 515 | "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu)\n", |