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