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

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
Loading
Loading
Loading
Loading
+22 −2
Original line number Diff line number Diff line
@@ -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);

+5 −1
Original line number Diff line number Diff line
@@ -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;
};