Commit f788baad authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'gadget' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs

Pull gadgetfs fixes from Al Viro:
 "Assorted fixes around AIO on gadgetfs: leaks, use-after-free, troubles
  caused by ->f_op flipping"

* 'gadget' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  gadgetfs: really get rid of switching ->f_op
  gadgetfs: get rid of flipping ->f_op in ep_config()
  gadget: switch ep_io_operations to ->read_iter/->write_iter
  gadgetfs: use-after-free in ->aio_read()
  gadget/function/f_fs.c: switch to ->{read,write}_iter()
  gadget/function/f_fs.c: use put iov_iter into io_data
  gadget/function/f_fs.c: close leaks
  move iov_iter.c from mm/ to lib/
  new helper: dup_iter()
parents c202baf0 96b62a57
...@@ -144,10 +144,9 @@ struct ffs_io_data { ...@@ -144,10 +144,9 @@ struct ffs_io_data {
bool read; bool read;
struct kiocb *kiocb; struct kiocb *kiocb;
const struct iovec *iovec; struct iov_iter data;
unsigned long nr_segs; const void *to_free;
char __user *buf; char *buf;
size_t len;
struct mm_struct *mm; struct mm_struct *mm;
struct work_struct work; struct work_struct work;
...@@ -649,29 +648,10 @@ static void ffs_user_copy_worker(struct work_struct *work) ...@@ -649,29 +648,10 @@ static void ffs_user_copy_worker(struct work_struct *work)
io_data->req->actual; io_data->req->actual;
if (io_data->read && ret > 0) { if (io_data->read && ret > 0) {
int i;
size_t pos = 0;
/*
* Since req->length may be bigger than io_data->len (after
* being rounded up to maxpacketsize), we may end up with more
* data then user space has space for.
*/
ret = min_t(int, ret, io_data->len);
use_mm(io_data->mm); use_mm(io_data->mm);
for (i = 0; i < io_data->nr_segs; i++) { ret = copy_to_iter(io_data->buf, ret, &io_data->data);
size_t len = min_t(size_t, ret - pos, if (iov_iter_count(&io_data->data))
io_data->iovec[i].iov_len);
if (!len)
break;
if (unlikely(copy_to_user(io_data->iovec[i].iov_base,
&io_data->buf[pos], len))) {
ret = -EFAULT; ret = -EFAULT;
break;
}
pos += len;
}
unuse_mm(io_data->mm); unuse_mm(io_data->mm);
} }
...@@ -684,7 +664,7 @@ static void ffs_user_copy_worker(struct work_struct *work) ...@@ -684,7 +664,7 @@ static void ffs_user_copy_worker(struct work_struct *work)
io_data->kiocb->private = NULL; io_data->kiocb->private = NULL;
if (io_data->read) if (io_data->read)
kfree(io_data->iovec); kfree(io_data->to_free);
kfree(io_data->buf); kfree(io_data->buf);
kfree(io_data); kfree(io_data);
} }
...@@ -743,6 +723,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) ...@@ -743,6 +723,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
* before the waiting completes, so do not assign to 'gadget' earlier * before the waiting completes, so do not assign to 'gadget' earlier
*/ */
struct usb_gadget *gadget = epfile->ffs->gadget; struct usb_gadget *gadget = epfile->ffs->gadget;
size_t copied;
spin_lock_irq(&epfile->ffs->eps_lock); spin_lock_irq(&epfile->ffs->eps_lock);
/* In the meantime, endpoint got disabled or changed. */ /* In the meantime, endpoint got disabled or changed. */
...@@ -750,34 +731,21 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) ...@@ -750,34 +731,21 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
spin_unlock_irq(&epfile->ffs->eps_lock); spin_unlock_irq(&epfile->ffs->eps_lock);
return -ESHUTDOWN; return -ESHUTDOWN;
} }
data_len = iov_iter_count(&io_data->data);
/* /*
* Controller may require buffer size to be aligned to * Controller may require buffer size to be aligned to
* maxpacketsize of an out endpoint. * maxpacketsize of an out endpoint.
*/ */
data_len = io_data->read ? if (io_data->read)
usb_ep_align_maybe(gadget, ep->ep, io_data->len) : data_len = usb_ep_align_maybe(gadget, ep->ep, data_len);
io_data->len;
spin_unlock_irq(&epfile->ffs->eps_lock); spin_unlock_irq(&epfile->ffs->eps_lock);
data = kmalloc(data_len, GFP_KERNEL); data = kmalloc(data_len, GFP_KERNEL);
if (unlikely(!data)) if (unlikely(!data))
return -ENOMEM; return -ENOMEM;
if (io_data->aio && !io_data->read) { if (!io_data->read) {
int i; copied = copy_from_iter(data, data_len, &io_data->data);
size_t pos = 0; if (copied != data_len) {
for (i = 0; i < io_data->nr_segs; i++) {
if (unlikely(copy_from_user(&data[pos],
io_data->iovec[i].iov_base,
io_data->iovec[i].iov_len))) {
ret = -EFAULT;
goto error;
}
pos += io_data->iovec[i].iov_len;
}
} else {
if (!io_data->read &&
unlikely(__copy_from_user(data, io_data->buf,
io_data->len))) {
ret = -EFAULT; ret = -EFAULT;
goto error; goto error;
} }
...@@ -876,10 +844,8 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) ...@@ -876,10 +844,8 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
*/ */
ret = ep->status; ret = ep->status;
if (io_data->read && ret > 0) { if (io_data->read && ret > 0) {
ret = min_t(size_t, ret, io_data->len); ret = copy_to_iter(data, ret, &io_data->data);
if (unlikely(iov_iter_count(&io_data->data)))
if (unlikely(copy_to_user(io_data->buf,
data, ret)))
ret = -EFAULT; ret = -EFAULT;
} }
} }
...@@ -898,37 +864,6 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) ...@@ -898,37 +864,6 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
return ret; return ret;
} }
static ssize_t
ffs_epfile_write(struct file *file, const char __user *buf, size_t len,
loff_t *ptr)
{
struct ffs_io_data io_data;
ENTER();
io_data.aio = false;
io_data.read = false;
io_data.buf = (char * __user)buf;
io_data.len = len;
return ffs_epfile_io(file, &io_data);
}
static ssize_t
ffs_epfile_read(struct file *file, char __user *buf, size_t len, loff_t *ptr)
{
struct ffs_io_data io_data;
ENTER();
io_data.aio = false;
io_data.read = true;
io_data.buf = buf;
io_data.len = len;
return ffs_epfile_io(file, &io_data);
}
static int static int
ffs_epfile_open(struct inode *inode, struct file *file) ffs_epfile_open(struct inode *inode, struct file *file)
{ {
...@@ -965,67 +900,86 @@ static int ffs_aio_cancel(struct kiocb *kiocb) ...@@ -965,67 +900,86 @@ static int ffs_aio_cancel(struct kiocb *kiocb)
return value; return value;
} }
static ssize_t ffs_epfile_aio_write(struct kiocb *kiocb, static ssize_t ffs_epfile_write_iter(struct kiocb *kiocb, struct iov_iter *from)
const struct iovec *iovec,
unsigned long nr_segs, loff_t loff)
{ {
struct ffs_io_data *io_data; struct ffs_io_data io_data, *p = &io_data;
ssize_t res;
ENTER(); ENTER();
io_data = kmalloc(sizeof(*io_data), GFP_KERNEL); if (!is_sync_kiocb(kiocb)) {
if (unlikely(!io_data)) p = kmalloc(sizeof(io_data), GFP_KERNEL);
if (unlikely(!p))
return -ENOMEM; return -ENOMEM;
p->aio = true;
} else {
p->aio = false;
}
io_data->aio = true; p->read = false;
io_data->read = false; p->kiocb = kiocb;
io_data->kiocb = kiocb; p->data = *from;
io_data->iovec = iovec; p->mm = current->mm;
io_data->nr_segs = nr_segs;
io_data->len = kiocb->ki_nbytes;
io_data->mm = current->mm;
kiocb->private = io_data; kiocb->private = p;
kiocb_set_cancel_fn(kiocb, ffs_aio_cancel); kiocb_set_cancel_fn(kiocb, ffs_aio_cancel);
return ffs_epfile_io(kiocb->ki_filp, io_data); res = ffs_epfile_io(kiocb->ki_filp, p);
if (res == -EIOCBQUEUED)
return res;
if (p->aio)
kfree(p);
else
*from = p->data;
return res;
} }
static ssize_t ffs_epfile_aio_read(struct kiocb *kiocb, static ssize_t ffs_epfile_read_iter(struct kiocb *kiocb, struct iov_iter *to)
const struct iovec *iovec,
unsigned long nr_segs, loff_t loff)
{ {
struct ffs_io_data *io_data; struct ffs_io_data io_data, *p = &io_data;
struct iovec *iovec_copy; ssize_t res;
ENTER(); ENTER();
iovec_copy = kmalloc_array(nr_segs, sizeof(*iovec_copy), GFP_KERNEL); if (!is_sync_kiocb(kiocb)) {
if (unlikely(!iovec_copy)) p = kmalloc(sizeof(io_data), GFP_KERNEL);
if (unlikely(!p))
return -ENOMEM; return -ENOMEM;
p->aio = true;
} else {
p->aio = false;
}
memcpy(iovec_copy, iovec, sizeof(struct iovec)*nr_segs); p->read = true;
p->kiocb = kiocb;
io_data = kmalloc(sizeof(*io_data), GFP_KERNEL); if (p->aio) {
if (unlikely(!io_data)) { p->to_free = dup_iter(&p->data, to, GFP_KERNEL);
kfree(iovec_copy); if (!p->to_free) {
kfree(p);
return -ENOMEM; return -ENOMEM;
} }
} else {
p->data = *to;
p->to_free = NULL;
}
p->mm = current->mm;
io_data->aio = true; kiocb->private = p;
io_data->read = true;
io_data->kiocb = kiocb;
io_data->iovec = iovec_copy;
io_data->nr_segs = nr_segs;
io_data->len = kiocb->ki_nbytes;
io_data->mm = current->mm;
kiocb->private = io_data;
kiocb_set_cancel_fn(kiocb, ffs_aio_cancel); kiocb_set_cancel_fn(kiocb, ffs_aio_cancel);
return ffs_epfile_io(kiocb->ki_filp, io_data); res = ffs_epfile_io(kiocb->ki_filp, p);
if (res == -EIOCBQUEUED)
return res;
if (p->aio) {
kfree(p->to_free);
kfree(p);
} else {
*to = p->data;
}
return res;
} }
static int static int
...@@ -1105,10 +1059,10 @@ static const struct file_operations ffs_epfile_operations = { ...@@ -1105,10 +1059,10 @@ static const struct file_operations ffs_epfile_operations = {
.llseek = no_llseek, .llseek = no_llseek,
.open = ffs_epfile_open, .open = ffs_epfile_open,
.write = ffs_epfile_write, .write = new_sync_write,
.read = ffs_epfile_read, .read = new_sync_read,
.aio_write = ffs_epfile_aio_write, .write_iter = ffs_epfile_write_iter,
.aio_read = ffs_epfile_aio_read, .read_iter = ffs_epfile_read_iter,
.release = ffs_epfile_release, .release = ffs_epfile_release,
.unlocked_ioctl = ffs_epfile_ioctl, .unlocked_ioctl = ffs_epfile_ioctl,
}; };
......
...@@ -74,6 +74,8 @@ MODULE_DESCRIPTION (DRIVER_DESC); ...@@ -74,6 +74,8 @@ MODULE_DESCRIPTION (DRIVER_DESC);
MODULE_AUTHOR ("David Brownell"); MODULE_AUTHOR ("David Brownell");
MODULE_LICENSE ("GPL"); MODULE_LICENSE ("GPL");
static int ep_open(struct inode *, struct file *);
/*----------------------------------------------------------------------*/ /*----------------------------------------------------------------------*/
...@@ -283,14 +285,15 @@ static void epio_complete (struct usb_ep *ep, struct usb_request *req) ...@@ -283,14 +285,15 @@ static void epio_complete (struct usb_ep *ep, struct usb_request *req)
* still need dev->lock to use epdata->ep. * still need dev->lock to use epdata->ep.
*/ */
static int static int
get_ready_ep (unsigned f_flags, struct ep_data *epdata) get_ready_ep (unsigned f_flags, struct ep_data *epdata, bool is_write)
{ {
int val; int val;
if (f_flags & O_NONBLOCK) { if (f_flags & O_NONBLOCK) {
if (!mutex_trylock(&epdata->lock)) if (!mutex_trylock(&epdata->lock))
goto nonblock; goto nonblock;
if (epdata->state != STATE_EP_ENABLED) { if (epdata->state != STATE_EP_ENABLED &&
(!is_write || epdata->state != STATE_EP_READY)) {
mutex_unlock(&epdata->lock); mutex_unlock(&epdata->lock);
nonblock: nonblock:
val = -EAGAIN; val = -EAGAIN;
...@@ -305,18 +308,20 @@ get_ready_ep (unsigned f_flags, struct ep_data *epdata) ...@@ -305,18 +308,20 @@ get_ready_ep (unsigned f_flags, struct ep_data *epdata)
switch (epdata->state) { switch (epdata->state) {
case STATE_EP_ENABLED: case STATE_EP_ENABLED:
return 0;
case STATE_EP_READY: /* not configured yet */
if (is_write)
return 0;
// FALLTHRU
case STATE_EP_UNBOUND: /* clean disconnect */
break; break;
// case STATE_EP_DISABLED: /* "can't happen" */ // case STATE_EP_DISABLED: /* "can't happen" */
// case STATE_EP_READY: /* "can't happen" */
default: /* error! */ default: /* error! */
pr_debug ("%s: ep %p not available, state %d\n", pr_debug ("%s: ep %p not available, state %d\n",
shortname, epdata, epdata->state); shortname, epdata, epdata->state);
// FALLTHROUGH
case STATE_EP_UNBOUND: /* clean disconnect */
val = -ENODEV;
mutex_unlock(&epdata->lock);
} }
return val; mutex_unlock(&epdata->lock);
return -ENODEV;
} }
static ssize_t static ssize_t
...@@ -363,97 +368,6 @@ ep_io (struct ep_data *epdata, void *buf, unsigned len) ...@@ -363,97 +368,6 @@ ep_io (struct ep_data *epdata, void *buf, unsigned len)
return value; return value;
} }
/* handle a synchronous OUT bulk/intr/iso transfer */
static ssize_t
ep_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
{
struct ep_data *data = fd->private_data;
void *kbuf;
ssize_t value;
if ((value = get_ready_ep (fd->f_flags, data)) < 0)
return value;
/* halt any endpoint by doing a "wrong direction" i/o call */
if (usb_endpoint_dir_in(&data->desc)) {
if (usb_endpoint_xfer_isoc(&data->desc)) {
mutex_unlock(&data->lock);
return -EINVAL;
}
DBG (data->dev, "%s halt\n", data->name);
spin_lock_irq (&data->dev->lock);
if (likely (data->ep != NULL))
usb_ep_set_halt (data->ep);
spin_unlock_irq (&data->dev->lock);
mutex_unlock(&data->lock);
return -EBADMSG;
}
/* FIXME readahead for O_NONBLOCK and poll(); careful with ZLPs */
value = -ENOMEM;
kbuf = kmalloc (len, GFP_KERNEL);
if (unlikely (!kbuf))
goto free1;
value = ep_io (data, kbuf, len);
VDEBUG (data->dev, "%s read %zu OUT, status %d\n",
data->name, len, (int) value);
if (value >= 0 && copy_to_user (buf, kbuf, value))
value = -EFAULT;
free1:
mutex_unlock(&data->lock);
kfree (kbuf);
return value;
}
/* handle a synchronous IN bulk/intr/iso transfer */
static ssize_t
ep_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
{
struct ep_data *data = fd->private_data;
void *kbuf;
ssize_t value;
if ((value = get_ready_ep (fd->f_flags, data)) < 0)
return value;
/* halt any endpoint by doing a "wrong direction" i/o call */
if (!usb_endpoint_dir_in(&data->desc)) {
if (usb_endpoint_xfer_isoc(&data->desc)) {
mutex_unlock(&data->lock);
return -EINVAL;
}
DBG (data->dev, "%s halt\n", data->name);
spin_lock_irq (&data->dev->lock);
if (likely (data->ep != NULL))
usb_ep_set_halt (data->ep);
spin_unlock_irq (&data->dev->lock);
mutex_unlock(&data->lock);
return -EBADMSG;
}
/* FIXME writebehind for O_NONBLOCK and poll(), qlen = 1 */
value = -ENOMEM;
kbuf = memdup_user(buf, len);
if (IS_ERR(kbuf)) {
value = PTR_ERR(kbuf);
kbuf = NULL;
goto free1;
}
value = ep_io (data, kbuf, len);
VDEBUG (data->dev, "%s write %zu IN, status %d\n",
data->name, len, (int) value);
free1:
mutex_unlock(&data->lock);
kfree (kbuf);
return value;
}
static int static int
ep_release (struct inode *inode, struct file *fd) ep_release (struct inode *inode, struct file *fd)
{ {
...@@ -481,7 +395,7 @@ static long ep_ioctl(struct file *fd, unsigned code, unsigned long value) ...@@ -481,7 +395,7 @@ static long ep_ioctl(struct file *fd, unsigned code, unsigned long value)
struct ep_data *data = fd->private_data; struct ep_data *data = fd->private_data;
int status; int status;
if ((status = get_ready_ep (fd->f_flags, data)) < 0) if ((status = get_ready_ep (fd->f_flags, data, false)) < 0)
return status; return status;
spin_lock_irq (&data->dev->lock); spin_lock_irq (&data->dev->lock);
...@@ -517,8 +431,8 @@ struct kiocb_priv { ...@@ -517,8 +431,8 @@ struct kiocb_priv {
struct mm_struct *mm; struct mm_struct *mm;
struct work_struct work; struct work_struct work;
void *buf; void *buf;
const struct iovec *iv; struct iov_iter to;
unsigned long nr_segs; const void *to_free;
unsigned actual; unsigned actual;
}; };
...@@ -541,35 +455,6 @@ static int ep_aio_cancel(struct kiocb *iocb) ...@@ -541,35 +455,6 @@ static int ep_aio_cancel(struct kiocb *iocb)
return value; return value;
} }
static ssize_t ep_copy_to_user(struct kiocb_priv *priv)
{
ssize_t len, total;
void *to_copy;
int i;
/* copy stuff into user buffers */
total = priv->actual;
len = 0;
to_copy = priv->buf;
for (i=0; i < priv->nr_segs; i++) {
ssize_t this = min((ssize_t)(priv->iv[i].iov_len), total);
if (copy_to_user(priv->iv[i].iov_base, to_copy, this)) {
if (len == 0)
len = -EFAULT;
break;
}
total -= this;
len += this;
to_copy += this;
if (total == 0)
break;
}
return len;
}
static void ep_user_copy_worker(struct work_struct *work) static void ep_user_copy_worker(struct work_struct *work)
{ {
struct kiocb_priv *priv = container_of(work, struct kiocb_priv, work); struct kiocb_priv *priv = container_of(work, struct kiocb_priv, work);
...@@ -578,13 +463,16 @@ static void ep_user_copy_worker(struct work_struct *work) ...@@ -578,13 +463,16 @@ static void ep_user_copy_worker(struct work_struct *work)
size_t ret; size_t ret;
use_mm(mm); use_mm(mm);
ret = ep_copy_to_user(priv); ret = copy_to_iter(priv->buf, priv->actual, &priv->to);
unuse_mm(mm); unuse_mm(mm);
if (!ret)
ret = -EFAULT;
/* completing the iocb can drop the ctx and mm, don't touch mm after */ /* completing the iocb can drop the ctx and mm, don't touch mm after */
aio_complete(iocb, ret, ret); aio_complete(iocb, ret, ret);
kfree(priv->buf); kfree(priv->buf);
kfree(priv->to_free);
kfree(priv); kfree(priv);
} }
...@@ -603,8 +491,9 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req) ...@@ -603,8 +491,9 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req)
* don't need to copy anything to userspace, so we can * don't need to copy anything to userspace, so we can
* complete the aio request immediately. * complete the aio request immediately.
*/ */
if (priv->iv == NULL || unlikely(req->actual == 0)) { if (priv->to_free == NULL || unlikely(req->actual == 0)) {
kfree(req->buf); kfree(req->buf);
kfree(priv->to_free);
kfree(priv); kfree(priv);
iocb->private = NULL; iocb->private = NULL;
/* aio_complete() reports bytes-transferred _and_ faults */ /* aio_complete() reports bytes-transferred _and_ faults */
...@@ -618,6 +507,7 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req) ...@@ -618,6 +507,7 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req)
priv->buf = req->buf; priv->buf = req->buf;
priv->actual = req->actual; priv->actual = req->actual;
INIT_WORK(&priv->work, ep_user_copy_worker);
schedule_work(&priv->work); schedule_work(&priv->work);
} }
spin_unlock(&epdata->dev->lock); spin_unlock(&epdata->dev->lock);
...@@ -626,38 +516,17 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req) ...@@ -626,38 +516,17 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req)
put_ep(epdata); put_ep(epdata);
} }
static ssize_t static ssize_t ep_aio(struct kiocb *iocb,
ep_aio_rwtail( struct kiocb_priv *priv,
struct kiocb *iocb,
char *buf,
size_t len,
struct ep_data *epdata, struct ep_data *epdata,
const struct iovec *iv, char *buf,
unsigned long nr_segs size_t len)
)
{ {
struct kiocb_priv *priv;
struct usb_request *req; struct usb_request *req;
ssize_t value; ssize_t value;
priv = kmalloc(sizeof *priv, GFP_KERNEL);
if (!priv) {
value = -ENOMEM;
fail:
kfree(buf);
return value;
}
iocb->private = priv; iocb->private = priv;
priv->iocb = iocb; priv->iocb = iocb;
priv->iv = iv;
priv->nr_segs = nr_segs;
INIT_WORK(&priv->work, ep_user_copy_worker);
value = get_ready_ep(iocb->ki_filp->f_flags, epdata);
if (unlikely(value < 0)) {
kfree(priv);
goto fail;
}
kiocb_set_cancel_fn(iocb, ep_aio_cancel); kiocb_set_cancel_fn(iocb, ep_aio_cancel);
get_ep(epdata); get_ep(epdata);
...@@ -669,75 +538,154 @@ ep_aio_rwtail( ...@@ -669,75 +538,154 @@ ep_aio_rwtail(
* allocate or submit those if the host disconnected. * allocate or submit those if the host disconnected.
*/ */
spin_lock_irq(&epdata->dev->lock); spin_lock_irq(&epdata->dev->lock);
if (likely(epdata->ep)) { value = -ENODEV;
if (unlikely(epdata->ep))
goto fail;
req = usb_ep_alloc_request(epdata->ep, GFP_ATOMIC); req = usb_ep_alloc_request(epdata->ep, GFP_ATOMIC);
if (likely(req)) { value = -ENOMEM;
if (unlikely(!req))
goto fail;
priv->req = req; priv->req = req;
req->buf = buf; req->buf = buf;
req->length = len; req->length = len;
req->complete = ep_aio_complete; req->complete = ep_aio_complete;
req->context = iocb; req->context = iocb;
value = usb_ep_queue(epdata->ep, req, GFP_ATOMIC); value = usb_ep_queue(epdata->ep, req, GFP_ATOMIC);
if (unlikely(0 != value)) if (unlikely(0 != value)) {
usb_ep_free_request(epdata->ep, req); usb_ep_free_request(epdata->ep, req);
} else goto fail;
value = -EAGAIN; }
} else
value = -ENODEV;
spin_unlock_irq(&epdata->dev->lock); spin_unlock_irq(&epdata->dev->lock);
return -EIOCBQUEUED;
mutex_unlock(&epdata->lock); fail:
spin_unlock_irq(&epdata->dev->lock);
if (unlikely(value)) { kfree(priv->to_free);
kfree(priv); kfree(priv);
put_ep(epdata); put_ep(epdata);
} else
value = -EIOCBQUEUED;
return value; return value;
} }
static ssize_t static ssize_t
ep_aio_read(struct kiocb *iocb, const struct iovec *iov, ep_read_iter(struct kiocb *iocb, struct iov_iter *to)
unsigned long nr_segs, loff_t o)
{ {
struct ep_data *epdata = iocb->ki_filp->private_data; struct file *file = iocb->ki_filp;
struct ep_data *epdata = file->private_data;
size_t len = iov_iter_count(to);
ssize_t value;
char *buf; char *buf;
if (unlikely(usb_endpoint_dir_in(&epdata->desc))) if ((value = get_ready_ep(file->f_flags, epdata, false)) < 0)
return value;
/* halt any endpoint by doing a "wrong direction" i/o call */
if (usb_endpoint_dir_in(&epdata->desc)) {
if (usb_endpoint_xfer_isoc(&epdata->desc) ||
!is_sync_kiocb(iocb)) {
mutex_unlock(&epdata->lock);
return -EINVAL; return -EINVAL;
}
DBG (epdata->dev, "%s halt\n", epdata->name);
spin_lock_irq(&epdata->dev->lock);
if (likely(epdata->ep != NULL))
usb_ep_set_halt(epdata->ep);
spin_unlock_irq(&epdata->dev->lock);
mutex_unlock(&epdata->lock);
return -EBADMSG;
}
buf = kmalloc(iocb->ki_nbytes, GFP_KERNEL); buf = kmalloc(len, GFP_KERNEL);
if (unlikely(!buf)) if (unlikely(!buf)) {
mutex_unlock(&epdata->lock);
return -ENOMEM; return -ENOMEM;
}
return ep_aio_rwtail(iocb, buf, iocb->ki_nbytes, epdata, iov, nr_segs); if (is_sync_kiocb(iocb)) {
value = ep_io(epdata, buf, len);
if (value >= 0 && copy_to_iter(buf, value, to))
value = -EFAULT;
} else {
struct kiocb_priv *priv = kzalloc(sizeof *priv, GFP_KERNEL);
value = -ENOMEM;
if (!priv)
goto fail;
priv->to_free = dup_iter(&priv->to, to, GFP_KERNEL);
if (!priv->to_free) {
kfree(priv);
goto fail;
}
value = ep_aio(iocb, priv, epdata, buf, len);
if (value == -EIOCBQUEUED)
buf = NULL;
}
fail:
kfree(buf);
mutex_unlock(&epdata->lock);
return value;
} }
static ssize_t ep_config(struct ep_data *, const char *, size_t);
static ssize_t static ssize_t
ep_aio_write(struct kiocb *iocb, const struct iovec *iov, ep_write_iter(struct kiocb *iocb, struct iov_iter *from)
unsigned long nr_segs, loff_t o)
{ {
struct ep_data *epdata = iocb->ki_filp->private_data; struct file *file = iocb->ki_filp;
struct ep_data *epdata = file->private_data;
size_t len = iov_iter_count(from);
bool configured;
ssize_t value;
char *buf; char *buf;
size_t len = 0;
int i = 0;
if (unlikely(!usb_endpoint_dir_in(&epdata->desc))) if ((value = get_ready_ep(file->f_flags, epdata, true)) < 0)
return value;
configured = epdata->state == STATE_EP_ENABLED;
/* halt any endpoint by doing a "wrong direction" i/o call */
if (configured && !usb_endpoint_dir_in(&epdata->desc)) {
if (usb_endpoint_xfer_isoc(&epdata->desc) ||
!is_sync_kiocb(iocb)) {
mutex_unlock(&epdata->lock);
return -EINVAL; return -EINVAL;
}
DBG (epdata->dev, "%s halt\n", epdata->name);
spin_lock_irq(&epdata->dev->lock);
if (likely(epdata->ep != NULL))
usb_ep_set_halt(epdata->ep);
spin_unlock_irq(&epdata->dev->lock);
mutex_unlock(&epdata->lock);
return -EBADMSG;
}
buf = kmalloc(iocb->ki_nbytes, GFP_KERNEL); buf = kmalloc(len, GFP_KERNEL);
if (unlikely(!buf)) if (unlikely(!buf)) {
mutex_unlock(&epdata->lock);
return -ENOMEM; return -ENOMEM;
}
for (i=0; i < nr_segs; i++) { if (unlikely(copy_from_iter(buf, len, from) != len)) {
if (unlikely(copy_from_user(&buf[len], iov[i].iov_base, value = -EFAULT;
iov[i].iov_len) != 0)) { goto out;
kfree(buf); }
return -EFAULT;
if (unlikely(!configured)) {
value = ep_config(epdata, buf, len);
} else if (is_sync_kiocb(iocb)) {
value = ep_io(epdata, buf, len);
} else {
struct kiocb_priv *priv = kzalloc(sizeof *priv, GFP_KERNEL);
value = -ENOMEM;
if (priv) {
value = ep_aio(iocb, priv, epdata, buf, len);
if (value == -EIOCBQUEUED)
buf = NULL;
} }
len += iov[i].iov_len;
} }
return ep_aio_rwtail(iocb, buf, len, epdata, NULL, 0); out:
kfree(buf);
mutex_unlock(&epdata->lock);
return value;
} }
/*----------------------------------------------------------------------*/ /*----------------------------------------------------------------------*/
...@@ -745,15 +693,15 @@ ep_aio_write(struct kiocb *iocb, const struct iovec *iov, ...@@ -745,15 +693,15 @@ ep_aio_write(struct kiocb *iocb, const struct iovec *iov,
/* used after endpoint configuration */ /* used after endpoint configuration */
static const struct file_operations ep_io_operations = { static const struct file_operations ep_io_operations = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.llseek = no_llseek,
.read = ep_read, .open = ep_open,
.write = ep_write,
.unlocked_ioctl = ep_ioctl,
.release = ep_release, .release = ep_release,
.llseek = no_llseek,
.aio_read = ep_aio_read, .read = new_sync_read,
.aio_write = ep_aio_write, .write = new_sync_write,
.unlocked_ioctl = ep_ioctl,
.read_iter = ep_read_iter,
.write_iter = ep_write_iter,
}; };
/* ENDPOINT INITIALIZATION /* ENDPOINT INITIALIZATION
...@@ -770,17 +718,12 @@ static const struct file_operations ep_io_operations = { ...@@ -770,17 +718,12 @@ static const struct file_operations ep_io_operations = {
* speed descriptor, then optional high speed descriptor. * speed descriptor, then optional high speed descriptor.
*/ */
static ssize_t static ssize_t
ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) ep_config (struct ep_data *data, const char *buf, size_t len)
{ {
struct ep_data *data = fd->private_data;
struct usb_ep *ep; struct usb_ep *ep;
u32 tag; u32 tag;
int value, length = len; int value, length = len;
value = mutex_lock_interruptible(&data->lock);
if (value < 0)
return value;
if (data->state != STATE_EP_READY) { if (data->state != STATE_EP_READY) {
value = -EL2HLT; value = -EL2HLT;
goto fail; goto fail;
...@@ -791,9 +734,7 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) ...@@ -791,9 +734,7 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
goto fail0; goto fail0;
/* we might need to change message format someday */ /* we might need to change message format someday */
if (copy_from_user (&tag, buf, 4)) { memcpy(&tag, buf, 4);
goto fail1;
}
if (tag != 1) { if (tag != 1) {
DBG(data->dev, "config %s, bad tag %d\n", data->name, tag); DBG(data->dev, "config %s, bad tag %d\n", data->name, tag);
goto fail0; goto fail0;
...@@ -806,19 +747,15 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) ...@@ -806,19 +747,15 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
*/ */
/* full/low speed descriptor, then high speed */ /* full/low speed descriptor, then high speed */
if (copy_from_user (&data->desc, buf, USB_DT_ENDPOINT_SIZE)) { memcpy(&data->desc, buf, USB_DT_ENDPOINT_SIZE);
goto fail1;
}
if (data->desc.bLength != USB_DT_ENDPOINT_SIZE if (data->desc.bLength != USB_DT_ENDPOINT_SIZE
|| data->desc.bDescriptorType != USB_DT_ENDPOINT) || data->desc.bDescriptorType != USB_DT_ENDPOINT)
goto fail0; goto fail0;
if (len != USB_DT_ENDPOINT_SIZE) { if (len != USB_DT_ENDPOINT_SIZE) {
if (len != 2 * USB_DT_ENDPOINT_SIZE) if (len != 2 * USB_DT_ENDPOINT_SIZE)
goto fail0; goto fail0;
if (copy_from_user (&data->hs_desc, buf + USB_DT_ENDPOINT_SIZE, memcpy(&data->hs_desc, buf + USB_DT_ENDPOINT_SIZE,
USB_DT_ENDPOINT_SIZE)) { USB_DT_ENDPOINT_SIZE);
goto fail1;
}
if (data->hs_desc.bLength != USB_DT_ENDPOINT_SIZE if (data->hs_desc.bLength != USB_DT_ENDPOINT_SIZE
|| data->hs_desc.bDescriptorType || data->hs_desc.bDescriptorType
!= USB_DT_ENDPOINT) { != USB_DT_ENDPOINT) {
...@@ -840,24 +777,20 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) ...@@ -840,24 +777,20 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
case USB_SPEED_LOW: case USB_SPEED_LOW:
case USB_SPEED_FULL: case USB_SPEED_FULL:
ep->desc = &data->desc; ep->desc = &data->desc;
value = usb_ep_enable(ep);
if (value == 0)
data->state = STATE_EP_ENABLED;
break; break;
case USB_SPEED_HIGH: case USB_SPEED_HIGH:
/* fails if caller didn't provide that descriptor... */ /* fails if caller didn't provide that descriptor... */
ep->desc = &data->hs_desc; ep->desc = &data->hs_desc;
value = usb_ep_enable(ep);
if (value == 0)
data->state = STATE_EP_ENABLED;
break; break;
default: default:
DBG(data->dev, "unconnected, %s init abandoned\n", DBG(data->dev, "unconnected, %s init abandoned\n",
data->name); data->name);
value = -EINVAL; value = -EINVAL;
goto gone;
} }
value = usb_ep_enable(ep);
if (value == 0) { if (value == 0) {
fd->f_op = &ep_io_operations; data->state = STATE_EP_ENABLED;
value = length; value = length;
} }
gone: gone:
...@@ -867,14 +800,10 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) ...@@ -867,14 +800,10 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
data->desc.bDescriptorType = 0; data->desc.bDescriptorType = 0;
data->hs_desc.bDescriptorType = 0; data->hs_desc.bDescriptorType = 0;
} }
mutex_unlock(&data->lock);
return value; return value;
fail0: fail0:
value = -EINVAL; value = -EINVAL;
goto fail; goto fail;
fail1:
value = -EFAULT;
goto fail;
} }
static int static int
...@@ -902,15 +831,6 @@ ep_open (struct inode *inode, struct file *fd) ...@@ -902,15 +831,6 @@ ep_open (struct inode *inode, struct file *fd)
return value; return value;
} }
/* used before endpoint configuration */
static const struct file_operations ep_config_operations = {
.llseek = no_llseek,
.open = ep_open,
.write = ep_config,
.release = ep_release,
};
/*----------------------------------------------------------------------*/ /*----------------------------------------------------------------------*/
/* EP0 IMPLEMENTATION can be partly in userspace. /* EP0 IMPLEMENTATION can be partly in userspace.
...@@ -989,6 +909,10 @@ ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr) ...@@ -989,6 +909,10 @@ ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
enum ep0_state state; enum ep0_state state;
spin_lock_irq (&dev->lock); spin_lock_irq (&dev->lock);
if (dev->state <= STATE_DEV_OPENED) {
retval = -EINVAL;
goto done;
}
/* report fd mode change before acting on it */ /* report fd mode change before acting on it */
if (dev->setup_abort) { if (dev->setup_abort) {
...@@ -1187,8 +1111,6 @@ ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) ...@@ -1187,8 +1111,6 @@ ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
struct dev_data *dev = fd->private_data; struct dev_data *dev = fd->private_data;
ssize_t retval = -ESRCH; ssize_t retval = -ESRCH;
spin_lock_irq (&dev->lock);
/* report fd mode change before acting on it */ /* report fd mode change before acting on it */
if (dev->setup_abort) { if (dev->setup_abort) {
dev->setup_abort = 0; dev->setup_abort = 0;
...@@ -1234,7 +1156,6 @@ ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) ...@@ -1234,7 +1156,6 @@ ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
} else } else
DBG (dev, "fail %s, state %d\n", __func__, dev->state); DBG (dev, "fail %s, state %d\n", __func__, dev->state);
spin_unlock_irq (&dev->lock);
return retval; return retval;
} }
...@@ -1281,6 +1202,9 @@ ep0_poll (struct file *fd, poll_table *wait) ...@@ -1281,6 +1202,9 @@ ep0_poll (struct file *fd, poll_table *wait)
struct dev_data *dev = fd->private_data; struct dev_data *dev = fd->private_data;
int mask = 0; int mask = 0;
if (dev->state <= STATE_DEV_OPENED)
return DEFAULT_POLLMASK;
poll_wait(fd, &dev->wait, wait); poll_wait(fd, &dev->wait, wait);
spin_lock_irq (&dev->lock); spin_lock_irq (&dev->lock);
...@@ -1316,19 +1240,6 @@ static long dev_ioctl (struct file *fd, unsigned code, unsigned long value) ...@@ -1316,19 +1240,6 @@ static long dev_ioctl (struct file *fd, unsigned code, unsigned long value)
return ret; return ret;
} }
/* used after device configuration */
static const struct file_operations ep0_io_operations = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = ep0_read,
.write = ep0_write,
.fasync = ep0_fasync,
.poll = ep0_poll,
.unlocked_ioctl = dev_ioctl,
.release = dev_release,
};
/*----------------------------------------------------------------------*/ /*----------------------------------------------------------------------*/
/* The in-kernel gadget driver handles most ep0 issues, in particular /* The in-kernel gadget driver handles most ep0 issues, in particular
...@@ -1650,7 +1561,7 @@ static int activate_ep_files (struct dev_data *dev) ...@@ -1650,7 +1561,7 @@ static int activate_ep_files (struct dev_data *dev)
goto enomem1; goto enomem1;
data->dentry = gadgetfs_create_file (dev->sb, data->name, data->dentry = gadgetfs_create_file (dev->sb, data->name,
data, &ep_config_operations); data, &ep_io_operations);
if (!data->dentry) if (!data->dentry)
goto enomem2; goto enomem2;
list_add_tail (&data->epfiles, &dev->epfiles); list_add_tail (&data->epfiles, &dev->epfiles);
...@@ -1852,6 +1763,14 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) ...@@ -1852,6 +1763,14 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
u32 tag; u32 tag;
char *kbuf; char *kbuf;
spin_lock_irq(&dev->lock);
if (dev->state > STATE_DEV_OPENED) {
value = ep0_write(fd, buf, len, ptr);
spin_unlock_irq(&dev->lock);
return value;
}
spin_unlock_irq(&dev->lock);
if (len < (USB_DT_CONFIG_SIZE + USB_DT_DEVICE_SIZE + 4)) if (len < (USB_DT_CONFIG_SIZE + USB_DT_DEVICE_SIZE + 4))
return -EINVAL; return -EINVAL;
...@@ -1925,7 +1844,6 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) ...@@ -1925,7 +1844,6 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
* on, they can work ... except in cleanup paths that * on, they can work ... except in cleanup paths that
* kick in after the ep0 descriptor is closed. * kick in after the ep0 descriptor is closed.
*/ */
fd->f_op = &ep0_io_operations;
value = len; value = len;
} }
return value; return value;
...@@ -1956,12 +1874,14 @@ dev_open (struct inode *inode, struct file *fd) ...@@ -1956,12 +1874,14 @@ dev_open (struct inode *inode, struct file *fd)
return value; return value;
} }
static const struct file_operations dev_init_operations = { static const struct file_operations ep0_operations = {
.llseek = no_llseek, .llseek = no_llseek,
.open = dev_open, .open = dev_open,
.read = ep0_read,
.write = dev_config, .write = dev_config,
.fasync = ep0_fasync, .fasync = ep0_fasync,
.poll = ep0_poll,
.unlocked_ioctl = dev_ioctl, .unlocked_ioctl = dev_ioctl,
.release = dev_release, .release = dev_release,
}; };
...@@ -2077,7 +1997,7 @@ gadgetfs_fill_super (struct super_block *sb, void *opts, int silent) ...@@ -2077,7 +1997,7 @@ gadgetfs_fill_super (struct super_block *sb, void *opts, int silent)
goto Enomem; goto Enomem;
dev->sb = sb; dev->sb = sb;
dev->dentry = gadgetfs_create_file(sb, CHIP, dev, &dev_init_operations); dev->dentry = gadgetfs_create_file(sb, CHIP, dev, &ep0_operations);
if (!dev->dentry) { if (!dev->dentry) {
put_dev(dev); put_dev(dev);
goto Enomem; goto Enomem;
......
...@@ -98,6 +98,8 @@ ssize_t iov_iter_get_pages_alloc(struct iov_iter *i, struct page ***pages, ...@@ -98,6 +98,8 @@ ssize_t iov_iter_get_pages_alloc(struct iov_iter *i, struct page ***pages,
size_t maxsize, size_t *start); size_t maxsize, size_t *start);
int iov_iter_npages(const struct iov_iter *i, int maxpages); int iov_iter_npages(const struct iov_iter *i, int maxpages);
const void *dup_iter(struct iov_iter *new, struct iov_iter *old, gfp_t flags);
static inline size_t iov_iter_count(struct iov_iter *i) static inline size_t iov_iter_count(struct iov_iter *i)
{ {
return i->count; return i->count;
......
...@@ -24,7 +24,7 @@ obj-y += lockref.o ...@@ -24,7 +24,7 @@ obj-y += lockref.o
obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \ obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \
bust_spinlocks.o kasprintf.o bitmap.o scatterlist.o \ bust_spinlocks.o kasprintf.o bitmap.o scatterlist.o \
gcd.o lcm.o list_sort.o uuid.o flex_array.o clz_ctz.o \ gcd.o lcm.o list_sort.o uuid.o flex_array.o iov_iter.o clz_ctz.o \
bsearch.o find_last_bit.o find_next_bit.o llist.o memweight.o kfifo.o \ bsearch.o find_last_bit.o find_next_bit.o llist.o memweight.o kfifo.o \
percpu-refcount.o percpu_ida.o rhashtable.o reciprocal_div.o percpu-refcount.o percpu_ida.o rhashtable.o reciprocal_div.o
obj-y += string_helpers.o obj-y += string_helpers.o
......
...@@ -751,3 +751,18 @@ int iov_iter_npages(const struct iov_iter *i, int maxpages) ...@@ -751,3 +751,18 @@ int iov_iter_npages(const struct iov_iter *i, int maxpages)
return npages; return npages;
} }
EXPORT_SYMBOL(iov_iter_npages); EXPORT_SYMBOL(iov_iter_npages);
const void *dup_iter(struct iov_iter *new, struct iov_iter *old, gfp_t flags)
{
*new = *old;
if (new->type & ITER_BVEC)
return new->bvec = kmemdup(new->bvec,
new->nr_segs * sizeof(struct bio_vec),
flags);
else
/* iovec and kvec have identical layout */
return new->iov = kmemdup(new->iov,
new->nr_segs * sizeof(struct iovec),
flags);
}
EXPORT_SYMBOL(dup_iter);
...@@ -21,7 +21,7 @@ obj-y := filemap.o mempool.o oom_kill.o \ ...@@ -21,7 +21,7 @@ obj-y := filemap.o mempool.o oom_kill.o \
mm_init.o mmu_context.o percpu.o slab_common.o \ mm_init.o mmu_context.o percpu.o slab_common.o \
compaction.o vmacache.o \ compaction.o vmacache.o \
interval_tree.o list_lru.o workingset.o \ interval_tree.o list_lru.o workingset.o \
iov_iter.o debug.o $(mmu-y) debug.o $(mmu-y)
obj-y += init-mm.o obj-y += init-mm.o
......
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