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