aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2012-01-13 05:56:11 -0500
committerWey-Yi Guy <wey-yi.w.guy@intel.com>2012-01-28 11:08:13 -0500
commit98d4bf0c49de0d7c68fa7e1f7bd0a37420f712ca (patch)
treea90739756b6ec6e1f82d86bdf218ad69ef200ecd /drivers/net/wireless
parentde46fb079ff16fabb3bdf7892c9a2900a9880329 (diff)
iwlwifi: fix uCode event tracing
Fix multiple bugs in event tracing: 1) If you enable uCode tracing with the device down, it will still attempt to access the device and continuously log "MAC is in deep sleep!" errors. Fix this by only starting logging when the device is actually alive. 2) Now you can set the flag when the device is down, but logging doesn't happen when you bring it up. To fix that, start logging when the device comes alive. This means we don't log before -- we could do that but I don't need it right now. 3) For some reason we read the error instead of the event log -- use the right pointer. 4) Optimise SRAM reading of event log header. 5) Fix reading write pointer == capacity, which can happen due to racy SRAM access 6) Most importantly: fix an error where we would try to read WAY too many events (like 2^32-300) when we read the wrap counter before it is updated by the uCode -- this does happen in practice and will cause the driver to hang the machine. 7) Finally, change the timer to 10ms instead of 100ms as 100ms is too slow to capture all data with a normal event log and with 100ms the log will wrap multiple times before we have a chance to read it. Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com>
Diffstat (limited to 'drivers/net/wireless')
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn.c99
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-debugfs.c7
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-dev.h2
3 files changed, 76 insertions, 32 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
index 7321e7d31406..3e429a3e518d 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
@@ -315,7 +315,7 @@ static void iwl_bg_statistics_periodic(unsigned long data)
315 315
316static void iwl_print_cont_event_trace(struct iwl_priv *priv, u32 base, 316static void iwl_print_cont_event_trace(struct iwl_priv *priv, u32 base,
317 u32 start_idx, u32 num_events, 317 u32 start_idx, u32 num_events,
318 u32 mode) 318 u32 capacity, u32 mode)
319{ 319{
320 u32 i; 320 u32 i;
321 u32 ptr; /* SRAM byte address of log data */ 321 u32 ptr; /* SRAM byte address of log data */
@@ -339,6 +339,15 @@ static void iwl_print_cont_event_trace(struct iwl_priv *priv, u32 base,
339 rmb(); 339 rmb();
340 340
341 /* 341 /*
342 * Refuse to read more than would have fit into the log from
343 * the current start_idx. This used to happen due to the race
344 * described below, but now WARN because the code below should
345 * prevent it from happening here.
346 */
347 if (WARN_ON(num_events > capacity - start_idx))
348 num_events = capacity - start_idx;
349
350 /*
342 * "time" is actually "data" for mode 0 (no timestamp). 351 * "time" is actually "data" for mode 0 (no timestamp).
343 * place event id # at far right for easier visual parsing. 352 * place event id # at far right for easier visual parsing.
344 */ 353 */
@@ -346,12 +355,11 @@ static void iwl_print_cont_event_trace(struct iwl_priv *priv, u32 base,
346 ev = iwl_read32(bus(priv), HBUS_TARG_MEM_RDAT); 355 ev = iwl_read32(bus(priv), HBUS_TARG_MEM_RDAT);
347 time = iwl_read32(bus(priv), HBUS_TARG_MEM_RDAT); 356 time = iwl_read32(bus(priv), HBUS_TARG_MEM_RDAT);
348 if (mode == 0) { 357 if (mode == 0) {
349 trace_iwlwifi_dev_ucode_cont_event(priv, 358 trace_iwlwifi_dev_ucode_cont_event(priv, 0, time, ev);
350 0, time, ev);
351 } else { 359 } else {
352 data = iwl_read32(bus(priv), HBUS_TARG_MEM_RDAT); 360 data = iwl_read32(bus(priv), HBUS_TARG_MEM_RDAT);
353 trace_iwlwifi_dev_ucode_cont_event(priv, 361 trace_iwlwifi_dev_ucode_cont_event(priv, time,
354 time, data, ev); 362 data, ev);
355 } 363 }
356 } 364 }
357 /* Allow device to power down */ 365 /* Allow device to power down */
@@ -362,53 +370,83 @@ static void iwl_print_cont_event_trace(struct iwl_priv *priv, u32 base,
362static void iwl_continuous_event_trace(struct iwl_priv *priv) 370static void iwl_continuous_event_trace(struct iwl_priv *priv)
363{ 371{
364 u32 capacity; /* event log capacity in # entries */ 372 u32 capacity; /* event log capacity in # entries */
373 struct {
374 u32 capacity;
375 u32 mode;
376 u32 wrap_counter;
377 u32 write_counter;
378 } __packed read;
365 u32 base; /* SRAM byte address of event log header */ 379 u32 base; /* SRAM byte address of event log header */
366 u32 mode; /* 0 - no timestamp, 1 - timestamp recorded */ 380 u32 mode; /* 0 - no timestamp, 1 - timestamp recorded */
367 u32 num_wraps; /* # times uCode wrapped to top of log */ 381 u32 num_wraps; /* # times uCode wrapped to top of log */
368 u32 next_entry; /* index of next entry to be written by uCode */ 382 u32 next_entry; /* index of next entry to be written by uCode */
369 383
370 base = priv->shrd->device_pointers.error_event_table; 384 base = priv->shrd->device_pointers.log_event_table;
371 if (iwlagn_hw_valid_rtc_data_addr(base)) { 385 if (iwlagn_hw_valid_rtc_data_addr(base)) {
372 capacity = iwl_read_targ_mem(bus(priv), base); 386 iwl_read_targ_mem_words(bus(priv), base, &read, sizeof(read));
373 num_wraps = iwl_read_targ_mem(bus(priv), 387
374 base + (2 * sizeof(u32))); 388 capacity = read.capacity;
375 mode = iwl_read_targ_mem(bus(priv), base + (1 * sizeof(u32))); 389 mode = read.mode;
376 next_entry = iwl_read_targ_mem(bus(priv), 390 num_wraps = read.wrap_counter;
377 base + (3 * sizeof(u32))); 391 next_entry = read.write_counter;
378 } else 392 } else
379 return; 393 return;
380 394
395 /*
396 * Unfortunately, the uCode doesn't use temporary variables.
397 * Therefore, it can happen that we read next_entry == capacity,
398 * which really means next_entry == 0.
399 */
400 if (unlikely(next_entry == capacity))
401 next_entry = 0;
402 /*
403 * Additionally, the uCode increases the write pointer before
404 * the wraps counter, so if the write pointer is smaller than
405 * the old write pointer (wrap occurred) but we read that no
406 * wrap occurred, we actually read between the next_entry and
407 * num_wraps update (this does happen in practice!!) -- take
408 * that into account by increasing num_wraps.
409 */
410 if (unlikely(next_entry < priv->event_log.next_entry &&
411 num_wraps == priv->event_log.num_wraps))
412 num_wraps++;
413
381 if (num_wraps == priv->event_log.num_wraps) { 414 if (num_wraps == priv->event_log.num_wraps) {
382 iwl_print_cont_event_trace(priv, 415 iwl_print_cont_event_trace(
383 base, priv->event_log.next_entry, 416 priv, base, priv->event_log.next_entry,
384 next_entry - priv->event_log.next_entry, 417 next_entry - priv->event_log.next_entry,
385 mode); 418 capacity, mode);
419
386 priv->event_log.non_wraps_count++; 420 priv->event_log.non_wraps_count++;
387 } else { 421 } else {
388 if ((num_wraps - priv->event_log.num_wraps) > 1) 422 if (num_wraps - priv->event_log.num_wraps > 1)
389 priv->event_log.wraps_more_count++; 423 priv->event_log.wraps_more_count++;
390 else 424 else
391 priv->event_log.wraps_once_count++; 425 priv->event_log.wraps_once_count++;
426
392 trace_iwlwifi_dev_ucode_wrap_event(priv, 427 trace_iwlwifi_dev_ucode_wrap_event(priv,
393 num_wraps - priv->event_log.num_wraps, 428 num_wraps - priv->event_log.num_wraps,
394 next_entry, priv->event_log.next_entry); 429 next_entry, priv->event_log.next_entry);
430
395 if (next_entry < priv->event_log.next_entry) { 431 if (next_entry < priv->event_log.next_entry) {
396 iwl_print_cont_event_trace(priv, base, 432 iwl_print_cont_event_trace(
397 priv->event_log.next_entry, 433 priv, base, priv->event_log.next_entry,
398 capacity - priv->event_log.next_entry, 434 capacity - priv->event_log.next_entry,
399 mode); 435 capacity, mode);
400 436
401 iwl_print_cont_event_trace(priv, base, 0, 437 iwl_print_cont_event_trace(
402 next_entry, mode); 438 priv, base, 0, next_entry, capacity, mode);
403 } else { 439 } else {
404 iwl_print_cont_event_trace(priv, base, 440 iwl_print_cont_event_trace(
405 next_entry, capacity - next_entry, 441 priv, base, next_entry,
406 mode); 442 capacity - next_entry,
443 capacity, mode);
407 444
408 iwl_print_cont_event_trace(priv, base, 0, 445 iwl_print_cont_event_trace(
409 next_entry, mode); 446 priv, base, 0, next_entry, capacity, mode);
410 } 447 }
411 } 448 }
449
412 priv->event_log.num_wraps = num_wraps; 450 priv->event_log.num_wraps = num_wraps;
413 priv->event_log.next_entry = next_entry; 451 priv->event_log.next_entry = next_entry;
414} 452}
@@ -1219,6 +1257,11 @@ int iwl_alive_start(struct iwl_priv *priv)
1219 if (iwl_is_rfkill(priv->shrd)) 1257 if (iwl_is_rfkill(priv->shrd))
1220 return -ERFKILL; 1258 return -ERFKILL;
1221 1259
1260 if (priv->event_log.ucode_trace) {
1261 /* start collecting data now */
1262 mod_timer(&priv->ucode_trace, jiffies);
1263 }
1264
1222 /* download priority table before any calibration request */ 1265 /* download priority table before any calibration request */
1223 if (cfg(priv)->bt_params && 1266 if (cfg(priv)->bt_params &&
1224 cfg(priv)->bt_params->advanced_bt_coexist) { 1267 cfg(priv)->bt_params->advanced_bt_coexist) {
diff --git a/drivers/net/wireless/iwlwifi/iwl-debugfs.c b/drivers/net/wireless/iwlwifi/iwl-debugfs.c
index f837f32bb71c..978a1d4c6a0a 100644
--- a/drivers/net/wireless/iwlwifi/iwl-debugfs.c
+++ b/drivers/net/wireless/iwlwifi/iwl-debugfs.c
@@ -2131,9 +2131,10 @@ static ssize_t iwl_dbgfs_ucode_tracing_write(struct file *file,
2131 2131
2132 if (trace) { 2132 if (trace) {
2133 priv->event_log.ucode_trace = true; 2133 priv->event_log.ucode_trace = true;
2134 /* schedule the ucode timer to occur in UCODE_TRACE_PERIOD */ 2134 if (iwl_is_alive(priv->shrd)) {
2135 mod_timer(&priv->ucode_trace, 2135 /* start collecting data now */
2136 jiffies + msecs_to_jiffies(UCODE_TRACE_PERIOD)); 2136 mod_timer(&priv->ucode_trace, jiffies);
2137 }
2137 } else { 2138 } else {
2138 priv->event_log.ucode_trace = false; 2139 priv->event_log.ucode_trace = false;
2139 del_timer_sync(&priv->ucode_trace); 2140 del_timer_sync(&priv->ucode_trace);
diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h
index 4579d61da25e..af846002150a 100644
--- a/drivers/net/wireless/iwlwifi/iwl-dev.h
+++ b/drivers/net/wireless/iwlwifi/iwl-dev.h
@@ -661,7 +661,7 @@ struct traffic_stats {
661 * schedule the timer to wake up every UCODE_TRACE_PERIOD milliseconds 661 * schedule the timer to wake up every UCODE_TRACE_PERIOD milliseconds
662 * to perform continuous uCode event logging operation if enabled 662 * to perform continuous uCode event logging operation if enabled
663 */ 663 */
664#define UCODE_TRACE_PERIOD (100) 664#define UCODE_TRACE_PERIOD (10)
665 665
666/* 666/*
667 * iwl_event_log: current uCode event log position 667 * iwl_event_log: current uCode event log position