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: Juliet Kim <julietk@linux.vnet.ibm.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
ece0d7bd74
commit
7d7195a026
@ -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)
|
|||||||
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)
|
|||||||
__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)
|
|||||||
{
|
{
|
||||||
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,
|
|||||||
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 {
|
|||||||
|
|
||||||
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;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user