New upstream version 18.02
[deb_dpdk.git] / drivers / net / sfc / sfc_mcdi.c
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 }