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