Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+132 -28
+6
include/sound/pcm.h
··· 262 262 unsigned int mask; 263 263 }; 264 264 265 + struct snd_pcm_hwptr_log; 266 + 265 267 struct snd_pcm_runtime { 266 268 /* -- Status -- */ 267 269 struct snd_pcm_substream *trigger_master; ··· 341 339 #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) 342 340 /* -- OSS things -- */ 343 341 struct snd_pcm_oss_runtime oss; 342 + #endif 343 + 344 + #ifdef CONFIG_SND_PCM_XRUN_DEBUG 345 + struct snd_pcm_hwptr_log *hwptr_log; 344 346 #endif 345 347 }; 346 348
+4
sound/core/pcm.c
··· 921 921 snd_free_pages((void*)runtime->control, 922 922 PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control))); 923 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 928 kfree(runtime); 925 929 substream->runtime = NULL; 926 930 put_pid(substream->pid);
+122 -28
sound/core/pcm_lib.c
··· 126 126 } 127 127 } 128 128 129 - #define XRUN_DEBUG_BASIC (1<<0) 130 - #define XRUN_DEBUG_STACK (1<<1) /* dump also stack */ 131 - #define XRUN_DEBUG_JIFFIESCHECK (1<<2) /* do jiffies check */ 132 - #define XRUN_DEBUG_PERIODUPDATE (1<<3) /* full period update info */ 133 - #define XRUN_DEBUG_HWPTRUPDATE (1<<4) /* full hwptr update info */ 134 - 135 - #ifdef CONFIG_SND_PCM_XRUN_DEBUG 136 - #define xrun_debug(substream, mask) \ 137 - ((substream)->pstr->xrun_debug & (mask)) 138 - #else 139 - #define xrun_debug(substream, mask) 0 140 - #endif 141 - 142 - #define dump_stack_on_xrun(substream) do { \ 143 - if (xrun_debug(substream, XRUN_DEBUG_STACK)) \ 144 - dump_stack(); \ 145 - } while (0) 146 - 147 129 static void pcm_debug_name(struct snd_pcm_substream *substream, 148 130 char *name, size_t len) 149 131 { ··· 135 153 substream->stream ? 'c' : 'p', 136 154 substream->number); 137 155 } 156 + 157 + #define XRUN_DEBUG_BASIC (1<<0) 158 + #define XRUN_DEBUG_STACK (1<<1) /* dump also stack */ 159 + #define XRUN_DEBUG_JIFFIESCHECK (1<<2) /* do jiffies check */ 160 + #define XRUN_DEBUG_PERIODUPDATE (1<<3) /* full period update info */ 161 + #define XRUN_DEBUG_HWPTRUPDATE (1<<4) /* full hwptr update info */ 162 + #define XRUN_DEBUG_LOG (1<<5) /* show last 10 positions on err */ 163 + #define XRUN_DEBUG_LOGONCE (1<<6) /* do above only once */ 164 + 165 + #ifdef CONFIG_SND_PCM_XRUN_DEBUG 166 + 167 + #define xrun_debug(substream, mask) \ 168 + ((substream)->pstr->xrun_debug & (mask)) 169 + 170 + #define dump_stack_on_xrun(substream) do { \ 171 + if (xrun_debug(substream, XRUN_DEBUG_STACK)) \ 172 + dump_stack(); \ 173 + } while (0) 138 174 139 175 static void xrun(struct snd_pcm_substream *substream) 140 176 { ··· 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 268 static snd_pcm_uframes_t 173 269 snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream, 174 270 struct snd_pcm_runtime *runtime) ··· 278 182 if (printk_ratelimit()) { 279 183 char name[16]; 280 184 pcm_debug_name(substream, name, sizeof(name)); 185 + xrun_log_show(substream); 281 186 snd_printd(KERN_ERR "BUG: %s, pos = 0x%lx, " 282 187 "buffer size = 0x%lx, period size = 0x%lx\n", 283 188 name, pos, runtime->buffer_size, ··· 287 190 pos = 0; 288 191 } 289 192 pos -= pos % runtime->min_align; 193 + if (xrun_debug(substream, XRUN_DEBUG_LOG)) 194 + xrun_log(substream, pos); 290 195 return pos; 291 196 } 292 197 ··· 318 219 wake_up(&runtime->sleep); 319 220 return 0; 320 221 } 321 - 322 - #define hw_ptr_error(substream, fmt, args...) \ 323 - do { \ 324 - if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { \ 325 - if (printk_ratelimit()) { \ 326 - snd_printd("PCM: " fmt, ##args); \ 327 - } \ 328 - dump_stack_on_xrun(substream); \ 329 - } \ 330 - } while (0) 331 222 332 223 static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) 333 224 { ··· 359 270 if (runtime->periods == 1 || new_hw_ptr < old_hw_ptr) 360 271 delta += runtime->buffer_size; 361 272 if (delta < 0) { 273 + xrun_log_show(substream); 362 274 hw_ptr_error(substream, 363 275 "Unexpected hw_pointer value " 364 276 "(stream=%i, pos=%ld, intr_ptr=%ld)\n", ··· 405 315 delta = jdelta / 406 316 (((runtime->period_size * HZ) / runtime->rate) 407 317 + HZ/100); 318 + xrun_log_show(substream); 408 319 hw_ptr_error(substream, 409 320 "hw_ptr skipping! [Q] " 410 321 "(pos=%ld, delta=%ld, period=%ld, " ··· 425 334 } 426 335 no_jiffies_check: 427 336 if (delta > runtime->period_size + runtime->period_size / 2) { 337 + xrun_log_show(substream); 428 338 hw_ptr_error(substream, 429 339 "Lost interrupts? " 430 340 "(stream=%i, delta=%ld, intr_ptr=%ld)\n", ··· 489 397 if (delta < 0) { 490 398 delta += runtime->buffer_size; 491 399 if (delta < 0) { 400 + xrun_log_show(substream); 492 401 hw_ptr_error(substream, 493 402 "Unexpected hw_pointer value [2] " 494 403 "(stream=%i, pos=%ld, old_ptr=%ld, jdelta=%li)\n", ··· 509 416 goto no_jiffies_check; 510 417 delta -= runtime->delay; 511 418 if (((delta * HZ) / runtime->rate) > jdelta + HZ/100) { 419 + xrun_log_show(substream); 512 420 hw_ptr_error(substream, 513 421 "hw_ptr skipping! " 514 422 "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu)\n",