diff options
author | Takashi Iwai <tiwai@suse.de> | 2014-11-05 05:34:36 -0500 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2014-11-05 05:34:36 -0500 |
commit | c009b7ef9409f957ab8846d362463d05678a969d (patch) | |
tree | d70f30311304508038a15f476172bacc14d2d799 | |
parent | ae366c2049b48b54e5250cb57438bbebd1dbe610 (diff) | |
parent | 2b30d411dbc6eddfb5b4f9afd5a2c57b6f4dd96c (diff) |
Merge branch 'topic/pcm-locking' into for-next
here is a small series of patches for cleaning up / enhancing the
PCM core stuff.
-rw-r--r-- | Documentation/sound/alsa/Procfile.txt | 17 | ||||
-rw-r--r-- | include/sound/pcm.h | 3 | ||||
-rw-r--r-- | sound/core/Makefile | 3 | ||||
-rw-r--r-- | sound/core/pcm.c | 33 | ||||
-rw-r--r-- | sound/core/pcm_lib.c | 148 | ||||
-rw-r--r-- | sound/core/pcm_native.c | 76 | ||||
-rw-r--r-- | sound/core/pcm_trace.h | 110 |
7 files changed, 191 insertions, 199 deletions
diff --git a/Documentation/sound/alsa/Procfile.txt b/Documentation/sound/alsa/Procfile.txt index 7fcd1ad96fcc..7f8a0d325905 100644 --- a/Documentation/sound/alsa/Procfile.txt +++ b/Documentation/sound/alsa/Procfile.txt | |||
@@ -101,10 +101,6 @@ card*/pcm*/xrun_debug | |||
101 | bit 0 = Enable XRUN/jiffies debug messages | 101 | bit 0 = Enable XRUN/jiffies debug messages |
102 | bit 1 = Show stack trace at XRUN / jiffies check | 102 | bit 1 = Show stack trace at XRUN / jiffies check |
103 | bit 2 = Enable additional jiffies check | 103 | bit 2 = Enable additional jiffies check |
104 | bit 3 = Log hwptr update at each period interrupt | ||
105 | bit 4 = Log hwptr update at each snd_pcm_update_hw_ptr() | ||
106 | bit 5 = Show last 10 positions on error | ||
107 | bit 6 = Do above only once | ||
108 | 104 | ||
109 | When the bit 0 is set, the driver will show the messages to | 105 | When the bit 0 is set, the driver will show the messages to |
110 | kernel log when an xrun is detected. The debug message is | 106 | kernel log when an xrun is detected. The debug message is |
@@ -121,15 +117,6 @@ card*/pcm*/xrun_debug | |||
121 | buggy) hardware that doesn't give smooth pointer updates. | 117 | buggy) hardware that doesn't give smooth pointer updates. |
122 | This feature is enabled via the bit 2. | 118 | This feature is enabled via the bit 2. |
123 | 119 | ||
124 | Bits 3 and 4 are for logging the hwptr records. Note that | ||
125 | these will give flood of kernel messages. | ||
126 | |||
127 | When bit 5 is set, the driver logs the last 10 xrun errors and | ||
128 | the proc file shows each jiffies, position, period_size, | ||
129 | buffer_size, old_hw_ptr, and hw_ptr_base values. | ||
130 | |||
131 | When bit 6 is set, the full xrun log is shown only once. | ||
132 | |||
133 | card*/pcm*/sub*/info | 120 | card*/pcm*/sub*/info |
134 | The general information of this PCM sub-stream. | 121 | The general information of this PCM sub-stream. |
135 | 122 | ||
@@ -146,6 +133,10 @@ card*/pcm*/sub*/sw_params | |||
146 | card*/pcm*/sub*/prealloc | 133 | card*/pcm*/sub*/prealloc |
147 | The buffer pre-allocation information. | 134 | The buffer pre-allocation information. |
148 | 135 | ||
136 | card*/pcm*/sub*/xrun_injection | ||
137 | Triggers an XRUN to the running stream when any value is | ||
138 | written to this proc file. Used for fault injection. | ||
139 | This entry is write-only. | ||
149 | 140 | ||
150 | AC97 Codec Information | 141 | AC97 Codec Information |
151 | ---------------------- | 142 | ---------------------- |
diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 29eb09ef2969..0b8daeed0a33 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h | |||
@@ -416,7 +416,10 @@ struct snd_pcm_substream { | |||
416 | struct snd_info_entry *proc_status_entry; | 416 | struct snd_info_entry *proc_status_entry; |
417 | struct snd_info_entry *proc_prealloc_entry; | 417 | struct snd_info_entry *proc_prealloc_entry; |
418 | struct snd_info_entry *proc_prealloc_max_entry; | 418 | struct snd_info_entry *proc_prealloc_max_entry; |
419 | #ifdef CONFIG_SND_PCM_XRUN_DEBUG | ||
420 | struct snd_info_entry *proc_xrun_injection_entry; | ||
419 | #endif | 421 | #endif |
422 | #endif /* CONFIG_SND_VERBOSE_PROCFS */ | ||
420 | /* misc flags */ | 423 | /* misc flags */ |
421 | unsigned int hw_opened: 1; | 424 | unsigned int hw_opened: 1; |
422 | }; | 425 | }; |
diff --git a/sound/core/Makefile b/sound/core/Makefile index 394a38909f6b..4daf2f58261c 100644 --- a/sound/core/Makefile +++ b/sound/core/Makefile | |||
@@ -14,6 +14,9 @@ snd-pcm-y := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \ | |||
14 | pcm_memory.o memalloc.o | 14 | pcm_memory.o memalloc.o |
15 | snd-pcm-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o | 15 | snd-pcm-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o |
16 | 16 | ||
17 | # for trace-points | ||
18 | CFLAGS_pcm_lib.o := -I$(src) | ||
19 | |||
17 | snd-pcm-dmaengine-objs := pcm_dmaengine.o | 20 | snd-pcm-dmaengine-objs := pcm_dmaengine.o |
18 | 21 | ||
19 | snd-rawmidi-objs := rawmidi.o | 22 | snd-rawmidi-objs := rawmidi.o |
diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 31acc3df62cd..8f624b7af0ca 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c | |||
@@ -483,6 +483,19 @@ static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry, | |||
483 | } | 483 | } |
484 | 484 | ||
485 | #ifdef CONFIG_SND_PCM_XRUN_DEBUG | 485 | #ifdef CONFIG_SND_PCM_XRUN_DEBUG |
486 | static void snd_pcm_xrun_injection_write(struct snd_info_entry *entry, | ||
487 | struct snd_info_buffer *buffer) | ||
488 | { | ||
489 | struct snd_pcm_substream *substream = entry->private_data; | ||
490 | struct snd_pcm_runtime *runtime; | ||
491 | |||
492 | snd_pcm_stream_lock_irq(substream); | ||
493 | runtime = substream->runtime; | ||
494 | if (runtime && runtime->status->state == SNDRV_PCM_STATE_RUNNING) | ||
495 | snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); | ||
496 | snd_pcm_stream_unlock_irq(substream); | ||
497 | } | ||
498 | |||
486 | static void snd_pcm_xrun_debug_read(struct snd_info_entry *entry, | 499 | static void snd_pcm_xrun_debug_read(struct snd_info_entry *entry, |
487 | struct snd_info_buffer *buffer) | 500 | struct snd_info_buffer *buffer) |
488 | { | 501 | { |
@@ -614,6 +627,22 @@ static int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) | |||
614 | } | 627 | } |
615 | substream->proc_status_entry = entry; | 628 | substream->proc_status_entry = entry; |
616 | 629 | ||
630 | #ifdef CONFIG_SND_PCM_XRUN_DEBUG | ||
631 | entry = snd_info_create_card_entry(card, "xrun_injection", | ||
632 | substream->proc_root); | ||
633 | if (entry) { | ||
634 | entry->private_data = substream; | ||
635 | entry->c.text.read = NULL; | ||
636 | entry->c.text.write = snd_pcm_xrun_injection_write; | ||
637 | entry->mode = S_IFREG | S_IWUSR; | ||
638 | if (snd_info_register(entry) < 0) { | ||
639 | snd_info_free_entry(entry); | ||
640 | entry = NULL; | ||
641 | } | ||
642 | } | ||
643 | substream->proc_xrun_injection_entry = entry; | ||
644 | #endif /* CONFIG_SND_PCM_XRUN_DEBUG */ | ||
645 | |||
617 | return 0; | 646 | return 0; |
618 | } | 647 | } |
619 | 648 | ||
@@ -627,6 +656,10 @@ static int snd_pcm_substream_proc_done(struct snd_pcm_substream *substream) | |||
627 | substream->proc_sw_params_entry = NULL; | 656 | substream->proc_sw_params_entry = NULL; |
628 | snd_info_free_entry(substream->proc_status_entry); | 657 | snd_info_free_entry(substream->proc_status_entry); |
629 | substream->proc_status_entry = NULL; | 658 | substream->proc_status_entry = NULL; |
659 | #ifdef CONFIG_SND_PCM_XRUN_DEBUG | ||
660 | snd_info_free_entry(substream->proc_xrun_injection_entry); | ||
661 | substream->proc_xrun_injection_entry = NULL; | ||
662 | #endif | ||
630 | snd_info_free_entry(substream->proc_root); | 663 | snd_info_free_entry(substream->proc_root); |
631 | substream->proc_root = NULL; | 664 | substream->proc_root = NULL; |
632 | return 0; | 665 | return 0; |
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index dfc28542a007..ec9e7866177f 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c | |||
@@ -32,6 +32,15 @@ | |||
32 | #include <sound/pcm_params.h> | 32 | #include <sound/pcm_params.h> |
33 | #include <sound/timer.h> | 33 | #include <sound/timer.h> |
34 | 34 | ||
35 | #ifdef CONFIG_SND_PCM_XRUN_DEBUG | ||
36 | #define CREATE_TRACE_POINTS | ||
37 | #include "pcm_trace.h" | ||
38 | #else | ||
39 | #define trace_hwptr(substream, pos, in_interrupt) | ||
40 | #define trace_xrun(substream) | ||
41 | #define trace_hw_ptr_error(substream, reason) | ||
42 | #endif | ||
43 | |||
35 | /* | 44 | /* |
36 | * fill ring buffer with silence | 45 | * fill ring buffer with silence |
37 | * runtime->silence_start: starting pointer to silence area | 46 | * runtime->silence_start: starting pointer to silence area |
@@ -146,10 +155,6 @@ EXPORT_SYMBOL(snd_pcm_debug_name); | |||
146 | #define XRUN_DEBUG_BASIC (1<<0) | 155 | #define XRUN_DEBUG_BASIC (1<<0) |
147 | #define XRUN_DEBUG_STACK (1<<1) /* dump also stack */ | 156 | #define XRUN_DEBUG_STACK (1<<1) /* dump also stack */ |
148 | #define XRUN_DEBUG_JIFFIESCHECK (1<<2) /* do jiffies check */ | 157 | #define XRUN_DEBUG_JIFFIESCHECK (1<<2) /* do jiffies check */ |
149 | #define XRUN_DEBUG_PERIODUPDATE (1<<3) /* full period update info */ | ||
150 | #define XRUN_DEBUG_HWPTRUPDATE (1<<4) /* full hwptr update info */ | ||
151 | #define XRUN_DEBUG_LOG (1<<5) /* show last 10 positions on err */ | ||
152 | #define XRUN_DEBUG_LOGONCE (1<<6) /* do above only once */ | ||
153 | 158 | ||
154 | #ifdef CONFIG_SND_PCM_XRUN_DEBUG | 159 | #ifdef CONFIG_SND_PCM_XRUN_DEBUG |
155 | 160 | ||
@@ -168,6 +173,7 @@ static void xrun(struct snd_pcm_substream *substream) | |||
168 | { | 173 | { |
169 | struct snd_pcm_runtime *runtime = substream->runtime; | 174 | struct snd_pcm_runtime *runtime = substream->runtime; |
170 | 175 | ||
176 | trace_xrun(substream); | ||
171 | if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) | 177 | if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) |
172 | snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); | 178 | snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); |
173 | snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); | 179 | snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); |
@@ -180,97 +186,19 @@ static void xrun(struct snd_pcm_substream *substream) | |||
180 | } | 186 | } |
181 | 187 | ||
182 | #ifdef CONFIG_SND_PCM_XRUN_DEBUG | 188 | #ifdef CONFIG_SND_PCM_XRUN_DEBUG |
183 | #define hw_ptr_error(substream, fmt, args...) \ | 189 | #define hw_ptr_error(substream, in_interrupt, reason, fmt, args...) \ |
184 | do { \ | 190 | do { \ |
191 | trace_hw_ptr_error(substream, reason); \ | ||
185 | if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { \ | 192 | if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { \ |
186 | xrun_log_show(substream); \ | 193 | pr_err_ratelimited("ALSA: PCM: [%c] " reason ": " fmt, \ |
187 | pr_err_ratelimited("ALSA: PCM: " fmt, ##args); \ | 194 | (in_interrupt) ? 'Q' : 'P', ##args); \ |
188 | dump_stack_on_xrun(substream); \ | 195 | dump_stack_on_xrun(substream); \ |
189 | } \ | 196 | } \ |
190 | } while (0) | 197 | } while (0) |
191 | 198 | ||
192 | #define XRUN_LOG_CNT 10 | ||
193 | |||
194 | struct hwptr_log_entry { | ||
195 | unsigned int in_interrupt; | ||
196 | unsigned long jiffies; | ||
197 | snd_pcm_uframes_t pos; | ||
198 | snd_pcm_uframes_t period_size; | ||
199 | snd_pcm_uframes_t buffer_size; | ||
200 | snd_pcm_uframes_t old_hw_ptr; | ||
201 | snd_pcm_uframes_t hw_ptr_base; | ||
202 | }; | ||
203 | |||
204 | struct snd_pcm_hwptr_log { | ||
205 | unsigned int idx; | ||
206 | unsigned int hit: 1; | ||
207 | struct hwptr_log_entry entries[XRUN_LOG_CNT]; | ||
208 | }; | ||
209 | |||
210 | static void xrun_log(struct snd_pcm_substream *substream, | ||
211 | snd_pcm_uframes_t pos, int in_interrupt) | ||
212 | { | ||
213 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
214 | struct snd_pcm_hwptr_log *log = runtime->hwptr_log; | ||
215 | struct hwptr_log_entry *entry; | ||
216 | |||
217 | if (log == NULL) { | ||
218 | log = kzalloc(sizeof(*log), GFP_ATOMIC); | ||
219 | if (log == NULL) | ||
220 | return; | ||
221 | runtime->hwptr_log = log; | ||
222 | } else { | ||
223 | if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit) | ||
224 | return; | ||
225 | } | ||
226 | entry = &log->entries[log->idx]; | ||
227 | entry->in_interrupt = in_interrupt; | ||
228 | entry->jiffies = jiffies; | ||
229 | entry->pos = pos; | ||
230 | entry->period_size = runtime->period_size; | ||
231 | entry->buffer_size = runtime->buffer_size; | ||
232 | entry->old_hw_ptr = runtime->status->hw_ptr; | ||
233 | entry->hw_ptr_base = runtime->hw_ptr_base; | ||
234 | log->idx = (log->idx + 1) % XRUN_LOG_CNT; | ||
235 | } | ||
236 | |||
237 | static void xrun_log_show(struct snd_pcm_substream *substream) | ||
238 | { | ||
239 | struct snd_pcm_hwptr_log *log = substream->runtime->hwptr_log; | ||
240 | struct hwptr_log_entry *entry; | ||
241 | char name[16]; | ||
242 | unsigned int idx; | ||
243 | int cnt; | ||
244 | |||
245 | if (log == NULL) | ||
246 | return; | ||
247 | if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit) | ||
248 | return; | ||
249 | snd_pcm_debug_name(substream, name, sizeof(name)); | ||
250 | for (cnt = 0, idx = log->idx; cnt < XRUN_LOG_CNT; cnt++) { | ||
251 | entry = &log->entries[idx]; | ||
252 | if (entry->period_size == 0) | ||
253 | break; | ||
254 | pr_info("hwptr log: %s: %sj=%lu, pos=%ld/%ld/%ld, " | ||
255 | "hwptr=%ld/%ld\n", | ||
256 | name, entry->in_interrupt ? "[Q] " : "", | ||
257 | entry->jiffies, | ||
258 | (unsigned long)entry->pos, | ||
259 | (unsigned long)entry->period_size, | ||
260 | (unsigned long)entry->buffer_size, | ||
261 | (unsigned long)entry->old_hw_ptr, | ||
262 | (unsigned long)entry->hw_ptr_base); | ||
263 | idx++; | ||
264 | idx %= XRUN_LOG_CNT; | ||
265 | } | ||
266 | log->hit = 1; | ||
267 | } | ||
268 | |||
269 | #else /* ! CONFIG_SND_PCM_XRUN_DEBUG */ | 199 | #else /* ! CONFIG_SND_PCM_XRUN_DEBUG */ |
270 | 200 | ||
271 | #define hw_ptr_error(substream, fmt, args...) do { } while (0) | 201 | #define hw_ptr_error(substream, fmt, args...) do { } while (0) |
272 | #define xrun_log(substream, pos, in_interrupt) do { } while (0) | ||
273 | #define xrun_log_show(substream) do { } while (0) | ||
274 | 202 | ||
275 | #endif | 203 | #endif |
276 | 204 | ||
@@ -343,17 +271,15 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, | |||
343 | if (printk_ratelimit()) { | 271 | if (printk_ratelimit()) { |
344 | char name[16]; | 272 | char name[16]; |
345 | snd_pcm_debug_name(substream, name, sizeof(name)); | 273 | snd_pcm_debug_name(substream, name, sizeof(name)); |
346 | xrun_log_show(substream); | ||
347 | pcm_err(substream->pcm, | 274 | pcm_err(substream->pcm, |
348 | "XRUN: %s, pos = %ld, buffer size = %ld, period size = %ld\n", | 275 | "BUG: %s, pos = %ld, buffer size = %ld, period size = %ld\n", |
349 | name, pos, runtime->buffer_size, | 276 | name, pos, runtime->buffer_size, |
350 | runtime->period_size); | 277 | runtime->period_size); |
351 | } | 278 | } |
352 | pos = 0; | 279 | pos = 0; |
353 | } | 280 | } |
354 | pos -= pos % runtime->min_align; | 281 | pos -= pos % runtime->min_align; |
355 | if (xrun_debug(substream, XRUN_DEBUG_LOG)) | 282 | trace_hwptr(substream, pos, in_interrupt); |
356 | xrun_log(substream, pos, in_interrupt); | ||
357 | hw_base = runtime->hw_ptr_base; | 283 | hw_base = runtime->hw_ptr_base; |
358 | new_hw_ptr = hw_base + pos; | 284 | new_hw_ptr = hw_base + pos; |
359 | if (in_interrupt) { | 285 | if (in_interrupt) { |
@@ -388,22 +314,6 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, | |||
388 | delta = new_hw_ptr - old_hw_ptr; | 314 | delta = new_hw_ptr - old_hw_ptr; |
389 | if (delta < 0) | 315 | if (delta < 0) |
390 | delta += runtime->boundary; | 316 | delta += runtime->boundary; |
391 | if (xrun_debug(substream, in_interrupt ? | ||
392 | XRUN_DEBUG_PERIODUPDATE : XRUN_DEBUG_HWPTRUPDATE)) { | ||
393 | char name[16]; | ||
394 | snd_pcm_debug_name(substream, name, sizeof(name)); | ||
395 | pcm_dbg(substream->pcm, | ||
396 | "%s_update: %s: pos=%u/%u/%u, hwptr=%ld/%ld/%ld/%ld\n", | ||
397 | in_interrupt ? "period" : "hwptr", | ||
398 | name, | ||
399 | (unsigned int)pos, | ||
400 | (unsigned int)runtime->period_size, | ||
401 | (unsigned int)runtime->buffer_size, | ||
402 | (unsigned long)delta, | ||
403 | (unsigned long)old_hw_ptr, | ||
404 | (unsigned long)new_hw_ptr, | ||
405 | (unsigned long)runtime->hw_ptr_base); | ||
406 | } | ||
407 | 317 | ||
408 | if (runtime->no_period_wakeup) { | 318 | if (runtime->no_period_wakeup) { |
409 | snd_pcm_sframes_t xrun_threshold; | 319 | snd_pcm_sframes_t xrun_threshold; |
@@ -431,13 +341,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, | |||
431 | 341 | ||
432 | /* something must be really wrong */ | 342 | /* something must be really wrong */ |
433 | if (delta >= runtime->buffer_size + runtime->period_size) { | 343 | if (delta >= runtime->buffer_size + runtime->period_size) { |
434 | hw_ptr_error(substream, | 344 | hw_ptr_error(substream, in_interrupt, "Unexpected hw_ptr", |
435 | "Unexpected hw_pointer value %s" | 345 | "(stream=%i, pos=%ld, new_hw_ptr=%ld, old_hw_ptr=%ld)\n", |
436 | "(stream=%i, pos=%ld, new_hw_ptr=%ld, " | 346 | substream->stream, (long)pos, |
437 | "old_hw_ptr=%ld)\n", | 347 | (long)new_hw_ptr, (long)old_hw_ptr); |
438 | in_interrupt ? "[Q] " : "[P]", | ||
439 | substream->stream, (long)pos, | ||
440 | (long)new_hw_ptr, (long)old_hw_ptr); | ||
441 | return 0; | 348 | return 0; |
442 | } | 349 | } |
443 | 350 | ||
@@ -474,11 +381,8 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, | |||
474 | delta--; | 381 | delta--; |
475 | } | 382 | } |
476 | /* align hw_base to buffer_size */ | 383 | /* align hw_base to buffer_size */ |
477 | hw_ptr_error(substream, | 384 | hw_ptr_error(substream, in_interrupt, "hw_ptr skipping", |
478 | "hw_ptr skipping! %s" | 385 | "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu/%lu, hw_ptr=%ld/%ld)\n", |
479 | "(pos=%ld, delta=%ld, period=%ld, " | ||
480 | "jdelta=%lu/%lu/%lu, hw_ptr=%ld/%ld)\n", | ||
481 | in_interrupt ? "[Q] " : "", | ||
482 | (long)pos, (long)hdelta, | 386 | (long)pos, (long)hdelta, |
483 | (long)runtime->period_size, jdelta, | 387 | (long)runtime->period_size, jdelta, |
484 | ((hdelta * HZ) / runtime->rate), hw_base, | 388 | ((hdelta * HZ) / runtime->rate), hw_base, |
@@ -490,11 +394,9 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, | |||
490 | } | 394 | } |
491 | no_jiffies_check: | 395 | no_jiffies_check: |
492 | if (delta > runtime->period_size + runtime->period_size / 2) { | 396 | if (delta > runtime->period_size + runtime->period_size / 2) { |
493 | hw_ptr_error(substream, | 397 | hw_ptr_error(substream, in_interrupt, |
494 | "Lost interrupts? %s" | 398 | "Lost interrupts?", |
495 | "(stream=%i, delta=%ld, new_hw_ptr=%ld, " | 399 | "(stream=%i, delta=%ld, new_hw_ptr=%ld, old_hw_ptr=%ld)\n", |
496 | "old_hw_ptr=%ld)\n", | ||
497 | in_interrupt ? "[Q] " : "", | ||
498 | substream->stream, (long)delta, | 400 | substream->stream, (long)delta, |
499 | (long)new_hw_ptr, | 401 | (long)new_hw_ptr, |
500 | (long)old_hw_ptr); | 402 | (long)old_hw_ptr); |
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 4d5795d8b9f7..ca224fa2a33a 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c | |||
@@ -900,14 +900,19 @@ static int snd_pcm_action_single(struct action_ops *ops, | |||
900 | return res; | 900 | return res; |
901 | } | 901 | } |
902 | 902 | ||
903 | /* call in mutex-protected context */ | 903 | /* |
904 | static int snd_pcm_action_mutex(struct action_ops *ops, | 904 | * Note: call with stream lock |
905 | struct snd_pcm_substream *substream, | 905 | */ |
906 | int state) | 906 | static int snd_pcm_action(struct action_ops *ops, |
907 | struct snd_pcm_substream *substream, | ||
908 | int state) | ||
907 | { | 909 | { |
908 | int res; | 910 | int res; |
909 | 911 | ||
910 | if (snd_pcm_stream_linked(substream)) { | 912 | if (!snd_pcm_stream_linked(substream)) |
913 | return snd_pcm_action_single(ops, substream, state); | ||
914 | |||
915 | if (substream->pcm->nonatomic) { | ||
911 | if (!mutex_trylock(&substream->group->mutex)) { | 916 | if (!mutex_trylock(&substream->group->mutex)) { |
912 | mutex_unlock(&substream->self_group.mutex); | 917 | mutex_unlock(&substream->self_group.mutex); |
913 | mutex_lock(&substream->group->mutex); | 918 | mutex_lock(&substream->group->mutex); |
@@ -916,24 +921,6 @@ static int snd_pcm_action_mutex(struct action_ops *ops, | |||
916 | res = snd_pcm_action_group(ops, substream, state, 1); | 921 | res = snd_pcm_action_group(ops, substream, state, 1); |
917 | mutex_unlock(&substream->group->mutex); | 922 | mutex_unlock(&substream->group->mutex); |
918 | } else { | 923 | } else { |
919 | res = snd_pcm_action_single(ops, substream, state); | ||
920 | } | ||
921 | return res; | ||
922 | } | ||
923 | |||
924 | /* | ||
925 | * Note: call with stream lock | ||
926 | */ | ||
927 | static int snd_pcm_action(struct action_ops *ops, | ||
928 | struct snd_pcm_substream *substream, | ||
929 | int state) | ||
930 | { | ||
931 | int res; | ||
932 | |||
933 | if (substream->pcm->nonatomic) | ||
934 | return snd_pcm_action_mutex(ops, substream, state); | ||
935 | |||
936 | if (snd_pcm_stream_linked(substream)) { | ||
937 | if (!spin_trylock(&substream->group->lock)) { | 924 | if (!spin_trylock(&substream->group->lock)) { |
938 | spin_unlock(&substream->self_group.lock); | 925 | spin_unlock(&substream->self_group.lock); |
939 | spin_lock(&substream->group->lock); | 926 | spin_lock(&substream->group->lock); |
@@ -941,34 +928,10 @@ static int snd_pcm_action(struct action_ops *ops, | |||
941 | } | 928 | } |
942 | res = snd_pcm_action_group(ops, substream, state, 1); | 929 | res = snd_pcm_action_group(ops, substream, state, 1); |
943 | spin_unlock(&substream->group->lock); | 930 | spin_unlock(&substream->group->lock); |
944 | } else { | ||
945 | res = snd_pcm_action_single(ops, substream, state); | ||
946 | } | 931 | } |
947 | return res; | 932 | return res; |
948 | } | 933 | } |
949 | 934 | ||
950 | static int snd_pcm_action_lock_mutex(struct action_ops *ops, | ||
951 | struct snd_pcm_substream *substream, | ||
952 | int state) | ||
953 | { | ||
954 | int res; | ||
955 | |||
956 | down_read(&snd_pcm_link_rwsem); | ||
957 | if (snd_pcm_stream_linked(substream)) { | ||
958 | mutex_lock(&substream->group->mutex); | ||
959 | mutex_lock(&substream->self_group.mutex); | ||
960 | res = snd_pcm_action_group(ops, substream, state, 1); | ||
961 | mutex_unlock(&substream->self_group.mutex); | ||
962 | mutex_unlock(&substream->group->mutex); | ||
963 | } else { | ||
964 | mutex_lock(&substream->self_group.mutex); | ||
965 | res = snd_pcm_action_single(ops, substream, state); | ||
966 | mutex_unlock(&substream->self_group.mutex); | ||
967 | } | ||
968 | up_read(&snd_pcm_link_rwsem); | ||
969 | return res; | ||
970 | } | ||
971 | |||
972 | /* | 935 | /* |
973 | * Note: don't use any locks before | 936 | * Note: don't use any locks before |
974 | */ | 937 | */ |
@@ -978,22 +941,9 @@ static int snd_pcm_action_lock_irq(struct action_ops *ops, | |||
978 | { | 941 | { |
979 | int res; | 942 | int res; |
980 | 943 | ||
981 | if (substream->pcm->nonatomic) | 944 | snd_pcm_stream_lock_irq(substream); |
982 | return snd_pcm_action_lock_mutex(ops, substream, state); | 945 | res = snd_pcm_action(ops, substream, state); |
983 | 946 | snd_pcm_stream_unlock_irq(substream); | |
984 | read_lock_irq(&snd_pcm_link_rwlock); | ||
985 | if (snd_pcm_stream_linked(substream)) { | ||
986 | spin_lock(&substream->group->lock); | ||
987 | spin_lock(&substream->self_group.lock); | ||
988 | res = snd_pcm_action_group(ops, substream, state, 1); | ||
989 | spin_unlock(&substream->self_group.lock); | ||
990 | spin_unlock(&substream->group->lock); | ||
991 | } else { | ||
992 | spin_lock(&substream->self_group.lock); | ||
993 | res = snd_pcm_action_single(ops, substream, state); | ||
994 | spin_unlock(&substream->self_group.lock); | ||
995 | } | ||
996 | read_unlock_irq(&snd_pcm_link_rwlock); | ||
997 | return res; | 947 | return res; |
998 | } | 948 | } |
999 | 949 | ||
diff --git a/sound/core/pcm_trace.h b/sound/core/pcm_trace.h new file mode 100644 index 000000000000..b63b654da5ff --- /dev/null +++ b/sound/core/pcm_trace.h | |||
@@ -0,0 +1,110 @@ | |||
1 | #undef TRACE_SYSTEM | ||
2 | #define TRACE_SYSTEM snd_pcm | ||
3 | #define TRACE_INCLUDE_FILE pcm_trace | ||
4 | |||
5 | #if !defined(_PCM_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) | ||
6 | #define _PCM_TRACE_H | ||
7 | |||
8 | #include <linux/tracepoint.h> | ||
9 | |||
10 | TRACE_EVENT(hwptr, | ||
11 | TP_PROTO(struct snd_pcm_substream *substream, snd_pcm_uframes_t pos, bool irq), | ||
12 | TP_ARGS(substream, pos, irq), | ||
13 | TP_STRUCT__entry( | ||
14 | __field( bool, in_interrupt ) | ||
15 | __field( unsigned int, card ) | ||
16 | __field( unsigned int, device ) | ||
17 | __field( unsigned int, number ) | ||
18 | __field( unsigned int, stream ) | ||
19 | __field( snd_pcm_uframes_t, pos ) | ||
20 | __field( snd_pcm_uframes_t, period_size ) | ||
21 | __field( snd_pcm_uframes_t, buffer_size ) | ||
22 | __field( snd_pcm_uframes_t, old_hw_ptr ) | ||
23 | __field( snd_pcm_uframes_t, hw_ptr_base ) | ||
24 | ), | ||
25 | TP_fast_assign( | ||
26 | __entry->in_interrupt = (irq); | ||
27 | __entry->card = (substream)->pcm->card->number; | ||
28 | __entry->device = (substream)->pcm->device; | ||
29 | __entry->number = (substream)->number; | ||
30 | __entry->stream = (substream)->stream; | ||
31 | __entry->pos = (pos); | ||
32 | __entry->period_size = (substream)->runtime->period_size; | ||
33 | __entry->buffer_size = (substream)->runtime->buffer_size; | ||
34 | __entry->old_hw_ptr = (substream)->runtime->status->hw_ptr; | ||
35 | __entry->hw_ptr_base = (substream)->runtime->hw_ptr_base; | ||
36 | ), | ||
37 | TP_printk("pcmC%dD%d%c/sub%d: %s: pos=%lu, old=%lu, base=%lu, period=%lu, buf=%lu", | ||
38 | __entry->card, __entry->device, | ||
39 | __entry->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c', | ||
40 | __entry->number, | ||
41 | __entry->in_interrupt ? "IRQ" : "POS", | ||
42 | (unsigned long)__entry->pos, | ||
43 | (unsigned long)__entry->old_hw_ptr, | ||
44 | (unsigned long)__entry->hw_ptr_base, | ||
45 | (unsigned long)__entry->period_size, | ||
46 | (unsigned long)__entry->buffer_size) | ||
47 | ); | ||
48 | |||
49 | TRACE_EVENT(xrun, | ||
50 | TP_PROTO(struct snd_pcm_substream *substream), | ||
51 | TP_ARGS(substream), | ||
52 | TP_STRUCT__entry( | ||
53 | __field( unsigned int, card ) | ||
54 | __field( unsigned int, device ) | ||
55 | __field( unsigned int, number ) | ||
56 | __field( unsigned int, stream ) | ||
57 | __field( snd_pcm_uframes_t, period_size ) | ||
58 | __field( snd_pcm_uframes_t, buffer_size ) | ||
59 | __field( snd_pcm_uframes_t, old_hw_ptr ) | ||
60 | __field( snd_pcm_uframes_t, hw_ptr_base ) | ||
61 | ), | ||
62 | TP_fast_assign( | ||
63 | __entry->card = (substream)->pcm->card->number; | ||
64 | __entry->device = (substream)->pcm->device; | ||
65 | __entry->number = (substream)->number; | ||
66 | __entry->stream = (substream)->stream; | ||
67 | __entry->period_size = (substream)->runtime->period_size; | ||
68 | __entry->buffer_size = (substream)->runtime->buffer_size; | ||
69 | __entry->old_hw_ptr = (substream)->runtime->status->hw_ptr; | ||
70 | __entry->hw_ptr_base = (substream)->runtime->hw_ptr_base; | ||
71 | ), | ||
72 | TP_printk("pcmC%dD%d%c/sub%d: XRUN: old=%lu, base=%lu, period=%lu, buf=%lu", | ||
73 | __entry->card, __entry->device, | ||
74 | __entry->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c', | ||
75 | __entry->number, | ||
76 | (unsigned long)__entry->old_hw_ptr, | ||
77 | (unsigned long)__entry->hw_ptr_base, | ||
78 | (unsigned long)__entry->period_size, | ||
79 | (unsigned long)__entry->buffer_size) | ||
80 | ); | ||
81 | |||
82 | TRACE_EVENT(hw_ptr_error, | ||
83 | TP_PROTO(struct snd_pcm_substream *substream, const char *why), | ||
84 | TP_ARGS(substream, why), | ||
85 | TP_STRUCT__entry( | ||
86 | __field( unsigned int, card ) | ||
87 | __field( unsigned int, device ) | ||
88 | __field( unsigned int, number ) | ||
89 | __field( unsigned int, stream ) | ||
90 | __field( const char *, reason ) | ||
91 | ), | ||
92 | TP_fast_assign( | ||
93 | __entry->card = (substream)->pcm->card->number; | ||
94 | __entry->device = (substream)->pcm->device; | ||
95 | __entry->number = (substream)->number; | ||
96 | __entry->stream = (substream)->stream; | ||
97 | __entry->reason = (why); | ||
98 | ), | ||
99 | TP_printk("pcmC%dD%d%c/sub%d: ERROR: %s", | ||
100 | __entry->card, __entry->device, | ||
101 | __entry->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c', | ||
102 | __entry->number, __entry->reason) | ||
103 | ); | ||
104 | |||
105 | #endif /* _PCM_TRACE_H */ | ||
106 | |||
107 | /* This part must be outside protection */ | ||
108 | #undef TRACE_INCLUDE_PATH | ||
109 | #define TRACE_INCLUDE_PATH . | ||
110 | #include <trace/define_trace.h> | ||