On Mon, May 16 2016, Felipe Balbi wrote: > Michal Nazarewicz <min...@mina86.com> writes: > >>> Alan Stern <st...@rowland.harvard.edu> writes: >>>> The point is that you don't know whether the host sent more data than >>>> expected. All you know is that the host sent more data than the user >>>> asked the kernel for -- but maybe the user didn't ask for all the >>>> data that he expected. Maybe the user wanted to retrieve the full >>>> set of data using two read() system calls. >> >> On Mon, May 16 2016, Felipe Balbi wrote: >>> right, but that just means we need to buffer the data instead of bailing >>> out of the first read() completely. >> >> Correct. >> >> I have a ~4h bus ride ahead of me so I’ll try to implement it. If you >> don’t hear from me by the end of the day, there probably wasn’t enough >> space/comfort in the bus to use a laptop. > > Cool, Michal. Thanks > > seems like a kfifo would do well here(?)
There appears to be no kfifo support for iov_iter though, so I just went with a simple buffer. I haven’t looked at the patch too carefully so this is an RFC rather than an actual patch at this point. It does compile at least. Regardless, the more I thin about it, the more I’m under the impression that the whole rounding up in f_fs was a mistake. And the more I’m leaning towards ignoring the excess data set by the host. ---------- >8 ---------------------------------------------------------- Subject: usb: gadget: f_fs: buffer data from ‘oversized’ OUT requests f_fs rounds up read(2) requests to a multiple of a max packet size which means that host may provide more data than user has space for. So far, the excess data has been silently ignored. This introduces a buffer for a tail of such requests so that they are returned on next read instead of being ignored. Signed-off-by: Michal Nazarewicz <min...@mina86.com> --- drivers/usb/gadget/function/f_fs.c | 63 +++++++++++++++++++++++++++++++++----- 1 file changed, 56 insertions(+), 7 deletions(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 2c314c1..7d3c51a 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -130,6 +130,12 @@ struct ffs_epfile { struct dentry *dentry; + /* + * Buffer for holding data from partial reads which may happen since + * we’re rounding user read requests to a multiple of a max packet size. + */ + struct ffs_buffer *read_buffer; + char name[5]; unsigned char in; /* P: ffs->eps_lock */ @@ -138,6 +144,12 @@ struct ffs_epfile { unsigned char _pad; }; +struct ffs_buffer { + size_t length; + char *data; + char storage[]; +}; + /* ffs_io_data structure ***************************************************/ struct ffs_io_data { @@ -681,6 +693,24 @@ static void ffs_epfile_async_io_complete(struct usb_ep *_ep, schedule_work(&io_data->work); } +static ssize_t ffs_epfile_read_buffered(struct ffs_epfile *epfile, + struct iov_iter *iter) +{ + struct ffs_buffer *buf = epfile->read_buffer; + ssize_t ret = 0; + if (buf) { + ret = copy_to_iter(buf->data, buf->length, iter); + buf->length -= ret; + if (buf->length) { + buf->data += ret; + } else { + kfree(buf); + epfile->read_buffer = NULL; + } + } + return ret; +} + static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) { struct ffs_epfile *epfile = file->private_data; @@ -710,6 +740,18 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) if (halt && epfile->isoc) return -EINVAL; + /* + * Do we have buffered data from previous partial read? Check that for + * synchronous case only because we do not have facility to ‘wake up’ + * a pending asynchronous read and push buffered data to it which we + * would need to make things behave consistently. + */ + if (!halt && !io_data->aio && io_data->read) { + ret = ffs_epfile_read_buffered(epfile, &io_data->data); + if (ret) + return ret; + } + /* Allocate & copy */ if (!halt) { /* @@ -804,17 +846,24 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) interrupted = ep->status < 0; } - /* - * XXX We may end up silently droping data here. Since data_len - * (i.e. req->length) may be bigger than len (after being - * rounded up to maxpacketsize), we may end up with more data - * then user space has space for. - */ ret = interrupted ? -EINTR : ep->status; if (io_data->read && ret > 0) { + size_t left; ret = copy_to_iter(data, ret, &io_data->data); - if (!ret) + left = ep->status - ret; + if (!left) { + /* nop */ + } else if (iov_iter_count(&io_data->data)) { ret = -EFAULT; + } else { + struct ffs_buffer *buf = kmalloc( + sizeof(*epfile->read_buffer) + left, + GFP_KERNEL); + buf->length = left; + buf->data = buf->storage; + memcpy(buf->storage, data + ret, left); + epfile->read_buffer = buf; + } } goto error_mutex; } else if (!(req = usb_ep_alloc_request(ep->ep, GFP_KERNEL))) { -- Best regards ミハウ “𝓶𝓲𝓷𝓪86” ナザレヴイツ «If at first you don’t succeed, give up skydiving» -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html