Skip to content

Commit 3e5b72a

Browse files
ferasdSaeed Mahameed
authored andcommitted
net/mlx5: Issue SW reset on FW assert
If a FW assert is considered fatal, indicated by a new bit in the health buffer, reset the FW. After the reset go through the normal recovery flow. Only one PF needs to issue the reset, so an attempt is made to prevent the 2nd function from also issuing the reset. It's not an error if that happens, it just slows recovery. Signed-off-by: Feras Daoud <[email protected]> Signed-off-by: Alex Vesker <[email protected]> Signed-off-by: Moshe Shemesh <[email protected]> Signed-off-by: Daniel Jurgens <[email protected]> Signed-off-by: Saeed Mahameed <[email protected]>
1 parent 1ef6f1a commit 3e5b72a

File tree

6 files changed

+176
-8
lines changed

6 files changed

+176
-8
lines changed

drivers/net/ethernet/mellanox/mlx5/core/diag/crdump.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,23 @@ int mlx5_crdump_collect(struct mlx5_core_dev *dev, u32 *cr_data)
5151
ret);
5252
return ret;
5353
}
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+
}
5461

5562
ret = mlx5_vsc_gw_set_space(dev, MLX5_VSC_SPACE_SCAN_CRSPACE, NULL);
5663
if (ret)
57-
goto unlock;
64+
goto unlock_sem;
5865

5966
ret = mlx5_crdump_fill(dev, cr_data);
6067

61-
unlock:
68+
unlock_sem:
69+
mlx5_vsc_sem_set_space(dev, MLX5_SEMAPHORE_SW_RESET, MLX5_VSC_UNLOCK);
70+
unlock_gw:
6271
mlx5_vsc_gw_unlock(dev);
6372
return ret;
6473
}

drivers/net/ethernet/mellanox/mlx5/core/health.c

Lines changed: 152 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include "mlx5_core.h"
4141
#include "lib/eq.h"
4242
#include "lib/mlx5.h"
43+
#include "lib/pci_vsc.h"
4344

