Commit 7d7195a0 authored by Juliet Kim's avatar Juliet Kim Committed by David S. Miller

ibmvnic: Do not process device remove during device reset

The ibmvnic driver does not check the device state when the device
is removed. If the device is removed while a device reset is being
processed, the remove may free structures needed by the reset,
causing an oops.

Fix this by checking the device state before processing device remove.
Signed-off-by: default avatarJuliet Kim <julietk@linux.vnet.ibm.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent ece0d7bd
...@@ -2142,6 +2142,8 @@ static void __ibmvnic_reset(struct work_struct *work) ...@@ -2142,6 +2142,8 @@ static void __ibmvnic_reset(struct work_struct *work)
{ {
struct ibmvnic_rwi *rwi; struct ibmvnic_rwi *rwi;
struct ibmvnic_adapter *adapter; struct ibmvnic_adapter *adapter;
bool saved_state = false;
unsigned long flags;
u32 reset_state; u32 reset_state;
int rc = 0; int rc = 0;
...@@ -2153,17 +2155,25 @@ static void __ibmvnic_reset(struct work_struct *work) ...@@ -2153,17 +2155,25 @@ static void __ibmvnic_reset(struct work_struct *work)
return; return;
} }
reset_state = adapter->state;
rwi = get_next_rwi(adapter); rwi = get_next_rwi(adapter);
while (rwi) { while (rwi) {
spin_lock_irqsave(&adapter->state_lock, flags);
if (adapter->state == VNIC_REMOVING || if (adapter->state == VNIC_REMOVING ||
adapter->state == VNIC_REMOVED) { adapter->state == VNIC_REMOVED) {
spin_unlock_irqrestore(&adapter->state_lock, flags);
kfree(rwi); kfree(rwi);
rc = EBUSY; rc = EBUSY;
break; break;
} }
if (!saved_state) {
reset_state = adapter->state;
adapter->state = VNIC_RESETTING;
saved_state = true;
}
spin_unlock_irqrestore(&adapter->state_lock, flags);
if (rwi->reset_reason == VNIC_RESET_CHANGE_PARAM) { if (rwi->reset_reason == VNIC_RESET_CHANGE_PARAM) {
/* CHANGE_PARAM requestor holds rtnl_lock */ /* CHANGE_PARAM requestor holds rtnl_lock */
rc = do_change_param_reset(adapter, rwi, reset_state); rc = do_change_param_reset(adapter, rwi, reset_state);
...@@ -5091,6 +5101,7 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id) ...@@ -5091,6 +5101,7 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id)
__ibmvnic_delayed_reset); __ibmvnic_delayed_reset);
INIT_LIST_HEAD(&adapter->rwi_list); INIT_LIST_HEAD(&adapter->rwi_list);
spin_lock_init(&adapter->rwi_lock); spin_lock_init(&adapter->rwi_lock);
spin_lock_init(&adapter->state_lock);
mutex_init(&adapter->fw_lock); mutex_init(&adapter->fw_lock);
init_completion(&adapter->init_done); init_completion(&adapter->init_done);
init_completion(&adapter->fw_done); init_completion(&adapter->fw_done);
...@@ -5163,8 +5174,17 @@ static int ibmvnic_remove(struct vio_dev *dev) ...@@ -5163,8 +5174,17 @@ static int ibmvnic_remove(struct vio_dev *dev)
{ {
struct net_device *netdev = dev_get_drvdata(&dev->dev); struct net_device *netdev = dev_get_drvdata(&dev->dev);
struct ibmvnic_adapter *adapter = netdev_priv(netdev); struct ibmvnic_adapter *adapter = netdev_priv(netdev);
unsigned long flags;
spin_lock_irqsave(&adapter->state_lock, flags);
if (adapter->state == VNIC_RESETTING) {
spin_unlock_irqrestore(&adapter->state_lock, flags);
return -EBUSY;
}
adapter->state = VNIC_REMOVING; adapter->state = VNIC_REMOVING;
spin_unlock_irqrestore(&adapter->state_lock, flags);
rtnl_lock(); rtnl_lock();
unregister_netdevice(netdev); unregister_netdevice(netdev);
......
...@@ -941,7 +941,8 @@ enum vnic_state {VNIC_PROBING = 1, ...@@ -941,7 +941,8 @@ enum vnic_state {VNIC_PROBING = 1,
VNIC_CLOSING, VNIC_CLOSING,
VNIC_CLOSED, VNIC_CLOSED,
VNIC_REMOVING, VNIC_REMOVING,
VNIC_REMOVED}; VNIC_REMOVED,
VNIC_RESETTING};
enum ibmvnic_reset_reason {VNIC_RESET_FAILOVER = 1, enum ibmvnic_reset_reason {VNIC_RESET_FAILOVER = 1,
VNIC_RESET_MOBILITY, VNIC_RESET_MOBILITY,
...@@ -1090,4 +1091,7 @@ struct ibmvnic_adapter { ...@@ -1090,4 +1091,7 @@ struct ibmvnic_adapter {
struct ibmvnic_tunables desired; struct ibmvnic_tunables desired;
struct ibmvnic_tunables fallback; struct ibmvnic_tunables fallback;
/* Used for serializatin of state field */
spinlock_t state_lock;
}; };
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment