net/sfc: implement MCDI logging callback
[dpdk.git] / drivers / net / sfc / sfc_mcdi.c
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 }