1 /* 2 * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. 3 * 4 * Permission to use, copy, modify, and/or distribute this software for 5 * any purpose with or without fee is hereby granted, provided that the 6 * above copyright notice and this permission notice appear in all 7 * copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 10 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 11 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 12 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 13 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 14 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 15 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 16 * PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <hif_exec.h> 20 #include <ce_main.h> 21 #include <hif_irq_affinity.h> 22 #include "qdf_module.h" 23 #include "qdf_net_if.h" 24 25 /* mapping NAPI budget 0 to internal budget 0 26 * NAPI budget 1 to internal budget [1,scaler -1] 27 * NAPI budget 2 to internal budget [scaler, 2 * scaler - 1], etc 28 */ 29 #define NAPI_BUDGET_TO_INTERNAL_BUDGET(n, s) \ 30 (((n) << (s)) - 1) 31 #define INTERNAL_BUDGET_TO_NAPI_BUDGET(n, s) \ 32 (((n) + 1) >> (s)) 33 34 static struct hif_exec_context *hif_exec_tasklet_create(void); 35 36 /** 37 * hif_print_napi_stats() - print NAPI stats 38 * @hif_ctx: hif context 39 * 40 * return: void 41 */ 42 void hif_print_napi_stats(struct hif_opaque_softc *hif_ctx) 43 { 44 struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(hif_ctx); 45 struct hif_exec_context *hif_ext_group; 46 struct qca_napi_stat *napi_stats; 47 int i, j; 48 49 QDF_TRACE(QDF_MODULE_ID_HIF, QDF_TRACE_LEVEL_FATAL, 50 "NAPI[#ctx]CPU[#] |schedules |polls |completes |workdone"); 51 52 for (i = 0; i < hif_state->hif_num_extgroup; i++) { 53 if (hif_state->hif_ext_group[i]) { 54 hif_ext_group = hif_state->hif_ext_group[i]; 55 for (j = 0; j < num_possible_cpus(); j++) { 56 napi_stats = &(hif_ext_group->stats[j]); 57 if (napi_stats->napi_schedules != 0) 58 QDF_TRACE(QDF_MODULE_ID_HIF, 59 QDF_TRACE_LEVEL_FATAL, 60 "NAPI[%2d]CPU[%d]: " 61 "%7d %7d %7d %7d ", 62 i, j, 63 napi_stats->napi_schedules, 64 napi_stats->napi_polls, 65 napi_stats->napi_completes, 66 napi_stats->napi_workdone); 67 } 68 } 69 } 70 } 71 qdf_export_symbol(hif_print_napi_stats); 72 73 static void hif_exec_tasklet_schedule(struct hif_exec_context *ctx) 74 { 75 struct hif_tasklet_exec_context *t_ctx = hif_exec_get_tasklet(ctx); 76 77 tasklet_schedule(&t_ctx->tasklet); 78 } 79 80 /** 81 * hif_exec_tasklet() - grp tasklet 82 * data: context 83 * 84 * return: void 85 */ 86 static void hif_exec_tasklet_fn(unsigned long data) 87 { 88 struct hif_exec_context *hif_ext_group = 89 (struct hif_exec_context *)data; 90 struct hif_softc *scn = HIF_GET_SOFTC(hif_ext_group->hif); 91 unsigned int work_done; 92 93 work_done = 94 hif_ext_group->handler(hif_ext_group->context, HIF_MAX_BUDGET); 95 96 if (hif_ext_group->work_complete(hif_ext_group, work_done)) { 97 qdf_atomic_dec(&(scn->active_grp_tasklet_cnt)); 98 hif_ext_group->irq_enable(hif_ext_group); 99 } else { 100 hif_exec_tasklet_schedule(hif_ext_group); 101 } 102 } 103 104 /** 105 * hif_exec_poll() - grp tasklet 106 * data: context 107 * 108 * return: void 109 */ 110 static int hif_exec_poll(struct napi_struct *napi, int budget) 111 { 112 struct hif_napi_exec_context *exec_ctx = 113 qdf_container_of(napi, struct hif_napi_exec_context, napi); 114 struct hif_exec_context *hif_ext_group = &exec_ctx->exec_ctx; 115 struct hif_softc *scn = HIF_GET_SOFTC(hif_ext_group->hif); 116 int work_done; 117 int normalized_budget = 0; 118 int shift = hif_ext_group->scale_bin_shift; 119 int cpu = smp_processor_id(); 120 121 if (budget) 122 normalized_budget = NAPI_BUDGET_TO_INTERNAL_BUDGET(budget, shift); 123 work_done = hif_ext_group->handler(hif_ext_group->context, 124 normalized_budget); 125 126 if (work_done < normalized_budget) { 127 napi_complete(napi); 128 qdf_atomic_dec(&scn->active_grp_tasklet_cnt); 129 hif_ext_group->irq_enable(hif_ext_group); 130 hif_ext_group->stats[cpu].napi_completes++; 131 } else { 132 /* if the ext_group supports time based yield, claim full work 133 * done anyways */ 134 work_done = normalized_budget; 135 } 136 137 hif_ext_group->stats[cpu].napi_polls++; 138 hif_ext_group->stats[cpu].napi_workdone += work_done; 139 140 /* map internal budget to NAPI budget */ 141 if (work_done) 142 work_done = INTERNAL_BUDGET_TO_NAPI_BUDGET(work_done, shift); 143 144 return work_done; 145 } 146 147 /** 148 * hif_exec_napi_schedule() - schedule the napi exec instance 149 * @ctx: a hif_exec_context known to be of napi type 150 */ 151 static void hif_exec_napi_schedule(struct hif_exec_context *ctx) 152 { 153 struct hif_napi_exec_context *n_ctx = hif_exec_get_napi(ctx); 154 ctx->stats[smp_processor_id()].napi_schedules++; 155 156 napi_schedule(&n_ctx->napi); 157 } 158 159 /** 160 * hif_exec_napi_kill() - stop a napi exec context from being rescheduled 161 * @ctx: a hif_exec_context known to be of napi type 162 */ 163 static void hif_exec_napi_kill(struct hif_exec_context *ctx) 164 { 165 struct hif_napi_exec_context *n_ctx = hif_exec_get_napi(ctx); 166 int irq_ind; 167 168 if (ctx->inited) { 169 napi_disable(&n_ctx->napi); 170 ctx->inited = 0; 171 } 172 173 for (irq_ind = 0; irq_ind < ctx->numirq; irq_ind++) 174 hif_irq_affinity_remove(ctx->os_irq[irq_ind]); 175 176 netif_napi_del(&(n_ctx->napi)); 177 } 178 179 struct hif_execution_ops napi_sched_ops = { 180 .schedule = &hif_exec_napi_schedule, 181 .kill = &hif_exec_napi_kill, 182 }; 183 184 #ifdef FEATURE_NAPI 185 /** 186 * hif_exec_napi_create() - allocate and initialize a napi exec context 187 * @scale: a binary shift factor to map NAPI budget from\to internal 188 * budget 189 */ 190 static struct hif_exec_context *hif_exec_napi_create(uint32_t scale) 191 { 192 struct hif_napi_exec_context *ctx; 193 194 ctx = qdf_mem_malloc(sizeof(struct hif_napi_exec_context)); 195 if (ctx == NULL) 196 return NULL; 197 198 ctx->exec_ctx.sched_ops = &napi_sched_ops; 199 ctx->exec_ctx.inited = true; 200 ctx->exec_ctx.scale_bin_shift = scale; 201 qdf_net_if_create_dummy_if((struct qdf_net_if *)&ctx->netdev); 202 netif_napi_add(&(ctx->netdev), &(ctx->napi), hif_exec_poll, 203 QCA_NAPI_BUDGET); 204 napi_enable(&ctx->napi); 205 206 return &ctx->exec_ctx; 207 } 208 #else 209 static struct hif_exec_context *hif_exec_napi_create(uint32_t scale) 210 { 211 HIF_WARN("%s: FEATURE_NAPI not defined, making tasklet"); 212 return hif_exec_tasklet_create(); 213 } 214 #endif 215 216 217 /** 218 * hif_exec_tasklet_kill() - stop a tasklet exec context from being rescheduled 219 * @ctx: a hif_exec_context known to be of tasklet type 220 */ 221 static void hif_exec_tasklet_kill(struct hif_exec_context *ctx) 222 { 223 struct hif_tasklet_exec_context *t_ctx = hif_exec_get_tasklet(ctx); 224 int irq_ind; 225 226 if (ctx->inited) { 227 tasklet_disable(&t_ctx->tasklet); 228 tasklet_kill(&t_ctx->tasklet); 229 } 230 ctx->inited = false; 231 232 for (irq_ind = 0; irq_ind < ctx->numirq; irq_ind++) 233 hif_irq_affinity_remove(ctx->os_irq[irq_ind]); 234 } 235 236 struct hif_execution_ops tasklet_sched_ops = { 237 .schedule = &hif_exec_tasklet_schedule, 238 .kill = &hif_exec_tasklet_kill, 239 }; 240 241 /** 242 * hif_exec_tasklet_schedule() - allocate and initialize a tasklet exec context 243 */ 244 static struct hif_exec_context *hif_exec_tasklet_create(void) 245 { 246 struct hif_tasklet_exec_context *ctx; 247 248 ctx = qdf_mem_malloc(sizeof(struct hif_tasklet_exec_context)); 249 if (ctx == NULL) 250 return NULL; 251 252 ctx->exec_ctx.sched_ops = &tasklet_sched_ops; 253 tasklet_init(&ctx->tasklet, hif_exec_tasklet_fn, 254 (unsigned long)ctx); 255 256 ctx->exec_ctx.inited = true; 257 258 return &ctx->exec_ctx; 259 } 260 261 /** 262 * hif_exec_get_ctx() - retrieve an exec context based on an id 263 * @softc: the hif context owning the exec context 264 * @id: the id of the exec context 265 * 266 * mostly added to make it easier to rename or move the context array 267 */ 268 struct hif_exec_context *hif_exec_get_ctx(struct hif_opaque_softc *softc, 269 uint8_t id) 270 { 271 struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(softc); 272 273 if (id < hif_state->hif_num_extgroup) 274 return hif_state->hif_ext_group[id]; 275 276 return NULL; 277 } 278 279 /** 280 * hif_configure_ext_group_interrupts() - API to configure external group 281 * interrpts 282 * @hif_ctx : HIF Context 283 * 284 * Return: status 285 */ 286 uint32_t hif_configure_ext_group_interrupts(struct hif_opaque_softc *hif_ctx) 287 { 288 struct hif_softc *scn = HIF_GET_SOFTC(hif_ctx); 289 struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(hif_ctx); 290 struct hif_exec_context *hif_ext_group; 291 int i, status; 292 293 if (scn->ext_grp_irq_configured) { 294 HIF_ERROR("%s Called after ext grp irq configured\n", __func__); 295 return QDF_STATUS_E_FAILURE; 296 } 297 298 for (i = 0; i < hif_state->hif_num_extgroup; i++) { 299 hif_ext_group = hif_state->hif_ext_group[i]; 300 status = 0; 301 qdf_spinlock_create(&hif_ext_group->irq_lock); 302 if (hif_ext_group->configured && 303 hif_ext_group->irq_requested == false) { 304 hif_ext_group->irq_enabled = true; 305 status = hif_grp_irq_configure(scn, hif_ext_group); 306 } 307 if (status != 0) { 308 HIF_ERROR("%s: failed for group %d", __func__, i); 309 hif_ext_group->irq_enabled = false; 310 } 311 } 312 313 scn->ext_grp_irq_configured = true; 314 315 return QDF_STATUS_SUCCESS; 316 } 317 qdf_export_symbol(hif_configure_ext_group_interrupts); 318 319 /** 320 * hif_ext_group_interrupt_handler() - handler for related interrupts 321 * @irq: irq number of the interrupt 322 * @context: the associated hif_exec_group context 323 * 324 * This callback function takes care of dissabling the associated interrupts 325 * and scheduling the expected bottom half for the exec_context. 326 * This callback function also helps keep track of the count running contexts. 327 */ 328 irqreturn_t hif_ext_group_interrupt_handler(int irq, void *context) 329 { 330 struct hif_exec_context *hif_ext_group = context; 331 struct hif_softc *scn = HIF_GET_SOFTC(hif_ext_group->hif); 332 333 if (hif_ext_group->irq_requested) { 334 hif_ext_group->irq_disable(hif_ext_group); 335 qdf_atomic_inc(&scn->active_grp_tasklet_cnt); 336 337 hif_ext_group->sched_ops->schedule(hif_ext_group); 338 } 339 340 return IRQ_HANDLED; 341 } 342 343 /** 344 * hif_exec_kill() - grp tasklet kill 345 * scn: hif_softc 346 * 347 * return: void 348 */ 349 void hif_exec_kill(struct hif_opaque_softc *hif_ctx) 350 { 351 int i; 352 struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(hif_ctx); 353 354 for (i = 0; i < hif_state->hif_num_extgroup; i++) 355 hif_state->hif_ext_group[i]->sched_ops->kill( 356 hif_state->hif_ext_group[i]); 357 358 qdf_atomic_set(&hif_state->ol_sc.active_grp_tasklet_cnt, 0); 359 } 360 361 /** 362 * hif_register_ext_group() - API to register external group 363 * interrupt handler. 364 * @hif_ctx : HIF Context 365 * @numirq: number of irq's in the group 366 * @irq: array of irq values 367 * @handler: callback interrupt handler function 368 * @cb_ctx: context to passed in callback 369 * @type: napi vs tasklet 370 * 371 * Return: status 372 */ 373 uint32_t hif_register_ext_group(struct hif_opaque_softc *hif_ctx, 374 uint32_t numirq, uint32_t irq[], ext_intr_handler handler, 375 void *cb_ctx, const char *context_name, 376 enum hif_exec_type type, uint32_t scale) 377 { 378 struct hif_softc *scn = HIF_GET_SOFTC(hif_ctx); 379 struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(scn); 380 struct hif_exec_context *hif_ext_group; 381 382 if (scn->ext_grp_irq_configured) { 383 HIF_ERROR("%s Called after ext grp irq configured\n", __func__); 384 return QDF_STATUS_E_FAILURE; 385 } 386 387 if (hif_state->hif_num_extgroup >= HIF_MAX_GROUP) { 388 HIF_ERROR("%s Max groups reached\n", __func__); 389 return QDF_STATUS_E_FAILURE; 390 } 391 392 if (numirq >= HIF_MAX_GRP_IRQ) { 393 HIF_ERROR("%s invalid numirq\n", __func__); 394 return QDF_STATUS_E_FAILURE; 395 } 396 397 hif_ext_group = hif_exec_create(type, scale); 398 if (hif_ext_group == NULL) 399 return QDF_STATUS_E_FAILURE; 400 401 hif_state->hif_ext_group[hif_state->hif_num_extgroup] = 402 hif_ext_group; 403 404 hif_ext_group->numirq = numirq; 405 qdf_mem_copy(&hif_ext_group->irq[0], irq, numirq * sizeof(irq[0])); 406 hif_ext_group->context = cb_ctx; 407 hif_ext_group->handler = handler; 408 hif_ext_group->configured = true; 409 hif_ext_group->grp_id = hif_state->hif_num_extgroup; 410 hif_ext_group->hif = hif_ctx; 411 hif_ext_group->context_name = context_name; 412 413 hif_state->hif_num_extgroup++; 414 return QDF_STATUS_SUCCESS; 415 } 416 qdf_export_symbol(hif_register_ext_group); 417 418 /** 419 * hif_exec_create() - create an execution context 420 * @type: the type of execution context to create 421 */ 422 struct hif_exec_context *hif_exec_create(enum hif_exec_type type, 423 uint32_t scale) 424 { 425 HIF_INFO("%s: create exec_type %d budget %d\n", 426 __func__, type, QCA_NAPI_BUDGET * scale); 427 428 switch (type) { 429 case HIF_EXEC_NAPI_TYPE: 430 return hif_exec_napi_create(scale); 431 432 case HIF_EXEC_TASKLET_TYPE: 433 return hif_exec_tasklet_create(); 434 default: 435 return NULL; 436 } 437 } 438 439 /** 440 * hif_exec_destroy() - free the hif_exec context 441 * @ctx: context to free 442 * 443 * please kill the context before freeing it to avoid a use after free. 444 */ 445 void hif_exec_destroy(struct hif_exec_context *ctx) 446 { 447 qdf_spinlock_destroy(&ctx->irq_lock); 448 qdf_mem_free(ctx); 449 } 450 451 /** 452 * hif_deregister_exec_group() - API to free the exec contexts 453 * @hif_ctx: HIF context 454 * @context_name: name of the module whose contexts need to be deregistered 455 * 456 * This function deregisters the contexts of the requestor identified 457 * based on the context_name & frees the memory. 458 * 459 * Return: void 460 */ 461 void hif_deregister_exec_group(struct hif_opaque_softc *hif_ctx, 462 const char *context_name) 463 { 464 struct hif_softc *scn = HIF_GET_SOFTC(hif_ctx); 465 struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(scn); 466 struct hif_exec_context *hif_ext_group; 467 int i; 468 469 for (i = 0; i < HIF_MAX_GROUP; i++) { 470 hif_ext_group = hif_state->hif_ext_group[i]; 471 472 if (!hif_ext_group) 473 continue; 474 475 HIF_INFO("%s: Deregistering grp id %d name %s\n", 476 __func__, 477 hif_ext_group->grp_id, 478 hif_ext_group->context_name); 479 480 if (strcmp(hif_ext_group->context_name, context_name) == 0) { 481 hif_ext_group->sched_ops->kill(hif_ext_group); 482 hif_state->hif_ext_group[i] = NULL; 483 hif_exec_destroy(hif_ext_group); 484 hif_state->hif_num_extgroup--; 485 } 486 487 } 488 } 489 qdf_export_symbol(hif_deregister_exec_group); 490