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