4040#include "mlx5_core.h"
4141#include "lib/eq.h"
4242#include "lib/mlx5.h"
43+ #include "lib/pci_vsc.h"
4344
4445enum {
4546MLX5_HEALTH_POLL_INTERVAL = 2 * HZ ,
@@ -67,8 +68,10 @@ enum {
6768enum {
6869MLX5_SENSOR_NO_ERR = 0 ,
6970MLX5_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
7477u8 mlx5_get_nic_state (struct mlx5_core_dev * dev )
@@ -95,32 +98,162 @@ static bool sensor_pci_not_working(struct mlx5_core_dev *dev)
9598return (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+
98113static u32 check_fatal_sensors (struct mlx5_core_dev * dev )
99114{
100115if (sensor_pci_not_working (dev ))
101116return MLX5_SENSOR_PCI_COMM_ERR ;
117+ if (pci_channel_offline (dev -> pdev ))
118+ return MLX5_SENSOR_PCI_ERR ;
102119if (mlx5_get_nic_state (dev ) == MLX5_NIC_IFC_DISABLED )
103120return MLX5_SENSOR_NIC_DISABLED ;
104121if (mlx5_get_nic_state (dev ) == MLX5_NIC_IFC_SW_RESET )
105122return MLX5_SENSOR_NIC_SW_RESET ;
123+ if (sensor_fw_synd_rfr (dev ))
124+ return MLX5_SENSOR_FW_SYND_RFR ;
106125
107126return 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+
110193void mlx5_enter_error_state (struct mlx5_core_dev * dev , bool force )
111194{
112195mutex_lock (& dev -> intf_state_mutex );
113196if (dev -> state == MLX5_DEVICE_STATE_INTERNAL_ERROR )
114197goto 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 ) {
119204dev -> state = MLX5_DEVICE_STATE_INTERNAL_ERROR ;
120205mlx5_cmd_flush (dev );
121206}
122207
123208mlx5_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+
124257mlx5_core_err (dev , "end\n" );
125258
126259unlock :
@@ -143,6 +276,20 @@ static void mlx5_handle_bad_state(struct mlx5_core_dev *dev)
143276case MLX5_NIC_IFC_NO_DRAM_NIC :
144277mlx5_core_warn (dev , "Expected to see disabled NIC but it is no dram nic\n" );
145278break ;
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+
146293default :
147294mlx5_core_warn (dev , "Expected to see disabled NIC but it is has invalid value %d\n" ,
148295 nic_interface );
0 commit comments