History

Merge tag 'mlx5-updates-2019-06-13' of git://git.kernel.org/pub/scm/linux/kernel/git/saeed/linux

Saeed Mahameed says: ==================== mlx5-updates-2019-06-13 Mlx5 devlink health fw reporters and sw reset support This series provides mlx5 firmware reset support and firmware devlink health reporters. 1) Add initial mlx5 kernel documentation and include devlink health reporters 2) Add CR-Space access and FW Crdump snapshot support via devlink region_snapshot 3) Issue software reset upon FW asserts 4) Add fw and fw_fatal devlink heath reporters to follow fw errors indication by dump and recover procedures and enable trigger these functionality by user. 4.1) fw reporter: The fw reporter implements diagnose and dump callbacks. It follows symptoms of fw error such as fw syndrome by triggering fw core dump and storing it and any other fw trace into the dump buffer. The fw reporter diagnose command can be triggered any time by the user to check current fw status. 4.2) fw_fatal repoter: The fw_fatal reporter implements dump and recover callbacks. It follows fatal errors indications by CR-space dump and recover flow. The CR-space dump uses vsc interface which is valid even if the FW command interface is not functional, which is the case in most FW fatal errors. The CR-space dump is stored as a memory region snapshot to ease read by address. The recover function runs recover flow which reloads the driver and triggers fw reset if needed. ====================

David S. Miller committed 7 years ago

Commit b4a6d9a2 parents 363887a 06efeb519 files · +1516 −144Browse files

Unified · Split

19 files
  1. Documentation/networking/device_drivers/index.rst +1 −0
  2. Documentation/networking/device_drivers/mellanox/mlx5.rst +173 −0
  3. MAINTAINERS +1 −0
  4. drivers/net/ethernet/mellanox/mlx5/core/Makefile +2 −1
  5. drivers/net/ethernet/mellanox/mlx5/core/devlink.c +58 −0
  6. drivers/net/ethernet/mellanox/mlx5/core/devlink.h +14 −0
  7. drivers/net/ethernet/mellanox/mlx5/core/diag/crdump.c +115 −0
  8. drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c +139 −0
  9. drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.h +20 −0
  10. drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c +1 −1
  11. drivers/net/ethernet/mellanox/mlx5/core/health.c +496 −73
  12. drivers/net/ethernet/mellanox/mlx5/core/lib/mlx5.h +3 −0
  13. drivers/net/ethernet/mellanox/mlx5/core/lib/pci_vsc.c +316 −0
  14. drivers/net/ethernet/mellanox/mlx5/core/lib/pci_vsc.h +32 −0
  15. drivers/net/ethernet/mellanox/mlx5/core/main.c +21 −44
  16. drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h +7 −1
  17. include/linux/mlx5/device.h +9 −1
  18. include/linux/mlx5/driver.h +10 −3
  19. net/core/devlink.c +98 −20
