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

Commit 33e4c1a9 authored by Krzysztof Opasiak's avatar Krzysztof Opasiak Committed by Felipe Balbi
Browse files

usb: gadget: f_hid: Use spinlock instead of mutex



As IN request has to be allocated in set_alt() and released in
disable() we cannot use mutex to protect it as we cannot sleep
in those funcitons. Let's replace this mutex with a spinlock.

Cc: stable@vger.kernel.org
Tested-by: default avatarDavid Lechner <david@lechnology.com>
Signed-off-by: default avatarKrzysztof Opasiak <k.opasiak@samsung.com>
Signed-off-by: default avatarFelipe Balbi <felipe.balbi@linux.intel.com>
parent aa65d11a
Loading
Loading
Loading
Loading
+34 −23
Original line number Diff line number Diff line
@@ -50,12 +50,12 @@ struct f_hidg {

	/* recv report */
	struct list_head		completed_out_req;
	spinlock_t			spinlock;
	spinlock_t			read_spinlock;
	wait_queue_head_t		read_queue;
	unsigned int			qlen;

	/* send report */
	struct mutex			lock;
	spinlock_t			write_spinlock;
	bool				write_pending;
	wait_queue_head_t		write_queue;
	struct usb_request		*req;
@@ -258,20 +258,20 @@ static ssize_t f_hidg_read(struct file *file, char __user *buffer,
	if (!access_ok(VERIFY_WRITE, buffer, count))
		return -EFAULT;

	spin_lock_irqsave(&hidg->spinlock, flags);
	spin_lock_irqsave(&hidg->read_spinlock, flags);

#define READ_COND (!list_empty(&hidg->completed_out_req))

	/* wait for at least one buffer to complete */
	while (!READ_COND) {
		spin_unlock_irqrestore(&hidg->spinlock, flags);
		spin_unlock_irqrestore(&hidg->read_spinlock, flags);
		if (file->f_flags & O_NONBLOCK)
			return -EAGAIN;

		if (wait_event_interruptible(hidg->read_queue, READ_COND))
			return -ERESTARTSYS;

		spin_lock_irqsave(&hidg->spinlock, flags);
		spin_lock_irqsave(&hidg->read_spinlock, flags);
	}

	/* pick the first one */
@@ -286,7 +286,7 @@ static ssize_t f_hidg_read(struct file *file, char __user *buffer,

	req = list->req;
	count = min_t(unsigned int, count, req->actual - list->pos);
	spin_unlock_irqrestore(&hidg->spinlock, flags);
	spin_unlock_irqrestore(&hidg->read_spinlock, flags);

	/* copy to user outside spinlock */
	count -= copy_to_user(buffer, req->buf + list->pos, count);
@@ -308,9 +308,9 @@ static ssize_t f_hidg_read(struct file *file, char __user *buffer,
			return ret;
		}
	} else {
		spin_lock_irqsave(&hidg->spinlock, flags);
		spin_lock_irqsave(&hidg->read_spinlock, flags);
		list_add(&list->list, &hidg->completed_out_req);
		spin_unlock_irqrestore(&hidg->spinlock, flags);
		spin_unlock_irqrestore(&hidg->read_spinlock, flags);

		wake_up(&hidg->read_queue);
	}
@@ -321,13 +321,16 @@ static ssize_t f_hidg_read(struct file *file, char __user *buffer,
static void f_hidg_req_complete(struct usb_ep *ep, struct usb_request *req)
{
	struct f_hidg *hidg = (struct f_hidg *)ep->driver_data;
	unsigned long flags;

	if (req->status != 0) {
		ERROR(hidg->func.config->cdev,
			"End Point Request ERROR: %d\n", req->status);
	}

	spin_lock_irqsave(&hidg->write_spinlock, flags);
	hidg->write_pending = 0;
	spin_unlock_irqrestore(&hidg->write_spinlock, flags);
	wake_up(&hidg->write_queue);
}

@@ -335,18 +338,19 @@ static ssize_t f_hidg_write(struct file *file, const char __user *buffer,
			    size_t count, loff_t *offp)
{
	struct f_hidg *hidg  = file->private_data;
	unsigned long flags;
	ssize_t status = -ENOMEM;

	if (!access_ok(VERIFY_READ, buffer, count))
		return -EFAULT;

	mutex_lock(&hidg->lock);
	spin_lock_irqsave(&hidg->write_spinlock, flags);

#define WRITE_COND (!hidg->write_pending)

	/* write queue */
	while (!WRITE_COND) {
		mutex_unlock(&hidg->lock);
		spin_unlock_irqrestore(&hidg->write_spinlock, flags);
		if (file->f_flags & O_NONBLOCK)
			return -EAGAIN;

@@ -354,17 +358,20 @@ static ssize_t f_hidg_write(struct file *file, const char __user *buffer,
				hidg->write_queue, WRITE_COND))
			return -ERESTARTSYS;

		mutex_lock(&hidg->lock);
		spin_lock_irqsave(&hidg->write_spinlock, flags);
	}

	hidg->write_pending = 1;
	count  = min_t(unsigned, count, hidg->report_length);

	spin_unlock_irqrestore(&hidg->write_spinlock, flags);
	status = copy_from_user(hidg->req->buf, buffer, count);

	if (status != 0) {
		ERROR(hidg->func.config->cdev,
			"copy_from_user error\n");
		mutex_unlock(&hidg->lock);
		return -EINVAL;
		status = -EINVAL;
		goto release_write_pending;
	}

	hidg->req->status   = 0;
@@ -372,19 +379,23 @@ static ssize_t f_hidg_write(struct file *file, const char __user *buffer,
	hidg->req->length   = count;
	hidg->req->complete = f_hidg_req_complete;
	hidg->req->context  = hidg;
	hidg->write_pending = 1;

	status = usb_ep_queue(hidg->in_ep, hidg->req, GFP_ATOMIC);
	if (status < 0) {
		ERROR(hidg->func.config->cdev,
			"usb_ep_queue error on int endpoint %zd\n", status);
		hidg->write_pending = 0;
		wake_up(&hidg->write_queue);
		goto release_write_pending;
	} else {
		status = count;
	}

	mutex_unlock(&hidg->lock);
	return status;
release_write_pending:
	spin_lock_irqsave(&hidg->write_spinlock, flags);
	hidg->write_pending = 0;
	spin_unlock_irqrestore(&hidg->write_spinlock, flags);

	wake_up(&hidg->write_queue);

	return status;
}
@@ -451,9 +462,9 @@ static void hidg_set_report_complete(struct usb_ep *ep, struct usb_request *req)

		req_list->req = req;

		spin_lock_irqsave(&hidg->spinlock, flags);
		spin_lock_irqsave(&hidg->read_spinlock, flags);
		list_add_tail(&req_list->list, &hidg->completed_out_req);
		spin_unlock_irqrestore(&hidg->spinlock, flags);
		spin_unlock_irqrestore(&hidg->read_spinlock, flags);

		wake_up(&hidg->read_queue);
		break;
@@ -577,13 +588,13 @@ static void hidg_disable(struct usb_function *f)
	usb_ep_disable(hidg->in_ep);
	usb_ep_disable(hidg->out_ep);

	spin_lock_irqsave(&hidg->spinlock, flags);
	spin_lock_irqsave(&hidg->read_spinlock, flags);
	list_for_each_entry_safe(list, next, &hidg->completed_out_req, list) {
		free_ep_req(hidg->out_ep, list->req);
		list_del(&list->list);
		kfree(list);
	}
	spin_unlock_irqrestore(&hidg->spinlock, flags);
	spin_unlock_irqrestore(&hidg->read_spinlock, flags);
}

static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
@@ -743,8 +754,8 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
	if (status)
		goto fail;

	mutex_init(&hidg->lock);
	spin_lock_init(&hidg->spinlock);
	spin_lock_init(&hidg->write_spinlock);
	spin_lock_init(&hidg->read_spinlock);
	init_waitqueue_head(&hidg->write_queue);
	init_waitqueue_head(&hidg->read_queue);
	INIT_LIST_HEAD(&hidg->completed_out_req);