static void iwl_print_cont_event_trace(struct iwl_priv *priv, u32 base,
u32 start_idx, u32 num_events,
- u32 mode)
+ u32 capacity, u32 mode)
{
u32 i;
u32 ptr; /* SRAM byte address of log data */
iwl_write32(bus(priv), HBUS_TARG_MEM_RADDR, ptr);
rmb();
+ /*
+ * Refuse to read more than would have fit into the log from
+ * the current start_idx. This used to happen due to the race
+ * described below, but now WARN because the code below should
+ * prevent it from happening here.
+ */
+ if (WARN_ON(num_events > capacity - start_idx))
+ num_events = capacity - start_idx;
+
/*
* "time" is actually "data" for mode 0 (no timestamp).
* place event id # at far right for easier visual parsing.
ev = iwl_read32(bus(priv), HBUS_TARG_MEM_RDAT);
time = iwl_read32(bus(priv), HBUS_TARG_MEM_RDAT);
if (mode == 0) {
- trace_iwlwifi_dev_ucode_cont_event(priv,
- 0, time, ev);
+ trace_iwlwifi_dev_ucode_cont_event(priv, 0, time, ev);
} else {
data = iwl_read32(bus(priv), HBUS_TARG_MEM_RDAT);
- trace_iwlwifi_dev_ucode_cont_event(priv,
- time, data, ev);
+ trace_iwlwifi_dev_ucode_cont_event(priv, time,
+ data, ev);
}
}
/* Allow device to power down */
static void iwl_continuous_event_trace(struct iwl_priv *priv)
{
u32 capacity; /* event log capacity in # entries */
+ struct {
+ u32 capacity;
+ u32 mode;
+ u32 wrap_counter;
+ u32 write_counter;
+ } __packed read;
u32 base; /* SRAM byte address of event log header */
u32 mode; /* 0 - no timestamp, 1 - timestamp recorded */
u32 num_wraps; /* # times uCode wrapped to top of log */
u32 next_entry; /* index of next entry to be written by uCode */
- base = priv->shrd->device_pointers.error_event_table;
+ base = priv->shrd->device_pointers.log_event_table;
if (iwlagn_hw_valid_rtc_data_addr(base)) {
- capacity = iwl_read_targ_mem(bus(priv), base);
- num_wraps = iwl_read_targ_mem(bus(priv),
- base + (2 * sizeof(u32)));
- mode = iwl_read_targ_mem(bus(priv), base + (1 * sizeof(u32)));
- next_entry = iwl_read_targ_mem(bus(priv),
- base + (3 * sizeof(u32)));
+ iwl_read_targ_mem_words(bus(priv), base, &read, sizeof(read));
+
+ capacity = read.capacity;
+ mode = read.mode;
+ num_wraps = read.wrap_counter;
+ next_entry = read.write_counter;
} else
return;
+ /*
+ * Unfortunately, the uCode doesn't use temporary variables.
+ * Therefore, it can happen that we read next_entry == capacity,
+ * which really means next_entry == 0.
+ */
+ if (unlikely(next_entry == capacity))
+ next_entry = 0;
+ /*
+ * Additionally, the uCode increases the write pointer before
+ * the wraps counter, so if the write pointer is smaller than
+ * the old write pointer (wrap occurred) but we read that no
+ * wrap occurred, we actually read between the next_entry and
+ * num_wraps update (this does happen in practice!!) -- take
+ * that into account by increasing num_wraps.
+ */
+ if (unlikely(next_entry < priv->event_log.next_entry &&
+ num_wraps == priv->event_log.num_wraps))
+ num_wraps++;
+
if (num_wraps == priv->event_log.num_wraps) {
- iwl_print_cont_event_trace(priv,
- base, priv->event_log.next_entry,
- next_entry - priv->event_log.next_entry,
- mode);
+ iwl_print_cont_event_trace(
+ priv, base, priv->event_log.next_entry,
+ next_entry - priv->event_log.next_entry,
+ capacity, mode);
+
priv->event_log.non_wraps_count++;
} else {
- if ((num_wraps - priv->event_log.num_wraps) > 1)
+ if (num_wraps - priv->event_log.num_wraps > 1)
priv->event_log.wraps_more_count++;
else
priv->event_log.wraps_once_count++;
+
trace_iwlwifi_dev_ucode_wrap_event(priv,
num_wraps - priv->event_log.num_wraps,
next_entry, priv->event_log.next_entry);
+
if (next_entry < priv->event_log.next_entry) {
- iwl_print_cont_event_trace(priv, base,
- priv->event_log.next_entry,
- capacity - priv->event_log.next_entry,
- mode);
+ iwl_print_cont_event_trace(
+ priv, base, priv->event_log.next_entry,
+ capacity - priv->event_log.next_entry,
+ capacity, mode);
- iwl_print_cont_event_trace(priv, base, 0,
- next_entry, mode);
+ iwl_print_cont_event_trace(
+ priv, base, 0, next_entry, capacity, mode);
} else {
- iwl_print_cont_event_trace(priv, base,
- next_entry, capacity - next_entry,
- mode);
+ iwl_print_cont_event_trace(
+ priv, base, next_entry,
+ capacity - next_entry,
+ capacity, mode);
- iwl_print_cont_event_trace(priv, base, 0,
- next_entry, mode);
+ iwl_print_cont_event_trace(
+ priv, base, 0, next_entry, capacity, mode);
}
}
+
priv->event_log.num_wraps = num_wraps;
priv->event_log.next_entry = next_entry;
}
if (iwl_is_rfkill(priv->shrd))
return -ERFKILL;
+ if (priv->event_log.ucode_trace) {
+ /* start collecting data now */
+ mod_timer(&priv->ucode_trace, jiffies);
+ }
+
/* download priority table before any calibration request */
if (cfg(priv)->bt_params &&
cfg(priv)->bt_params->advanced_bt_coexist) {