blob: ce097963afe3c9471b378c8970c7e5ddd5a013ef [file] [log] [blame]
/*
* Copyright (c) 2012-2018 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*
* This file was originally distributed by Qualcomm Atheros, Inc.
* under proprietary terms before Copyright ownership was assigned
* to the Linux Foundation.
*/
/*===========================================================================
@file vos_sched.c
@brief VOS Scheduler Implementation
===========================================================================*/
/*===========================================================================
EDIT HISTORY FOR FILE
This section contains comments describing changes made to the module.
Notice that changes are listed in reverse chronological order.
$Header:$ $DateTime: $ $Author: $
when who what, where, why
-------- --- --------------------------------------------------------
===========================================================================*/
/*---------------------------------------------------------------------------
* Include Files
* ------------------------------------------------------------------------*/
#include <vos_mq.h>
#include <vos_api.h>
#include <aniGlobal.h>
#include <sirTypes.h>
#include <halTypes.h>
#include <limApi.h>
#include <sme_Api.h>
#include <wlan_qct_sys.h>
#include <wlan_qct_tl.h>
#include "vos_sched.h"
#include <wlan_hdd_power.h>
#include "wlan_qct_wda.h"
#include <linux/spinlock.h>
#include <linux/kthread.h>
#include <linux/cpu.h>
#include <linux/topology.h>
#include "vos_cnss.h"
/*---------------------------------------------------------------------------
* Preprocessor Definitions and Constants
* ------------------------------------------------------------------------*/
#define VOS_SCHED_THREAD_HEART_BEAT INFINITE
/* Milli seconds to delay SSR thread when an Entry point is Active */
#define SSR_WAIT_SLEEP_TIME 200
#define LOAD_UNLOAD_WAIT_SLEEP_TIME 200
/* MAX iteration count to wait for Entry point to exit before
* we proceed with SSR in WD Thread
*/
#define MAX_SSR_WAIT_ITERATIONS 200
#define MAX_LOAD_UNLOAD_WAIT_ITERATIONS 50
#define MAX_SSR_PROTECT_LOG (16)
/* Timer value for detecting thread stuck issues */
#define THREAD_STUCK_TIMER_VAL 10000 /* 10 seconds */
#define THREAD_STUCK_THRESHOLD 3
static atomic_t ssr_protect_entry_count;
static atomic_t load_unload_protect_count;
struct ssr_protect {
const char* func;
bool free;
uint32_t pid;
};
static spinlock_t ssr_protect_lock;
static struct ssr_protect ssr_protect_log[MAX_SSR_PROTECT_LOG];
/*---------------------------------------------------------------------------
* Type Declarations
* ------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------
* Data definitions
* ------------------------------------------------------------------------*/
static pVosSchedContext gpVosSchedContext;
static pVosWatchdogContext gpVosWatchdogContext;
/*---------------------------------------------------------------------------
* Forward declaration
* ------------------------------------------------------------------------*/
static int VosMCThread(void *Arg);
static int VosWDThread(void *Arg);
#ifdef QCA_CONFIG_SMP
static int VosTlshimRxThread(void *arg);
static unsigned long affine_cpu = 0;
static VOS_STATUS vos_alloc_tlshim_pkt_freeq(pVosSchedContext pSchedContext);
#endif
extern v_VOID_t vos_core_return_msg(v_PVOID_t pVContext, pVosMsgWrapper pMsgWrapper);
#ifdef QCA_CONFIG_SMP
/*Maximum 2 clusters supported*/
#define VOS_MAX_CPU_CLUSTERS 2
#define VOS_CPU_CLUSTER_TYPE_LITTLE 0
#define VOS_CPU_CLUSTER_TYPE_PERF 1
/**
* vos_sched_find_attach_cpu - find available cores and attach to required core
* @pSchedContext: wlan scheduler context
* @high_throughput: high throughput is required or not
*
* Find current online cores.
* high troughput required and PERF core online, then attach to last PERF core
* low throughput required or only little cores online, the attach any little
* core
*
* Return: 0 success
* 1 fail
*/
static int vos_sched_find_attach_cpu(pVosSchedContext pSchedContext,
bool high_throughput)
{
unsigned long *online_perf_cpu = NULL;
unsigned long *online_litl_cpu = NULL;
unsigned char perf_core_count = 0;
unsigned char litl_core_count = 0;
int vosMaxClusterId = 0;
#if defined(WLAN_OPEN_SOURCE) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0))
struct cpumask litl_mask;
unsigned long cpus;
int i;
#endif
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO_LOW,
"%s: num possible cpu %d",
__func__, num_possible_cpus());
online_perf_cpu = vos_mem_malloc(
num_possible_cpus() * sizeof(unsigned long));
if (!online_perf_cpu) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: perf cpu cache alloc fail", __func__);
return 1;
}
vos_mem_zero(online_perf_cpu,
num_possible_cpus() * sizeof(unsigned long));
online_litl_cpu = vos_mem_malloc(
num_possible_cpus() * sizeof(unsigned long));
if (!online_litl_cpu) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: lttl cpu cache alloc fail", __func__);
vos_mem_free(online_perf_cpu);
return 1;
}
vos_mem_zero(online_litl_cpu,
num_possible_cpus() * sizeof(unsigned long));
/* Get Online perf CPU count */
#if defined(WLAN_OPEN_SOURCE) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0))
for_each_online_cpu(cpus) {
if ( topology_physical_package_id(cpus) > VOS_MAX_CPU_CLUSTERS) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: can handle max %d clusters, returning...",
__func__, VOS_MAX_CPU_CLUSTERS);
goto err;
}
if (topology_physical_package_id(cpus) == VOS_CPU_CLUSTER_TYPE_PERF) {
online_perf_cpu[perf_core_count] = cpus;
perf_core_count++;
} else {
online_litl_cpu[litl_core_count] = cpus;
litl_core_count++;
}
vosMaxClusterId = topology_physical_package_id(cpus);
}
#else
vosMaxClusterId = 0;
#endif
/* Single cluster system, not need to handle this */
if (0 == vosMaxClusterId) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO_LOW,
"%s: single cluster system. returning", __func__);
goto success;
}
if ((!litl_core_count) && (!perf_core_count)) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: Both Cluster off, do nothing", __func__);
goto success;
}
if ((high_throughput && perf_core_count) || (!litl_core_count)) {
/* Attach RX thread to PERF CPU */
if (pSchedContext->rx_thread_cpu !=
online_perf_cpu[perf_core_count - 1]) {
if (vos_set_cpus_allowed_ptr(
pSchedContext->TlshimRxThread,
online_perf_cpu[perf_core_count - 1])) {
VOS_TRACE(VOS_MODULE_ID_VOSS,
VOS_TRACE_LEVEL_ERROR,
"%s: rx thread perf core set fail",
__func__);
goto err;
}
pSchedContext->rx_thread_cpu =
online_perf_cpu[perf_core_count - 1];
}
} else {
#if defined(WLAN_OPEN_SOURCE) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0))
/* Attach to any little core
* Final decision should made by scheduler */
cpumask_clear(&litl_mask);
for (i = 0;
i < litl_core_count;
i++) {
cpumask_set_cpu(online_litl_cpu[i], &litl_mask);
}
set_cpus_allowed_ptr(pSchedContext->TlshimRxThread, &litl_mask);
pSchedContext->rx_thread_cpu = 0;
#else
/* Attach RX thread to last little core CPU */
if (pSchedContext->rx_thread_cpu !=
online_litl_cpu[litl_core_count - 1]) {
if (vos_set_cpus_allowed_ptr(
pSchedContext->TlshimRxThread,
online_litl_cpu[litl_core_count - 1])) {
VOS_TRACE(VOS_MODULE_ID_VOSS,
VOS_TRACE_LEVEL_ERROR,
"%s: rx thread litl core set fail",
__func__);
goto err;
}
pSchedContext->rx_thread_cpu =
online_litl_cpu[litl_core_count - 1];
}
#endif
}
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO,
"%s: NUM PERF CORE %d, HIGH TPUTR REQ %d, RX THRE CPU %lu",
__func__, perf_core_count,
(int)pSchedContext->high_throughput_required,
pSchedContext->rx_thread_cpu);
success:
vos_mem_free(online_perf_cpu);
vos_mem_free(online_litl_cpu);
return 0;
err:
vos_mem_free(online_perf_cpu);
vos_mem_free(online_litl_cpu);
return 1;
}
/**
* vos_sched_handle_cpu_hot_plug - cpu hotplug event handler
*
* cpu hotplug indication handler
* will find online cores and will assign proper core based on perf requirement
*
* Return: 0 success
* 1 fail
*/
int vos_sched_handle_cpu_hot_plug(void)
{
pVosSchedContext pSchedContext = get_vos_sched_ctxt();
if (!pSchedContext) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: invalid context", __func__);
return 1;
}
if (vos_is_load_unload_in_progress(VOS_MODULE_ID_VOSS, NULL) ||
(vos_is_logp_in_progress(VOS_MODULE_ID_VOSS, NULL)))
return 0;
vos_lock_acquire(&pSchedContext->affinity_lock);
if (vos_sched_find_attach_cpu(pSchedContext,
pSchedContext->high_throughput_required)) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: handle hot plug fail", __func__);
vos_lock_release(&pSchedContext->affinity_lock);
return 1;
}
vos_lock_release(&pSchedContext->affinity_lock);
return 0;
}
/**
* vos_sched_handle_throughput_req - cpu throughput requirement handler
* @high_tput_required: high throughput is required or not
*
* high or low throughput indication ahndler
* will find online cores and will assign proper core based on perf requirement
*
* Return: 0 success
* 1 fail
*/
int vos_sched_handle_throughput_req(bool high_tput_required)
{
pVosSchedContext pSchedContext = get_vos_sched_ctxt();
if (!pSchedContext) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: invalid context", __func__);
return 1;
}
if (vos_is_load_unload_in_progress(VOS_MODULE_ID_VOSS, NULL) ||
(vos_is_logp_in_progress(VOS_MODULE_ID_VOSS, NULL)))
return 0;
vos_lock_acquire(&pSchedContext->affinity_lock);
pSchedContext->high_throughput_required = high_tput_required;
if (vos_sched_find_attach_cpu(pSchedContext, high_tput_required)) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: handle throughput req fail", __func__);
vos_lock_release(&pSchedContext->affinity_lock);
return 1;
}
vos_lock_release(&pSchedContext->affinity_lock);
return 0;
}
/**
* __vos_cpu_hotplug_notify - cpu core on-off notification handler
* @block: notifier block
* @state: state of core
* @hcpu: target cpu core
*
* pre-registered core status change notify callback function
* will handle only ONLINE, OFFLINE notification
* based on cpu architecture, rx thread affinity will be different
*
* Return: 0 success
* 1 fail
*/
static int __vos_cpu_hotplug_notify(struct notifier_block *block,
unsigned long state, void *hcpu)
{
unsigned long cpu = (unsigned long) hcpu;
unsigned long pref_cpu = 0;
pVosSchedContext pSchedContext = get_vos_sched_ctxt();
int i;
unsigned int multi_cluster;
unsigned int num_cpus;
#if defined(WLAN_OPEN_SOURCE) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0))
int cpus;
#endif
if ((NULL == pSchedContext) || (NULL == pSchedContext->TlshimRxThread))
return NOTIFY_OK;
if (vos_is_load_unload_in_progress(VOS_MODULE_ID_VOSS, NULL) ||
(vos_is_logp_in_progress(VOS_MODULE_ID_VOSS, NULL)))
return NOTIFY_OK;
num_cpus = num_possible_cpus();
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO_LOW,
"%s: RX CORE %d, STATE %d, NUM CPUS %d",
__func__, (int)affine_cpu, (int)state, num_cpus);
multi_cluster = 0;
#if defined(WLAN_OPEN_SOURCE) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0))
for_each_online_cpu(cpus) {
multi_cluster = topology_physical_package_id(cpus);
}
#endif
if ((multi_cluster) &&
((CPU_ONLINE == state) || (CPU_DEAD == state))) {
vos_sched_handle_cpu_hot_plug();
return NOTIFY_OK;
}
switch (state) {
case CPU_ONLINE:
if (affine_cpu != 0)
return NOTIFY_OK;
for_each_online_cpu(i) {
if (i == 0)
continue;
pref_cpu = i;
break;
}
break;
case CPU_DEAD:
if (cpu != affine_cpu)
return NOTIFY_OK;
affine_cpu = 0;
for_each_online_cpu(i) {
if (i == 0)
continue;
pref_cpu = i;
break;
}
}
if (pref_cpu == 0)
return NOTIFY_OK;
if (!vos_set_cpus_allowed_ptr(pSchedContext->TlshimRxThread, pref_cpu))
affine_cpu = pref_cpu;
return NOTIFY_OK;
}
/**
* vos_cpu_hotplug_notify - cpu core on-off notification handler wrapper
* @block: notifier block
* @state: state of core
* @hcpu: target cpu core
*
* pre-registered core status change notify callback function
* will handle only ONLINE, OFFLINE notification
* based on cpu architecture, rx thread affinity will be different
* wrapper function
*
* Return: 0 success
* 1 fail
*/
static int vos_cpu_hotplug_notify(struct notifier_block *block,
unsigned long state, void *hcpu)
{
int ret;
vos_ssr_protect(__func__);
ret = __vos_cpu_hotplug_notify(block, state, hcpu);
vos_ssr_unprotect(__func__);
return ret;
}
static struct notifier_block vos_cpu_hotplug_notifier = {
.notifier_call = vos_cpu_hotplug_notify,
};
#endif
/*---------------------------------------------------------------------------
* External Function implementation
* ------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------
\brief vos_sched_open() - initialize the vOSS Scheduler
The \a vos_sched_open() function initializes the vOSS Scheduler
Upon successful initialization:
- All the message queues are initialized
- The Main Controller thread is created and ready to receive and
dispatch messages.
- The Tx thread is created and ready to receive and dispatch messages
\param pVosContext - pointer to the global vOSS Context
\param pVosSchedContext - pointer to a previously allocated buffer big
enough to hold a scheduler context.
\return VOS_STATUS_SUCCESS - Scheduler was successfully initialized and
is ready to be used.
VOS_STATUS_E_RESOURCES - System resources (other than memory)
are unavailable to initilize the scheduler
VOS_STATUS_E_NOMEM - insufficient memory exists to initialize
the scheduler
VOS_STATUS_E_INVAL - Invalid parameter passed to the scheduler Open
function
VOS_STATUS_E_FAILURE - Failure to initialize the scheduler/
\sa vos_sched_open()
-------------------------------------------------------------------------*/
VOS_STATUS
vos_sched_open
(
v_PVOID_t pVosContext,
pVosSchedContext pSchedContext,
v_SIZE_t SchedCtxSize
)
{
VOS_STATUS vStatus = VOS_STATUS_SUCCESS;
#ifdef CONFIG_PERF_NON_QC_PLATFORM
struct sched_param param = {.sched_priority = 99};
#endif
/*-------------------------------------------------------------------------*/
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: Opening the VOSS Scheduler",__func__);
// Sanity checks
if ((pVosContext == NULL) || (pSchedContext == NULL)) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: Null params being passed",__func__);
return VOS_STATUS_E_FAILURE;
}
if (sizeof(VosSchedContext) != SchedCtxSize)
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: Incorrect VOS Sched Context size passed",__func__);
return VOS_STATUS_E_INVAL;
}
vos_mem_zero(pSchedContext, sizeof(VosSchedContext));
pSchedContext->pVContext = pVosContext;
vStatus = vos_sched_init_mqs(pSchedContext);
if (!VOS_IS_STATUS_SUCCESS(vStatus))
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: Failed to initialize VOS Scheduler MQs",__func__);
return vStatus;
}
// Initialize the helper events and event queues
init_completion(&pSchedContext->McStartEvent);
init_completion(&pSchedContext->McShutdown);
init_completion(&pSchedContext->ResumeMcEvent);
spin_lock_init(&pSchedContext->McThreadLock);
#ifdef QCA_CONFIG_SMP
spin_lock_init(&pSchedContext->TlshimRxThreadLock);
#endif
init_waitqueue_head(&pSchedContext->mcWaitQueue);
pSchedContext->mcEventFlag = 0;
#ifdef QCA_CONFIG_SMP
init_waitqueue_head(&pSchedContext->tlshimRxWaitQueue);
init_completion(&pSchedContext->TlshimRxStartEvent);
init_completion(&pSchedContext->SuspndTlshimRxEvent);
init_completion(&pSchedContext->ResumeTlshimRxEvent);
init_completion(&pSchedContext->TlshimRxShutdown);
pSchedContext->tlshimRxEvtFlg = 0;
spin_lock_init(&pSchedContext->TlshimRxQLock);
spin_lock_init(&pSchedContext->VosTlshimPktFreeQLock);
INIT_LIST_HEAD(&pSchedContext->tlshimRxQueue);
SPIN_LOCK_BH(&pSchedContext->VosTlshimPktFreeQLock);
INIT_LIST_HEAD(&pSchedContext->VosTlshimPktFreeQ);
if (vos_alloc_tlshim_pkt_freeq(pSchedContext) != VOS_STATUS_SUCCESS)
{
SPIN_UNLOCK_BH(&pSchedContext->VosTlshimPktFreeQLock);
return VOS_STATUS_E_FAILURE;
}
SPIN_UNLOCK_BH(&pSchedContext->VosTlshimPktFreeQLock);
register_hotcpu_notifier(&vos_cpu_hotplug_notifier);
pSchedContext->cpuHotPlugNotifier = &vos_cpu_hotplug_notifier;
vos_lock_init(&pSchedContext->affinity_lock);
pSchedContext->high_throughput_required = false;
#endif
/*
** This initialization is critical as the threads will later access the
** global contexts normally,
**
** I shall put some memory barrier here after the next piece of code but
** I am keeping it simple for now.
*/
gpVosSchedContext = pSchedContext;
//Create the VOSS Main Controller thread
pSchedContext->McThread = kthread_create(VosMCThread, pSchedContext,
"VosMCThread");
if (IS_ERR(pSchedContext->McThread))
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
"%s: Could not Create VOSS Main Thread Controller",__func__);
goto MC_THREAD_START_FAILURE;
}
wake_up_process(pSchedContext->McThread);
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: VOSS Main Controller thread Created",__func__);
#ifdef QCA_CONFIG_SMP
pSchedContext->TlshimRxThread = kthread_create(VosTlshimRxThread,
pSchedContext,
"VosTlshimRxThread");
#ifdef CONFIG_PERF_NON_QC_PLATFORM
sched_setscheduler(pSchedContext->TlshimRxThread, SCHED_FIFO, &param);
#endif
if (IS_ERR(pSchedContext->TlshimRxThread))
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
"%s: Could not Create VOSS Tlshim RX Thread", __func__);
goto TLSHIM_RX_THREAD_START_FAILURE;
}
wake_up_process(pSchedContext->TlshimRxThread);
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO_HIGH,
("VOSS Tlshim RX thread Created"));
#endif
/*
** Now make sure all threads have started before we exit.
** Each thread should normally ACK back when it starts.
*/
wait_for_completion_interruptible(&pSchedContext->McStartEvent);
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: VOSS MC Thread has started",__func__);
#ifdef QCA_CONFIG_SMP
wait_for_completion_interruptible(&pSchedContext->TlshimRxStartEvent);
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: VOSS Tlshim Rx Thread has started", __func__);
#endif
/*
** We're good now: Let's get the ball rolling!!!
*/
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: VOSS Scheduler successfully Opened",__func__);
return VOS_STATUS_SUCCESS;
#ifdef QCA_CONFIG_SMP
TLSHIM_RX_THREAD_START_FAILURE:
#endif
MC_THREAD_START_FAILURE:
//Try and force the Main thread controller to exit
set_bit(MC_SHUTDOWN_EVENT, &pSchedContext->mcEventFlag);
set_bit(MC_POST_EVENT, &pSchedContext->mcEventFlag);
wake_up_interruptible(&pSchedContext->mcWaitQueue);
//Wait for MC to exit
wait_for_completion_interruptible(&pSchedContext->McShutdown);
//De-initialize all the message queues
vos_sched_deinit_mqs(pSchedContext);
#ifdef QCA_CONFIG_SMP
unregister_hotcpu_notifier(&vos_cpu_hotplug_notifier);
vos_free_tlshim_pkt_freeq(gpVosSchedContext);
#endif
return VOS_STATUS_E_RESOURCES;
} /* vos_sched_open() */
VOS_STATUS vos_watchdog_open
(
v_PVOID_t pVosContext,
pVosWatchdogContext pWdContext,
v_SIZE_t wdCtxSize
)
{
/*-------------------------------------------------------------------------*/
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: Opening the VOSS Watchdog module",__func__);
//Sanity checks
if ((pVosContext == NULL) || (pWdContext == NULL)) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: Null params being passed",__func__);
return VOS_STATUS_E_FAILURE;
}
if (sizeof(VosWatchdogContext) != wdCtxSize)
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: Incorrect VOS Watchdog Context size passed",__func__);
return VOS_STATUS_E_INVAL;
}
vos_mem_zero(pWdContext, sizeof(VosWatchdogContext));
pWdContext->pVContext = pVosContext;
gpVosWatchdogContext = pWdContext;
pWdContext->thread_stuck_timer.state = VOS_TIMER_STATE_UNUSED;
//Initialize the helper events and event queues
init_completion(&pWdContext->WdStartEvent);
init_completion(&pWdContext->WdShutdown);
init_waitqueue_head(&pWdContext->wdWaitQueue);
pWdContext->wdEventFlag = 0;
// Initialize the lock
spin_lock_init(&pWdContext->wdLock);
spin_lock_init(&pWdContext->thread_stuck_lock);
//Create the Watchdog thread
pWdContext->WdThread = kthread_create(VosWDThread, pWdContext,"VosWDThread");
if (IS_ERR(pWdContext->WdThread))
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
"%s: Could not Create Watchdog thread",__func__);
return VOS_STATUS_E_RESOURCES;
}
else
{
wake_up_process(pWdContext->WdThread);
}
/*
** Now make sure thread has started before we exit.
** Each thread should normally ACK back when it starts.
*/
wait_for_completion_interruptible(&pWdContext->WdStartEvent);
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: VOSS Watchdog Thread has started",__func__);
return VOS_STATUS_SUCCESS;
} /* vos_watchdog_open() */
/*---------------------------------------------------------------------------
\brief VosMcThread() - The VOSS Main Controller thread
The \a VosMcThread() is the VOSS main controller thread:
\param Arg - pointer to the global vOSS Sched Context
\return Thread exit code
\sa VosMcThread()
-------------------------------------------------------------------------*/
static int
VosMCThread
(
void * Arg
)
{
pVosSchedContext pSchedContext = (pVosSchedContext)Arg;
pVosMsgWrapper pMsgWrapper = NULL;
tpAniSirGlobal pMacContext = NULL;
tSirRetStatus macStatus = eSIR_SUCCESS;
VOS_STATUS vStatus = VOS_STATUS_SUCCESS;
int retWaitStatus = 0;
v_BOOL_t shutdown = VOS_FALSE;
hdd_context_t *pHddCtx = NULL;
v_CONTEXT_t pVosContext = NULL;
if (Arg == NULL)
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: Bad Args passed", __func__);
return 0;
}
set_user_nice(current, -2);
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0))
daemonize("MC_Thread");
#endif
/*
** Ack back to the context from which the main controller thread has been
** created.
*/
complete(&pSchedContext->McStartEvent);
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO,
"%s: MC Thread %d (%s) starting up",__func__, current->pid, current->comm);
/* Get the Global VOSS Context */
pVosContext = vos_get_global_context(VOS_MODULE_ID_SYS, NULL);
if(!pVosContext) {
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: Global VOS context is Null", __func__);
return 0;
}
/* Get the HDD context */
pHddCtx = (hdd_context_t *)vos_get_context(VOS_MODULE_ID_HDD, pVosContext );
if(!pHddCtx) {
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: HDD context is Null",__func__);
return 0;
}
while(!shutdown)
{
// This implements the execution model algorithm
retWaitStatus = wait_event_interruptible(pSchedContext->mcWaitQueue,
test_bit(MC_POST_EVENT, &pSchedContext->mcEventFlag) ||
test_bit(MC_SUSPEND_EVENT, &pSchedContext->mcEventFlag));
if(retWaitStatus == -ERESTARTSYS)
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: wait_event_interruptible returned -ERESTARTSYS", __func__);
VOS_BUG(0);
}
clear_bit(MC_POST_EVENT, &pSchedContext->mcEventFlag);
while(1)
{
// Check if MC needs to shutdown
if(test_bit(MC_SHUTDOWN_EVENT, &pSchedContext->mcEventFlag))
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO,
"%s: MC thread signaled to shutdown", __func__);
shutdown = VOS_TRUE;
/* Check for any Suspend Indication */
if(test_bit(MC_SUSPEND_EVENT, &pSchedContext->mcEventFlag))
{
clear_bit(MC_SUSPEND_EVENT, &pSchedContext->mcEventFlag);
/* Unblock anyone waiting on suspend */
complete(&pHddCtx->mc_sus_event_var);
}
break;
}
// Check the SYS queue first
if (!vos_is_mq_empty(&pSchedContext->sysMcMq))
{
// Service the SYS message queue
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO,
"%s: Servicing the VOS SYS MC Message queue",__func__);
pMsgWrapper = vos_mq_get(&pSchedContext->sysMcMq);
if (pMsgWrapper == NULL)
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: pMsgWrapper is NULL", __func__);
VOS_ASSERT(0);
break;
}
vStatus = sysMcProcessMsg(pSchedContext->pVContext,
pMsgWrapper->pVosMsg);
if (!VOS_IS_STATUS_SUCCESS(vStatus))
{
VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: Issue Processing SYS message",__func__);
}
//return message to the Core
vos_core_return_msg(pSchedContext->pVContext, pMsgWrapper);
continue;
}
// Check the WDA queue
if (!vos_is_mq_empty(&pSchedContext->wdaMcMq))
{
// Service the WDA message queue
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO,
"%s: Servicing the VOS WDA MC Message queue",__func__);
pMsgWrapper = vos_mq_get(&pSchedContext->wdaMcMq);
if (pMsgWrapper == NULL)
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: pMsgWrapper is NULL", __func__);
VOS_ASSERT(0);
break;
}
vStatus = WDA_McProcessMsg( pSchedContext->pVContext, pMsgWrapper->pVosMsg);
if (!VOS_IS_STATUS_SUCCESS(vStatus))
{
VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: Issue Processing WDA message",__func__);
}
// return message to the Core
vos_core_return_msg(pSchedContext->pVContext, pMsgWrapper);
continue;
}
// Check the PE queue
if (!vos_is_mq_empty(&pSchedContext->peMcMq))
{
// Service the PE message queue
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO,
"%s: Servicing the VOS PE MC Message queue",__func__);
pMsgWrapper = vos_mq_get(&pSchedContext->peMcMq);
if (NULL == pMsgWrapper)
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: pMsgWrapper is NULL", __func__);
VOS_ASSERT(0);
break;
}
/* Need some optimization*/
pMacContext = vos_get_context(VOS_MODULE_ID_PE, pSchedContext->pVContext);
if (NULL == pMacContext)
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO,
"MAC Context not ready yet");
vos_core_return_msg(pSchedContext->pVContext, pMsgWrapper);
continue;
}
macStatus = peProcessMessages( pMacContext, (tSirMsgQ*)pMsgWrapper->pVosMsg);
if (eSIR_SUCCESS != macStatus)
{
VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: Issue Processing PE message",__func__);
}
// return message to the Core
vos_core_return_msg(pSchedContext->pVContext, pMsgWrapper);
continue;
}
/** Check the SME queue **/
if (!vos_is_mq_empty(&pSchedContext->smeMcMq))
{
/* Service the SME message queue */
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO,
"%s: Servicing the VOS SME MC Message queue",__func__);
pMsgWrapper = vos_mq_get(&pSchedContext->smeMcMq);
if (NULL == pMsgWrapper)
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: pMsgWrapper is NULL", __func__);
VOS_ASSERT(0);
break;
}
/* Need some optimization*/
pMacContext = vos_get_context(VOS_MODULE_ID_SME, pSchedContext->pVContext);
if (NULL == pMacContext)
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO,
"MAC Context not ready yet");
vos_core_return_msg(pSchedContext->pVContext, pMsgWrapper);
continue;
}
vStatus = sme_ProcessMsg( (tHalHandle)pMacContext, pMsgWrapper->pVosMsg);
if (!VOS_IS_STATUS_SUCCESS(vStatus))
{
VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: Issue Processing SME message",__func__);
}
// return message to the Core
vos_core_return_msg(pSchedContext->pVContext, pMsgWrapper);
continue;
}
/** Check the TL queue **/
if (!vos_is_mq_empty(&pSchedContext->tlMcMq))
{
// Service the TL message queue
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO,
("Servicing the VOS TL MC Message queue"));
pMsgWrapper = vos_mq_get(&pSchedContext->tlMcMq);
if (pMsgWrapper == NULL)
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: pMsgWrapper is NULL", __func__);
VOS_ASSERT(0);
break;
}
vStatus = WLANTL_McProcessMsg( pSchedContext->pVContext,
pMsgWrapper->pVosMsg);
if (!VOS_IS_STATUS_SUCCESS(vStatus))
{
VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: Issue Processing TL message",__func__);
}
// return message to the Core
vos_core_return_msg(pSchedContext->pVContext, pMsgWrapper);
continue;
}
/* Check for any Suspend Indication */
if(test_bit(MC_SUSPEND_EVENT, &pSchedContext->mcEventFlag))
{
clear_bit(MC_SUSPEND_EVENT, &pSchedContext->mcEventFlag);
spin_lock(&pSchedContext->McThreadLock);
INIT_COMPLETION(pSchedContext->ResumeMcEvent);
/* Mc Thread Suspended */
complete(&pHddCtx->mc_sus_event_var);
spin_unlock(&pSchedContext->McThreadLock);
/* Wait foe Resume Indication */
wait_for_completion_interruptible(&pSchedContext->ResumeMcEvent);
}
break; //All queues are empty now
} // while message loop processing
} // while TRUE
// If we get here the MC thread must exit
VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: MC Thread exiting!!!!", __func__);
complete_and_exit(&pSchedContext->McShutdown, 0);
} /* VosMCThread() */
v_BOOL_t isWDresetInProgress(void)
{
if(gpVosWatchdogContext!=NULL)
{
return gpVosWatchdogContext->resetInProgress;
}
else
{
return FALSE;
}
}
/**
* vos_wd_detect_thread_stuck()- Detect thread stuck
* by probing MC thread and take action if Thread doesnt respond.
*
* This function is called to detect thread stuck
* and probe threads.
*
* Return: void
*/
static void vos_wd_detect_thread_stuck(void)
{
unsigned long flags;
spin_lock_irqsave(&gpVosWatchdogContext->thread_stuck_lock, flags);
if (gpVosWatchdogContext->mc_thread_stuck_count ==
THREAD_STUCK_THRESHOLD) {
spin_unlock_irqrestore(&gpVosWatchdogContext->thread_stuck_lock,
flags);
hddLog(LOGE, FL("MC Thread Stuck!!!"));
vos_dump_stack(gpVosSchedContext->McThread);
vos_flush_logs(WLAN_LOG_TYPE_FATAL,
WLAN_LOG_INDICATOR_HOST_ONLY,
WLAN_LOG_REASON_THREAD_STUCK,
DUMP_VOS_TRACE);
spin_lock_irqsave(&gpVosWatchdogContext->thread_stuck_lock,
flags);
}
/* Increment the thread stuck count for all threads */
gpVosWatchdogContext->mc_thread_stuck_count++;
if (gpVosWatchdogContext->mc_thread_stuck_count <=
THREAD_STUCK_THRESHOLD) {
spin_unlock_irqrestore(&gpVosWatchdogContext->thread_stuck_lock,
flags);
vos_probe_threads();
} else
spin_unlock_irqrestore(&gpVosWatchdogContext->thread_stuck_lock,
flags);
/* Restart the timer */
if (VOS_STATUS_SUCCESS !=
vos_timer_start(&gpVosWatchdogContext->thread_stuck_timer,
THREAD_STUCK_TIMER_VAL))
hddLog(LOGE, FL("Unable to start thread stuck timer"));
}
/**
* vos_wd_detect_thread_stuck_cb()- Call back of the
* thread stuck timer.
* @priv: timer data.
* This function is called when the thread stuck timer
* expire to detect thread stuck and probe threads.
*
* Return: void
*/
static void vos_wd_detect_thread_stuck_cb(void *priv)
{
if (!(vos_is_logp_in_progress(VOS_MODULE_ID_SYS, NULL) ||
vos_is_load_unload_in_progress(VOS_MODULE_ID_SYS
, NULL))) {
set_bit(WD_WLAN_DETECT_THREAD_STUCK,
&gpVosWatchdogContext->wdEventFlag);
set_bit(WD_POST_EVENT, &gpVosWatchdogContext->wdEventFlag);
wake_up_interruptible(&gpVosWatchdogContext->wdWaitQueue);
}
}
/**
* vos_thread_stuck_timer_init - Initialize thread stuck timer
*
* @wd_ctx: watchdog context.
*
* Return: void
*/
void vos_thread_stuck_timer_init(pVosWatchdogContext wd_ctx)
{
if (vos_timer_init(&wd_ctx->thread_stuck_timer,
VOS_TIMER_TYPE_SW,
vos_wd_detect_thread_stuck_cb, NULL))
hddLog(LOGE, FL("Unable to initialize thread stuck timer"));
else
{
if (VOS_STATUS_SUCCESS !=
vos_timer_start(&wd_ctx->thread_stuck_timer,
THREAD_STUCK_TIMER_VAL))
hddLog(LOGE, FL("Unable to start thread stuck timer"));
else
hddLog(LOG1, FL("Successfully started thread stuck timer"));
}
}
/**
* vos_wd_reset_thread_stuck_count()- Callback to
* probe msg sent to Threads.
*
* @thread_id: passed threadid
*
* This function is called to by the thread after
* processing the probe msg, with their own thread id.
*
* Return: void.
*/
void vos_wd_reset_thread_stuck_count(int thread_id)
{
unsigned long flags;
spin_lock_irqsave(&gpVosWatchdogContext->thread_stuck_lock, flags);
if (vos_sched_is_mc_thread(thread_id))
gpVosWatchdogContext->mc_thread_stuck_count = 0;
spin_unlock_irqrestore(&gpVosWatchdogContext->thread_stuck_lock, flags);
}
/*---------------------------------------------------------------------------
\brief VosWdThread() - The VOSS Watchdog thread
The \a VosWdThread() is the Watchdog thread:
\param Arg - pointer to the global vOSS Sched Context
\return Thread exit code
\sa VosMcThread()
-------------------------------------------------------------------------*/
static int
VosWDThread
(
void * Arg
)
{
pVosWatchdogContext pWdContext = (pVosWatchdogContext)Arg;
int retWaitStatus = 0;
v_BOOL_t shutdown = VOS_FALSE;
int count = 0;
VOS_STATUS vosStatus = VOS_STATUS_SUCCESS;
set_user_nice(current, -4);
if (Arg == NULL)
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: Bad Args passed", __func__);
return 0;
}
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0))
daemonize("WD_Thread");
#endif
/*
** Ack back to the context from which the Watchdog thread has been
** created.
*/
complete(&pWdContext->WdStartEvent);
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO,
"%s: Watchdog Thread %d (%s) starting up",__func__, current->pid, current->comm);
while(!shutdown)
{
// This implements the Watchdog execution model algorithm
retWaitStatus = wait_event_interruptible(pWdContext->wdWaitQueue,
test_bit(WD_POST_EVENT, &pWdContext->wdEventFlag));
if(retWaitStatus == -ERESTARTSYS)
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: wait_event_interruptible returned -ERESTARTSYS", __func__);
break;
}
clear_bit(WD_POST_EVENT, &pWdContext->wdEventFlag);
while(1)
{
/* Post Msg to detect thread stuck */
if (test_and_clear_bit(WD_WLAN_DETECT_THREAD_STUCK,
&pWdContext->wdEventFlag)) {
if (!test_bit(MC_SUSPEND_EVENT, &gpVosSchedContext->mcEventFlag))
vos_wd_detect_thread_stuck();
else
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO,
"%s: controller thread %s id: %d is suspended do not attemp probing",
__func__, current->comm, current->pid);
/*
* Process here and return without processing any SSR
* related logic.
*/
break;
}
/* Check for any Active Entry Points
* If active, delay SSR until no entry point is active or
* delay until count is decremented to ZERO
*/
count = MAX_SSR_WAIT_ITERATIONS;
while (count)
{
if (!atomic_read(&ssr_protect_entry_count))
{
/* no external threads are executing */
break;
}
/* at least one external thread is executing */
if (--count)
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: Waiting for active entry points to exit", __func__);
msleep(SSR_WAIT_SLEEP_TIME);
}
}
/* at least one external thread is executing */
if (!count)
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: Continuing SSR when %d Entry points are still active",
__func__, atomic_read(&ssr_protect_entry_count));
}
// Check if Watchdog needs to shutdown
if(test_bit(WD_SHUTDOWN_EVENT, &pWdContext->wdEventFlag))
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO,
"%s: Watchdog thread signaled to shutdown", __func__);
clear_bit(WD_SHUTDOWN_EVENT, &pWdContext->wdEventFlag);
shutdown = VOS_TRUE;
break;
}
/* subsystem restart: shutdown event handler */
else if(test_bit(WD_WLAN_SHUTDOWN_EVENT, &pWdContext->wdEventFlag))
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
"%s: Watchdog thread signaled to perform WLAN shutdown",__func__);
clear_bit(WD_WLAN_SHUTDOWN_EVENT, &pWdContext->wdEventFlag);
//Perform WLAN shutdown
if(!pWdContext->resetInProgress)
{
pWdContext->resetInProgress = true;
vosStatus = hdd_wlan_shutdown();
if (! VOS_IS_STATUS_SUCCESS(vosStatus))
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
"%s: Failed to shutdown WLAN",__func__);
VOS_ASSERT(0);
goto err_reset;
}
}
}
/* subsystem restart: re-init event handler */
else if(test_bit(WD_WLAN_REINIT_EVENT, &pWdContext->wdEventFlag))
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
"%s: Watchdog thread signaled to perform WLAN re-init",__func__);
clear_bit(WD_WLAN_REINIT_EVENT, &pWdContext->wdEventFlag);
//Perform WLAN re-init
if(!pWdContext->resetInProgress)
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
"%s: Trying to do WLAN re-init when it is not shutdown !!",__func__);
}
vosStatus = hdd_wlan_re_init(NULL);
if (! VOS_IS_STATUS_SUCCESS(vosStatus))
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
"%s: Failed to re-init WLAN",__func__);
VOS_ASSERT(0);
goto err_reset;
}
pWdContext->resetInProgress = false;
}
else
{
//Unnecessary wakeup - Should never happen!!
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
"%s: Watchdog thread woke up unnecessarily",__func__);
}
break;
} // while message loop processing
} // while shutdown
/* destroy the timer only if intialized */
if (pWdContext->thread_stuck_timer.state != VOS_TIMER_STATE_UNUSED) {
vos_timer_destroy(&pWdContext->thread_stuck_timer);
}
// If we get here the Watchdog thread must exit
VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO,
"%s: Watchdog Thread exiting !!!!", __func__);
complete_and_exit(&pWdContext->WdShutdown, 0);
err_reset:
VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
"%s: Watchdog Thread Failed to Reset, Exiting!!!!", __func__);
return 0;
} /* VosMCThread() */
#ifdef QCA_CONFIG_SMP
/*---------------------------------------------------------------------------
\brief vos_free_tlshim_pkt_freeq() - Free voss buffer free queue
The \a vos_free_tlshim_pkt_freeq() does mem free of the buffers
available in free vos buffer queue which is used for Data rx processing
from Tlshim.
\param pSchedContext - pointer to the global vOSS Sched Context
\return Nothing
\sa vos_free_tlshim_pkt_freeq()
-------------------------------------------------------------------------*/
void vos_free_tlshim_pkt_freeq(pVosSchedContext pSchedContext)
{
struct VosTlshimPkt *pkt;
SPIN_LOCK_BH(&pSchedContext->VosTlshimPktFreeQLock);
while (!list_empty(&pSchedContext->VosTlshimPktFreeQ)) {
pkt = list_entry((&pSchedContext->VosTlshimPktFreeQ)->next,
typeof(*pkt), list);
list_del(&pkt->list);
SPIN_UNLOCK_BH(&pSchedContext->VosTlshimPktFreeQLock);
vos_mem_free(pkt);
SPIN_LOCK_BH(&pSchedContext->VosTlshimPktFreeQLock);
}
SPIN_UNLOCK_BH(&pSchedContext->VosTlshimPktFreeQLock);
}
/*---------------------------------------------------------------------------
\brief vos_alloc_tlshim_pkt_freeq() - Function to allocate free buffer queue
The \a vos_alloc_tlshim_pkt_freeq() allocates VOSS_MAX_TLSHIM_PKT
number of vos message buffers which are used for Rx data processing
from Tlshim.
\param pSchedContext - pointer to the global vOSS Sched Context
\return status of memory allocation
\sa vos_alloc_tlshim_pkt_freeq()
-------------------------------------------------------------------------*/
static VOS_STATUS vos_alloc_tlshim_pkt_freeq(pVosSchedContext pSchedContext)
{
struct VosTlshimPkt *pkt, *tmp;
int i;
for (i = 0; i < VOSS_MAX_TLSHIM_PKT; i++) {
pkt = vos_mem_malloc(sizeof(*pkt));
if (!pkt) {
VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s Vos packet allocation for tlshim thread failed",
__func__);
goto free;
}
memset(pkt, 0, sizeof(*pkt));
list_add_tail(&pkt->list, &pSchedContext->VosTlshimPktFreeQ);
}
return VOS_STATUS_SUCCESS;
free:
list_for_each_entry_safe(pkt, tmp, &pSchedContext->VosTlshimPktFreeQ, list) {
list_del(&pkt->list);
vos_mem_free(pkt);
}
return VOS_STATUS_E_NOMEM;
}
/*---------------------------------------------------------------------------
\brief vos_free_tlshim_pkt() - API to release vos message to the freeq
The \a vos_free_tlshim_pkt() returns the vos message used for Rx data
to the free queue.
\param pSchedContext - pointer to the global vOSS Sched Context
\param pkt - Vos message buffer to be returned to free queue.
\return Nothing
\sa vos_free_tlshim_pkt()
-------------------------------------------------------------------------*/
void vos_free_tlshim_pkt(pVosSchedContext pSchedContext,
struct VosTlshimPkt *pkt)
{
memset(pkt, 0, sizeof(*pkt));
SPIN_LOCK_BH(&pSchedContext->VosTlshimPktFreeQLock);
list_add_tail(&pkt->list, &pSchedContext->VosTlshimPktFreeQ);
SPIN_UNLOCK_BH(&pSchedContext->VosTlshimPktFreeQLock);
}
/*---------------------------------------------------------------------------
\brief vos_alloc_tlshim_pkt() - API to return next available vos message
The \a vos_alloc_tlshim_pkt() returns next available vos message buffer
used for Rx Data processing.
\param pSchedContext - pointer to the global vOSS Sched Context
\return pointer to vos message buffer
\sa vos_alloc_tlshim_pkt()
-------------------------------------------------------------------------*/
struct VosTlshimPkt *vos_alloc_tlshim_pkt(pVosSchedContext pSchedContext)
{
struct VosTlshimPkt *pkt;
SPIN_LOCK_BH(&pSchedContext->VosTlshimPktFreeQLock);
if (list_empty(&pSchedContext->VosTlshimPktFreeQ)) {
SPIN_UNLOCK_BH(&pSchedContext->VosTlshimPktFreeQLock);
return NULL;
}
pkt = list_first_entry(&pSchedContext->VosTlshimPktFreeQ,
struct VosTlshimPkt, list);
list_del(&pkt->list);
SPIN_UNLOCK_BH(&pSchedContext->VosTlshimPktFreeQLock);
return pkt;
}
/*---------------------------------------------------------------------------
\brief vos_indicate_rxpkt() - API to Indicate rx data packet
The \a vos_indicate_rxpkt() enqueues the rx packet onto tlshimRxQueue
and notifies VosTlshimRxThread().
\param Arg - pointer to the global vOSS Sched Context
\param pkt - Vos data message buffer
\return Nothing
\sa vos_indicate_rxpkt()
-------------------------------------------------------------------------*/
void vos_indicate_rxpkt(pVosSchedContext pSchedContext,
struct VosTlshimPkt *pkt)
{
SPIN_LOCK_BH(&pSchedContext->TlshimRxQLock);
list_add_tail(&pkt->list, &pSchedContext->tlshimRxQueue);
SPIN_UNLOCK_BH(&pSchedContext->TlshimRxQLock);
set_bit(RX_POST_EVENT, &pSchedContext->tlshimRxEvtFlg);
wake_up_interruptible(&pSchedContext->tlshimRxWaitQueue);
}
/*---------------------------------------------------------------------------
\brief vos_drop_rxpkt_by_staid() - API to drop pending Rx packets for a sta
The \a vos_drop_rxpkt_by_staid() drops queued packets for a station, to drop
all the pending packets the caller has to send WLAN_MAX_STA_COUNT as staId.
\param pSchedContext - pointer to the global vOSS Sched Context
\param staId - Station Id
\return Nothing
\sa vos_drop_rxpkt_by_staid()
-------------------------------------------------------------------------*/
void vos_drop_rxpkt_by_staid(pVosSchedContext pSchedContext, u_int16_t staId)
{
struct list_head local_list;
struct VosTlshimPkt *pkt, *tmp;
adf_nbuf_t buf, next_buf;
INIT_LIST_HEAD(&local_list);
SPIN_LOCK_BH(&pSchedContext->TlshimRxQLock);
if (list_empty(&pSchedContext->tlshimRxQueue)) {
SPIN_UNLOCK_BH(&pSchedContext->TlshimRxQLock);
return;
}
list_for_each_entry_safe(pkt, tmp, &pSchedContext->tlshimRxQueue, list) {
if (pkt->staId == staId || staId == WLAN_MAX_STA_COUNT)
list_move_tail(&pkt->list, &local_list);
}
SPIN_UNLOCK_BH(&pSchedContext->TlshimRxQLock);
list_for_each_entry_safe(pkt, tmp, &local_list, list) {
list_del(&pkt->list);
buf = pkt->Rxpkt;
while (buf) {
next_buf = adf_nbuf_queue_next(buf);
adf_nbuf_free(buf);
buf = next_buf;
}
vos_free_tlshim_pkt(pSchedContext, pkt);
}
}
/*---------------------------------------------------------------------------
\brief vos_rx_from_queue() - Function to process pending Rx packets
The \a vos_rx_from_queue() traverses the pending buffer list and calling
the callback. This callback would essentially send the packet to HDD.
\param pSchedContext - pointer to the global vOSS Sched Context
\return Nothing
\sa vos_rx_from_queue()
-------------------------------------------------------------------------*/
static void vos_rx_from_queue(pVosSchedContext pSchedContext)
{
struct VosTlshimPkt *pkt;
u_int16_t sta_id;
SPIN_LOCK_BH(&pSchedContext->TlshimRxQLock);
while (!list_empty(&pSchedContext->tlshimRxQueue)) {
pkt = list_first_entry(&pSchedContext->tlshimRxQueue,
struct VosTlshimPkt, list);
list_del(&pkt->list);
SPIN_UNLOCK_BH(&pSchedContext->TlshimRxQLock);
sta_id = pkt->staId;
pkt->callback(pkt->context, pkt->Rxpkt, sta_id);
vos_free_tlshim_pkt(pSchedContext, pkt);
SPIN_LOCK_BH(&pSchedContext->TlshimRxQLock);
}
SPIN_UNLOCK_BH(&pSchedContext->TlshimRxQLock);
}
/*---------------------------------------------------------------------------
\brief VosTlshimRxThread() - The VOSS Main Tlshim Rx thread
The \a VosTlshimRxThread() is the thread for Tlshim Data packet processing.
\param Arg - pointer to the global vOSS Sched Context
\return Thread exit code
\sa VosTlshimRxThread()
-------------------------------------------------------------------------*/
static int VosTlshimRxThread(void *arg)
{
pVosSchedContext pSchedContext = (pVosSchedContext)arg;
unsigned long pref_cpu = 0;
bool shutdown = false;
int status, i;
set_user_nice(current, -1);
#ifdef MSM_PLATFORM
set_wake_up_idle(true);
#endif
/* Find the available cpu core other than cpu 0 and
* bind the thread */
for_each_online_cpu(i) {
if (i == 0)
continue;
pref_cpu = i;
break;
}
if (pref_cpu != 0 && (!vos_set_cpus_allowed_ptr(current, pref_cpu)))
affine_cpu = pref_cpu;
if (!arg) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: Bad Args passed", __func__);
return 0;
}
complete(&pSchedContext->TlshimRxStartEvent);
while (!shutdown) {
status = wait_event_interruptible(pSchedContext->tlshimRxWaitQueue,
test_bit(RX_POST_EVENT,
&pSchedContext->tlshimRxEvtFlg) ||
test_bit(RX_SUSPEND_EVENT,
&pSchedContext->tlshimRxEvtFlg));
if (status == -ERESTARTSYS)
break;
clear_bit(RX_POST_EVENT, &pSchedContext->tlshimRxEvtFlg);
while (true) {
if (test_bit(RX_SHUTDOWN_EVENT,
&pSchedContext->tlshimRxEvtFlg)) {
clear_bit(RX_SHUTDOWN_EVENT,
&pSchedContext->tlshimRxEvtFlg);
if (test_bit(RX_SUSPEND_EVENT,
&pSchedContext->tlshimRxEvtFlg)) {
clear_bit(RX_SUSPEND_EVENT,
&pSchedContext->tlshimRxEvtFlg);
complete(&pSchedContext->SuspndTlshimRxEvent);
}
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: Shutting down tl shim Tlshim rx thread", __func__);
shutdown = true;
break;
}
vos_rx_from_queue(pSchedContext);
if (test_bit(RX_SUSPEND_EVENT,
&pSchedContext->tlshimRxEvtFlg)) {
clear_bit(RX_SUSPEND_EVENT,
&pSchedContext->tlshimRxEvtFlg);
spin_lock(&pSchedContext->TlshimRxThreadLock);
INIT_COMPLETION(pSchedContext->ResumeTlshimRxEvent);
complete(&pSchedContext->SuspndTlshimRxEvent);
spin_unlock(&pSchedContext->TlshimRxThreadLock);
wait_for_completion_interruptible(
&pSchedContext->ResumeTlshimRxEvent);
}
break;
}
}
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: Exiting VOSS Tlshim rx thread", __func__);
complete_and_exit(&pSchedContext->TlshimRxShutdown, 0);
}
#endif
/*---------------------------------------------------------------------------
\brief vos_sched_close() - Close the vOSS Scheduler
The \a vos_sched_closes() function closes the vOSS Scheduler
Upon successful closing:
- All the message queues are flushed
- The Main Controller thread is closed
- The Tx thread is closed
\param pVosContext - pointer to the global vOSS Context
\return VOS_STATUS_SUCCESS - Scheduler was successfully initialized and
is ready to be used.
VOS_STATUS_E_INVAL - Invalid parameter passed to the scheduler Open
function
VOS_STATUS_E_FAILURE - Failure to initialize the scheduler/
\sa vos_sched_close()
---------------------------------------------------------------------------*/
VOS_STATUS vos_sched_close ( v_PVOID_t pVosContext )
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: invoked", __func__);
if (gpVosSchedContext == NULL)
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: gpVosSchedContext == NULL",__func__);
return VOS_STATUS_E_FAILURE;
}
// shut down MC Thread
set_bit(MC_SHUTDOWN_EVENT, &gpVosSchedContext->mcEventFlag);
set_bit(MC_POST_EVENT, &gpVosSchedContext->mcEventFlag);
wake_up_interruptible(&gpVosSchedContext->mcWaitQueue);
//Wait for MC to exit
wait_for_completion(&gpVosSchedContext->McShutdown);
gpVosSchedContext->McThread = 0;
//Clean up message queues of MC thread
vos_sched_flush_mc_mqs(gpVosSchedContext);
//Deinit all the queues
vos_sched_deinit_mqs(gpVosSchedContext);
#ifdef QCA_CONFIG_SMP
vos_lock_destroy(&gpVosSchedContext->affinity_lock);
// Shut down Tlshim Rx thread
set_bit(RX_SHUTDOWN_EVENT, &gpVosSchedContext->tlshimRxEvtFlg);
set_bit(RX_POST_EVENT, &gpVosSchedContext->tlshimRxEvtFlg);
wake_up_interruptible(&gpVosSchedContext->tlshimRxWaitQueue);
wait_for_completion(&gpVosSchedContext->TlshimRxShutdown);
gpVosSchedContext->TlshimRxThread = NULL;
vos_drop_rxpkt_by_staid(gpVosSchedContext, WLAN_MAX_STA_COUNT);
vos_free_tlshim_pkt_freeq(gpVosSchedContext);
unregister_hotcpu_notifier(&vos_cpu_hotplug_notifier);
#endif
return VOS_STATUS_SUCCESS;
} /* vox_sched_close() */
VOS_STATUS vos_watchdog_close ( v_PVOID_t pVosContext )
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: vos_watchdog closing now", __func__);
if (gpVosWatchdogContext == NULL)
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: gpVosWatchdogContext is NULL",__func__);
return VOS_STATUS_E_FAILURE;
}
set_bit(WD_SHUTDOWN_EVENT, &gpVosWatchdogContext->wdEventFlag);
set_bit(WD_POST_EVENT, &gpVosWatchdogContext->wdEventFlag);
wake_up_interruptible(&gpVosWatchdogContext->wdWaitQueue);
//Wait for Watchdog thread to exit
wait_for_completion(&gpVosWatchdogContext->WdShutdown);
return VOS_STATUS_SUCCESS;
} /* vos_watchdog_close() */
/*---------------------------------------------------------------------------
\brief vos_sched_init_mqs: Initialize the vOSS Scheduler message queues
The \a vos_sched_init_mqs() function initializes the vOSS Scheduler
message queues.
\param pVosSchedContext - pointer to the Scheduler Context.
\return VOS_STATUS_SUCCESS - Scheduler was successfully initialized and
is ready to be used.
VOS_STATUS_E_RESOURCES - System resources (other than memory)
are unavailable to initilize the scheduler
\sa vos_sched_init_mqs()
-------------------------------------------------------------------------*/
VOS_STATUS vos_sched_init_mqs ( pVosSchedContext pSchedContext )
{
VOS_STATUS vStatus = VOS_STATUS_SUCCESS;
// Now intialize all the message queues
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: Initializing the WDA MC Message queue",__func__);
vStatus = vos_mq_init(&pSchedContext->wdaMcMq);
if (! VOS_IS_STATUS_SUCCESS(vStatus))
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: Failed to init WDA MC Message queue",__func__);
VOS_ASSERT(0);
return vStatus;
}
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: Initializing the PE MC Message queue",__func__);
vStatus = vos_mq_init(&pSchedContext->peMcMq);
if (! VOS_IS_STATUS_SUCCESS(vStatus))
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: Failed to init PE MC Message queue",__func__);
VOS_ASSERT(0);
return vStatus;
}
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: Initializing the SME MC Message queue", __func__);
vStatus = vos_mq_init(&pSchedContext->smeMcMq);
if (! VOS_IS_STATUS_SUCCESS(vStatus))
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: Failed to init SME MC Message queue",__func__);
VOS_ASSERT(0);
return vStatus;
}
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: Initializing the TL MC Message queue",__func__);
vStatus = vos_mq_init(&pSchedContext->tlMcMq);
if (! VOS_IS_STATUS_SUCCESS(vStatus))
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: Failed to init TL MC Message queue",__func__);
VOS_ASSERT(0);
return vStatus;
}
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: Initializing the SYS MC Message queue",__func__);
vStatus = vos_mq_init(&pSchedContext->sysMcMq);
if (! VOS_IS_STATUS_SUCCESS(vStatus))
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: Failed to init SYS MC Message queue",__func__);
VOS_ASSERT(0);
return vStatus;
}
return VOS_STATUS_SUCCESS;
} /* vos_sched_init_mqs() */
/*---------------------------------------------------------------------------
\brief vos_sched_deinit_mqs: Deinitialize the vOSS Scheduler message queues
The \a vos_sched_init_mqs() function deinitializes the vOSS Scheduler
message queues.
\param pVosSchedContext - pointer to the Scheduler Context.
\return None
\sa vos_sched_deinit_mqs()
-------------------------------------------------------------------------*/
void vos_sched_deinit_mqs ( pVosSchedContext pSchedContext )
{
// Now de-intialize all message queues
// MC WDA
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO_HIGH,
"%s De-Initializing the WDA MC Message queue",__func__);
vos_mq_deinit(&pSchedContext->wdaMcMq);
//MC PE
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO_HIGH,
"%s De-Initializing the PE MC Message queue",__func__);
vos_mq_deinit(&pSchedContext->peMcMq);
//MC SME
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO_HIGH,
"%s De-Initializing the SME MC Message queue",__func__);
vos_mq_deinit(&pSchedContext->smeMcMq);
//MC TL
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO_HIGH,
"%s De-Initializing the TL MC Message queue",__func__);
vos_mq_deinit(&pSchedContext->tlMcMq);
//MC SYS
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO_HIGH,
"%s De-Initializing the SYS MC Message queue",__func__);
vos_mq_deinit(&pSchedContext->sysMcMq);
} /* vos_sched_deinit_mqs() */
/*-------------------------------------------------------------------------
this helper function flushes all the MC message queues
-------------------------------------------------------------------------*/
void vos_sched_flush_mc_mqs ( pVosSchedContext pSchedContext )
{
pVosMsgWrapper pMsgWrapper = NULL;
pVosContextType vosCtx;
/*
** Here each of the MC thread MQ shall be drained and returned to the
** Core. Before returning a wrapper to the Core, the VOS message shall be
** freed first
*/
VOS_TRACE( VOS_MODULE_ID_VOSS,
VOS_TRACE_LEVEL_INFO,
("Flushing the MC Thread message queue") );
if (NULL == pSchedContext)
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: pSchedContext is NULL", __func__);
return;
}
vosCtx = (pVosContextType)(pSchedContext->pVContext);
if (NULL == vosCtx)
{
VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: vosCtx is NULL", __func__);
return;
}
/* Flush the SYS Mq */
while( NULL != (pMsgWrapper = vos_mq_get(&pSchedContext->sysMcMq) ))
{
VOS_TRACE( VOS_MODULE_ID_VOSS,
VOS_TRACE_LEVEL_INFO,
"%s: Freeing MC SYS message type %d ",__func__,
pMsgWrapper->pVosMsg->type );
sysMcFreeMsg(pSchedContext->pVContext, pMsgWrapper->pVosMsg);
vos_core_return_msg(pSchedContext->pVContext, pMsgWrapper);
}
/* Flush the WDA Mq */
while( NULL != (pMsgWrapper = vos_mq_get(&pSchedContext->wdaMcMq) ))
{
if(pMsgWrapper->pVosMsg != NULL)
{
VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO,
"%s: Freeing MC WDA MSG message type %d",
__func__, pMsgWrapper->pVosMsg->type );
if (pMsgWrapper->pVosMsg->bodyptr) {
vos_mem_free((v_VOID_t*)pMsgWrapper->pVosMsg->bodyptr);
}
pMsgWrapper->pVosMsg->bodyptr = NULL;
pMsgWrapper->pVosMsg->bodyval = 0;
pMsgWrapper->pVosMsg->type = 0;
}
vos_core_return_msg(pSchedContext->pVContext, pMsgWrapper);
}
/* Flush the PE Mq */
while( NULL != (pMsgWrapper = vos_mq_get(&pSchedContext->peMcMq) ))
{
VOS_TRACE( VOS_MODULE_ID_VOSS,
VOS_TRACE_LEVEL_INFO,
"%s: Freeing MC PE MSG message type %d",__func__,
pMsgWrapper->pVosMsg->type );
peFreeMsg(vosCtx->pMACContext, (tSirMsgQ*)pMsgWrapper->pVosMsg);
vos_core_return_msg(pSchedContext->pVContext, pMsgWrapper);
}
/* Flush the SME Mq */
while( NULL != (pMsgWrapper = vos_mq_get(&pSchedContext->smeMcMq) ))
{
VOS_TRACE( VOS_MODULE_ID_VOSS,
VOS_TRACE_LEVEL_INFO,
"%s: Freeing MC SME MSG message type %d", __func__,
pMsgWrapper->pVosMsg->type );
sme_FreeMsg(vosCtx->pMACContext, pMsgWrapper->pVosMsg);
vos_core_return_msg(pSchedContext->pVContext, pMsgWrapper);
}
/* Flush the TL Mq */
while( NULL != (pMsgWrapper = vos_mq_get(&pSchedContext->tlMcMq) ))
{
VOS_TRACE( VOS_MODULE_ID_VOSS,
VOS_TRACE_LEVEL_INFO,
"%s: Freeing MC TL message type %d",__func__,
pMsgWrapper->pVosMsg->type );
WLANTL_McFreeMsg(pSchedContext->pVContext, pMsgWrapper->pVosMsg);
vos_core_return_msg(pSchedContext->pVContext, pMsgWrapper);
}
} /* vos_sched_flush_mc_mqs() */
/*-------------------------------------------------------------------------
Helper function to get the scheduler context
------------------------------------------------------------------------*/
pVosSchedContext get_vos_sched_ctxt(void)
{
//Make sure that Vos Scheduler context has been initialized
if (gpVosSchedContext == NULL)
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: gpVosSchedContext == NULL",__func__);
}
return (gpVosSchedContext);
}
/**
* vos_is_mc_thread()- Check if threadid is
* of mc thread
*
* @thread_id: passed threadid
* This function is called to check if threadid is
* of mc thread.
*
* Return: true if threadid is of mc thread.
*/
int vos_sched_is_mc_thread(int thread_id)
{
/* Make sure that Vos Scheduler context has been initialized */
VOS_ASSERT(NULL != gpVosSchedContext);
if (gpVosSchedContext == NULL) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: gpVosSchedContext == NULL", __func__);
return 0;
}
return ((gpVosSchedContext->McThread) &&
(thread_id ==
gpVosSchedContext->McThread->pid));
}
/*-------------------------------------------------------------------------
Helper function to get the watchdog context
------------------------------------------------------------------------*/
pVosWatchdogContext get_vos_watchdog_ctxt(void)
{
//Make sure that Vos Scheduler context has been initialized
if (gpVosWatchdogContext == NULL)
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: gpVosWatchdogContext == NULL",__func__);
}
return (gpVosWatchdogContext);
}
/**
@brief vos_watchdog_wlan_shutdown()
This function is called to shutdown WLAN driver during SSR.
Adapters are disabled, and the watchdog task will be signalled
to shutdown WLAN driver.
@param
NONE
@return
VOS_STATUS_SUCCESS - Operation completed successfully.
VOS_STATUS_E_FAILURE - Operation failed.
*/
VOS_STATUS vos_watchdog_wlan_shutdown(void)
{
v_CONTEXT_t pVosContext = NULL;
hdd_context_t *pHddCtx = NULL;
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
"%s: WLAN driver is shutting down ", __func__);
if (NULL == gpVosWatchdogContext)
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
"%s: Watchdog not enabled. LOGP ignored.", __func__);
return VOS_STATUS_E_FAILURE;
}
pVosContext = vos_get_global_context(VOS_MODULE_ID_HDD, NULL);
pHddCtx = (hdd_context_t *)vos_get_context(VOS_MODULE_ID_HDD, pVosContext );
if (NULL == pHddCtx)
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
"%s: Invalid HDD Context", __func__);
return VOS_STATUS_E_FAILURE;
}
/* Take the lock here */
spin_lock(&gpVosWatchdogContext->wdLock);
/* reuse the existing 'reset in progress' */
if (gpVosWatchdogContext->resetInProgress)
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
"%s: Shutdown already in Progress. Ignoring signaling Watchdog",
__func__);
/* Release the lock here */
spin_unlock(&gpVosWatchdogContext->wdLock);
return VOS_STATUS_E_FAILURE;
}
/* reuse the existing 'logp in progress', eventhough it is not
* exactly the same */
else if (pHddCtx->isLogpInProgress)
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
"%s: shutdown/re-init already in Progress. Ignoring signaling Watchdog",
__func__);
/* Release the lock here */
spin_unlock(&gpVosWatchdogContext->wdLock);
return VOS_STATUS_E_FAILURE;
}
/* Set the flags so that all future CMD53 and Wext commands get blocked right away */
vos_set_logp_in_progress(VOS_MODULE_ID_VOSS, TRUE);
vos_set_reinit_in_progress(VOS_MODULE_ID_VOSS, FALSE);
pHddCtx->isLogpInProgress = TRUE;
/* Release the lock here */
spin_unlock(&gpVosWatchdogContext->wdLock);
if ((pHddCtx->isLoadInProgress) ||
(pHddCtx->isUnloadInProgress))
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
"%s: Load/unload in Progress. Ignoring signaling Watchdog",
__func__);
/* wcnss has crashed, and SSR has alredy been started by Kernel driver.
* So disable SSR from WLAN driver */
hdd_set_ssr_required( HDD_SSR_DISABLED );
return VOS_STATUS_E_FAILURE;
}
/* Update Riva Reset Statistics */
pHddCtx->hddRivaResetStats++;
#ifdef CONFIG_HAS_EARLYSUSPEND
if(VOS_STATUS_SUCCESS != hdd_wlan_reset_initialization())
{
VOS_ASSERT(0);
}
#endif
set_bit(WD_WLAN_SHUTDOWN_EVENT, &gpVosWatchdogContext->wdEventFlag);
set_bit(WD_POST_EVENT, &gpVosWatchdogContext->wdEventFlag);
wake_up_interruptible(&gpVosWatchdogContext->wdWaitQueue);
return VOS_STATUS_SUCCESS;
}
/**
@brief vos_watchdog_wlan_re_init()
This function is called to re-initialize WLAN driver, and this is
called when Riva SS reboots.
@param
NONE
@return
VOS_STATUS_SUCCESS - Operation completed successfully.
VOS_STATUS_E_FAILURE - Operation failed.
*/
VOS_STATUS vos_watchdog_wlan_re_init(void)
{
/* Make sure that Vos Watchdog context has been initialized */
if (gpVosWatchdogContext == NULL) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO,
"%s: gpVosWatchdogContext == NULL", __func__);
return VOS_STATUS_SUCCESS;
}
/* watchdog task is still running, it is not closed in shutdown */
set_bit(WD_WLAN_REINIT_EVENT, &gpVosWatchdogContext->wdEventFlag);
set_bit(WD_POST_EVENT, &gpVosWatchdogContext->wdEventFlag);
wake_up_interruptible(&gpVosWatchdogContext->wdWaitQueue);
return VOS_STATUS_SUCCESS;
}
/**
* vos_ssr_protect_init() - initialize ssr protection debug functionality
*
* Return:
* void
*/
void vos_ssr_protect_init(void)
{
int i = 0;
spin_lock_init(&ssr_protect_lock);
while (i < MAX_SSR_PROTECT_LOG) {
ssr_protect_log[i].func = NULL;
ssr_protect_log[i].free = true;
ssr_protect_log[i].pid = 0;
i++;
}
}
/**
* vos_print_external_threads() - print external threads stuck in driver
*
* Return:
* void
*/
static void vos_print_external_threads(void)
{
int i = 0;
unsigned long irq_flags;
spin_lock_irqsave(&ssr_protect_lock, irq_flags);
while (i < MAX_SSR_PROTECT_LOG) {
if (!ssr_protect_log[i].free) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"PID %d is stuck at %s", ssr_protect_log[i].pid,
ssr_protect_log[i].func);
}
i++;
}
spin_unlock_irqrestore(&ssr_protect_lock, irq_flags);
}
/**
* vos_is_load_unload_ready() - check load/unload ready
* @caller_func: Pointer to caller function
*
* This function will check if calling execution can call
* kickstart driver for load/unload
*
* Return: true if ready else false.
*/
bool vos_is_load_unload_ready(const char *caller_func)
{
int count = MAX_LOAD_UNLOAD_WAIT_ITERATIONS;
while (count) {
if (!atomic_read(&load_unload_protect_count))
break;
if (--count) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: Waiting for load/unload active entry points to exit",
__func__);
msleep(LOAD_UNLOAD_WAIT_SLEEP_TIME);
}
}
/* at least one external thread is executing */
if (!count) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s : Thread stuck for load/unload", __func__);
return false;
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"Allowing load/Unload for %s", caller_func);
return true;
}
/**
* vos_load_unload_protect () - Protect load/Unload
* @caller_func : Pointer to caller function
*
* This function will protect the atomic variable by incrementing
* its value
*
* Return: void
*/
void vos_load_unload_protect(const char *caller_func)
{
atomic_inc(&load_unload_protect_count);
}
/**
* vos_load_unload_unprotect () - Unprotect load/unload
* @caller_func : Pointer to caller_func
*
* This function will decrement the atomic variable value
*
* Return: void
*/
void vos_load_unload_unprotect(const char *caller_func)
{
atomic_dec(&load_unload_protect_count);
}
/**
@brief vos_ssr_protect()
This function is called to keep track of active driver entry points
@param
caller_func - Name of calling function.
@return
void
*/
void vos_ssr_protect(const char *caller_func)
{
int count;
int i = 0;
bool status = false;
unsigned long irq_flags;
count = atomic_inc_return(&ssr_protect_entry_count);
spin_lock_irqsave(&ssr_protect_lock, irq_flags);
while (i < MAX_SSR_PROTECT_LOG) {
if (ssr_protect_log[i].free) {
ssr_protect_log[i].func = caller_func;
ssr_protect_log[i].free = false;
ssr_protect_log[i].pid = current->pid;
status = true;
break;
}
i++;
}
spin_unlock_irqrestore(&ssr_protect_lock, irq_flags);
if (!status)
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Could not track PID %d call %s: log is full",
current->pid, caller_func);
}
/**
@brief vos_ssr_unprotect()
@param
caller_func - Name of calling function.
@return
void
*/
void vos_ssr_unprotect(const char *caller_func)
{
int count;
int i = 0;
bool status = false;
unsigned long irq_flags;
count = atomic_dec_return(&ssr_protect_entry_count);
spin_lock_irqsave(&ssr_protect_lock, irq_flags);
while (i < MAX_SSR_PROTECT_LOG) {
if (!ssr_protect_log[i].free) {
if ((ssr_protect_log[i].pid == current->pid) &&
!strcmp(ssr_protect_log[i].func, caller_func)) {
ssr_protect_log[i].func = NULL;
ssr_protect_log[i].free = true;
ssr_protect_log[i].pid = 0;
status = true;
break;
}
}
i++;
}
spin_unlock_irqrestore(&ssr_protect_lock, irq_flags);
if (!status)
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Untracked call %s", caller_func);
}
/**
* vos_is_wd_thread()- Check if threadid is
* of Watchdog thread
*
* @thread_id: passed threadid
* This function is called to check if threadid is
* of wd thread.
*
* Return: true if threadid is of wd thread.
*/
bool vos_is_wd_thread(int thread_id)
{
/*
* Make sure that Vos Watchdong Scheduler context
* has been initialized
*/
if (NULL == gpVosWatchdogContext)
return 0;
return ((gpVosWatchdogContext->WdThread) &&
(thread_id == gpVosWatchdogContext->WdThread->pid));
}
/**
@brief vos_is_ssr_ready()
This function will check if the calling execution can
proceed with SSR.
@param
caller_func - Name of calling function.
@return
true or false
*/
bool vos_is_ssr_ready(const char *caller_func)
{
int count = MAX_SSR_WAIT_ITERATIONS;
while (count) {
if (!atomic_read(&ssr_protect_entry_count))
break;
if (--count) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: Waiting for active entry points to exit", __func__);
msleep(SSR_WAIT_SLEEP_TIME);
}
}
/* at least one external thread is executing */
if (!count) {
vos_print_external_threads();
return false;
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"Allowing SSR for %s", caller_func);
return true;
}
/**
* vos_get_gfp_flags(): get GFP flags
*
* Based on the scheduled context, return GFP flags
* Return: gfp flags
*/
int vos_get_gfp_flags(void)
{
int flags = GFP_KERNEL;
if (in_interrupt() || in_atomic() || irqs_disabled())
flags = GFP_ATOMIC;
return flags;
}