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()
This commit is contained in:
Linus Torvalds 2015-03-13 10:55:32 -07:00
commit f788baadbd
6 changed files with 303 additions and 412 deletions

View File

@ -144,10 +144,9 @@ struct ffs_io_data {
bool read;
struct kiocb *kiocb;
const struct iovec *iovec;
unsigned long nr_segs;
char __user *buf;
size_t len;
struct iov_iter data;
const void *to_free;
char *buf;
struct mm_struct *mm;
struct work_struct work;
@ -649,29 +648,10 @@ static void ffs_user_copy_worker(struct work_struct *work)
io_data->req->actual;
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);
for (i = 0; i < io_data->nr_segs; i++) {
size_t len = min_t(size_t, ret - pos,
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;
break;
}
pos += len;
}
ret = copy_to_iter(io_data->buf, ret, &io_data->data);
if (iov_iter_count(&io_data->data))
ret = -EFAULT;
unuse_mm(io_data->mm);
}
@ -684,7 +664,7 @@ static void ffs_user_copy_worker(struct work_struct *work)
io_data->kiocb->private = NULL;
if (io_data->read)
kfree(io_data->iovec);
kfree(io_data->to_free);
kfree(io_data->buf);
kfree(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
*/
struct usb_gadget *gadget = epfile->ffs->gadget;
size_t copied;
spin_lock_irq(&epfile->ffs->eps_lock);
/* 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)
spin_unlock_irq(&epfile->ffs->eps_lock);
return -ESHUTDOWN;
}
data_len = iov_iter_count(&io_data->data);
/*
* Controller may require buffer size to be aligned to
* maxpacketsize of an out endpoint.
*/
data_len = io_data->read ?
usb_ep_align_maybe(gadget, ep->ep, io_data->len) :
io_data->len;
if (io_data->read)
data_len = usb_ep_align_maybe(gadget, ep->ep, data_len);
spin_unlock_irq(&epfile->ffs->eps_lock);
data = kmalloc(data_len, GFP_KERNEL);
if (unlikely(!data))
return -ENOMEM;
if (io_data->aio && !io_data->read) {
int i;
size_t pos = 0;
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))) {
if (!io_data->read) {
copied = copy_from_iter(data, data_len, &io_data->data);
if (copied != data_len) {
ret = -EFAULT;
goto error;
}
@ -876,10 +844,8 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
*/
ret = ep->status;
if (io_data->read && ret > 0) {
ret = min_t(size_t, ret, io_data->len);
if (unlikely(copy_to_user(io_data->buf,
data, ret)))
ret = copy_to_iter(data, ret, &io_data->data);
if (unlikely(iov_iter_count(&io_data->data)))
ret = -EFAULT;
}
}
@ -898,37 +864,6 @@ error:
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
ffs_epfile_open(struct inode *inode, struct file *file)
{
@ -965,67 +900,86 @@ static int ffs_aio_cancel(struct kiocb *kiocb)
return value;
}
static ssize_t ffs_epfile_aio_write(struct kiocb *kiocb,
const struct iovec *iovec,
unsigned long nr_segs, loff_t loff)
static ssize_t ffs_epfile_write_iter(struct kiocb *kiocb, struct iov_iter *from)
{
struct ffs_io_data *io_data;
struct ffs_io_data io_data, *p = &io_data;
ssize_t res;
ENTER();
io_data = kmalloc(sizeof(*io_data), GFP_KERNEL);
if (unlikely(!io_data))
return -ENOMEM;
io_data->aio = true;
io_data->read = false;
io_data->kiocb = kiocb;
io_data->iovec = iovec;
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);
return ffs_epfile_io(kiocb->ki_filp, io_data);
}
static ssize_t ffs_epfile_aio_read(struct kiocb *kiocb,
const struct iovec *iovec,
unsigned long nr_segs, loff_t loff)
{
struct ffs_io_data *io_data;
struct iovec *iovec_copy;
ENTER();
iovec_copy = kmalloc_array(nr_segs, sizeof(*iovec_copy), GFP_KERNEL);
if (unlikely(!iovec_copy))
return -ENOMEM;
memcpy(iovec_copy, iovec, sizeof(struct iovec)*nr_segs);
io_data = kmalloc(sizeof(*io_data), GFP_KERNEL);
if (unlikely(!io_data)) {
kfree(iovec_copy);
return -ENOMEM;
if (!is_sync_kiocb(kiocb)) {
p = kmalloc(sizeof(io_data), GFP_KERNEL);
if (unlikely(!p))
return -ENOMEM;
p->aio = true;
} else {
p->aio = false;
}
io_data->aio = true;
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;
p->read = false;
p->kiocb = kiocb;
p->data = *from;
p->mm = current->mm;
kiocb->private = io_data;
kiocb->private = p;
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_read_iter(struct kiocb *kiocb, struct iov_iter *to)
{
struct ffs_io_data io_data, *p = &io_data;
ssize_t res;
ENTER();
if (!is_sync_kiocb(kiocb)) {
p = kmalloc(sizeof(io_data), GFP_KERNEL);
if (unlikely(!p))
return -ENOMEM;
p->aio = true;
} else {
p->aio = false;
}
p->read = true;
p->kiocb = kiocb;
if (p->aio) {
p->to_free = dup_iter(&p->data, to, GFP_KERNEL);
if (!p->to_free) {
kfree(p);
return -ENOMEM;
}
} else {
p->data = *to;
p->to_free = NULL;
}
p->mm = current->mm;
kiocb->private = p;
kiocb_set_cancel_fn(kiocb, ffs_aio_cancel);
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
@ -1105,10 +1059,10 @@ static const struct file_operations ffs_epfile_operations = {
.llseek = no_llseek,
.open = ffs_epfile_open,
.write = ffs_epfile_write,
.read = ffs_epfile_read,
.aio_write = ffs_epfile_aio_write,
.aio_read = ffs_epfile_aio_read,
.write = new_sync_write,
.read = new_sync_read,
.write_iter = ffs_epfile_write_iter,
.read_iter = ffs_epfile_read_iter,
.release = ffs_epfile_release,
.unlocked_ioctl = ffs_epfile_ioctl,
};

View File

@ -74,6 +74,8 @@ MODULE_DESCRIPTION (DRIVER_DESC);
MODULE_AUTHOR ("David Brownell");
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)
* still need dev->lock to use epdata->ep.
*/
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;
if (f_flags & O_NONBLOCK) {
if (!mutex_trylock(&epdata->lock))
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);
nonblock:
val = -EAGAIN;
@ -305,18 +308,20 @@ nonblock:
switch (epdata->state) {
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;
// case STATE_EP_DISABLED: /* "can't happen" */
// case STATE_EP_READY: /* "can't happen" */
default: /* error! */
pr_debug ("%s: ep %p not available, state %d\n",
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
@ -363,97 +368,6 @@ ep_io (struct ep_data *epdata, void *buf, unsigned len)
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
ep_release (struct inode *inode, struct file *fd)
{
@ -481,7 +395,7 @@ static long ep_ioctl(struct file *fd, unsigned code, unsigned long value)
struct ep_data *data = fd->private_data;
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;
spin_lock_irq (&data->dev->lock);
@ -517,8 +431,8 @@ struct kiocb_priv {
struct mm_struct *mm;
struct work_struct work;
void *buf;
const struct iovec *iv;
unsigned long nr_segs;
struct iov_iter to;
const void *to_free;
unsigned actual;
};
@ -541,35 +455,6 @@ static int ep_aio_cancel(struct kiocb *iocb)
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)
{
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)
size_t ret;
use_mm(mm);
ret = ep_copy_to_user(priv);
ret = copy_to_iter(priv->buf, priv->actual, &priv->to);
unuse_mm(mm);
if (!ret)
ret = -EFAULT;
/* completing the iocb can drop the ctx and mm, don't touch mm after */
aio_complete(iocb, ret, ret);
kfree(priv->buf);
kfree(priv->to_free);
kfree(priv);
}
@ -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
* 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(priv->to_free);
kfree(priv);
iocb->private = NULL;
/* aio_complete() reports bytes-transferred _and_ faults */
@ -618,6 +507,7 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req)
priv->buf = req->buf;
priv->actual = req->actual;
INIT_WORK(&priv->work, ep_user_copy_worker);
schedule_work(&priv->work);
}
spin_unlock(&epdata->dev->lock);
@ -626,38 +516,17 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req)
put_ep(epdata);
}
static ssize_t
ep_aio_rwtail(
struct kiocb *iocb,
char *buf,
size_t len,
struct ep_data *epdata,
const struct iovec *iv,
unsigned long nr_segs
)
static ssize_t ep_aio(struct kiocb *iocb,
struct kiocb_priv *priv,
struct ep_data *epdata,
char *buf,
size_t len)
{
struct kiocb_priv *priv;
struct usb_request *req;
ssize_t value;
struct usb_request *req;
ssize_t value;
priv = kmalloc(sizeof *priv, GFP_KERNEL);
if (!priv) {
value = -ENOMEM;
fail:
kfree(buf);
return value;
}
iocb->private = priv;
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);
get_ep(epdata);
@ -669,75 +538,154 @@ fail:
* allocate or submit those if the host disconnected.
*/
spin_lock_irq(&epdata->dev->lock);
if (likely(epdata->ep)) {
req = usb_ep_alloc_request(epdata->ep, GFP_ATOMIC);
if (likely(req)) {
priv->req = req;
req->buf = buf;
req->length = len;
req->complete = ep_aio_complete;
req->context = iocb;
value = usb_ep_queue(epdata->ep, req, GFP_ATOMIC);
if (unlikely(0 != value))
usb_ep_free_request(epdata->ep, req);
} else
value = -EAGAIN;
} else
value = -ENODEV;
value = -ENODEV;
if (unlikely(epdata->ep))
goto fail;
req = usb_ep_alloc_request(epdata->ep, GFP_ATOMIC);
value = -ENOMEM;
if (unlikely(!req))
goto fail;
priv->req = req;
req->buf = buf;
req->length = len;
req->complete = ep_aio_complete;
req->context = iocb;
value = usb_ep_queue(epdata->ep, req, GFP_ATOMIC);
if (unlikely(0 != value)) {
usb_ep_free_request(epdata->ep, req);
goto fail;
}
spin_unlock_irq(&epdata->dev->lock);
return -EIOCBQUEUED;
mutex_unlock(&epdata->lock);
if (unlikely(value)) {
kfree(priv);
put_ep(epdata);
} else
value = -EIOCBQUEUED;
fail:
spin_unlock_irq(&epdata->dev->lock);
kfree(priv->to_free);
kfree(priv);
put_ep(epdata);
return value;
}
static ssize_t
ep_aio_read(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t o)
ep_read_iter(struct kiocb *iocb, struct iov_iter *to)
{
struct ep_data *epdata = iocb->ki_filp->private_data;
char *buf;
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;
if (unlikely(usb_endpoint_dir_in(&epdata->desc)))
return -EINVAL;
if ((value = get_ready_ep(file->f_flags, epdata, false)) < 0)
return value;
buf = kmalloc(iocb->ki_nbytes, GFP_KERNEL);
if (unlikely(!buf))
/* 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;
}
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(len, GFP_KERNEL);
if (unlikely(!buf)) {
mutex_unlock(&epdata->lock);
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
ep_aio_write(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t o)
ep_write_iter(struct kiocb *iocb, struct iov_iter *from)
{
struct ep_data *epdata = iocb->ki_filp->private_data;
char *buf;
size_t len = 0;
int i = 0;
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;
if (unlikely(!usb_endpoint_dir_in(&epdata->desc)))
return -EINVAL;
if ((value = get_ready_ep(file->f_flags, epdata, true)) < 0)
return value;
buf = kmalloc(iocb->ki_nbytes, GFP_KERNEL);
if (unlikely(!buf))
return -ENOMEM;
configured = epdata->state == STATE_EP_ENABLED;
for (i=0; i < nr_segs; i++) {
if (unlikely(copy_from_user(&buf[len], iov[i].iov_base,
iov[i].iov_len) != 0)) {
kfree(buf);
return -EFAULT;
/* 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;
}
len += iov[i].iov_len;
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;
}
return ep_aio_rwtail(iocb, buf, len, epdata, NULL, 0);
buf = kmalloc(len, GFP_KERNEL);
if (unlikely(!buf)) {
mutex_unlock(&epdata->lock);
return -ENOMEM;
}
if (unlikely(copy_from_iter(buf, len, from) != len)) {
value = -EFAULT;
goto out;
}
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;
}
}
out:
kfree(buf);
mutex_unlock(&epdata->lock);
return value;
}
/*----------------------------------------------------------------------*/
@ -745,15 +693,15 @@ ep_aio_write(struct kiocb *iocb, const struct iovec *iov,
/* used after endpoint configuration */
static const struct file_operations ep_io_operations = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = ep_read,
.write = ep_write,
.unlocked_ioctl = ep_ioctl,
.open = ep_open,
.release = ep_release,
.aio_read = ep_aio_read,
.aio_write = ep_aio_write,
.llseek = no_llseek,
.read = new_sync_read,
.write = new_sync_write,
.unlocked_ioctl = ep_ioctl,
.read_iter = ep_read_iter,
.write_iter = ep_write_iter,
};
/* ENDPOINT INITIALIZATION
@ -770,17 +718,12 @@ static const struct file_operations ep_io_operations = {
* speed descriptor, then optional high speed descriptor.
*/
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;
u32 tag;
int value, length = len;
value = mutex_lock_interruptible(&data->lock);
if (value < 0)
return value;
if (data->state != STATE_EP_READY) {
value = -EL2HLT;
goto fail;
@ -791,9 +734,7 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
goto fail0;
/* we might need to change message format someday */
if (copy_from_user (&tag, buf, 4)) {
goto fail1;
}
memcpy(&tag, buf, 4);
if (tag != 1) {
DBG(data->dev, "config %s, bad tag %d\n", data->name, tag);
goto fail0;
@ -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 */
if (copy_from_user (&data->desc, buf, USB_DT_ENDPOINT_SIZE)) {
goto fail1;
}
memcpy(&data->desc, buf, USB_DT_ENDPOINT_SIZE);
if (data->desc.bLength != USB_DT_ENDPOINT_SIZE
|| data->desc.bDescriptorType != USB_DT_ENDPOINT)
goto fail0;
if (len != USB_DT_ENDPOINT_SIZE) {
if (len != 2 * USB_DT_ENDPOINT_SIZE)
goto fail0;
if (copy_from_user (&data->hs_desc, buf + USB_DT_ENDPOINT_SIZE,
USB_DT_ENDPOINT_SIZE)) {
goto fail1;
}
memcpy(&data->hs_desc, buf + USB_DT_ENDPOINT_SIZE,
USB_DT_ENDPOINT_SIZE);
if (data->hs_desc.bLength != USB_DT_ENDPOINT_SIZE
|| data->hs_desc.bDescriptorType
!= USB_DT_ENDPOINT) {
@ -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_FULL:
ep->desc = &data->desc;
value = usb_ep_enable(ep);
if (value == 0)
data->state = STATE_EP_ENABLED;
break;
case USB_SPEED_HIGH:
/* fails if caller didn't provide that descriptor... */
ep->desc = &data->hs_desc;
value = usb_ep_enable(ep);
if (value == 0)
data->state = STATE_EP_ENABLED;
break;
default:
DBG(data->dev, "unconnected, %s init abandoned\n",
data->name);
value = -EINVAL;
goto gone;
}
value = usb_ep_enable(ep);
if (value == 0) {
fd->f_op = &ep_io_operations;
data->state = STATE_EP_ENABLED;
value = length;
}
gone:
@ -867,14 +800,10 @@ fail:
data->desc.bDescriptorType = 0;
data->hs_desc.bDescriptorType = 0;
}
mutex_unlock(&data->lock);
return value;
fail0:
value = -EINVAL;
goto fail;
fail1:
value = -EFAULT;
goto fail;
}
static int
@ -902,15 +831,6 @@ ep_open (struct inode *inode, struct file *fd)
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.
@ -989,6 +909,10 @@ ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
enum ep0_state state;
spin_lock_irq (&dev->lock);
if (dev->state <= STATE_DEV_OPENED) {
retval = -EINVAL;
goto done;
}
/* report fd mode change before acting on it */
if (dev->setup_abort) {
@ -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;
ssize_t retval = -ESRCH;
spin_lock_irq (&dev->lock);
/* report fd mode change before acting on it */
if (dev->setup_abort) {
dev->setup_abort = 0;
@ -1234,7 +1156,6 @@ ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
} else
DBG (dev, "fail %s, state %d\n", __func__, dev->state);
spin_unlock_irq (&dev->lock);
return retval;
}
@ -1281,6 +1202,9 @@ ep0_poll (struct file *fd, poll_table *wait)
struct dev_data *dev = fd->private_data;
int mask = 0;
if (dev->state <= STATE_DEV_OPENED)
return DEFAULT_POLLMASK;
poll_wait(fd, &dev->wait, wait);
spin_lock_irq (&dev->lock);
@ -1316,19 +1240,6 @@ static long dev_ioctl (struct file *fd, unsigned code, unsigned long value)
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
@ -1650,7 +1561,7 @@ static int activate_ep_files (struct dev_data *dev)
goto enomem1;
data->dentry = gadgetfs_create_file (dev->sb, data->name,
data, &ep_config_operations);
data, &ep_io_operations);
if (!data->dentry)
goto enomem2;
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)
u32 tag;
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))
return -EINVAL;
@ -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
* kick in after the ep0 descriptor is closed.
*/
fd->f_op = &ep0_io_operations;
value = len;
}
return value;
@ -1956,12 +1874,14 @@ dev_open (struct inode *inode, struct file *fd)
return value;
}
static const struct file_operations dev_init_operations = {
static const struct file_operations ep0_operations = {
.llseek = no_llseek,
.open = dev_open,
.read = ep0_read,
.write = dev_config,
.fasync = ep0_fasync,
.poll = ep0_poll,
.unlocked_ioctl = dev_ioctl,
.release = dev_release,
};
@ -2077,7 +1997,7 @@ gadgetfs_fill_super (struct super_block *sb, void *opts, int silent)
goto Enomem;
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) {
put_dev(dev);
goto Enomem;

View File

@ -98,6 +98,8 @@ ssize_t iov_iter_get_pages_alloc(struct iov_iter *i, struct page ***pages,
size_t maxsize, size_t *start);
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)
{
return i->count;

View File

@ -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 \
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 \
percpu-refcount.o percpu_ida.o rhashtable.o reciprocal_div.o
obj-y += string_helpers.o

View File

@ -751,3 +751,18 @@ int iov_iter_npages(const struct iov_iter *i, int maxpages)
return 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);

View File

@ -21,7 +21,7 @@ obj-y := filemap.o mempool.o oom_kill.o \
mm_init.o mmu_context.o percpu.o slab_common.o \
compaction.o vmacache.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