xref: /dpdk/drivers/net/sfc/sfc_mcdi.c (revision 89f0711f9ddfb5822da9d34f384b92f72a61c4dc)
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 }
310