diff --git a/qdf/inc/qdf_nbuf.h b/qdf/inc/qdf_nbuf.h index 9573d21478..54b324bf34 100644 --- a/qdf/inc/qdf_nbuf.h +++ b/qdf/inc/qdf_nbuf.h @@ -3756,6 +3756,29 @@ static inline void qdf_nbuf_trim_add_frag_size(qdf_nbuf_t nbuf, uint8_t idx, __qdf_nbuf_trim_add_frag_size(nbuf, idx, size, truesize); } +/** + * qdf_nbuf_set_mark() - Set nbuf mark + * @nbuf: qdf_nbuf_t + * @mark: Value to set mark + * + * Return: none + */ +static inline void qdf_nbuf_set_mark(qdf_nbuf_t nbuf, uint32_t mark) +{ + __qdf_nbuf_set_mark(nbuf, mark); +} + +/** + * qdf_nbuf_get_mark() - Get nbuf mark + * @nbuf: qdf_nbuf_t + * + * Return: mark value + */ +static inline uint32_t qdf_nbuf_get_mark(qdf_nbuf_t nbuf) +{ + return __qdf_nbuf_get_mark(nbuf); +} + #ifdef NBUF_FRAG_MEMORY_DEBUG #define qdf_nbuf_move_frag_page_offset(f, i, o) \ diff --git a/qdf/inc/qdf_types.h b/qdf/inc/qdf_types.h index a49f683e5a..7bc6473ed4 100644 --- a/qdf/inc/qdf_types.h +++ b/qdf/inc/qdf_types.h @@ -1338,6 +1338,7 @@ enum qdf_suspend_type { * @QDF_VDEV_STOP_RESPONSE_TIMED_OUT: Stop response timeout from FW * @QDF_VDEV_DELETE_RESPONSE_TIMED_OUT: Delete response timeout from FW * @QDF_VDEV_PEER_DELETE_ALL_RESPONSE_TIMED_OUT: Peer delete all resp timeout + * @QDF_WMI_BUF_SEQUENCE_MISMATCH: WMI Tx completion buffer sequence mismatch */ enum qdf_hang_reason { QDF_REASON_UNSPECIFIED, @@ -1360,6 +1361,7 @@ enum qdf_hang_reason { QDF_VDEV_STOP_RESPONSE_TIMED_OUT, QDF_VDEV_DELETE_RESPONSE_TIMED_OUT, QDF_VDEV_PEER_DELETE_ALL_RESPONSE_TIMED_OUT, + QDF_WMI_BUF_SEQUENCE_MISMATCH, }; /** diff --git a/qdf/linux/src/i_qdf_nbuf.h b/qdf/linux/src/i_qdf_nbuf.h index c82c6b3132..294cfafb4e 100644 --- a/qdf/linux/src/i_qdf_nbuf.h +++ b/qdf/linux/src/i_qdf_nbuf.h @@ -2451,6 +2451,29 @@ void __qdf_nbuf_add_rx_frag(__qdf_frag_t buf, __qdf_nbuf_t nbuf, int offset, int frag_len, unsigned int truesize, bool take_frag_ref); +/** + * __qdf_nbuf_set_mark() - Set nbuf mark + * @buf: Pointer to nbuf + * @mark: Value to set mark + * + * Return: None + */ +static inline void __qdf_nbuf_set_mark(__qdf_nbuf_t buf, uint32_t mark) +{ + buf->mark = mark; +} + +/** + * __qdf_nbuf_get_mark() - Get nbuf mark + * @buf: Pointer to nbuf + * + * Return: Value of mark + */ +static inline uint32_t __qdf_nbuf_get_mark(__qdf_nbuf_t buf) +{ + return buf->mark; +} + #ifdef CONFIG_NBUF_AP_PLATFORM #include #else diff --git a/wmi/inc/wmi_unified_priv.h b/wmi/inc/wmi_unified_priv.h index 4ccc337f4a..8bb8ad7ccb 100644 --- a/wmi/inc/wmi_unified_priv.h +++ b/wmi/inc/wmi_unified_priv.h @@ -2543,6 +2543,14 @@ struct wmi_unified { uint32_t *evt_pdev_id_map; uint32_t *cmd_phy_id_map; uint32_t *evt_phy_id_map; +#ifdef WMI_INTERFACE_SEQUENCE_CHECK + /* wmi next transmit sequence number */ + uint32_t wmi_sequence; + /* wmi completion expected sequence number */ + uint32_t wmi_exp_sequence; + qdf_spinlock_t wmi_seq_lock; +#endif /*WMI_INTERFACE_SEQUENCE_CHECK*/ + qdf_atomic_t num_stats_over_qmi; }; diff --git a/wmi/src/wmi_unified.c b/wmi/src/wmi_unified.c index b11d005ade..b575c7faa1 100644 --- a/wmi/src/wmi_unified.c +++ b/wmi/src/wmi_unified.c @@ -1709,6 +1709,101 @@ static bool wmi_is_legacy_d0wow_disable_cmd(wmi_buf_t buf, uint32_t cmd_id) #endif +#ifdef WMI_INTERFACE_SEQUENCE_CHECK +static inline void wmi_interface_sequence_init(struct wmi_unified *wmi_handle) +{ + qdf_spinlock_create(&wmi_handle->wmi_seq_lock); + wmi_handle->wmi_sequence = 0; + wmi_handle->wmi_exp_sequence = 0; +} + +static inline void wmi_interface_sequence_deinit(struct wmi_unified *wmi_handle) +{ + qdf_spinlock_destroy(&wmi_handle->wmi_seq_lock); +} + +static inline QDF_STATUS wmi_htc_send_pkt(struct wmi_unified *wmi_handle, + HTC_PACKET *pkt, + const char *func, uint32_t line) +{ + wmi_buf_t buf = GET_HTC_PACKET_NET_BUF_CONTEXT(pkt); + QDF_STATUS status; + + qdf_spin_lock_bh(&wmi_handle->wmi_seq_lock); + status = htc_send_pkt(wmi_handle->htc_handle, pkt); + if (QDF_STATUS_SUCCESS != status) { + qdf_spin_unlock_bh(&wmi_handle->wmi_seq_lock); + qdf_atomic_dec(&wmi_handle->pending_cmds); + wmi_nofl_err("%s:%d, htc_send_pkt failed, status:%d", + func, line, status); + qdf_mem_free(pkt); + return status; + } + /* Record the sequence number in the SKB */ + qdf_nbuf_set_mark(buf, wmi_handle->wmi_sequence); + /* Increment the sequence number */ + wmi_handle->wmi_sequence = (wmi_handle->wmi_sequence + 1) + & (wmi_handle->wmi_max_cmds - 1); + qdf_spin_unlock_bh(&wmi_handle->wmi_seq_lock); + + return status; +} + +static inline void wmi_interface_sequence_check(struct wmi_unified *wmi_handle, + wmi_buf_t buf) +{ + qdf_spin_lock_bh(&wmi_handle->wmi_seq_lock); + /* Match the completion sequence and expected sequence number */ + if (qdf_nbuf_get_mark(buf) != wmi_handle->wmi_exp_sequence) { + qdf_spin_unlock_bh(&wmi_handle->wmi_seq_lock); + wmi_nofl_err("WMI Tx Completion Sequence number mismatch"); + wmi_nofl_err("Expected %d Received %d", + wmi_handle->wmi_exp_sequence, + qdf_nbuf_get_mark(buf)); + /* Trigger Recovery */ + qdf_trigger_self_recovery(wmi_handle->soc, + QDF_WMI_BUF_SEQUENCE_MISMATCH); + } else { + /* Increment the expected sequence number */ + wmi_handle->wmi_exp_sequence = + (wmi_handle->wmi_exp_sequence + 1) + & (wmi_handle->wmi_max_cmds - 1); + qdf_spin_unlock_bh(&wmi_handle->wmi_seq_lock); + } +} +#else +static inline void wmi_interface_sequence_init(struct wmi_unified *wmi_handle) +{ +} + +static inline void wmi_interface_sequence_deinit(struct wmi_unified *wmi_handle) +{ +} + +static inline QDF_STATUS wmi_htc_send_pkt(struct wmi_unified *wmi_handle, + HTC_PACKET *pkt, + const char *func, uint32_t line) +{ + QDF_STATUS status; + + status = htc_send_pkt(wmi_handle->htc_handle, pkt); + if (QDF_STATUS_SUCCESS != status) { + qdf_atomic_dec(&wmi_handle->pending_cmds); + wmi_nofl_err("%s:%d, htc_send_pkt failed, status:%d", + func, line, status); + qdf_mem_free(pkt); + return status; + } + + return status; +} + +static inline void wmi_interface_sequence_check(struct wmi_unified *wmi_handle, + wmi_buf_t buf) +{ +} +#endif + static inline void wmi_unified_debug_dump(wmi_unified_t wmi_handle) { wmi_nofl_err("Endpoint ID = %d, Tx Queue Depth = %d, soc_id = %u, target type = %s", @@ -1726,7 +1821,6 @@ QDF_STATUS wmi_unified_cmd_send_fl(wmi_unified_t wmi_handle, wmi_buf_t buf, const char *func, uint32_t line) { HTC_PACKET *pkt; - QDF_STATUS status; uint16_t htc_tag = 0; if (wmi_get_runtime_pm_inprogress(wmi_handle)) { @@ -1816,21 +1910,11 @@ QDF_STATUS wmi_unified_cmd_send_fl(wmi_unified_t wmi_handle, wmi_buf_t buf, WMI_COMMAND_RECORD(wmi_handle, cmd_id, tmpbuf); wmi_specific_cmd_record(wmi_handle, cmd_id, tmpbuf); } + qdf_spin_unlock_bh(&wmi_handle->log_info.wmi_record_lock); } #endif - - status = htc_send_pkt(wmi_handle->htc_handle, pkt); - - if (QDF_STATUS_SUCCESS != status) { - qdf_atomic_dec(&wmi_handle->pending_cmds); - wmi_nofl_err("%s:%d, htc_send_pkt failed, status:%d", - func, line, status); - qdf_mem_free(pkt); - return status; - } - - return QDF_STATUS_SUCCESS; + return wmi_htc_send_pkt(wmi_handle, pkt, func, line); } qdf_export_symbol(wmi_unified_cmd_send_fl); @@ -2669,6 +2753,8 @@ void *wmi_unified_get_pdev_handle(struct wmi_soc *soc, uint32_t pdev_idx) wmi_handle->target_type = soc->target_type; wmi_handle->wmi_max_cmds = soc->wmi_max_cmds; + wmi_interface_sequence_init(wmi_handle); + soc->wmi_pdev[pdev_idx] = wmi_handle; } else wmi_handle = soc->wmi_pdev[pdev_idx]; @@ -2804,6 +2890,7 @@ void *wmi_unified_attach(void *scn_handle, WMI_LOGE("wmi attach is not registered"); goto error; } + wmi_interface_sequence_init(wmi_handle); /* Assign target cookie capablity */ wmi_handle->use_cookie = param->use_cookie; wmi_handle->osdev = param->osdev; @@ -2813,7 +2900,6 @@ void *wmi_unified_attach(void *scn_handle, /* Increase the ref count once refcount infra is present */ soc->wmi_psoc = param->psoc; qdf_spinlock_create(&soc->ctx_lock); - soc->ops = wmi_handle->ops; soc->wmi_pdev[0] = wmi_handle; if (wmi_ext_dbgfs_init(wmi_handle) != QDF_STATUS_SUCCESS) @@ -2875,6 +2961,9 @@ void wmi_unified_detach(struct wmi_unified *wmi_handle) soc->wmi_pdev[i]->events_logs_list); qdf_spinlock_destroy(&soc->wmi_pdev[i]->eventq_lock); + + wmi_interface_sequence_deinit(soc->wmi_pdev[i]); + qdf_mem_free(soc->wmi_pdev[i]); } } @@ -2942,7 +3031,9 @@ static void wmi_htc_tx_complete(void *ctx, HTC_PACKET *htc_pkt) u_int32_t len; struct wmi_unified *wmi_handle; #ifdef WMI_INTERFACE_EVENT_LOGGING + struct wmi_debug_log_info *log_info; uint32_t cmd_id; + uint8_t *offset_ptr; #endif ASSERT(wmi_cmd_buf); @@ -2952,28 +3043,33 @@ static void wmi_htc_tx_complete(void *ctx, HTC_PACKET *htc_pkt) QDF_ASSERT(0); return; } + buf_ptr = (u_int8_t *)wmi_buf_data(wmi_cmd_buf); #ifdef WMI_INTERFACE_EVENT_LOGGING - if (wmi_handle && wmi_handle->log_info.wmi_logging_enable) { + log_info = &wmi_handle->log_info; + + if (wmi_handle && log_info->wmi_logging_enable) { cmd_id = WMI_GET_FIELD(qdf_nbuf_data(wmi_cmd_buf), WMI_CMD_HDR, COMMANDID); - qdf_spin_lock_bh(&wmi_handle->log_info.wmi_record_lock); - /* Record 16 bytes of WMI cmd tx complete data - - exclude TLV and WMI headers */ - if (wmi_handle->ops->is_management_record(cmd_id)) { - WMI_MGMT_COMMAND_TX_CMP_RECORD(wmi_handle, cmd_id, - qdf_nbuf_data(wmi_cmd_buf) + - wmi_handle->soc->buf_offset_command); - } else { - WMI_COMMAND_TX_CMP_RECORD(wmi_handle, cmd_id, - qdf_nbuf_data(wmi_cmd_buf) + - wmi_handle->soc->buf_offset_command); - } + qdf_spin_lock_bh(&log_info->wmi_record_lock); + /* Record 16 bytes of WMI cmd tx complete data + * - exclude TLV and WMI headers + */ + offset_ptr = buf_ptr + wmi_handle->soc->buf_offset_command; + if (wmi_handle->ops->is_management_record(cmd_id)) { + WMI_MGMT_COMMAND_TX_CMP_RECORD(wmi_handle, cmd_id, + offset_ptr); + } else { + WMI_COMMAND_TX_CMP_RECORD(wmi_handle, cmd_id, + offset_ptr); + } - qdf_spin_unlock_bh(&wmi_handle->log_info.wmi_record_lock); + qdf_spin_unlock_bh(&log_info->wmi_record_lock); } #endif - buf_ptr = (u_int8_t *) wmi_buf_data(wmi_cmd_buf); + + wmi_interface_sequence_check(wmi_handle, wmi_cmd_buf); + len = qdf_nbuf_len(wmi_cmd_buf); qdf_mem_zero(buf_ptr, len); wmi_buf_free(wmi_cmd_buf);