dhd_linux_lb.c 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333
  1. /*
  2. * Broadcom Dongle Host Driver (DHD), Linux-specific network interface
  3. * Basically selected code segments from usb-cdc.c and usb-rndis.c
  4. *
  5. * Portions of this code are copyright (c) 2020 Cypress Semiconductor Corporation
  6. *
  7. * Copyright (C) 1999-2020, Broadcom Corporation
  8. *
  9. * Unless you and Broadcom execute a separate written software license
  10. * agreement governing use of this software, this software is licensed to you
  11. * under the terms of the GNU General Public License version 2 (the "GPL"),
  12. * available at http://www.broadcom.com/licenses/GPLv2.php, with the
  13. * following added to such license:
  14. *
  15. * As a special exception, the copyright holders of this software give you
  16. * permission to link this software with independent modules, and to copy and
  17. * distribute the resulting executable under terms of your choice, provided that
  18. * you also meet, for each linked independent module, the terms and conditions of
  19. * the license of that module. An independent module is a module which is not
  20. * derived from this software. The special exception does not apply to any
  21. * modifications of the software.
  22. *
  23. * Notwithstanding the above, under no circumstances may you combine this
  24. * software in any way with any other Broadcom software provided under a license
  25. * other than the GPL, without Broadcom's express prior written consent.
  26. *
  27. *
  28. * <<Broadcom-WL-IPTag/Open:>>
  29. *
  30. * $Id: dhd_linux_lb.c 726017 2020-06-09 12:27:33Z $
  31. */
  32. #include <dhd_linux_priv.h>
  33. extern dhd_pub_t* g_dhd_pub;
  34. #if defined(DHD_LB)
  35. void
  36. dhd_lb_set_default_cpus(dhd_info_t *dhd)
  37. {
  38. /* Default CPU allocation for the jobs */
  39. atomic_set(&dhd->rx_napi_cpu, 1);
  40. atomic_set(&dhd->rx_compl_cpu, 2);
  41. atomic_set(&dhd->tx_compl_cpu, 2);
  42. atomic_set(&dhd->tx_cpu, 2);
  43. atomic_set(&dhd->net_tx_cpu, 0);
  44. }
  45. void
  46. dhd_cpumasks_deinit(dhd_info_t *dhd)
  47. {
  48. free_cpumask_var(dhd->cpumask_curr_avail);
  49. free_cpumask_var(dhd->cpumask_primary);
  50. free_cpumask_var(dhd->cpumask_primary_new);
  51. free_cpumask_var(dhd->cpumask_secondary);
  52. free_cpumask_var(dhd->cpumask_secondary_new);
  53. }
  54. int
  55. dhd_cpumasks_init(dhd_info_t *dhd)
  56. {
  57. int id;
  58. uint32 cpus, num_cpus = num_possible_cpus();
  59. int ret = 0;
  60. DHD_ERROR(("%s CPU masks primary(big)=0x%x secondary(little)=0x%x\n", __FUNCTION__,
  61. DHD_LB_PRIMARY_CPUS, DHD_LB_SECONDARY_CPUS));
  62. if (!alloc_cpumask_var(&dhd->cpumask_curr_avail, GFP_KERNEL) ||
  63. !alloc_cpumask_var(&dhd->cpumask_primary, GFP_KERNEL) ||
  64. !alloc_cpumask_var(&dhd->cpumask_primary_new, GFP_KERNEL) ||
  65. !alloc_cpumask_var(&dhd->cpumask_secondary, GFP_KERNEL) ||
  66. !alloc_cpumask_var(&dhd->cpumask_secondary_new, GFP_KERNEL)) {
  67. DHD_ERROR(("%s Failed to init cpumasks\n", __FUNCTION__));
  68. ret = -ENOMEM;
  69. goto fail;
  70. }
  71. cpumask_copy(dhd->cpumask_curr_avail, cpu_online_mask);
  72. cpumask_clear(dhd->cpumask_primary);
  73. cpumask_clear(dhd->cpumask_secondary);
  74. if (num_cpus > 32) {
  75. DHD_ERROR(("%s max cpus must be 32, %d too big\n", __FUNCTION__, num_cpus));
  76. ASSERT(0);
  77. }
  78. cpus = DHD_LB_PRIMARY_CPUS;
  79. for (id = 0; id < num_cpus; id++) {
  80. if (isset(&cpus, id))
  81. cpumask_set_cpu(id, dhd->cpumask_primary);
  82. }
  83. cpus = DHD_LB_SECONDARY_CPUS;
  84. for (id = 0; id < num_cpus; id++) {
  85. if (isset(&cpus, id))
  86. cpumask_set_cpu(id, dhd->cpumask_secondary);
  87. }
  88. return ret;
  89. fail:
  90. dhd_cpumasks_deinit(dhd);
  91. return ret;
  92. }
  93. /*
  94. * The CPU Candidacy Algorithm
  95. * ~~~~~~~~~~~~~~~~~~~~~~~~~~~
  96. * The available CPUs for selection are divided into two groups
  97. * Primary Set - A CPU mask that carries the First Choice CPUs
  98. * Secondary Set - A CPU mask that carries the Second Choice CPUs.
  99. *
  100. * There are two types of Job, that needs to be assigned to
  101. * the CPUs, from one of the above mentioned CPU group. The Jobs are
  102. * 1) Rx Packet Processing - napi_cpu
  103. * 2) Completion Processiong (Tx, RX) - compl_cpu
  104. *
  105. * To begin with both napi_cpu and compl_cpu are on CPU0. Whenever a CPU goes
  106. * on-line/off-line the CPU candidacy algorithm is triggerd. The candidacy
  107. * algo tries to pickup the first available non boot CPU (CPU0) for napi_cpu.
  108. * If there are more processors free, it assigns one to compl_cpu.
  109. * It also tries to ensure that both napi_cpu and compl_cpu are not on the same
  110. * CPU, as much as possible.
  111. *
  112. * By design, both Tx and Rx completion jobs are run on the same CPU core, as it
  113. * would allow Tx completion skb's to be released into a local free pool from
  114. * which the rx buffer posts could have been serviced. it is important to note
  115. * that a Tx packet may not have a large enough buffer for rx posting.
  116. */
  117. void dhd_select_cpu_candidacy(dhd_info_t *dhd)
  118. {
  119. uint32 primary_available_cpus; /* count of primary available cpus */
  120. uint32 secondary_available_cpus; /* count of secondary available cpus */
  121. uint32 napi_cpu = 0; /* cpu selected for napi rx processing */
  122. uint32 compl_cpu = 0; /* cpu selected for completion jobs */
  123. uint32 tx_cpu = 0; /* cpu selected for tx processing job */
  124. cpumask_clear(dhd->cpumask_primary_new);
  125. cpumask_clear(dhd->cpumask_secondary_new);
  126. /*
  127. * Now select from the primary mask. Even if a Job is
  128. * already running on a CPU in secondary group, we still move
  129. * to primary CPU. So no conditional checks.
  130. */
  131. cpumask_and(dhd->cpumask_primary_new, dhd->cpumask_primary,
  132. dhd->cpumask_curr_avail);
  133. cpumask_and(dhd->cpumask_secondary_new, dhd->cpumask_secondary,
  134. dhd->cpumask_curr_avail);
  135. primary_available_cpus = cpumask_weight(dhd->cpumask_primary_new);
  136. if (primary_available_cpus > 0) {
  137. napi_cpu = cpumask_first(dhd->cpumask_primary_new);
  138. /* If no further CPU is available,
  139. * cpumask_next returns >= nr_cpu_ids
  140. */
  141. tx_cpu = cpumask_next(napi_cpu, dhd->cpumask_primary_new);
  142. if (tx_cpu >= nr_cpu_ids)
  143. tx_cpu = 0;
  144. /* In case there are no more CPUs, do completions & Tx in same CPU */
  145. compl_cpu = cpumask_next(tx_cpu, dhd->cpumask_primary_new);
  146. if (compl_cpu >= nr_cpu_ids)
  147. compl_cpu = tx_cpu;
  148. }
  149. DHD_INFO(("%s After primary CPU check napi_cpu %d compl_cpu %d tx_cpu %d\n",
  150. __FUNCTION__, napi_cpu, compl_cpu, tx_cpu));
  151. /* -- Now check for the CPUs from the secondary mask -- */
  152. secondary_available_cpus = cpumask_weight(dhd->cpumask_secondary_new);
  153. DHD_INFO(("%s Available secondary cpus %d nr_cpu_ids %d\n",
  154. __FUNCTION__, secondary_available_cpus, nr_cpu_ids));
  155. if (secondary_available_cpus > 0) {
  156. /* At this point if napi_cpu is unassigned it means no CPU
  157. * is online from Primary Group
  158. */
  159. if (napi_cpu == 0) {
  160. napi_cpu = cpumask_first(dhd->cpumask_secondary_new);
  161. tx_cpu = cpumask_next(napi_cpu, dhd->cpumask_secondary_new);
  162. compl_cpu = cpumask_next(tx_cpu, dhd->cpumask_secondary_new);
  163. } else if (tx_cpu == 0) {
  164. tx_cpu = cpumask_first(dhd->cpumask_secondary_new);
  165. compl_cpu = cpumask_next(tx_cpu, dhd->cpumask_secondary_new);
  166. } else if (compl_cpu == 0) {
  167. compl_cpu = cpumask_first(dhd->cpumask_secondary_new);
  168. }
  169. /* If no CPU was available for tx processing, choose CPU 0 */
  170. if (tx_cpu >= nr_cpu_ids)
  171. tx_cpu = 0;
  172. /* If no CPU was available for completion, choose CPU 0 */
  173. if (compl_cpu >= nr_cpu_ids)
  174. compl_cpu = 0;
  175. }
  176. if ((primary_available_cpus == 0) &&
  177. (secondary_available_cpus == 0)) {
  178. /* No CPUs available from primary or secondary mask */
  179. napi_cpu = 1;
  180. compl_cpu = 0;
  181. tx_cpu = 2;
  182. }
  183. DHD_INFO(("%s After secondary CPU check napi_cpu %d compl_cpu %d tx_cpu %d\n",
  184. __FUNCTION__, napi_cpu, compl_cpu, tx_cpu));
  185. ASSERT(napi_cpu < nr_cpu_ids);
  186. ASSERT(compl_cpu < nr_cpu_ids);
  187. ASSERT(tx_cpu < nr_cpu_ids);
  188. atomic_set(&dhd->rx_napi_cpu, napi_cpu);
  189. atomic_set(&dhd->tx_compl_cpu, compl_cpu);
  190. atomic_set(&dhd->rx_compl_cpu, compl_cpu);
  191. atomic_set(&dhd->tx_cpu, tx_cpu);
  192. return;
  193. }
  194. /*
  195. * Function to handle CPU Hotplug notifications.
  196. * One of the task it does is to trigger the CPU Candidacy algorithm
  197. * for load balancing.
  198. */
  199. #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0))
  200. int dhd_cpu_startup_callback(unsigned int cpu)
  201. {
  202. dhd_info_t *dhd = g_dhd_pub->info;
  203. DHD_INFO(("%s(): \r\n cpu:%d", __FUNCTION__, cpu));
  204. DHD_LB_STATS_INCR(dhd->cpu_online_cnt[cpu]);
  205. cpumask_set_cpu(cpu, dhd->cpumask_curr_avail);
  206. dhd_select_cpu_candidacy(dhd);
  207. return 0;
  208. }
  209. int dhd_cpu_teardown_callback(unsigned int cpu)
  210. {
  211. dhd_info_t *dhd = g_dhd_pub->info;
  212. DHD_INFO(("%s(): \r\n cpu:%d", __FUNCTION__, cpu));
  213. DHD_LB_STATS_INCR(dhd->cpu_offline_cnt[cpu]);
  214. cpumask_clear_cpu(cpu, dhd->cpumask_curr_avail);
  215. dhd_select_cpu_candidacy(dhd);
  216. return 0;
  217. }
  218. #else
  219. int
  220. dhd_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu)
  221. {
  222. unsigned long int cpu = (unsigned long int)hcpu;
  223. #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
  224. #pragma GCC diagnostic push
  225. #pragma GCC diagnostic ignored "-Wcast-qual"
  226. #endif // endif
  227. dhd_info_t *dhd = container_of(nfb, dhd_info_t, cpu_notifier);
  228. #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
  229. #pragma GCC diagnostic pop
  230. #endif // endif
  231. if (!dhd || !(dhd->dhd_state & DHD_ATTACH_STATE_LB_ATTACH_DONE)) {
  232. DHD_INFO(("%s(): LB data is not initialized yet.\n",
  233. __FUNCTION__));
  234. return NOTIFY_BAD;
  235. }
  236. switch (action)
  237. {
  238. case CPU_ONLINE:
  239. case CPU_ONLINE_FROZEN:
  240. DHD_LB_STATS_INCR(dhd->cpu_online_cnt[cpu]);
  241. cpumask_set_cpu(cpu, dhd->cpumask_curr_avail);
  242. dhd_select_cpu_candidacy(dhd);
  243. break;
  244. case CPU_DOWN_PREPARE:
  245. case CPU_DOWN_PREPARE_FROZEN:
  246. DHD_LB_STATS_INCR(dhd->cpu_offline_cnt[cpu]);
  247. cpumask_clear_cpu(cpu, dhd->cpumask_curr_avail);
  248. dhd_select_cpu_candidacy(dhd);
  249. break;
  250. default:
  251. break;
  252. }
  253. return NOTIFY_OK;
  254. }
  255. #endif /* LINUX_VERSION_CODE < 4.10.0 */
  256. int dhd_register_cpuhp_callback(dhd_info_t *dhd)
  257. {
  258. int cpuhp_ret = 0;
  259. #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0))
  260. cpuhp_ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "dhd",
  261. dhd_cpu_startup_callback, dhd_cpu_teardown_callback);
  262. if (cpuhp_ret < 0) {
  263. DHD_ERROR(("%s(): cpuhp_setup_state failed %d RX LB won't happen \r\n",
  264. __FUNCTION__, cpuhp_ret));
  265. }
  266. #else
  267. /*
  268. * If we are able to initialize CPU masks, lets register to the
  269. * CPU Hotplug framework to change the CPU for each job dynamically
  270. * using candidacy algorithm.
  271. */
  272. dhd->cpu_notifier.notifier_call = dhd_cpu_callback;
  273. register_hotcpu_notifier(&dhd->cpu_notifier); /* Register a callback */
  274. #endif /* LINUX_VERSION_CODE < 4.10.0 */
  275. return cpuhp_ret;
  276. }
  277. int dhd_unregister_cpuhp_callback(dhd_info_t *dhd)
  278. {
  279. int ret = 0;
  280. #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0))
  281. /* Don't want to call tear down while unregistering */
  282. cpuhp_remove_state_nocalls(CPUHP_AP_ONLINE_DYN);
  283. #else
  284. if (dhd->cpu_notifier.notifier_call != NULL) {
  285. unregister_cpu_notifier(&dhd->cpu_notifier);
  286. }
  287. #endif // endif
  288. return ret;
  289. }
  290. #if defined(DHD_LB_STATS)
  291. void dhd_lb_stats_init(dhd_pub_t *dhdp)
  292. {
  293. dhd_info_t *dhd;
  294. int i, j, num_cpus = num_possible_cpus();
  295. int alloc_size = sizeof(uint32) * num_cpus;
  296. if (dhdp == NULL) {
  297. DHD_ERROR(("%s(): Invalid argument dhd pubb pointer is NULL \n",
  298. __FUNCTION__));
  299. return;
  300. }
  301. dhd = dhdp->info;
  302. if (dhd == NULL) {
  303. DHD_ERROR(("%s(): DHD pointer is NULL \n", __FUNCTION__));
  304. return;
  305. }
  306. DHD_LB_STATS_CLR(dhd->dhd_dpc_cnt);
  307. DHD_LB_STATS_CLR(dhd->napi_sched_cnt);
  308. dhd->napi_percpu_run_cnt = (uint32 *)MALLOC(dhdp->osh, alloc_size);
  309. if (!dhd->napi_percpu_run_cnt) {
  310. DHD_ERROR(("%s(): napi_percpu_run_cnt malloc failed \n",
  311. __FUNCTION__));
  312. return;
  313. }
  314. for (i = 0; i < num_cpus; i++)
  315. DHD_LB_STATS_CLR(dhd->napi_percpu_run_cnt[i]);
  316. DHD_LB_STATS_CLR(dhd->rxc_sched_cnt);
  317. dhd->rxc_percpu_run_cnt = (uint32 *)MALLOC(dhdp->osh, alloc_size);
  318. if (!dhd->rxc_percpu_run_cnt) {
  319. DHD_ERROR(("%s(): rxc_percpu_run_cnt malloc failed \n",
  320. __FUNCTION__));
  321. return;
  322. }
  323. for (i = 0; i < num_cpus; i++)
  324. DHD_LB_STATS_CLR(dhd->rxc_percpu_run_cnt[i]);
  325. DHD_LB_STATS_CLR(dhd->txc_sched_cnt);
  326. dhd->txc_percpu_run_cnt = (uint32 *)MALLOC(dhdp->osh, alloc_size);
  327. if (!dhd->txc_percpu_run_cnt) {
  328. DHD_ERROR(("%s(): txc_percpu_run_cnt malloc failed \n",
  329. __FUNCTION__));
  330. return;
  331. }
  332. for (i = 0; i < num_cpus; i++)
  333. DHD_LB_STATS_CLR(dhd->txc_percpu_run_cnt[i]);
  334. dhd->cpu_online_cnt = (uint32 *)MALLOC(dhdp->osh, alloc_size);
  335. if (!dhd->cpu_online_cnt) {
  336. DHD_ERROR(("%s(): cpu_online_cnt malloc failed \n",
  337. __FUNCTION__));
  338. return;
  339. }
  340. for (i = 0; i < num_cpus; i++)
  341. DHD_LB_STATS_CLR(dhd->cpu_online_cnt[i]);
  342. dhd->cpu_offline_cnt = (uint32 *)MALLOC(dhdp->osh, alloc_size);
  343. if (!dhd->cpu_offline_cnt) {
  344. DHD_ERROR(("%s(): cpu_offline_cnt malloc failed \n",
  345. __FUNCTION__));
  346. return;
  347. }
  348. for (i = 0; i < num_cpus; i++)
  349. DHD_LB_STATS_CLR(dhd->cpu_offline_cnt[i]);
  350. dhd->txp_percpu_run_cnt = (uint32 *)MALLOC(dhdp->osh, alloc_size);
  351. if (!dhd->txp_percpu_run_cnt) {
  352. DHD_ERROR(("%s(): txp_percpu_run_cnt malloc failed \n",
  353. __FUNCTION__));
  354. return;
  355. }
  356. for (i = 0; i < num_cpus; i++)
  357. DHD_LB_STATS_CLR(dhd->txp_percpu_run_cnt[i]);
  358. dhd->tx_start_percpu_run_cnt = (uint32 *)MALLOC(dhdp->osh, alloc_size);
  359. if (!dhd->tx_start_percpu_run_cnt) {
  360. DHD_ERROR(("%s(): tx_start_percpu_run_cnt malloc failed \n",
  361. __FUNCTION__));
  362. return;
  363. }
  364. for (i = 0; i < num_cpus; i++)
  365. DHD_LB_STATS_CLR(dhd->tx_start_percpu_run_cnt[i]);
  366. for (j = 0; j < HIST_BIN_SIZE; j++) {
  367. dhd->napi_rx_hist[j] = (uint32 *)MALLOC(dhdp->osh, alloc_size);
  368. if (!dhd->napi_rx_hist[j]) {
  369. DHD_ERROR(("%s(): dhd->napi_rx_hist[%d] malloc failed \n",
  370. __FUNCTION__, j));
  371. return;
  372. }
  373. for (i = 0; i < num_cpus; i++) {
  374. DHD_LB_STATS_CLR(dhd->napi_rx_hist[j][i]);
  375. }
  376. }
  377. #ifdef DHD_LB_TXC
  378. for (j = 0; j < HIST_BIN_SIZE; j++) {
  379. dhd->txc_hist[j] = (uint32 *)MALLOC(dhdp->osh, alloc_size);
  380. if (!dhd->txc_hist[j]) {
  381. DHD_ERROR(("%s(): dhd->txc_hist[%d] malloc failed \n",
  382. __FUNCTION__, j));
  383. return;
  384. }
  385. for (i = 0; i < num_cpus; i++) {
  386. DHD_LB_STATS_CLR(dhd->txc_hist[j][i]);
  387. }
  388. }
  389. #endif /* DHD_LB_TXC */
  390. #ifdef DHD_LB_RXC
  391. for (j = 0; j < HIST_BIN_SIZE; j++) {
  392. dhd->rxc_hist[j] = (uint32 *)MALLOC(dhdp->osh, alloc_size);
  393. if (!dhd->rxc_hist[j]) {
  394. DHD_ERROR(("%s(): dhd->rxc_hist[%d] malloc failed \n",
  395. __FUNCTION__, j));
  396. return;
  397. }
  398. for (i = 0; i < num_cpus; i++) {
  399. DHD_LB_STATS_CLR(dhd->rxc_hist[j][i]);
  400. }
  401. }
  402. #endif /* DHD_LB_RXC */
  403. return;
  404. }
  405. void dhd_lb_stats_deinit(dhd_pub_t *dhdp)
  406. {
  407. dhd_info_t *dhd;
  408. int j, num_cpus = num_possible_cpus();
  409. int alloc_size = sizeof(uint32) * num_cpus;
  410. if (dhdp == NULL) {
  411. DHD_ERROR(("%s(): Invalid argument dhd pubb pointer is NULL \n",
  412. __FUNCTION__));
  413. return;
  414. }
  415. dhd = dhdp->info;
  416. if (dhd == NULL) {
  417. DHD_ERROR(("%s(): DHD pointer is NULL \n", __FUNCTION__));
  418. return;
  419. }
  420. if (dhd->napi_percpu_run_cnt) {
  421. MFREE(dhdp->osh, dhd->napi_percpu_run_cnt, alloc_size);
  422. dhd->napi_percpu_run_cnt = NULL;
  423. }
  424. if (dhd->rxc_percpu_run_cnt) {
  425. MFREE(dhdp->osh, dhd->rxc_percpu_run_cnt, alloc_size);
  426. dhd->rxc_percpu_run_cnt = NULL;
  427. }
  428. if (dhd->txc_percpu_run_cnt) {
  429. MFREE(dhdp->osh, dhd->txc_percpu_run_cnt, alloc_size);
  430. dhd->txc_percpu_run_cnt = NULL;
  431. }
  432. if (dhd->cpu_online_cnt) {
  433. MFREE(dhdp->osh, dhd->cpu_online_cnt, alloc_size);
  434. dhd->cpu_online_cnt = NULL;
  435. }
  436. if (dhd->cpu_offline_cnt) {
  437. MFREE(dhdp->osh, dhd->cpu_offline_cnt, alloc_size);
  438. dhd->cpu_offline_cnt = NULL;
  439. }
  440. if (dhd->txp_percpu_run_cnt) {
  441. MFREE(dhdp->osh, dhd->txp_percpu_run_cnt, alloc_size);
  442. dhd->txp_percpu_run_cnt = NULL;
  443. }
  444. if (dhd->tx_start_percpu_run_cnt) {
  445. MFREE(dhdp->osh, dhd->tx_start_percpu_run_cnt, alloc_size);
  446. dhd->tx_start_percpu_run_cnt = NULL;
  447. }
  448. for (j = 0; j < HIST_BIN_SIZE; j++) {
  449. if (dhd->napi_rx_hist[j]) {
  450. MFREE(dhdp->osh, dhd->napi_rx_hist[j], alloc_size);
  451. dhd->napi_rx_hist[j] = NULL;
  452. }
  453. #ifdef DHD_LB_TXC
  454. if (dhd->txc_hist[j]) {
  455. MFREE(dhdp->osh, dhd->txc_hist[j], alloc_size);
  456. dhd->txc_hist[j] = NULL;
  457. }
  458. #endif /* DHD_LB_TXC */
  459. #ifdef DHD_LB_RXC
  460. if (dhd->rxc_hist[j]) {
  461. MFREE(dhdp->osh, dhd->rxc_hist[j], alloc_size);
  462. dhd->rxc_hist[j] = NULL;
  463. }
  464. #endif /* DHD_LB_RXC */
  465. }
  466. return;
  467. }
  468. void dhd_lb_stats_dump_histo(dhd_pub_t *dhdp,
  469. struct bcmstrbuf *strbuf, uint32 **hist)
  470. {
  471. int i, j;
  472. uint32 *per_cpu_total;
  473. uint32 total = 0;
  474. uint32 num_cpus = num_possible_cpus();
  475. per_cpu_total = (uint32 *)MALLOC(dhdp->osh, sizeof(uint32) * num_cpus);
  476. if (!per_cpu_total) {
  477. DHD_ERROR(("%s(): dhd->per_cpu_total malloc failed \n", __FUNCTION__));
  478. return;
  479. }
  480. bzero(per_cpu_total, sizeof(uint32) * num_cpus);
  481. bcm_bprintf(strbuf, "CPU: \t\t");
  482. for (i = 0; i < num_cpus; i++)
  483. bcm_bprintf(strbuf, "%d\t", i);
  484. bcm_bprintf(strbuf, "\nBin\n");
  485. for (i = 0; i < HIST_BIN_SIZE; i++) {
  486. bcm_bprintf(strbuf, "%d:\t\t", 1<<i);
  487. for (j = 0; j < num_cpus; j++) {
  488. bcm_bprintf(strbuf, "%d\t", hist[i][j]);
  489. }
  490. bcm_bprintf(strbuf, "\n");
  491. }
  492. bcm_bprintf(strbuf, "Per CPU Total \t");
  493. total = 0;
  494. for (i = 0; i < num_cpus; i++) {
  495. for (j = 0; j < HIST_BIN_SIZE; j++) {
  496. per_cpu_total[i] += (hist[j][i] * (1<<j));
  497. }
  498. bcm_bprintf(strbuf, "%d\t", per_cpu_total[i]);
  499. total += per_cpu_total[i];
  500. }
  501. bcm_bprintf(strbuf, "\nTotal\t\t%d \n", total);
  502. if (per_cpu_total) {
  503. MFREE(dhdp->osh, per_cpu_total, sizeof(uint32) * num_cpus);
  504. per_cpu_total = NULL;
  505. }
  506. return;
  507. }
  508. void dhd_lb_stats_dump_cpu_array(struct bcmstrbuf *strbuf, uint32 *p)
  509. {
  510. int i, num_cpus = num_possible_cpus();
  511. bcm_bprintf(strbuf, "CPU: \t");
  512. for (i = 0; i < num_cpus; i++)
  513. bcm_bprintf(strbuf, "%d\t", i);
  514. bcm_bprintf(strbuf, "\n");
  515. bcm_bprintf(strbuf, "Val: \t");
  516. for (i = 0; i < num_cpus; i++)
  517. bcm_bprintf(strbuf, "%u\t", *(p+i));
  518. bcm_bprintf(strbuf, "\n");
  519. return;
  520. }
  521. void dhd_lb_stats_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf)
  522. {
  523. dhd_info_t *dhd;
  524. if (dhdp == NULL || strbuf == NULL) {
  525. DHD_ERROR(("%s(): Invalid argument dhdp %p strbuf %p \n",
  526. __FUNCTION__, dhdp, strbuf));
  527. return;
  528. }
  529. dhd = dhdp->info;
  530. if (dhd == NULL) {
  531. DHD_ERROR(("%s(): DHD pointer is NULL \n", __FUNCTION__));
  532. return;
  533. }
  534. bcm_bprintf(strbuf, "\ncpu_online_cnt:\n");
  535. dhd_lb_stats_dump_cpu_array(strbuf, dhd->cpu_online_cnt);
  536. bcm_bprintf(strbuf, "\ncpu_offline_cnt:\n");
  537. dhd_lb_stats_dump_cpu_array(strbuf, dhd->cpu_offline_cnt);
  538. bcm_bprintf(strbuf, "\nsched_cnt: dhd_dpc %u napi %u rxc %u txc %u\n",
  539. dhd->dhd_dpc_cnt, dhd->napi_sched_cnt, dhd->rxc_sched_cnt,
  540. dhd->txc_sched_cnt);
  541. #ifdef DHD_LB_RXP
  542. bcm_bprintf(strbuf, "\nnapi_percpu_run_cnt:\n");
  543. dhd_lb_stats_dump_cpu_array(strbuf, dhd->napi_percpu_run_cnt);
  544. bcm_bprintf(strbuf, "\nNAPI Packets Received Histogram:\n");
  545. dhd_lb_stats_dump_histo(dhdp, strbuf, dhd->napi_rx_hist);
  546. #endif /* DHD_LB_RXP */
  547. #ifdef DHD_LB_RXC
  548. bcm_bprintf(strbuf, "\nrxc_percpu_run_cnt:\n");
  549. dhd_lb_stats_dump_cpu_array(strbuf, dhd->rxc_percpu_run_cnt);
  550. bcm_bprintf(strbuf, "\nRX Completions (Buffer Post) Histogram:\n");
  551. dhd_lb_stats_dump_histo(dhdp, strbuf, dhd->rxc_hist);
  552. #endif /* DHD_LB_RXC */
  553. #ifdef DHD_LB_TXC
  554. bcm_bprintf(strbuf, "\ntxc_percpu_run_cnt:\n");
  555. dhd_lb_stats_dump_cpu_array(strbuf, dhd->txc_percpu_run_cnt);
  556. bcm_bprintf(strbuf, "\nTX Completions (Buffer Free) Histogram:\n");
  557. dhd_lb_stats_dump_histo(dhdp, strbuf, dhd->txc_hist);
  558. #endif /* DHD_LB_TXC */
  559. #ifdef DHD_LB_TXP
  560. bcm_bprintf(strbuf, "\ntxp_percpu_run_cnt:\n");
  561. dhd_lb_stats_dump_cpu_array(strbuf, dhd->txp_percpu_run_cnt);
  562. bcm_bprintf(strbuf, "\ntx_start_percpu_run_cnt:\n");
  563. dhd_lb_stats_dump_cpu_array(strbuf, dhd->tx_start_percpu_run_cnt);
  564. #endif /* DHD_LB_TXP */
  565. }
  566. /* Given a number 'n' returns 'm' that is next larger power of 2 after n */
  567. static inline uint32 next_larger_power2(uint32 num)
  568. {
  569. num--;
  570. num |= (num >> 1);
  571. num |= (num >> 2);
  572. num |= (num >> 4);
  573. num |= (num >> 8);
  574. num |= (num >> 16);
  575. return (num + 1);
  576. }
  577. void dhd_lb_stats_update_histo(uint32 **bin, uint32 count, uint32 cpu)
  578. {
  579. uint32 bin_power;
  580. uint32 *p;
  581. bin_power = next_larger_power2(count);
  582. switch (bin_power) {
  583. case 1: p = bin[0] + cpu; break;
  584. case 2: p = bin[1] + cpu; break;
  585. case 4: p = bin[2] + cpu; break;
  586. case 8: p = bin[3] + cpu; break;
  587. case 16: p = bin[4] + cpu; break;
  588. case 32: p = bin[5] + cpu; break;
  589. case 64: p = bin[6] + cpu; break;
  590. case 128: p = bin[7] + cpu; break;
  591. default : p = bin[8] + cpu; break;
  592. }
  593. *p = *p + 1;
  594. return;
  595. }
  596. void dhd_lb_stats_update_napi_histo(dhd_pub_t *dhdp, uint32 count)
  597. {
  598. int cpu;
  599. dhd_info_t *dhd = dhdp->info;
  600. cpu = get_cpu();
  601. put_cpu();
  602. dhd_lb_stats_update_histo(dhd->napi_rx_hist, count, cpu);
  603. return;
  604. }
  605. void dhd_lb_stats_update_txc_histo(dhd_pub_t *dhdp, uint32 count)
  606. {
  607. int cpu;
  608. dhd_info_t *dhd = dhdp->info;
  609. cpu = get_cpu();
  610. put_cpu();
  611. dhd_lb_stats_update_histo(dhd->txc_hist, count, cpu);
  612. return;
  613. }
  614. void dhd_lb_stats_update_rxc_histo(dhd_pub_t *dhdp, uint32 count)
  615. {
  616. int cpu;
  617. dhd_info_t *dhd = dhdp->info;
  618. cpu = get_cpu();
  619. put_cpu();
  620. dhd_lb_stats_update_histo(dhd->rxc_hist, count, cpu);
  621. return;
  622. }
  623. void dhd_lb_stats_txc_percpu_cnt_incr(dhd_pub_t *dhdp)
  624. {
  625. dhd_info_t *dhd = dhdp->info;
  626. DHD_LB_STATS_PERCPU_ARR_INCR(dhd->txc_percpu_run_cnt);
  627. }
  628. void dhd_lb_stats_rxc_percpu_cnt_incr(dhd_pub_t *dhdp)
  629. {
  630. dhd_info_t *dhd = dhdp->info;
  631. DHD_LB_STATS_PERCPU_ARR_INCR(dhd->rxc_percpu_run_cnt);
  632. }
  633. #endif /* DHD_LB_STATS */
  634. #endif /* DHD_LB */
  635. #if defined(DHD_LB)
  636. /**
  637. * dhd_tasklet_schedule - Function that runs in IPI context of the destination
  638. * CPU and schedules a tasklet.
  639. * @tasklet: opaque pointer to the tasklet
  640. */
  641. INLINE void
  642. dhd_tasklet_schedule(void *tasklet)
  643. {
  644. tasklet_schedule((struct tasklet_struct *)tasklet);
  645. }
  646. /**
  647. * dhd_tasklet_schedule_on - Executes the passed takslet in a given CPU
  648. * @tasklet: tasklet to be scheduled
  649. * @on_cpu: cpu core id
  650. *
  651. * If the requested cpu is online, then an IPI is sent to this cpu via the
  652. * smp_call_function_single with no wait and the tasklet_schedule function
  653. * will be invoked to schedule the specified tasklet on the requested CPU.
  654. */
  655. INLINE void
  656. dhd_tasklet_schedule_on(struct tasklet_struct *tasklet, int on_cpu)
  657. {
  658. const int wait = 0;
  659. smp_call_function_single(on_cpu,
  660. dhd_tasklet_schedule, (void *)tasklet, wait);
  661. }
  662. /**
  663. * dhd_work_schedule_on - Executes the passed work in a given CPU
  664. * @work: work to be scheduled
  665. * @on_cpu: cpu core id
  666. *
  667. * If the requested cpu is online, then an IPI is sent to this cpu via the
  668. * schedule_work_on and the work function
  669. * will be invoked to schedule the specified work on the requested CPU.
  670. */
  671. INLINE void
  672. dhd_work_schedule_on(struct work_struct *work, int on_cpu)
  673. {
  674. schedule_work_on(on_cpu, work);
  675. }
  676. #if defined(DHD_LB_TXC)
  677. /**
  678. * dhd_lb_tx_compl_dispatch - load balance by dispatching the tx_compl_tasklet
  679. * on another cpu. The tx_compl_tasklet will take care of DMA unmapping and
  680. * freeing the packets placed in the tx_compl workq
  681. */
  682. void
  683. dhd_lb_tx_compl_dispatch(dhd_pub_t *dhdp)
  684. {
  685. dhd_info_t *dhd = dhdp->info;
  686. int curr_cpu, on_cpu;
  687. if (dhd->rx_napi_netdev == NULL) {
  688. DHD_ERROR(("%s: dhd->rx_napi_netdev is NULL\n", __FUNCTION__));
  689. return;
  690. }
  691. DHD_LB_STATS_INCR(dhd->txc_sched_cnt);
  692. /*
  693. * If the destination CPU is NOT online or is same as current CPU
  694. * no need to schedule the work
  695. */
  696. curr_cpu = get_cpu();
  697. put_cpu();
  698. on_cpu = atomic_read(&dhd->tx_compl_cpu);
  699. if ((on_cpu == curr_cpu) || (!cpu_online(on_cpu))) {
  700. dhd_tasklet_schedule(&dhd->tx_compl_tasklet);
  701. } else {
  702. schedule_work(&dhd->tx_compl_dispatcher_work);
  703. }
  704. }
  705. static void dhd_tx_compl_dispatcher_fn(struct work_struct * work)
  706. {
  707. struct dhd_info *dhd =
  708. container_of(work, struct dhd_info, tx_compl_dispatcher_work);
  709. int cpu;
  710. get_online_cpus();
  711. cpu = atomic_read(&dhd->tx_compl_cpu);
  712. if (!cpu_online(cpu))
  713. dhd_tasklet_schedule(&dhd->tx_compl_tasklet);
  714. else
  715. dhd_tasklet_schedule_on(&dhd->tx_compl_tasklet, cpu);
  716. put_online_cpus();
  717. }
  718. #endif /* DHD_LB_TXC */
  719. #if defined(DHD_LB_RXC)
  720. /**
  721. * dhd_lb_rx_compl_dispatch - load balance by dispatching the rx_compl_tasklet
  722. * on another cpu. The rx_compl_tasklet will take care of reposting rx buffers
  723. * in the H2D RxBuffer Post common ring, by using the recycled pktids that were
  724. * placed in the rx_compl workq.
  725. *
  726. * @dhdp: pointer to dhd_pub object
  727. */
  728. void
  729. dhd_lb_rx_compl_dispatch(dhd_pub_t *dhdp)
  730. {
  731. dhd_info_t *dhd = dhdp->info;
  732. int curr_cpu, on_cpu;
  733. if (dhd->rx_napi_netdev == NULL) {
  734. DHD_ERROR(("%s: dhd->rx_napi_netdev is NULL\n", __FUNCTION__));
  735. return;
  736. }
  737. DHD_LB_STATS_INCR(dhd->rxc_sched_cnt);
  738. /*
  739. * If the destination CPU is NOT online or is same as current CPU
  740. * no need to schedule the work
  741. */
  742. curr_cpu = get_cpu();
  743. put_cpu();
  744. on_cpu = atomic_read(&dhd->rx_compl_cpu);
  745. if ((on_cpu == curr_cpu) || (!cpu_online(on_cpu))) {
  746. dhd_tasklet_schedule(&dhd->rx_compl_tasklet);
  747. } else {
  748. schedule_work(&dhd->rx_compl_dispatcher_work);
  749. }
  750. }
  751. void dhd_rx_compl_dispatcher_fn(struct work_struct * work)
  752. {
  753. struct dhd_info *dhd =
  754. container_of(work, struct dhd_info, rx_compl_dispatcher_work);
  755. int cpu;
  756. get_online_cpus();
  757. cpu = atomic_read(&dhd->rx_compl_cpu);
  758. if (!cpu_online(cpu))
  759. dhd_tasklet_schedule(&dhd->rx_compl_tasklet);
  760. else {
  761. dhd_tasklet_schedule_on(&dhd->rx_compl_tasklet, cpu);
  762. }
  763. put_online_cpus();
  764. }
  765. #endif /* DHD_LB_RXC */
  766. #if defined(DHD_LB_TXP)
  767. void dhd_tx_dispatcher_work(struct work_struct * work)
  768. {
  769. #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
  770. #pragma GCC diagnostic push
  771. #pragma GCC diagnostic ignored "-Wcast-qual"
  772. #endif // endif
  773. struct dhd_info *dhd =
  774. container_of(work, struct dhd_info, tx_dispatcher_work);
  775. #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
  776. #pragma GCC diagnostic pop
  777. #endif // endif
  778. dhd_tasklet_schedule(&dhd->tx_tasklet);
  779. }
  780. void dhd_tx_dispatcher_fn(dhd_pub_t *dhdp)
  781. {
  782. int cpu;
  783. int net_tx_cpu;
  784. dhd_info_t *dhd = dhdp->info;
  785. preempt_disable();
  786. cpu = atomic_read(&dhd->tx_cpu);
  787. net_tx_cpu = atomic_read(&dhd->net_tx_cpu);
  788. /*
  789. * Now if the NET_TX has pushed the packet in the same
  790. * CPU that is chosen for Tx processing, seperate it out
  791. * i.e run the TX processing tasklet in compl_cpu
  792. */
  793. if (net_tx_cpu == cpu)
  794. cpu = atomic_read(&dhd->tx_compl_cpu);
  795. if (!cpu_online(cpu)) {
  796. /*
  797. * Ooohh... but the Chosen CPU is not online,
  798. * Do the job in the current CPU itself.
  799. */
  800. dhd_tasklet_schedule(&dhd->tx_tasklet);
  801. } else {
  802. /*
  803. * Schedule tx_dispatcher_work to on the cpu which
  804. * in turn will schedule tx_tasklet.
  805. */
  806. dhd_work_schedule_on(&dhd->tx_dispatcher_work, cpu);
  807. }
  808. preempt_enable();
  809. }
  810. /**
  811. * dhd_lb_tx_dispatch - load balance by dispatching the tx_tasklet
  812. * on another cpu. The tx_tasklet will take care of actually putting
  813. * the skbs into appropriate flow ring and ringing H2D interrupt
  814. *
  815. * @dhdp: pointer to dhd_pub object
  816. */
  817. void
  818. dhd_lb_tx_dispatch(dhd_pub_t *dhdp)
  819. {
  820. dhd_info_t *dhd = dhdp->info;
  821. int curr_cpu;
  822. curr_cpu = get_cpu();
  823. put_cpu();
  824. /* Record the CPU in which the TX request from Network stack came */
  825. atomic_set(&dhd->net_tx_cpu, curr_cpu);
  826. /* Schedule the work to dispatch ... */
  827. dhd_tx_dispatcher_fn(dhdp);
  828. }
  829. #endif /* DHD_LB_TXP */
  830. #if defined(DHD_LB_RXP)
  831. /**
  832. * dhd_napi_poll - Load balance napi poll function to process received
  833. * packets and send up the network stack using netif_receive_skb()
  834. *
  835. * @napi: napi object in which context this poll function is invoked
  836. * @budget: number of packets to be processed.
  837. *
  838. * Fetch the dhd_info given the rx_napi_struct. Move all packets from the
  839. * rx_napi_queue into a local rx_process_queue (lock and queue move and unlock).
  840. * Dequeue each packet from head of rx_process_queue, fetch the ifid from the
  841. * packet tag and sendup.
  842. */
  843. int
  844. dhd_napi_poll(struct napi_struct *napi, int budget)
  845. {
  846. int ifid;
  847. const int pkt_count = 1;
  848. const int chan = 0;
  849. struct sk_buff * skb;
  850. unsigned long flags;
  851. struct dhd_info *dhd;
  852. int processed = 0;
  853. struct sk_buff_head rx_process_queue;
  854. #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
  855. #pragma GCC diagnostic push
  856. #pragma GCC diagnostic ignored "-Wcast-qual"
  857. #endif // endif
  858. dhd = container_of(napi, struct dhd_info, rx_napi_struct);
  859. #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
  860. #pragma GCC diagnostic pop
  861. #endif // endif
  862. DHD_INFO(("%s napi_queue<%d> budget<%d>\n",
  863. __FUNCTION__, skb_queue_len(&dhd->rx_napi_queue), budget));
  864. __skb_queue_head_init(&rx_process_queue);
  865. /* extract the entire rx_napi_queue into local rx_process_queue */
  866. spin_lock_irqsave(&dhd->rx_napi_queue.lock, flags);
  867. skb_queue_splice_tail_init(&dhd->rx_napi_queue, &rx_process_queue);
  868. spin_unlock_irqrestore(&dhd->rx_napi_queue.lock, flags);
  869. while ((skb = __skb_dequeue(&rx_process_queue)) != NULL) {
  870. OSL_PREFETCH(skb->data);
  871. ifid = DHD_PKTTAG_IFID((dhd_pkttag_fr_t *)PKTTAG(skb));
  872. DHD_INFO(("%s dhd_rx_frame pkt<%p> ifid<%d>\n",
  873. __FUNCTION__, skb, ifid));
  874. dhd_rx_frame(&dhd->pub, ifid, skb, pkt_count, chan);
  875. processed++;
  876. }
  877. DHD_LB_STATS_UPDATE_NAPI_HISTO(&dhd->pub, processed);
  878. DHD_INFO(("%s processed %d\n", __FUNCTION__, processed));
  879. napi_complete(napi);
  880. return budget - 1;
  881. }
  882. /**
  883. * dhd_napi_schedule - Place the napi struct into the current cpus softnet napi
  884. * poll list. This function may be invoked via the smp_call_function_single
  885. * from a remote CPU.
  886. *
  887. * This function will essentially invoke __raise_softirq_irqoff(NET_RX_SOFTIRQ)
  888. * after the napi_struct is added to the softnet data's poll_list
  889. *
  890. * @info: pointer to a dhd_info struct
  891. */
  892. static void
  893. dhd_napi_schedule(void *info)
  894. {
  895. dhd_info_t *dhd = (dhd_info_t *)info;
  896. DHD_INFO(("%s rx_napi_struct<%p> on cpu<%d>\n",
  897. __FUNCTION__, &dhd->rx_napi_struct, atomic_read(&dhd->rx_napi_cpu)));
  898. /* add napi_struct to softnet data poll list and raise NET_RX_SOFTIRQ */
  899. if (napi_schedule_prep(&dhd->rx_napi_struct)) {
  900. __napi_schedule(&dhd->rx_napi_struct);
  901. #ifdef WAKEUP_KSOFTIRQD_POST_NAPI_SCHEDULE
  902. raise_softirq(NET_RX_SOFTIRQ);
  903. #endif /* WAKEUP_KSOFTIRQD_POST_NAPI_SCHEDULE */
  904. }
  905. /*
  906. * If the rx_napi_struct was already running, then we let it complete
  907. * processing all its packets. The rx_napi_struct may only run on one
  908. * core at a time, to avoid out-of-order handling.
  909. */
  910. }
  911. /**
  912. * dhd_napi_schedule_on - API to schedule on a desired CPU core a NET_RX_SOFTIRQ
  913. * action after placing the dhd's rx_process napi object in the the remote CPU's
  914. * softnet data's poll_list.
  915. *
  916. * @dhd: dhd_info which has the rx_process napi object
  917. * @on_cpu: desired remote CPU id
  918. */
  919. static INLINE int
  920. dhd_napi_schedule_on(dhd_info_t *dhd, int on_cpu)
  921. {
  922. int wait = 0; /* asynchronous IPI */
  923. DHD_INFO(("%s dhd<%p> napi<%p> on_cpu<%d>\n",
  924. __FUNCTION__, dhd, &dhd->rx_napi_struct, on_cpu));
  925. if (smp_call_function_single(on_cpu, dhd_napi_schedule, dhd, wait)) {
  926. DHD_ERROR(("%s smp_call_function_single on_cpu<%d> failed\n",
  927. __FUNCTION__, on_cpu));
  928. }
  929. DHD_LB_STATS_INCR(dhd->napi_sched_cnt);
  930. return 0;
  931. }
  932. /*
  933. * Call get_online_cpus/put_online_cpus around dhd_napi_schedule_on
  934. * Why should we do this?
  935. * The candidacy algorithm is run from the call back function
  936. * registered to CPU hotplug notifier. This call back happens from Worker
  937. * context. The dhd_napi_schedule_on is also from worker context.
  938. * Note that both of this can run on two different CPUs at the same time.
  939. * So we can possibly have a window where a given CPUn is being brought
  940. * down from CPUm while we try to run a function on CPUn.
  941. * To prevent this its better have the whole code to execute an SMP
  942. * function under get_online_cpus.
  943. * This function call ensures that hotplug mechanism does not kick-in
  944. * until we are done dealing with online CPUs
  945. * If the hotplug worker is already running, no worries because the
  946. * candidacy algo would then reflect the same in dhd->rx_napi_cpu.
  947. *
  948. * The below mentioned code structure is proposed in
  949. * https://www.kernel.org/doc/Documentation/cpu-hotplug.txt
  950. * for the question
  951. * Q: I need to ensure that a particular cpu is not removed when there is some
  952. * work specific to this cpu is in progress
  953. *
  954. * According to the documentation calling get_online_cpus is NOT required, if
  955. * we are running from tasklet context. Since dhd_rx_napi_dispatcher_fn can
  956. * run from Work Queue context we have to call these functions
  957. */
  958. void dhd_rx_napi_dispatcher_fn(struct work_struct * work)
  959. {
  960. #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
  961. #pragma GCC diagnostic push
  962. #pragma GCC diagnostic ignored "-Wcast-qual"
  963. #endif // endif
  964. struct dhd_info *dhd =
  965. container_of(work, struct dhd_info, rx_napi_dispatcher_work);
  966. #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
  967. #pragma GCC diagnostic pop
  968. #endif // endif
  969. int cpu;
  970. get_online_cpus();
  971. cpu = atomic_read(&dhd->rx_napi_cpu);
  972. if (!cpu_online(cpu))
  973. dhd_napi_schedule(dhd);
  974. else
  975. dhd_napi_schedule_on(dhd, cpu);
  976. put_online_cpus();
  977. }
  978. /**
  979. * dhd_lb_rx_napi_dispatch - load balance by dispatching the rx_napi_struct
  980. * to run on another CPU. The rx_napi_struct's poll function will retrieve all
  981. * the packets enqueued into the rx_napi_queue and sendup.
  982. * The producer's rx packet queue is appended to the rx_napi_queue before
  983. * dispatching the rx_napi_struct.
  984. */
  985. void
  986. dhd_lb_rx_napi_dispatch(dhd_pub_t *dhdp)
  987. {
  988. unsigned long flags;
  989. dhd_info_t *dhd = dhdp->info;
  990. int curr_cpu;
  991. int on_cpu;
  992. #ifdef DHD_LB_IRQSET
  993. cpumask_t cpus;
  994. #endif /* DHD_LB_IRQSET */
  995. if (dhd->rx_napi_netdev == NULL) {
  996. DHD_ERROR(("%s: dhd->rx_napi_netdev is NULL\n", __FUNCTION__));
  997. return;
  998. }
  999. DHD_INFO(("%s append napi_queue<%d> pend_queue<%d>\n", __FUNCTION__,
  1000. skb_queue_len(&dhd->rx_napi_queue), skb_queue_len(&dhd->rx_pend_queue)));
  1001. /* append the producer's queue of packets to the napi's rx process queue */
  1002. spin_lock_irqsave(&dhd->rx_napi_queue.lock, flags);
  1003. skb_queue_splice_tail_init(&dhd->rx_pend_queue, &dhd->rx_napi_queue);
  1004. spin_unlock_irqrestore(&dhd->rx_napi_queue.lock, flags);
  1005. DHD_LB_STATS_PERCPU_ARR_INCR(dhd->napi_percpu_run_cnt);
  1006. /* if LB RXP is disabled directly schedule NAPI */
  1007. if (atomic_read(&dhd->lb_rxp_active) == 0) {
  1008. dhd_napi_schedule(dhd);
  1009. return;
  1010. }
  1011. /*
  1012. * If the destination CPU is NOT online or is same as current CPU
  1013. * no need to schedule the work
  1014. */
  1015. curr_cpu = get_cpu();
  1016. put_cpu();
  1017. preempt_disable();
  1018. on_cpu = atomic_read(&dhd->rx_napi_cpu);
  1019. #ifdef DHD_LB_IRQSET
  1020. if (cpumask_and(&cpus, cpumask_of(curr_cpu), dhd->cpumask_primary) ||
  1021. (!cpu_online(on_cpu))) {
  1022. #else
  1023. if ((on_cpu == curr_cpu) || (!cpu_online(on_cpu))) {
  1024. #endif /* DHD_LB_IRQSET */
  1025. DHD_INFO(("%s : curr_cpu : %d, cpumask : 0x%lx\n", __FUNCTION__,
  1026. curr_cpu, *cpumask_bits(dhd->cpumask_primary)));
  1027. dhd_napi_schedule(dhd);
  1028. } else {
  1029. DHD_INFO(("%s : schedule to curr_cpu : %d, cpumask : 0x%lx\n",
  1030. __FUNCTION__, curr_cpu, *cpumask_bits(dhd->cpumask_primary)));
  1031. schedule_work(&dhd->rx_napi_dispatcher_work);
  1032. DHD_LB_STATS_INCR(dhd->napi_sched_cnt);
  1033. }
  1034. preempt_enable();
  1035. }
  1036. /**
  1037. * dhd_lb_rx_pkt_enqueue - Enqueue the packet into the producer's queue
  1038. */
  1039. void
  1040. dhd_lb_rx_pkt_enqueue(dhd_pub_t *dhdp, void *pkt, int ifidx)
  1041. {
  1042. dhd_info_t *dhd = dhdp->info;
  1043. DHD_INFO(("%s enqueue pkt<%p> ifidx<%d> pend_queue<%d>\n", __FUNCTION__,
  1044. pkt, ifidx, skb_queue_len(&dhd->rx_pend_queue)));
  1045. DHD_PKTTAG_SET_IFID((dhd_pkttag_fr_t *)PKTTAG(pkt), ifidx);
  1046. __skb_queue_tail(&dhd->rx_pend_queue, pkt);
  1047. }
  1048. #endif /* DHD_LB_RXP */
  1049. #endif /* DHD_LB */
  1050. #if defined(DHD_LB_IRQSET) || defined(DHD_CONTROL_PCIE_CPUCORE_WIFI_TURNON)
  1051. void
  1052. dhd_irq_set_affinity(dhd_pub_t *dhdp, const struct cpumask *cpumask)
  1053. {
  1054. unsigned int irq = (unsigned int)-1;
  1055. int err = BCME_OK;
  1056. if (!dhdp) {
  1057. DHD_ERROR(("%s : dhdp is NULL\n", __FUNCTION__));
  1058. return;
  1059. }
  1060. if (!dhdp->bus) {
  1061. DHD_ERROR(("%s : bus is NULL\n", __FUNCTION__));
  1062. return;
  1063. }
  1064. DHD_ERROR(("%s : irq set affinity cpu:0x%lx\n",
  1065. __FUNCTION__, *cpumask_bits(cpumask)));
  1066. dhdpcie_get_pcieirq(dhdp->bus, &irq);
  1067. err = irq_set_affinity(irq, cpumask);
  1068. if (err)
  1069. DHD_ERROR(("%s : irq set affinity is failed cpu:0x%lx\n",
  1070. __FUNCTION__, *cpumask_bits(cpumask)));
  1071. }
  1072. #endif /* DHD_LB_IRQSET || DHD_CONTROL_PCIE_CPUCORE_WIFI_TURNON */
  1073. #if defined(DHD_LB_TXP)
  1074. int BCMFASTPATH
  1075. dhd_lb_sendpkt(dhd_info_t *dhd, struct net_device *net,
  1076. int ifidx, void *skb)
  1077. {
  1078. DHD_LB_STATS_PERCPU_ARR_INCR(dhd->tx_start_percpu_run_cnt);
  1079. /* If the feature is disabled run-time do TX from here */
  1080. if (atomic_read(&dhd->lb_txp_active) == 0) {
  1081. DHD_LB_STATS_PERCPU_ARR_INCR(dhd->txp_percpu_run_cnt);
  1082. return __dhd_sendpkt(&dhd->pub, ifidx, skb);
  1083. }
  1084. /* Store the address of net device and interface index in the Packet tag */
  1085. DHD_LB_TX_PKTTAG_SET_NETDEV((dhd_tx_lb_pkttag_fr_t *)PKTTAG(skb), net);
  1086. DHD_LB_TX_PKTTAG_SET_IFIDX((dhd_tx_lb_pkttag_fr_t *)PKTTAG(skb), ifidx);
  1087. /* Enqueue the skb into tx_pend_queue */
  1088. skb_queue_tail(&dhd->tx_pend_queue, skb);
  1089. DHD_TRACE(("%s(): Added skb %p for netdev %p \r\n", __FUNCTION__, skb, net));
  1090. /* Dispatch the Tx job to be processed by the tx_tasklet */
  1091. dhd_lb_tx_dispatch(&dhd->pub);
  1092. return NETDEV_TX_OK;
  1093. }
  1094. #endif /* DHD_LB_TXP */
  1095. #ifdef DHD_LB_TXP
  1096. #define DHD_LB_TXBOUND 64
  1097. /*
  1098. * Function that performs the TX processing on a given CPU
  1099. */
  1100. bool
  1101. dhd_lb_tx_process(dhd_info_t *dhd)
  1102. {
  1103. struct sk_buff *skb;
  1104. int cnt = 0;
  1105. struct net_device *net;
  1106. int ifidx;
  1107. bool resched = FALSE;
  1108. DHD_TRACE(("%s(): TX Processing \r\n", __FUNCTION__));
  1109. if (dhd == NULL) {
  1110. DHD_ERROR((" Null pointer DHD \r\n"));
  1111. return resched;
  1112. }
  1113. BCM_REFERENCE(net);
  1114. DHD_LB_STATS_PERCPU_ARR_INCR(dhd->txp_percpu_run_cnt);
  1115. /* Base Loop to perform the actual Tx */
  1116. do {
  1117. skb = skb_dequeue(&dhd->tx_pend_queue);
  1118. if (skb == NULL) {
  1119. DHD_TRACE(("Dequeued a Null Packet \r\n"));
  1120. break;
  1121. }
  1122. cnt++;
  1123. net = DHD_LB_TX_PKTTAG_NETDEV((dhd_tx_lb_pkttag_fr_t *)PKTTAG(skb));
  1124. ifidx = DHD_LB_TX_PKTTAG_IFIDX((dhd_tx_lb_pkttag_fr_t *)PKTTAG(skb));
  1125. DHD_TRACE(("Processing skb %p for net %p index %d \r\n", skb,
  1126. net, ifidx));
  1127. __dhd_sendpkt(&dhd->pub, ifidx, skb);
  1128. if (cnt >= DHD_LB_TXBOUND) {
  1129. resched = TRUE;
  1130. break;
  1131. }
  1132. } while (1);
  1133. DHD_INFO(("%s(): Processed %d packets \r\n", __FUNCTION__, cnt));
  1134. return resched;
  1135. }
  1136. void
  1137. dhd_lb_tx_handler(unsigned long data)
  1138. {
  1139. dhd_info_t *dhd = (dhd_info_t *)data;
  1140. if (dhd_lb_tx_process(dhd)) {
  1141. dhd_tasklet_schedule(&dhd->tx_tasklet);
  1142. }
  1143. }
  1144. #endif /* DHD_LB_TXP */