Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit dcf14779 authored by Harvey Yang's avatar Harvey Yang Committed by Greg Kroah-Hartman
Browse files

staging: usbip: use interrupt safe spinlock to avoid potential deadlock.



The function 'usbip_event_add()' may be called in interrupt context on
the stub side:
'stub_complete'->'stub_enqueue_ret_unlink'->'usbip_event_add'.
In this function it tries to get the lock 'ud->lock', so we should
disable irq when we get this lock in process context.

Signed-off-by: default avatarHarvey Yang <harvey.huawei.yang@gmail.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 26ef1021
Loading
Loading
Loading
Loading
+17 −17
Original line number Diff line number Diff line
@@ -67,9 +67,9 @@ static ssize_t show_status(struct device *dev, struct device_attribute *attr,
		return -ENODEV;
	}

	spin_lock(&sdev->ud.lock);
	spin_lock_irq(&sdev->ud.lock);
	status = sdev->ud.status;
	spin_unlock(&sdev->ud.lock);
	spin_unlock_irq(&sdev->ud.lock);

	return snprintf(buf, PAGE_SIZE, "%d\n", status);
}
@@ -97,39 +97,39 @@ static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr,
	if (sockfd != -1) {
		dev_info(dev, "stub up\n");

		spin_lock(&sdev->ud.lock);
		spin_lock_irq(&sdev->ud.lock);

		if (sdev->ud.status != SDEV_ST_AVAILABLE) {
			dev_err(dev, "not ready\n");
			spin_unlock(&sdev->ud.lock);
			spin_unlock_irq(&sdev->ud.lock);
			return -EINVAL;
		}

		socket = sockfd_to_socket(sockfd);
		if (!socket) {
			spin_unlock(&sdev->ud.lock);
			spin_unlock_irq(&sdev->ud.lock);
			return -EINVAL;
		}
		sdev->ud.tcp_socket = socket;

		spin_unlock(&sdev->ud.lock);
		spin_unlock_irq(&sdev->ud.lock);

		sdev->ud.tcp_rx = kthread_get_run(stub_rx_loop, &sdev->ud, "stub_rx");
		sdev->ud.tcp_tx = kthread_get_run(stub_tx_loop, &sdev->ud, "stub_tx");

		spin_lock(&sdev->ud.lock);
		spin_lock_irq(&sdev->ud.lock);
		sdev->ud.status = SDEV_ST_USED;
		spin_unlock(&sdev->ud.lock);
		spin_unlock_irq(&sdev->ud.lock);

	} else {
		dev_info(dev, "stub down\n");

		spin_lock(&sdev->ud.lock);
		spin_lock_irq(&sdev->ud.lock);
		if (sdev->ud.status != SDEV_ST_USED) {
			spin_unlock(&sdev->ud.lock);
			spin_unlock_irq(&sdev->ud.lock);
			return -EINVAL;
		}
		spin_unlock(&sdev->ud.lock);
		spin_unlock_irq(&sdev->ud.lock);

		usbip_event_add(&sdev->ud, SDEV_EVENT_DOWN);
	}
@@ -241,9 +241,9 @@ static void stub_device_reset(struct usbip_device *ud)
	ret = usb_lock_device_for_reset(udev, sdev->interface);
	if (ret < 0) {
		dev_err(&udev->dev, "lock for reset\n");
		spin_lock(&ud->lock);
		spin_lock_irq(&ud->lock);
		ud->status = SDEV_ST_ERROR;
		spin_unlock(&ud->lock);
		spin_unlock_irq(&ud->lock);
		return;
	}

@@ -251,7 +251,7 @@ static void stub_device_reset(struct usbip_device *ud)
	ret = usb_reset_device(udev);
	usb_unlock_device(udev);

	spin_lock(&ud->lock);
	spin_lock_irq(&ud->lock);
	if (ret) {
		dev_err(&udev->dev, "device reset\n");
		ud->status = SDEV_ST_ERROR;
@@ -259,14 +259,14 @@ static void stub_device_reset(struct usbip_device *ud)
		dev_info(&udev->dev, "device reset\n");
		ud->status = SDEV_ST_AVAILABLE;
	}
	spin_unlock(&ud->lock);
	spin_unlock_irq(&ud->lock);
}

static void stub_device_unusable(struct usbip_device *ud)
{
	spin_lock(&ud->lock);
	spin_lock_irq(&ud->lock);
	ud->status = SDEV_ST_ERROR;
	spin_unlock(&ud->lock);
	spin_unlock_irq(&ud->lock);
}

/**
+2 −2
Original line number Diff line number Diff line
@@ -307,12 +307,12 @@ static int valid_request(struct stub_device *sdev, struct usbip_header *pdu)
	int valid = 0;

	if (pdu->base.devid == sdev->devid) {
		spin_lock(&ud->lock);
		spin_lock_irq(&ud->lock);
		if (ud->status == SDEV_ST_USED) {
			/* A request is valid. */
			valid = 1;
		}
		spin_unlock(&ud->lock);
		spin_unlock_irq(&ud->lock);
	}

	return valid;
+4 −2
Original line number Diff line number Diff line
@@ -105,10 +105,12 @@ EXPORT_SYMBOL_GPL(usbip_stop_eh);

void usbip_event_add(struct usbip_device *ud, unsigned long event)
{
	spin_lock(&ud->lock);
	unsigned long flags;

	spin_lock_irqsave(&ud->lock, flags);
	ud->event |= event;
	wake_up(&ud->eh_waitq);
	spin_unlock(&ud->lock);
	spin_unlock_irqrestore(&ud->lock, flags);
}
EXPORT_SYMBOL_GPL(usbip_event_add);