xref: /OK3568_Linux_fs/external/rkwifibt/drivers/bcmdhd/dhd_linux_lb.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * Broadcom Dongle Host Driver (DHD), Linux-specific network interface
3*4882a593Smuzhiyun  * Basically selected code segments from usb-cdc.c and usb-rndis.c
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2020, Broadcom.
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  *      Unless you and Broadcom execute a separate written software license
8*4882a593Smuzhiyun  * agreement governing use of this software, this software is licensed to you
9*4882a593Smuzhiyun  * under the terms of the GNU General Public License version 2 (the "GPL"),
10*4882a593Smuzhiyun  * available at http://www.broadcom.com/licenses/GPLv2.php, with the
11*4882a593Smuzhiyun  * following added to such license:
12*4882a593Smuzhiyun  *
13*4882a593Smuzhiyun  *      As a special exception, the copyright holders of this software give you
14*4882a593Smuzhiyun  * permission to link this software with independent modules, and to copy and
15*4882a593Smuzhiyun  * distribute the resulting executable under terms of your choice, provided that
16*4882a593Smuzhiyun  * you also meet, for each linked independent module, the terms and conditions of
17*4882a593Smuzhiyun  * the license of that module.  An independent module is a module which is not
18*4882a593Smuzhiyun  * derived from this software.  The special exception does not apply to any
19*4882a593Smuzhiyun  * modifications of the software.
20*4882a593Smuzhiyun  *
21*4882a593Smuzhiyun  *
22*4882a593Smuzhiyun  * <<Broadcom-WL-IPTag/Open:>>
23*4882a593Smuzhiyun  *
24*4882a593Smuzhiyun  * $Id$
25*4882a593Smuzhiyun  */
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun #include <dhd_linux_priv.h>
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun extern dhd_pub_t* g_dhd_pub;
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun #if defined(DHD_LB)
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun #ifdef DHD_LB_STATS
34*4882a593Smuzhiyun #define DHD_NUM_NAPI_LATENCY_ROWS (17u)
35*4882a593Smuzhiyun #define DHD_NAPI_LATENCY_SIZE (sizeof(uint64) * DHD_NUM_NAPI_LATENCY_ROWS)
36*4882a593Smuzhiyun #endif /* DHD_LB_STATS */
37*4882a593Smuzhiyun 
38*4882a593Smuzhiyun #ifdef DHD_REPLACE_LOG_INFO_TO_TRACE
39*4882a593Smuzhiyun #define DHD_LB_INFO DHD_TRACE
40*4882a593Smuzhiyun #else
41*4882a593Smuzhiyun #define DHD_LB_INFO DHD_INFO
42*4882a593Smuzhiyun #endif /* DHD_REPLACE_LOG_INFO_TO_TRACE */
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun void
dhd_lb_set_default_cpus(dhd_info_t * dhd)45*4882a593Smuzhiyun dhd_lb_set_default_cpus(dhd_info_t *dhd)
46*4882a593Smuzhiyun {
47*4882a593Smuzhiyun 	/* Default CPU allocation for the jobs */
48*4882a593Smuzhiyun 	atomic_set(&dhd->rx_napi_cpu, 1);
49*4882a593Smuzhiyun 	atomic_set(&dhd->tx_cpu, 2);
50*4882a593Smuzhiyun 	atomic_set(&dhd->net_tx_cpu, 0);
51*4882a593Smuzhiyun 	atomic_set(&dhd->dpc_cpu, 0);
52*4882a593Smuzhiyun }
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun void
dhd_cpumasks_deinit(dhd_info_t * dhd)55*4882a593Smuzhiyun dhd_cpumasks_deinit(dhd_info_t *dhd)
56*4882a593Smuzhiyun {
57*4882a593Smuzhiyun 	free_cpumask_var(dhd->cpumask_curr_avail);
58*4882a593Smuzhiyun 	free_cpumask_var(dhd->cpumask_primary);
59*4882a593Smuzhiyun 	free_cpumask_var(dhd->cpumask_primary_new);
60*4882a593Smuzhiyun 	free_cpumask_var(dhd->cpumask_secondary);
61*4882a593Smuzhiyun 	free_cpumask_var(dhd->cpumask_secondary_new);
62*4882a593Smuzhiyun }
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun int
dhd_cpumasks_init(dhd_info_t * dhd)65*4882a593Smuzhiyun dhd_cpumasks_init(dhd_info_t *dhd)
66*4882a593Smuzhiyun {
67*4882a593Smuzhiyun 	int id;
68*4882a593Smuzhiyun 	uint32 cpus, num_cpus = num_possible_cpus();
69*4882a593Smuzhiyun 	int ret = 0;
70*4882a593Smuzhiyun 
71*4882a593Smuzhiyun 	DHD_ERROR(("%s CPU masks primary(big)=0x%x secondary(little)=0x%x\n", __FUNCTION__,
72*4882a593Smuzhiyun 		DHD_LB_PRIMARY_CPUS, DHD_LB_SECONDARY_CPUS));
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun 	/* FIXME: If one alloc fails we must free_cpumask_var the previous */
75*4882a593Smuzhiyun 	if (!alloc_cpumask_var(&dhd->cpumask_curr_avail, GFP_KERNEL) ||
76*4882a593Smuzhiyun 	    !alloc_cpumask_var(&dhd->cpumask_primary, GFP_KERNEL) ||
77*4882a593Smuzhiyun 	    !alloc_cpumask_var(&dhd->cpumask_primary_new, GFP_KERNEL) ||
78*4882a593Smuzhiyun 	    !alloc_cpumask_var(&dhd->cpumask_secondary, GFP_KERNEL) ||
79*4882a593Smuzhiyun 	    !alloc_cpumask_var(&dhd->cpumask_secondary_new, GFP_KERNEL)) {
80*4882a593Smuzhiyun 		DHD_ERROR(("%s Failed to init cpumasks\n", __FUNCTION__));
81*4882a593Smuzhiyun 		ret = -ENOMEM;
82*4882a593Smuzhiyun 		goto fail;
83*4882a593Smuzhiyun 	}
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun 	cpumask_copy(dhd->cpumask_curr_avail, cpu_online_mask);
86*4882a593Smuzhiyun 	cpumask_clear(dhd->cpumask_primary);
87*4882a593Smuzhiyun 	cpumask_clear(dhd->cpumask_secondary);
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun 	if (num_cpus > 32) {
90*4882a593Smuzhiyun 		DHD_ERROR(("%s max cpus must be 32, %d too big\n", __FUNCTION__, num_cpus));
91*4882a593Smuzhiyun 		ASSERT(0);
92*4882a593Smuzhiyun 	}
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun 	cpus = DHD_LB_PRIMARY_CPUS;
95*4882a593Smuzhiyun 	for (id = 0; id < num_cpus; id++) {
96*4882a593Smuzhiyun 		if (isset(&cpus, id))
97*4882a593Smuzhiyun 			cpumask_set_cpu(id, dhd->cpumask_primary);
98*4882a593Smuzhiyun 	}
99*4882a593Smuzhiyun 
100*4882a593Smuzhiyun 	cpus = DHD_LB_SECONDARY_CPUS;
101*4882a593Smuzhiyun 	for (id = 0; id < num_cpus; id++) {
102*4882a593Smuzhiyun 		if (isset(&cpus, id))
103*4882a593Smuzhiyun 			cpumask_set_cpu(id, dhd->cpumask_secondary);
104*4882a593Smuzhiyun 	}
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun 	return ret;
107*4882a593Smuzhiyun fail:
108*4882a593Smuzhiyun 	dhd_cpumasks_deinit(dhd);
109*4882a593Smuzhiyun 	return ret;
110*4882a593Smuzhiyun }
111*4882a593Smuzhiyun 
112*4882a593Smuzhiyun /*
113*4882a593Smuzhiyun  * The CPU Candidacy Algorithm
114*4882a593Smuzhiyun  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~
115*4882a593Smuzhiyun  * The available CPUs for selection are divided into two groups
116*4882a593Smuzhiyun  *  Primary Set - A CPU mask that carries the First Choice CPUs
117*4882a593Smuzhiyun  *  Secondary Set - A CPU mask that carries the Second Choice CPUs.
118*4882a593Smuzhiyun  *
119*4882a593Smuzhiyun  * There are two types of Job, that needs to be assigned to
120*4882a593Smuzhiyun  * the CPUs, from one of the above mentioned CPU group. The Jobs are
121*4882a593Smuzhiyun  * 1) Rx Packet Processing - napi_cpu
122*4882a593Smuzhiyun  *
123*4882a593Smuzhiyun  * To begin with napi_cpu is on CPU0. Whenever a CPU goes
124*4882a593Smuzhiyun  * on-line/off-line the CPU candidacy algorithm is triggerd. The candidacy
125*4882a593Smuzhiyun  * algo tries to pickup the first available non boot CPU (CPU0) for napi_cpu.
126*4882a593Smuzhiyun  *
127*4882a593Smuzhiyun  */
dhd_select_cpu_candidacy(dhd_info_t * dhd)128*4882a593Smuzhiyun void dhd_select_cpu_candidacy(dhd_info_t *dhd)
129*4882a593Smuzhiyun {
130*4882a593Smuzhiyun 	uint32 primary_available_cpus; /* count of primary available cpus */
131*4882a593Smuzhiyun 	uint32 secondary_available_cpus; /* count of secondary available cpus */
132*4882a593Smuzhiyun 	uint32 napi_cpu = 0; /* cpu selected for napi rx processing */
133*4882a593Smuzhiyun 	uint32 tx_cpu = 0; /* cpu selected for tx processing job */
134*4882a593Smuzhiyun 	uint32 dpc_cpu = atomic_read(&dhd->dpc_cpu);
135*4882a593Smuzhiyun 	uint32 net_tx_cpu = atomic_read(&dhd->net_tx_cpu);
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 	cpumask_clear(dhd->cpumask_primary_new);
138*4882a593Smuzhiyun 	cpumask_clear(dhd->cpumask_secondary_new);
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun 	/*
141*4882a593Smuzhiyun 	 * Now select from the primary mask. Even if a Job is
142*4882a593Smuzhiyun 	 * already running on a CPU in secondary group, we still move
143*4882a593Smuzhiyun 	 * to primary CPU. So no conditional checks.
144*4882a593Smuzhiyun 	 */
145*4882a593Smuzhiyun 	cpumask_and(dhd->cpumask_primary_new, dhd->cpumask_primary,
146*4882a593Smuzhiyun 		dhd->cpumask_curr_avail);
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun 	cpumask_and(dhd->cpumask_secondary_new, dhd->cpumask_secondary,
149*4882a593Smuzhiyun 		dhd->cpumask_curr_avail);
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun 	/* Clear DPC cpu from new masks so that dpc cpu is not chosen for LB */
152*4882a593Smuzhiyun 	cpumask_clear_cpu(dpc_cpu, dhd->cpumask_primary_new);
153*4882a593Smuzhiyun 	cpumask_clear_cpu(dpc_cpu, dhd->cpumask_secondary_new);
154*4882a593Smuzhiyun 
155*4882a593Smuzhiyun 	/* Clear net_tx_cpu from new masks so that same is not chosen for LB */
156*4882a593Smuzhiyun 	cpumask_clear_cpu(net_tx_cpu, dhd->cpumask_primary_new);
157*4882a593Smuzhiyun 	cpumask_clear_cpu(net_tx_cpu, dhd->cpumask_secondary_new);
158*4882a593Smuzhiyun 
159*4882a593Smuzhiyun 	primary_available_cpus = cpumask_weight(dhd->cpumask_primary_new);
160*4882a593Smuzhiyun 
161*4882a593Smuzhiyun #if defined(DHD_LB_HOST_CTRL)
162*4882a593Smuzhiyun 	/* Does not use promary cpus if DHD received affinity off cmd
163*4882a593Smuzhiyun 	*  from framework
164*4882a593Smuzhiyun 	*/
165*4882a593Smuzhiyun 	if (primary_available_cpus > 0 && dhd->permitted_primary_cpu)
166*4882a593Smuzhiyun #else
167*4882a593Smuzhiyun 	if (primary_available_cpus > 0)
168*4882a593Smuzhiyun #endif /* DHD_LB_HOST_CTRL */
169*4882a593Smuzhiyun 	{
170*4882a593Smuzhiyun 		napi_cpu = cpumask_first(dhd->cpumask_primary_new);
171*4882a593Smuzhiyun 
172*4882a593Smuzhiyun 		/* If no further CPU is available,
173*4882a593Smuzhiyun 		 * cpumask_next returns >= nr_cpu_ids
174*4882a593Smuzhiyun 		 */
175*4882a593Smuzhiyun 		tx_cpu = cpumask_next(napi_cpu, dhd->cpumask_primary_new);
176*4882a593Smuzhiyun 		if (tx_cpu >= nr_cpu_ids)
177*4882a593Smuzhiyun 			tx_cpu = 0;
178*4882a593Smuzhiyun 	}
179*4882a593Smuzhiyun 
180*4882a593Smuzhiyun 	DHD_INFO(("%s After primary CPU check napi_cpu %d tx_cpu %d\n",
181*4882a593Smuzhiyun 		__FUNCTION__, napi_cpu, tx_cpu));
182*4882a593Smuzhiyun 
183*4882a593Smuzhiyun 	/* -- Now check for the CPUs from the secondary mask -- */
184*4882a593Smuzhiyun 	secondary_available_cpus = cpumask_weight(dhd->cpumask_secondary_new);
185*4882a593Smuzhiyun 
186*4882a593Smuzhiyun 	DHD_INFO(("%s Available secondary cpus %d nr_cpu_ids %d\n",
187*4882a593Smuzhiyun 		__FUNCTION__, secondary_available_cpus, nr_cpu_ids));
188*4882a593Smuzhiyun 
189*4882a593Smuzhiyun 	if (secondary_available_cpus > 0) {
190*4882a593Smuzhiyun 		/* At this point if napi_cpu is unassigned it means no CPU
191*4882a593Smuzhiyun 		 * is online from Primary Group
192*4882a593Smuzhiyun 		 */
193*4882a593Smuzhiyun #if defined(DHD_LB_TXP_LITTLE_CORE_CTRL)
194*4882a593Smuzhiyun 		/* Clear tx_cpu, so that it can be picked from little core */
195*4882a593Smuzhiyun 		tx_cpu = 0;
196*4882a593Smuzhiyun #endif /* DHD_LB_TXP_LITTLE_CORE_CTRL */
197*4882a593Smuzhiyun 		if (napi_cpu == 0) {
198*4882a593Smuzhiyun 			napi_cpu = cpumask_first(dhd->cpumask_secondary_new);
199*4882a593Smuzhiyun 			tx_cpu = cpumask_next(napi_cpu, dhd->cpumask_secondary_new);
200*4882a593Smuzhiyun 		} else if (tx_cpu == 0) {
201*4882a593Smuzhiyun 			tx_cpu = cpumask_first(dhd->cpumask_secondary_new);
202*4882a593Smuzhiyun 		}
203*4882a593Smuzhiyun 
204*4882a593Smuzhiyun 		/* If no CPU was available for tx processing, choose CPU 0 */
205*4882a593Smuzhiyun 		if (tx_cpu >= nr_cpu_ids)
206*4882a593Smuzhiyun 			tx_cpu = 0;
207*4882a593Smuzhiyun 	}
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun 	if ((primary_available_cpus == 0) &&
210*4882a593Smuzhiyun 		(secondary_available_cpus == 0)) {
211*4882a593Smuzhiyun 		/* No CPUs available from primary or secondary mask */
212*4882a593Smuzhiyun 		napi_cpu = 1;
213*4882a593Smuzhiyun 		tx_cpu = 2;
214*4882a593Smuzhiyun 	}
215*4882a593Smuzhiyun 
216*4882a593Smuzhiyun 	DHD_INFO(("%s After secondary CPU check napi_cpu %d tx_cpu %d\n",
217*4882a593Smuzhiyun 		__FUNCTION__, napi_cpu, tx_cpu));
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun 	ASSERT(napi_cpu < nr_cpu_ids);
220*4882a593Smuzhiyun 	ASSERT(tx_cpu < nr_cpu_ids);
221*4882a593Smuzhiyun 
222*4882a593Smuzhiyun 	atomic_set(&dhd->rx_napi_cpu, napi_cpu);
223*4882a593Smuzhiyun 	atomic_set(&dhd->tx_cpu, tx_cpu);
224*4882a593Smuzhiyun 
225*4882a593Smuzhiyun 	return;
226*4882a593Smuzhiyun }
227*4882a593Smuzhiyun 
228*4882a593Smuzhiyun /*
229*4882a593Smuzhiyun  * Function to handle CPU Hotplug notifications.
230*4882a593Smuzhiyun  * One of the task it does is to trigger the CPU Candidacy algorithm
231*4882a593Smuzhiyun  * for load balancing.
232*4882a593Smuzhiyun  */
233*4882a593Smuzhiyun 
234*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0))
235*4882a593Smuzhiyun 
dhd_cpu_startup_callback(unsigned int cpu)236*4882a593Smuzhiyun int dhd_cpu_startup_callback(unsigned int cpu)
237*4882a593Smuzhiyun {
238*4882a593Smuzhiyun 	dhd_info_t *dhd = g_dhd_pub->info;
239*4882a593Smuzhiyun 
240*4882a593Smuzhiyun 	DHD_INFO(("%s(): \r\n cpu:%d", __FUNCTION__, cpu));
241*4882a593Smuzhiyun 	DHD_LB_STATS_INCR(dhd->cpu_online_cnt[cpu]);
242*4882a593Smuzhiyun 	cpumask_set_cpu(cpu, dhd->cpumask_curr_avail);
243*4882a593Smuzhiyun 	dhd_select_cpu_candidacy(dhd);
244*4882a593Smuzhiyun 
245*4882a593Smuzhiyun 	return 0;
246*4882a593Smuzhiyun }
247*4882a593Smuzhiyun 
dhd_cpu_teardown_callback(unsigned int cpu)248*4882a593Smuzhiyun int dhd_cpu_teardown_callback(unsigned int cpu)
249*4882a593Smuzhiyun {
250*4882a593Smuzhiyun 	dhd_info_t *dhd = g_dhd_pub->info;
251*4882a593Smuzhiyun 
252*4882a593Smuzhiyun 	DHD_INFO(("%s(): \r\n cpu:%d", __FUNCTION__, cpu));
253*4882a593Smuzhiyun 	DHD_LB_STATS_INCR(dhd->cpu_offline_cnt[cpu]);
254*4882a593Smuzhiyun 	cpumask_clear_cpu(cpu, dhd->cpumask_curr_avail);
255*4882a593Smuzhiyun 	dhd_select_cpu_candidacy(dhd);
256*4882a593Smuzhiyun 
257*4882a593Smuzhiyun 	return 0;
258*4882a593Smuzhiyun }
259*4882a593Smuzhiyun #else
260*4882a593Smuzhiyun int
dhd_cpu_callback(struct notifier_block * nfb,unsigned long action,void * hcpu)261*4882a593Smuzhiyun dhd_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu)
262*4882a593Smuzhiyun {
263*4882a593Smuzhiyun 	unsigned long int cpu = (unsigned long int)hcpu;
264*4882a593Smuzhiyun 	dhd_info_t *dhd;
265*4882a593Smuzhiyun 
266*4882a593Smuzhiyun 	GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
267*4882a593Smuzhiyun 	dhd = container_of(nfb, dhd_info_t, cpu_notifier);
268*4882a593Smuzhiyun 	GCC_DIAGNOSTIC_POP();
269*4882a593Smuzhiyun 
270*4882a593Smuzhiyun 	if (!dhd || !(dhd->dhd_state & DHD_ATTACH_STATE_LB_ATTACH_DONE)) {
271*4882a593Smuzhiyun 		DHD_INFO(("%s(): LB data is not initialized yet.\n",
272*4882a593Smuzhiyun 			__FUNCTION__));
273*4882a593Smuzhiyun 		return NOTIFY_BAD;
274*4882a593Smuzhiyun 	}
275*4882a593Smuzhiyun 
276*4882a593Smuzhiyun 	/* XXX: Do we need other action types ? */
277*4882a593Smuzhiyun 	switch (action)
278*4882a593Smuzhiyun 	{
279*4882a593Smuzhiyun 		case CPU_ONLINE:
280*4882a593Smuzhiyun 		case CPU_ONLINE_FROZEN:
281*4882a593Smuzhiyun 			DHD_LB_STATS_INCR(dhd->cpu_online_cnt[cpu]);
282*4882a593Smuzhiyun 			cpumask_set_cpu(cpu, dhd->cpumask_curr_avail);
283*4882a593Smuzhiyun 			dhd_select_cpu_candidacy(dhd);
284*4882a593Smuzhiyun 			break;
285*4882a593Smuzhiyun 
286*4882a593Smuzhiyun 		case CPU_DOWN_PREPARE:
287*4882a593Smuzhiyun 		case CPU_DOWN_PREPARE_FROZEN:
288*4882a593Smuzhiyun 			DHD_LB_STATS_INCR(dhd->cpu_offline_cnt[cpu]);
289*4882a593Smuzhiyun 			cpumask_clear_cpu(cpu, dhd->cpumask_curr_avail);
290*4882a593Smuzhiyun 			dhd_select_cpu_candidacy(dhd);
291*4882a593Smuzhiyun 			break;
292*4882a593Smuzhiyun 		default:
293*4882a593Smuzhiyun 			break;
294*4882a593Smuzhiyun 	}
295*4882a593Smuzhiyun 
296*4882a593Smuzhiyun 	return NOTIFY_OK;
297*4882a593Smuzhiyun }
298*4882a593Smuzhiyun #endif /* LINUX_VERSION_CODE < 4.10.0 */
299*4882a593Smuzhiyun 
dhd_register_cpuhp_callback(dhd_info_t * dhd)300*4882a593Smuzhiyun int dhd_register_cpuhp_callback(dhd_info_t *dhd)
301*4882a593Smuzhiyun {
302*4882a593Smuzhiyun 	int cpuhp_ret = 0;
303*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0))
304*4882a593Smuzhiyun 	cpuhp_ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "dhd",
305*4882a593Smuzhiyun 		dhd_cpu_startup_callback, dhd_cpu_teardown_callback);
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun 	if (cpuhp_ret < 0) {
308*4882a593Smuzhiyun 		DHD_ERROR(("%s(): cpuhp_setup_state failed %d RX LB won't happen \r\n",
309*4882a593Smuzhiyun 			__FUNCTION__, cpuhp_ret));
310*4882a593Smuzhiyun 	}
311*4882a593Smuzhiyun #else
312*4882a593Smuzhiyun 	/*
313*4882a593Smuzhiyun 	 * If we are able to initialize CPU masks, lets register to the
314*4882a593Smuzhiyun 	 * CPU Hotplug framework to change the CPU for each job dynamically
315*4882a593Smuzhiyun 	 * using candidacy algorithm.
316*4882a593Smuzhiyun 	 */
317*4882a593Smuzhiyun 	dhd->cpu_notifier.notifier_call = dhd_cpu_callback;
318*4882a593Smuzhiyun 	register_hotcpu_notifier(&dhd->cpu_notifier); /* Register a callback */
319*4882a593Smuzhiyun #endif /* LINUX_VERSION_CODE < 4.10.0 */
320*4882a593Smuzhiyun 	return cpuhp_ret;
321*4882a593Smuzhiyun }
322*4882a593Smuzhiyun 
dhd_unregister_cpuhp_callback(dhd_info_t * dhd)323*4882a593Smuzhiyun int dhd_unregister_cpuhp_callback(dhd_info_t *dhd)
324*4882a593Smuzhiyun {
325*4882a593Smuzhiyun 	int ret = 0;
326*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0))
327*4882a593Smuzhiyun 	/* Don't want to call tear down while unregistering */
328*4882a593Smuzhiyun 	cpuhp_remove_state_nocalls(CPUHP_AP_ONLINE_DYN);
329*4882a593Smuzhiyun #else
330*4882a593Smuzhiyun 	if (dhd->cpu_notifier.notifier_call != NULL) {
331*4882a593Smuzhiyun 		unregister_cpu_notifier(&dhd->cpu_notifier);
332*4882a593Smuzhiyun 	}
333*4882a593Smuzhiyun #endif
334*4882a593Smuzhiyun 	return ret;
335*4882a593Smuzhiyun }
336*4882a593Smuzhiyun 
337*4882a593Smuzhiyun #if defined(DHD_LB_STATS)
dhd_lb_stats_reset(dhd_pub_t * dhdp)338*4882a593Smuzhiyun void dhd_lb_stats_reset(dhd_pub_t *dhdp)
339*4882a593Smuzhiyun {
340*4882a593Smuzhiyun 	dhd_info_t *dhd;
341*4882a593Smuzhiyun 	int i, j, num_cpus = num_possible_cpus();
342*4882a593Smuzhiyun 
343*4882a593Smuzhiyun 	if (dhdp == NULL) {
344*4882a593Smuzhiyun 		DHD_ERROR(("%s dhd pub pointer is NULL \n",
345*4882a593Smuzhiyun 			__FUNCTION__));
346*4882a593Smuzhiyun 		return;
347*4882a593Smuzhiyun 	}
348*4882a593Smuzhiyun 
349*4882a593Smuzhiyun 	dhd = dhdp->info;
350*4882a593Smuzhiyun 	if (dhd == NULL) {
351*4882a593Smuzhiyun 		DHD_ERROR(("%s(): DHD pointer is NULL \n", __FUNCTION__));
352*4882a593Smuzhiyun 		return;
353*4882a593Smuzhiyun 	}
354*4882a593Smuzhiyun 
355*4882a593Smuzhiyun 	DHD_LB_STATS_CLR(dhd->dhd_dpc_cnt);
356*4882a593Smuzhiyun 	DHD_LB_STATS_CLR(dhd->napi_sched_cnt);
357*4882a593Smuzhiyun 
358*4882a593Smuzhiyun 	/* reset NAPI latency stats */
359*4882a593Smuzhiyun 	if (dhd->napi_latency) {
360*4882a593Smuzhiyun 		bzero(dhd->napi_latency, DHD_NAPI_LATENCY_SIZE);
361*4882a593Smuzhiyun 	}
362*4882a593Smuzhiyun 	/* reset NAPI per cpu stats */
363*4882a593Smuzhiyun 	if (dhd->napi_percpu_run_cnt) {
364*4882a593Smuzhiyun 		for (i = 0; i < num_cpus; i++) {
365*4882a593Smuzhiyun 			DHD_LB_STATS_CLR(dhd->napi_percpu_run_cnt[i]);
366*4882a593Smuzhiyun 		}
367*4882a593Smuzhiyun 	}
368*4882a593Smuzhiyun 
369*4882a593Smuzhiyun 	DHD_LB_STATS_CLR(dhd->rxc_sched_cnt);
370*4882a593Smuzhiyun 
371*4882a593Smuzhiyun 	if (dhd->rxc_percpu_run_cnt) {
372*4882a593Smuzhiyun 		for (i = 0; i < num_cpus; i++) {
373*4882a593Smuzhiyun 			DHD_LB_STATS_CLR(dhd->rxc_percpu_run_cnt[i]);
374*4882a593Smuzhiyun 		}
375*4882a593Smuzhiyun 	}
376*4882a593Smuzhiyun 
377*4882a593Smuzhiyun 	DHD_LB_STATS_CLR(dhd->txc_sched_cnt);
378*4882a593Smuzhiyun 
379*4882a593Smuzhiyun 	if (dhd->txc_percpu_run_cnt) {
380*4882a593Smuzhiyun 		for (i = 0; i < num_cpus; i++) {
381*4882a593Smuzhiyun 			DHD_LB_STATS_CLR(dhd->txc_percpu_run_cnt[i]);
382*4882a593Smuzhiyun 		}
383*4882a593Smuzhiyun 	}
384*4882a593Smuzhiyun 
385*4882a593Smuzhiyun 	if (dhd->txp_percpu_run_cnt) {
386*4882a593Smuzhiyun 		for (i = 0; i < num_cpus; i++) {
387*4882a593Smuzhiyun 			DHD_LB_STATS_CLR(dhd->txp_percpu_run_cnt[i]);
388*4882a593Smuzhiyun 		}
389*4882a593Smuzhiyun 	}
390*4882a593Smuzhiyun 
391*4882a593Smuzhiyun 	if (dhd->tx_start_percpu_run_cnt) {
392*4882a593Smuzhiyun 		for (i = 0; i < num_cpus; i++) {
393*4882a593Smuzhiyun 			DHD_LB_STATS_CLR(dhd->tx_start_percpu_run_cnt[i]);
394*4882a593Smuzhiyun 		}
395*4882a593Smuzhiyun 	}
396*4882a593Smuzhiyun 
397*4882a593Smuzhiyun 	for (j = 0; j < HIST_BIN_SIZE; j++) {
398*4882a593Smuzhiyun 		for (i = 0; i < num_cpus; i++) {
399*4882a593Smuzhiyun 			DHD_LB_STATS_CLR(dhd->napi_rx_hist[j][i]);
400*4882a593Smuzhiyun 		}
401*4882a593Smuzhiyun 	}
402*4882a593Smuzhiyun 
403*4882a593Smuzhiyun 	dhd->pub.lb_rxp_strt_thr_hitcnt = 0;
404*4882a593Smuzhiyun 	dhd->pub.lb_rxp_stop_thr_hitcnt = 0;
405*4882a593Smuzhiyun 
406*4882a593Smuzhiyun 	dhd->pub.lb_rxp_napi_sched_cnt = 0;
407*4882a593Smuzhiyun 	dhd->pub.lb_rxp_napi_complete_cnt = 0;
408*4882a593Smuzhiyun 	return;
409*4882a593Smuzhiyun }
410*4882a593Smuzhiyun 
dhd_lb_stats_init(dhd_pub_t * dhdp)411*4882a593Smuzhiyun void dhd_lb_stats_init(dhd_pub_t *dhdp)
412*4882a593Smuzhiyun {
413*4882a593Smuzhiyun 	dhd_info_t *dhd;
414*4882a593Smuzhiyun 	int i, j, num_cpus = num_possible_cpus();
415*4882a593Smuzhiyun 	int alloc_size = sizeof(uint32) * num_cpus;
416*4882a593Smuzhiyun 
417*4882a593Smuzhiyun 	if (dhdp == NULL) {
418*4882a593Smuzhiyun 		DHD_ERROR(("%s(): Invalid argument dhd pubb pointer is NULL \n",
419*4882a593Smuzhiyun 			__FUNCTION__));
420*4882a593Smuzhiyun 		return;
421*4882a593Smuzhiyun 	}
422*4882a593Smuzhiyun 
423*4882a593Smuzhiyun 	dhd = dhdp->info;
424*4882a593Smuzhiyun 	if (dhd == NULL) {
425*4882a593Smuzhiyun 		DHD_ERROR(("%s(): DHD pointer is NULL \n", __FUNCTION__));
426*4882a593Smuzhiyun 		return;
427*4882a593Smuzhiyun 	}
428*4882a593Smuzhiyun 
429*4882a593Smuzhiyun 	DHD_LB_STATS_CLR(dhd->dhd_dpc_cnt);
430*4882a593Smuzhiyun 	DHD_LB_STATS_CLR(dhd->napi_sched_cnt);
431*4882a593Smuzhiyun 
432*4882a593Smuzhiyun 	/* NAPI latency stats */
433*4882a593Smuzhiyun 	dhd->napi_latency = (uint64 *)MALLOCZ(dhdp->osh, DHD_NAPI_LATENCY_SIZE);
434*4882a593Smuzhiyun 	/* NAPI per cpu stats */
435*4882a593Smuzhiyun 	dhd->napi_percpu_run_cnt = (uint32 *)MALLOC(dhdp->osh, alloc_size);
436*4882a593Smuzhiyun 	if (!dhd->napi_percpu_run_cnt) {
437*4882a593Smuzhiyun 		DHD_ERROR(("%s(): napi_percpu_run_cnt malloc failed \n",
438*4882a593Smuzhiyun 			__FUNCTION__));
439*4882a593Smuzhiyun 		return;
440*4882a593Smuzhiyun 	}
441*4882a593Smuzhiyun 	for (i = 0; i < num_cpus; i++)
442*4882a593Smuzhiyun 		DHD_LB_STATS_CLR(dhd->napi_percpu_run_cnt[i]);
443*4882a593Smuzhiyun 
444*4882a593Smuzhiyun 	DHD_LB_STATS_CLR(dhd->rxc_sched_cnt);
445*4882a593Smuzhiyun 
446*4882a593Smuzhiyun 	dhd->rxc_percpu_run_cnt = (uint32 *)MALLOC(dhdp->osh, alloc_size);
447*4882a593Smuzhiyun 	if (!dhd->rxc_percpu_run_cnt) {
448*4882a593Smuzhiyun 		DHD_ERROR(("%s(): rxc_percpu_run_cnt malloc failed \n",
449*4882a593Smuzhiyun 			__FUNCTION__));
450*4882a593Smuzhiyun 		return;
451*4882a593Smuzhiyun 	}
452*4882a593Smuzhiyun 	for (i = 0; i < num_cpus; i++)
453*4882a593Smuzhiyun 		DHD_LB_STATS_CLR(dhd->rxc_percpu_run_cnt[i]);
454*4882a593Smuzhiyun 
455*4882a593Smuzhiyun 	DHD_LB_STATS_CLR(dhd->txc_sched_cnt);
456*4882a593Smuzhiyun 
457*4882a593Smuzhiyun 	dhd->txc_percpu_run_cnt = (uint32 *)MALLOC(dhdp->osh, alloc_size);
458*4882a593Smuzhiyun 	if (!dhd->txc_percpu_run_cnt) {
459*4882a593Smuzhiyun 		DHD_ERROR(("%s(): txc_percpu_run_cnt malloc failed \n",
460*4882a593Smuzhiyun 			__FUNCTION__));
461*4882a593Smuzhiyun 		return;
462*4882a593Smuzhiyun 	}
463*4882a593Smuzhiyun 	for (i = 0; i < num_cpus; i++)
464*4882a593Smuzhiyun 		DHD_LB_STATS_CLR(dhd->txc_percpu_run_cnt[i]);
465*4882a593Smuzhiyun 
466*4882a593Smuzhiyun 	dhd->cpu_online_cnt = (uint32 *)MALLOC(dhdp->osh, alloc_size);
467*4882a593Smuzhiyun 	if (!dhd->cpu_online_cnt) {
468*4882a593Smuzhiyun 		DHD_ERROR(("%s(): cpu_online_cnt malloc failed \n",
469*4882a593Smuzhiyun 			__FUNCTION__));
470*4882a593Smuzhiyun 		return;
471*4882a593Smuzhiyun 	}
472*4882a593Smuzhiyun 	for (i = 0; i < num_cpus; i++)
473*4882a593Smuzhiyun 		DHD_LB_STATS_CLR(dhd->cpu_online_cnt[i]);
474*4882a593Smuzhiyun 
475*4882a593Smuzhiyun 	dhd->cpu_offline_cnt = (uint32 *)MALLOC(dhdp->osh, alloc_size);
476*4882a593Smuzhiyun 	if (!dhd->cpu_offline_cnt) {
477*4882a593Smuzhiyun 		DHD_ERROR(("%s(): cpu_offline_cnt malloc failed \n",
478*4882a593Smuzhiyun 			__FUNCTION__));
479*4882a593Smuzhiyun 		return;
480*4882a593Smuzhiyun 	}
481*4882a593Smuzhiyun 	for (i = 0; i < num_cpus; i++)
482*4882a593Smuzhiyun 		DHD_LB_STATS_CLR(dhd->cpu_offline_cnt[i]);
483*4882a593Smuzhiyun 
484*4882a593Smuzhiyun 	dhd->txp_percpu_run_cnt = (uint32 *)MALLOC(dhdp->osh, alloc_size);
485*4882a593Smuzhiyun 	if (!dhd->txp_percpu_run_cnt) {
486*4882a593Smuzhiyun 		DHD_ERROR(("%s(): txp_percpu_run_cnt malloc failed \n",
487*4882a593Smuzhiyun 			__FUNCTION__));
488*4882a593Smuzhiyun 		return;
489*4882a593Smuzhiyun 	}
490*4882a593Smuzhiyun 	for (i = 0; i < num_cpus; i++)
491*4882a593Smuzhiyun 		DHD_LB_STATS_CLR(dhd->txp_percpu_run_cnt[i]);
492*4882a593Smuzhiyun 
493*4882a593Smuzhiyun 	dhd->tx_start_percpu_run_cnt = (uint32 *)MALLOC(dhdp->osh, alloc_size);
494*4882a593Smuzhiyun 	if (!dhd->tx_start_percpu_run_cnt) {
495*4882a593Smuzhiyun 		DHD_ERROR(("%s(): tx_start_percpu_run_cnt malloc failed \n",
496*4882a593Smuzhiyun 			__FUNCTION__));
497*4882a593Smuzhiyun 		return;
498*4882a593Smuzhiyun 	}
499*4882a593Smuzhiyun 	for (i = 0; i < num_cpus; i++)
500*4882a593Smuzhiyun 		DHD_LB_STATS_CLR(dhd->tx_start_percpu_run_cnt[i]);
501*4882a593Smuzhiyun 
502*4882a593Smuzhiyun 	for (j = 0; j < HIST_BIN_SIZE; j++) {
503*4882a593Smuzhiyun 		dhd->napi_rx_hist[j] = (uint32 *)MALLOC(dhdp->osh, alloc_size);
504*4882a593Smuzhiyun 		if (!dhd->napi_rx_hist[j]) {
505*4882a593Smuzhiyun 			DHD_ERROR(("%s(): dhd->napi_rx_hist[%d] malloc failed \n",
506*4882a593Smuzhiyun 				__FUNCTION__, j));
507*4882a593Smuzhiyun 			return;
508*4882a593Smuzhiyun 		}
509*4882a593Smuzhiyun 		for (i = 0; i < num_cpus; i++) {
510*4882a593Smuzhiyun 			DHD_LB_STATS_CLR(dhd->napi_rx_hist[j][i]);
511*4882a593Smuzhiyun 		}
512*4882a593Smuzhiyun 	}
513*4882a593Smuzhiyun 
514*4882a593Smuzhiyun 	dhd->pub.lb_rxp_strt_thr_hitcnt = 0;
515*4882a593Smuzhiyun 	dhd->pub.lb_rxp_stop_thr_hitcnt = 0;
516*4882a593Smuzhiyun 
517*4882a593Smuzhiyun 	dhd->pub.lb_rxp_napi_sched_cnt = 0;
518*4882a593Smuzhiyun 	dhd->pub.lb_rxp_napi_complete_cnt = 0;
519*4882a593Smuzhiyun 	return;
520*4882a593Smuzhiyun }
521*4882a593Smuzhiyun 
dhd_lb_stats_deinit(dhd_pub_t * dhdp)522*4882a593Smuzhiyun void dhd_lb_stats_deinit(dhd_pub_t *dhdp)
523*4882a593Smuzhiyun {
524*4882a593Smuzhiyun 	dhd_info_t *dhd;
525*4882a593Smuzhiyun 	int j, num_cpus = num_possible_cpus();
526*4882a593Smuzhiyun 	int alloc_size = sizeof(uint32) * num_cpus;
527*4882a593Smuzhiyun 
528*4882a593Smuzhiyun 	if (dhdp == NULL) {
529*4882a593Smuzhiyun 		DHD_ERROR(("%s(): Invalid argument dhd pubb pointer is NULL \n",
530*4882a593Smuzhiyun 			__FUNCTION__));
531*4882a593Smuzhiyun 		return;
532*4882a593Smuzhiyun 	}
533*4882a593Smuzhiyun 
534*4882a593Smuzhiyun 	dhd = dhdp->info;
535*4882a593Smuzhiyun 	if (dhd == NULL) {
536*4882a593Smuzhiyun 		DHD_ERROR(("%s(): DHD pointer is NULL \n", __FUNCTION__));
537*4882a593Smuzhiyun 		return;
538*4882a593Smuzhiyun 	}
539*4882a593Smuzhiyun 
540*4882a593Smuzhiyun 	if (dhd->napi_percpu_run_cnt) {
541*4882a593Smuzhiyun 		MFREE(dhdp->osh, dhd->napi_percpu_run_cnt, alloc_size);
542*4882a593Smuzhiyun 	}
543*4882a593Smuzhiyun 	if (dhd->rxc_percpu_run_cnt) {
544*4882a593Smuzhiyun 		MFREE(dhdp->osh, dhd->rxc_percpu_run_cnt, alloc_size);
545*4882a593Smuzhiyun 	}
546*4882a593Smuzhiyun 	if (dhd->txc_percpu_run_cnt) {
547*4882a593Smuzhiyun 		MFREE(dhdp->osh, dhd->txc_percpu_run_cnt, alloc_size);
548*4882a593Smuzhiyun 	}
549*4882a593Smuzhiyun 	if (dhd->cpu_online_cnt) {
550*4882a593Smuzhiyun 		MFREE(dhdp->osh, dhd->cpu_online_cnt, alloc_size);
551*4882a593Smuzhiyun 	}
552*4882a593Smuzhiyun 	if (dhd->cpu_offline_cnt) {
553*4882a593Smuzhiyun 		MFREE(dhdp->osh, dhd->cpu_offline_cnt, alloc_size);
554*4882a593Smuzhiyun 	}
555*4882a593Smuzhiyun 
556*4882a593Smuzhiyun 	if (dhd->txp_percpu_run_cnt) {
557*4882a593Smuzhiyun 		MFREE(dhdp->osh, dhd->txp_percpu_run_cnt, alloc_size);
558*4882a593Smuzhiyun 	}
559*4882a593Smuzhiyun 	if (dhd->tx_start_percpu_run_cnt) {
560*4882a593Smuzhiyun 		MFREE(dhdp->osh, dhd->tx_start_percpu_run_cnt, alloc_size);
561*4882a593Smuzhiyun 	}
562*4882a593Smuzhiyun 	if (dhd->napi_latency) {
563*4882a593Smuzhiyun 		MFREE(dhdp->osh, dhd->napi_latency, DHD_NAPI_LATENCY_SIZE);
564*4882a593Smuzhiyun 	}
565*4882a593Smuzhiyun 
566*4882a593Smuzhiyun 	for (j = 0; j < HIST_BIN_SIZE; j++) {
567*4882a593Smuzhiyun 		if (dhd->napi_rx_hist[j]) {
568*4882a593Smuzhiyun 			MFREE(dhdp->osh, dhd->napi_rx_hist[j], alloc_size);
569*4882a593Smuzhiyun 		}
570*4882a593Smuzhiyun 	}
571*4882a593Smuzhiyun 
572*4882a593Smuzhiyun 	return;
573*4882a593Smuzhiyun }
574*4882a593Smuzhiyun 
dhd_lb_stats_dump_napi_latency(dhd_pub_t * dhdp,struct bcmstrbuf * strbuf,uint64 * napi_latency)575*4882a593Smuzhiyun void dhd_lb_stats_dump_napi_latency(dhd_pub_t *dhdp,
576*4882a593Smuzhiyun 	struct bcmstrbuf *strbuf, uint64 *napi_latency)
577*4882a593Smuzhiyun {
578*4882a593Smuzhiyun 	uint32 i;
579*4882a593Smuzhiyun 
580*4882a593Smuzhiyun 	bcm_bprintf(strbuf, "napi-latency(us): \t count\n");
581*4882a593Smuzhiyun 	for (i = 0; i < DHD_NUM_NAPI_LATENCY_ROWS; i++) {
582*4882a593Smuzhiyun 		bcm_bprintf(strbuf, "%16u: \t %llu\n", 1U<<i, napi_latency[i]);
583*4882a593Smuzhiyun 	}
584*4882a593Smuzhiyun }
585*4882a593Smuzhiyun 
dhd_lb_stats_dump_histo(dhd_pub_t * dhdp,struct bcmstrbuf * strbuf,uint32 ** hist)586*4882a593Smuzhiyun void dhd_lb_stats_dump_histo(dhd_pub_t *dhdp,
587*4882a593Smuzhiyun 	struct bcmstrbuf *strbuf, uint32 **hist)
588*4882a593Smuzhiyun {
589*4882a593Smuzhiyun 	int i, j;
590*4882a593Smuzhiyun 	uint32 *per_cpu_total;
591*4882a593Smuzhiyun 	uint32 total = 0;
592*4882a593Smuzhiyun 	uint32 num_cpus = num_possible_cpus();
593*4882a593Smuzhiyun 
594*4882a593Smuzhiyun 	per_cpu_total = (uint32 *)MALLOC(dhdp->osh, sizeof(uint32) * num_cpus);
595*4882a593Smuzhiyun 	if (!per_cpu_total) {
596*4882a593Smuzhiyun 		DHD_ERROR(("%s(): dhd->per_cpu_total malloc failed \n", __FUNCTION__));
597*4882a593Smuzhiyun 		return;
598*4882a593Smuzhiyun 	}
599*4882a593Smuzhiyun 	bzero(per_cpu_total, sizeof(uint32) * num_cpus);
600*4882a593Smuzhiyun 
601*4882a593Smuzhiyun 	bcm_bprintf(strbuf, "CPU: \t\t");
602*4882a593Smuzhiyun 	for (i = 0; i < num_cpus; i++)
603*4882a593Smuzhiyun 		bcm_bprintf(strbuf, "%d\t", i);
604*4882a593Smuzhiyun 	bcm_bprintf(strbuf, "\nBin\n");
605*4882a593Smuzhiyun 
606*4882a593Smuzhiyun 	for (i = 0; i < HIST_BIN_SIZE; i++) {
607*4882a593Smuzhiyun 		bcm_bprintf(strbuf, "%d:\t\t", 1<<i);
608*4882a593Smuzhiyun 		for (j = 0; j < num_cpus; j++) {
609*4882a593Smuzhiyun 			bcm_bprintf(strbuf, "%d\t", hist[i][j]);
610*4882a593Smuzhiyun 		}
611*4882a593Smuzhiyun 		bcm_bprintf(strbuf, "\n");
612*4882a593Smuzhiyun 	}
613*4882a593Smuzhiyun 	bcm_bprintf(strbuf, "Per CPU Total \t");
614*4882a593Smuzhiyun 	total = 0;
615*4882a593Smuzhiyun 	for (i = 0; i < num_cpus; i++) {
616*4882a593Smuzhiyun 		for (j = 0; j < HIST_BIN_SIZE; j++) {
617*4882a593Smuzhiyun 			per_cpu_total[i] += (hist[j][i] * (1<<j));
618*4882a593Smuzhiyun 		}
619*4882a593Smuzhiyun 		bcm_bprintf(strbuf, "%d\t", per_cpu_total[i]);
620*4882a593Smuzhiyun 		total += per_cpu_total[i];
621*4882a593Smuzhiyun 	}
622*4882a593Smuzhiyun 	bcm_bprintf(strbuf, "\nTotal\t\t%d \n", total);
623*4882a593Smuzhiyun 
624*4882a593Smuzhiyun 	if (per_cpu_total) {
625*4882a593Smuzhiyun 		MFREE(dhdp->osh, per_cpu_total, sizeof(uint32) * num_cpus);
626*4882a593Smuzhiyun 	}
627*4882a593Smuzhiyun 	return;
628*4882a593Smuzhiyun }
629*4882a593Smuzhiyun 
dhd_lb_stats_dump_cpu_array(struct bcmstrbuf * strbuf,uint32 * p)630*4882a593Smuzhiyun void dhd_lb_stats_dump_cpu_array(struct bcmstrbuf *strbuf, uint32 *p)
631*4882a593Smuzhiyun {
632*4882a593Smuzhiyun 	int i, num_cpus = num_possible_cpus();
633*4882a593Smuzhiyun 
634*4882a593Smuzhiyun 	bcm_bprintf(strbuf, "CPU: \t\t");
635*4882a593Smuzhiyun 	for (i = 0; i < num_cpus; i++)
636*4882a593Smuzhiyun 		bcm_bprintf(strbuf, "%d\t", i);
637*4882a593Smuzhiyun 	bcm_bprintf(strbuf, "\n");
638*4882a593Smuzhiyun 
639*4882a593Smuzhiyun 	bcm_bprintf(strbuf, "Val: \t\t");
640*4882a593Smuzhiyun 	for (i = 0; i < num_cpus; i++)
641*4882a593Smuzhiyun 		bcm_bprintf(strbuf, "%u\t", *(p+i));
642*4882a593Smuzhiyun 	bcm_bprintf(strbuf, "\n");
643*4882a593Smuzhiyun 	return;
644*4882a593Smuzhiyun }
645*4882a593Smuzhiyun 
646*4882a593Smuzhiyun #ifdef DHD_MEM_STATS
dhd_lb_mem_usage(dhd_pub_t * dhdp,struct bcmstrbuf * strbuf)647*4882a593Smuzhiyun uint64 dhd_lb_mem_usage(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf)
648*4882a593Smuzhiyun {
649*4882a593Smuzhiyun 	dhd_info_t *dhd;
650*4882a593Smuzhiyun 	uint16 rxbufpost_sz;
651*4882a593Smuzhiyun 	uint16 rx_post_active = 0;
652*4882a593Smuzhiyun 	uint16 rx_cmpl_active = 0;
653*4882a593Smuzhiyun 	uint64 rx_path_memory_usage = 0;
654*4882a593Smuzhiyun 
655*4882a593Smuzhiyun 	if (dhdp == NULL || strbuf == NULL) {
656*4882a593Smuzhiyun 		DHD_ERROR(("%s(): Invalid argument dhdp %p strbuf %p \n",
657*4882a593Smuzhiyun 			__FUNCTION__, dhdp, strbuf));
658*4882a593Smuzhiyun 		return 0;
659*4882a593Smuzhiyun 	}
660*4882a593Smuzhiyun 
661*4882a593Smuzhiyun 	dhd = dhdp->info;
662*4882a593Smuzhiyun 	if (dhd == NULL) {
663*4882a593Smuzhiyun 		DHD_ERROR(("%s(): DHD pointer is NULL \n", __FUNCTION__));
664*4882a593Smuzhiyun 		return 0;
665*4882a593Smuzhiyun 	}
666*4882a593Smuzhiyun 	rxbufpost_sz = dhd_prot_get_rxbufpost_sz(dhdp);
667*4882a593Smuzhiyun 	if (rxbufpost_sz == 0) {
668*4882a593Smuzhiyun 		rxbufpost_sz = DHD_FLOWRING_RX_BUFPOST_PKTSZ;
669*4882a593Smuzhiyun 	}
670*4882a593Smuzhiyun 	rx_path_memory_usage = rxbufpost_sz * (skb_queue_len(&dhd->rx_pend_queue) +
671*4882a593Smuzhiyun 		skb_queue_len(&dhd->rx_napi_queue) +
672*4882a593Smuzhiyun 		skb_queue_len(&dhd->rx_process_queue));
673*4882a593Smuzhiyun 	rx_post_active = dhd_prot_get_h2d_rx_post_active(dhdp);
674*4882a593Smuzhiyun 	if (rx_post_active != 0) {
675*4882a593Smuzhiyun 		rx_path_memory_usage += (rxbufpost_sz * rx_post_active);
676*4882a593Smuzhiyun 	}
677*4882a593Smuzhiyun 
678*4882a593Smuzhiyun 	rx_cmpl_active = dhd_prot_get_d2h_rx_cpln_active(dhdp);
679*4882a593Smuzhiyun 	if (rx_cmpl_active != 0) {
680*4882a593Smuzhiyun 		rx_path_memory_usage += (rxbufpost_sz * rx_cmpl_active);
681*4882a593Smuzhiyun 	}
682*4882a593Smuzhiyun 
683*4882a593Smuzhiyun 	dhdp->rxpath_mem = rx_path_memory_usage;
684*4882a593Smuzhiyun 	bcm_bprintf(strbuf, "\nrxbufpost_sz: %d rx_post_active: %d rx_cmpl_active: %d "
685*4882a593Smuzhiyun 		"pend_queue_len: %d napi_queue_len: %d process_queue_len: %d\n",
686*4882a593Smuzhiyun 		rxbufpost_sz, rx_post_active, rx_cmpl_active,
687*4882a593Smuzhiyun 		skb_queue_len(&dhd->rx_pend_queue),
688*4882a593Smuzhiyun 		skb_queue_len(&dhd->rx_napi_queue), skb_queue_len(&dhd->rx_process_queue));
689*4882a593Smuzhiyun 	bcm_bprintf(strbuf, "DHD rx-path memory_usage: %llubytes %lluKB \n",
690*4882a593Smuzhiyun 		rx_path_memory_usage, (rx_path_memory_usage/ 1024));
691*4882a593Smuzhiyun 	return rx_path_memory_usage;
692*4882a593Smuzhiyun }
693*4882a593Smuzhiyun #endif /* DHD_MEM_STATS */
694*4882a593Smuzhiyun 
dhd_lb_stats_dump(dhd_pub_t * dhdp,struct bcmstrbuf * strbuf)695*4882a593Smuzhiyun void dhd_lb_stats_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf)
696*4882a593Smuzhiyun {
697*4882a593Smuzhiyun 	dhd_info_t *dhd;
698*4882a593Smuzhiyun 
699*4882a593Smuzhiyun 	if (dhdp == NULL || strbuf == NULL) {
700*4882a593Smuzhiyun 		DHD_ERROR(("%s(): Invalid argument dhdp %p strbuf %p \n",
701*4882a593Smuzhiyun 			__FUNCTION__, dhdp, strbuf));
702*4882a593Smuzhiyun 		return;
703*4882a593Smuzhiyun 	}
704*4882a593Smuzhiyun 
705*4882a593Smuzhiyun 	dhd = dhdp->info;
706*4882a593Smuzhiyun 	if (dhd == NULL) {
707*4882a593Smuzhiyun 		DHD_ERROR(("%s(): DHD pointer is NULL \n", __FUNCTION__));
708*4882a593Smuzhiyun 		return;
709*4882a593Smuzhiyun 	}
710*4882a593Smuzhiyun 
711*4882a593Smuzhiyun 	bcm_bprintf(strbuf, "\ncpu_online_cnt:\n");
712*4882a593Smuzhiyun 	dhd_lb_stats_dump_cpu_array(strbuf, dhd->cpu_online_cnt);
713*4882a593Smuzhiyun 
714*4882a593Smuzhiyun 	bcm_bprintf(strbuf, "\ncpu_offline_cnt:\n");
715*4882a593Smuzhiyun 	dhd_lb_stats_dump_cpu_array(strbuf, dhd->cpu_offline_cnt);
716*4882a593Smuzhiyun 
717*4882a593Smuzhiyun 	bcm_bprintf(strbuf, "\nsched_cnt: dhd_dpc %u napi %u rxc %u txc %u\n",
718*4882a593Smuzhiyun 		dhd->dhd_dpc_cnt, dhd->napi_sched_cnt, dhd->rxc_sched_cnt,
719*4882a593Smuzhiyun 		dhd->txc_sched_cnt);
720*4882a593Smuzhiyun 
721*4882a593Smuzhiyun 	bcm_bprintf(strbuf, "\nCPUs: dpc_cpu %u napi_cpu %u net_tx_cpu %u tx_cpu %u\n",
722*4882a593Smuzhiyun 		atomic_read(&dhd->dpc_cpu),
723*4882a593Smuzhiyun 		atomic_read(&dhd->rx_napi_cpu),
724*4882a593Smuzhiyun 		atomic_read(&dhd->net_tx_cpu),
725*4882a593Smuzhiyun 		atomic_read(&dhd->tx_cpu));
726*4882a593Smuzhiyun 
727*4882a593Smuzhiyun #ifdef DHD_LB_RXP
728*4882a593Smuzhiyun 	bcm_bprintf(strbuf, "\nnapi_percpu_run_cnt:\n");
729*4882a593Smuzhiyun 	dhd_lb_stats_dump_cpu_array(strbuf, dhd->napi_percpu_run_cnt);
730*4882a593Smuzhiyun 	bcm_bprintf(strbuf, "\nNAPI Packets Received Histogram:\n");
731*4882a593Smuzhiyun 	dhd_lb_stats_dump_histo(dhdp, strbuf, dhd->napi_rx_hist);
732*4882a593Smuzhiyun 	bcm_bprintf(strbuf, "\nNAPI poll latency stats ie from napi schedule to napi execution\n");
733*4882a593Smuzhiyun 	dhd_lb_stats_dump_napi_latency(dhdp, strbuf, dhd->napi_latency);
734*4882a593Smuzhiyun #endif /* DHD_LB_RXP */
735*4882a593Smuzhiyun 
736*4882a593Smuzhiyun #ifdef DHD_LB_TXP
737*4882a593Smuzhiyun 	bcm_bprintf(strbuf, "\ntxp_percpu_run_cnt:\n");
738*4882a593Smuzhiyun 	dhd_lb_stats_dump_cpu_array(strbuf, dhd->txp_percpu_run_cnt);
739*4882a593Smuzhiyun 
740*4882a593Smuzhiyun 	bcm_bprintf(strbuf, "\ntx_start_percpu_run_cnt:\n");
741*4882a593Smuzhiyun 	dhd_lb_stats_dump_cpu_array(strbuf, dhd->tx_start_percpu_run_cnt);
742*4882a593Smuzhiyun #endif /* DHD_LB_TXP */
743*4882a593Smuzhiyun }
744*4882a593Smuzhiyun 
dhd_lb_stats_update_napi_latency(uint64 * bin,uint32 latency)745*4882a593Smuzhiyun void dhd_lb_stats_update_napi_latency(uint64 *bin, uint32 latency)
746*4882a593Smuzhiyun {
747*4882a593Smuzhiyun 	uint64 *p;
748*4882a593Smuzhiyun 	uint32 bin_power;
749*4882a593Smuzhiyun 	bin_power = next_larger_power2(latency);
750*4882a593Smuzhiyun 
751*4882a593Smuzhiyun 	switch (bin_power) {
752*4882a593Smuzhiyun 		case   1: p = bin + 0; break;
753*4882a593Smuzhiyun 		case   2: p = bin + 1; break;
754*4882a593Smuzhiyun 		case   4: p = bin + 2; break;
755*4882a593Smuzhiyun 		case   8: p = bin + 3; break;
756*4882a593Smuzhiyun 		case  16: p = bin + 4; break;
757*4882a593Smuzhiyun 		case  32: p = bin + 5; break;
758*4882a593Smuzhiyun 		case  64: p = bin + 6; break;
759*4882a593Smuzhiyun 		case 128: p = bin + 7; break;
760*4882a593Smuzhiyun 		case 256: p = bin + 8; break;
761*4882a593Smuzhiyun 		case 512: p = bin + 9; break;
762*4882a593Smuzhiyun 		case 1024: p = bin + 10; break;
763*4882a593Smuzhiyun 		case 2048: p = bin + 11; break;
764*4882a593Smuzhiyun 		case 4096: p = bin + 12; break;
765*4882a593Smuzhiyun 		case 8192: p = bin + 13; break;
766*4882a593Smuzhiyun 		case 16384: p = bin + 14; break;
767*4882a593Smuzhiyun 		case 32768: p = bin + 15; break;
768*4882a593Smuzhiyun 		default : p = bin + 16; break;
769*4882a593Smuzhiyun 	}
770*4882a593Smuzhiyun 	ASSERT((p - bin) < DHD_NUM_NAPI_LATENCY_ROWS);
771*4882a593Smuzhiyun 	*p = *p + 1;
772*4882a593Smuzhiyun 	return;
773*4882a593Smuzhiyun 
774*4882a593Smuzhiyun }
775*4882a593Smuzhiyun 
dhd_lb_stats_update_histo(uint32 ** bin,uint32 count,uint32 cpu)776*4882a593Smuzhiyun void dhd_lb_stats_update_histo(uint32 **bin, uint32 count, uint32 cpu)
777*4882a593Smuzhiyun {
778*4882a593Smuzhiyun 	uint32 bin_power;
779*4882a593Smuzhiyun 	uint32 *p;
780*4882a593Smuzhiyun 	bin_power = next_larger_power2(count);
781*4882a593Smuzhiyun 
782*4882a593Smuzhiyun 	switch (bin_power) {
783*4882a593Smuzhiyun 		case   1: p = bin[0] + cpu; break;
784*4882a593Smuzhiyun 		case   2: p = bin[1] + cpu; break;
785*4882a593Smuzhiyun 		case   4: p = bin[2] + cpu; break;
786*4882a593Smuzhiyun 		case   8: p = bin[3] + cpu; break;
787*4882a593Smuzhiyun 		case  16: p = bin[4] + cpu; break;
788*4882a593Smuzhiyun 		case  32: p = bin[5] + cpu; break;
789*4882a593Smuzhiyun 		case  64: p = bin[6] + cpu; break;
790*4882a593Smuzhiyun 		case 128: p = bin[7] + cpu; break;
791*4882a593Smuzhiyun 		default : p = bin[8] + cpu; break;
792*4882a593Smuzhiyun 	}
793*4882a593Smuzhiyun 
794*4882a593Smuzhiyun 	*p = *p + 1;
795*4882a593Smuzhiyun 	return;
796*4882a593Smuzhiyun }
797*4882a593Smuzhiyun 
dhd_lb_stats_update_napi_histo(dhd_pub_t * dhdp,uint32 count)798*4882a593Smuzhiyun void dhd_lb_stats_update_napi_histo(dhd_pub_t *dhdp, uint32 count)
799*4882a593Smuzhiyun {
800*4882a593Smuzhiyun 	int cpu;
801*4882a593Smuzhiyun 	dhd_info_t *dhd = dhdp->info;
802*4882a593Smuzhiyun 
803*4882a593Smuzhiyun 	cpu = get_cpu();
804*4882a593Smuzhiyun 	put_cpu();
805*4882a593Smuzhiyun 	dhd_lb_stats_update_histo(dhd->napi_rx_hist, count, cpu);
806*4882a593Smuzhiyun 
807*4882a593Smuzhiyun 	return;
808*4882a593Smuzhiyun }
809*4882a593Smuzhiyun 
dhd_lb_stats_update_txc_histo(dhd_pub_t * dhdp,uint32 count)810*4882a593Smuzhiyun void dhd_lb_stats_update_txc_histo(dhd_pub_t *dhdp, uint32 count)
811*4882a593Smuzhiyun {
812*4882a593Smuzhiyun 	int cpu;
813*4882a593Smuzhiyun 	dhd_info_t *dhd = dhdp->info;
814*4882a593Smuzhiyun 
815*4882a593Smuzhiyun 	cpu = get_cpu();
816*4882a593Smuzhiyun 	put_cpu();
817*4882a593Smuzhiyun 	dhd_lb_stats_update_histo(dhd->txc_hist, count, cpu);
818*4882a593Smuzhiyun 
819*4882a593Smuzhiyun 	return;
820*4882a593Smuzhiyun }
821*4882a593Smuzhiyun 
dhd_lb_stats_update_rxc_histo(dhd_pub_t * dhdp,uint32 count)822*4882a593Smuzhiyun void dhd_lb_stats_update_rxc_histo(dhd_pub_t *dhdp, uint32 count)
823*4882a593Smuzhiyun {
824*4882a593Smuzhiyun 	int cpu;
825*4882a593Smuzhiyun 	dhd_info_t *dhd = dhdp->info;
826*4882a593Smuzhiyun 
827*4882a593Smuzhiyun 	cpu = get_cpu();
828*4882a593Smuzhiyun 	put_cpu();
829*4882a593Smuzhiyun 	dhd_lb_stats_update_histo(dhd->rxc_hist, count, cpu);
830*4882a593Smuzhiyun 
831*4882a593Smuzhiyun 	return;
832*4882a593Smuzhiyun }
833*4882a593Smuzhiyun 
dhd_lb_stats_txc_percpu_cnt_incr(dhd_pub_t * dhdp)834*4882a593Smuzhiyun void dhd_lb_stats_txc_percpu_cnt_incr(dhd_pub_t *dhdp)
835*4882a593Smuzhiyun {
836*4882a593Smuzhiyun 	dhd_info_t *dhd = dhdp->info;
837*4882a593Smuzhiyun 	DHD_LB_STATS_PERCPU_ARR_INCR(dhd->txc_percpu_run_cnt);
838*4882a593Smuzhiyun }
839*4882a593Smuzhiyun 
dhd_lb_stats_rxc_percpu_cnt_incr(dhd_pub_t * dhdp)840*4882a593Smuzhiyun void dhd_lb_stats_rxc_percpu_cnt_incr(dhd_pub_t *dhdp)
841*4882a593Smuzhiyun {
842*4882a593Smuzhiyun 	dhd_info_t *dhd = dhdp->info;
843*4882a593Smuzhiyun 	DHD_LB_STATS_PERCPU_ARR_INCR(dhd->rxc_percpu_run_cnt);
844*4882a593Smuzhiyun }
845*4882a593Smuzhiyun #endif /* DHD_LB_STATS */
846*4882a593Smuzhiyun 
847*4882a593Smuzhiyun /**
848*4882a593Smuzhiyun  * dhd_tasklet_schedule - Function that runs in IPI context of the destination
849*4882a593Smuzhiyun  * CPU and schedules a tasklet.
850*4882a593Smuzhiyun  * @tasklet: opaque pointer to the tasklet
851*4882a593Smuzhiyun  */
852*4882a593Smuzhiyun INLINE void
dhd_tasklet_schedule(void * tasklet)853*4882a593Smuzhiyun dhd_tasklet_schedule(void *tasklet)
854*4882a593Smuzhiyun {
855*4882a593Smuzhiyun 	tasklet_schedule((struct tasklet_struct *)tasklet);
856*4882a593Smuzhiyun }
857*4882a593Smuzhiyun 
858*4882a593Smuzhiyun /**
859*4882a593Smuzhiyun  * dhd_work_schedule_on - Executes the passed work in a given CPU
860*4882a593Smuzhiyun  * @work: work to be scheduled
861*4882a593Smuzhiyun  * @on_cpu: cpu core id
862*4882a593Smuzhiyun  *
863*4882a593Smuzhiyun  * If the requested cpu is online, then an IPI is sent to this cpu via the
864*4882a593Smuzhiyun  * schedule_work_on and the work function
865*4882a593Smuzhiyun  * will be invoked to schedule the specified work on the requested CPU.
866*4882a593Smuzhiyun  */
867*4882a593Smuzhiyun 
868*4882a593Smuzhiyun INLINE void
dhd_work_schedule_on(struct work_struct * work,int on_cpu)869*4882a593Smuzhiyun dhd_work_schedule_on(struct work_struct *work, int on_cpu)
870*4882a593Smuzhiyun {
871*4882a593Smuzhiyun 	schedule_work_on(on_cpu, work);
872*4882a593Smuzhiyun }
873*4882a593Smuzhiyun 
874*4882a593Smuzhiyun INLINE void
dhd_delayed_work_schedule_on(struct delayed_work * dwork,int on_cpu,ulong delay)875*4882a593Smuzhiyun dhd_delayed_work_schedule_on(struct delayed_work *dwork, int on_cpu, ulong delay)
876*4882a593Smuzhiyun {
877*4882a593Smuzhiyun 	schedule_delayed_work_on(on_cpu, dwork, delay);
878*4882a593Smuzhiyun }
879*4882a593Smuzhiyun 
880*4882a593Smuzhiyun #if defined(DHD_LB_TXP)
dhd_tx_dispatcher_work(struct work_struct * work)881*4882a593Smuzhiyun void dhd_tx_dispatcher_work(struct work_struct * work)
882*4882a593Smuzhiyun {
883*4882a593Smuzhiyun 	struct dhd_info *dhd;
884*4882a593Smuzhiyun 
885*4882a593Smuzhiyun 	GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
886*4882a593Smuzhiyun 	dhd = container_of(work, struct dhd_info, tx_dispatcher_work);
887*4882a593Smuzhiyun 	GCC_DIAGNOSTIC_POP();
888*4882a593Smuzhiyun 
889*4882a593Smuzhiyun 	dhd_tasklet_schedule(&dhd->tx_tasklet);
890*4882a593Smuzhiyun }
891*4882a593Smuzhiyun 
892*4882a593Smuzhiyun /**
893*4882a593Smuzhiyun  * dhd_lb_tx_dispatch - load balance by dispatching the tx_tasklet
894*4882a593Smuzhiyun  * on another cpu. The tx_tasklet will take care of actually putting
895*4882a593Smuzhiyun  * the skbs into appropriate flow ring and ringing H2D interrupt
896*4882a593Smuzhiyun  *
897*4882a593Smuzhiyun  * @dhdp: pointer to dhd_pub object
898*4882a593Smuzhiyun  */
899*4882a593Smuzhiyun void
dhd_lb_tx_dispatch(dhd_pub_t * dhdp)900*4882a593Smuzhiyun dhd_lb_tx_dispatch(dhd_pub_t *dhdp)
901*4882a593Smuzhiyun {
902*4882a593Smuzhiyun 	dhd_info_t *dhd = dhdp->info;
903*4882a593Smuzhiyun 	int curr_cpu;
904*4882a593Smuzhiyun 	int tx_cpu;
905*4882a593Smuzhiyun 	int prev_net_tx_cpu;
906*4882a593Smuzhiyun 
907*4882a593Smuzhiyun 	/*
908*4882a593Smuzhiyun 	 * Get cpu will disable pre-ermption and will not allow any cpu to go offline
909*4882a593Smuzhiyun 	 * and call put_cpu() only after scheduling rx_napi_dispatcher_work.
910*4882a593Smuzhiyun 	 */
911*4882a593Smuzhiyun 	curr_cpu = get_cpu();
912*4882a593Smuzhiyun 
913*4882a593Smuzhiyun 	/* Record the CPU in which the TX request from Network stack came */
914*4882a593Smuzhiyun 	prev_net_tx_cpu = atomic_read(&dhd->net_tx_cpu);
915*4882a593Smuzhiyun 	atomic_set(&dhd->net_tx_cpu, curr_cpu);
916*4882a593Smuzhiyun 
917*4882a593Smuzhiyun 	tx_cpu = atomic_read(&dhd->tx_cpu);
918*4882a593Smuzhiyun 
919*4882a593Smuzhiyun 	/*
920*4882a593Smuzhiyun 	 * Avoid cpu candidacy, if override is set via sysfs for changing cpu mannually
921*4882a593Smuzhiyun 	 */
922*4882a593Smuzhiyun 	if (dhd->dhd_lb_candidacy_override) {
923*4882a593Smuzhiyun 		if (!cpu_online(tx_cpu)) {
924*4882a593Smuzhiyun 			tx_cpu = curr_cpu;
925*4882a593Smuzhiyun 		}
926*4882a593Smuzhiyun 	} else {
927*4882a593Smuzhiyun 		/*
928*4882a593Smuzhiyun 		 * Now if the NET TX has scheduled in the same CPU
929*4882a593Smuzhiyun 		 * that is chosen for Tx processing
930*4882a593Smuzhiyun 		 * OR scheduled on different cpu than previously it was scheduled,
931*4882a593Smuzhiyun 		 * OR if tx_cpu is offline,
932*4882a593Smuzhiyun 		 * Call cpu candidacy algorithm to recompute tx_cpu.
933*4882a593Smuzhiyun 		 */
934*4882a593Smuzhiyun 		if ((curr_cpu == tx_cpu) || (curr_cpu != prev_net_tx_cpu) ||
935*4882a593Smuzhiyun 			!cpu_online(tx_cpu)) {
936*4882a593Smuzhiyun 			/* Re compute LB CPUs */
937*4882a593Smuzhiyun 			dhd_select_cpu_candidacy(dhd);
938*4882a593Smuzhiyun 			/* Use updated tx cpu */
939*4882a593Smuzhiyun 			tx_cpu = atomic_read(&dhd->tx_cpu);
940*4882a593Smuzhiyun 		}
941*4882a593Smuzhiyun 	}
942*4882a593Smuzhiyun 	/*
943*4882a593Smuzhiyun 	 * Schedule tx_dispatcher_work to on the cpu which
944*4882a593Smuzhiyun 	 * in turn will schedule tx_tasklet.
945*4882a593Smuzhiyun 	 */
946*4882a593Smuzhiyun 	dhd_work_schedule_on(&dhd->tx_dispatcher_work, tx_cpu);
947*4882a593Smuzhiyun 
948*4882a593Smuzhiyun 	put_cpu();
949*4882a593Smuzhiyun }
950*4882a593Smuzhiyun #endif /* DHD_LB_TXP */
951*4882a593Smuzhiyun 
952*4882a593Smuzhiyun #if defined(DHD_LB_RXP)
953*4882a593Smuzhiyun 
954*4882a593Smuzhiyun /**
955*4882a593Smuzhiyun  * dhd_napi_poll - Load balance napi poll function to process received
956*4882a593Smuzhiyun  * packets and send up the network stack using netif_receive_skb()
957*4882a593Smuzhiyun  *
958*4882a593Smuzhiyun  * @napi: napi object in which context this poll function is invoked
959*4882a593Smuzhiyun  * @budget: number of packets to be processed.
960*4882a593Smuzhiyun  *
961*4882a593Smuzhiyun  * Fetch the dhd_info given the rx_napi_struct. Move all packets from the
962*4882a593Smuzhiyun  * rx_napi_queue into a local rx_process_queue (lock and queue move and unlock).
963*4882a593Smuzhiyun  * Dequeue each packet from head of rx_process_queue, fetch the ifid from the
964*4882a593Smuzhiyun  * packet tag and sendup.
965*4882a593Smuzhiyun  */
966*4882a593Smuzhiyun int
dhd_napi_poll(struct napi_struct * napi,int budget)967*4882a593Smuzhiyun dhd_napi_poll(struct napi_struct *napi, int budget)
968*4882a593Smuzhiyun {
969*4882a593Smuzhiyun 	int ifid;
970*4882a593Smuzhiyun 	const int pkt_count = 1;
971*4882a593Smuzhiyun 	const int chan = 0;
972*4882a593Smuzhiyun 	struct sk_buff * skb;
973*4882a593Smuzhiyun 	unsigned long flags;
974*4882a593Smuzhiyun 	struct dhd_info *dhd;
975*4882a593Smuzhiyun 	int processed = 0;
976*4882a593Smuzhiyun 	int dpc_cpu;
977*4882a593Smuzhiyun #ifdef DHD_LB_STATS
978*4882a593Smuzhiyun 	uint32 napi_latency;
979*4882a593Smuzhiyun #endif /* DHD_LB_STATS */
980*4882a593Smuzhiyun 
981*4882a593Smuzhiyun 	GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
982*4882a593Smuzhiyun 	dhd = container_of(napi, struct dhd_info, rx_napi_struct);
983*4882a593Smuzhiyun 	GCC_DIAGNOSTIC_POP();
984*4882a593Smuzhiyun 
985*4882a593Smuzhiyun #ifdef DHD_LB_STATS
986*4882a593Smuzhiyun 	napi_latency = (uint32)(OSL_SYSUPTIME_US() - dhd->napi_schedule_time);
987*4882a593Smuzhiyun 	dhd_lb_stats_update_napi_latency(dhd->napi_latency, napi_latency);
988*4882a593Smuzhiyun #endif /* DHD_LB_STATS */
989*4882a593Smuzhiyun 	DHD_LB_INFO(("%s napi_queue<%d> budget<%d>\n",
990*4882a593Smuzhiyun 		__FUNCTION__, skb_queue_len(&dhd->rx_napi_queue), budget));
991*4882a593Smuzhiyun 
992*4882a593Smuzhiyun 	/*
993*4882a593Smuzhiyun 	 * Extract the entire rx_napi_queue into another rx_process_queue
994*4882a593Smuzhiyun 	 * and process only 'budget' number of skbs from rx_process_queue.
995*4882a593Smuzhiyun 	 * If there are more items to be processed, napi poll will be rescheduled
996*4882a593Smuzhiyun 	 * During the next iteration, next set of skbs from
997*4882a593Smuzhiyun 	 * rx_napi_queue will be extracted and attached to the tail of rx_process_queue.
998*4882a593Smuzhiyun 	 * Again budget number of skbs will be processed from rx_process_queue.
999*4882a593Smuzhiyun 	 * If there are less than budget number of skbs in rx_process_queue,
1000*4882a593Smuzhiyun 	 * call napi_complete to stop rescheduling napi poll.
1001*4882a593Smuzhiyun 	 */
1002*4882a593Smuzhiyun 	DHD_RX_NAPI_QUEUE_LOCK(&dhd->rx_napi_queue.lock, flags);
1003*4882a593Smuzhiyun 	skb_queue_splice_tail_init(&dhd->rx_napi_queue, &dhd->rx_process_queue);
1004*4882a593Smuzhiyun 	DHD_RX_NAPI_QUEUE_UNLOCK(&dhd->rx_napi_queue.lock, flags);
1005*4882a593Smuzhiyun 
1006*4882a593Smuzhiyun 	while ((processed < budget) && (skb = __skb_dequeue(&dhd->rx_process_queue)) != NULL) {
1007*4882a593Smuzhiyun 		OSL_PREFETCH(skb->data);
1008*4882a593Smuzhiyun 
1009*4882a593Smuzhiyun 		ifid = DHD_PKTTAG_IFID((dhd_pkttag_fr_t *)PKTTAG(skb));
1010*4882a593Smuzhiyun 
1011*4882a593Smuzhiyun 		DHD_LB_INFO(("%s dhd_rx_frame pkt<%p> ifid<%d>\n",
1012*4882a593Smuzhiyun 			__FUNCTION__, skb, ifid));
1013*4882a593Smuzhiyun 
1014*4882a593Smuzhiyun 		dhd_rx_frame(&dhd->pub, ifid, skb, pkt_count, chan);
1015*4882a593Smuzhiyun 		processed++;
1016*4882a593Smuzhiyun 	}
1017*4882a593Smuzhiyun 
1018*4882a593Smuzhiyun 	if (atomic_read(&dhd->pub.lb_rxp_flow_ctrl) &&
1019*4882a593Smuzhiyun 		(dhd_lb_rxp_process_qlen(&dhd->pub) <= dhd->pub.lb_rxp_strt_thr)) {
1020*4882a593Smuzhiyun 		/*
1021*4882a593Smuzhiyun 		 * If the dpc CPU is online Schedule dhd_dpc_dispatcher_work on the dpc cpu which
1022*4882a593Smuzhiyun 		 * in turn will schedule dpc tasklet. Else schedule dpc takslet.
1023*4882a593Smuzhiyun 		 */
1024*4882a593Smuzhiyun 		get_cpu();
1025*4882a593Smuzhiyun 		dpc_cpu = atomic_read(&dhd->dpc_cpu);
1026*4882a593Smuzhiyun 		if (!cpu_online(dpc_cpu)) {
1027*4882a593Smuzhiyun 			dhd_tasklet_schedule(&dhd->tasklet);
1028*4882a593Smuzhiyun 		} else {
1029*4882a593Smuzhiyun 			dhd_delayed_work_schedule_on(&dhd->dhd_dpc_dispatcher_work, dpc_cpu, 0);
1030*4882a593Smuzhiyun 		}
1031*4882a593Smuzhiyun 		put_cpu();
1032*4882a593Smuzhiyun 	}
1033*4882a593Smuzhiyun 	DHD_LB_STATS_UPDATE_NAPI_HISTO(&dhd->pub, processed);
1034*4882a593Smuzhiyun 
1035*4882a593Smuzhiyun 	DHD_LB_INFO(("%s processed %d\n", __FUNCTION__, processed));
1036*4882a593Smuzhiyun 
1037*4882a593Smuzhiyun 	/*
1038*4882a593Smuzhiyun 	 * Signal napi complete only when no more packets are processed and
1039*4882a593Smuzhiyun 	 * none are left in the enqueued queue.
1040*4882a593Smuzhiyun 	 */
1041*4882a593Smuzhiyun 	if ((processed == 0) && (skb_queue_len(&dhd->rx_napi_queue) == 0)) {
1042*4882a593Smuzhiyun 		napi_complete(napi);
1043*4882a593Smuzhiyun #ifdef DHD_LB_STATS
1044*4882a593Smuzhiyun 		dhd->pub.lb_rxp_napi_complete_cnt++;
1045*4882a593Smuzhiyun #endif /* DHD_LB_STATS */
1046*4882a593Smuzhiyun 		DHD_GENERAL_LOCK(&dhd->pub, flags);
1047*4882a593Smuzhiyun 		DHD_BUS_BUSY_CLEAR_IN_NAPI(&dhd->pub);
1048*4882a593Smuzhiyun 		DHD_GENERAL_UNLOCK(&dhd->pub, flags);
1049*4882a593Smuzhiyun 		return 0;
1050*4882a593Smuzhiyun 	}
1051*4882a593Smuzhiyun 
1052*4882a593Smuzhiyun #ifdef DHD_LB_STATS
1053*4882a593Smuzhiyun 	dhd->napi_schedule_time = OSL_SYSUPTIME_US();
1054*4882a593Smuzhiyun #endif /* DHD_LB_STATS */
1055*4882a593Smuzhiyun 
1056*4882a593Smuzhiyun 	/* Return budget so that it gets rescheduled immediately */
1057*4882a593Smuzhiyun 	return budget;
1058*4882a593Smuzhiyun }
1059*4882a593Smuzhiyun 
1060*4882a593Smuzhiyun /**
1061*4882a593Smuzhiyun  * dhd_napi_schedule - Place the napi struct into the current cpus softnet napi
1062*4882a593Smuzhiyun  * poll list. This function may be invoked via the smp_call_function_single
1063*4882a593Smuzhiyun  * from a remote CPU.
1064*4882a593Smuzhiyun  *
1065*4882a593Smuzhiyun  * This function will essentially invoke __raise_softirq_irqoff(NET_RX_SOFTIRQ)
1066*4882a593Smuzhiyun  * after the napi_struct is added to the softnet data's poll_list
1067*4882a593Smuzhiyun  *
1068*4882a593Smuzhiyun  * @info: pointer to a dhd_info struct
1069*4882a593Smuzhiyun  */
1070*4882a593Smuzhiyun static void
dhd_napi_schedule(void * info)1071*4882a593Smuzhiyun dhd_napi_schedule(void *info)
1072*4882a593Smuzhiyun {
1073*4882a593Smuzhiyun 	dhd_info_t *dhd = (dhd_info_t *)info;
1074*4882a593Smuzhiyun 	unsigned long flags;
1075*4882a593Smuzhiyun 
1076*4882a593Smuzhiyun 	DHD_INFO(("%s rx_napi_struct<%p> on cpu<%d>\n",
1077*4882a593Smuzhiyun 		__FUNCTION__, &dhd->rx_napi_struct, atomic_read(&dhd->rx_napi_cpu)));
1078*4882a593Smuzhiyun 
1079*4882a593Smuzhiyun 	/* add napi_struct to softnet data poll list and raise NET_RX_SOFTIRQ */
1080*4882a593Smuzhiyun 	if (napi_schedule_prep(&dhd->rx_napi_struct)) {
1081*4882a593Smuzhiyun 
1082*4882a593Smuzhiyun 		/*
1083*4882a593Smuzhiyun 		 * Set busbusystate in NAPI, which will be cleared after
1084*4882a593Smuzhiyun 		 * napi_complete from napi_poll context
1085*4882a593Smuzhiyun 		 */
1086*4882a593Smuzhiyun 		DHD_GENERAL_LOCK(&dhd->pub, flags);
1087*4882a593Smuzhiyun 		DHD_BUS_BUSY_SET_IN_NAPI(&dhd->pub);
1088*4882a593Smuzhiyun 		DHD_GENERAL_UNLOCK(&dhd->pub, flags);
1089*4882a593Smuzhiyun 
1090*4882a593Smuzhiyun #ifdef DHD_LB_STATS
1091*4882a593Smuzhiyun 		dhd->napi_schedule_time = OSL_SYSUPTIME_US();
1092*4882a593Smuzhiyun 		dhd->pub.lb_rxp_napi_sched_cnt++;
1093*4882a593Smuzhiyun #endif /* DHD_LB_STATS */
1094*4882a593Smuzhiyun 		__napi_schedule(&dhd->rx_napi_struct);
1095*4882a593Smuzhiyun #ifdef WAKEUP_KSOFTIRQD_POST_NAPI_SCHEDULE
1096*4882a593Smuzhiyun 		raise_softirq(NET_RX_SOFTIRQ);
1097*4882a593Smuzhiyun #endif /* WAKEUP_KSOFTIRQD_POST_NAPI_SCHEDULE */
1098*4882a593Smuzhiyun 	}
1099*4882a593Smuzhiyun 
1100*4882a593Smuzhiyun 	/*
1101*4882a593Smuzhiyun 	 * If the rx_napi_struct was already running, then we let it complete
1102*4882a593Smuzhiyun 	 * processing all its packets. The rx_napi_struct may only run on one
1103*4882a593Smuzhiyun 	 * core at a time, to avoid out-of-order handling.
1104*4882a593Smuzhiyun 	 */
1105*4882a593Smuzhiyun }
1106*4882a593Smuzhiyun 
1107*4882a593Smuzhiyun /**
1108*4882a593Smuzhiyun  * dhd_napi_schedule_on - API to schedule on a desired CPU core a NET_RX_SOFTIRQ
1109*4882a593Smuzhiyun  * action after placing the dhd's rx_process napi object in the the remote CPU's
1110*4882a593Smuzhiyun  * softnet data's poll_list.
1111*4882a593Smuzhiyun  *
1112*4882a593Smuzhiyun  * @dhd: dhd_info which has the rx_process napi object
1113*4882a593Smuzhiyun  * @on_cpu: desired remote CPU id
1114*4882a593Smuzhiyun  */
1115*4882a593Smuzhiyun static INLINE int
dhd_napi_schedule_on(dhd_info_t * dhd,int on_cpu)1116*4882a593Smuzhiyun dhd_napi_schedule_on(dhd_info_t *dhd, int on_cpu)
1117*4882a593Smuzhiyun {
1118*4882a593Smuzhiyun 	int wait = 0; /* asynchronous IPI */
1119*4882a593Smuzhiyun 	DHD_INFO(("%s dhd<%p> napi<%p> on_cpu<%d>\n",
1120*4882a593Smuzhiyun 		__FUNCTION__, dhd, &dhd->rx_napi_struct, on_cpu));
1121*4882a593Smuzhiyun 
1122*4882a593Smuzhiyun 	if (smp_call_function_single(on_cpu, dhd_napi_schedule, dhd, wait)) {
1123*4882a593Smuzhiyun 		DHD_ERROR(("%s smp_call_function_single on_cpu<%d> failed\n",
1124*4882a593Smuzhiyun 			__FUNCTION__, on_cpu));
1125*4882a593Smuzhiyun 	}
1126*4882a593Smuzhiyun 
1127*4882a593Smuzhiyun 	DHD_LB_STATS_INCR(dhd->napi_sched_cnt);
1128*4882a593Smuzhiyun 
1129*4882a593Smuzhiyun 	return 0;
1130*4882a593Smuzhiyun }
1131*4882a593Smuzhiyun 
1132*4882a593Smuzhiyun /*
1133*4882a593Smuzhiyun  * Call get_online_cpus/put_online_cpus around dhd_napi_schedule_on
1134*4882a593Smuzhiyun  * Why should we do this?
1135*4882a593Smuzhiyun  * The candidacy algorithm is run from the call back function
1136*4882a593Smuzhiyun  * registered to CPU hotplug notifier. This call back happens from Worker
1137*4882a593Smuzhiyun  * context. The dhd_napi_schedule_on is also from worker context.
1138*4882a593Smuzhiyun  * Note that both of this can run on two different CPUs at the same time.
1139*4882a593Smuzhiyun  * So we can possibly have a window where a given CPUn is being brought
1140*4882a593Smuzhiyun  * down from CPUm while we try to run a function on CPUn.
1141*4882a593Smuzhiyun  * To prevent this its better have the whole code to execute an SMP
1142*4882a593Smuzhiyun  * function under get_online_cpus.
1143*4882a593Smuzhiyun  * This function call ensures that hotplug mechanism does not kick-in
1144*4882a593Smuzhiyun  * until we are done dealing with online CPUs
1145*4882a593Smuzhiyun  * If the hotplug worker is already running, no worries because the
1146*4882a593Smuzhiyun  * candidacy algo would then reflect the same in dhd->rx_napi_cpu.
1147*4882a593Smuzhiyun  *
1148*4882a593Smuzhiyun  * The below mentioned code structure is proposed in
1149*4882a593Smuzhiyun  * https://www.kernel.org/doc/Documentation/cpu-hotplug.txt
1150*4882a593Smuzhiyun  * for the question
1151*4882a593Smuzhiyun  * Q: I need to ensure that a particular cpu is not removed when there is some
1152*4882a593Smuzhiyun  *    work specific to this cpu is in progress
1153*4882a593Smuzhiyun  *
1154*4882a593Smuzhiyun  * According to the documentation calling get_online_cpus is NOT required, if
1155*4882a593Smuzhiyun  * we are running from tasklet context. Since dhd_rx_napi_dispatcher_work can
1156*4882a593Smuzhiyun  * run from Work Queue context we have to call these functions
1157*4882a593Smuzhiyun  */
dhd_rx_napi_dispatcher_work(struct work_struct * work)1158*4882a593Smuzhiyun void dhd_rx_napi_dispatcher_work(struct work_struct * work)
1159*4882a593Smuzhiyun {
1160*4882a593Smuzhiyun 	struct dhd_info *dhd;
1161*4882a593Smuzhiyun 	GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
1162*4882a593Smuzhiyun 	dhd = container_of(work, struct dhd_info, rx_napi_dispatcher_work);
1163*4882a593Smuzhiyun 	GCC_DIAGNOSTIC_POP();
1164*4882a593Smuzhiyun 
1165*4882a593Smuzhiyun 	dhd_napi_schedule(dhd);
1166*4882a593Smuzhiyun }
1167*4882a593Smuzhiyun 
1168*4882a593Smuzhiyun /**
1169*4882a593Smuzhiyun  * dhd_lb_rx_napi_dispatch - load balance by dispatching the rx_napi_struct
1170*4882a593Smuzhiyun  * to run on another CPU. The rx_napi_struct's poll function will retrieve all
1171*4882a593Smuzhiyun  * the packets enqueued into the rx_napi_queue and sendup.
1172*4882a593Smuzhiyun  * The producer's rx packet queue is appended to the rx_napi_queue before
1173*4882a593Smuzhiyun  * dispatching the rx_napi_struct.
1174*4882a593Smuzhiyun  */
1175*4882a593Smuzhiyun void
dhd_lb_rx_napi_dispatch(dhd_pub_t * dhdp)1176*4882a593Smuzhiyun dhd_lb_rx_napi_dispatch(dhd_pub_t *dhdp)
1177*4882a593Smuzhiyun {
1178*4882a593Smuzhiyun 	unsigned long flags;
1179*4882a593Smuzhiyun 	dhd_info_t *dhd = dhdp->info;
1180*4882a593Smuzhiyun 	int curr_cpu;
1181*4882a593Smuzhiyun 	int rx_napi_cpu;
1182*4882a593Smuzhiyun 	int prev_dpc_cpu;
1183*4882a593Smuzhiyun 
1184*4882a593Smuzhiyun 	if (dhd->rx_napi_netdev == NULL) {
1185*4882a593Smuzhiyun 		DHD_ERROR(("%s: dhd->rx_napi_netdev is NULL\n", __FUNCTION__));
1186*4882a593Smuzhiyun 		return;
1187*4882a593Smuzhiyun 	}
1188*4882a593Smuzhiyun 
1189*4882a593Smuzhiyun 	DHD_LB_INFO(("%s append napi_queue<%d> pend_queue<%d>\n", __FUNCTION__,
1190*4882a593Smuzhiyun 		skb_queue_len(&dhd->rx_napi_queue), skb_queue_len(&dhd->rx_pend_queue)));
1191*4882a593Smuzhiyun 
1192*4882a593Smuzhiyun 	/* append the producer's queue of packets to the napi's rx process queue */
1193*4882a593Smuzhiyun 	DHD_RX_NAPI_QUEUE_LOCK(&dhd->rx_napi_queue.lock, flags);
1194*4882a593Smuzhiyun 	skb_queue_splice_tail_init(&dhd->rx_pend_queue, &dhd->rx_napi_queue);
1195*4882a593Smuzhiyun 	DHD_RX_NAPI_QUEUE_UNLOCK(&dhd->rx_napi_queue.lock, flags);
1196*4882a593Smuzhiyun 
1197*4882a593Smuzhiyun 	/* If sysfs lb_rxp_active is not set, schedule on current cpu */
1198*4882a593Smuzhiyun 	if (!atomic_read(&dhd->lb_rxp_active))
1199*4882a593Smuzhiyun 	{
1200*4882a593Smuzhiyun 		dhd_napi_schedule(dhd);
1201*4882a593Smuzhiyun 		return;
1202*4882a593Smuzhiyun 	}
1203*4882a593Smuzhiyun 
1204*4882a593Smuzhiyun 	/*
1205*4882a593Smuzhiyun 	 * Get cpu will disable pre-ermption and will not allow any cpu to go offline
1206*4882a593Smuzhiyun 	 * and call put_cpu() only after scheduling rx_napi_dispatcher_work.
1207*4882a593Smuzhiyun 	 */
1208*4882a593Smuzhiyun 	curr_cpu = get_cpu();
1209*4882a593Smuzhiyun 
1210*4882a593Smuzhiyun 	prev_dpc_cpu = atomic_read(&dhd->prev_dpc_cpu);
1211*4882a593Smuzhiyun 
1212*4882a593Smuzhiyun 	rx_napi_cpu = atomic_read(&dhd->rx_napi_cpu);
1213*4882a593Smuzhiyun 
1214*4882a593Smuzhiyun 	/*
1215*4882a593Smuzhiyun 	 * Avoid cpu candidacy, if override is set via sysfs for changing cpu mannually
1216*4882a593Smuzhiyun 	 */
1217*4882a593Smuzhiyun 	if (dhd->dhd_lb_candidacy_override) {
1218*4882a593Smuzhiyun 		if (!cpu_online(rx_napi_cpu)) {
1219*4882a593Smuzhiyun 			rx_napi_cpu = curr_cpu;
1220*4882a593Smuzhiyun 		}
1221*4882a593Smuzhiyun 	} else {
1222*4882a593Smuzhiyun 		/*
1223*4882a593Smuzhiyun 		 * Now if the DPC has scheduled in the same CPU
1224*4882a593Smuzhiyun 		 * that is chosen for Rx napi processing
1225*4882a593Smuzhiyun 		 * OR scheduled on different cpu than previously it was scheduled,
1226*4882a593Smuzhiyun 		 * OR if rx_napi_cpu is offline,
1227*4882a593Smuzhiyun 		 * Call cpu candidacy algorithm to recompute napi_cpu.
1228*4882a593Smuzhiyun 		 */
1229*4882a593Smuzhiyun 		if ((curr_cpu == rx_napi_cpu) || (curr_cpu != prev_dpc_cpu) ||
1230*4882a593Smuzhiyun 			!cpu_online(rx_napi_cpu)) {
1231*4882a593Smuzhiyun 			/* Re compute LB CPUs */
1232*4882a593Smuzhiyun 			dhd_select_cpu_candidacy(dhd);
1233*4882a593Smuzhiyun 			/* Use updated napi cpu */
1234*4882a593Smuzhiyun 			rx_napi_cpu = atomic_read(&dhd->rx_napi_cpu);
1235*4882a593Smuzhiyun 		}
1236*4882a593Smuzhiyun 
1237*4882a593Smuzhiyun 	}
1238*4882a593Smuzhiyun 
1239*4882a593Smuzhiyun 	DHD_LB_INFO(("%s : schedule to curr_cpu : %d, rx_napi_cpu : %d\n",
1240*4882a593Smuzhiyun 		__FUNCTION__, curr_cpu, rx_napi_cpu));
1241*4882a593Smuzhiyun 	dhd_work_schedule_on(&dhd->rx_napi_dispatcher_work, rx_napi_cpu);
1242*4882a593Smuzhiyun 	DHD_LB_STATS_INCR(dhd->napi_sched_cnt);
1243*4882a593Smuzhiyun 
1244*4882a593Smuzhiyun 	put_cpu();
1245*4882a593Smuzhiyun }
1246*4882a593Smuzhiyun 
1247*4882a593Smuzhiyun /**
1248*4882a593Smuzhiyun  * dhd_lb_rx_pkt_enqueue - Enqueue the packet into the producer's queue
1249*4882a593Smuzhiyun  */
1250*4882a593Smuzhiyun void
dhd_lb_rx_pkt_enqueue(dhd_pub_t * dhdp,void * pkt,int ifidx)1251*4882a593Smuzhiyun dhd_lb_rx_pkt_enqueue(dhd_pub_t *dhdp, void *pkt, int ifidx)
1252*4882a593Smuzhiyun {
1253*4882a593Smuzhiyun 	dhd_info_t *dhd = dhdp->info;
1254*4882a593Smuzhiyun 
1255*4882a593Smuzhiyun 	DHD_LB_INFO(("%s enqueue pkt<%p> ifidx<%d> pend_queue<%d>\n", __FUNCTION__,
1256*4882a593Smuzhiyun 		pkt, ifidx, skb_queue_len(&dhd->rx_pend_queue)));
1257*4882a593Smuzhiyun 	DHD_PKTTAG_SET_IFID((dhd_pkttag_fr_t *)PKTTAG(pkt), ifidx);
1258*4882a593Smuzhiyun 	__skb_queue_tail(&dhd->rx_pend_queue, pkt);
1259*4882a593Smuzhiyun 	DHD_LB_STATS_PERCPU_ARR_INCR(dhd->napi_percpu_run_cnt);
1260*4882a593Smuzhiyun }
1261*4882a593Smuzhiyun 
1262*4882a593Smuzhiyun unsigned long
dhd_read_lb_rxp(dhd_pub_t * dhdp)1263*4882a593Smuzhiyun dhd_read_lb_rxp(dhd_pub_t *dhdp)
1264*4882a593Smuzhiyun {
1265*4882a593Smuzhiyun 	dhd_info_t *dhd = dhdp->info;
1266*4882a593Smuzhiyun 	return atomic_read(&dhd->lb_rxp_active);
1267*4882a593Smuzhiyun }
1268*4882a593Smuzhiyun 
1269*4882a593Smuzhiyun uint32
dhd_lb_rxp_process_qlen(dhd_pub_t * dhdp)1270*4882a593Smuzhiyun dhd_lb_rxp_process_qlen(dhd_pub_t *dhdp)
1271*4882a593Smuzhiyun {
1272*4882a593Smuzhiyun 	dhd_info_t *dhd = dhdp->info;
1273*4882a593Smuzhiyun 	return skb_queue_len(&dhd->rx_process_queue);
1274*4882a593Smuzhiyun }
1275*4882a593Smuzhiyun #endif /* DHD_LB_RXP */
1276*4882a593Smuzhiyun 
1277*4882a593Smuzhiyun #if defined(DHD_LB_TXP)
1278*4882a593Smuzhiyun int
BCMFASTPATH(dhd_lb_sendpkt)1279*4882a593Smuzhiyun BCMFASTPATH(dhd_lb_sendpkt)(dhd_info_t *dhd, struct net_device *net,
1280*4882a593Smuzhiyun 	int ifidx, void *skb)
1281*4882a593Smuzhiyun {
1282*4882a593Smuzhiyun 	DHD_LB_STATS_PERCPU_ARR_INCR(dhd->tx_start_percpu_run_cnt);
1283*4882a593Smuzhiyun 
1284*4882a593Smuzhiyun 	/* If the feature is disabled run-time do TX from here */
1285*4882a593Smuzhiyun 	if (atomic_read(&dhd->lb_txp_active) == 0) {
1286*4882a593Smuzhiyun 		DHD_LB_STATS_PERCPU_ARR_INCR(dhd->txp_percpu_run_cnt);
1287*4882a593Smuzhiyun 		 return __dhd_sendpkt(&dhd->pub, ifidx, skb);
1288*4882a593Smuzhiyun 	}
1289*4882a593Smuzhiyun 
1290*4882a593Smuzhiyun 	/* Store the address of net device and interface index in the Packet tag */
1291*4882a593Smuzhiyun 	DHD_LB_TX_PKTTAG_SET_NETDEV((dhd_tx_lb_pkttag_fr_t *)PKTTAG(skb), net);
1292*4882a593Smuzhiyun 	DHD_LB_TX_PKTTAG_SET_IFIDX((dhd_tx_lb_pkttag_fr_t *)PKTTAG(skb), ifidx);
1293*4882a593Smuzhiyun 
1294*4882a593Smuzhiyun 	/* Enqueue the skb into tx_pend_queue */
1295*4882a593Smuzhiyun 	skb_queue_tail(&dhd->tx_pend_queue, skb);
1296*4882a593Smuzhiyun 
1297*4882a593Smuzhiyun 	DHD_TRACE(("%s(): Added skb %p for netdev %p \r\n", __FUNCTION__, skb, net));
1298*4882a593Smuzhiyun 
1299*4882a593Smuzhiyun 	/* Dispatch the Tx job to be processed by the tx_tasklet */
1300*4882a593Smuzhiyun 	dhd_lb_tx_dispatch(&dhd->pub);
1301*4882a593Smuzhiyun 
1302*4882a593Smuzhiyun 	return NETDEV_TX_OK;
1303*4882a593Smuzhiyun }
1304*4882a593Smuzhiyun #endif /* DHD_LB_TXP */
1305*4882a593Smuzhiyun 
1306*4882a593Smuzhiyun #ifdef DHD_LB_TXP
1307*4882a593Smuzhiyun #define DHD_LB_TXBOUND	64
1308*4882a593Smuzhiyun /*
1309*4882a593Smuzhiyun  * Function that performs the TX processing on a given CPU
1310*4882a593Smuzhiyun  */
1311*4882a593Smuzhiyun bool
dhd_lb_tx_process(dhd_info_t * dhd)1312*4882a593Smuzhiyun dhd_lb_tx_process(dhd_info_t *dhd)
1313*4882a593Smuzhiyun {
1314*4882a593Smuzhiyun 	struct sk_buff *skb;
1315*4882a593Smuzhiyun 	int cnt = 0;
1316*4882a593Smuzhiyun 	struct net_device *net;
1317*4882a593Smuzhiyun 	int ifidx;
1318*4882a593Smuzhiyun 	bool resched = FALSE;
1319*4882a593Smuzhiyun 
1320*4882a593Smuzhiyun 	DHD_TRACE(("%s(): TX Processing \r\n", __FUNCTION__));
1321*4882a593Smuzhiyun 	if (dhd == NULL) {
1322*4882a593Smuzhiyun 		DHD_ERROR((" Null pointer DHD \r\n"));
1323*4882a593Smuzhiyun 		return resched;
1324*4882a593Smuzhiyun 	}
1325*4882a593Smuzhiyun 
1326*4882a593Smuzhiyun 	BCM_REFERENCE(net);
1327*4882a593Smuzhiyun 
1328*4882a593Smuzhiyun 	DHD_LB_STATS_PERCPU_ARR_INCR(dhd->txp_percpu_run_cnt);
1329*4882a593Smuzhiyun 
1330*4882a593Smuzhiyun 	/* Base Loop to perform the actual Tx */
1331*4882a593Smuzhiyun 	do {
1332*4882a593Smuzhiyun 		skb = skb_dequeue(&dhd->tx_pend_queue);
1333*4882a593Smuzhiyun 		if (skb == NULL) {
1334*4882a593Smuzhiyun 			DHD_TRACE(("Dequeued a Null Packet \r\n"));
1335*4882a593Smuzhiyun 			break;
1336*4882a593Smuzhiyun 		}
1337*4882a593Smuzhiyun 		cnt++;
1338*4882a593Smuzhiyun 
1339*4882a593Smuzhiyun 		net =  DHD_LB_TX_PKTTAG_NETDEV((dhd_tx_lb_pkttag_fr_t *)PKTTAG(skb));
1340*4882a593Smuzhiyun 		ifidx = DHD_LB_TX_PKTTAG_IFIDX((dhd_tx_lb_pkttag_fr_t *)PKTTAG(skb));
1341*4882a593Smuzhiyun 
1342*4882a593Smuzhiyun 		DHD_TRACE(("Processing skb %p for net %p index %d \r\n", skb,
1343*4882a593Smuzhiyun 			net, ifidx));
1344*4882a593Smuzhiyun 
1345*4882a593Smuzhiyun 		__dhd_sendpkt(&dhd->pub, ifidx, skb);
1346*4882a593Smuzhiyun 
1347*4882a593Smuzhiyun 		if (cnt >= DHD_LB_TXBOUND) {
1348*4882a593Smuzhiyun 			resched = TRUE;
1349*4882a593Smuzhiyun 			break;
1350*4882a593Smuzhiyun 		}
1351*4882a593Smuzhiyun 
1352*4882a593Smuzhiyun 	} while (1);
1353*4882a593Smuzhiyun 
1354*4882a593Smuzhiyun 	DHD_LB_INFO(("%s(): Processed %d packets \r\n", __FUNCTION__, cnt));
1355*4882a593Smuzhiyun 
1356*4882a593Smuzhiyun 	return resched;
1357*4882a593Smuzhiyun }
1358*4882a593Smuzhiyun 
1359*4882a593Smuzhiyun void
dhd_lb_tx_handler(unsigned long data)1360*4882a593Smuzhiyun dhd_lb_tx_handler(unsigned long data)
1361*4882a593Smuzhiyun {
1362*4882a593Smuzhiyun 	dhd_info_t *dhd = (dhd_info_t *)data;
1363*4882a593Smuzhiyun 
1364*4882a593Smuzhiyun 	if (dhd_lb_tx_process(dhd)) {
1365*4882a593Smuzhiyun 		dhd_tasklet_schedule(&dhd->tx_tasklet);
1366*4882a593Smuzhiyun 	}
1367*4882a593Smuzhiyun }
1368*4882a593Smuzhiyun 
1369*4882a593Smuzhiyun #endif /* DHD_LB_TXP */
1370*4882a593Smuzhiyun #endif /* DHD_LB */
1371*4882a593Smuzhiyun 
1372*4882a593Smuzhiyun #if defined(DHD_CONTROL_PCIE_CPUCORE_WIFI_TURNON)
1373*4882a593Smuzhiyun void
dhd_irq_set_affinity(dhd_pub_t * dhdp,const struct cpumask * cpumask)1374*4882a593Smuzhiyun dhd_irq_set_affinity(dhd_pub_t *dhdp, const struct cpumask *cpumask)
1375*4882a593Smuzhiyun {
1376*4882a593Smuzhiyun 	unsigned int irq = (unsigned int)-1;
1377*4882a593Smuzhiyun 	int err = BCME_OK;
1378*4882a593Smuzhiyun 
1379*4882a593Smuzhiyun 	if (!dhdp) {
1380*4882a593Smuzhiyun 		DHD_ERROR(("%s : dhdp is NULL\n", __FUNCTION__));
1381*4882a593Smuzhiyun 		return;
1382*4882a593Smuzhiyun 	}
1383*4882a593Smuzhiyun 
1384*4882a593Smuzhiyun 	if (!dhdp->bus) {
1385*4882a593Smuzhiyun 		DHD_ERROR(("%s : bus is NULL\n", __FUNCTION__));
1386*4882a593Smuzhiyun 		return;
1387*4882a593Smuzhiyun 	}
1388*4882a593Smuzhiyun 
1389*4882a593Smuzhiyun 	DHD_ERROR(("%s : irq set affinity cpu:0x%lx\n",
1390*4882a593Smuzhiyun 			__FUNCTION__, *cpumask_bits(cpumask)));
1391*4882a593Smuzhiyun 
1392*4882a593Smuzhiyun 	dhdpcie_get_pcieirq(dhdp->bus, &irq);
1393*4882a593Smuzhiyun #ifdef BCMDHD_MODULAR
1394*4882a593Smuzhiyun 	err = irq_set_affinity_hint(irq, cpumask);
1395*4882a593Smuzhiyun #else
1396*4882a593Smuzhiyun 	err = irq_set_affinity(irq, cpumask);
1397*4882a593Smuzhiyun #endif /* BCMDHD_MODULAR */
1398*4882a593Smuzhiyun 	if (err)
1399*4882a593Smuzhiyun 		DHD_ERROR(("%s : irq set affinity is failed cpu:0x%lx\n",
1400*4882a593Smuzhiyun 				__FUNCTION__, *cpumask_bits(cpumask)));
1401*4882a593Smuzhiyun }
1402*4882a593Smuzhiyun #endif /* DHD_CONTROL_PCIE_CPUCORE_WIFI_TURNON */
1403