diff mbox

[3/5] dev-mtp: retry write for incomplete transfers

Message ID 20180720214020.22897-4-bsd@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

Bandan Das July 20, 2018, 9:40 p.m. UTC
For large buffers, write may not copy the full buffer. For example,
on Linux, write imposes a limit of 0x7ffff000. Note that this does
not fix >4G transfers but ~>2G files will transfer successfully.

Signed-off-by: Bandan Das <bsd@redhat.com>
---
 hw/usb/dev-mtp.c | 22 ++++++++++++++++++++--
 1 file changed, 20 insertions(+), 2 deletions(-)

Comments

Gerd Hoffmann Aug. 7, 2018, 1:28 p.m. UTC | #1
On Fri, Jul 20, 2018 at 05:40:18PM -0400, Bandan Das wrote:
> For large buffers, write may not copy the full buffer. For example,
> on Linux, write imposes a limit of 0x7ffff000. Note that this does
> not fix >4G transfers but ~>2G files will transfer successfully.

Hmm, I guess it would be a good idea to write the file in smaller
pieces, so we don't need a 2G host buffer to let the guest write
a 2G file ...

(as incremental improvement on top of this series).

cheers,
  Gerd
Bandan Das Aug. 7, 2018, 6:15 p.m. UTC | #2
Gerd Hoffmann <kraxel@redhat.com> writes:

> On Fri, Jul 20, 2018 at 05:40:18PM -0400, Bandan Das wrote:
>> For large buffers, write may not copy the full buffer. For example,
>> on Linux, write imposes a limit of 0x7ffff000. Note that this does
>> not fix >4G transfers but ~>2G files will transfer successfully.
>
> Hmm, I guess it would be a good idea to write the file in smaller
> pieces, so we don't need a 2G host buffer to let the guest write
> a 2G file ...
>
> (as incremental improvement on top of this series).
>

Sounds good, I will add this to my todo list.

> cheers,
>   Gerd
Thomas Huth Oct. 19, 2018, 9:48 a.m. UTC | #3
On 2018-07-20 23:40, Bandan Das wrote:
> For large buffers, write may not copy the full buffer. For example,
> on Linux, write imposes a limit of 0x7ffff000. Note that this does
> not fix >4G transfers but ~>2G files will transfer successfully.
> 
> Signed-off-by: Bandan Das <bsd@redhat.com>
> ---
>  hw/usb/dev-mtp.c | 22 ++++++++++++++++++++--
>  1 file changed, 20 insertions(+), 2 deletions(-)
> 
> diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c
> index 1b72603dc5..c8f6eb4e9e 100644
> --- a/hw/usb/dev-mtp.c
> +++ b/hw/usb/dev-mtp.c
> @@ -1602,6 +1602,24 @@ static void utf16_to_str(uint8_t len, uint16_t *arr, char *name)
>      g_free(wstr);
>  }
>  
> +/* Wrapper around write, returns 0 on failure */
> +static uint64_t write_retry(int fd, void *buf, uint64_t size)
> +{
> +        uint64_t bytes_left = size, ret;
> +
> +        while (bytes_left > 0) {
> +                ret = write(fd, buf, bytes_left);
> +                if ((ret == -1) && (errno != EINTR || errno != EAGAIN ||
> +                                    errno != EWOULDBLOCK)) {

Someone opened a bug ticket about this here:

 https://bugs.launchpad.net/qemu/+bug/1798780

The check looks wrong, indeed - either "!=" should be "==" or "||"
should be "&&" here ?

 Thomas
diff mbox

Patch

diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c
index 1b72603dc5..c8f6eb4e9e 100644
--- a/hw/usb/dev-mtp.c
+++ b/hw/usb/dev-mtp.c
@@ -1602,6 +1602,24 @@  static void utf16_to_str(uint8_t len, uint16_t *arr, char *name)
     g_free(wstr);
 }
 
+/* Wrapper around write, returns 0 on failure */
+static uint64_t write_retry(int fd, void *buf, uint64_t size)
+{
+        uint64_t bytes_left = size, ret;
+
+        while (bytes_left > 0) {
+                ret = write(fd, buf, bytes_left);
+                if ((ret == -1) && (errno != EINTR || errno != EAGAIN ||
+                                    errno != EWOULDBLOCK)) {
+                        break;
+                }
+                bytes_left -= ret;
+                buf += ret;
+        }
+
+        return size - bytes_left;
+}
+
 static void usb_mtp_write_data(MTPState *s)
 {
     MTPData *d = s->data_out;
@@ -1644,8 +1662,8 @@  static void usb_mtp_write_data(MTPState *s)
             goto success;
         }
 
-        rc = write(d->fd, d->data, s->dataset.size);
-        if (rc == -1) {
+        rc = write_retry(d->fd, d->data, s->dataset.size);
+        if (!rc) {
             usb_mtp_queue_result(s, RES_STORE_FULL, d->trans,
                                  0, 0, 0, 0);
             goto done;