aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2014-11-05 05:34:36 -0500
committerTakashi Iwai <tiwai@suse.de>2014-11-05 05:34:36 -0500
commitc009b7ef9409f957ab8846d362463d05678a969d (patch)
treed70f30311304508038a15f476172bacc14d2d799
parentae366c2049b48b54e5250cb57438bbebd1dbe610 (diff)
parent2b30d411dbc6eddfb5b4f9afd5a2c57b6f4dd96c (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.txt17
-rw-r--r--include/sound/pcm.h3
-rw-r--r--sound/core/Makefile3
-rw-r--r--sound/core/pcm.c33
-rw-r--r--sound/core/pcm_lib.c148
-rw-r--r--sound/core/pcm_native.c76
-rw-r--r--sound/core/pcm_trace.h110
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
133card*/pcm*/sub*/info 120card*/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
146card*/pcm*/sub*/prealloc 133card*/pcm*/sub*/prealloc
147 The buffer pre-allocation information. 134 The buffer pre-allocation information.
148 135
136card*/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
150AC97 Codec Information 141AC97 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
15snd-pcm-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o 15snd-pcm-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o
16 16
17# for trace-points
18CFLAGS_pcm_lib.o := -I$(src)
19
17snd-pcm-dmaengine-objs := pcm_dmaengine.o 20snd-pcm-dmaengine-objs := pcm_dmaengine.o
18 21
19snd-rawmidi-objs := rawmidi.o 22snd-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
486static 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
486static void snd_pcm_xrun_debug_read(struct snd_info_entry *entry, 499static 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
194struct 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
204struct snd_pcm_hwptr_log {
205 unsigned int idx;
206 unsigned int hit: 1;
207 struct hwptr_log_entry entries[XRUN_LOG_CNT];
208};
209
210static 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
237static 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/*
904static 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) 906static 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 */
927static 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
950static 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
10TRACE_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
49TRACE_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
82TRACE_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>