Documentation/networking/device_drivers/index.rst+1−0
@@ -21,6 +21,7 @@ Contents: · 20 unmodified lines
2121 intel/i40e
2222 intel/iavf
2323 intel/ice
24+ mellanox/mlx5
2425
2526 .. only:: subproject
2627
Documentation/networking/device_drivers/mellanox/mlx5.rst+173−0
@@ -0,0 +1,173 @@
1+.. SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2+
3+=================================================
4+Mellanox ConnectX(R) mlx5 core VPI Network Driver
5+=================================================
6+
7+Copyright (c) 2019, Mellanox Technologies LTD.
8+
9+Contents
10+========
11+
12+- `Enabling the driver and kconfig options`_
13+- `Devlink health reporters`_
14+
15+Enabling the driver and kconfig options
16+================================================
17+
18+| mlx5 core is modular and most of the major mlx5 core driver features can be selected (compiled in/out)
19+| at build time via kernel Kconfig flags.
20+| Basic features, ethernet net device rx/tx offloads and XDP, are available with the most basic flags
21+| CONFIG_MLX5_CORE=y/m and CONFIG_MLX5_CORE_EN=y.
22+| For the list of advanced features please see below.
23+
24+**CONFIG_MLX5_CORE=(y/m/n)** (module mlx5_core.ko)
25+
26+| The driver can be enabled by choosing CONFIG_MLX5_CORE=y/m in kernel config.
27+| This will provide mlx5 core driver for mlx5 ulps to interface with (mlx5e, mlx5_ib).
28+
29+
30+**CONFIG_MLX5_CORE_EN=(y/n)**
31+
32+| Choosing this option will allow basic ethernet netdevice support with all of the standard rx/tx offloads.
33+| mlx5e is the mlx5 ulp driver which provides netdevice kernel interface, when chosen, mlx5e will be
34+| built-in into mlx5_core.ko.
35+
36+
37+**CONFIG_MLX5_EN_ARFS=(y/n)**
38+
39+| Enables Hardware-accelerated receive flow steering (arfs) support, and ntuple filtering.
40+| https://community.mellanox.com/s/article/howto-configure-arfs-on-connectx-4
41+
42+
43+**CONFIG_MLX5_EN_RXNFC=(y/n)**
44+
45+| Enables ethtool receive network flow classification, which allows user defined
46+| flow rules to direct traffic into arbitrary rx queue via ethtool set/get_rxnfc API.
47+
48+
49+**CONFIG_MLX5_CORE_EN_DCB=(y/n)**:
50+
51+| Enables `Data Center Bridging (DCB) Support <https://community.mellanox.com/s/article/howto-auto-config-pfc-and-ets-on-connectx-4-via-lldp-dcbx>`_.
52+
53+
54+**CONFIG_MLX5_MPFS=(y/n)**
55+
56+| Ethernet Multi-Physical Function Switch (MPFS) support in ConnectX NIC.
57+| MPFs is required for when `Multi-Host <http://www.mellanox.com/page/multihost>`_ configuration is enabled to allow passing
58+| user configured unicast MAC addresses to the requesting PF.
59+
60+
61+**CONFIG_MLX5_ESWITCH=(y/n)**
62+
63+| Ethernet SRIOV E-Switch support in ConnectX NIC. E-Switch provides internal SRIOV packet steering
64+| and switching for the enabled VFs and PF in two available modes:
65+| 1) `Legacy SRIOV mode (L2 mac vlan steering based) <https://community.mellanox.com/s/article/howto-configure-sr-iov-for-connectx-4-connectx-5-with-kvm--ethernet-x>`_.
66+| 2) `Switchdev mode (eswitch offloads) <https://www.mellanox.com/related-docs/prod_software/ASAP2_Hardware_Offloading_for_vSwitches_User_Manual_v4.4.pdf>`_.
67+
68+
69+**CONFIG_MLX5_CORE_IPOIB=(y/n)**
70+
71+| IPoIB offloads & acceleration support.
72+| Requires CONFIG_MLX5_CORE_EN to provide an accelerated interface for the rdma
73+| IPoIB ulp netdevice.
74+
75+
76+**CONFIG_MLX5_FPGA=(y/n)**
77+
78+| Build support for the Innova family of network cards by Mellanox Technologies.
79+| Innova network cards are comprised of a ConnectX chip and an FPGA chip on one board.
80+| If you select this option, the mlx5_core driver will include the Innova FPGA core and allow
81+| building sandbox-specific client drivers.
82+
83+
84+**CONFIG_MLX5_EN_IPSEC=(y/n)**
85+
86+| Enables `IPSec XFRM cryptography-offload accelaration <http://www.mellanox.com/related-docs/prod_software/Mellanox_Innova_IPsec_Ethernet_Adapter_Card_User_Manual.pdf>`_.
87+
88+**CONFIG_MLX5_EN_TLS=(y/n)**
89+
90+| TLS cryptography-offload accelaration.
91+
92+
93+**CONFIG_MLX5_INFINIBAND=(y/n/m)** (module mlx5_ib.ko)
94+
95+| Provides low-level InfiniBand/RDMA and `RoCE <https://community.mellanox.com/s/article/recommended-network-configuration-examples-for-roce-deployment>`_ support.
96+
97+
98+**External options** ( Choose if the corresponding mlx5 feature is required )
99+
100+- CONFIG_PTP_1588_CLOCK: When chosen, mlx5 ptp support will be enabled
101+- CONFIG_VXLAN: When chosen, mlx5 vxaln support will be enabled.
102+- CONFIG_MLXFW: When chosen, mlx5 firmware flashing support will be enabled (via devlink and ethtool).
103+
104+
105+Devlink health reporters
106+========================
107+
108+tx reporter
109+-----------
110+The tx reporter is responsible of two error scenarios:
111+
112+- TX timeout
113+ Report on kernel tx timeout detection.
114+ Recover by searching lost interrupts.
115+- TX error completion
116+ Report on error tx completion.
117+ Recover by flushing the TX queue and reset it.
118+
119+TX reporter also support Diagnose callback, on which it provides
120+real time information of its send queues status.
121+
122+User commands examples:
123+
124+- Diagnose send queues status::
125+
126+ $ devlink health diagnose pci/0000:82:00.0 reporter tx
127+
128+- Show number of tx errors indicated, number of recover flows ended successfully,
129+ is autorecover enabled and graceful period from last recover::
130+
131+ $ devlink health show pci/0000:82:00.0 reporter tx
132+
133+fw reporter
134+-----------
135+The fw reporter implements diagnose and dump callbacks.
136+It follows symptoms of fw error such as fw syndrome by triggering
137+fw core dump and storing it into the dump buffer.
138+The fw reporter diagnose command can be triggered any time by the user to check
139+current fw status.
140+
141+User commands examples:
142+
143+- Check fw heath status::
144+
145+ $ devlink health diagnose pci/0000:82:00.0 reporter fw
146+
147+- Read FW core dump if already stored or trigger new one::
148+
149+ $ devlink health dump show pci/0000:82:00.0 reporter fw
150+
151+NOTE: This command can run only on the PF which has fw tracer ownership,
152+running it on other PF or any VF will return "Operation not permitted".
153+
154+fw fatal reporter
155+-----------------
156+The fw fatal reporter implements dump and recover callbacks.
157+It follows fatal errors indications by CR-space dump and recover flow.
158+The CR-space dump uses vsc interface which is valid even if the FW command
159+interface is not functional, which is the case in most FW fatal errors.
160+The recover function runs recover flow which reloads the driver and triggers fw
161+reset if needed.
162+
163+User commands examples:
164+
165+- Run fw recover flow manually::
166+
167+ $ devlink health recover pci/0000:82:00.0 reporter fw_fatal
168+
169+- Read FW CR-space dump if already strored or trigger new one::
170+
171+ $ devlink health dump show pci/0000:82:00.1 reporter fw_fatal
172+
173+NOTE: This command can run only on PF.
MAINTAINERS+1−0
@@ -10108,6 +10108,7 @@ Q: http://patchwork.ozlabs.org/project/netdev/list/ · 10107 unmodified lines
1010810108 S: Supported
1010910109 F: drivers/net/ethernet/mellanox/mlx5/core/
1011010110 F: include/linux/mlx5/
10111+F: Documentation/networking/device_drivers/mellanox/
1011110112
1011210113 MELLANOX MLX5 IB driver
1011310114 M: Leon Romanovsky <leonro@mellanox.com>
drivers/net/ethernet/mellanox/mlx5/core/Makefile+2−1
@@ -15,7 +15,8 @@ mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ · 14 unmodified lines
1515 health.o mcg.o cq.o alloc.o qp.o port.o mr.o pd.o \
1616 transobj.o vport.o sriov.o fs_cmd.o fs_core.o \
1717 fs_counters.o rl.o lag.o dev.o events.o wq.o lib/gid.o \
18− lib/devcom.o diag/fs_tracepoint.o diag/fw_tracer.o
18+ lib/devcom.o lib/pci_vsc.o diag/fs_tracepoint.o \
19+ diag/fw_tracer.o diag/crdump.o devlink.o
1920
2021 #
2122 # Netdev basic
drivers/net/ethernet/mellanox/mlx5/core/devlink.c+58−0
@@ -0,0 +1,58 @@
1+// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2+/* Copyright (c) 2019 Mellanox Technologies */
3+
4+#include <devlink.h>
5+
6+#include "mlx5_core.h"
7+#include "eswitch.h"
8+
9+static int mlx5_devlink_flash_update(struct devlink *devlink,
10+ const char *file_name,
11+ const char *component,
12+ struct netlink_ext_ack *extack)
13+{
14+ struct mlx5_core_dev *dev = devlink_priv(devlink);
15+ const struct firmware *fw;
16+ int err;
17+
18+ if (component)
19+ return -EOPNOTSUPP;
20+
21+ err = request_firmware_direct(&fw, file_name, &dev->pdev->dev);
22+ if (err)
23+ return err;
24+
25+ return mlx5_firmware_flash(dev, fw, extack);
26+}
27+
28+static const struct devlink_ops mlx5_devlink_ops = {
29+#ifdef CONFIG_MLX5_ESWITCH
30+ .eswitch_mode_set = mlx5_devlink_eswitch_mode_set,
31+ .eswitch_mode_get = mlx5_devlink_eswitch_mode_get,
32+ .eswitch_inline_mode_set = mlx5_devlink_eswitch_inline_mode_set,
33+ .eswitch_inline_mode_get = mlx5_devlink_eswitch_inline_mode_get,
34+ .eswitch_encap_mode_set = mlx5_devlink_eswitch_encap_mode_set,
35+ .eswitch_encap_mode_get = mlx5_devlink_eswitch_encap_mode_get,
36+#endif
37+ .flash_update = mlx5_devlink_flash_update,
38+};
39+
40+struct devlink *mlx5_devlink_alloc()
41+{
42+ return devlink_alloc(&mlx5_devlink_ops, sizeof(struct mlx5_core_dev));
43+}
44+
45+void mlx5_devlink_free(struct devlink *devlink)
46+{
47+ devlink_free(devlink);
48+}
49+
50+int mlx5_devlink_register(struct devlink *devlink, struct device *dev)
51+{
52+ return devlink_register(devlink, dev);
53+}
54+
55+void mlx5_devlink_unregister(struct devlink *devlink)
56+{
57+ devlink_unregister(devlink);
58+}
drivers/net/ethernet/mellanox/mlx5/core/devlink.h+14−0
@@ -0,0 +1,14 @@
1+/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
2+/* Copyright (c) 2019, Mellanox Technologies */
3+
4+#ifndef __MLX5_DEVLINK_H__
5+#define __MLX5_DEVLINK_H__
6+
7+#include <net/devlink.h>
8+
9+struct devlink *mlx5_devlink_alloc(void);
10+void mlx5_devlink_free(struct devlink *devlink);
11+int mlx5_devlink_register(struct devlink *devlink, struct device *dev);
12+void mlx5_devlink_unregister(struct devlink *devlink);
13+
14+#endif /* __MLX5_DEVLINK_H__ */
drivers/net/ethernet/mellanox/mlx5/core/diag/crdump.c+115−0
@@ -0,0 +1,115 @@
1+// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2+/* Copyright (c) 2019 Mellanox Technologies */
3+
4+#include <linux/mlx5/driver.h>
5+#include "mlx5_core.h"
6+#include "lib/pci_vsc.h"
7+#include "lib/mlx5.h"
8+
9+#define BAD_ACCESS 0xBADACCE5
10+#define MLX5_PROTECTED_CR_SCAN_CRSPACE 0x7
11+
12+static bool mlx5_crdump_enabled(struct mlx5_core_dev *dev)
13+{
14+ return !!dev->priv.health.crdump_size;
15+}
16+
17+static int mlx5_crdump_fill(struct mlx5_core_dev *dev, u32 *cr_data)
18+{
19+ u32 crdump_size = dev->priv.health.crdump_size;
20+ int i, ret;
21+
22+ for (i = 0; i < (crdump_size / 4); i++)
23+ cr_data[i] = BAD_ACCESS;
24+
25+ ret = mlx5_vsc_gw_read_block_fast(dev, cr_data, crdump_size);
26+ if (ret <= 0) {
27+ if (ret == 0)
28+ return -EIO;
29+ return ret;
30+ }
31+
32+ if (crdump_size != ret) {
33+ mlx5_core_warn(dev, "failed to read full dump, read %d out of %u\n",
34+ ret, crdump_size);
35+ return -EINVAL;
36+ }
37+
38+ return 0;
39+}
40+
41+int mlx5_crdump_collect(struct mlx5_core_dev *dev, u32 *cr_data)
42+{
43+ int ret;
44+
45+ if (!mlx5_crdump_enabled(dev))
46+ return -ENODEV;
47+
48+ ret = mlx5_vsc_gw_lock(dev);
49+ if (ret) {
50+ mlx5_core_warn(dev, "crdump: failed to lock vsc gw err %d\n",
51+ ret);
52+ return ret;
53+ }
54+ /* Verify no other PF is running cr-dump or sw reset */
55+ ret = mlx5_vsc_sem_set_space(dev, MLX5_SEMAPHORE_SW_RESET,
56+ MLX5_VSC_LOCK);
57+ if (ret) {
58+ mlx5_core_warn(dev, "Failed to lock SW reset semaphore\n");
59+ goto unlock_gw;
60+ }
61+
62+ ret = mlx5_vsc_gw_set_space(dev, MLX5_VSC_SPACE_SCAN_CRSPACE, NULL);
63+ if (ret)
64+ goto unlock_sem;
65+
66+ ret = mlx5_crdump_fill(dev, cr_data);
67+
68+unlock_sem:
69+ mlx5_vsc_sem_set_space(dev, MLX5_SEMAPHORE_SW_RESET, MLX5_VSC_UNLOCK);
70+unlock_gw:
71+ mlx5_vsc_gw_unlock(dev);
72+ return ret;
73+}
74+
75+int mlx5_crdump_enable(struct mlx5_core_dev *dev)
76+{
77+ struct mlx5_priv *priv = &dev->priv;
78+ u32 space_size;
79+ int ret;
80+
81+ if (!mlx5_core_is_pf(dev) || !mlx5_vsc_accessible(dev) ||
82+ mlx5_crdump_enabled(dev))
83+ return 0;
84+
85+ ret = mlx5_vsc_gw_lock(dev);
86+ if (ret)
87+ return ret;
88+
89+ /* Check if space is supported and get space size */
90+ ret = mlx5_vsc_gw_set_space(dev, MLX5_VSC_SPACE_SCAN_CRSPACE,
91+ &space_size);
92+ if (ret) {
93+ /* Unlock and mask error since space is not supported */
94+ mlx5_vsc_gw_unlock(dev);
95+ return 0;
96+ }
97+
98+ if (!space_size) {
99+ mlx5_core_warn(dev, "Invalid Crspace size, zero\n");
100+ mlx5_vsc_gw_unlock(dev);
101+ return -EINVAL;
102+ }
103+
104+ ret = mlx5_vsc_gw_unlock(dev);
105+ if (ret)
106+ return ret;
107+
108+ priv->health.crdump_size = space_size;
109+ return 0;
110+}
111+
112+void mlx5_crdump_disable(struct mlx5_core_dev *dev)
113+{
114+ dev->priv.health.crdump_size = 0;
115+}
drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c+139−0
@@ -243,6 +243,19 @@ free_strings_db: · 242 unmodified lines
243243 return -ENOMEM;
244244 }
245245
246+static void
247+mlx5_fw_tracer_init_saved_traces_array(struct mlx5_fw_tracer *tracer)
248+{
249+ tracer->st_arr.saved_traces_index = 0;
250+ mutex_init(&tracer->st_arr.lock);
251+}
252+
253+static void
254+mlx5_fw_tracer_clean_saved_traces_array(struct mlx5_fw_tracer *tracer)
255+{
256+ mutex_destroy(&tracer->st_arr.lock);
257+}
258+
246259 static void mlx5_tracer_read_strings_db(struct work_struct *work)
247260 {
248261 struct mlx5_fw_tracer *tracer = container_of(work, struct mlx5_fw_tracer,
@@ -522,6 +535,24 @@ static void mlx5_fw_tracer_clean_ready_list(struct mlx5_fw_tracer *tracer) · 273 unmodified lines
522535 list_del(&str_frmt->list);
523536 }
524537
538+static void mlx5_fw_tracer_save_trace(struct mlx5_fw_tracer *tracer,
539+ u64 timestamp, bool lost,
540+ u8 event_id, char *msg)
541+{
542+ struct mlx5_fw_trace_data *trace_data;
543+
544+ mutex_lock(&tracer->st_arr.lock);
545+ trace_data = &tracer->st_arr.straces[tracer->st_arr.saved_traces_index];
546+ trace_data->timestamp = timestamp;
547+ trace_data->lost = lost;
548+ trace_data->event_id = event_id;
549+ strncpy(trace_data->msg, msg, TRACE_STR_MSG);
550+
551+ tracer->st_arr.saved_traces_index =
552+ (tracer->st_arr.saved_traces_index + 1) & (SAVED_TRACES_NUM - 1);
553+ mutex_unlock(&tracer->st_arr.lock);
554+}
555+
525556 static void mlx5_tracer_print_trace(struct tracer_string_format *str_frmt,
526557 struct mlx5_core_dev *dev,
527558 u64 trace_timestamp)
@@ -540,6 +571,9 @@ static void mlx5_tracer_print_trace(struct tracer_string_format *str_frmt, · 12 unmodified lines
540571 trace_mlx5_fw(dev->tracer, trace_timestamp, str_frmt->lost,
541572 str_frmt->event_id, tmp);
542573
574+ mlx5_fw_tracer_save_trace(dev->tracer, trace_timestamp,
575+ str_frmt->lost, str_frmt->event_id, tmp);
576+
543577 /* remove it from hash */
544578 mlx5_tracer_clean_message(str_frmt);
545579 }
@@ -786,6 +820,109 @@ static void mlx5_fw_tracer_ownership_change(struct work_struct *work) · 240 unmodified lines
786820 mlx5_fw_tracer_start(tracer);
787821 }
788822
823+static int mlx5_fw_tracer_set_core_dump_reg(struct mlx5_core_dev *dev,
824+ u32 *in, int size_in)
825+{
826+ u32 out[MLX5_ST_SZ_DW(core_dump_reg)] = {};
827+
828+ if (!MLX5_CAP_DEBUG(dev, core_dump_general) &&
829+ !MLX5_CAP_DEBUG(dev, core_dump_qp))
830+ return -EOPNOTSUPP;
831+
832+ return mlx5_core_access_reg(dev, in, size_in, out, sizeof(out),
833+ MLX5_REG_CORE_DUMP, 0, 1);
834+}
835+
836+int mlx5_fw_tracer_trigger_core_dump_general(struct mlx5_core_dev *dev)
837+{
838+ struct mlx5_fw_tracer *tracer = dev->tracer;
839+ u32 in[MLX5_ST_SZ_DW(core_dump_reg)] = {};
840+ int err;
841+
842+ if (!MLX5_CAP_DEBUG(dev, core_dump_general) || !tracer)
843+ return -EOPNOTSUPP;
844+ if (!tracer->owner)
845+ return -EPERM;
846+
847+ MLX5_SET(core_dump_reg, in, core_dump_type, 0x0);
848+
849+ err = mlx5_fw_tracer_set_core_dump_reg(dev, in, sizeof(in));
850+ if (err)
851+ return err;
852+ queue_work(tracer->work_queue, &tracer->handle_traces_work);
853+ flush_workqueue(tracer->work_queue);
854+ return 0;
855+}
856+
857+static int
858+mlx5_devlink_fmsg_fill_trace(struct devlink_fmsg *fmsg,
859+ struct mlx5_fw_trace_data *trace_data)
860+{
861+ int err;
862+
863+ err = devlink_fmsg_obj_nest_start(fmsg);
864+ if (err)
865+ return err;
866+
867+ err = devlink_fmsg_u64_pair_put(fmsg, "timestamp", trace_data->timestamp);
868+ if (err)
869+ return err;
870+
871+ err = devlink_fmsg_bool_pair_put(fmsg, "lost", trace_data->lost);
872+ if (err)
873+ return err;
874+
875+ err = devlink_fmsg_u8_pair_put(fmsg, "event_id", trace_data->event_id);
876+ if (err)
877+ return err;
878+
879+ err = devlink_fmsg_string_pair_put(fmsg, "msg", trace_data->msg);
880+ if (err)
881+ return err;
882+
883+ err = devlink_fmsg_obj_nest_end(fmsg);
884+ if (err)
885+ return err;
886+ return 0;
887+}
888+
889+int mlx5_fw_tracer_get_saved_traces_objects(struct mlx5_fw_tracer *tracer,
890+ struct devlink_fmsg *fmsg)
891+{
892+ struct mlx5_fw_trace_data *straces = tracer->st_arr.straces;
893+ u32 index, start_index, end_index;
894+ u32 saved_traces_index;
895+ int err;
896+
897+ if (!straces[0].timestamp)
898+ return -ENOMSG;
899+
900+ mutex_lock(&tracer->st_arr.lock);
901+ saved_traces_index = tracer->st_arr.saved_traces_index;
902+ if (straces[saved_traces_index].timestamp)
903+ start_index = saved_traces_index;
904+ else
905+ start_index = 0;
906+ end_index = (saved_traces_index - 1) & (SAVED_TRACES_NUM - 1);
907+
908+ err = devlink_fmsg_arr_pair_nest_start(fmsg, "dump fw traces");
909+ if (err)
910+ goto unlock;
911+ index = start_index;
912+ while (index != end_index) {
913+ err = mlx5_devlink_fmsg_fill_trace(fmsg, &straces[index]);
914+ if (err)
915+ goto unlock;
916+
917+ index = (index + 1) & (SAVED_TRACES_NUM - 1);
918+ }
919+
920+ err = devlink_fmsg_arr_pair_nest_end(fmsg);
921+unlock:
922+ mutex_unlock(&tracer->st_arr.lock);
923+ return err;
924+}
925+
789926 /* Create software resources (Buffers, etc ..) */
790927 struct mlx5_fw_tracer *mlx5_fw_tracer_create(struct mlx5_core_dev *dev)
791928 {
@@ -833,6 +970,7 @@ struct mlx5_fw_tracer *mlx5_fw_tracer_create(struct mlx5_core_dev *dev) · 41 unmodified lines
833970 goto free_log_buf;
834971 }
835972
973+ mlx5_fw_tracer_init_saved_traces_array(tracer);
836974 mlx5_core_dbg(dev, "FWTracer: Tracer created\n");
837975
838976 return tracer;
@@ -917,6 +1055,7 @@ void mlx5_fw_tracer_destroy(struct mlx5_fw_tracer *tracer) · 78 unmodified lines
9171055 cancel_work_sync(&tracer->read_fw_strings_work);
9181056 mlx5_fw_tracer_clean_ready_list(tracer);
9191057 mlx5_fw_tracer_clean_print_hash(tracer);
1058+ mlx5_fw_tracer_clean_saved_traces_array(tracer);
9201059 mlx5_fw_tracer_free_strings_db(tracer);
9211060 mlx5_fw_tracer_destroy_log_buf(tracer);
9221061 flush_workqueue(tracer->work_queue);
drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.h+20−0
@@ -46,6 +46,9 @@ · 45 unmodified lines
4646 #define TRACER_BLOCK_SIZE_BYTE 256
4747 #define TRACES_PER_BLOCK 32
4848
49+#define TRACE_STR_MSG 256
50+#define SAVED_TRACES_NUM 8192
51+
4952 #define TRACER_MAX_PARAMS 7
5053 #define MESSAGE_HASH_BITS 6
5154 #define MESSAGE_HASH_SIZE BIT(MESSAGE_HASH_BITS)
@@ -53,6 +56,13 @@ · 1 unmodified line
5356 #define MASK_52_7 (0x1FFFFFFFFFFF80)
5457 #define MASK_6_0 (0x7F)
5558
59+struct mlx5_fw_trace_data {
60+ u64 timestamp;
61+ bool lost;
62+ u8 event_id;
63+ char msg[TRACE_STR_MSG];
64+};
65+
5666 struct mlx5_fw_tracer {
5767 struct mlx5_core_dev *dev;
5868 struct mlx5_nb nb;
@@ -83,6 +93,13 @@ struct mlx5_fw_tracer { · 24 unmodified lines
8393 u32 consumer_index;
8494 } buff;
8595
96+ /* Saved Traces Array */
97+ struct {
98+ struct mlx5_fw_trace_data straces[SAVED_TRACES_NUM];
99+ u32 saved_traces_index;
100+ struct mutex lock; /* Protect st_arr access */
101+ } st_arr;
102+
86103 u64 last_timestamp;
87104 struct work_struct handle_traces_work;
88105 struct hlist_head hash[MESSAGE_HASH_SIZE];
@@ -171,5 +188,8 @@ struct mlx5_fw_tracer *mlx5_fw_tracer_create(struct mlx5_core_dev *dev); · 82 unmodified lines
171188 int mlx5_fw_tracer_init(struct mlx5_fw_tracer *tracer);
172189 void mlx5_fw_tracer_cleanup(struct mlx5_fw_tracer *tracer);
173190 void mlx5_fw_tracer_destroy(struct mlx5_fw_tracer *tracer);
191+int mlx5_fw_tracer_trigger_core_dump_general(struct mlx5_core_dev *dev);
192+int mlx5_fw_tracer_get_saved_traces_objects(struct mlx5_fw_tracer *tracer,
193+ struct devlink_fmsg *fmsg);
174194
175195 #endif
drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c+1−1
@@ -64,7 +64,7 @@ static int mlx5e_test_health_info(struct mlx5e_priv *priv) · 63 unmodified lines
6464 {
6565 struct mlx5_core_health *health = &priv->mdev->priv.health;
6666
67− return health->sick ? 1 : 0;
67+ return health->fatal_error ? 1 : 0;
6868 }
6969
7070 static int mlx5e_test_link_state(struct mlx5e_priv *priv)
drivers/net/ethernet/mellanox/mlx5/core/health.c+496−73
@@ -40,6 +40,8 @@ · 39 unmodified lines
4040 #include "mlx5_core.h"
4141 #include "lib/eq.h"
4242 #include "lib/mlx5.h"
43+#include "lib/pci_vsc.h"
44+#include "diag/fw_tracer.h"
4345
4446 enum {
4547 MLX5_HEALTH_POLL_INTERVAL = 2 * HZ,
@@ -62,12 +64,20 @@ enum { · 16 unmodified lines
6264
6365 enum {
6466 MLX5_DROP_NEW_HEALTH_WORK,
65− MLX5_DROP_NEW_RECOVERY_WORK,
67+};
68+
69+enum {
70+ MLX5_SENSOR_NO_ERR = 0,
71+ MLX5_SENSOR_PCI_COMM_ERR = 1,
72+ MLX5_SENSOR_PCI_ERR = 2,
73+ MLX5_SENSOR_NIC_DISABLED = 3,
74+ MLX5_SENSOR_NIC_SW_RESET = 4,
75+ MLX5_SENSOR_FW_SYND_RFR = 5,
6676 };
6777
6878 u8 mlx5_get_nic_state(struct mlx5_core_dev *dev)
6979 {
70− return (ioread32be(&dev->iseg->cmdq_addr_l_sz) >> 8) & 3;
80+ return (ioread32be(&dev->iseg->cmdq_addr_l_sz) >> 8) & 7;
7181 }
7282
7383 void mlx5_set_nic_state(struct mlx5_core_dev *dev, u8 state)
@@ -80,18 +90,105 @@ void mlx5_set_nic_state(struct mlx5_core_dev *dev, u8 state) · 6 unmodified lines
8090 &dev->iseg->cmdq_addr_l_sz);
8191 }
8292
83−static int in_fatal(struct mlx5_core_dev *dev)
93+static bool sensor_pci_not_working(struct mlx5_core_dev *dev)
8494 {
8595 struct mlx5_core_health *health = &dev->priv.health;
8696 struct health_buffer __iomem *h = health->health;
8797
98+ /* Offline PCI reads return 0xffffffff */
99+ return (ioread32be(&h->fw_ver) == 0xffffffff);
100+}
101+
102+static bool sensor_fw_synd_rfr(struct mlx5_core_dev *dev)
103+{
104+ struct mlx5_core_health *health = &dev->priv.health;
105+ struct health_buffer __iomem *h = health->health;
106+ u32 rfr = ioread32be(&h->rfr) >> MLX5_RFR_OFFSET;
107+ u8 synd = ioread8(&h->synd);
108+
109+ if (rfr && synd)
110+ mlx5_core_dbg(dev, "FW requests reset, synd: %d\n", synd);
111+ return rfr && synd;
112+}
113+
114+static u32 check_fatal_sensors(struct mlx5_core_dev *dev)
115+{
116+ if (sensor_pci_not_working(dev))
117+ return MLX5_SENSOR_PCI_COMM_ERR;
118+ if (pci_channel_offline(dev->pdev))
119+ return MLX5_SENSOR_PCI_ERR;
88120 if (mlx5_get_nic_state(dev) == MLX5_NIC_IFC_DISABLED)
89− return 1;
121+ return MLX5_SENSOR_NIC_DISABLED;
122+ if (mlx5_get_nic_state(dev) == MLX5_NIC_IFC_SW_RESET)
123+ return MLX5_SENSOR_NIC_SW_RESET;
124+ if (sensor_fw_synd_rfr(dev))
125+ return MLX5_SENSOR_FW_SYND_RFR;
90126
91− if (ioread32be(&h->fw_ver) == 0xffffffff)
92− return 1;
127+ return MLX5_SENSOR_NO_ERR;
128+}
93129
94− return 0;
130+static int lock_sem_sw_reset(struct mlx5_core_dev *dev, bool lock)
131+{
132+ enum mlx5_vsc_state state;
133+ int ret;
134+
135+ if (!mlx5_core_is_pf(dev))
136+ return -EBUSY;
137+
138+ /* Try to lock GW access, this stage doesn't return
139+ * EBUSY because locked GW does not mean that other PF
140+ * already started the reset.
141+ */
142+ ret = mlx5_vsc_gw_lock(dev);
143+ if (ret == -EBUSY)
144+ return -EINVAL;
145+ if (ret)
146+ return ret;
147+
148+ state = lock ? MLX5_VSC_LOCK : MLX5_VSC_UNLOCK;
149+ /* At this stage, if the return status == EBUSY, then we know
150+ * for sure that another PF started the reset, so don't allow
151+ * another reset.
152+ */
153+ ret = mlx5_vsc_sem_set_space(dev, MLX5_SEMAPHORE_SW_RESET, state);
154+ if (ret)
155+ mlx5_core_warn(dev, "Failed to lock SW reset semaphore\n");
156+
157+ /* Unlock GW access */
158+ mlx5_vsc_gw_unlock(dev);
159+
160+ return ret;
161+}
162+
163+static bool reset_fw_if_needed(struct mlx5_core_dev *dev)
164+{
165+ bool supported = (ioread32be(&dev->iseg->initializing) >>
166+ MLX5_FW_RESET_SUPPORTED_OFFSET) & 1;
167+ u32 fatal_error;
168+
169+ if (!supported)
170+ return false;
171+
172+ /* The reset only needs to be issued by one PF. The health buffer is
173+ * shared between all functions, and will be cleared during a reset.
174+ * Check again to avoid a redundant 2nd reset. If the fatal erros was
175+ * PCI related a reset won't help.
176+ */
177+ fatal_error = check_fatal_sensors(dev);
178+ if (fatal_error == MLX5_SENSOR_PCI_COMM_ERR ||
179+ fatal_error == MLX5_SENSOR_NIC_DISABLED ||
180+ fatal_error == MLX5_SENSOR_NIC_SW_RESET) {
181+ mlx5_core_warn(dev, "Not issuing FW reset. Either it's already done or won't help.");
182+ return false;
183+ }
184+
185+ mlx5_core_warn(dev, "Issuing FW Reset\n");
186+ /* Write the NIC interface field to initiate the reset, the command
187+ * interface address also resides here, don't overwrite it.
188+ */
189+ mlx5_set_nic_state(dev, MLX5_NIC_IFC_SW_RESET);
190+
191+ return true;
95192 }
96193
97194 void mlx5_enter_error_state(struct mlx5_core_dev *dev, bool force)
@@ -99,14 +196,65 @@ void mlx5_enter_error_state(struct mlx5_core_dev *dev, bool force) · 1 unmodified line
99196 mutex_lock(&dev->intf_state_mutex);
100197 if (dev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR)
101198 goto unlock;
199+ if (dev->state == MLX5_DEVICE_STATE_UNINITIALIZED) {
200+ dev->state = MLX5_DEVICE_STATE_INTERNAL_ERROR;
201+ goto unlock;
202+ }
102203
103− mlx5_core_err(dev, "start\n");
104− if (pci_channel_offline(dev->pdev) || in_fatal(dev) || force) {
204+ if (check_fatal_sensors(dev) || force) {
105205 dev->state = MLX5_DEVICE_STATE_INTERNAL_ERROR;
106206 mlx5_cmd_flush(dev);
107207 }
108208
109209 mlx5_notifier_call_chain(dev->priv.events, MLX5_DEV_EVENT_SYS_ERROR, (void *)1);
210+unlock:
211+ mutex_unlock(&dev->intf_state_mutex);
212+}
213+
214+#define MLX5_CRDUMP_WAIT_MS 60000
215+#define MLX5_FW_RESET_WAIT_MS 1000
216+void mlx5_error_sw_reset(struct mlx5_core_dev *dev)
217+{
218+ unsigned long end, delay_ms = MLX5_FW_RESET_WAIT_MS;
219+ int lock = -EBUSY;
220+
221+ mutex_lock(&dev->intf_state_mutex);
222+ if (dev->state != MLX5_DEVICE_STATE_INTERNAL_ERROR)
223+ goto unlock;
224+
225+ mlx5_core_err(dev, "start\n");
226+
227+ if (check_fatal_sensors(dev) == MLX5_SENSOR_FW_SYND_RFR) {
228+ /* Get cr-dump and reset FW semaphore */
229+ lock = lock_sem_sw_reset(dev, true);
230+
231+ if (lock == -EBUSY) {
232+ delay_ms = MLX5_CRDUMP_WAIT_MS;
233+ goto recover_from_sw_reset;
234+ }
235+ /* Execute SW reset */
236+ reset_fw_if_needed(dev);
237+ }
238+
239+recover_from_sw_reset:
240+ /* Recover from SW reset */
241+ end = jiffies + msecs_to_jiffies(delay_ms);
242+ do {
243+ if (mlx5_get_nic_state(dev) == MLX5_NIC_IFC_DISABLED)
244+ break;
245+
246+ cond_resched();
247+ } while (!time_after(jiffies, end));
248+
249+ if (mlx5_get_nic_state(dev) != MLX5_NIC_IFC_DISABLED) {
250+ dev_err(&dev->pdev->dev, "NIC IFC still %d after %lums.\n",
251+ mlx5_get_nic_state(dev), delay_ms);
252+ }
253+
254+ /* Release FW semaphore if you are the lock owner */
255+ if (!lock)
256+ lock_sem_sw_reset(dev, false);
257+
110258 mlx5_core_err(dev, "end\n");
111259
112260 unlock:
@@ -129,6 +277,20 @@ static void mlx5_handle_bad_state(struct mlx5_core_dev *dev) · 16 unmodified lines
129277 case MLX5_NIC_IFC_NO_DRAM_NIC:
130278 mlx5_core_warn(dev, "Expected to see disabled NIC but it is no dram nic\n");
131279 break;
280+
281+ case MLX5_NIC_IFC_SW_RESET:
282+ /* The IFC mode field is 3 bits, so it will read 0x7 in 2 cases:
283+ * 1. PCI has been disabled (ie. PCI-AER, PF driver unloaded
284+ * and this is a VF), this is not recoverable by SW reset.
285+ * Logging of this is handled elsewhere.
286+ * 2. FW reset has been issued by another function, driver can
287+ * be reloaded to recover after the mode switches to
288+ * MLX5_NIC_IFC_DISABLED.
289+ */
290+ if (dev->priv.health.fatal_error != MLX5_SENSOR_PCI_COMM_ERR)
291+ mlx5_core_warn(dev, "NIC SW reset in progress\n");
292+ break;
293+
132294 default:
133295 mlx5_core_warn(dev, "Expected to see disabled NIC but it is has invalid value %d\n",
134296 nic_interface);
@@ -137,52 +299,32 @@ static void mlx5_handle_bad_state(struct mlx5_core_dev *dev) · 2 unmodified lines
137299 mlx5_disable_device(dev);
138300 }
139301
140−static void health_recover(struct work_struct *work)
141−{
142− struct mlx5_core_health *health;
143− struct delayed_work *dwork;
144− struct mlx5_core_dev *dev;
145− struct mlx5_priv *priv;
146− u8 nic_state;
147−
148− dwork = container_of(work, struct delayed_work, work);
149− health = container_of(dwork, struct mlx5_core_health, recover_work);
150− priv = container_of(health, struct mlx5_priv, health);
151− dev = container_of(priv, struct mlx5_core_dev, priv);
152−
153− nic_state = mlx5_get_nic_state(dev);
154− if (nic_state == MLX5_NIC_IFC_INVALID) {
155− mlx5_core_err(dev, "health recovery flow aborted since the nic state is invalid\n");
156− return;
157− }
158−
159− mlx5_core_err(dev, "starting health recovery flow\n");
160− mlx5_recover_device(dev);
161−}
162−
163302 /* How much time to wait until health resetting the driver (in msecs) */
164−#define MLX5_RECOVERY_DELAY_MSECS 60000
165−static void health_care(struct work_struct *work)
303+#define MLX5_RECOVERY_WAIT_MSECS 60000
304+static int mlx5_health_try_recover(struct mlx5_core_dev *dev)
166305 {
167− unsigned long recover_delay = msecs_to_jiffies(MLX5_RECOVERY_DELAY_MSECS);
168− struct mlx5_core_health *health;
169− struct mlx5_core_dev *dev;
170− struct mlx5_priv *priv;
171− unsigned long flags;
306+ unsigned long end;
172307
173− health = container_of(work, struct mlx5_core_health, work);
174− priv = container_of(health, struct mlx5_priv, health);
175− dev = container_of(priv, struct mlx5_core_dev, priv);
176308 mlx5_core_warn(dev, "handling bad device here\n");
177309 mlx5_handle_bad_state(dev);
310+ end = jiffies + msecs_to_jiffies(MLX5_RECOVERY_WAIT_MSECS);
311+ while (sensor_pci_not_working(dev)) {
312+ if (time_after(jiffies, end)) {
313+ mlx5_core_err(dev,
314+ "health recovery flow aborted, PCI reads still not working\n");
315+ return -EIO;
316+ }
317+ msleep(100);
318+ }
178319
179− spin_lock_irqsave(&health->wq_lock, flags);
180− if (!test_bit(MLX5_DROP_NEW_RECOVERY_WORK, &health->flags))
181− schedule_delayed_work(&health->recover_work, recover_delay);
182− else
183− mlx5_core_err(dev,
184− "new health works are not permitted at this stage\n");
185− spin_unlock_irqrestore(&health->wq_lock, flags);
320+ mlx5_core_err(dev, "starting health recovery flow\n");
321+ mlx5_recover_device(dev);
322+ if (!test_bit(MLX5_INTERFACE_STATE_UP, &dev->intf_state) ||
323+ check_fatal_sensors(dev)) {
324+ mlx5_core_err(dev, "health recovery failed\n");
325+ return -EIO;
326+ }
327+ return 0;
186328 }
187329
188330 static const char *hsynd_str(u8 synd)
@@ -246,6 +388,282 @@ static void print_health_info(struct mlx5_core_dev *dev) · 57 unmodified lines
246388 mlx5_core_err(dev, "raw fw_ver 0x%08x\n", fw);
247389 }
248390
391+static int
392+mlx5_fw_reporter_diagnose(struct devlink_health_reporter *reporter,
393+ struct devlink_fmsg *fmsg)
394+{
395+ struct mlx5_core_dev *dev = devlink_health_reporter_priv(reporter);
396+ struct mlx5_core_health *health = &dev->priv.health;
397+ struct health_buffer __iomem *h = health->health;
398+ u8 synd;
399+ int err;
400+
401+ synd = ioread8(&h->synd);
402+ err = devlink_fmsg_u8_pair_put(fmsg, "Syndrome", synd);
403+ if (err || !synd)
404+ return err;
405+ return devlink_fmsg_string_pair_put(fmsg, "Description", hsynd_str(synd));
406+}
407+
408+struct mlx5_fw_reporter_ctx {
409+ u8 err_synd;
410+ int miss_counter;
411+};
412+
413+static int
414+mlx5_fw_reporter_ctx_pairs_put(struct devlink_fmsg *fmsg,
415+ struct mlx5_fw_reporter_ctx *fw_reporter_ctx)
416+{
417+ int err;
418+
419+ err = devlink_fmsg_u8_pair_put(fmsg, "syndrome",
420+ fw_reporter_ctx->err_synd);
421+ if (err)
422+ return err;
423+ err = devlink_fmsg_u32_pair_put(fmsg, "fw_miss_counter",
424+ fw_reporter_ctx->miss_counter);
425+ if (err)
426+ return err;
427+ return 0;
428+}
429+
430+static int
431+mlx5_fw_reporter_heath_buffer_data_put(struct mlx5_core_dev *dev,
432+ struct devlink_fmsg *fmsg)
433+{
434+ struct mlx5_core_health *health = &dev->priv.health;
435+ struct health_buffer __iomem *h = health->health;
436+ int err;
437+ int i;
438+
439+ if (!ioread8(&h->synd))
440+ return 0;
441+
442+ err = devlink_fmsg_pair_nest_start(fmsg, "health buffer");
443+ if (err)
444+ return err;
445+ err = devlink_fmsg_obj_nest_start(fmsg);
446+ if (err)
447+ return err;
448+ err = devlink_fmsg_arr_pair_nest_start(fmsg, "assert_var");
449+ if (err)
450+ return err;
451+
452+ for (i = 0; i < ARRAY_SIZE(h->assert_var); i++) {
453+ err = devlink_fmsg_u32_put(fmsg, ioread32be(h->assert_var + i));
454+ if (err)
455+ return err;
456+ }
457+ err = devlink_fmsg_arr_pair_nest_end(fmsg);
458+ if (err)
459+ return err;
460+ err = devlink_fmsg_u32_pair_put(fmsg, "assert_exit_ptr",
461+ ioread32be(&h->assert_exit_ptr));
462+ if (err)
463+ return err;
464+ err = devlink_fmsg_u32_pair_put(fmsg, "assert_callra",
465+ ioread32be(&h->assert_callra));
466+ if (err)
467+ return err;
468+ err = devlink_fmsg_u32_pair_put(fmsg, "hw_id", ioread32be(&h->hw_id));
469+ if (err)
470+ return err;
471+ err = devlink_fmsg_u8_pair_put(fmsg, "irisc_index",
472+ ioread8(&h->irisc_index));
473+ if (err)
474+ return err;
475+ err = devlink_fmsg_u8_pair_put(fmsg, "synd", ioread8(&h->synd));
476+ if (err)
477+ return err;
478+ err = devlink_fmsg_u32_pair_put(fmsg, "ext_synd",
479+ ioread16be(&h->ext_synd));
480+ if (err)
481+ return err;
482+ err = devlink_fmsg_u32_pair_put(fmsg, "raw_fw_ver",
483+ ioread32be(&h->fw_ver));
484+ if (err)
485+ return err;
486+ err = devlink_fmsg_obj_nest_end(fmsg);
487+ if (err)
488+ return err;
489+ return devlink_fmsg_pair_nest_end(fmsg);
490+}
491+
492+static int
493+mlx5_fw_reporter_dump(struct devlink_health_reporter *reporter,
494+ struct devlink_fmsg *fmsg, void *priv_ctx)
495+{
496+ struct mlx5_core_dev *dev = devlink_health_reporter_priv(reporter);
497+ int err;
498+
499+ err = mlx5_fw_tracer_trigger_core_dump_general(dev);
500+ if (err)
501+ return err;
502+
503+ if (priv_ctx) {
504+ struct mlx5_fw_reporter_ctx *fw_reporter_ctx = priv_ctx;
505+
506+ err = mlx5_fw_reporter_ctx_pairs_put(fmsg, fw_reporter_ctx);
507+ if (err)
508+ return err;
509+ }
510+
511+ err = mlx5_fw_reporter_heath_buffer_data_put(dev, fmsg);
512+ if (err)
513+ return err;
514+ return mlx5_fw_tracer_get_saved_traces_objects(dev->tracer, fmsg);
515+}
516+
517+static void mlx5_fw_reporter_err_work(struct work_struct *work)
518+{
519+ struct mlx5_fw_reporter_ctx fw_reporter_ctx;
520+ struct mlx5_core_health *health;
521+
522+ health = container_of(work, struct mlx5_core_health, report_work);
523+
524+ if (IS_ERR_OR_NULL(health->fw_reporter))
525+ return;
526+
527+ fw_reporter_ctx.err_synd = health->synd;
528+ fw_reporter_ctx.miss_counter = health->miss_counter;
529+ if (fw_reporter_ctx.err_synd) {
530+ devlink_health_report(health->fw_reporter,
531+ "FW syndrom reported", &fw_reporter_ctx);
532+ return;
533+ }
534+ if (fw_reporter_ctx.miss_counter)
535+ devlink_health_report(health->fw_reporter,
536+ "FW miss counter reported",
537+ &fw_reporter_ctx);
538+}
539+
540+static const struct devlink_health_reporter_ops mlx5_fw_reporter_ops = {
541+ .name = "fw",
542+ .diagnose = mlx5_fw_reporter_diagnose,
543+ .dump = mlx5_fw_reporter_dump,
544+};
545+
546+static int
547+mlx5_fw_fatal_reporter_recover(struct devlink_health_reporter *reporter,
548+ void *priv_ctx)
549+{
550+ struct mlx5_core_dev *dev = devlink_health_reporter_priv(reporter);
551+
552+ return mlx5_health_try_recover(dev);
553+}
554+
555+#define MLX5_CR_DUMP_CHUNK_SIZE 256
556+static int
557+mlx5_fw_fatal_reporter_dump(struct devlink_health_reporter *reporter,
558+ struct devlink_fmsg *fmsg, void *priv_ctx)
559+{
560+ struct mlx5_core_dev *dev = devlink_health_reporter_priv(reporter);
561+ u32 crdump_size = dev->priv.health.crdump_size;
562+ u32 *cr_data;
563+ u32 data_size;
564+ u32 offset;
565+ int err;
566+
567+ if (!mlx5_core_is_pf(dev))
568+ return -EPERM;
569+
570+ cr_data = kvmalloc(crdump_size, GFP_KERNEL);
571+ if (!cr_data)
572+ return -ENOMEM;
573+ err = mlx5_crdump_collect(dev, cr_data);
574+ if (err)
575+ return err;
576+
577+ if (priv_ctx) {
578+ struct mlx5_fw_reporter_ctx *fw_reporter_ctx = priv_ctx;
579+
580+ err = mlx5_fw_reporter_ctx_pairs_put(fmsg, fw_reporter_ctx);
581+ if (err)
582+ goto free_data;
583+ }
584+
585+ err = devlink_fmsg_arr_pair_nest_start(fmsg, "crdump_data");
586+ if (err)
587+ goto free_data;
588+ for (offset = 0; offset < crdump_size; offset += data_size) {
589+ if (crdump_size - offset < MLX5_CR_DUMP_CHUNK_SIZE)
590+ data_size = crdump_size - offset;
591+ else
592+ data_size = MLX5_CR_DUMP_CHUNK_SIZE;
593+ err = devlink_fmsg_binary_put(fmsg, cr_data, data_size);
594+ if (err)
595+ goto free_data;
596+ }
597+ err = devlink_fmsg_arr_pair_nest_end(fmsg);
598+
599+free_data:
600+ kfree(cr_data);
601+ return err;
602+}
603+
604+static void mlx5_fw_fatal_reporter_err_work(struct work_struct *work)
605+{
606+ struct mlx5_fw_reporter_ctx fw_reporter_ctx;
607+ struct mlx5_core_health *health;
608+ struct mlx5_core_dev *dev;
609+ struct mlx5_priv *priv;
610+
611+ health = container_of(work, struct mlx5_core_health, fatal_report_work);
612+ priv = container_of(health, struct mlx5_priv, health);
613+ dev = container_of(priv, struct mlx5_core_dev, priv);
614+
615+ mlx5_enter_error_state(dev, false);
616+ if (IS_ERR_OR_NULL(health->fw_fatal_reporter)) {
617+ if (mlx5_health_try_recover(dev))
618+ mlx5_core_err(dev, "health recovery failed\n");
619+ return;
620+ }
621+ fw_reporter_ctx.err_synd = health->synd;
622+ fw_reporter_ctx.miss_counter = health->miss_counter;
623+ devlink_health_report(health->fw_fatal_reporter,
624+ "FW fatal error reported", &fw_reporter_ctx);
625+}
626+
627+static const struct devlink_health_reporter_ops mlx5_fw_fatal_reporter_ops = {
628+ .name = "fw_fatal",
629+ .recover = mlx5_fw_fatal_reporter_recover,
630+ .dump = mlx5_fw_fatal_reporter_dump,
631+};
632+
633+#define MLX5_REPORTER_FW_GRACEFUL_PERIOD 1200000
634+static void mlx5_fw_reporters_create(struct mlx5_core_dev *dev)
635+{
636+ struct mlx5_core_health *health = &dev->priv.health;
637+ struct devlink *devlink = priv_to_devlink(dev);
638+
639+ health->fw_reporter =
640+ devlink_health_reporter_create(devlink, &mlx5_fw_reporter_ops,
641+ 0, false, dev);
642+ if (IS_ERR(health->fw_reporter))
643+ mlx5_core_warn(dev, "Failed to create fw reporter, err = %ld\n",
644+ PTR_ERR(health->fw_reporter));
645+
646+ health->fw_fatal_reporter =
647+ devlink_health_reporter_create(devlink,
648+ &mlx5_fw_fatal_reporter_ops,
649+ MLX5_REPORTER_FW_GRACEFUL_PERIOD,
650+ true, dev);
651+ if (IS_ERR(health->fw_fatal_reporter))
652+ mlx5_core_warn(dev, "Failed to create fw fatal reporter, err = %ld\n",
653+ PTR_ERR(health->fw_fatal_reporter));
654+}
655+
656+static void mlx5_fw_reporters_destroy(struct mlx5_core_dev *dev)
657+{
658+ struct mlx5_core_health *health = &dev->priv.health;
659+
660+ if (!IS_ERR_OR_NULL(health->fw_reporter))
661+ devlink_health_reporter_destroy(health->fw_reporter);
662+
663+ if (!IS_ERR_OR_NULL(health->fw_fatal_reporter))
664+ devlink_health_reporter_destroy(health->fw_fatal_reporter);
665+}
666+
249667 static unsigned long get_next_poll_jiffies(void)
250668 {
251669 unsigned long next;
@@ -264,7 +682,7 @@ void mlx5_trigger_health_work(struct mlx5_core_dev *dev) · 12 unmodified lines
264682
265683 spin_lock_irqsave(&health->wq_lock, flags);
266684 if (!test_bit(MLX5_DROP_NEW_HEALTH_WORK, &health->flags))
267− queue_work(health->wq, &health->work);
685+ queue_work(health->wq, &health->fatal_report_work);
268686 else
269687 mlx5_core_err(dev, "new health works are not permitted at this stage\n");
270688 spin_unlock_irqrestore(&health->wq_lock, flags);
@@ -274,6 +692,9 @@ static void poll_health(struct timer_list *t) · 3 unmodified lines
274692 {
275693 struct mlx5_core_dev *dev = from_timer(dev, t, priv.health.timer);
276694 struct mlx5_core_health *health = &dev->priv.health;
695+ struct health_buffer __iomem *h = health->health;
696+ u32 fatal_error;
697+ u8 prev_synd;
277698 u32 count;
278699
279700 if (dev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR)
@@ -289,10 +710,19 @@ static void poll_health(struct timer_list *t) · 9 unmodified lines
289710 if (health->miss_counter == MAX_MISSES) {
290711 mlx5_core_err(dev, "device's health compromised - reached miss count\n");
291712 print_health_info(dev);
713+ queue_work(health->wq, &health->report_work);
292714 }
293715
294− if (in_fatal(dev) && !health->sick) {
295− health->sick = true;
716+ prev_synd = health->synd;
717+ health->synd = ioread8(&h->synd);
718+ if (health->synd && health->synd != prev_synd)
719+ queue_work(health->wq, &health->report_work);
720+
721+ fatal_error = check_fatal_sensors(dev);
722+
723+ if (fatal_error && !health->fatal_error) {
724+ mlx5_core_err(dev, "Fatal error %u detected\n", fatal_error);
725+ dev->priv.health.fatal_error = fatal_error;
296726 print_health_info(dev);
297727 mlx5_trigger_health_work(dev);
298728 }
@@ -306,9 +736,8 @@ void mlx5_start_health_poll(struct mlx5_core_dev *dev) · 7 unmodified lines
306736 struct mlx5_core_health *health = &dev->priv.health;
307737
308738 timer_setup(&health->timer, poll_health, 0);
309− health->sick = 0;
739+ health->fatal_error = MLX5_SENSOR_NO_ERR;
310740 clear_bit(MLX5_DROP_NEW_HEALTH_WORK, &health->flags);
311− clear_bit(MLX5_DROP_NEW_RECOVERY_WORK, &health->flags);
312741 health->health = &dev->iseg->health;
313742 health->health_counter = &dev->iseg->health_counter;
314743
@@ -324,7 +753,6 @@ void mlx5_stop_health_poll(struct mlx5_core_dev *dev, bool disable_health) · 9 unmodified lines
324753 if (disable_health) {
325754 spin_lock_irqsave(&health->wq_lock, flags);
326755 set_bit(MLX5_DROP_NEW_HEALTH_WORK, &health->flags);
327− set_bit(MLX5_DROP_NEW_RECOVERY_WORK, &health->flags);
328756 spin_unlock_irqrestore(&health->wq_lock, flags);
329757 }
330758
@@ -338,21 +766,9 @@ void mlx5_drain_health_wq(struct mlx5_core_dev *dev) · 7 unmodified lines
338766
339767 spin_lock_irqsave(&health->wq_lock, flags);
340768 set_bit(MLX5_DROP_NEW_HEALTH_WORK, &health->flags);
341− set_bit(MLX5_DROP_NEW_RECOVERY_WORK, &health->flags);
342769 spin_unlock_irqrestore(&health->wq_lock, flags);
343− cancel_delayed_work_sync(&health->recover_work);
344− cancel_work_sync(&health->work);
345−}
346−
347−void mlx5_drain_health_recovery(struct mlx5_core_dev *dev)
348−{
349− struct mlx5_core_health *health = &dev->priv.health;
350− unsigned long flags;
351−
352− spin_lock_irqsave(&health->wq_lock, flags);
353− set_bit(MLX5_DROP_NEW_RECOVERY_WORK, &health->flags);
354− spin_unlock_irqrestore(&health->wq_lock, flags);
355− cancel_delayed_work_sync(&dev->priv.health.recover_work);
770+ cancel_work_sync(&health->report_work);
771+ cancel_work_sync(&health->fatal_report_work);
356772 }
357773
358774 void mlx5_health_flush(struct mlx5_core_dev *dev)
@@ -367,6 +783,7 @@ void mlx5_health_cleanup(struct mlx5_core_dev *dev) · 8 unmodified lines
367783 struct mlx5_core_health *health = &dev->priv.health;
368784
369785 destroy_workqueue(health->wq);
786+ mlx5_fw_reporters_destroy(dev);
370787 }
371788
372789 int mlx5_health_init(struct mlx5_core_dev *dev)
@@ -374,20 +791,26 @@ int mlx5_health_init(struct mlx5_core_dev *dev) · 1 unmodified line
374791 struct mlx5_core_health *health;
375792 char *name;
376793
794+ mlx5_fw_reporters_create(dev);
795+
377796 health = &dev->priv.health;
378797 name = kmalloc(64, GFP_KERNEL);
379798 if (!name)
380− return -ENOMEM;
799+ goto out_err;
381800
382801 strcpy(name, "mlx5_health");
383802 strcat(name, dev_name(dev->device));
384803 health->wq = create_singlethread_workqueue(name);
385804 kfree(name);
386805 if (!health->wq)
387− return -ENOMEM;
806+ goto out_err;
388807 spin_lock_init(&health->wq_lock);
389− INIT_WORK(&health->work, health_care);
390− INIT_DELAYED_WORK(&health->recover_work, health_recover);
808+ INIT_WORK(&health->fatal_report_work, mlx5_fw_fatal_reporter_err_work);
809+ INIT_WORK(&health->report_work, mlx5_fw_reporter_err_work);
391810
392811 return 0;
812+
813+out_err:
814+ mlx5_fw_reporters_destroy(dev);
815+ return -ENOMEM;
393816 }
drivers/net/ethernet/mellanox/mlx5/core/lib/mlx5.h+3−0
@@ -41,6 +41,9 @@ int mlx5_core_reserve_gids(struct mlx5_core_dev *dev, unsigned int count); · 40 unmodified lines
4141 void mlx5_core_unreserve_gids(struct mlx5_core_dev *dev, unsigned int count);
4242 int mlx5_core_reserved_gid_alloc(struct mlx5_core_dev *dev, int *gid_index);
4343 void mlx5_core_reserved_gid_free(struct mlx5_core_dev *dev, int gid_index);
44+int mlx5_crdump_enable(struct mlx5_core_dev *dev);
45+void mlx5_crdump_disable(struct mlx5_core_dev *dev);
46+int mlx5_crdump_collect(struct mlx5_core_dev *dev, u32 *cr_data);
4447
4548 /* TODO move to lib/events.h */
4649
drivers/net/ethernet/mellanox/mlx5/core/lib/pci_vsc.c+316−0
@@ -0,0 +1,316 @@
1+// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2+/* Copyright (c) 2019 Mellanox Technologies */
3+
4+#include <linux/pci.h>
5+#include "mlx5_core.h"
6+#include "pci_vsc.h"
7+
8+#define MLX5_EXTRACT_C(source, offset, size) \
9+ ((((u32)(source)) >> (offset)) & MLX5_ONES32(size))
10+#define MLX5_EXTRACT(src, start, len) \
11+ (((len) == 32) ? (src) : MLX5_EXTRACT_C(src, start, len))
12+#define MLX5_ONES32(size) \
13+ ((size) ? (0xffffffff >> (32 - (size))) : 0)
14+#define MLX5_MASK32(offset, size) \
15+ (MLX5_ONES32(size) << (offset))
16+#define MLX5_MERGE_C(rsrc1, rsrc2, start, len) \
17+ ((((rsrc2) << (start)) & (MLX5_MASK32((start), (len)))) | \
18+ ((rsrc1) & (~MLX5_MASK32((start), (len)))))
19+#define MLX5_MERGE(rsrc1, rsrc2, start, len) \
20+ (((len) == 32) ? (rsrc2) : MLX5_MERGE_C(rsrc1, rsrc2, start, len))
21+#define vsc_read(dev, offset, val) \
22+ pci_read_config_dword((dev)->pdev, (dev)->vsc_addr + (offset), (val))
23+#define vsc_write(dev, offset, val) \
24+ pci_write_config_dword((dev)->pdev, (dev)->vsc_addr + (offset), (val))
25+#define VSC_MAX_RETRIES 2048
26+
27+enum {
28+ VSC_CTRL_OFFSET = 0x4,
29+ VSC_COUNTER_OFFSET = 0x8,
30+ VSC_SEMAPHORE_OFFSET = 0xc,
31+ VSC_ADDR_OFFSET = 0x10,
32+ VSC_DATA_OFFSET = 0x14,
33+
34+ VSC_FLAG_BIT_OFFS = 31,
35+ VSC_FLAG_BIT_LEN = 1,
36+
37+ VSC_SYND_BIT_OFFS = 30,
38+ VSC_SYND_BIT_LEN = 1,
39+
40+ VSC_ADDR_BIT_OFFS = 0,
41+ VSC_ADDR_BIT_LEN = 30,
42+
43+ VSC_SPACE_BIT_OFFS = 0,
44+ VSC_SPACE_BIT_LEN = 16,
45+
46+ VSC_SIZE_VLD_BIT_OFFS = 28,
47+ VSC_SIZE_VLD_BIT_LEN = 1,
48+
49+ VSC_STATUS_BIT_OFFS = 29,
50+ VSC_STATUS_BIT_LEN = 3,
51+};
52+
53+void mlx5_pci_vsc_init(struct mlx5_core_dev *dev)
54+{
55+ if (!mlx5_core_is_pf(dev))
56+ return;
57+
58+ dev->vsc_addr = pci_find_capability(dev->pdev,
59+ PCI_CAP_ID_VNDR);
60+ if (!dev->vsc_addr)
61+ mlx5_core_warn(dev, "Failed to get valid vendor specific ID\n");
62+}
63+
64+int mlx5_vsc_gw_lock(struct mlx5_core_dev *dev)
65+{
66+ u32 counter = 0;
67+ int retries = 0;
68+ u32 lock_val;
69+ int ret;
70+
71+ pci_cfg_access_lock(dev->pdev);
72+ do {
73+ if (retries > VSC_MAX_RETRIES) {
74+ ret = -EBUSY;
75+ goto pci_unlock;
76+ }
77+
78+ /* Check if semaphore is already locked */
79+ ret = vsc_read(dev, VSC_SEMAPHORE_OFFSET, &lock_val);
80+ if (ret)
81+ goto pci_unlock;
82+
83+ if (lock_val) {
84+ retries++;
85+ usleep_range(1000, 2000);
86+ continue;
87+ }
88+
89+ /* Read and write counter value, if written value is
90+ * the same, semaphore was acquired successfully.
91+ */
92+ ret = vsc_read(dev, VSC_COUNTER_OFFSET, &counter);
93+ if (ret)
94+ goto pci_unlock;
95+
96+ ret = vsc_write(dev, VSC_SEMAPHORE_OFFSET, counter);
97+ if (ret)
98+ goto pci_unlock;
99+
100+ ret = vsc_read(dev, VSC_SEMAPHORE_OFFSET, &lock_val);
101+ if (ret)
102+ goto pci_unlock;
103+
104+ retries++;
105+ } while (counter != lock_val);
106+
107+ return 0;
108+
109+pci_unlock:
110+ pci_cfg_access_unlock(dev->pdev);
111+ return ret;
112+}
113+
114+int mlx5_vsc_gw_unlock(struct mlx5_core_dev *dev)
115+{
116+ int ret;
117+
118+ ret = vsc_write(dev, VSC_SEMAPHORE_OFFSET, MLX5_VSC_UNLOCK);
119+ pci_cfg_access_unlock(dev->pdev);
120+ return ret;
121+}
122+
123+int mlx5_vsc_gw_set_space(struct mlx5_core_dev *dev, u16 space,
124+ u32 *ret_space_size)
125+{
126+ int ret;
127+ u32 val = 0;
128+
129+ if (!mlx5_vsc_accessible(dev))
130+ return -EINVAL;
131+
132+ if (ret_space_size)
133+ *ret_space_size = 0;
134+
135+ /* Get a unique val */
136+ ret = vsc_read(dev, VSC_CTRL_OFFSET, &val);
137+ if (ret)
138+ goto out;
139+
140+ /* Try to modify the lock */
141+ val = MLX5_MERGE(val, space, VSC_SPACE_BIT_OFFS, VSC_SPACE_BIT_LEN);
142+ ret = vsc_write(dev, VSC_CTRL_OFFSET, val);
143+ if (ret)
144+ goto out;
145+
146+ /* Verify lock was modified */
147+ ret = vsc_read(dev, VSC_CTRL_OFFSET, &val);
148+ if (ret)
149+ goto out;
150+
151+ if (MLX5_EXTRACT(val, VSC_STATUS_BIT_OFFS, VSC_STATUS_BIT_LEN) == 0)
152+ return -EINVAL;
153+
154+ /* Get space max address if indicated by size valid bit */
155+ if (ret_space_size &&
156+ MLX5_EXTRACT(val, VSC_SIZE_VLD_BIT_OFFS, VSC_SIZE_VLD_BIT_LEN)) {
157+ ret = vsc_read(dev, VSC_ADDR_OFFSET, &val);
158+ if (ret) {
159+ mlx5_core_warn(dev, "Failed to get max space size\n");
160+ goto out;
161+ }
162+ *ret_space_size = MLX5_EXTRACT(val, VSC_ADDR_BIT_OFFS,
163+ VSC_ADDR_BIT_LEN);
164+ }
165+ return 0;
166+
167+out:
168+ return ret;
169+}
170+
171+static int mlx5_vsc_wait_on_flag(struct mlx5_core_dev *dev, u8 expected_val)
172+{
173+ int retries = 0;
174+ u32 flag;
175+ int ret;
176+
177+ do {
178+ if (retries > VSC_MAX_RETRIES)
179+ return -EBUSY;
180+
181+ ret = vsc_read(dev, VSC_ADDR_OFFSET, &flag);
182+ if (ret)
183+ return ret;
184+ flag = MLX5_EXTRACT(flag, VSC_FLAG_BIT_OFFS, VSC_FLAG_BIT_LEN);
185+ retries++;
186+
187+ if ((retries & 0xf) == 0)
188+ usleep_range(1000, 2000);
189+
190+ } while (flag != expected_val);
191+
192+ return 0;
193+}
194+
195+static int mlx5_vsc_gw_write(struct mlx5_core_dev *dev, unsigned int address,
196+ u32 data)
197+{
198+ int ret;
199+
200+ if (MLX5_EXTRACT(address, VSC_SYND_BIT_OFFS,
201+ VSC_FLAG_BIT_LEN + VSC_SYND_BIT_LEN))
202+ return -EINVAL;
203+
204+ /* Set flag to 0x1 */
205+ address = MLX5_MERGE(address, 1, VSC_FLAG_BIT_OFFS, 1);
206+ ret = vsc_write(dev, VSC_DATA_OFFSET, data);
207+ if (ret)
208+ goto out;
209+
210+ ret = vsc_write(dev, VSC_ADDR_OFFSET, address);
211+ if (ret)
212+ goto out;
213+
214+ /* Wait for the flag to be cleared */
215+ ret = mlx5_vsc_wait_on_flag(dev, 0);
216+
217+out:
218+ return ret;
219+}
220+
221+static int mlx5_vsc_gw_read(struct mlx5_core_dev *dev, unsigned int address,
222+ u32 *data)
223+{
224+ int ret;
225+
226+ if (MLX5_EXTRACT(address, VSC_SYND_BIT_OFFS,
227+ VSC_FLAG_BIT_LEN + VSC_SYND_BIT_LEN))
228+ return -EINVAL;
229+
230+ ret = vsc_write(dev, VSC_ADDR_OFFSET, address);
231+ if (ret)
232+ goto out;
233+
234+ ret = mlx5_vsc_wait_on_flag(dev, 1);
235+ if (ret)
236+ goto out;
237+
238+ ret = vsc_read(dev, VSC_DATA_OFFSET, data);
239+out:
240+ return ret;
241+}
242+
243+static int mlx5_vsc_gw_read_fast(struct mlx5_core_dev *dev,
244+ unsigned int read_addr,
245+ unsigned int *next_read_addr,
246+ u32 *data)
247+{
248+ int ret;
249+
250+ ret = mlx5_vsc_gw_read(dev, read_addr, data);
251+ if (ret)
252+ goto out;
253+
254+ ret = vsc_read(dev, VSC_ADDR_OFFSET, next_read_addr);
255+ if (ret)
256+ goto out;
257+
258+ *next_read_addr = MLX5_EXTRACT(*next_read_addr, VSC_ADDR_BIT_OFFS,
259+ VSC_ADDR_BIT_LEN);
260+
261+ if (*next_read_addr <= read_addr)
262+ ret = -EINVAL;
263+out:
264+ return ret;
265+}
266+
267+int mlx5_vsc_gw_read_block_fast(struct mlx5_core_dev *dev, u32 *data,
268+ int length)
269+{
270+ unsigned int next_read_addr = 0;
271+ unsigned int read_addr = 0;
272+
273+ while (read_addr < length) {
274+ if (mlx5_vsc_gw_read_fast(dev, read_addr, &next_read_addr,
275+ &data[(read_addr >> 2)]))
276+ return read_addr;
277+
278+ read_addr = next_read_addr;
279+ }
280+ return length;
281+}
282+
283+int mlx5_vsc_sem_set_space(struct mlx5_core_dev *dev, u16 space,
284+ enum mlx5_vsc_state state)
285+{
286+ u32 data, id = 0;
287+ int ret;
288+
289+ ret = mlx5_vsc_gw_set_space(dev, MLX5_SEMAPHORE_SPACE_DOMAIN, NULL);
290+ if (ret) {
291+ mlx5_core_warn(dev, "Failed to set gw space %d\n", ret);
292+ return ret;
293+ }
294+
295+ if (state == MLX5_VSC_LOCK) {
296+ /* Get a unique ID based on the counter */
297+ ret = vsc_read(dev, VSC_COUNTER_OFFSET, &id);
298+ if (ret)
299+ return ret;
300+ }
301+
302+ /* Try to modify lock */
303+ ret = mlx5_vsc_gw_write(dev, space, id);
304+ if (ret)
305+ return ret;
306+
307+ /* Verify lock was modified */
308+ ret = mlx5_vsc_gw_read(dev, space, &data);
309+ if (ret)
310+ return -EINVAL;
311+
312+ if (data != id)
313+ return -EBUSY;
314+
315+ return 0;
316+}
drivers/net/ethernet/mellanox/mlx5/core/lib/pci_vsc.h+32−0
@@ -0,0 +1,32 @@
1+/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
2+/* Copyright (c) 2019 Mellanox Technologies */
3+
4+#ifndef __MLX5_PCI_VSC_H__
5+#define __MLX5_PCI_VSC_H__
6+
7+enum mlx5_vsc_state {
8+ MLX5_VSC_UNLOCK,
9+ MLX5_VSC_LOCK,
10+};
11+
12+enum {
13+ MLX5_VSC_SPACE_SCAN_CRSPACE = 0x7,
14+};
15+
16+void mlx5_pci_vsc_init(struct mlx5_core_dev *dev);
17+int mlx5_vsc_gw_lock(struct mlx5_core_dev *dev);
18+int mlx5_vsc_gw_unlock(struct mlx5_core_dev *dev);
19+int mlx5_vsc_gw_set_space(struct mlx5_core_dev *dev, u16 space,
20+ u32 *ret_space_size);
21+int mlx5_vsc_gw_read_block_fast(struct mlx5_core_dev *dev, u32 *data,
22+ int length);
23+
24+static inline bool mlx5_vsc_accessible(struct mlx5_core_dev *dev)
25+{
26+ return !!dev->vsc_addr;
27+}
28+
29+int mlx5_vsc_sem_set_space(struct mlx5_core_dev *dev, u16 space,
30+ enum mlx5_vsc_state state);
31+
32+#endif /* __MLX5_PCI_VSC_H__ */
drivers/net/ethernet/mellanox/mlx5/core/main.c+21−44
@@ -56,6 +56,7 @@ · 55 unmodified lines
5656 #include "fs_core.h"
5757 #include "lib/mpfs.h"
5858 #include "eswitch.h"
59+#include "devlink.h"
5960 #include "lib/mlx5.h"
6061 #include "fpga/core.h"
6162 #include "fpga/ipsec.h"
@@ -65,6 +66,7 @@ · 3 unmodified lines
6566 #include "lib/vxlan.h"
6667 #include "lib/geneve.h"
6768 #include "lib/devcom.h"
69+#include "lib/pci_vsc.h"
6870 #include "diag/fw_tracer.h"
6971 #include "ecpf.h"
7072
@@ -762,6 +764,8 @@ static int mlx5_pci_init(struct mlx5_core_dev *dev, struct pci_dev *pdev, · 691 unmodified lines
762764 goto err_clr_master;
763765 }
764766
767+ mlx5_pci_vsc_init(dev);
768+
765769 return 0;
766770
767771 err_clr_master:
@@ -1187,7 +1191,7 @@ static int mlx5_unload_one(struct mlx5_core_dev *dev, bool cleanup) · 419 unmodified lines
11871191 int err = 0;
11881192
11891193 if (cleanup)
1190− mlx5_drain_health_recovery(dev);
1194+ mlx5_drain_health_wq(dev);
11911195
11921196 mutex_lock(&dev->intf_state_mutex);
11931197 if (!test_bit(MLX5_INTERFACE_STATE_UP, &dev->intf_state)) {
@@ -1214,37 +1218,6 @@ out: · 20 unmodified lines
12141218 return err;
12151219 }
12161220
1217−static int mlx5_devlink_flash_update(struct devlink *devlink,
1218− const char *file_name,
1219− const char *component,
1220− struct netlink_ext_ack *extack)
1221−{
1222− struct mlx5_core_dev *dev = devlink_priv(devlink);
1223− const struct firmware *fw;
1224− int err;
1225−
1226− if (component)
1227− return -EOPNOTSUPP;
1228−
1229− err = request_firmware_direct(&fw, file_name, &dev->pdev->dev);
1230− if (err)
1231− return err;
1232−
1233− return mlx5_firmware_flash(dev, fw, extack);
1234−}
1235−
1236−static const struct devlink_ops mlx5_devlink_ops = {
1237−#ifdef CONFIG_MLX5_ESWITCH
1238− .eswitch_mode_set = mlx5_devlink_eswitch_mode_set,
1239− .eswitch_mode_get = mlx5_devlink_eswitch_mode_get,
1240− .eswitch_inline_mode_set = mlx5_devlink_eswitch_inline_mode_set,
1241− .eswitch_inline_mode_get = mlx5_devlink_eswitch_inline_mode_get,
1242− .eswitch_encap_mode_set = mlx5_devlink_eswitch_encap_mode_set,
1243− .eswitch_encap_mode_get = mlx5_devlink_eswitch_encap_mode_get,
1244−#endif
1245− .flash_update = mlx5_devlink_flash_update,
1246−};
1247−
12481221 static int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx)
12491222 {
12501223 struct mlx5_priv *priv = &dev->priv;
@@ -1306,9 +1279,9 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *id) · 55 unmodified lines
13061279 struct devlink *devlink;
13071280 int err;
13081281
1309− devlink = devlink_alloc(&mlx5_devlink_ops, sizeof(*dev));
1282+ devlink = mlx5_devlink_alloc();
13101283 if (!devlink) {
1311− dev_err(&pdev->dev, "kzalloc failed\n");
1284+ dev_err(&pdev->dev, "devlink alloc failed\n");
13121285 return -ENOMEM;
13131286 }
13141287
@@ -1336,10 +1309,14 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *id) · 21 unmodified lines
13361309
13371310 request_module_nowait(MLX5_IB_MOD);
13381311
1339− err = devlink_register(devlink, &pdev->dev);
1312+ err = mlx5_devlink_register(devlink, &pdev->dev);
13401313 if (err)
13411314 goto clean_load;
13421315
1316+ err = mlx5_crdump_enable(dev);
1317+ if (err)
1318+ dev_err(&pdev->dev, "mlx5_crdump_enable failed with error code %d\n", err);
1319+
13431320 pci_save_state(pdev);
13441321 return 0;
13451322
@@ -1351,7 +1328,7 @@ err_load_one: · 5 unmodified lines
13511328 pci_init_err:
13521329 mlx5_mdev_uninit(dev);
13531330 mdev_init_err:
1354− devlink_free(devlink);
1331+ mlx5_devlink_free(devlink);
13551332
13561333 return err;
13571334 }
@@ -1361,7 +1338,8 @@ static void remove_one(struct pci_dev *pdev) · 3 unmodified lines
13611338 struct mlx5_core_dev *dev = pci_get_drvdata(pdev);
13621339 struct devlink *devlink = priv_to_devlink(dev);
13631340
1364− devlink_unregister(devlink);
1341+ mlx5_crdump_disable(dev);
1342+ mlx5_devlink_unregister(devlink);
13651343 mlx5_unregister_device(dev);
13661344
13671345 if (mlx5_unload_one(dev, true)) {
@@ -1372,7 +1350,7 @@ static void remove_one(struct pci_dev *pdev) · 4 unmodified lines
13721350
13731351 mlx5_pci_close(dev);
13741352 mlx5_mdev_uninit(dev);
1375− devlink_free(devlink);
1353+ mlx5_devlink_free(devlink);
13761354 }
13771355
13781356 static pci_ers_result_t mlx5_pci_err_detected(struct pci_dev *pdev,
@@ -1383,12 +1361,10 @@ static pci_ers_result_t mlx5_pci_err_detected(struct pci_dev *pdev, · 4 unmodified lines
13831361 mlx5_core_info(dev, "%s was called\n", __func__);
13841362
13851363 mlx5_enter_error_state(dev, false);
1364+ mlx5_error_sw_reset(dev);
13861365 mlx5_unload_one(dev, false);
1387− /* In case of kernel call drain the health wq */
1388− if (state) {
1389− mlx5_drain_health_wq(dev);
1390− mlx5_pci_disable_device(dev);
1391− }
1366+ mlx5_drain_health_wq(dev);
1367+ mlx5_pci_disable_device(dev);
13921368
13931369 return state == pci_channel_io_perm_failure ?
13941370 PCI_ERS_RESULT_DISCONNECT : PCI_ERS_RESULT_NEED_RESET;
@@ -1556,7 +1532,8 @@ MODULE_DEVICE_TABLE(pci, mlx5_core_pci_table); · 161 unmodified lines
15561532
15571533 void mlx5_disable_device(struct mlx5_core_dev *dev)
15581534 {
1559− mlx5_pci_err_detected(dev->pdev, 0);
1535+ mlx5_error_sw_reset(dev);
1536+ mlx5_unload_one(dev, false);
15601537 }
15611538
15621539 void mlx5_recover_device(struct mlx5_core_dev *dev)
drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h+7−1
@@ -111,6 +111,11 @@ enum { · 110 unmodified lines
111111 MLX5_DRIVER_SYND = 0xbadd00de,
112112 };
113113
114+enum mlx5_semaphore_space_address {
115+ MLX5_SEMAPHORE_SPACE_DOMAIN = 0xA,
116+ MLX5_SEMAPHORE_SW_RESET = 0x20,
117+};
118+
114119 int mlx5_query_hca_caps(struct mlx5_core_dev *dev);
115120 int mlx5_query_board_id(struct mlx5_core_dev *dev);
116121 int mlx5_cmd_init_hca(struct mlx5_core_dev *dev, uint32_t *sw_owner_id);
@@ -118,6 +123,7 @@ int mlx5_cmd_teardown_hca(struct mlx5_core_dev *dev); · 1 unmodified line
118123 int mlx5_cmd_force_teardown_hca(struct mlx5_core_dev *dev);
119124 int mlx5_cmd_fast_teardown_hca(struct mlx5_core_dev *dev);
120125 void mlx5_enter_error_state(struct mlx5_core_dev *dev, bool force);
126+void mlx5_error_sw_reset(struct mlx5_core_dev *dev);
121127 void mlx5_disable_device(struct mlx5_core_dev *dev);
122128 void mlx5_recover_device(struct mlx5_core_dev *dev);
123129 int mlx5_sriov_init(struct mlx5_core_dev *dev);
@@ -214,7 +220,7 @@ enum { · 90 unmodified lines
214220 MLX5_NIC_IFC_FULL = 0,
215221 MLX5_NIC_IFC_DISABLED = 1,
216222 MLX5_NIC_IFC_NO_DRAM_NIC = 2,
217− MLX5_NIC_IFC_INVALID = 3
223+ MLX5_NIC_IFC_SW_RESET = 7
218224 };
219225
220226 u8 mlx5_get_nic_state(struct mlx5_core_dev *dev);
include/linux/mlx5/device.h+9−1
@@ -510,6 +510,10 @@ struct mlx5_cmd_layout { · 509 unmodified lines
510510 u8 status_own;
511511 };
512512
513+enum mlx5_fatal_assert_bit_offsets {
514+ MLX5_RFR_OFFSET = 31,
515+};
516+
513517 struct health_buffer {
514518 __be32 assert_var[5];
515519 __be32 rsvd0[3];
@@ -518,12 +522,16 @@ struct health_buffer { · 2 unmodified lines
518522 __be32 rsvd1[2];
519523 __be32 fw_ver;
520524 __be32 hw_id;
521− __be32 rsvd2;
525+ __be32 rfr;
522526 u8 irisc_index;
523527 u8 synd;
524528 __be16 ext_synd;
525529 };
526530
531+enum mlx5_initializing_bit_offsets {
532+ MLX5_FW_RESET_SUPPORTED_OFFSET = 30,
533+};
534+
527535 enum mlx5_cmd_addr_l_sz_offset {
528536 MLX5_NIC_IFC_OFFSET = 8,
529537 };
include/linux/mlx5/driver.h+10−3
@@ -53,6 +53,7 @@ · 52 unmodified lines
5353 #include <linux/mlx5/eq.h>
5454 #include <linux/timecounter.h>
5555 #include <linux/ptp_clock_kernel.h>
56+#include <net/devlink.h>
5657
5758 enum {
5859 MLX5_BOARD_ID_LEN = 64,
@@ -434,13 +435,18 @@ struct mlx5_core_health { · 375 unmodified lines
434435 struct timer_list timer;
435436 u32 prev;
436437 int miss_counter;
437− bool sick;
438+ u8 synd;
439+ u32 fatal_error;
440+ u32 crdump_size;
438441 /* wq spinlock to synchronize draining */
439442 spinlock_t wq_lock;
440443 struct workqueue_struct *wq;
441444 unsigned long flags;
442− struct work_struct work;
445+ struct work_struct fatal_report_work;
446+ struct work_struct report_work;
443447 struct delayed_work recover_work;
448+ struct devlink_health_reporter *fw_reporter;
449+ struct devlink_health_reporter *fw_fatal_reporter;
444450 };
445451
446452 struct mlx5_qp_table {
@@ -581,6 +587,7 @@ struct mlx5_priv { · 134 unmodified lines
581587 };
582588
583589 enum mlx5_device_state {
590+ MLX5_DEVICE_STATE_UNINITIALIZED,
584591 MLX5_DEVICE_STATE_UP,
585592 MLX5_DEVICE_STATE_INTERNAL_ERROR,
586593 };
@@ -693,6 +700,7 @@ struct mlx5_core_dev { · 106 unmodified lines
693700 struct mlx5_clock clock;
694701 struct mlx5_ib_clock_info *clock_info;
695702 struct mlx5_fw_tracer *tracer;
703+ u32 vsc_addr;
696704 };
697705
698706 struct mlx5_db {
@@ -904,7 +912,6 @@ void mlx5_start_health_poll(struct mlx5_core_dev *dev); · 205 unmodified lines
904912 void mlx5_stop_health_poll(struct mlx5_core_dev *dev, bool disable_health);
905913 void mlx5_drain_health_wq(struct mlx5_core_dev *dev);
906914 void mlx5_trigger_health_work(struct mlx5_core_dev *dev);
907−void mlx5_drain_health_recovery(struct mlx5_core_dev *dev);
908915 int mlx5_buf_alloc_node(struct mlx5_core_dev *dev, int size,
909916 struct mlx5_frag_buf *buf, int node);
910917 int mlx5_buf_alloc(struct mlx5_core_dev *dev,
net/core/devlink.c+98−20
@@ -4518,6 +4518,35 @@ nla_put_failure: · 4517 unmodified lines
45184518 return err;
45194519 }
45204520
4521+static int devlink_fmsg_dumpit(struct devlink_fmsg *fmsg, struct sk_buff *skb,
4522+ struct netlink_callback *cb,
4523+ enum devlink_command cmd)
4524+{
4525+ int index = cb->args[0];
4526+ int tmp_index = index;
4527+ void *hdr;
4528+ int err;
4529+
4530+ hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
4531+ &devlink_nl_family, NLM_F_ACK | NLM_F_MULTI, cmd);
4532+ if (!hdr) {
4533+ err = -EMSGSIZE;
4534+ goto nla_put_failure;
4535+ }
4536+
4537+ err = devlink_fmsg_prepare_skb(fmsg, skb, &index);
4538+ if ((err && err != -EMSGSIZE) || tmp_index == index)
4539+ goto nla_put_failure;
4540+
4541+ cb->args[0] = index;
4542+ genlmsg_end(skb, hdr);
4543+ return skb->len;
4544+
4545+nla_put_failure:
4546+ genlmsg_cancel(skb, hdr);
4547+ return err;
4548+}
4549+
45214550 struct devlink_health_reporter {
45224551 struct list_head list;
45234552 void *priv;
@@ -4750,17 +4779,16 @@ int devlink_health_report(struct devlink_health_reporter *reporter, · 226 unmodified lines
47504779 EXPORT_SYMBOL_GPL(devlink_health_report);
47514780
47524781 static struct devlink_health_reporter *
4753−devlink_health_reporter_get_from_info(struct devlink *devlink,
4754− struct genl_info *info)
4782+devlink_health_reporter_get_from_attrs(struct devlink *devlink,
4783+ struct nlattr **attrs)
47554784 {
47564785 struct devlink_health_reporter *reporter;
47574786 char *reporter_name;
47584787
4759− if (!info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_NAME])
4788+ if (!attrs[DEVLINK_ATTR_HEALTH_REPORTER_NAME])
47604789 return NULL;
47614790
4762− reporter_name =
4763− nla_data(info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_NAME]);
4791+ reporter_name = nla_data(attrs[DEVLINK_ATTR_HEALTH_REPORTER_NAME]);
47644792 mutex_lock(&devlink->reporters_lock);
47654793 reporter = devlink_health_reporter_find_by_name(devlink, reporter_name);
47664794 if (reporter)
@@ -4769,6 +4797,48 @@ devlink_health_reporter_get_from_info(struct devlink *devlink, · 2 unmodified lines
47694797 return reporter;
47704798 }
47714799
4800+static struct devlink_health_reporter *
4801+devlink_health_reporter_get_from_info(struct devlink *devlink,
4802+ struct genl_info *info)
4803+{
4804+ return devlink_health_reporter_get_from_attrs(devlink, info->attrs);
4805+}
4806+
4807+static struct devlink_health_reporter *
4808+devlink_health_reporter_get_from_cb(struct netlink_callback *cb)
4809+{
4810+ struct devlink_health_reporter *reporter;
4811+ struct devlink *devlink;
4812+ struct nlattr **attrs;
4813+ int err;
4814+
4815+ attrs = kmalloc_array(DEVLINK_ATTR_MAX + 1, sizeof(*attrs), GFP_KERNEL);
4816+ if (!attrs)
4817+ return NULL;
4818+
4819+ err = nlmsg_parse_deprecated(cb->nlh,
4820+ GENL_HDRLEN + devlink_nl_family.hdrsize,
4821+ attrs, DEVLINK_ATTR_MAX,
4822+ devlink_nl_family.policy, cb->extack);
4823+ if (err)
4824+ goto free;
4825+
4826+ mutex_lock(&devlink_mutex);
4827+ devlink = devlink_get_from_attrs(sock_net(cb->skb->sk), attrs);
4828+ if (IS_ERR(devlink))
4829+ goto unlock;
4830+
4831+ reporter = devlink_health_reporter_get_from_attrs(devlink, attrs);
4832+ mutex_unlock(&devlink_mutex);
4833+ kfree(attrs);
4834+ return reporter;
4835+unlock:
4836+ mutex_unlock(&devlink_mutex);
4837+free:
4838+ kfree(attrs);
4839+ return NULL;
4840+}
4841+
47724842 static void
47734843 devlink_health_reporter_put(struct devlink_health_reporter *reporter)
47744844 {
@@ -5004,32 +5074,40 @@ out: · 229 unmodified lines
50045074 return err;
50055075 }
50065076
5007−static int devlink_nl_cmd_health_reporter_dump_get_doit(struct sk_buff *skb,
5008− struct genl_info *info)
5077+static int
5078+devlink_nl_cmd_health_reporter_dump_get_dumpit(struct sk_buff *skb,
5079+ struct netlink_callback *cb)
50095080 {
5010− struct devlink *devlink = info->user_ptr[0];
50115081 struct devlink_health_reporter *reporter;
5082+ u64 start = cb->args[0];
50125083 int err;
50135084
5014− reporter = devlink_health_reporter_get_from_info(devlink, info);
5085+ reporter = devlink_health_reporter_get_from_cb(cb);
50155086 if (!reporter)
50165087 return -EINVAL;
50175088
50185089 if (!reporter->ops->dump) {
5019− devlink_health_reporter_put(reporter);
5020− return -EOPNOTSUPP;
5090+ err = -EOPNOTSUPP;
5091+ goto out;
50215092 }
5022−
50235093 mutex_lock(&reporter->dump_lock);
5024− err = devlink_health_do_dump(reporter, NULL);
5025− if (err)
5026− goto out;
5027−
5028− err = devlink_fmsg_snd(reporter->dump_fmsg, info,
5029− DEVLINK_CMD_HEALTH_REPORTER_DUMP_GET, 0);
5094+ if (!start) {
5095+ err = devlink_health_do_dump(reporter, NULL);
5096+ if (err)
5097+ goto unlock;
5098+ cb->args[1] = reporter->dump_ts;
5099+ }
5100+ if (!reporter->dump_fmsg || cb->args[1] != reporter->dump_ts) {
5101+ NL_SET_ERR_MSG_MOD(cb->extack, "Dump trampled, please retry");
5102+ err = -EAGAIN;
5103+ goto unlock;
5104+ }
50305105
5031−out:
5106+ err = devlink_fmsg_dumpit(reporter->dump_fmsg, skb, cb,
5107+ DEVLINK_CMD_HEALTH_REPORTER_DUMP_GET);
5108+unlock:
50325109 mutex_unlock(&reporter->dump_lock);
5110+out:
50335111 devlink_health_reporter_put(reporter);
50345112 return err;
50355113 }
@@ -5366,7 +5444,7 @@ static const struct genl_ops devlink_nl_ops[] = { · 330 unmodified lines
53665444 {
53675445 .cmd = DEVLINK_CMD_HEALTH_REPORTER_DUMP_GET,
53685446 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
5369− .doit = devlink_nl_cmd_health_reporter_dump_get_doit,
5447+ .dumpit = devlink_nl_cmd_health_reporter_dump_get_dumpit,
53705448 .flags = GENL_ADMIN_PERM,
53715449 .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
53725450 DEVLINK_NL_FLAG_NO_LOCK,

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t