40
40
#include "mlx5_core.h"
41
41
#include "lib/eq.h"
42
42
#include "lib/mlx5.h"
43
+ #include "lib/pci_vsc.h"
43
44
44
45
enum {
45
46
MLX5_HEALTH_POLL_INTERVAL = 2 * HZ ,
@@ -67,8 +68,10 @@ enum {
67
68
enum {
68
69
MLX5_SENSOR_NO_ERR = 0 ,
69
70
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 ,
72
75
};
73
76
74
77
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)
95
98
return (ioread32be (& h -> fw_ver ) == 0xffffffff );
96
99
}
97
100
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
+
98
113
static u32 check_fatal_sensors (struct mlx5_core_dev * dev )
99
114
{
100
115
if (sensor_pci_not_working (dev ))
101
116
return MLX5_SENSOR_PCI_COMM_ERR ;
117
+ if (pci_channel_offline (dev -> pdev ))
118
+ return MLX5_SENSOR_PCI_ERR ;
102
119
if (mlx5_get_nic_state (dev ) == MLX5_NIC_IFC_DISABLED )
103
120
return MLX5_SENSOR_NIC_DISABLED ;
104
121
if (mlx5_get_nic_state (dev ) == MLX5_NIC_IFC_SW_RESET )
105
122
return MLX5_SENSOR_NIC_SW_RESET ;
123
+ if (sensor_fw_synd_rfr (dev ))
124
+ return MLX5_SENSOR_FW_SYND_RFR ;
106
125
107
126
return MLX5_SENSOR_NO_ERR ;
108
127
}
109
128
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
+
110
193
void mlx5_enter_error_state (struct mlx5_core_dev * dev , bool force )
111
194
{
112
195
mutex_lock (& dev -> intf_state_mutex );
113
196
if (dev -> state == MLX5_DEVICE_STATE_INTERNAL_ERROR )
114
197
goto unlock ;
198
+ if (dev -> state == MLX5_DEVICE_STATE_UNINITIALIZED ) {
199
+ dev -> state = MLX5_DEVICE_STATE_INTERNAL_ERROR ;
200
+ goto unlock ;
201
+ }
115
202
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 ) {
119
204
dev -> state = MLX5_DEVICE_STATE_INTERNAL_ERROR ;
120
205
mlx5_cmd_flush (dev );
121
206
}
122
207
123
208
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
+
124
257
mlx5_core_err (dev , "end\n" );
125
258
126
259
unlock :
@@ -143,6 +276,20 @@ static void mlx5_handle_bad_state(struct mlx5_core_dev *dev)
143
276
case MLX5_NIC_IFC_NO_DRAM_NIC :
144
277
mlx5_core_warn (dev , "Expected to see disabled NIC but it is no dram nic\n" );
145
278
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
+
146
293
default :
147
294
mlx5_core_warn (dev , "Expected to see disabled NIC but it is has invalid value %d\n" ,
148
295
nic_interface );
0 commit comments