Prakash Dhavali | 7090c5f | 2015-11-02 17:55:19 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2015 The Linux Foundation. All rights reserved. |
| 3 | * |
| 4 | * Previously licensed under the ISC license by Qualcomm Atheros, Inc. |
| 5 | * |
| 6 | * |
| 7 | * Permission to use, copy, modify, and/or distribute this software for |
| 8 | * any purpose with or without fee is hereby granted, provided that the |
| 9 | * above copyright notice and this permission notice appear in all |
| 10 | * copies. |
| 11 | * |
| 12 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL |
| 13 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED |
| 14 | * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE |
| 15 | * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL |
| 16 | * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR |
| 17 | * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
| 18 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
| 19 | * PERFORMANCE OF THIS SOFTWARE. |
| 20 | */ |
| 21 | |
| 22 | /* |
| 23 | * This file was originally distributed by Qualcomm Atheros, Inc. |
| 24 | * under proprietary terms before Copyright ownership was assigned |
| 25 | * to the Linux Foundation. |
| 26 | */ |
| 27 | |
| 28 | /** |
| 29 | * DOC: wlan_hdd_napi.c |
| 30 | * |
| 31 | * WLAN HDD NAPI interface implementation |
| 32 | */ |
| 33 | #include <smp.h> /* get_cpu */ |
| 34 | |
| 35 | #include "wlan_hdd_napi.h" |
| 36 | #include "cds_api.h" /* cds_get_context */ |
| 37 | #include "hif.h" /* hif_map_service...*/ |
| 38 | #include "wlan_hdd_main.h" /* hdd_err/warn... */ |
| 39 | #include "cdf_types.h" /* CDF_MODULE_ID_... */ |
| 40 | #include "ce_api.h" |
| 41 | |
| 42 | /* guaranteed to be initialized to zero/NULL by the standard */ |
| 43 | static struct qca_napi_data *hdd_napi_ctx; |
| 44 | |
| 45 | /** |
| 46 | * hdd_napi_get_all() - return the whole NAPI structure from HIF |
| 47 | * |
| 48 | * Gets to the data structure common to all NAPI instances. |
| 49 | * |
| 50 | * Return: |
| 51 | * NULL : probably NAPI not initialized yet. |
| 52 | * <addr>: the address of the whole NAPI structure |
| 53 | */ |
| 54 | struct qca_napi_data *hdd_napi_get_all(void) |
| 55 | { |
Orhan K AKYILDIZ | c409461 | 2015-11-11 18:01:15 -0800 | [diff] [blame] | 56 | struct qca_napi_data *rp = NULL; |
Prakash Dhavali | 7090c5f | 2015-11-02 17:55:19 -0800 | [diff] [blame] | 57 | struct ol_softc *hif; |
| 58 | |
| 59 | NAPI_DEBUG("-->\n"); |
| 60 | |
| 61 | hif = cds_get_context(CDF_MODULE_ID_HIF); |
Orhan K AKYILDIZ | c409461 | 2015-11-11 18:01:15 -0800 | [diff] [blame] | 62 | if (unlikely(NULL == hif)) |
| 63 | CDF_ASSERT(NULL != hif); /* WARN */ |
| 64 | else |
| 65 | rp = hif_napi_get_all(hif); |
Prakash Dhavali | 7090c5f | 2015-11-02 17:55:19 -0800 | [diff] [blame] | 66 | |
| 67 | NAPI_DEBUG("<-- [addr=%p]\n", rp); |
| 68 | return rp; |
| 69 | } |
| 70 | |
| 71 | /** |
| 72 | * hdd_napi_get_map() - get a copy of napi pipe map |
| 73 | * |
| 74 | * Return: |
| 75 | * uint32_t : copy of pipe map |
| 76 | */ |
| 77 | static uint32_t hdd_napi_get_map(void) |
| 78 | { |
Orhan K AKYILDIZ | c409461 | 2015-11-11 18:01:15 -0800 | [diff] [blame] | 79 | uint32_t map = 0; |
Prakash Dhavali | 7090c5f | 2015-11-02 17:55:19 -0800 | [diff] [blame] | 80 | |
| 81 | NAPI_DEBUG("-->\n"); |
| 82 | /* cache once, use forever */ |
| 83 | if (hdd_napi_ctx == NULL) |
| 84 | hdd_napi_ctx = hdd_napi_get_all(); |
Orhan K AKYILDIZ | c409461 | 2015-11-11 18:01:15 -0800 | [diff] [blame] | 85 | if (hdd_napi_ctx != NULL) |
| 86 | map = hdd_napi_ctx->ce_map; |
Prakash Dhavali | 7090c5f | 2015-11-02 17:55:19 -0800 | [diff] [blame] | 87 | |
| 88 | NAPI_DEBUG("<--[map=0x%08x]\n", map); |
| 89 | return map; |
| 90 | } |
| 91 | |
| 92 | /** |
| 93 | * hdd_napi_create() - creates the NAPI structures for a given netdev |
| 94 | * |
| 95 | * Creates NAPI instances. This function is called |
| 96 | * unconditionally during initialization. It creates |
| 97 | * napi structures through the proper HTC/HIF calls. |
| 98 | * The structures are disabled on creation. |
| 99 | * |
| 100 | * Return: |
| 101 | * single-queue: <0: err, >0=id, 0 (should not happen) |
| 102 | * multi-queue: bitmap of created instances (0: none) |
| 103 | */ |
| 104 | int hdd_napi_create(void) |
| 105 | { |
| 106 | struct ol_softc *hif_ctx; |
| 107 | uint8_t ul, dl; |
| 108 | int ul_polled, dl_polled; |
| 109 | int rc = 0; |
| 110 | |
Prakash Dhavali | 7090c5f | 2015-11-02 17:55:19 -0800 | [diff] [blame] | 111 | NAPI_DEBUG("-->\n"); |
| 112 | |
Orhan K AKYILDIZ | c409461 | 2015-11-11 18:01:15 -0800 | [diff] [blame] | 113 | hif_ctx = cds_get_context(CDF_MODULE_ID_HIF); |
| 114 | if (unlikely(NULL == hif_ctx)) { |
| 115 | CDF_ASSERT(NULL != hif_ctx); |
| 116 | rc = -EFAULT; |
Prakash Dhavali | 7090c5f | 2015-11-02 17:55:19 -0800 | [diff] [blame] | 117 | } else { |
Orhan K AKYILDIZ | c409461 | 2015-11-11 18:01:15 -0800 | [diff] [blame] | 118 | /* |
| 119 | * Note: hif_service_to_pipe returns one pipe id per service. |
| 120 | * For multi-queue NAPI for Adrastea, we will use multiple |
| 121 | * services/calls. |
| 122 | * For Rome, there is only one service, hence a single call |
| 123 | */ |
| 124 | if (CDF_STATUS_SUCCESS != |
| 125 | hif_map_service_to_pipe(hif_ctx, HTT_DATA_MSG_SVC, |
| 126 | &ul, &dl, &ul_polled, &dl_polled)) { |
| 127 | hdd_err("cannot map service to pipe"); |
| 128 | rc = -EINVAL; |
| 129 | } else { |
| 130 | rc = hif_napi_create(hif_ctx, dl, hdd_napi_poll, |
| 131 | QCA_NAPI_BUDGET, |
| 132 | QCA_NAPI_DEF_SCALE); |
| 133 | if (rc < 0) |
| 134 | hdd_err("ERR(%d) creating NAPI on pipe %d", |
| 135 | rc, dl); |
| 136 | else { |
| 137 | hdd_info("napi instance %d created on pipe %d", |
| 138 | rc, dl); |
| 139 | /* rc = (0x01 << rc); -- phase 2 */ |
| 140 | } |
Prakash Dhavali | 7090c5f | 2015-11-02 17:55:19 -0800 | [diff] [blame] | 141 | } |
| 142 | } |
| 143 | NAPI_DEBUG("<-- [rc=%d]\n", rc); |
| 144 | |
| 145 | return rc; |
| 146 | } |
| 147 | |
| 148 | /** |
| 149 | * hdd_napi_destroy() - destroys the NAPI structures for a given netdev |
| 150 | * @force: if set, will force-disable the instance before _del'ing |
| 151 | * |
| 152 | * Destroy NAPI instances. This function is called |
| 153 | * unconditionally during module removal. It destroy |
| 154 | * napi structures through the proper HTC/HIF calls. |
| 155 | * |
| 156 | * Return: |
| 157 | * number of NAPI instances destroyed |
| 158 | */ |
| 159 | int hdd_napi_destroy(int force) |
| 160 | { |
| 161 | int rc = 0; |
| 162 | int i; |
| 163 | uint32_t hdd_napi_map = hdd_napi_get_map(); |
| 164 | |
| 165 | NAPI_DEBUG("--> (force=%d)\n", force); |
| 166 | if (hdd_napi_map) { |
| 167 | struct ol_softc *hif_ctx; |
| 168 | |
| 169 | hif_ctx = cds_get_context(CDF_MODULE_ID_HIF); |
Orhan K AKYILDIZ | c409461 | 2015-11-11 18:01:15 -0800 | [diff] [blame] | 170 | if (unlikely(NULL == hif_ctx)) |
| 171 | CDF_ASSERT(NULL != hif_ctx); |
| 172 | else |
| 173 | for (i = 0; i < CE_COUNT_MAX; i++) |
| 174 | if (hdd_napi_map & (0x01 << i)) { |
| 175 | if (0 <= hif_napi_destroy( |
| 176 | hif_ctx, |
| 177 | NAPI_PIPE2ID(i), force)) { |
| 178 | rc++; |
| 179 | hdd_napi_map &= ~(0x01 << i); |
| 180 | } else |
| 181 | hdd_err("cannot destroy napi %d: (pipe:%d), f=%d\n", |
| 182 | i, |
| 183 | NAPI_PIPE2ID(i), force); |
| 184 | } |
Prakash Dhavali | 7090c5f | 2015-11-02 17:55:19 -0800 | [diff] [blame] | 185 | } |
| 186 | |
| 187 | /* if all instances are removed, it is likely that hif_context has been |
| 188 | * removed as well, so the cached value of the napi context also needs |
| 189 | * to be removed |
| 190 | */ |
| 191 | if (force) |
| 192 | CDF_ASSERT(hdd_napi_map == 0); |
| 193 | if (0 == hdd_napi_map) |
| 194 | hdd_napi_ctx = NULL; |
| 195 | |
| 196 | NAPI_DEBUG("<-- [rc=%d]\n", rc); |
| 197 | return rc; |
| 198 | } |
| 199 | |
| 200 | /** |
| 201 | * hdd_napi_enabled() - checks if NAPI is enabled (for a given id) |
| 202 | * @id: the id of the NAPI to check (any= -1) |
| 203 | * |
| 204 | * Return: |
| 205 | * int: 0 = false (NOT enabled) |
| 206 | * !0 = true (enabbled) |
| 207 | */ |
Orhan K AKYILDIZ | c409461 | 2015-11-11 18:01:15 -0800 | [diff] [blame] | 208 | int hdd_napi_enabled(int id) |
Prakash Dhavali | 7090c5f | 2015-11-02 17:55:19 -0800 | [diff] [blame] | 209 | { |
| 210 | struct ol_softc *hif; |
Orhan K AKYILDIZ | c409461 | 2015-11-11 18:01:15 -0800 | [diff] [blame] | 211 | int rc = 0; /* NOT enabled */ |
Prakash Dhavali | 7090c5f | 2015-11-02 17:55:19 -0800 | [diff] [blame] | 212 | |
| 213 | hif = cds_get_context(CDF_MODULE_ID_HIF); |
Orhan K AKYILDIZ | c409461 | 2015-11-11 18:01:15 -0800 | [diff] [blame] | 214 | if (unlikely(NULL == hif)) |
| 215 | CDF_ASSERT(hif != NULL); /* WARN_ON; rc = 0 */ |
| 216 | else if (-1 == id) |
| 217 | rc = hif_napi_enabled(hif, id); |
Prakash Dhavali | 7090c5f | 2015-11-02 17:55:19 -0800 | [diff] [blame] | 218 | else |
Orhan K AKYILDIZ | c409461 | 2015-11-11 18:01:15 -0800 | [diff] [blame] | 219 | rc = hif_napi_enabled(hif, NAPI_ID2PIPE(id)); |
| 220 | return rc; |
Prakash Dhavali | 7090c5f | 2015-11-02 17:55:19 -0800 | [diff] [blame] | 221 | } |
| 222 | |
| 223 | /** |
| 224 | * hdd_napi_event() - relay the event detected by HDD to HIF NAPI decision maker |
| 225 | * @event: event code |
| 226 | * @data : event-specific auxiliary data |
| 227 | * |
| 228 | * Return code does not indicate a change, but whether or not NAPI is |
| 229 | * enabled at the time of the return of the function. That is, if NAPI |
| 230 | * was disabled before the call, and the event does not cause NAPI to be |
| 231 | * enabled, a value of 0 will be returned indicating that it is (still) |
| 232 | * disabled. |
| 233 | * |
| 234 | * Return: |
| 235 | * < 0: error code |
| 236 | * = 0: NAPI state = disabled (after processing the event) |
| 237 | * = 1: NAPI state = enabled (after processing the event) |
| 238 | */ |
| 239 | int hdd_napi_event(enum qca_napi_event event, void *data) |
| 240 | { |
Orhan K AKYILDIZ | c409461 | 2015-11-11 18:01:15 -0800 | [diff] [blame] | 241 | int rc = -EFAULT; /* assume err */ |
Prakash Dhavali | 7090c5f | 2015-11-02 17:55:19 -0800 | [diff] [blame] | 242 | struct ol_softc *hif; |
| 243 | |
| 244 | NAPI_DEBUG("-->(event=%d, aux=%p)\n", event, data); |
| 245 | |
| 246 | hif = cds_get_context(CDF_MODULE_ID_HIF); |
Orhan K AKYILDIZ | c409461 | 2015-11-11 18:01:15 -0800 | [diff] [blame] | 247 | if (unlikely(NULL == hif)) |
| 248 | CDF_ASSERT(hif != NULL); |
| 249 | else |
| 250 | rc = hif_napi_event(hif, event, data); |
Prakash Dhavali | 7090c5f | 2015-11-02 17:55:19 -0800 | [diff] [blame] | 251 | |
| 252 | NAPI_DEBUG("<--[rc=%d]\n", rc); |
| 253 | return rc; |
| 254 | } |
| 255 | |
| 256 | /** |
| 257 | * hdd_napi_poll() - NAPI poll function |
| 258 | * @napi : pointer to NAPI struct |
| 259 | * @budget: the pre-declared budget |
| 260 | * |
| 261 | * Implementation of poll function. This function is called |
| 262 | * by kernel during softirq processing. |
| 263 | * |
| 264 | * NOTE FOR THE MAINTAINER: |
| 265 | * Make sure this is very close to the ce_tasklet code. |
| 266 | * |
| 267 | * Return: |
| 268 | * int: the amount of work done ( <= budget ) |
| 269 | */ |
| 270 | int hdd_napi_poll(struct napi_struct *napi, int budget) |
| 271 | { |
| 272 | return hif_napi_poll(napi, budget); |
| 273 | } |