4445
enum {
4546
MLX5_HEALTH_POLL_INTERVAL = 2 * HZ,
@@ -67,8 +68,10 @@ enum {
6768
enum {
6869
MLX5_SENSOR_NO_ERR = 0,
6970
MLX5_SENSOR_PCI_COMM_ERR = 1,
70-
MLX5_SENSOR_NIC_DISABLED = 2,
71-
MLX5_SENSOR_NIC_SW_RESET = 3,
71+
MLX5_SENSOR_PCI_ERR = 2,
72+
MLX5_SENSOR_NIC_DISABLED = 3,
73+
MLX5_SENSOR_NIC_SW_RESET = 4,
74+
MLX5_SENSOR_FW_SYND_RFR = 5,
7275
};
7376

7477
u8 mlx5_get_nic_state(struct mlx5_core_dev *dev)
@@ -95,32 +98,162 @@ static bool sensor_pci_not_working(struct mlx5_core_dev *dev)
9598
return (ioread32be(&h->fw_ver) == 0xffffffff);
9699
}
97100

101+
static bool sensor_fw_synd_rfr(struct mlx5_core_dev *dev)
102+
{
103+
struct mlx5_core_health *health = &dev->priv.health;
104+
struct health_buffer __iomem *h = health->health;
105+
u32 rfr = ioread32be(&h->rfr) >> MLX5_RFR_OFFSET;
106+
u8 synd = ioread8(&h->synd);
107+
108+
if (rfr && synd)
109+
mlx5_core_dbg(dev, "FW requests reset, synd: %d\n", synd);
110+
return rfr && synd;
111+
}
112+
98113
static u32 check_fatal_sensors(struct mlx5_core_dev *dev)
99114
{
100115
if (sensor_pci_not_working(dev))
101116
return MLX5_SENSOR_PCI_COMM_ERR;
117+
if (pci_channel_offline(dev->pdev))
118+
return MLX5_SENSOR_PCI_ERR;
102119
if (mlx5_get_nic_state(dev) == MLX5_NIC_IFC_DISABLED)
103120
return MLX5_SENSOR_NIC_DISABLED;
104121
if (mlx5_get_nic_state(dev) == MLX5_NIC_IFC_SW_RESET)
105122
return MLX5_SENSOR_NIC_SW_RESET;
123+
if (sensor_fw_synd_rfr(dev))
124+
return MLX5_SENSOR_FW_SYND_RFR;
106125

107126
return MLX5_SENSOR_NO_ERR;
108127
}
109128

129+
static int lock_sem_sw_reset(struct mlx5_core_dev *dev, bool lock)
130+
{
131+
enum mlx5_vsc_state state;
132+
int ret;
133+
134+
if (!mlx5_core_is_pf(dev))
135+
return -EBUSY;
136+
137+
/* Try to lock GW access, this stage doesn't return
138+
* EBUSY because locked GW does not mean that other PF
139+
* already started the reset.
140+
*/
141+
ret = mlx5_vsc_gw_lock(dev);
142+
if (ret == -EBUSY)
143+
return -EINVAL;
144+
if (ret)
145+
return ret;
146+
147+
state = lock ? MLX5_VSC_LOCK : MLX5_VSC_UNLOCK;
148+
/* At this stage, if the return status == EBUSY, then we know
149+
* for sure that another PF started the reset, so don't allow
150+
* another reset.
151+
*/
152+
ret = mlx5_vsc_sem_set_space(dev, MLX5_SEMAPHORE_SW_RESET, state);
153+
if (ret)
154+
mlx5_core_warn(dev, "Failed to lock SW reset semaphore\n");
155+
156+
/* Unlock GW access */
157+
mlx5_vsc_gw_unlock(dev);
158+
159+
return ret;
160+
}
161+
162+
static bool reset_fw_if_needed(struct mlx5_core_dev *dev)
163+
{
164+
bool supported = (ioread32be(&dev->iseg->initializing) >>
165+
MLX5_FW_RESET_SUPPORTED_OFFSET) & 1;
166+
u32 fatal_error;
167+
168+
if (!supported)
169+
return false;
170+
171+
/* The reset only needs to be issued by one PF. The health buffer is
172+
* shared between all functions, and will be cleared during a reset.
173+
* Check again to avoid a redundant 2nd reset. If the fatal erros was
174+
* PCI related a reset won't help.
175+
*/
176+
fatal_error = check_fatal_sensors(dev);
177+
if (fatal_error == MLX5_SENSOR_PCI_COMM_ERR ||
178+
fatal_error == MLX5_SENSOR_NIC_DISABLED ||
179+
fatal_error == MLX5_SENSOR_NIC_SW_RESET) {
180+
mlx5_core_warn(dev, "Not issuing FW reset. Either it's already done or won't help.");
181+
return false;
182+
}
183+
184+
mlx5_core_warn(dev, "Issuing FW Reset\n");
185+
/* Write the NIC interface field to initiate the reset, the command
186+
* interface address also resides here, don't overwrite it.
187+
*/
188+
mlx5_set_nic_state(dev, MLX5_NIC_IFC_SW_RESET);
189+
190+
return true;
191+
}
192+
110193
void mlx5_enter_error_state(struct mlx5_core_dev *dev, bool force)
111194
{
112195
mutex_lock(&dev->intf_state_mutex);
113196
if (dev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR)
114197
goto unlock;
198+
if (dev->state == MLX5_DEVICE_STATE_UNINITIALIZED) {
199+
dev->state = MLX5_DEVICE_STATE_INTERNAL_ERROR;
200+
goto unlock;
201+
}
115202

116-
mlx5_core_err(dev, "start\n");
117-
if (pci_channel_offline(dev->pdev) ||
118-
dev->priv.health.fatal_error != MLX5_SENSOR_NO_ERR || force) {
203+
if (check_fatal_sensors(dev) || force) {
119204
dev->state = MLX5_DEVICE_STATE_INTERNAL_ERROR;
120205
mlx5_cmd_flush(dev);
121206
}
122207

123208
mlx5_notifier_call_chain(dev->priv.events, MLX5_DEV_EVENT_SYS_ERROR, (void *)1);
209+
unlock:
210+
mutex_unlock(&dev->intf_state_mutex);
211+
}
212+
213+
#define MLX5_CRDUMP_WAIT_MS 60000
214+
#define MLX5_FW_RESET_WAIT_MS 1000
215+
void mlx5_error_sw_reset(struct mlx5_core_dev *dev)
216+
{
217+
unsigned long end, delay_ms = MLX5_FW_RESET_WAIT_MS;
218+
int lock = -EBUSY;
219+
220+
mutex_lock(&dev->intf_state_mutex);
221+
if (dev->state != MLX5_DEVICE_STATE_INTERNAL_ERROR)
222+
goto unlock;
223+
224+
mlx5_core_err(dev, "start\n");
225+
226+
if (check_fatal_sensors(dev) == MLX5_SENSOR_FW_SYND_RFR) {
227+
/* Get cr-dump and reset FW semaphore */
228+
lock = lock_sem_sw_reset(dev, true);
229+
230+
if (lock == -EBUSY) {
231+
delay_ms = MLX5_CRDUMP_WAIT_MS;
232+
goto recover_from_sw_reset;
233+
}
234+
/* Execute SW reset */
235+
reset_fw_if_needed(dev);
236+
}
237+
238+
recover_from_sw_reset:
239+
/* Recover from SW reset */
240+
end = jiffies + msecs_to_jiffies(delay_ms);
241+
do {
242+
if (mlx5_get_nic_state(dev) == MLX5_NIC_IFC_DISABLED)
243+
break;
244+
245+
cond_resched();
246+
} while (!time_after(jiffies, end));
247+
248+
if (mlx5_get_nic_state(dev) != MLX5_NIC_IFC_DISABLED) {
249+
dev_err(&dev->pdev->dev, "NIC IFC still %d after %lums.\n",
250+
mlx5_get_nic_state(dev), delay_ms);
251+
}
252+
253+
/* Release FW semaphore if you are the lock owner */
254+
if (!lock)
255+
lock_sem_sw_reset(dev, false);
256+
124257
mlx5_core_err(dev, "end\n");
125258

126259
unlock:
@@ -143,6 +276,20 @@ static void mlx5_handle_bad_state(struct mlx5_core_dev *dev)
143276
case MLX5_NIC_IFC_NO_DRAM_NIC:
144277
mlx5_core_warn(dev, "Expected to see disabled NIC but it is no dram nic\n");
145278
break;
279+
280+
case MLX5_NIC_IFC_SW_RESET:
281+
/* The IFC mode field is 3 bits, so it will read 0x7 in 2 cases:
282+
* 1. PCI has been disabled (ie. PCI-AER, PF driver unloaded
283+
* and this is a VF), this is not recoverable by SW reset.
284+
* Logging of this is handled elsewhere.
285+
* 2. FW reset has been issued by another function, driver can
286+
* be reloaded to recover after the mode switches to
287+
* MLX5_NIC_IFC_DISABLED.
288+
*/
289+
if (dev->priv.health.fatal_error != MLX5_SENSOR_PCI_COMM_ERR)
290+
mlx5_core_warn(dev, "NIC SW reset in progress\n");
291+
break;
292+
146293
default:
147294
mlx5_core_warn(dev, "Expected to see disabled NIC but it is has invalid value %d\n",
148295
nic_interface);

drivers/net/ethernet/mellanox/mlx5/core/main.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1361,6 +1361,7 @@ static pci_ers_result_t mlx5_pci_err_detected(struct pci_dev *pdev,
13611361
mlx5_core_info(dev, "%s was called\n", __func__);
13621362

13631363
mlx5_enter_error_state(dev, false);
1364+
mlx5_error_sw_reset(dev);
13641365
mlx5_unload_one(dev, false);
13651366
/* In case of kernel call drain the health wq */
13661367
if (state) {

drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ enum {
113113

114114
enum mlx5_semaphore_space_address {
115115
MLX5_SEMAPHORE_SPACE_DOMAIN = 0xA,
116+
MLX5_SEMAPHORE_SW_RESET = 0x20,
116117
};
117118

118119
int mlx5_query_hca_caps(struct mlx5_core_dev *dev);
@@ -122,6 +123,7 @@ int mlx5_cmd_teardown_hca(struct mlx5_core_dev *dev);
122123
int mlx5_cmd_force_teardown_hca(struct mlx5_core_dev *dev);
123124
int mlx5_cmd_fast_teardown_hca(struct mlx5_core_dev *dev);
124125
void mlx5_enter_error_state(struct mlx5_core_dev *dev, bool force);
126+
void mlx5_error_sw_reset(struct mlx5_core_dev *dev);
125127
void mlx5_disable_device(struct mlx5_core_dev *dev);
126128
void mlx5_recover_device(struct mlx5_core_dev *dev);
127129
int mlx5_sriov_init(struct mlx5_core_dev *dev);

include/linux/mlx5/device.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,10 @@ struct mlx5_cmd_layout {
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 {
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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,7 @@ struct mlx5_priv {
583583
};
584584

585585
enum mlx5_device_state {
586+
MLX5_DEVICE_STATE_UNINITIALIZED,
586587
MLX5_DEVICE_STATE_UP,
587588
MLX5_DEVICE_STATE_INTERNAL_ERROR,
588589
};

0 commit comments

Comments
 (0)