xref: /dpdk/drivers/net/bnxt/bnxt_cpr.c (revision ef9e424aa72477cc5fd35efeb31c923a67383241)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2014-2023 Broadcom
3  * All rights reserved.
4  */
5 
6 #include <rte_malloc.h>
7 #include <rte_alarm.h>
8 #include <rte_cycles.h>
9 
10 #include "bnxt.h"
11 #include "bnxt_hwrm.h"
12 #include "bnxt_ring.h"
13 #include "hsi_struct_def_dpdk.h"
14 #include "tfc_vf2pf_msg.h"
15 
16 void bnxt_wait_for_device_shutdown(struct bnxt *bp)
17 {
18 	uint32_t val, timeout;
19 
20 	/* if HWRM_FUNC_QCAPS_OUTPUT_FLAGS_ERR_RECOVER_RELOAD is set
21 	 * in HWRM_FUNC_QCAPS command, wait for FW_STATUS to set
22 	 * the SHUTDOWN bit in health register
23 	 */
24 	if (!(bp->recovery_info &&
25 	      (bp->fw_cap & BNXT_FW_CAP_ERR_RECOVER_RELOAD)))
26 		return;
27 
28 	/* Driver has to wait for fw_reset_max_msecs or shutdown bit which comes
29 	 * first for FW to collect crash dump.
30 	 */
31 	timeout = bp->fw_reset_max_msecs;
32 
33 	/* Driver has to poll for shutdown bit in fw_status register
34 	 *
35 	 * 1. in case of hot fw upgrade, this bit will be set after all
36 	 *    function drivers unregistered with fw.
37 	 * 2. in case of fw initiated error recovery, this bit will be
38 	 *    set after fw has collected the core dump
39 	 */
40 	do {
41 		val = bnxt_read_fw_status_reg(bp, BNXT_FW_STATUS_REG);
42 		if (val & BNXT_FW_STATUS_SHUTDOWN)
43 			return;
44 
45 		rte_delay_ms(100);
46 		timeout -= 100;
47 	} while (timeout);
48 }
49 
50 static void bnxt_handle_event_error_report(struct bnxt *bp,
51 					   uint32_t data1,
52 					   uint32_t data2)
53 {
54 	switch (BNXT_EVENT_ERROR_REPORT_TYPE(data1)) {
55 	case HWRM_ASYNC_EVENT_CMPL_ERROR_REPORT_BASE_EVENT_DATA1_ERROR_TYPE_PAUSE_STORM:
56 		PMD_DRV_LOG_LINE(WARNING, "Port:%d Pause Storm detected!",
57 			    bp->eth_dev->data->port_id);
58 		break;
59 	case HWRM_ASYNC_EVENT_CMPL_ERROR_REPORT_BASE_EVENT_DATA1_ERROR_TYPE_DUAL_DATA_RATE_NOT_SUPPORTED:
60 		PMD_DRV_LOG_LINE(WARNING, "Port:%d Speed change not supported with dual rate transceivers on this board",
61 			    bp->eth_dev->data->port_id);
62 		break;
63 	default:
64 		PMD_DRV_LOG_LINE(INFO, "FW reported unknown error type data1 %d"
65 			    " data2: %d", data1, data2);
66 		break;
67 	}
68 }
69 
70 void bnxt_handle_vf_cfg_change(void *arg)
71 {
72 	struct bnxt *bp = arg;
73 	struct rte_eth_dev *eth_dev = bp->eth_dev;
74 	int rc;
75 
76 	/* Free and recreate filters with default VLAN */
77 	if (eth_dev->data->dev_started) {
78 		rc = bnxt_dev_stop_op(eth_dev);
79 		if (rc != 0) {
80 			PMD_DRV_LOG_LINE(ERR, "Failed to stop Port:%u", eth_dev->data->port_id);
81 			return;
82 		}
83 
84 		rc = bnxt_dev_start_op(eth_dev);
85 		if (rc != 0)
86 			PMD_DRV_LOG_LINE(ERR, "Failed to start Port:%u", eth_dev->data->port_id);
87 	}
88 }
89 
90 static void
91 bnxt_process_vf_flr(struct bnxt *bp, uint32_t data1)
92 {
93 	uint16_t pfid, vfid;
94 	int rc;
95 
96 	if (!BNXT_TRUFLOW_EN(bp))
97 		return;
98 
99 	pfid = (data1 & HWRM_ASYNC_EVENT_CMPL_VF_FLR_EVENT_DATA1_PF_ID_MASK) >>
100 		HWRM_ASYNC_EVENT_CMPL_VF_FLR_EVENT_DATA1_PF_ID_SFT;
101 	vfid = (data1 & HWRM_ASYNC_EVENT_CMPL_VF_FLR_EVENT_DATA1_VF_ID_MASK) >>
102 		HWRM_ASYNC_EVENT_CMPL_VF_FLR_EVENT_DATA1_VF_ID_SFT;
103 
104 	PMD_DRV_LOG_LINE(INFO, "VF FLR async event received pfid: %u, vfid: %u",
105 			 pfid, vfid);
106 
107 	rc = tfc_tbl_scope_func_reset(&bp->tfcp, vfid);
108 	if (rc != 0)
109 		PMD_DRV_LOG_LINE(ERR, "Failed to reset vf");
110 }
111 
112 /*
113  * Async event handling
114  */
115 void bnxt_handle_async_event(struct bnxt *bp,
116 			     struct cmpl_base *cmp)
117 {
118 	struct hwrm_async_event_cmpl *async_cmp =
119 				(struct hwrm_async_event_cmpl *)cmp;
120 	uint16_t event_id = rte_le_to_cpu_16(async_cmp->event_id);
121 	uint16_t port_id = bp->eth_dev->data->port_id;
122 	struct bnxt_error_recovery_info *info;
123 	uint32_t event_data;
124 	uint32_t data1, data2;
125 	uint32_t status;
126 
127 	data1 = rte_le_to_cpu_32(async_cmp->event_data1);
128 	data2 = rte_le_to_cpu_32(async_cmp->event_data2);
129 
130 	switch (event_id) {
131 	case HWRM_ASYNC_EVENT_CMPL_EVENT_ID_LINK_STATUS_CHANGE:
132 	case HWRM_ASYNC_EVENT_CMPL_EVENT_ID_LINK_SPEED_CHANGE:
133 	case HWRM_ASYNC_EVENT_CMPL_EVENT_ID_LINK_SPEED_CFG_CHANGE:
134 		/* FALLTHROUGH */
135 		bnxt_link_update_op(bp->eth_dev, 0);
136 		rte_eth_dev_callback_process(bp->eth_dev,
137 			RTE_ETH_EVENT_INTR_LSC, NULL);
138 		break;
139 	case HWRM_ASYNC_EVENT_CMPL_EVENT_ID_PF_DRVR_UNLOAD:
140 		PMD_DRV_LOG_LINE(INFO, "Async event: PF driver unloaded");
141 		break;
142 	case HWRM_ASYNC_EVENT_CMPL_EVENT_ID_VF_CFG_CHANGE:
143 		PMD_DRV_LOG_LINE(INFO, "Port %u: VF config change async event", port_id);
144 		PMD_DRV_LOG_LINE(INFO, "event: data1 %#x data2 %#x", data1, data2);
145 		bnxt_hwrm_func_qcfg(bp, NULL);
146 		if (BNXT_VF(bp))
147 			rte_eal_alarm_set(1, bnxt_handle_vf_cfg_change, (void *)bp);
148 		break;
149 	case HWRM_ASYNC_EVENT_CMPL_EVENT_ID_PORT_CONN_NOT_ALLOWED:
150 		PMD_DRV_LOG_LINE(INFO, "Port conn async event");
151 		break;
152 	case HWRM_ASYNC_EVENT_CMPL_EVENT_ID_RESET_NOTIFY:
153 		/*
154 		 * Avoid any rx/tx packet processing during firmware reset
155 		 * operation.
156 		 */
157 		bnxt_stop_rxtx(bp->eth_dev);
158 
159 		/* Ignore reset notify async events when stopping the port */
160 		if (!bp->eth_dev->data->dev_started) {
161 			bp->flags |= BNXT_FLAG_FATAL_ERROR;
162 			return;
163 		}
164 
165 		rte_eth_dev_callback_process(bp->eth_dev,
166 					     RTE_ETH_EVENT_ERR_RECOVERING,
167 					     NULL);
168 
169 		pthread_mutex_lock(&bp->err_recovery_lock);
170 		event_data = data1;
171 		/* timestamp_lo/hi values are in units of 100ms */
172 		bp->fw_reset_max_msecs = async_cmp->timestamp_hi ?
173 			rte_le_to_cpu_16(async_cmp->timestamp_hi) * 100 :
174 			BNXT_MAX_FW_RESET_TIMEOUT;
175 		bp->fw_reset_min_msecs = async_cmp->timestamp_lo ?
176 			async_cmp->timestamp_lo * 100 :
177 			BNXT_MIN_FW_READY_TIMEOUT;
178 		if ((event_data & EVENT_DATA1_REASON_CODE_MASK) ==
179 		    EVENT_DATA1_REASON_CODE_FW_EXCEPTION_FATAL) {
180 			PMD_DRV_LOG_LINE(INFO,
181 				    "Port %u: Firmware fatal reset event received",
182 				    port_id);
183 			bp->flags |= BNXT_FLAG_FATAL_ERROR;
184 		} else {
185 			PMD_DRV_LOG_LINE(INFO,
186 				    "Port %u: Firmware non-fatal reset event received",
187 				    port_id);
188 		}
189 
190 		bp->flags |= BNXT_FLAG_FW_RESET;
191 		pthread_mutex_unlock(&bp->err_recovery_lock);
192 		rte_eal_alarm_set(US_PER_MS, bnxt_dev_reset_and_resume,
193 				  (void *)bp);
194 		break;
195 	case HWRM_ASYNC_EVENT_CMPL_EVENT_ID_ERROR_RECOVERY:
196 		info = bp->recovery_info;
197 
198 		if (!info)
199 			return;
200 
201 		event_data = data1 & EVENT_DATA1_FLAGS_MASK;
202 
203 		if (event_data & EVENT_DATA1_FLAGS_RECOVERY_ENABLED) {
204 			info->flags |= BNXT_FLAG_RECOVERY_ENABLED;
205 		} else {
206 			info->flags &= ~BNXT_FLAG_RECOVERY_ENABLED;
207 			PMD_DRV_LOG_LINE(INFO, "Driver recovery watchdog is disabled");
208 			return;
209 		}
210 
211 		if (event_data & EVENT_DATA1_FLAGS_MASTER_FUNC)
212 			info->flags |= BNXT_FLAG_PRIMARY_FUNC;
213 		else
214 			info->flags &= ~BNXT_FLAG_PRIMARY_FUNC;
215 
216 		status = bnxt_read_fw_status_reg(bp, BNXT_FW_STATUS_REG);
217 		PMD_DRV_LOG_LINE(INFO,
218 			    "Port: %u Driver recovery watchdog, role: %s, FW status: 0x%x (%s)",
219 			    port_id, bnxt_is_primary_func(bp) ? "primary" : "backup", status,
220 			    (status == BNXT_FW_STATUS_HEALTHY) ? "healthy" : "unhealthy");
221 
222 		if (bp->flags & BNXT_FLAG_FW_HEALTH_CHECK_SCHEDULED)
223 			return;
224 
225 		info->last_heart_beat =
226 			bnxt_read_fw_status_reg(bp, BNXT_FW_HEARTBEAT_CNT_REG);
227 		info->last_reset_counter =
228 			bnxt_read_fw_status_reg(bp, BNXT_FW_RECOVERY_CNT_REG);
229 
230 		bnxt_schedule_fw_health_check(bp);
231 		break;
232 	case HWRM_ASYNC_EVENT_CMPL_EVENT_ID_DEBUG_NOTIFICATION:
233 		PMD_DRV_LOG_LINE(INFO, "Port: %u DNC event: data1 %#x data2 %#x",
234 			    port_id, data1, data2);
235 		break;
236 	case HWRM_ASYNC_EVENT_CMPL_EVENT_ID_ECHO_REQUEST:
237 		PMD_DRV_LOG_LINE(INFO,
238 			    "Port %u: Received fw echo request: data1 %#x data2 %#x",
239 			    port_id, data1, data2);
240 		if (bp->recovery_info)
241 			bnxt_hwrm_fw_echo_reply(bp, data1, data2);
242 		break;
243 	case HWRM_ASYNC_EVENT_CMPL_EVENT_ID_ERROR_REPORT:
244 		bnxt_handle_event_error_report(bp, data1, data2);
245 		break;
246 	case HWRM_ASYNC_EVENT_CMPL_EVENT_ID_VF_FLR:
247 		bnxt_process_vf_flr(bp, data1);
248 		break;
249 	case HWRM_ASYNC_EVENT_CMPL_EVENT_ID_RSS_CHANGE:
250 		/* RSS change notification, re-read QCAPS */
251 		PMD_DRV_LOG_LINE(INFO, "Async event: RSS change event [%#x, %#x]",
252 				 data1, data2);
253 		bnxt_hwrm_vnic_qcaps(bp);
254 		break;
255 	default:
256 		PMD_DRV_LOG_LINE(DEBUG, "handle_async_event id = 0x%x", event_id);
257 		break;
258 	}
259 }
260 
261 void bnxt_handle_fwd_req(struct bnxt *bp, struct cmpl_base *cmpl)
262 {
263 	struct hwrm_exec_fwd_resp_input *fwreq;
264 	struct hwrm_fwd_req_cmpl *fwd_cmpl = (struct hwrm_fwd_req_cmpl *)cmpl;
265 	struct input *fwd_cmd;
266 	uint16_t fw_vf_id;
267 	uint16_t vf_id;
268 	uint16_t req_len;
269 	int rc;
270 
271 	if (bp->pf->active_vfs <= 0) {
272 		PMD_DRV_LOG_LINE(ERR, "Forwarded VF with no active VFs");
273 		return;
274 	}
275 
276 	/* Qualify the fwd request */
277 	fw_vf_id = rte_le_to_cpu_16(fwd_cmpl->source_id);
278 	vf_id = fw_vf_id - bp->pf->first_vf_id;
279 
280 	req_len = (rte_le_to_cpu_16(fwd_cmpl->req_len_type) &
281 		   HWRM_FWD_REQ_CMPL_REQ_LEN_MASK) >>
282 		HWRM_FWD_REQ_CMPL_REQ_LEN_SFT;
283 	if (req_len > sizeof(fwreq->encap_request))
284 		req_len = sizeof(fwreq->encap_request);
285 
286 	/* Locate VF's forwarded command */
287 	fwd_cmd = (struct input *)bp->pf->vf_info[vf_id].req_buf;
288 
289 	if (fw_vf_id < bp->pf->first_vf_id ||
290 	    fw_vf_id >= bp->pf->first_vf_id + bp->pf->active_vfs) {
291 		PMD_DRV_LOG_LINE(ERR,
292 		"FWD req's source_id 0x%x out of range 0x%x - 0x%x (%d %d)",
293 			fw_vf_id, bp->pf->first_vf_id,
294 			(bp->pf->first_vf_id) + bp->pf->active_vfs - 1,
295 			bp->pf->first_vf_id, bp->pf->active_vfs);
296 		goto reject;
297 	}
298 
299 	if (bnxt_rcv_msg_from_vf(bp, vf_id, fwd_cmd)) {
300 		/*
301 		 * In older firmware versions, the MAC had to be all zeros for
302 		 * the VF to set it's MAC via hwrm_func_vf_cfg. Set to all
303 		 * zeros if it's being configured and has been ok'd by caller.
304 		 */
305 		if (fwd_cmd->req_type == HWRM_FUNC_VF_CFG) {
306 			struct hwrm_func_vf_cfg_input *vfc = (void *)fwd_cmd;
307 
308 			if (vfc->enables &
309 			    HWRM_FUNC_VF_CFG_INPUT_ENABLES_DFLT_MAC_ADDR) {
310 				bnxt_hwrm_func_vf_mac(bp, vf_id,
311 				(const uint8_t *)"\x00\x00\x00\x00\x00");
312 			}
313 		}
314 
315 		if (fwd_cmd->req_type == HWRM_CFA_L2_SET_RX_MASK) {
316 			struct hwrm_cfa_l2_set_rx_mask_input *srm =
317 							(void *)fwd_cmd;
318 
319 			srm->vlan_tag_tbl_addr = rte_cpu_to_le_64(0);
320 			srm->num_vlan_tags = rte_cpu_to_le_32(0);
321 			srm->mask &= ~rte_cpu_to_le_32(
322 				HWRM_CFA_L2_SET_RX_MASK_INPUT_MASK_VLANONLY |
323 			    HWRM_CFA_L2_SET_RX_MASK_INPUT_MASK_VLAN_NONVLAN |
324 			    HWRM_CFA_L2_SET_RX_MASK_INPUT_MASK_ANYVLAN_NONVLAN);
325 		}
326 
327 		if (fwd_cmd->req_type == HWRM_OEM_CMD) {
328 			struct hwrm_oem_cmd_input *oem_cmd = (void *)fwd_cmd;
329 			struct hwrm_oem_cmd_output oem_out = { 0 };
330 
331 			if (oem_cmd->oem_id == 0x14e4 &&
332 			    oem_cmd->naming_authority
333 				== HWRM_OEM_CMD_INPUT_NAMING_AUTHORITY_PCI_SIG &&
334 			    oem_cmd->message_family
335 				== HWRM_OEM_CMD_INPUT_MESSAGE_FAMILY_TRUFLOW) {
336 				uint32_t resp[18] = { 0 };
337 				uint16_t oem_data_len = sizeof(oem_out.oem_data);
338 				uint16_t resp_len = oem_data_len;
339 
340 				rc = tfc_oem_cmd_process(&bp->tfcp,
341 							 oem_cmd->oem_data,
342 							 resp,
343 							 &resp_len);
344 				if (rc) {
345 					PMD_DRV_LOG_LINE(ERR,
346 						"OEM cmd process error id 0x%x, name 0x%x, family 0x%x",
347 						oem_cmd->oem_id,
348 						oem_cmd->naming_authority,
349 						oem_cmd->message_family);
350 					goto reject;
351 				}
352 
353 				oem_out.error_code = 0;
354 				oem_out.req_type = oem_cmd->req_type;
355 				oem_out.seq_id = oem_cmd->seq_id;
356 				oem_out.resp_len = rte_cpu_to_le_16(sizeof(oem_out));
357 				oem_out.oem_id = oem_cmd->oem_id;
358 				oem_out.naming_authority = oem_cmd->naming_authority;
359 				oem_out.message_family = oem_cmd->message_family;
360 				memcpy(oem_out.oem_data, resp, resp_len);
361 				oem_out.valid = 1;
362 
363 				rc = bnxt_hwrm_fwd_resp(bp, fw_vf_id, &oem_out, oem_out.resp_len,
364 						oem_cmd->resp_addr, oem_cmd->cmpl_ring);
365 				if (rc) {
366 					PMD_DRV_LOG_LINE(ERR,
367 							 "Failed to send HWRM_FWD_RESP VF 0x%x, type",
368 							 fw_vf_id - bp->pf->first_vf_id);
369 				}
370 			} else {
371 				PMD_DRV_LOG_LINE(ERR,
372 						 "Unsupported OEM cmd id 0x%x, name 0x%x, family 0x%x",
373 						 oem_cmd->oem_id, oem_cmd->naming_authority,
374 						 oem_cmd->message_family);
375 				goto reject;
376 			}
377 
378 			return;
379 		}
380 
381 		/* Forward */
382 		rc = bnxt_hwrm_exec_fwd_resp(bp, fw_vf_id, fwd_cmd, req_len);
383 		if (rc) {
384 			PMD_DRV_LOG_LINE(ERR,
385 				"Failed to send FWD req VF 0x%x, type 0x%x.",
386 				fw_vf_id - bp->pf->first_vf_id,
387 				rte_le_to_cpu_16(fwd_cmd->req_type));
388 		}
389 		return;
390 	}
391 
392 reject:
393 	rc = bnxt_hwrm_reject_fwd_resp(bp, fw_vf_id, fwd_cmd, req_len);
394 	if (rc) {
395 		PMD_DRV_LOG_LINE(ERR,
396 			"Failed to send REJECT req VF 0x%x, type 0x%x.",
397 			fw_vf_id - bp->pf->first_vf_id,
398 			rte_le_to_cpu_16(fwd_cmd->req_type));
399 	}
400 
401 	return;
402 }
403 
404 int bnxt_event_hwrm_resp_handler(struct bnxt *bp, struct cmpl_base *cmp)
405 {
406 	bool evt = 0;
407 
408 	if (bp == NULL || cmp == NULL) {
409 		PMD_DRV_LOG_LINE(ERR, "invalid NULL argument");
410 		return evt;
411 	}
412 
413 	if (unlikely(is_bnxt_in_error(bp)))
414 		return 0;
415 
416 	switch (CMP_TYPE(cmp)) {
417 	case CMPL_BASE_TYPE_HWRM_ASYNC_EVENT:
418 		/* Handle any async event */
419 		bnxt_handle_async_event(bp, cmp);
420 		evt = 1;
421 		break;
422 	case CMPL_BASE_TYPE_HWRM_FWD_REQ:
423 		/* Handle HWRM forwarded responses */
424 		bnxt_handle_fwd_req(bp, cmp);
425 		evt = 1;
426 		break;
427 	default:
428 		/* Ignore any other events */
429 		PMD_DRV_LOG_LINE(DEBUG, "Ignoring %02x completion", CMP_TYPE(cmp));
430 		break;
431 	}
432 
433 	return evt;
434 }
435 
436 bool bnxt_is_primary_func(struct bnxt *bp)
437 {
438 	if (bp->recovery_info->flags & BNXT_FLAG_PRIMARY_FUNC)
439 		return true;
440 
441 	return false;
442 }
443 
444 bool bnxt_is_recovery_enabled(struct bnxt *bp)
445 {
446 	struct bnxt_error_recovery_info *info;
447 
448 	info = bp->recovery_info;
449 	if (info && (info->flags & BNXT_FLAG_RECOVERY_ENABLED))
450 		return true;
451 
452 	return false;
453 }
454 
455 void bnxt_stop_rxtx(struct rte_eth_dev *eth_dev)
456 {
457 	eth_dev->rx_pkt_burst = rte_eth_pkt_burst_dummy;
458 	eth_dev->tx_pkt_burst = rte_eth_pkt_burst_dummy;
459 
460 	rte_eth_fp_ops[eth_dev->data->port_id].rx_pkt_burst =
461 		eth_dev->rx_pkt_burst;
462 	rte_eth_fp_ops[eth_dev->data->port_id].tx_pkt_burst =
463 		eth_dev->tx_pkt_burst;
464 	rte_mb();
465 
466 	/* Allow time for threads to exit the real burst functions. */
467 	rte_delay_ms(100);
468 }
469