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:
Родитель
ece0d7bd74
Коммит
7d7195a026
|
@ -2142,6 +2142,8 @@ static void __ibmvnic_reset(struct work_struct *work)
|
|||
{
|
||||
struct ibmvnic_rwi *rwi;
|
||||
struct ibmvnic_adapter *adapter;
|
||||
bool saved_state = false;
|
||||
unsigned long flags;
|
||||
u32 reset_state;
|
||||
int rc = 0;
|
||||
|
||||
|
@ -2153,17 +2155,25 @@ static void __ibmvnic_reset(struct work_struct *work)
|
|||
return;
|
||||
}
|
||||
|
||||
reset_state = adapter->state;
|
||||
|
||||
rwi = get_next_rwi(adapter);
|
||||
while (rwi) {
|
||||
spin_lock_irqsave(&adapter->state_lock, flags);
|
||||
|
||||
if (adapter->state == VNIC_REMOVING ||
|
||||
adapter->state == VNIC_REMOVED) {
|
||||
spin_unlock_irqrestore(&adapter->state_lock, flags);
|
||||
kfree(rwi);
|
||||
rc = EBUSY;
|
||||
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) {
|
||||
/* CHANGE_PARAM requestor holds rtnl_lock */
|
||||
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);
|
||||
INIT_LIST_HEAD(&adapter->rwi_list);
|
||||
spin_lock_init(&adapter->rwi_lock);
|
||||
spin_lock_init(&adapter->state_lock);
|
||||
mutex_init(&adapter->fw_lock);
|
||||
init_completion(&adapter->init_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 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;
|
||||
spin_unlock_irqrestore(&adapter->state_lock, flags);
|
||||
|
||||
rtnl_lock();
|
||||
unregister_netdevice(netdev);
|
||||
|
||||
|
|
|
@ -941,7 +941,8 @@ enum vnic_state {VNIC_PROBING = 1,
|
|||
VNIC_CLOSING,
|
||||
VNIC_CLOSED,
|
||||
VNIC_REMOVING,
|
||||
VNIC_REMOVED};
|
||||
VNIC_REMOVED,
|
||||
VNIC_RESETTING};
|
||||
|
||||
enum ibmvnic_reset_reason {VNIC_RESET_FAILOVER = 1,
|
||||
VNIC_RESET_MOBILITY,
|
||||
|
@ -1090,4 +1091,7 @@ struct ibmvnic_adapter {
|
|||
|
||||
struct ibmvnic_tunables desired;
|
||||
struct ibmvnic_tunables fallback;
|
||||
|
||||
/* Used for serializatin of state field */
|
||||
spinlock_t state_lock;
|
||||
};
|
||||
|
|
Загрузка…
Ссылка в новой задаче