1 /*- 2 * BSD LICENSE 3 * 4 * Copyright (c) 2016-2017 Solarflare Communications Inc. 5 * All rights reserved. 6 * 7 * This software was jointly developed between OKTET Labs (under contract 8 * for Solarflare) and Solarflare Communications, Inc. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions are met: 12 * 13 * 1. Redistributions of source code must retain the above copyright notice, 14 * this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright notice, 16 * this list of conditions and the following disclaimer in the documentation 17 * and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 26 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 28 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 29 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <rte_cycles.h> 33 34 #include "efx.h" 35 #include "efx_mcdi.h" 36 #include "efx_regs_mcdi.h" 37 38 #include "sfc.h" 39 #include "sfc_log.h" 40 #include "sfc_kvargs.h" 41 #include "sfc_ev.h" 42 43 #define SFC_MCDI_POLL_INTERVAL_MIN_US 10 /* 10us in 1us units */ 44 #define SFC_MCDI_POLL_INTERVAL_MAX_US (US_PER_S / 10) /* 100ms in 1us units */ 45 #define SFC_MCDI_WATCHDOG_INTERVAL_US (10 * US_PER_S) /* 10s in 1us units */ 46 47 static void 48 sfc_mcdi_timeout(struct sfc_adapter *sa) 49 { 50 sfc_warn(sa, "MC TIMEOUT"); 51 52 sfc_panic(sa, "MCDI timeout handling is not implemented\n"); 53 } 54 55 static inline boolean_t 56 sfc_mcdi_proxy_event_available(struct sfc_adapter *sa) 57 { 58 struct sfc_mcdi *mcdi = &sa->mcdi; 59 60 mcdi->proxy_handle = 0; 61 mcdi->proxy_result = ETIMEDOUT; 62 sfc_ev_mgmt_qpoll(sa); 63 if (mcdi->proxy_result != ETIMEDOUT) 64 return B_TRUE; 65 66 return B_FALSE; 67 } 68 69 static void 70 sfc_mcdi_poll(struct sfc_adapter *sa, boolean_t proxy) 71 { 72 efx_nic_t *enp; 73 unsigned int delay_total; 74 unsigned int delay_us; 75 boolean_t aborted __rte_unused; 76 77 delay_total = 0; 78 delay_us = SFC_MCDI_POLL_INTERVAL_MIN_US; 79 enp = sa->nic; 80 81 do { 82 boolean_t poll_completed; 83 84 poll_completed = (proxy) ? sfc_mcdi_proxy_event_available(sa) : 85 efx_mcdi_request_poll(enp); 86 if (poll_completed) 87 return; 88 89 if (delay_total > SFC_MCDI_WATCHDOG_INTERVAL_US) { 90 if (!proxy) { 91 aborted = efx_mcdi_request_abort(enp); 92 SFC_ASSERT(aborted); 93 sfc_mcdi_timeout(sa); 94 } 95 96 return; 97 } 98 99 rte_delay_us(delay_us); 100 101 delay_total += delay_us; 102 103 /* Exponentially back off the poll frequency */ 104 RTE_BUILD_BUG_ON(SFC_MCDI_POLL_INTERVAL_MAX_US > UINT_MAX / 2); 105 delay_us *= 2; 106 if (delay_us > SFC_MCDI_POLL_INTERVAL_MAX_US) 107 delay_us = SFC_MCDI_POLL_INTERVAL_MAX_US; 108 109 } while (1); 110 } 111 112 static void 113 sfc_mcdi_execute(void *arg, efx_mcdi_req_t *emrp) 114 { 115 struct sfc_adapter *sa = (struct sfc_adapter *)arg; 116 struct sfc_mcdi *mcdi = &sa->mcdi; 117 uint32_t proxy_handle; 118 119 rte_spinlock_lock(&mcdi->lock); 120 121 SFC_ASSERT(mcdi->state == SFC_MCDI_INITIALIZED); 122 123 efx_mcdi_request_start(sa->nic, emrp, B_FALSE); 124 sfc_mcdi_poll(sa, B_FALSE); 125 126 if (efx_mcdi_get_proxy_handle(sa->nic, emrp, &proxy_handle) == 0) { 127 /* 128 * Authorization is required for the MCDI request; 129 * wait for an MCDI proxy response event to bring 130 * a non-zero proxy handle (should be the same as 131 * the value obtained above) and operation status 132 */ 133 sfc_mcdi_poll(sa, B_TRUE); 134 135 if ((mcdi->proxy_handle != 0) && 136 (mcdi->proxy_handle != proxy_handle)) { 137 sfc_err(sa, "Unexpected MCDI proxy event"); 138 emrp->emr_rc = EFAULT; 139 } else if (mcdi->proxy_result == 0) { 140 /* 141 * Authorization succeeded; re-issue the original 142 * request and poll for an ordinary MCDI response 143 */ 144 efx_mcdi_request_start(sa->nic, emrp, B_FALSE); 145 sfc_mcdi_poll(sa, B_FALSE); 146 } else { 147 emrp->emr_rc = mcdi->proxy_result; 148 sfc_err(sa, "MCDI proxy authorization failed " 149 "(handle=%08x, result=%d)", 150 proxy_handle, mcdi->proxy_result); 151 } 152 } 153 154 rte_spinlock_unlock(&mcdi->lock); 155 } 156 157 static void 158 sfc_mcdi_ev_cpl(void *arg) 159 { 160 struct sfc_adapter *sa = (struct sfc_adapter *)arg; 161 struct sfc_mcdi *mcdi __rte_unused; 162 163 mcdi = &sa->mcdi; 164 SFC_ASSERT(mcdi->state == SFC_MCDI_INITIALIZED); 165 166 /* MCDI is polled, completions are not expected */ 167 SFC_ASSERT(0); 168 } 169 170 static void 171 sfc_mcdi_exception(void *arg, efx_mcdi_exception_t eme) 172 { 173 struct sfc_adapter *sa = (struct sfc_adapter *)arg; 174 175 sfc_warn(sa, "MC %s", 176 (eme == EFX_MCDI_EXCEPTION_MC_REBOOT) ? "REBOOT" : 177 (eme == EFX_MCDI_EXCEPTION_MC_BADASSERT) ? "BADASSERT" : "UNKNOWN"); 178 179 sfc_schedule_restart(sa); 180 } 181 182 #define SFC_MCDI_LOG_BUF_SIZE 128 183 184 static size_t 185 sfc_mcdi_do_log(const struct sfc_adapter *sa, 186 char *buffer, void *data, size_t data_size, 187 size_t pfxsize, size_t position) 188 { 189 uint32_t *words = data; 190 /* Space separator plus 2 characters per byte */ 191 const size_t word_str_space = 1 + 2 * sizeof(*words); 192 size_t i; 193 194 for (i = 0; i < data_size; i += sizeof(*words)) { 195 if (position + word_str_space >= 196 SFC_MCDI_LOG_BUF_SIZE) { 197 /* Flush at SFC_MCDI_LOG_BUF_SIZE with backslash 198 * at the end which is required by netlogdecode. 199 */ 200 buffer[position] = '\0'; 201 sfc_info(sa, "%s \\", buffer); 202 /* Preserve prefix for the next log message */ 203 position = pfxsize; 204 } 205 position += snprintf(buffer + position, 206 SFC_MCDI_LOG_BUF_SIZE - position, 207 " %08x", *words); 208 words++; 209 } 210 return position; 211 } 212 213 static void 214 sfc_mcdi_logger(void *arg, efx_log_msg_t type, 215 void *header, size_t header_size, 216 void *data, size_t data_size) 217 { 218 struct sfc_adapter *sa = (struct sfc_adapter *)arg; 219 char buffer[SFC_MCDI_LOG_BUF_SIZE]; 220 size_t pfxsize; 221 size_t start; 222 223 if (!sa->mcdi.logging) 224 return; 225 226 /* The format including prefix added by sfc_info() is the format 227 * consumed by the Solarflare netlogdecode tool. 228 */ 229 pfxsize = snprintf(buffer, sizeof(buffer), "MCDI RPC %s:", 230 type == EFX_LOG_MCDI_REQUEST ? "REQ" : 231 type == EFX_LOG_MCDI_RESPONSE ? "RESP" : "???"); 232 start = sfc_mcdi_do_log(sa, buffer, header, header_size, 233 pfxsize, pfxsize); 234 start = sfc_mcdi_do_log(sa, buffer, data, data_size, pfxsize, start); 235 if (start != pfxsize) { 236 buffer[start] = '\0'; 237 sfc_info(sa, "%s", buffer); 238 } 239 } 240 241 static void 242 sfc_mcdi_ev_proxy_response(void *arg, uint32_t handle, efx_rc_t result) 243 { 244 struct sfc_adapter *sa = (struct sfc_adapter *)arg; 245 struct sfc_mcdi *mcdi = &sa->mcdi; 246 247 mcdi->proxy_handle = handle; 248 mcdi->proxy_result = result; 249 } 250 251 int 252 sfc_mcdi_init(struct sfc_adapter *sa) 253 { 254 struct sfc_mcdi *mcdi; 255 size_t max_msg_size; 256 efx_mcdi_transport_t *emtp; 257 int rc; 258 259 sfc_log_init(sa, "entry"); 260 261 mcdi = &sa->mcdi; 262 263 SFC_ASSERT(mcdi->state == SFC_MCDI_UNINITIALIZED); 264 265 rte_spinlock_init(&mcdi->lock); 266 267 mcdi->state = SFC_MCDI_INITIALIZED; 268 269 max_msg_size = sizeof(uint32_t) + MCDI_CTL_SDU_LEN_MAX_V2; 270 rc = sfc_dma_alloc(sa, "mcdi", 0, max_msg_size, sa->socket_id, 271 &mcdi->mem); 272 if (rc != 0) 273 goto fail_dma_alloc; 274 275 /* Convert negative error to positive used in the driver */ 276 rc = sfc_kvargs_process(sa, SFC_KVARG_MCDI_LOGGING, 277 sfc_kvarg_bool_handler, &mcdi->logging); 278 if (rc != 0) 279 goto fail_kvargs_process; 280 281 emtp = &mcdi->transport; 282 emtp->emt_context = sa; 283 emtp->emt_dma_mem = &mcdi->mem; 284 emtp->emt_execute = sfc_mcdi_execute; 285 emtp->emt_ev_cpl = sfc_mcdi_ev_cpl; 286 emtp->emt_exception = sfc_mcdi_exception; 287 emtp->emt_logger = sfc_mcdi_logger; 288 emtp->emt_ev_proxy_response = sfc_mcdi_ev_proxy_response; 289 290 sfc_log_init(sa, "init MCDI"); 291 rc = efx_mcdi_init(sa->nic, emtp); 292 if (rc != 0) 293 goto fail_mcdi_init; 294 295 return 0; 296 297 fail_mcdi_init: 298 memset(emtp, 0, sizeof(*emtp)); 299 300 fail_kvargs_process: 301 sfc_dma_free(sa, &mcdi->mem); 302 303 fail_dma_alloc: 304 mcdi->state = SFC_MCDI_UNINITIALIZED; 305 return rc; 306 } 307 308 void 309 sfc_mcdi_fini(struct sfc_adapter *sa) 310 { 311 struct sfc_mcdi *mcdi; 312 efx_mcdi_transport_t *emtp; 313 314 sfc_log_init(sa, "entry"); 315 316 mcdi = &sa->mcdi; 317 emtp = &mcdi->transport; 318 319 rte_spinlock_lock(&mcdi->lock); 320 321 SFC_ASSERT(mcdi->state == SFC_MCDI_INITIALIZED); 322 mcdi->state = SFC_MCDI_UNINITIALIZED; 323 324 sfc_log_init(sa, "fini MCDI"); 325 efx_mcdi_fini(sa->nic); 326 memset(emtp, 0, sizeof(*emtp)); 327 328 rte_spinlock_unlock(&mcdi->lock); 329 330 sfc_dma_free(sa, &mcdi->mem); 331 } 332