1 /* SPDX-License-Identifier: BSD-3-Clause 2 * 3 * Copyright(c) 2019-2021 Xilinx, Inc. 4 * Copyright(c) 2016-2019 Solarflare Communications Inc. 5 * 6 * This software was jointly developed between OKTET Labs (under contract 7 * for Solarflare) and Solarflare Communications, Inc. 8 */ 9 10 #include <rte_cycles.h> 11 12 #include "efx.h" 13 #include "efx_mcdi.h" 14 #include "efx_regs_mcdi.h" 15 16 #include "sfc_efx_mcdi.h" 17 #include "sfc_efx_debug.h" 18 19 #define SFC_EFX_MCDI_POLL_INTERVAL_MIN_US 10 /* 10us */ 20 #define SFC_EFX_MCDI_POLL_INTERVAL_MAX_US (US_PER_S / 10) /* 100ms */ 21 #define SFC_EFX_MCDI_WATCHDOG_INTERVAL_US (10 * US_PER_S) /* 10s */ 22 23 #define sfc_efx_mcdi_log(mcdi, level, ...) \ 24 do { \ 25 const struct sfc_efx_mcdi *_mcdi = (mcdi); \ 26 \ 27 rte_log(level, _mcdi->logtype, \ 28 RTE_FMT("%s" RTE_FMT_HEAD(__VA_ARGS__ ,) "\n", \ 29 _mcdi->log_prefix, \ 30 RTE_FMT_TAIL(__VA_ARGS__,))); \ 31 } while (0) 32 33 #define sfc_efx_mcdi_crit(mcdi, ...) \ 34 sfc_efx_mcdi_log(mcdi, RTE_LOG_CRIT, __VA_ARGS__) 35 36 #define sfc_efx_mcdi_err(mcdi, ...) \ 37 sfc_efx_mcdi_log(mcdi, RTE_LOG_ERR, __VA_ARGS__) 38 39 #define sfc_efx_mcdi_warn(mcdi, ...) \ 40 sfc_efx_mcdi_log(mcdi, RTE_LOG_WARNING, __VA_ARGS__) 41 42 #define sfc_efx_mcdi_info(mcdi, ...) \ 43 sfc_efx_mcdi_log(mcdi, RTE_LOG_INFO, __VA_ARGS__) 44 45 /** Level value used by MCDI log statements */ 46 #define SFC_EFX_LOG_LEVEL_MCDI RTE_LOG_INFO 47 48 #define sfc_efx_log_mcdi(mcdi, ...) \ 49 sfc_efx_mcdi_log(mcdi, SFC_EFX_LOG_LEVEL_MCDI, __VA_ARGS__) 50 51 static void 52 sfc_efx_mcdi_timeout(struct sfc_efx_mcdi *mcdi) 53 { 54 sfc_efx_mcdi_warn(mcdi, "MC TIMEOUT"); 55 56 sfc_efx_mcdi_crit(mcdi, "MCDI timeout handling is not implemented"); 57 sfc_efx_mcdi_crit(mcdi, "NIC is unusable"); 58 mcdi->state = SFC_EFX_MCDI_DEAD; 59 } 60 61 static inline boolean_t 62 sfc_efx_mcdi_proxy_event_available(struct sfc_efx_mcdi *mcdi) 63 { 64 mcdi->proxy_handle = 0; 65 mcdi->proxy_result = ETIMEDOUT; 66 mcdi->ops->mgmt_evq_poll(mcdi->ops_cookie); 67 if (mcdi->proxy_result != ETIMEDOUT) 68 return B_TRUE; 69 70 return B_FALSE; 71 } 72 73 static void 74 sfc_efx_mcdi_poll(struct sfc_efx_mcdi *mcdi, boolean_t proxy) 75 { 76 efx_nic_t *enp; 77 unsigned int delay_total; 78 unsigned int delay_us; 79 boolean_t aborted __rte_unused; 80 81 delay_total = 0; 82 delay_us = SFC_EFX_MCDI_POLL_INTERVAL_MIN_US; 83 enp = mcdi->nic; 84 85 do { 86 boolean_t poll_completed; 87 88 poll_completed = (proxy) ? 89 sfc_efx_mcdi_proxy_event_available(mcdi) : 90 efx_mcdi_request_poll(enp); 91 if (poll_completed) 92 return; 93 94 if (delay_total > SFC_EFX_MCDI_WATCHDOG_INTERVAL_US) { 95 if (!proxy) { 96 aborted = efx_mcdi_request_abort(enp); 97 SFC_EFX_ASSERT(aborted); 98 sfc_efx_mcdi_timeout(mcdi); 99 } 100 101 return; 102 } 103 104 rte_delay_us(delay_us); 105 106 delay_total += delay_us; 107 108 /* Exponentially back off the poll frequency */ 109 RTE_BUILD_BUG_ON(SFC_EFX_MCDI_POLL_INTERVAL_MAX_US > 110 UINT_MAX / 2); 111 delay_us *= 2; 112 if (delay_us > SFC_EFX_MCDI_POLL_INTERVAL_MAX_US) 113 delay_us = SFC_EFX_MCDI_POLL_INTERVAL_MAX_US; 114 115 } while (1); 116 } 117 118 static void 119 sfc_efx_mcdi_execute(void *arg, efx_mcdi_req_t *emrp) 120 { 121 struct sfc_efx_mcdi *mcdi = (struct sfc_efx_mcdi *)arg; 122 uint32_t proxy_handle; 123 124 if (mcdi->state == SFC_EFX_MCDI_DEAD) { 125 emrp->emr_rc = ENOEXEC; 126 return; 127 } 128 129 rte_spinlock_lock(&mcdi->lock); 130 131 SFC_EFX_ASSERT(mcdi->state == SFC_EFX_MCDI_INITIALIZED); 132 133 efx_mcdi_request_start(mcdi->nic, emrp, B_FALSE); 134 sfc_efx_mcdi_poll(mcdi, B_FALSE); 135 136 if (efx_mcdi_get_proxy_handle(mcdi->nic, emrp, &proxy_handle) == 0) { 137 /* 138 * Authorization is required for the MCDI request; 139 * wait for an MCDI proxy response event to bring 140 * a non-zero proxy handle (should be the same as 141 * the value obtained above) and operation status 142 */ 143 sfc_efx_mcdi_poll(mcdi, B_TRUE); 144 145 if ((mcdi->proxy_handle != 0) && 146 (mcdi->proxy_handle != proxy_handle)) { 147 sfc_efx_mcdi_err(mcdi, "Unexpected MCDI proxy event"); 148 emrp->emr_rc = EFAULT; 149 } else if (mcdi->proxy_result == 0) { 150 /* 151 * Authorization succeeded; re-issue the original 152 * request and poll for an ordinary MCDI response 153 */ 154 efx_mcdi_request_start(mcdi->nic, emrp, B_FALSE); 155 sfc_efx_mcdi_poll(mcdi, B_FALSE); 156 } else { 157 emrp->emr_rc = mcdi->proxy_result; 158 sfc_efx_mcdi_err(mcdi, 159 "MCDI proxy authorization failed (handle=%08x, result=%d)", 160 proxy_handle, mcdi->proxy_result); 161 } 162 } 163 164 rte_spinlock_unlock(&mcdi->lock); 165 } 166 167 static void 168 sfc_efx_mcdi_ev_cpl(void *arg) 169 { 170 struct sfc_efx_mcdi *mcdi = (struct sfc_efx_mcdi *)arg; 171 172 RTE_SET_USED(mcdi); 173 SFC_EFX_ASSERT(mcdi->state == SFC_EFX_MCDI_INITIALIZED); 174 175 /* MCDI is polled, completions are not expected */ 176 SFC_EFX_ASSERT(0); 177 } 178 179 static void 180 sfc_efx_mcdi_exception(void *arg, efx_mcdi_exception_t eme) 181 { 182 struct sfc_efx_mcdi *mcdi = (struct sfc_efx_mcdi *)arg; 183 184 sfc_efx_mcdi_warn(mcdi, "MC %s", 185 (eme == EFX_MCDI_EXCEPTION_MC_REBOOT) ? "REBOOT" : 186 (eme == EFX_MCDI_EXCEPTION_MC_BADASSERT) ? "BADASSERT" : "UNKNOWN"); 187 188 mcdi->ops->sched_restart(mcdi->ops_cookie); 189 } 190 191 #define SFC_MCDI_LOG_BUF_SIZE 128 192 193 static size_t 194 sfc_efx_mcdi_do_log(const struct sfc_efx_mcdi *mcdi, 195 char *buffer, void *data, size_t data_size, 196 size_t pfxsize, size_t position) 197 { 198 uint32_t *words = data; 199 /* Space separator plus 2 characters per byte */ 200 const size_t word_str_space = 1 + 2 * sizeof(*words); 201 size_t i; 202 203 for (i = 0; i < data_size; i += sizeof(*words)) { 204 if (position + word_str_space >= 205 SFC_MCDI_LOG_BUF_SIZE) { 206 /* Flush at SFC_MCDI_LOG_BUF_SIZE with backslash 207 * at the end which is required by netlogdecode. 208 */ 209 buffer[position] = '\0'; 210 sfc_efx_log_mcdi(mcdi, "%s \\", buffer); 211 /* Preserve prefix for the next log message */ 212 position = pfxsize; 213 } 214 position += snprintf(buffer + position, 215 SFC_MCDI_LOG_BUF_SIZE - position, 216 " %08x", *words); 217 words++; 218 } 219 return position; 220 } 221 222 static void 223 sfc_efx_mcdi_logger(void *arg, efx_log_msg_t type, 224 void *header, size_t header_size, 225 void *data, size_t data_size) 226 { 227 struct sfc_efx_mcdi *mcdi = (struct sfc_efx_mcdi *)arg; 228 char buffer[SFC_MCDI_LOG_BUF_SIZE]; 229 size_t pfxsize; 230 size_t start; 231 232 /* 233 * Unlike the other cases, MCDI logging implies more onerous work 234 * needed to produce a message. If the dynamic log level prevents 235 * the end result from being printed, the CPU time will be wasted. 236 * 237 * To avoid wasting time, the actual level is examined in advance. 238 */ 239 if (rte_log_get_level(mcdi->logtype) < (int)SFC_EFX_LOG_LEVEL_MCDI) 240 return; 241 242 /* The format including prefix added by sfc_efx_log_mcdi() is the 243 * format consumed by the Solarflare netlogdecode tool. 244 */ 245 pfxsize = snprintf(buffer, sizeof(buffer), "MCDI RPC %s:", 246 type == EFX_LOG_MCDI_REQUEST ? "REQ" : 247 type == EFX_LOG_MCDI_RESPONSE ? "RESP" : "???"); 248 start = sfc_efx_mcdi_do_log(mcdi, buffer, header, header_size, 249 pfxsize, pfxsize); 250 start = sfc_efx_mcdi_do_log(mcdi, buffer, data, data_size, 251 pfxsize, start); 252 if (start != pfxsize) { 253 buffer[start] = '\0'; 254 sfc_efx_log_mcdi(mcdi, "%s", buffer); 255 } 256 } 257 258 static void 259 sfc_efx_mcdi_ev_proxy_response(void *arg, uint32_t handle, efx_rc_t result) 260 { 261 struct sfc_efx_mcdi *mcdi = (struct sfc_efx_mcdi *)arg; 262 263 mcdi->proxy_handle = handle; 264 mcdi->proxy_result = result; 265 } 266 267 int 268 sfc_efx_mcdi_init(struct sfc_efx_mcdi *mcdi, 269 uint32_t logtype, const char *log_prefix, efx_nic_t *nic, 270 const struct sfc_efx_mcdi_ops *ops, void *ops_cookie) 271 { 272 size_t max_msg_size; 273 efx_mcdi_transport_t *emtp; 274 int rc; 275 276 if (ops->dma_alloc == NULL || ops->dma_free == NULL || 277 ops->sched_restart == NULL || ops->mgmt_evq_poll == NULL) 278 return EINVAL; 279 280 SFC_EFX_ASSERT(mcdi->state == SFC_EFX_MCDI_UNINITIALIZED); 281 282 rte_spinlock_init(&mcdi->lock); 283 284 mcdi->ops = ops; 285 mcdi->ops_cookie = ops_cookie; 286 mcdi->nic = nic; 287 288 mcdi->state = SFC_EFX_MCDI_INITIALIZED; 289 290 mcdi->logtype = logtype; 291 mcdi->log_prefix = log_prefix; 292 293 max_msg_size = sizeof(uint32_t) + MCDI_CTL_SDU_LEN_MAX_V2; 294 rc = ops->dma_alloc(ops_cookie, "mcdi", max_msg_size, &mcdi->mem); 295 if (rc != 0) 296 goto fail_dma_alloc; 297 298 emtp = &mcdi->transport; 299 emtp->emt_context = mcdi; 300 emtp->emt_dma_mem = &mcdi->mem; 301 emtp->emt_execute = sfc_efx_mcdi_execute; 302 emtp->emt_ev_cpl = sfc_efx_mcdi_ev_cpl; 303 emtp->emt_exception = sfc_efx_mcdi_exception; 304 emtp->emt_logger = sfc_efx_mcdi_logger; 305 emtp->emt_ev_proxy_response = sfc_efx_mcdi_ev_proxy_response; 306 307 sfc_efx_mcdi_info(mcdi, "init MCDI"); 308 rc = efx_mcdi_init(mcdi->nic, emtp); 309 if (rc != 0) 310 goto fail_mcdi_init; 311 312 return 0; 313 314 fail_mcdi_init: 315 memset(emtp, 0, sizeof(*emtp)); 316 ops->dma_free(ops_cookie, &mcdi->mem); 317 318 fail_dma_alloc: 319 mcdi->state = SFC_EFX_MCDI_UNINITIALIZED; 320 return rc; 321 } 322 323 void 324 sfc_efx_mcdi_fini(struct sfc_efx_mcdi *mcdi) 325 { 326 efx_mcdi_transport_t *emtp; 327 328 emtp = &mcdi->transport; 329 330 rte_spinlock_lock(&mcdi->lock); 331 332 SFC_EFX_ASSERT(mcdi->state == SFC_EFX_MCDI_INITIALIZED || 333 mcdi->state == SFC_EFX_MCDI_DEAD); 334 mcdi->state = SFC_EFX_MCDI_UNINITIALIZED; 335 336 sfc_efx_mcdi_info(mcdi, "fini MCDI"); 337 efx_mcdi_fini(mcdi->nic); 338 memset(emtp, 0, sizeof(*emtp)); 339 340 rte_spinlock_unlock(&mcdi->lock); 341 342 mcdi->ops->dma_free(mcdi->ops_cookie, &mcdi->mem); 343 } 344