Commit 7b1522cb authored by Krzysztof Opasiak's avatar Krzysztof Opasiak Committed by Ben Hutchings

usb: gadget: f_hid: Use spinlock instead of mutex

commit 33e4c1a9 upstream.

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.
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>
[bwh: Backported to 3.2: adjust filename, context]
Signed-off-by: default avatarBen Hutchings <ben@decadent.org.uk>
parent 29c49277
...@@ -37,11 +37,11 @@ struct f_hidg { ...@@ -37,11 +37,11 @@ struct f_hidg {
/* recv report */ /* recv report */
char *set_report_buff; char *set_report_buff;
unsigned short set_report_length; unsigned short set_report_length;
spinlock_t spinlock; spinlock_t read_spinlock;
wait_queue_head_t read_queue; wait_queue_head_t read_queue;
/* send report */ /* send report */
struct mutex lock; spinlock_t write_spinlock;
bool write_pending; bool write_pending;
wait_queue_head_t write_queue; wait_queue_head_t write_queue;
struct usb_request *req; struct usb_request *req;
...@@ -140,19 +140,19 @@ static ssize_t f_hidg_read(struct file *file, char __user *buffer, ...@@ -140,19 +140,19 @@ static ssize_t f_hidg_read(struct file *file, char __user *buffer,
if (!access_ok(VERIFY_WRITE, buffer, count)) if (!access_ok(VERIFY_WRITE, buffer, count))
return -EFAULT; return -EFAULT;
spin_lock_irqsave(&hidg->spinlock, flags); spin_lock_irqsave(&hidg->read_spinlock, flags);
#define READ_COND (hidg->set_report_buff != NULL) #define READ_COND (hidg->set_report_buff != NULL)
while (!READ_COND) { while (!READ_COND) {
spin_unlock_irqrestore(&hidg->spinlock, flags); spin_unlock_irqrestore(&hidg->read_spinlock, flags);
if (file->f_flags & O_NONBLOCK) if (file->f_flags & O_NONBLOCK)
return -EAGAIN; return -EAGAIN;
if (wait_event_interruptible(hidg->read_queue, READ_COND)) if (wait_event_interruptible(hidg->read_queue, READ_COND))
return -ERESTARTSYS; return -ERESTARTSYS;
spin_lock_irqsave(&hidg->spinlock, flags); spin_lock_irqsave(&hidg->read_spinlock, flags);
} }
...@@ -160,7 +160,7 @@ static ssize_t f_hidg_read(struct file *file, char __user *buffer, ...@@ -160,7 +160,7 @@ static ssize_t f_hidg_read(struct file *file, char __user *buffer,
tmp_buff = hidg->set_report_buff; tmp_buff = hidg->set_report_buff;
hidg->set_report_buff = NULL; hidg->set_report_buff = NULL;
spin_unlock_irqrestore(&hidg->spinlock, flags); spin_unlock_irqrestore(&hidg->read_spinlock, flags);
if (tmp_buff != NULL) { if (tmp_buff != NULL) {
/* copy to user outside spinlock */ /* copy to user outside spinlock */
...@@ -175,13 +175,16 @@ static ssize_t f_hidg_read(struct file *file, char __user *buffer, ...@@ -175,13 +175,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) static void f_hidg_req_complete(struct usb_ep *ep, struct usb_request *req)
{ {
struct f_hidg *hidg = (struct f_hidg *)ep->driver_data; struct f_hidg *hidg = (struct f_hidg *)ep->driver_data;
unsigned long flags;
if (req->status != 0) { if (req->status != 0) {
ERROR(hidg->func.config->cdev, ERROR(hidg->func.config->cdev,
"End Point Request ERROR: %d\n", req->status); "End Point Request ERROR: %d\n", req->status);
} }
spin_lock_irqsave(&hidg->write_spinlock, flags);
hidg->write_pending = 0; hidg->write_pending = 0;
spin_unlock_irqrestore(&hidg->write_spinlock, flags);
wake_up(&hidg->write_queue); wake_up(&hidg->write_queue);
} }
...@@ -189,18 +192,19 @@ static ssize_t f_hidg_write(struct file *file, const char __user *buffer, ...@@ -189,18 +192,19 @@ static ssize_t f_hidg_write(struct file *file, const char __user *buffer,
size_t count, loff_t *offp) size_t count, loff_t *offp)
{ {
struct f_hidg *hidg = file->private_data; struct f_hidg *hidg = file->private_data;
unsigned long flags;
ssize_t status = -ENOMEM; ssize_t status = -ENOMEM;
if (!access_ok(VERIFY_READ, buffer, count)) if (!access_ok(VERIFY_READ, buffer, count))
return -EFAULT; return -EFAULT;
mutex_lock(&hidg->lock); spin_lock_irqsave(&hidg->write_spinlock, flags);
#define WRITE_COND (!hidg->write_pending) #define WRITE_COND (!hidg->write_pending)
/* write queue */ /* write queue */
while (!WRITE_COND) { while (!WRITE_COND) {
mutex_unlock(&hidg->lock); spin_unlock_irqrestore(&hidg->write_spinlock, flags);
if (file->f_flags & O_NONBLOCK) if (file->f_flags & O_NONBLOCK)
return -EAGAIN; return -EAGAIN;
...@@ -208,17 +212,20 @@ static ssize_t f_hidg_write(struct file *file, const char __user *buffer, ...@@ -208,17 +212,20 @@ static ssize_t f_hidg_write(struct file *file, const char __user *buffer,
hidg->write_queue, WRITE_COND)) hidg->write_queue, WRITE_COND))
return -ERESTARTSYS; 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); count = min_t(unsigned, count, hidg->report_length);
spin_unlock_irqrestore(&hidg->write_spinlock, flags);
status = copy_from_user(hidg->req->buf, buffer, count); status = copy_from_user(hidg->req->buf, buffer, count);
if (status != 0) { if (status != 0) {
ERROR(hidg->func.config->cdev, ERROR(hidg->func.config->cdev,
"copy_from_user error\n"); "copy_from_user error\n");
mutex_unlock(&hidg->lock); status = -EINVAL;
return -EINVAL; goto release_write_pending;
} }
hidg->req->status = 0; hidg->req->status = 0;
...@@ -226,19 +233,23 @@ static ssize_t f_hidg_write(struct file *file, const char __user *buffer, ...@@ -226,19 +233,23 @@ static ssize_t f_hidg_write(struct file *file, const char __user *buffer,
hidg->req->length = count; hidg->req->length = count;
hidg->req->complete = f_hidg_req_complete; hidg->req->complete = f_hidg_req_complete;
hidg->req->context = hidg; hidg->req->context = hidg;
hidg->write_pending = 1;
status = usb_ep_queue(hidg->in_ep, hidg->req, GFP_ATOMIC); status = usb_ep_queue(hidg->in_ep, hidg->req, GFP_ATOMIC);
if (status < 0) { if (status < 0) {
ERROR(hidg->func.config->cdev, ERROR(hidg->func.config->cdev,
"usb_ep_queue error on int endpoint %zd\n", status); "usb_ep_queue error on int endpoint %zd\n", status);
hidg->write_pending = 0; goto release_write_pending;
wake_up(&hidg->write_queue);
} else { } else {
status = count; 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; return status;
} }
...@@ -291,19 +302,19 @@ static void hidg_set_report_complete(struct usb_ep *ep, struct usb_request *req) ...@@ -291,19 +302,19 @@ static void hidg_set_report_complete(struct usb_ep *ep, struct usb_request *req)
return; return;
} }
spin_lock(&hidg->spinlock); spin_lock(&hidg->read_spinlock);
hidg->set_report_buff = krealloc(hidg->set_report_buff, hidg->set_report_buff = krealloc(hidg->set_report_buff,
req->actual, GFP_ATOMIC); req->actual, GFP_ATOMIC);
if (hidg->set_report_buff == NULL) { if (hidg->set_report_buff == NULL) {
spin_unlock(&hidg->spinlock); spin_unlock(&hidg->read_spinlock);
return; return;
} }
hidg->set_report_length = req->actual; hidg->set_report_length = req->actual;
memcpy(hidg->set_report_buff, req->buf, req->actual); memcpy(hidg->set_report_buff, req->buf, req->actual);
spin_unlock(&hidg->spinlock); spin_unlock(&hidg->read_spinlock);
wake_up(&hidg->read_queue); wake_up(&hidg->read_queue);
} }
...@@ -505,8 +516,8 @@ static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f) ...@@ -505,8 +516,8 @@ static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f)
goto fail; goto fail;
} }
mutex_init(&hidg->lock); spin_lock_init(&hidg->write_spinlock);
spin_lock_init(&hidg->spinlock); spin_lock_init(&hidg->read_spinlock);
init_waitqueue_head(&hidg->write_queue); init_waitqueue_head(&hidg->write_queue);
init_waitqueue_head(&hidg->read_queue); init_waitqueue_head(&hidg->read_queue);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment