ice: prevent ice_open and ice_stop during reset
authorKrzysztof Goreczny <krzysztof.goreczny@intel.com>
Fri, 26 Feb 2021 21:19:26 +0000 (13:19 -0800)
committerTony Nguyen <anthony.l.nguyen@intel.com>
Mon, 29 Mar 2021 17:11:55 +0000 (10:11 -0700)
There is a possibility of race between ice_open or ice_stop calls
performed by OS and reset handling routine both trying to modify VSI
resources. Observed scenarios:
- reset handler deallocates memory in ice_vsi_free_arrays and ice_open
  tries to access it in ice_vsi_cfg_txq leading to driver crash
- reset handler deallocates memory in ice_vsi_free_arrays and ice_close
  tries to access it in ice_down leading to driver crash
- reset handler clears port scheduler topology and sets port state to
  ICE_SCHED_PORT_STATE_INIT leading to ice_ena_vsi_txq fail in ice_open

To prevent this additional checks in ice_open and ice_stop are
introduced to make sure that OS is not allowed to alter VSI config while
reset is in progress.

Fixes: cdedef59deb0 ("ice: Configure VSIs for Tx/Rx")
Signed-off-by: Krzysztof Goreczny <krzysztof.goreczny@intel.com>
Tested-by: Tony Brelinski <tonyx.brelinski@intel.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
drivers/net/ethernet/intel/ice/ice.h
drivers/net/ethernet/intel/ice/ice_lib.c
drivers/net/ethernet/intel/ice/ice_main.c

index 357706444dd506aed7b5d86f80c1084f737a3602..1d4518638215c88e57a0024a2319eb8efefc8622 100644 (file)
@@ -642,6 +642,7 @@ int ice_fdir_create_dflt_rules(struct ice_pf *pf);
 int ice_aq_wait_for_event(struct ice_pf *pf, u16 opcode, unsigned long timeout,
                          struct ice_rq_event_info *event);
 int ice_open(struct net_device *netdev);
+int ice_open_internal(struct net_device *netdev);
 int ice_stop(struct net_device *netdev);
 void ice_service_task_schedule(struct ice_pf *pf);
 
index 8d4e2ad4328d1abba195e7a441281b199391b63f..7ac2beaed95c882708355f0e5b8f4ecdd5ee9054 100644 (file)
@@ -2620,7 +2620,7 @@ int ice_ena_vsi(struct ice_vsi *vsi, bool locked)
                        if (!locked)
                                rtnl_lock();
 
-                       err = ice_open(vsi->netdev);
+                       err = ice_open_internal(vsi->netdev);
 
                        if (!locked)
                                rtnl_unlock();
@@ -2649,7 +2649,7 @@ void ice_dis_vsi(struct ice_vsi *vsi, bool locked)
                        if (!locked)
                                rtnl_lock();
 
-                       ice_stop(vsi->netdev);
+                       ice_vsi_close(vsi);
 
                        if (!locked)
                                rtnl_unlock();
index 53e053c997ebef8d400c74e8909e6143d44be98f..255a07c1e33a227d5fee86597b0c3aedefb583e6 100644 (file)
@@ -6632,6 +6632,28 @@ static void ice_tx_timeout(struct net_device *netdev, unsigned int txqueue)
  * Returns 0 on success, negative value on failure
  */
 int ice_open(struct net_device *netdev)
+{
+       struct ice_netdev_priv *np = netdev_priv(netdev);
+       struct ice_pf *pf = np->vsi->back;
+
+       if (ice_is_reset_in_progress(pf->state)) {
+               netdev_err(netdev, "can't open net device while reset is in progress");
+               return -EBUSY;
+       }
+
+       return ice_open_internal(netdev);
+}
+
+/**
+ * ice_open_internal - Called when a network interface becomes active
+ * @netdev: network interface device structure
+ *
+ * Internal ice_open implementation. Should not be used directly except for ice_open and reset
+ * handling routine
+ *
+ * Returns 0 on success, negative value on failure
+ */
+int ice_open_internal(struct net_device *netdev)
 {
        struct ice_netdev_priv *np = netdev_priv(netdev);
        struct ice_vsi *vsi = np->vsi;
@@ -6712,6 +6734,12 @@ int ice_stop(struct net_device *netdev)
 {
        struct ice_netdev_priv *np = netdev_priv(netdev);
        struct ice_vsi *vsi = np->vsi;
+       struct ice_pf *pf = vsi->back;
+
+       if (ice_is_reset_in_progress(pf->state)) {
+               netdev_err(netdev, "can't stop net device while reset is in progress");
+               return -EBUSY;
+       }
 
        ice_vsi_close(vsi);