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