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