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