qcacmn: Process FW diag events in a separate worker thread
Currently, FW diag logs, management frames and stats responses are processed in worker context. Because of this, there is a possible out of memory scenario when there are huge number of diag events waiting in work queue. To address this, process diag events in a separate worker thread and set the limit for the number of diag events that can be queued in work queue. Change-Id: Ie6e5b905a3b591cf724cd6b024bec0612f9e2e0c CRs-Fixed: 2818013
This commit is contained in:
@@ -2232,6 +2232,30 @@ QDF_STATUS wmi_unified_unregister_event_handler(wmi_unified_t wmi_handle,
|
||||
}
|
||||
qdf_export_symbol(wmi_unified_unregister_event_handler);
|
||||
|
||||
static void
|
||||
wmi_process_rx_diag_event_worker_thread_ctx(struct wmi_unified *wmi_handle,
|
||||
void *evt_buf)
|
||||
{
|
||||
uint32_t num_diag_events_pending;
|
||||
|
||||
qdf_spin_lock_bh(&wmi_handle->diag_eventq_lock);
|
||||
if (RX_DIAG_WQ_MAX_SIZE > 0) {
|
||||
num_diag_events_pending = qdf_nbuf_queue_len(
|
||||
&wmi_handle->diag_event_queue);
|
||||
|
||||
if (num_diag_events_pending == RX_DIAG_WQ_MAX_SIZE) {
|
||||
qdf_spin_unlock_bh(&wmi_handle->diag_eventq_lock);
|
||||
wmi_handle->wmi_rx_diag_events_dropped++;
|
||||
qdf_nbuf_free(evt_buf);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
qdf_nbuf_queue_add(&wmi_handle->diag_event_queue, evt_buf);
|
||||
qdf_spin_unlock_bh(&wmi_handle->diag_eventq_lock);
|
||||
qdf_sched_work(0, &wmi_handle->rx_diag_event_work);
|
||||
}
|
||||
|
||||
void wmi_process_fw_event_worker_thread_ctx(struct wmi_unified *wmi_handle,
|
||||
void *evt_buf)
|
||||
{
|
||||
@@ -2447,12 +2471,14 @@ static void wmi_process_control_rx(struct wmi_unified *wmi_handle,
|
||||
#endif
|
||||
|
||||
if (exec_ctx == WMI_RX_WORK_CTX) {
|
||||
wmi_process_fw_event_worker_thread_ctx
|
||||
(wmi_handle, evt_buf);
|
||||
wmi_process_fw_event_worker_thread_ctx(wmi_handle, evt_buf);
|
||||
} else if (exec_ctx == WMI_RX_TASKLET_CTX) {
|
||||
wmi_process_fw_event(wmi_handle, evt_buf);
|
||||
} else if (exec_ctx == WMI_RX_SERIALIZER_CTX) {
|
||||
wmi_process_fw_event_sched_thread_ctx(wmi_handle, evt_buf);
|
||||
} else if (exec_ctx == WMI_RX_DIAG_WORK_CTX) {
|
||||
wmi_process_rx_diag_event_worker_thread_ctx(wmi_handle,
|
||||
evt_buf);
|
||||
} else {
|
||||
wmi_err("Invalid event context %d", exec_ctx);
|
||||
qdf_nbuf_free(evt_buf);
|
||||
@@ -2780,6 +2806,47 @@ static void wmi_rx_event_work(void *arg)
|
||||
qdf_timer_free(&wd_timer);
|
||||
}
|
||||
|
||||
/**
|
||||
* wmi_rx_diag_event_work() - process rx diag event in work queue context
|
||||
* @arg: opaque pointer to wmi handle
|
||||
*
|
||||
* This function process fw diag event to serialize it through rx worker thread.
|
||||
*
|
||||
* Return: none
|
||||
*/
|
||||
static void wmi_rx_diag_event_work(void *arg)
|
||||
{
|
||||
wmi_buf_t buf;
|
||||
struct wmi_unified *wmi = arg;
|
||||
qdf_timer_t wd_timer;
|
||||
struct wmi_wq_dbg_info info;
|
||||
|
||||
if (!wmi) {
|
||||
wmi_err("Invalid WMI handle");
|
||||
return;
|
||||
}
|
||||
|
||||
/* initialize WMI workqueue watchdog timer */
|
||||
qdf_timer_init(NULL, &wd_timer, &wmi_workqueue_watchdog_bite,
|
||||
&info, QDF_TIMER_TYPE_SW);
|
||||
qdf_spin_lock_bh(&wmi->diag_eventq_lock);
|
||||
buf = qdf_nbuf_queue_remove(&wmi->diag_event_queue);
|
||||
qdf_spin_unlock_bh(&wmi->diag_eventq_lock);
|
||||
while (buf) {
|
||||
qdf_timer_start(&wd_timer, WMI_WQ_WD_TIMEOUT);
|
||||
info.wd_msg_type_id =
|
||||
WMI_GET_FIELD(qdf_nbuf_data(buf), WMI_CMD_HDR, COMMANDID);
|
||||
info.wmi_wq = NULL;
|
||||
info.task = qdf_get_current_task();
|
||||
__wmi_control_rx(wmi, buf);
|
||||
qdf_timer_stop(&wd_timer);
|
||||
qdf_spin_lock_bh(&wmi->diag_eventq_lock);
|
||||
buf = qdf_nbuf_queue_remove(&wmi->diag_event_queue);
|
||||
qdf_spin_unlock_bh(&wmi->diag_eventq_lock);
|
||||
}
|
||||
qdf_timer_free(&wd_timer);
|
||||
}
|
||||
|
||||
#ifdef FEATURE_RUNTIME_PM
|
||||
/**
|
||||
* wmi_runtime_pm_init() - initialize runtime pm wmi variables
|
||||
@@ -2847,6 +2914,28 @@ static inline void wmi_interface_logging_init(struct wmi_unified *wmi_handle,
|
||||
}
|
||||
#endif
|
||||
|
||||
static QDF_STATUS wmi_initialize_worker_context(struct wmi_unified *wmi_handle)
|
||||
{
|
||||
wmi_handle->wmi_rx_work_queue =
|
||||
qdf_alloc_unbound_workqueue("wmi_rx_event_work_queue");
|
||||
if (!wmi_handle->wmi_rx_work_queue) {
|
||||
wmi_err("failed to create wmi_rx_event_work_queue");
|
||||
return QDF_STATUS_E_RESOURCES;
|
||||
}
|
||||
|
||||
qdf_spinlock_create(&wmi_handle->eventq_lock);
|
||||
qdf_nbuf_queue_init(&wmi_handle->event_queue);
|
||||
qdf_create_work(0, &wmi_handle->rx_event_work,
|
||||
wmi_rx_event_work, wmi_handle);
|
||||
qdf_spinlock_create(&wmi_handle->diag_eventq_lock);
|
||||
qdf_nbuf_queue_init(&wmi_handle->diag_event_queue);
|
||||
qdf_create_work(0, &wmi_handle->rx_diag_event_work,
|
||||
wmi_rx_diag_event_work, wmi_handle);
|
||||
wmi_handle->wmi_rx_diag_events_dropped = 0;
|
||||
|
||||
return QDF_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* wmi_unified_get_pdev_handle: Get WMI SoC handle
|
||||
* @param wmi_soc: Pointer to wmi soc object
|
||||
@@ -2857,6 +2946,7 @@ static inline void wmi_interface_logging_init(struct wmi_unified *wmi_handle,
|
||||
void *wmi_unified_get_pdev_handle(struct wmi_soc *soc, uint32_t pdev_idx)
|
||||
{
|
||||
struct wmi_unified *wmi_handle;
|
||||
QDF_STATUS status;
|
||||
|
||||
if (pdev_idx >= WMI_MAX_RADIOS)
|
||||
return NULL;
|
||||
@@ -2868,22 +2958,15 @@ void *wmi_unified_get_pdev_handle(struct wmi_soc *soc, uint32_t pdev_idx)
|
||||
if (!wmi_handle)
|
||||
return NULL;
|
||||
|
||||
status = wmi_initialize_worker_context(wmi_handle);
|
||||
if (QDF_IS_STATUS_ERROR(status))
|
||||
goto error;
|
||||
|
||||
wmi_handle->scn_handle = soc->scn_handle;
|
||||
wmi_handle->event_id = soc->event_id;
|
||||
wmi_handle->event_handler = soc->event_handler;
|
||||
wmi_handle->ctx = soc->ctx;
|
||||
wmi_handle->ops = soc->ops;
|
||||
qdf_spinlock_create(&wmi_handle->eventq_lock);
|
||||
qdf_nbuf_queue_init(&wmi_handle->event_queue);
|
||||
|
||||
qdf_create_work(0, &wmi_handle->rx_event_work,
|
||||
wmi_rx_event_work, wmi_handle);
|
||||
wmi_handle->wmi_rx_work_queue =
|
||||
qdf_alloc_unbound_workqueue("wmi_rx_event_work_queue");
|
||||
if (!wmi_handle->wmi_rx_work_queue) {
|
||||
wmi_err("failed to create wmi_rx_event_work_queue");
|
||||
goto error;
|
||||
}
|
||||
wmi_handle->wmi_events = soc->wmi_events;
|
||||
wmi_handle->services = soc->services;
|
||||
wmi_handle->soc = soc;
|
||||
@@ -2985,6 +3068,7 @@ void *wmi_unified_attach(void *scn_handle,
|
||||
{
|
||||
struct wmi_unified *wmi_handle;
|
||||
struct wmi_soc *soc;
|
||||
QDF_STATUS status;
|
||||
|
||||
soc = (struct wmi_soc *) qdf_mem_malloc(sizeof(struct wmi_soc));
|
||||
if (!soc)
|
||||
@@ -2997,6 +3081,11 @@ void *wmi_unified_attach(void *scn_handle,
|
||||
qdf_mem_free(soc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
status = wmi_initialize_worker_context(wmi_handle);
|
||||
if (QDF_IS_STATUS_ERROR(status))
|
||||
goto error;
|
||||
|
||||
wmi_handle->soc = soc;
|
||||
wmi_handle->soc->soc_idx = param->soc_id;
|
||||
wmi_handle->soc->is_async_ep = param->is_async_ep;
|
||||
@@ -3011,21 +3100,6 @@ void *wmi_unified_attach(void *scn_handle,
|
||||
wmi_handle->cmd_phy_id_map = soc->cmd_phy_id_map;
|
||||
wmi_handle->evt_phy_id_map = soc->evt_phy_id_map;
|
||||
soc->scn_handle = scn_handle;
|
||||
qdf_atomic_init(&wmi_handle->pending_cmds);
|
||||
qdf_atomic_init(&wmi_handle->is_target_suspended);
|
||||
qdf_atomic_init(&wmi_handle->num_stats_over_qmi);
|
||||
wmi_runtime_pm_init(wmi_handle);
|
||||
qdf_spinlock_create(&wmi_handle->eventq_lock);
|
||||
qdf_nbuf_queue_init(&wmi_handle->event_queue);
|
||||
qdf_create_work(0, &wmi_handle->rx_event_work,
|
||||
wmi_rx_event_work, wmi_handle);
|
||||
wmi_handle->wmi_rx_work_queue =
|
||||
qdf_alloc_unbound_workqueue("wmi_rx_event_work_queue");
|
||||
if (!wmi_handle->wmi_rx_work_queue) {
|
||||
wmi_err("failed to create wmi_rx_event_work_queue");
|
||||
goto error;
|
||||
}
|
||||
wmi_interface_logging_init(wmi_handle, WMI_HOST_PDEV_ID_0);
|
||||
wmi_handle->target_type = param->target_type;
|
||||
soc->target_type = param->target_type;
|
||||
|
||||
@@ -3038,6 +3112,13 @@ void *wmi_unified_attach(void *scn_handle,
|
||||
wmi_err("wmi attach is not registered");
|
||||
goto error;
|
||||
}
|
||||
|
||||
qdf_atomic_init(&wmi_handle->pending_cmds);
|
||||
qdf_atomic_init(&wmi_handle->is_target_suspended);
|
||||
qdf_atomic_init(&wmi_handle->num_stats_over_qmi);
|
||||
wmi_runtime_pm_init(wmi_handle);
|
||||
wmi_interface_logging_init(wmi_handle, WMI_HOST_PDEV_ID_0);
|
||||
|
||||
wmi_interface_sequence_init(wmi_handle);
|
||||
/* Assign target cookie capablity */
|
||||
wmi_handle->use_cookie = param->use_cookie;
|
||||
@@ -3099,6 +3180,14 @@ void wmi_unified_detach(struct wmi_unified *wmi_handle)
|
||||
&soc->wmi_pdev[i]->event_queue);
|
||||
}
|
||||
|
||||
buf = qdf_nbuf_queue_remove(
|
||||
&soc->wmi_pdev[i]->diag_event_queue);
|
||||
while (buf) {
|
||||
qdf_nbuf_free(buf);
|
||||
buf = qdf_nbuf_queue_remove(
|
||||
&soc->wmi_pdev[i]->diag_event_queue);
|
||||
}
|
||||
|
||||
wmi_log_buffer_free(soc->wmi_pdev[i]);
|
||||
|
||||
/* Free events logs list */
|
||||
@@ -3107,6 +3196,8 @@ 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);
|
||||
qdf_spinlock_destroy(
|
||||
&soc->wmi_pdev[i]->diag_eventq_lock);
|
||||
|
||||
wmi_interface_sequence_deinit(soc->wmi_pdev[i]);
|
||||
wmi_ext_dbgfs_deinit(soc->wmi_pdev[i]);
|
||||
|
Reference in New Issue
Block a user