aboutsummaryrefslogtreecommitdiffstats
path: root/sound/core
diff options
context:
space:
mode:
authorJaroslav Kysela <perex@perex.cz>2009-12-20 05:47:57 -0500
committerJaroslav Kysela <perex@perex.cz>2010-01-07 09:47:24 -0500
commit4d96eb255c53ab5e39b37fd4d484ea3dc39ab456 (patch)
tree139ce65a63a2a39ee7dedfa3ca16c70bd5a1b2ba /sound/core
parent741b20cfb9109760937f403d18d731bfde31f56f (diff)
ALSA: pcm_lib - add possibility to log last 10 DMA ring buffer positions
In some debug cases, it might be usefull to see previous ring buffer positions to determine position problems from the lowlevel drivers. Signed-off-by: Jaroslav Kysela <perex@perex.cz>
Diffstat (limited to 'sound/core')
-rw-r--r--sound/core/pcm.c4
-rw-r--r--sound/core/pcm_lib.c140
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
129static 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
147static 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
157static void xrun(struct snd_pcm_substream *substream) 157static 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
184struct 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
194struct snd_pcm_hwptr_log {
195 unsigned int idx;
196 unsigned int hit: 1;
197 struct hwptr_log_entry entries[XRUN_LOG_CNT];
198};
199
200static 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
227static 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
172static snd_pcm_uframes_t 268static snd_pcm_uframes_t
173snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream, 269snd_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
233static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) 322static 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",