* [PATCH v3 0/2] net/ibmvnic: serialization fixes @ 2019-09-17 17:15 ` Juliet Kim 0 siblings, 0 replies; 10+ messages in thread From: Juliet Kim @ 2019-09-17 17:15 UTC (permalink / raw) To: netdev; +Cc: julietk, linuxppc-dev, tlfalcon This series includes two fixes. The first improves reset code to allow linkwatch_event to proceed during reset. The second ensures that no more than one thread runs in reset at a time. v2: - Separate change param reset from do_reset() - Return IBMVNIC_OPEN_FAILED in open failiure case - Remove setting wait_for_reset to false from __ibmvnic_reset(), this is done in wait_for_reset() - Move the check for force_reset_recovery from patch 1 to patch 2 v3: - Restore reset’s successful return in open failure case Juliet Kim (2): net/ibmvnic: unlock rtnl_lock in reset so linkwatch_event can run net/ibmvnic: prevent more than one thread from running in reset drivers/net/ethernet/ibm/ibmvnic.c | 245 +++++++++++++++++++++++++++---------- drivers/net/ethernet/ibm/ibmvnic.h | 4 + 2 files changed, 181 insertions(+), 68 deletions(-) -- 2.16.4 ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v3 0/2] net/ibmvnic: serialization fixes @ 2019-09-17 17:15 ` Juliet Kim 0 siblings, 0 replies; 10+ messages in thread From: Juliet Kim @ 2019-09-17 17:15 UTC (permalink / raw) To: netdev; +Cc: julietk, tlfalcon, linuxppc-dev This series includes two fixes. The first improves reset code to allow linkwatch_event to proceed during reset. The second ensures that no more than one thread runs in reset at a time. v2: - Separate change param reset from do_reset() - Return IBMVNIC_OPEN_FAILED in open failiure case - Remove setting wait_for_reset to false from __ibmvnic_reset(), this is done in wait_for_reset() - Move the check for force_reset_recovery from patch 1 to patch 2 v3: - Restore reset’s successful return in open failure case Juliet Kim (2): net/ibmvnic: unlock rtnl_lock in reset so linkwatch_event can run net/ibmvnic: prevent more than one thread from running in reset drivers/net/ethernet/ibm/ibmvnic.c | 245 +++++++++++++++++++++++++++---------- drivers/net/ethernet/ibm/ibmvnic.h | 4 + 2 files changed, 181 insertions(+), 68 deletions(-) -- 2.16.4 ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v3 1/2] net/ibmvnic: unlock rtnl_lock in reset so linkwatch_event can run 2019-09-17 17:15 ` Juliet Kim @ 2019-09-17 17:15 ` Juliet Kim -1 siblings, 0 replies; 10+ messages in thread From: Juliet Kim @ 2019-09-17 17:15 UTC (permalink / raw) To: netdev; +Cc: julietk, linuxppc-dev, tlfalcon Commit a5681e20b541 ("net/ibmnvic: Fix deadlock problem in reset") made the change to hold the RTNL lock during a reset to avoid deadlock but linkwatch_event is fired during the reset and needs the RTNL lock. That keeps linkwatch_event process from proceeding until the reset is complete. The reset process cannot tolerate the linkwatch_event processing after reset completes, so release the RTNL lock during the process to allow a chance for linkwatch_event to run during reset. This does not guarantee that the linkwatch_event will be processed as soon as link state changes, but is an improvement over the current code where linkwatch_event processing is always delayed, which prevents transmissions on the device from being deactivated leading transmit watchdog timer to time-out. Release the RTNL lock before link state change and re-acquire after the link state change to allow linkwatch_event to grab the RTNL lock and run during the reset. Fixes: a5681e20b541 ("net/ibmnvic: Fix deadlock problem in reset") Signed-off-by: Juliet Kim <julietk@linux.vnet.ibm.com> --- drivers/net/ethernet/ibm/ibmvnic.c | 224 ++++++++++++++++++++++++++----------- drivers/net/ethernet/ibm/ibmvnic.h | 1 + 2 files changed, 157 insertions(+), 68 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 5cb55ea671e3..ba340aaff1b3 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -1723,6 +1723,86 @@ static int ibmvnic_set_mac(struct net_device *netdev, void *p) return rc; } +/** + * do_change_param_reset returns zero if we are able to keep processing reset + * events, or non-zero if we hit a fatal error and must halt. + */ +static int do_change_param_reset(struct ibmvnic_adapter *adapter, + struct ibmvnic_rwi *rwi, + u32 reset_state) +{ + struct net_device *netdev = adapter->netdev; + int i, rc; + + netdev_dbg(adapter->netdev, "Change param resetting driver (%d)\n", + rwi->reset_reason); + + netif_carrier_off(netdev); + adapter->reset_reason = rwi->reset_reason; + + ibmvnic_cleanup(netdev); + + if (reset_state == VNIC_OPEN) { + rc = __ibmvnic_close(netdev); + if (rc) + return rc; + } + + release_resources(adapter); + release_sub_crqs(adapter, 1); + release_crq_queue(adapter); + + adapter->state = VNIC_PROBED; + + rc = init_crq_queue(adapter); + + if (rc) { + netdev_err(adapter->netdev, + "Couldn't initialize crq. rc=%d\n", rc); + return rc; + } + + rc = ibmvnic_reset_init(adapter); + if (rc) + return IBMVNIC_INIT_FAILED; + + /* If the adapter was in PROBE state prior to the reset, + * exit here. + */ + if (reset_state == VNIC_PROBED) + return 0; + + rc = ibmvnic_login(netdev); + if (rc) { + adapter->state = reset_state; + return rc; + } + + rc = init_resources(adapter); + if (rc) + return rc; + + ibmvnic_disable_irqs(adapter); + + adapter->state = VNIC_CLOSED; + + if (reset_state == VNIC_CLOSED) + return 0; + + rc = __ibmvnic_open(netdev); + if (rc) + return IBMVNIC_OPEN_FAILED; + + /* refresh device's multicast list */ + ibmvnic_set_multi(netdev); + + /* kick napi */ + for (i = 0; i < adapter->req_rx_queues; i++) + napi_schedule(&adapter->napi[i]); + + return 0; +} + /** * do_reset returns zero if we are able to keep processing reset events, or * non-zero if we hit a fatal error and must halt. @@ -1738,6 +1818,8 @@ static int do_reset(struct ibmvnic_adapter *adapter, netdev_dbg(adapter->netdev, "Re-setting driver (%d)\n", rwi->reset_reason); + rtnl_lock(); + netif_carrier_off(netdev); adapter->reset_reason = rwi->reset_reason; @@ -1751,16 +1833,25 @@ static int do_reset(struct ibmvnic_adapter *adapter, if (reset_state == VNIC_OPEN && adapter->reset_reason != VNIC_RESET_MOBILITY && adapter->reset_reason != VNIC_RESET_FAILOVER) { - rc = __ibmvnic_close(netdev); + adapter->state = VNIC_CLOSING; + + /* Release the RTNL lock before link state change and + * re-acquire after the link state change to allow + * linkwatch_event to grab the RTNL lock and run during + * a reset. + */ + rtnl_unlock(); + rc = set_link_state(adapter, IBMVNIC_LOGICAL_LNK_DN); + rtnl_lock(); if (rc) - return rc; - } + goto out; - if (adapter->reset_reason == VNIC_RESET_CHANGE_PARAM || - adapter->wait_for_reset) { - release_resources(adapter); - release_sub_crqs(adapter, 1); - release_crq_queue(adapter); + if (adapter->state != VNIC_CLOSING) { + rc = -1; + goto out; + } + + adapter->state = VNIC_CLOSED; } if (adapter->reset_reason != VNIC_RESET_NON_FATAL) { @@ -1769,9 +1860,7 @@ static int do_reset(struct ibmvnic_adapter *adapter, */ adapter->state = VNIC_PROBED; - if (adapter->wait_for_reset) { - rc = init_crq_queue(adapter); - } else if (adapter->reset_reason == VNIC_RESET_MOBILITY) { + if (adapter->reset_reason == VNIC_RESET_MOBILITY) { rc = ibmvnic_reenable_crq_queue(adapter); release_sub_crqs(adapter, 1); } else { @@ -1783,36 +1872,35 @@ static int do_reset(struct ibmvnic_adapter *adapter, if (rc) { netdev_err(adapter->netdev, "Couldn't initialize crq. rc=%d\n", rc); - return rc; + goto out; } rc = ibmvnic_reset_init(adapter); - if (rc) - return IBMVNIC_INIT_FAILED; + if (rc) { + rc = IBMVNIC_INIT_FAILED; + goto out; + } /* If the adapter was in PROBE state prior to the reset, * exit here. */ - if (reset_state == VNIC_PROBED) - return 0; + if (reset_state == VNIC_PROBED) { + rc = 0; + goto out; + } rc = ibmvnic_login(netdev); if (rc) { adapter->state = reset_state; - return rc; + goto out; } - if (adapter->reset_reason == VNIC_RESET_CHANGE_PARAM || - adapter->wait_for_reset) { - rc = init_resources(adapter); - if (rc) - return rc; - } else if (adapter->req_rx_queues != old_num_rx_queues || - adapter->req_tx_queues != old_num_tx_queues || - adapter->req_rx_add_entries_per_subcrq != - old_num_rx_slots || - adapter->req_tx_entries_per_subcrq != - old_num_tx_slots) { + if (adapter->req_rx_queues != old_num_rx_queues || + adapter->req_tx_queues != old_num_tx_queues || + adapter->req_rx_add_entries_per_subcrq != + old_num_rx_slots || + adapter->req_tx_entries_per_subcrq != + old_num_tx_slots) { release_rx_pools(adapter); release_tx_pools(adapter); release_napi(adapter); @@ -1820,32 +1908,30 @@ static int do_reset(struct ibmvnic_adapter *adapter, rc = init_resources(adapter); if (rc) - return rc; + goto out; } else { rc = reset_tx_pools(adapter); if (rc) - return rc; + goto out; rc = reset_rx_pools(adapter); if (rc) - return rc; + goto out; } ibmvnic_disable_irqs(adapter); } adapter->state = VNIC_CLOSED; - if (reset_state == VNIC_CLOSED) - return 0; + if (reset_state == VNIC_CLOSED) { + rc = 0; + goto out; + } rc = __ibmvnic_open(netdev); if (rc) { - if (list_empty(&adapter->rwi_list)) - adapter->state = VNIC_CLOSED; - else - adapter->state = reset_state; - - return 0; + rc = IBMVNIC_OPEN_FAILED; + goto out; } /* refresh device's multicast list */ @@ -1855,11 +1941,15 @@ static int do_reset(struct ibmvnic_adapter *adapter, for (i = 0; i < adapter->req_rx_queues; i++) napi_schedule(&adapter->napi[i]); - if (adapter->reset_reason != VNIC_RESET_FAILOVER && - adapter->reset_reason != VNIC_RESET_CHANGE_PARAM) + if (adapter->reset_reason != VNIC_RESET_FAILOVER) call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, netdev); - return 0; + rc = 0; + +out: + rtnl_unlock(); + + return rc; } static int do_hard_reset(struct ibmvnic_adapter *adapter, @@ -1919,14 +2009,8 @@ static int do_hard_reset(struct ibmvnic_adapter *adapter, return 0; rc = __ibmvnic_open(netdev); - if (rc) { - if (list_empty(&adapter->rwi_list)) - adapter->state = VNIC_CLOSED; - else - adapter->state = reset_state; - - return 0; - } + if (rc) + return IBMVNIC_OPEN_FAILED; return 0; } @@ -1965,20 +2049,11 @@ static void __ibmvnic_reset(struct work_struct *work) { struct ibmvnic_rwi *rwi; struct ibmvnic_adapter *adapter; - bool we_lock_rtnl = false; u32 reset_state; int rc = 0; adapter = container_of(work, struct ibmvnic_adapter, ibmvnic_reset); - /* netif_set_real_num_xx_queues needs to take rtnl lock here - * unless wait_for_reset is set, in which case the rtnl lock - * has already been taken before initializing the reset - */ - if (!adapter->wait_for_reset) { - rtnl_lock(); - we_lock_rtnl = true; - } reset_state = adapter->state; rwi = get_next_rwi(adapter); @@ -1990,14 +2065,32 @@ static void __ibmvnic_reset(struct work_struct *work) break; } - if (adapter->force_reset_recovery) { - adapter->force_reset_recovery = false; - rc = do_hard_reset(adapter, rwi, reset_state); + if (rwi->reset_reason == VNIC_RESET_CHANGE_PARAM) { + /* CHANGE_PARAM requestor holds rtnl_lock */ + rc = do_change_param_reset(adapter, rwi, reset_state); + } else if (adapter->force_reset_recovery) { + /* Transport event occurred during previous reset */ + if (adapter->wait_for_reset) { + /* Previous was CHANGE_PARAM; caller locked */ + adapter->force_reset_recovery = false; + rc = do_hard_reset(adapter, rwi, reset_state); + } else { + rtnl_lock(); + adapter->force_reset_recovery = false; + rc = do_hard_reset(adapter, rwi, reset_state); + rtnl_unlock(); + } } else { rc = do_reset(adapter, rwi, reset_state); } kfree(rwi); - if (rc && rc != IBMVNIC_INIT_FAILED && + if (rc == IBMVNIC_OPEN_FAILED) { + if (list_empty(&adapter->rwi_list)) + adapter->state = VNIC_CLOSED; + else + adapter->state = reset_state; + rc = 0; + } else if (rc && rc != IBMVNIC_INIT_FAILED && !adapter->force_reset_recovery) break; @@ -2005,7 +2098,6 @@ static void __ibmvnic_reset(struct work_struct *work) } if (adapter->wait_for_reset) { - adapter->wait_for_reset = false; adapter->reset_done_rc = rc; complete(&adapter->reset_done); } @@ -2016,8 +2108,6 @@ static void __ibmvnic_reset(struct work_struct *work) } adapter->resetting = false; - if (we_lock_rtnl) - rtnl_unlock(); } static int ibmvnic_reset(struct ibmvnic_adapter *adapter, @@ -2078,8 +2168,6 @@ static int ibmvnic_reset(struct ibmvnic_adapter *adapter, return 0; err: - if (adapter->wait_for_reset) - adapter->wait_for_reset = false; return -ret; } diff --git a/drivers/net/ethernet/ibm/ibmvnic.h b/drivers/net/ethernet/ibm/ibmvnic.h index 70bd286f8932..9d3d35cc91d6 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.h +++ b/drivers/net/ethernet/ibm/ibmvnic.h @@ -20,6 +20,7 @@ #define IBMVNIC_INVALID_MAP -1 #define IBMVNIC_STATS_TIMEOUT 1 #define IBMVNIC_INIT_FAILED 2 +#define IBMVNIC_OPEN_FAILED 3 /* basic structures plus 100 2k buffers */ #define IBMVNIC_IO_ENTITLEMENT_DEFAULT 610305 -- 2.16.4 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v3 1/2] net/ibmvnic: unlock rtnl_lock in reset so linkwatch_event can run @ 2019-09-17 17:15 ` Juliet Kim 0 siblings, 0 replies; 10+ messages in thread From: Juliet Kim @ 2019-09-17 17:15 UTC (permalink / raw) To: netdev; +Cc: julietk, tlfalcon, linuxppc-dev Commit a5681e20b541 ("net/ibmnvic: Fix deadlock problem in reset") made the change to hold the RTNL lock during a reset to avoid deadlock but linkwatch_event is fired during the reset and needs the RTNL lock. That keeps linkwatch_event process from proceeding until the reset is complete. The reset process cannot tolerate the linkwatch_event processing after reset completes, so release the RTNL lock during the process to allow a chance for linkwatch_event to run during reset. This does not guarantee that the linkwatch_event will be processed as soon as link state changes, but is an improvement over the current code where linkwatch_event processing is always delayed, which prevents transmissions on the device from being deactivated leading transmit watchdog timer to time-out. Release the RTNL lock before link state change and re-acquire after the link state change to allow linkwatch_event to grab the RTNL lock and run during the reset. Fixes: a5681e20b541 ("net/ibmnvic: Fix deadlock problem in reset") Signed-off-by: Juliet Kim <julietk@linux.vnet.ibm.com> --- drivers/net/ethernet/ibm/ibmvnic.c | 224 ++++++++++++++++++++++++++----------- drivers/net/ethernet/ibm/ibmvnic.h | 1 + 2 files changed, 157 insertions(+), 68 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 5cb55ea671e3..ba340aaff1b3 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -1723,6 +1723,86 @@ static int ibmvnic_set_mac(struct net_device *netdev, void *p) return rc; } +/** + * do_change_param_reset returns zero if we are able to keep processing reset + * events, or non-zero if we hit a fatal error and must halt. + */ +static int do_change_param_reset(struct ibmvnic_adapter *adapter, + struct ibmvnic_rwi *rwi, + u32 reset_state) +{ + struct net_device *netdev = adapter->netdev; + int i, rc; + + netdev_dbg(adapter->netdev, "Change param resetting driver (%d)\n", + rwi->reset_reason); + + netif_carrier_off(netdev); + adapter->reset_reason = rwi->reset_reason; + + ibmvnic_cleanup(netdev); + + if (reset_state == VNIC_OPEN) { + rc = __ibmvnic_close(netdev); + if (rc) + return rc; + } + + release_resources(adapter); + release_sub_crqs(adapter, 1); + release_crq_queue(adapter); + + adapter->state = VNIC_PROBED; + + rc = init_crq_queue(adapter); + + if (rc) { + netdev_err(adapter->netdev, + "Couldn't initialize crq. rc=%d\n", rc); + return rc; + } + + rc = ibmvnic_reset_init(adapter); + if (rc) + return IBMVNIC_INIT_FAILED; + + /* If the adapter was in PROBE state prior to the reset, + * exit here. + */ + if (reset_state == VNIC_PROBED) + return 0; + + rc = ibmvnic_login(netdev); + if (rc) { + adapter->state = reset_state; + return rc; + } + + rc = init_resources(adapter); + if (rc) + return rc; + + ibmvnic_disable_irqs(adapter); + + adapter->state = VNIC_CLOSED; + + if (reset_state == VNIC_CLOSED) + return 0; + + rc = __ibmvnic_open(netdev); + if (rc) + return IBMVNIC_OPEN_FAILED; + + /* refresh device's multicast list */ + ibmvnic_set_multi(netdev); + + /* kick napi */ + for (i = 0; i < adapter->req_rx_queues; i++) + napi_schedule(&adapter->napi[i]); + + return 0; +} + /** * do_reset returns zero if we are able to keep processing reset events, or * non-zero if we hit a fatal error and must halt. @@ -1738,6 +1818,8 @@ static int do_reset(struct ibmvnic_adapter *adapter, netdev_dbg(adapter->netdev, "Re-setting driver (%d)\n", rwi->reset_reason); + rtnl_lock(); + netif_carrier_off(netdev); adapter->reset_reason = rwi->reset_reason; @@ -1751,16 +1833,25 @@ static int do_reset(struct ibmvnic_adapter *adapter, if (reset_state == VNIC_OPEN && adapter->reset_reason != VNIC_RESET_MOBILITY && adapter->reset_reason != VNIC_RESET_FAILOVER) { - rc = __ibmvnic_close(netdev); + adapter->state = VNIC_CLOSING; + + /* Release the RTNL lock before link state change and + * re-acquire after the link state change to allow + * linkwatch_event to grab the RTNL lock and run during + * a reset. + */ + rtnl_unlock(); + rc = set_link_state(adapter, IBMVNIC_LOGICAL_LNK_DN); + rtnl_lock(); if (rc) - return rc; - } + goto out; - if (adapter->reset_reason == VNIC_RESET_CHANGE_PARAM || - adapter->wait_for_reset) { - release_resources(adapter); - release_sub_crqs(adapter, 1); - release_crq_queue(adapter); + if (adapter->state != VNIC_CLOSING) { + rc = -1; + goto out; + } + + adapter->state = VNIC_CLOSED; } if (adapter->reset_reason != VNIC_RESET_NON_FATAL) { @@ -1769,9 +1860,7 @@ static int do_reset(struct ibmvnic_adapter *adapter, */ adapter->state = VNIC_PROBED; - if (adapter->wait_for_reset) { - rc = init_crq_queue(adapter); - } else if (adapter->reset_reason == VNIC_RESET_MOBILITY) { + if (adapter->reset_reason == VNIC_RESET_MOBILITY) { rc = ibmvnic_reenable_crq_queue(adapter); release_sub_crqs(adapter, 1); } else { @@ -1783,36 +1872,35 @@ static int do_reset(struct ibmvnic_adapter *adapter, if (rc) { netdev_err(adapter->netdev, "Couldn't initialize crq. rc=%d\n", rc); - return rc; + goto out; } rc = ibmvnic_reset_init(adapter); - if (rc) - return IBMVNIC_INIT_FAILED; + if (rc) { + rc = IBMVNIC_INIT_FAILED; + goto out; + } /* If the adapter was in PROBE state prior to the reset, * exit here. */ - if (reset_state == VNIC_PROBED) - return 0; + if (reset_state == VNIC_PROBED) { + rc = 0; + goto out; + } rc = ibmvnic_login(netdev); if (rc) { adapter->state = reset_state; - return rc; + goto out; } - if (adapter->reset_reason == VNIC_RESET_CHANGE_PARAM || - adapter->wait_for_reset) { - rc = init_resources(adapter); - if (rc) - return rc; - } else if (adapter->req_rx_queues != old_num_rx_queues || - adapter->req_tx_queues != old_num_tx_queues || - adapter->req_rx_add_entries_per_subcrq != - old_num_rx_slots || - adapter->req_tx_entries_per_subcrq != - old_num_tx_slots) { + if (adapter->req_rx_queues != old_num_rx_queues || + adapter->req_tx_queues != old_num_tx_queues || + adapter->req_rx_add_entries_per_subcrq != + old_num_rx_slots || + adapter->req_tx_entries_per_subcrq != + old_num_tx_slots) { release_rx_pools(adapter); release_tx_pools(adapter); release_napi(adapter); @@ -1820,32 +1908,30 @@ static int do_reset(struct ibmvnic_adapter *adapter, rc = init_resources(adapter); if (rc) - return rc; + goto out; } else { rc = reset_tx_pools(adapter); if (rc) - return rc; + goto out; rc = reset_rx_pools(adapter); if (rc) - return rc; + goto out; } ibmvnic_disable_irqs(adapter); } adapter->state = VNIC_CLOSED; - if (reset_state == VNIC_CLOSED) - return 0; + if (reset_state == VNIC_CLOSED) { + rc = 0; + goto out; + } rc = __ibmvnic_open(netdev); if (rc) { - if (list_empty(&adapter->rwi_list)) - adapter->state = VNIC_CLOSED; - else - adapter->state = reset_state; - - return 0; + rc = IBMVNIC_OPEN_FAILED; + goto out; } /* refresh device's multicast list */ @@ -1855,11 +1941,15 @@ static int do_reset(struct ibmvnic_adapter *adapter, for (i = 0; i < adapter->req_rx_queues; i++) napi_schedule(&adapter->napi[i]); - if (adapter->reset_reason != VNIC_RESET_FAILOVER && - adapter->reset_reason != VNIC_RESET_CHANGE_PARAM) + if (adapter->reset_reason != VNIC_RESET_FAILOVER) call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, netdev); - return 0; + rc = 0; + +out: + rtnl_unlock(); + + return rc; } static int do_hard_reset(struct ibmvnic_adapter *adapter, @@ -1919,14 +2009,8 @@ static int do_hard_reset(struct ibmvnic_adapter *adapter, return 0; rc = __ibmvnic_open(netdev); - if (rc) { - if (list_empty(&adapter->rwi_list)) - adapter->state = VNIC_CLOSED; - else - adapter->state = reset_state; - - return 0; - } + if (rc) + return IBMVNIC_OPEN_FAILED; return 0; } @@ -1965,20 +2049,11 @@ static void __ibmvnic_reset(struct work_struct *work) { struct ibmvnic_rwi *rwi; struct ibmvnic_adapter *adapter; - bool we_lock_rtnl = false; u32 reset_state; int rc = 0; adapter = container_of(work, struct ibmvnic_adapter, ibmvnic_reset); - /* netif_set_real_num_xx_queues needs to take rtnl lock here - * unless wait_for_reset is set, in which case the rtnl lock - * has already been taken before initializing the reset - */ - if (!adapter->wait_for_reset) { - rtnl_lock(); - we_lock_rtnl = true; - } reset_state = adapter->state; rwi = get_next_rwi(adapter); @@ -1990,14 +2065,32 @@ static void __ibmvnic_reset(struct work_struct *work) break; } - if (adapter->force_reset_recovery) { - adapter->force_reset_recovery = false; - rc = do_hard_reset(adapter, rwi, reset_state); + if (rwi->reset_reason == VNIC_RESET_CHANGE_PARAM) { + /* CHANGE_PARAM requestor holds rtnl_lock */ + rc = do_change_param_reset(adapter, rwi, reset_state); + } else if (adapter->force_reset_recovery) { + /* Transport event occurred during previous reset */ + if (adapter->wait_for_reset) { + /* Previous was CHANGE_PARAM; caller locked */ + adapter->force_reset_recovery = false; + rc = do_hard_reset(adapter, rwi, reset_state); + } else { + rtnl_lock(); + adapter->force_reset_recovery = false; + rc = do_hard_reset(adapter, rwi, reset_state); + rtnl_unlock(); + } } else { rc = do_reset(adapter, rwi, reset_state); } kfree(rwi); - if (rc && rc != IBMVNIC_INIT_FAILED && + if (rc == IBMVNIC_OPEN_FAILED) { + if (list_empty(&adapter->rwi_list)) + adapter->state = VNIC_CLOSED; + else + adapter->state = reset_state; + rc = 0; + } else if (rc && rc != IBMVNIC_INIT_FAILED && !adapter->force_reset_recovery) break; @@ -2005,7 +2098,6 @@ static void __ibmvnic_reset(struct work_struct *work) } if (adapter->wait_for_reset) { - adapter->wait_for_reset = false; adapter->reset_done_rc = rc; complete(&adapter->reset_done); } @@ -2016,8 +2108,6 @@ static void __ibmvnic_reset(struct work_struct *work) } adapter->resetting = false; - if (we_lock_rtnl) - rtnl_unlock(); } static int ibmvnic_reset(struct ibmvnic_adapter *adapter, @@ -2078,8 +2168,6 @@ static int ibmvnic_reset(struct ibmvnic_adapter *adapter, return 0; err: - if (adapter->wait_for_reset) - adapter->wait_for_reset = false; return -ret; } diff --git a/drivers/net/ethernet/ibm/ibmvnic.h b/drivers/net/ethernet/ibm/ibmvnic.h index 70bd286f8932..9d3d35cc91d6 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.h +++ b/drivers/net/ethernet/ibm/ibmvnic.h @@ -20,6 +20,7 @@ #define IBMVNIC_INVALID_MAP -1 #define IBMVNIC_STATS_TIMEOUT 1 #define IBMVNIC_INIT_FAILED 2 +#define IBMVNIC_OPEN_FAILED 3 /* basic structures plus 100 2k buffers */ #define IBMVNIC_IO_ENTITLEMENT_DEFAULT 610305 -- 2.16.4 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v3 2/2] net/ibmvnic: prevent more than one thread from running in reset 2019-09-17 17:15 ` Juliet Kim @ 2019-09-17 17:15 ` Juliet Kim -1 siblings, 0 replies; 10+ messages in thread From: Juliet Kim @ 2019-09-17 17:15 UTC (permalink / raw) To: netdev; +Cc: julietk, linuxppc-dev, tlfalcon Signed-off-by: Juliet Kim <julietk@linux.vnet.ibm.com> --- drivers/net/ethernet/ibm/ibmvnic.c | 23 ++++++++++++++++++++++- drivers/net/ethernet/ibm/ibmvnic.h | 3 +++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index ba340aaff1b3..f344ccd68ad9 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -2054,6 +2054,13 @@ static void __ibmvnic_reset(struct work_struct *work) adapter = container_of(work, struct ibmvnic_adapter, ibmvnic_reset); + if (adapter->resetting) { + schedule_delayed_work(&adapter->ibmvnic_delayed_reset, + IBMVNIC_RESET_DELAY); + return; + } + + adapter->resetting = true; reset_state = adapter->state; rwi = get_next_rwi(adapter); @@ -2095,6 +2102,10 @@ static void __ibmvnic_reset(struct work_struct *work) break; rwi = get_next_rwi(adapter); + + if (rwi && (rwi->reset_reason == VNIC_RESET_FAILOVER || + rwi->reset_reason == VNIC_RESET_MOBILITY)) + adapter->force_reset_recovery = true; } if (adapter->wait_for_reset) { @@ -2110,6 +2121,15 @@ static void __ibmvnic_reset(struct work_struct *work) adapter->resetting = false; } +static void __ibmvnic_delayed_reset(struct work_struct *work) +{ + struct ibmvnic_adapter *adapter; + + adapter = container_of(work, struct ibmvnic_adapter, + ibmvnic_delayed_reset.work); + __ibmvnic_reset(&adapter->ibmvnic_reset); +} + static int ibmvnic_reset(struct ibmvnic_adapter *adapter, enum ibmvnic_reset_reason reason) { @@ -2162,7 +2182,6 @@ static int ibmvnic_reset(struct ibmvnic_adapter *adapter, rwi->reset_reason = reason; list_add_tail(&rwi->list, &adapter->rwi_list); spin_unlock_irqrestore(&adapter->rwi_lock, flags); - adapter->resetting = true; netdev_dbg(adapter->netdev, "Scheduling reset (reason %d)\n", reason); schedule_work(&adapter->ibmvnic_reset); @@ -4933,6 +4952,8 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id) spin_lock_init(&adapter->stats_lock); INIT_WORK(&adapter->ibmvnic_reset, __ibmvnic_reset); + INIT_DELAYED_WORK(&adapter->ibmvnic_delayed_reset, + __ibmvnic_delayed_reset); INIT_LIST_HEAD(&adapter->rwi_list); spin_lock_init(&adapter->rwi_lock); init_completion(&adapter->init_done); diff --git a/drivers/net/ethernet/ibm/ibmvnic.h b/drivers/net/ethernet/ibm/ibmvnic.h index 9d3d35cc91d6..4f4651d92cc1 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.h +++ b/drivers/net/ethernet/ibm/ibmvnic.h @@ -39,6 +39,8 @@ #define IBMVNIC_MAX_LTB_SIZE ((1 << (MAX_ORDER - 1)) * PAGE_SIZE) #define IBMVNIC_BUFFER_HLEN 500 +#define IBMVNIC_RESET_DELAY 100 + static const char ibmvnic_priv_flags[][ETH_GSTRING_LEN] = { #define IBMVNIC_USE_SERVER_MAXES 0x1 "use-server-maxes" @@ -1077,6 +1079,7 @@ struct ibmvnic_adapter { spinlock_t rwi_lock; struct list_head rwi_list; struct work_struct ibmvnic_reset; + struct delayed_work ibmvnic_delayed_reset; bool resetting; bool napi_enabled, from_passive_init; -- 2.16.4 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v3 2/2] net/ibmvnic: prevent more than one thread from running in reset @ 2019-09-17 17:15 ` Juliet Kim 0 siblings, 0 replies; 10+ messages in thread From: Juliet Kim @ 2019-09-17 17:15 UTC (permalink / raw) To: netdev; +Cc: julietk, tlfalcon, linuxppc-dev Signed-off-by: Juliet Kim <julietk@linux.vnet.ibm.com> --- drivers/net/ethernet/ibm/ibmvnic.c | 23 ++++++++++++++++++++++- drivers/net/ethernet/ibm/ibmvnic.h | 3 +++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index ba340aaff1b3..f344ccd68ad9 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -2054,6 +2054,13 @@ static void __ibmvnic_reset(struct work_struct *work) adapter = container_of(work, struct ibmvnic_adapter, ibmvnic_reset); + if (adapter->resetting) { + schedule_delayed_work(&adapter->ibmvnic_delayed_reset, + IBMVNIC_RESET_DELAY); + return; + } + + adapter->resetting = true; reset_state = adapter->state; rwi = get_next_rwi(adapter); @@ -2095,6 +2102,10 @@ static void __ibmvnic_reset(struct work_struct *work) break; rwi = get_next_rwi(adapter); + + if (rwi && (rwi->reset_reason == VNIC_RESET_FAILOVER || + rwi->reset_reason == VNIC_RESET_MOBILITY)) + adapter->force_reset_recovery = true; } if (adapter->wait_for_reset) { @@ -2110,6 +2121,15 @@ static void __ibmvnic_reset(struct work_struct *work) adapter->resetting = false; } +static void __ibmvnic_delayed_reset(struct work_struct *work) +{ + struct ibmvnic_adapter *adapter; + + adapter = container_of(work, struct ibmvnic_adapter, + ibmvnic_delayed_reset.work); + __ibmvnic_reset(&adapter->ibmvnic_reset); +} + static int ibmvnic_reset(struct ibmvnic_adapter *adapter, enum ibmvnic_reset_reason reason) { @@ -2162,7 +2182,6 @@ static int ibmvnic_reset(struct ibmvnic_adapter *adapter, rwi->reset_reason = reason; list_add_tail(&rwi->list, &adapter->rwi_list); spin_unlock_irqrestore(&adapter->rwi_lock, flags); - adapter->resetting = true; netdev_dbg(adapter->netdev, "Scheduling reset (reason %d)\n", reason); schedule_work(&adapter->ibmvnic_reset); @@ -4933,6 +4952,8 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id) spin_lock_init(&adapter->stats_lock); INIT_WORK(&adapter->ibmvnic_reset, __ibmvnic_reset); + INIT_DELAYED_WORK(&adapter->ibmvnic_delayed_reset, + __ibmvnic_delayed_reset); INIT_LIST_HEAD(&adapter->rwi_list); spin_lock_init(&adapter->rwi_lock); init_completion(&adapter->init_done); diff --git a/drivers/net/ethernet/ibm/ibmvnic.h b/drivers/net/ethernet/ibm/ibmvnic.h index 9d3d35cc91d6..4f4651d92cc1 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.h +++ b/drivers/net/ethernet/ibm/ibmvnic.h @@ -39,6 +39,8 @@ #define IBMVNIC_MAX_LTB_SIZE ((1 << (MAX_ORDER - 1)) * PAGE_SIZE) #define IBMVNIC_BUFFER_HLEN 500 +#define IBMVNIC_RESET_DELAY 100 + static const char ibmvnic_priv_flags[][ETH_GSTRING_LEN] = { #define IBMVNIC_USE_SERVER_MAXES 0x1 "use-server-maxes" @@ -1077,6 +1079,7 @@ struct ibmvnic_adapter { spinlock_t rwi_lock; struct list_head rwi_list; struct work_struct ibmvnic_reset; + struct delayed_work ibmvnic_delayed_reset; bool resetting; bool napi_enabled, from_passive_init; -- 2.16.4 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH v3 2/2] net/ibmvnic: prevent more than one thread from running in reset 2019-09-17 17:15 ` Juliet Kim @ 2019-09-18 6:12 ` Michael Ellerman -1 siblings, 0 replies; 10+ messages in thread From: Michael Ellerman @ 2019-09-18 6:12 UTC (permalink / raw) To: Juliet Kim, netdev; +Cc: julietk, tlfalcon, linuxppc-dev Hi Juliet, Juliet Kim <julietk@linux.vnet.ibm.com> writes: > Signed-off-by: Juliet Kim <julietk@linux.vnet.ibm.com> > --- > drivers/net/ethernet/ibm/ibmvnic.c | 23 ++++++++++++++++++++++- > drivers/net/ethernet/ibm/ibmvnic.h | 3 +++ > 2 files changed, 25 insertions(+), 1 deletion(-) > > diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c > index ba340aaff1b3..f344ccd68ad9 100644 > --- a/drivers/net/ethernet/ibm/ibmvnic.c > +++ b/drivers/net/ethernet/ibm/ibmvnic.c > @@ -2054,6 +2054,13 @@ static void __ibmvnic_reset(struct work_struct *work) > > adapter = container_of(work, struct ibmvnic_adapter, ibmvnic_reset); > > + if (adapter->resetting) { > + schedule_delayed_work(&adapter->ibmvnic_delayed_reset, > + IBMVNIC_RESET_DELAY); > + return; > + } > + > + adapter->resetting = true; > reset_state = adapter->state; Is there some locking/serialisation around this? Otherwise that looks very racy. ie. two CPUs could both see adapter->resetting == false, then both set it to true, and then continue executing and stomp on each other. cheers ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v3 2/2] net/ibmvnic: prevent more than one thread from running in reset @ 2019-09-18 6:12 ` Michael Ellerman 0 siblings, 0 replies; 10+ messages in thread From: Michael Ellerman @ 2019-09-18 6:12 UTC (permalink / raw) To: Juliet Kim, netdev; +Cc: julietk, linuxppc-dev, tlfalcon Hi Juliet, Juliet Kim <julietk@linux.vnet.ibm.com> writes: > Signed-off-by: Juliet Kim <julietk@linux.vnet.ibm.com> > --- > drivers/net/ethernet/ibm/ibmvnic.c | 23 ++++++++++++++++++++++- > drivers/net/ethernet/ibm/ibmvnic.h | 3 +++ > 2 files changed, 25 insertions(+), 1 deletion(-) > > diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c > index ba340aaff1b3..f344ccd68ad9 100644 > --- a/drivers/net/ethernet/ibm/ibmvnic.c > +++ b/drivers/net/ethernet/ibm/ibmvnic.c > @@ -2054,6 +2054,13 @@ static void __ibmvnic_reset(struct work_struct *work) > > adapter = container_of(work, struct ibmvnic_adapter, ibmvnic_reset); > > + if (adapter->resetting) { > + schedule_delayed_work(&adapter->ibmvnic_delayed_reset, > + IBMVNIC_RESET_DELAY); > + return; > + } > + > + adapter->resetting = true; > reset_state = adapter->state; Is there some locking/serialisation around this? Otherwise that looks very racy. ie. two CPUs could both see adapter->resetting == false, then both set it to true, and then continue executing and stomp on each other. cheers ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v3 2/2] net/ibmvnic: prevent more than one thread from running in reset 2019-09-18 6:12 ` Michael Ellerman @ 2019-09-18 22:21 ` Juliet Kim -1 siblings, 0 replies; 10+ messages in thread From: Juliet Kim @ 2019-09-18 22:21 UTC (permalink / raw) To: Michael Ellerman, netdev; +Cc: linuxppc-dev, tlfalcon On 9/18/19 1:12 AM, Michael Ellerman wrote: > Hi Juliet, > > Juliet Kim <julietk@linux.vnet.ibm.com> writes: >> Signed-off-by: Juliet Kim <julietk@linux.vnet.ibm.com> >> --- >> drivers/net/ethernet/ibm/ibmvnic.c | 23 ++++++++++++++++++++++- >> drivers/net/ethernet/ibm/ibmvnic.h | 3 +++ >> 2 files changed, 25 insertions(+), 1 deletion(-) >> >> diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c >> index ba340aaff1b3..f344ccd68ad9 100644 >> --- a/drivers/net/ethernet/ibm/ibmvnic.c >> +++ b/drivers/net/ethernet/ibm/ibmvnic.c >> @@ -2054,6 +2054,13 @@ static void __ibmvnic_reset(struct work_struct *work) >> >> adapter = container_of(work, struct ibmvnic_adapter, ibmvnic_reset); >> >> + if (adapter->resetting) { >> + schedule_delayed_work(&adapter->ibmvnic_delayed_reset, >> + IBMVNIC_RESET_DELAY); >> + return; >> + } >> + >> + adapter->resetting = true; >> reset_state = adapter->state; > Is there some locking/serialisation around this? > > Otherwise that looks very racy. ie. two CPUs could both see > adapter->resetting == false, then both set it to true, and then continue > executing and stomp on each other. > > cheers I agree there may be a race here. Thank you for reviewing. I will address it in the next version. ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v3 2/2] net/ibmvnic: prevent more than one thread from running in reset @ 2019-09-18 22:21 ` Juliet Kim 0 siblings, 0 replies; 10+ messages in thread From: Juliet Kim @ 2019-09-18 22:21 UTC (permalink / raw) To: Michael Ellerman, netdev; +Cc: tlfalcon, linuxppc-dev On 9/18/19 1:12 AM, Michael Ellerman wrote: > Hi Juliet, > > Juliet Kim <julietk@linux.vnet.ibm.com> writes: >> Signed-off-by: Juliet Kim <julietk@linux.vnet.ibm.com> >> --- >> drivers/net/ethernet/ibm/ibmvnic.c | 23 ++++++++++++++++++++++- >> drivers/net/ethernet/ibm/ibmvnic.h | 3 +++ >> 2 files changed, 25 insertions(+), 1 deletion(-) >> >> diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c >> index ba340aaff1b3..f344ccd68ad9 100644 >> --- a/drivers/net/ethernet/ibm/ibmvnic.c >> +++ b/drivers/net/ethernet/ibm/ibmvnic.c >> @@ -2054,6 +2054,13 @@ static void __ibmvnic_reset(struct work_struct *work) >> >> adapter = container_of(work, struct ibmvnic_adapter, ibmvnic_reset); >> >> + if (adapter->resetting) { >> + schedule_delayed_work(&adapter->ibmvnic_delayed_reset, >> + IBMVNIC_RESET_DELAY); >> + return; >> + } >> + >> + adapter->resetting = true; >> reset_state = adapter->state; > Is there some locking/serialisation around this? > > Otherwise that looks very racy. ie. two CPUs could both see > adapter->resetting == false, then both set it to true, and then continue > executing and stomp on each other. > > cheers I agree there may be a race here. Thank you for reviewing. I will address it in the next version. ^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2019-09-18 22:23 UTC | newest] Thread overview: 10+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2019-09-17 17:15 [PATCH v3 0/2] net/ibmvnic: serialization fixes Juliet Kim 2019-09-17 17:15 ` Juliet Kim 2019-09-17 17:15 ` [PATCH v3 1/2] net/ibmvnic: unlock rtnl_lock in reset so linkwatch_event can run Juliet Kim 2019-09-17 17:15 ` Juliet Kim 2019-09-17 17:15 ` [PATCH v3 2/2] net/ibmvnic: prevent more than one thread from running in reset Juliet Kim 2019-09-17 17:15 ` Juliet Kim 2019-09-18 6:12 ` Michael Ellerman 2019-09-18 6:12 ` Michael Ellerman 2019-09-18 22:21 ` Juliet Kim 2019-09-18 22:21 ` Juliet Kim
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.