From 0c7c251f6e554ff113e6b860715379eccc8d4a93 Mon Sep 17 00:00:00 2001 From: Karthik Kantamneni Date: Wed, 27 Jan 2021 19:41:01 +0530 Subject: [PATCH] qcacld-3.0: Add RX refill thread infra to replenish RX buffers Add Rx refill thread infrastructure to replenish RX buffer pool Change-Id: I2553e0e35d251cf72e741321a2389f741f1bb485 CRs-Fixed: 2869355 --- core/cds/inc/cds_sched.h | 6 +- core/dp/txrx3.0/dp_rx_thread.c | 210 ++++++++++++++++++++++++++++++++- core/dp/txrx3.0/dp_rx_thread.h | 77 +++++++++++- core/dp/txrx3.0/dp_txrx.c | 53 +++++++++ core/dp/txrx3.0/dp_txrx.h | 20 +++- 5 files changed, 362 insertions(+), 4 deletions(-) diff --git a/core/cds/inc/cds_sched.h b/core/cds/inc/cds_sched.h index 1b086ff23333..83b3c19282db 100644 --- a/core/cds/inc/cds_sched.h +++ b/core/cds/inc/cds_sched.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -48,6 +48,10 @@ #define RX_VDEV_DEL_EVENT 0x004 #define RX_SHUTDOWN_EVENT 0x010 +#define RX_REFILL_POST_EVENT 0x001 +#define RX_REFILL_SUSPEND_EVENT 0x002 +#define RX_REFILL_SHUTDOWN_EVENT 0x004 + #ifdef QCA_CONFIG_SMP /* ** Maximum number of cds messages to be allocated for diff --git a/core/dp/txrx3.0/dp_rx_thread.c b/core/dp/txrx3.0/dp_rx_thread.c index 8b2c6415e4bc..700ce4bdd336 100644 --- a/core/dp/txrx3.0/dp_rx_thread.c +++ b/core/dp/txrx3.0/dp_rx_thread.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2021 The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -19,6 +19,7 @@ #include #include "dp_peer.h" #include "dp_internal.h" +#include "dp_types.h" #include #include #include @@ -581,6 +582,86 @@ static int dp_rx_thread_loop(void *arg) return 0; } +static int dp_rx_refill_thread_sub_loop(struct dp_rx_refill_thread *rx_thread, + bool *shutdown) +{ + while (true) { + if (qdf_atomic_test_and_clear_bit(RX_REFILL_SHUTDOWN_EVENT, + &rx_thread->event_flag)) { + if (qdf_atomic_test_and_clear_bit(RX_REFILL_SUSPEND_EVENT, + &rx_thread->event_flag)) { + qdf_event_set(&rx_thread->suspend_event); + } + dp_debug("shutting down (%s) pid %d", + qdf_get_current_comm(), qdf_get_current_pid()); + *shutdown = true; + break; + } + + dp_rx_refill_buff_pool_enqueue((struct dp_soc *)rx_thread->soc); + + if (qdf_atomic_test_and_clear_bit(RX_REFILL_SUSPEND_EVENT, + &rx_thread->event_flag)) { + dp_debug("refill thread received suspend ind (%s) pid %d", + qdf_get_current_comm(), + qdf_get_current_pid()); + qdf_event_set(&rx_thread->suspend_event); + dp_debug("refill thread waiting for resume (%s) pid %d", + qdf_get_current_comm(), + qdf_get_current_pid()); + qdf_wait_single_event(&rx_thread->resume_event, 0); + } + break; + } + return 0; +} + +static int dp_rx_refill_thread_loop(void *arg) +{ + struct dp_rx_refill_thread *rx_thread = arg; + bool shutdown = false; + int status; + + if (!arg) { + dp_err("bad Args passed"); + return 0; + } + + qdf_set_user_nice(qdf_get_current_task(), -1); + qdf_set_wake_up_idle(true); + + qdf_event_set(&rx_thread->start_event); + dp_info("starting rx_refill_thread (%s) pid %d", qdf_get_current_comm(), + qdf_get_current_pid()); + while (!shutdown) { + /* This implements the execution model algorithm */ + dp_debug("refill thread sleeping"); + status = + qdf_wait_queue_interruptible + (rx_thread->wait_q, + qdf_atomic_test_bit(RX_REFILL_POST_EVENT, + &rx_thread->event_flag) || + qdf_atomic_test_bit(RX_REFILL_SUSPEND_EVENT, + &rx_thread->event_flag)); + dp_debug("refill thread woken up"); + + if (status == -ERESTARTSYS) { + QDF_DEBUG_PANIC("wait_event_interruptible returned -ERESTARTSYS"); + break; + } + dp_rx_refill_thread_sub_loop(rx_thread, &shutdown); + qdf_atomic_clear_bit(RX_REFILL_POST_EVENT, &rx_thread->event_flag); + } + + /* If we get here the scheduler thread must exit */ + dp_info("exiting (%s) pid %d", qdf_get_current_comm(), + qdf_get_current_pid()); + qdf_event_set(&rx_thread->shutdown_event); + qdf_exit_thread(QDF_STATUS_SUCCESS); + + return 0; +} + /** * dp_rx_tm_thread_napi_poll() - dummy napi poll for rx_thread NAPI * @napi: pointer to DP rx_thread NAPI @@ -697,6 +778,60 @@ static QDF_STATUS dp_rx_tm_thread_deinit(struct dp_rx_thread *rx_thread) return QDF_STATUS_SUCCESS; } +QDF_STATUS dp_rx_refill_thread_init(struct dp_rx_refill_thread *refill_thread) +{ + char refill_thread_name[20] = {0}; + QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + + qdf_scnprintf(refill_thread_name, sizeof(refill_thread_name), + "dp_refill_thread"); + dp_info("Initializing %s", refill_thread_name); + + refill_thread->state = DP_RX_REFILL_THREAD_INVALID; + refill_thread->event_flag = 0; + qdf_event_create(&refill_thread->start_event); + qdf_event_create(&refill_thread->suspend_event); + qdf_event_create(&refill_thread->resume_event); + qdf_event_create(&refill_thread->shutdown_event); + qdf_init_waitqueue_head(&refill_thread->wait_q); + refill_thread->task = qdf_create_thread(dp_rx_refill_thread_loop, + refill_thread, + refill_thread_name); + if (!refill_thread->task) { + dp_err("could not create dp_rx_refill_thread"); + return QDF_STATUS_E_FAILURE; + } + qdf_wake_up_process(refill_thread->task); + qdf_status = qdf_wait_single_event(&refill_thread->start_event, + 0); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + dp_err("failed waiting for refill thread creation status: %d", + qdf_status); + return QDF_STATUS_E_FAILURE; + } + + refill_thread->state = DP_RX_REFILL_THREAD_RUNNING; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS dp_rx_refill_thread_deinit(struct dp_rx_refill_thread *refill_thread) +{ + qdf_set_bit(RX_REFILL_SHUTDOWN_EVENT, + &refill_thread->event_flag); + qdf_set_bit(RX_REFILL_POST_EVENT, + &refill_thread->event_flag); + qdf_wake_up_interruptible(&refill_thread->wait_q); + qdf_wait_single_event(&refill_thread->shutdown_event, 0); + + qdf_event_destroy(&refill_thread->start_event); + qdf_event_destroy(&refill_thread->suspend_event); + qdf_event_destroy(&refill_thread->resume_event); + qdf_event_destroy(&refill_thread->shutdown_event); + + refill_thread->state = DP_RX_REFILL_THREAD_INVALID; + return QDF_STATUS_SUCCESS; +} + QDF_STATUS dp_rx_tm_init(struct dp_rx_tm_handle *rx_tm_hdl, uint8_t num_dp_rx_threads) { @@ -805,6 +940,50 @@ suspend_fail: return qdf_status; } +/** + * dp_rx_refill_thread_suspend() - Suspend DP RX refill threads + * @refill_thread: containing the overall refill thread infrastructure + * + * Return: Success/Failure + */ +QDF_STATUS +dp_rx_refill_thread_suspend(struct dp_rx_refill_thread *refill_thread) +{ + QDF_STATUS qdf_status; + + if (refill_thread->state == DP_RX_REFILL_THREAD_SUSPENDED) { + dp_info("already in suspend state! Ignoring."); + return QDF_STATUS_E_INVAL; + } + + refill_thread->state = DP_RX_REFILL_THREAD_SUSPENDING; + + qdf_event_reset(&refill_thread->resume_event); + qdf_event_reset(&refill_thread->suspend_event); + qdf_set_bit(RX_REFILL_SUSPEND_EVENT, + &refill_thread->event_flag); + qdf_wake_up_interruptible(&refill_thread->wait_q); + + qdf_status = qdf_wait_single_event(&refill_thread->suspend_event, + DP_RX_THREAD_WAIT_TIMEOUT); + if (QDF_IS_STATUS_SUCCESS(qdf_status)) + dp_debug("Refill thread suspended"); + else + goto suspend_fail; + + refill_thread->state = DP_RX_REFILL_THREAD_SUSPENDED; + return QDF_STATUS_SUCCESS; + +suspend_fail: + dp_err("Refill thread %s(%d) while waiting for suspend", + qdf_status == QDF_STATUS_E_TIMEOUT ? "timeout out" : "failed", + qdf_status); + + dp_rx_refill_thread_resume(refill_thread); + + return qdf_status; +} + /** * dp_rx_thread_flush_by_vdev_id() - flush rx packets by vdev_id in a particular rx thread queue @@ -920,6 +1099,35 @@ QDF_STATUS dp_rx_tm_resume(struct dp_rx_tm_handle *rx_tm_hdl) return QDF_STATUS_SUCCESS; } +/** + * dp_rx_refill_thread_resume() - Resume DP RX refill threads + * @refill_thread: refill_thread containing the overall thread infrastructure + * + * Return: QDF_STATUS_SUCCESS on resume success. QDF error otherwise. + */ +QDF_STATUS dp_rx_refill_thread_resume(struct dp_rx_refill_thread *refill_thread) +{ + dp_debug("calling refill thread to resume"); + + if (refill_thread->state != DP_RX_REFILL_THREAD_SUSPENDED && + refill_thread->state != DP_RX_REFILL_THREAD_SUSPENDING) { + dp_info("resume callback received in %d state ! Ignoring.", + refill_thread->state); + return QDF_STATUS_E_INVAL; + } + + /* postively reset event_flag for DP_RX_REFILL_THREAD_SUSPENDING + * state + */ + qdf_clear_bit(RX_REFILL_SUSPEND_EVENT, + &refill_thread->event_flag); + qdf_event_set(&refill_thread->resume_event); + + refill_thread->state = DP_RX_REFILL_THREAD_RUNNING; + + return QDF_STATUS_SUCCESS; +} + /** * dp_rx_tm_shutdown() - shutdown all DP RX threads * @rx_tm_hdl: dp_rx_tm_handle containing the overall thread infrastructure diff --git a/core/dp/txrx3.0/dp_rx_thread.h b/core/dp/txrx3.0/dp_rx_thread.h index 24a020b37202..e4b73de83cd2 100644 --- a/core/dp/txrx3.0/dp_rx_thread.h +++ b/core/dp/txrx3.0/dp_rx_thread.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2021 The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -70,6 +70,21 @@ struct dp_rx_thread_stats { unsigned int dropped_enq_fail; }; +/** + * enum dp_rx_refill_thread_state - enum to keep track of rx refill thread state + * @DP_RX_REFILL_THREAD_INVALID: initial invalid state + * @DP_RX_REFILL_THREAD_RUNNING: rx refill thread functional(NOT suspended, + * processing packets or waiting on a wait_queue) + * @DP_RX_REFILL_THREAD_SUSPENDING: rx refill thread is suspending + * @DP_RX_REFILL_THREAD_SUSPENDED: rx refill_thread suspended + */ +enum dp_rx_refill_thread_state { + DP_RX_REFILL_THREAD_INVALID, + DP_RX_REFILL_THREAD_RUNNING, + DP_RX_REFILL_THREAD_SUSPENDING, + DP_RX_REFILL_THREAD_SUSPENDED +}; + /** * struct dp_rx_thread - structure holding variables for a single DP RX thread * @id: id of the dp_rx_thread (0 or 1 or 2..DP_MAX_RX_THREADS - 1) @@ -109,6 +124,32 @@ struct dp_rx_thread { struct net_device netdev; }; +/** + * struct dp_rx_refill_thread - structure holding info of DP Rx refill thread + * @task: task structure corresponding to the thread + * @start_event: handle of Event for DP Rx refill thread to signal startup + * @suspend_event: handle of Event for DP Rx refill thread to signal suspend + * @resume_event: handle of Event for DP Rx refill thread to signal resume + * @shutdown_event: handle of Event for DP Rx refill thread to signal shutdown + * @event_flag: event flag to post events to DP Rx refill thread + * @wait_q: wait queue to conditionally wait on events for DP Rx refill thread + * @enabled: flag to check whether DP Rx refill thread is enabled + * @soc: abstract DP soc reference used in internal API's + * @state: state of DP Rx refill thread + */ +struct dp_rx_refill_thread { + qdf_thread_t *task; + qdf_event_t start_event; + qdf_event_t suspend_event; + qdf_event_t resume_event; + qdf_event_t shutdown_event; + unsigned long event_flag; + qdf_wait_queue_head_t wait_q; + bool enabled; + void *soc; + enum dp_rx_refill_thread_state state; +}; + /** * enum dp_rx_thread_state - enum to keep track of the state of the rx threads * @DP_RX_THREADS_INVALID: initial invalid state @@ -152,6 +193,23 @@ enum dp_rx_gro_flush_code { DP_RX_GRO_LOW_TPUT_FLUSH }; +/** + * dp_rx_refill_thread_init() - Initialize DP Rx refill threads + * @refill_thread: Contains over all rx refill thread info + * + * Return: QDF_STATUS + */ +QDF_STATUS dp_rx_refill_thread_init(struct dp_rx_refill_thread *refill_thread); + +/** + * dp_rx_refill_thread_deinit() - De-initialize DP Rx refill threads + * @refill_thread: Contains over all rx refill thread info + * + * Return: QDF_STATUS + */ +QDF_STATUS +dp_rx_refill_thread_deinit(struct dp_rx_refill_thread *refill_thread); + /** * dp_rx_tm_init() - initialize DP Rx thread infrastructure * @rx_tm_hdl: dp_rx_tm_handle containing the overall thread infrastructure @@ -191,6 +249,14 @@ QDF_STATUS dp_rx_tm_enqueue_pkt(struct dp_rx_tm_handle *rx_tm_hdl, QDF_STATUS dp_rx_tm_gro_flush_ind(struct dp_rx_tm_handle *rx_tm_handle, int rx_ctx_id, enum dp_rx_gro_flush_code flush_code); +/** + * dp_rx_refill_thread_suspend() - Suspend RX refill thread + * @refill_thread: pointer to dp_rx_refill_thread object + * + * Return: QDF_STATUS_SUCCESS on success, error qdf status on failure + */ +QDF_STATUS +dp_rx_refill_thread_suspend(struct dp_rx_refill_thread *refill_thread); /** * dp_rx_tm_suspend() - suspend all threads in RXTI @@ -212,6 +278,15 @@ QDF_STATUS dp_rx_tm_suspend(struct dp_rx_tm_handle *rx_tm_handle); QDF_STATUS dp_rx_tm_flush_by_vdev_id(struct dp_rx_tm_handle *rx_tm_hdl, uint8_t vdev_id); +/** + * dp_rx_refill_thread_resume() - Resume RX refill thread + * @refill_thread: pointer to dp_rx_refill_thread + * + * Return: QDF_STATUS_SUCCESS on success, error qdf status on failure + */ +QDF_STATUS +dp_rx_refill_thread_resume(struct dp_rx_refill_thread *refill_thread); + /** * dp_rx_tm_resume() - resume all threads in RXTI * @rx_tm_handle: pointer to dp_rx_tm_handle object diff --git a/core/dp/txrx3.0/dp_txrx.c b/core/dp/txrx3.0/dp_txrx.c index 1120899f6fa9..55bcfdd7ef9f 100644 --- a/core/dp/txrx3.0/dp_txrx.c +++ b/core/dp/txrx3.0/dp_txrx.c @@ -26,6 +26,35 @@ #include #include #include +#include + +/** + * dp_rx_refill_thread_schedule() - Schedule rx refill thread + * @soc: ol_txrx_soc_handle object + * + */ +#ifdef WLAN_FEATURE_RX_PREALLOC_BUFFER_POOL +static void dp_rx_refill_thread_schedule(ol_txrx_soc_handle soc) +{ + struct dp_rx_refill_thread *rx_thread; + struct dp_txrx_handle *dp_ext_hdl; + + if (!soc) + return; + + dp_ext_hdl = cdp_soc_get_dp_txrx_handle(soc); + if (!dp_ext_hdl) + return; + + rx_thread = &dp_ext_hdl->refill_thread; + qdf_set_bit(RX_REFILL_POST_EVENT, &rx_thread->event_flag); + qdf_wake_up_interruptible(&rx_thread->wait_q); +} +#else +static void dp_rx_refill_thread_schedule(ol_txrx_soc_handle soc) +{ +} +#endif QDF_STATUS dp_txrx_init(ol_txrx_soc_handle soc, uint8_t pdev_id, struct dp_txrx_config *config) @@ -34,6 +63,7 @@ QDF_STATUS dp_txrx_init(ol_txrx_soc_handle soc, uint8_t pdev_id, QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; uint8_t num_dp_rx_threads; struct dp_pdev *pdev; + struct dp_soc *dp_soc; if (qdf_unlikely(!soc)) { dp_err("soc is NULL"); @@ -61,6 +91,21 @@ QDF_STATUS dp_txrx_init(ol_txrx_soc_handle soc, uint8_t pdev_id, dp_ext_hdl->rx_tm_hdl.txrx_handle_cmn = dp_txrx_get_cmn_hdl_frm_ext_hdl(dp_ext_hdl); + dp_soc = cdp_soc_t_to_dp_soc(soc); + if (wlan_cfg_is_rx_refill_buffer_pool_enabled(dp_soc->wlan_cfg_ctx)) { + dp_ext_hdl->refill_thread.soc = soc; + dp_ext_hdl->refill_thread.enabled = true; + qdf_status = + dp_rx_refill_thread_init(&dp_ext_hdl->refill_thread); + if (qdf_status != QDF_STATUS_SUCCESS) { + dp_err("Failed to initialize RX refill thread status:%d", + qdf_status); + return qdf_status; + } + cdp_register_rx_refill_thread_sched_handler(soc, + dp_rx_refill_thread_schedule); + } + num_dp_rx_threads = cdp_get_num_rx_contexts(soc); if (dp_ext_hdl->config.enable_rx_threads) { @@ -74,6 +119,7 @@ QDF_STATUS dp_txrx_init(ol_txrx_soc_handle soc, uint8_t pdev_id, QDF_STATUS dp_txrx_deinit(ol_txrx_soc_handle soc) { struct dp_txrx_handle *dp_ext_hdl; + struct dp_soc *dp_soc; if (!soc) return QDF_STATUS_E_INVAL; @@ -82,6 +128,13 @@ QDF_STATUS dp_txrx_deinit(ol_txrx_soc_handle soc) if (!dp_ext_hdl) return QDF_STATUS_E_FAULT; + dp_soc = cdp_soc_t_to_dp_soc(soc); + if (wlan_cfg_is_rx_refill_buffer_pool_enabled(dp_soc->wlan_cfg_ctx)) { + dp_rx_refill_thread_deinit(&dp_ext_hdl->refill_thread); + dp_ext_hdl->refill_thread.soc = NULL; + dp_ext_hdl->refill_thread.enabled = false; + } + if (dp_ext_hdl->config.enable_rx_threads) dp_rx_tm_deinit(&dp_ext_hdl->rx_tm_hdl); diff --git a/core/dp/txrx3.0/dp_txrx.h b/core/dp/txrx3.0/dp_txrx.h index e8b49f5cf52c..78c69892f22c 100644 --- a/core/dp/txrx3.0/dp_txrx.h +++ b/core/dp/txrx3.0/dp_txrx.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2021 The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -37,12 +37,14 @@ struct dp_txrx_handle_cmn; /** * struct dp_txrx_handle - main dp txrx container handle * @soc: ol_txrx_soc_handle soc handle + * @refill_thread: rx refill thread infra handle * @rx_tm_hdl: rx thread infrastructure handle */ struct dp_txrx_handle { ol_txrx_soc_handle soc; struct cdp_pdev *pdev; struct dp_rx_tm_handle rx_tm_hdl; + struct dp_rx_refill_thread refill_thread; struct dp_txrx_config config; }; @@ -187,6 +189,7 @@ static inline QDF_STATUS dp_txrx_resume(ol_txrx_soc_handle soc) { struct dp_txrx_handle *dp_ext_hdl; QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + struct dp_rx_refill_thread *refill_thread; if (!soc) { qdf_status = QDF_STATUS_E_INVAL; @@ -199,6 +202,13 @@ static inline QDF_STATUS dp_txrx_resume(ol_txrx_soc_handle soc) goto ret; } + refill_thread = &dp_ext_hdl->refill_thread; + if (refill_thread->enabled) { + qdf_status = dp_rx_refill_thread_resume(refill_thread); + if (qdf_status != QDF_STATUS_SUCCESS) + return qdf_status; + } + qdf_status = dp_rx_tm_resume(&dp_ext_hdl->rx_tm_hdl); ret: return qdf_status; @@ -214,6 +224,7 @@ static inline QDF_STATUS dp_txrx_suspend(ol_txrx_soc_handle soc) { struct dp_txrx_handle *dp_ext_hdl; QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; + struct dp_rx_refill_thread *refill_thread; if (!soc) { qdf_status = QDF_STATUS_E_INVAL; @@ -226,6 +237,13 @@ static inline QDF_STATUS dp_txrx_suspend(ol_txrx_soc_handle soc) goto ret; } + refill_thread = &dp_ext_hdl->refill_thread; + if (refill_thread->enabled) { + qdf_status = dp_rx_refill_thread_suspend(refill_thread); + if (qdf_status != QDF_STATUS_SUCCESS) + return qdf_status; + } + qdf_status = dp_rx_tm_suspend(&dp_ext_hdl->rx_tm_hdl); ret: