get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/2215519/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 2215519,
    "url": "http://patchwork.ozlabs.org/api/patches/2215519/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/qemu-devel/patch/20260324185619.296946-5-kwolf@redhat.com/",
    "project": {
        "id": 14,
        "url": "http://patchwork.ozlabs.org/api/projects/14/?format=api",
        "name": "QEMU Development",
        "link_name": "qemu-devel",
        "list_id": "qemu-devel.nongnu.org",
        "list_email": "qemu-devel@nongnu.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": "",
        "list_archive_url": "",
        "list_archive_url_format": "",
        "commit_url_format": ""
    },
    "msgid": "<20260324185619.296946-5-kwolf@redhat.com>",
    "list_archive_url": null,
    "date": "2026-03-24T18:56:19",
    "name": "[PULL,4/4] io-uring: Resubmit tails of short writes",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "ca35c2e1f8ecddb9b108602cf7b9387e7653d7bb",
    "submitter": {
        "id": 2714,
        "url": "http://patchwork.ozlabs.org/api/people/2714/?format=api",
        "name": "Kevin Wolf",
        "email": "kwolf@redhat.com"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/qemu-devel/patch/20260324185619.296946-5-kwolf@redhat.com/mbox/",
    "series": [
        {
            "id": 497329,
            "url": "http://patchwork.ozlabs.org/api/series/497329/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/qemu-devel/list/?series=497329",
            "date": "2026-03-24T18:56:15",
            "name": "[PULL,1/4] block/curl: free s->password in cleanup paths",
            "version": 1,
            "mbox": "http://patchwork.ozlabs.org/series/497329/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/2215519/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/2215519/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org>",
        "X-Original-To": "incoming@patchwork.ozlabs.org",
        "Delivered-To": "patchwork-incoming@legolas.ozlabs.org",
        "Authentication-Results": [
            "legolas.ozlabs.org;\n\tdkim=pass (1024-bit key;\n unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=a1sEjUp7;\n\tdkim-atps=neutral",
            "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org\n (client-ip=209.51.188.17; helo=lists.gnu.org;\n envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org;\n receiver=patchwork.ozlabs.org)"
        ],
        "Received": [
            "from lists.gnu.org (lists.gnu.org [209.51.188.17])\n\t(using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fgK6P4BrQz1y1g\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 25 Mar 2026 05:57:33 +1100 (AEDT)",
            "from localhost ([::1] helo=lists1p.gnu.org)\n\tby lists.gnu.org with esmtp (Exim 4.90_1)\n\t(envelope-from <qemu-devel-bounces@nongnu.org>)\n\tid 1w56vo-0004nG-1j; Tue, 24 Mar 2026 14:56:48 -0400",
            "from eggs.gnu.org ([2001:470:142:3::10])\n by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)\n (Exim 4.90_1) (envelope-from <kwolf@redhat.com>) id 1w56vl-0004lf-Gx\n for qemu-devel@nongnu.org; Tue, 24 Mar 2026 14:56:45 -0400",
            "from us-smtp-delivery-124.mimecast.com ([170.10.133.124])\n by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)\n (Exim 4.90_1) (envelope-from <kwolf@redhat.com>) id 1w56vi-0006gj-7o\n for qemu-devel@nongnu.org; Tue, 24 Mar 2026 14:56:44 -0400",
            "from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com\n (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by\n relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3,\n cipher=TLS_AES_256_GCM_SHA384) id us-mta-442-jx4g9OdBPEiQKKVwZr7gKw-1; Tue,\n 24 Mar 2026 14:56:39 -0400",
            "from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com\n (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4])\n (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest\n SHA256)\n (No client certificate requested)\n by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS\n id D60871955D84; Tue, 24 Mar 2026 18:56:38 +0000 (UTC)",
            "from merkur.fritz.box (unknown [10.44.33.246])\n by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP\n id 79F81300019F; Tue, 24 Mar 2026 18:56:36 +0000 (UTC)"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n s=mimecast20190719; t=1774378601;\n h=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n to:to:cc:cc:mime-version:mime-version:\n content-transfer-encoding:content-transfer-encoding:\n in-reply-to:in-reply-to:references:references;\n bh=vYLRVkEFu3u6NRtdyf4XOOl7vJtxtDskGaH7IviXuiY=;\n b=a1sEjUp7zcsNdIcKbA/9fq10o01xzNThTCGzUNQnWjgFa4Jhv9ecIy0pQLx3Q30yupdd/j\n AwjE1PkE5xS83d9WBZf78fHWlT2wCrr2SI6x6rsd4FptnrKkEVM4OGefTzEvMgF9jiIO5n\n vHizkKx0LpZB52Hc4SXSDRt/sDYJdDg=",
        "X-MC-Unique": "jx4g9OdBPEiQKKVwZr7gKw-1",
        "X-Mimecast-MFC-AGG-ID": "jx4g9OdBPEiQKKVwZr7gKw_1774378599",
        "From": "Kevin Wolf <kwolf@redhat.com>",
        "To": "qemu-block@nongnu.org",
        "Cc": "kwolf@redhat.com,\n\tqemu-devel@nongnu.org",
        "Subject": "[PULL 4/4] io-uring: Resubmit tails of short writes",
        "Date": "Tue, 24 Mar 2026 19:56:19 +0100",
        "Message-ID": "<20260324185619.296946-5-kwolf@redhat.com>",
        "In-Reply-To": "<20260324185619.296946-1-kwolf@redhat.com>",
        "References": "<20260324185619.296946-1-kwolf@redhat.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "X-Scanned-By": "MIMEDefang 3.4.1 on 10.30.177.4",
        "Received-SPF": "pass client-ip=170.10.133.124; envelope-from=kwolf@redhat.com;\n helo=us-smtp-delivery-124.mimecast.com",
        "X-Spam_score_int": "-20",
        "X-Spam_score": "-2.1",
        "X-Spam_bar": "--",
        "X-Spam_report": "(-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001,\n DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1,\n RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H5=0.001, RCVD_IN_MSPIKE_WL=0.001,\n RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001,\n RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001,\n SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no",
        "X-Spam_action": "no action",
        "X-BeenThere": "qemu-devel@nongnu.org",
        "X-Mailman-Version": "2.1.29",
        "Precedence": "list",
        "List-Id": "qemu development <qemu-devel.nongnu.org>",
        "List-Unsubscribe": "<https://lists.nongnu.org/mailman/options/qemu-devel>,\n <mailto:qemu-devel-request@nongnu.org?subject=unsubscribe>",
        "List-Archive": "<https://lists.nongnu.org/archive/html/qemu-devel>",
        "List-Post": "<mailto:qemu-devel@nongnu.org>",
        "List-Help": "<mailto:qemu-devel-request@nongnu.org?subject=help>",
        "List-Subscribe": "<https://lists.nongnu.org/mailman/listinfo/qemu-devel>,\n <mailto:qemu-devel-request@nongnu.org?subject=subscribe>",
        "Errors-To": "qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org",
        "Sender": "qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org"
    },
    "content": "From: Hanna Czenczek <hreitz@redhat.com>\n\nShort writes can happen, too, not just short reads.  The difference to\naio=native is that the kernel will actually retry the tail of short\nrequests internally already -- so it is harder to reproduce.  But if the\ntail of a short request returns an error to the kernel, we will see it\nin userspace still.  To reproduce this, apply the following patch on top\nof the one shown in HEAD^ (again %s/escaped // to apply):\n\nescaped diff --git a/block/export/fuse.c b/block/export/fuse.c\nescaped index 67dc50a412..2b98489a32 100644\nescaped --- a/block/export/fuse.c\nescaped +++ b/block/export/fuse.c\n@@ -1059,8 +1059,15 @@ fuse_co_read(FuseExport *exp, void **bufptr, uint64_t offset, uint32_t size)\n     int64_t blk_len;\n     void *buf;\n     int ret;\n+    static uint32_t error_size;\n\n-    size = MIN(size, 4096);\n+    if (error_size == size) {\n+        error_size = 0;\n+        return -EIO;\n+    } else if (size > 4096) {\n+        error_size = size - 4096;\n+        size = 4096;\n+    }\n\n     /* Limited by max_read, should not happen */\n     if (size > FUSE_MAX_READ_BYTES) {\n@@ -1111,8 +1118,15 @@ fuse_co_write(FuseExport *exp, struct fuse_write_out *out,\n {\n     int64_t blk_len;\n     int ret;\n+    static uint32_t error_size;\n\n-    size = MIN(size, 4096);\n+    if (error_size == size) {\n+        error_size = 0;\n+        return -EIO;\n+    } else if (size > 4096) {\n+        error_size = size - 4096;\n+        size = 4096;\n+    }\n\n     QEMU_BUILD_BUG_ON(FUSE_MAX_WRITE_BYTES > BDRV_REQUEST_MAX_BYTES);\n     /* Limited by max_write, should not happen */\n\nI know this is a bit artificial because to produce this, there must be\nan I/O error somewhere anyway, but if it does happen, qemu will\nunderstand it to mean ENOSPC for short writes, which is incorrect.  So I\nbelieve we need to resubmit the tail to maybe have it succeed now, or at\nleast get the correct error code.\n\nReproducer as before:\n$ ./qemu-img create -f raw test.raw 8k\nFormatting 'test.raw', fmt=raw size=8192\n$ ./qemu-io -f raw -c 'write -P 42 0 8k' test.raw\nwrote 8192/8192 bytes at offset 0\n8 KiB, 1 ops; 00.00 sec (64.804 MiB/sec and 8294.9003 ops/sec)\n$ hexdump -C test.raw\n00000000  2a 2a 2a 2a 2a 2a 2a 2a  2a 2a 2a 2a 2a 2a 2a 2a  |****************|\n*\n00002000\n$ storage-daemon/qemu-storage-daemon \\\n    --blockdev file,node-name=test,filename=test.raw \\\n    --export fuse,id=exp,node-name=test,mountpoint=test.raw,writable=true\n\n$ ./qemu-io --image-opts -c 'read -P 23 0 8k' \\\n    driver=file,filename=test.raw,cache.direct=on,aio=io_uring\nread 8192/8192 bytes at offset 0\n8 KiB, 1 ops; 00.00 sec (58.481 MiB/sec and 7485.5342 ops/sec)\n$ ./qemu-io --image-opts -c 'write -P 23 0 8k' \\\n    driver=file,filename=test.raw,cache.direct=on,aio=io_uring\nwrite failed: No space left on device\n$ hexdump -C test.raw\n00000000  17 17 17 17 17 17 17 17  17 17 17 17 17 17 17 17  |................|\n*\n00001000  2a 2a 2a 2a 2a 2a 2a 2a  2a 2a 2a 2a 2a 2a 2a 2a  |****************|\n*\n00002000\n\nSo short reads already work (because there is code for that), but short\nwrites incorrectly produce ENOSPC.  This patch fixes that by\nresubmitting not only the tail of short reads but short writes also.\n\n(And this patch uses the opportunity to make it so qemu_iovec_destroy()\nis called only if req->resubmit_qiov.iov is non-NULL.  Functionally a\nnon-op, but this is how the code generally checks whether the\nresubmit_qiov has been set up or not.)\n\nReviewed-by: Kevin Wolf <kwolf@redhat.com>\nSigned-off-by: Hanna Czenczek <hreitz@redhat.com>\nMessage-ID: <20260324084338.37453-4-hreitz@redhat.com>\nSigned-off-by: Kevin Wolf <kwolf@redhat.com>\n---\n block/io_uring.c   | 82 +++++++++++++++++++++++++---------------------\n block/trace-events |  2 +-\n 2 files changed, 46 insertions(+), 38 deletions(-)",
    "diff": "diff --git a/block/io_uring.c b/block/io_uring.c\nindex cb131d3b8b5..c48a72d37eb 100644\n--- a/block/io_uring.c\n+++ b/block/io_uring.c\n@@ -27,10 +27,10 @@ typedef struct {\n     BdrvRequestFlags flags;\n \n     /*\n-     * Buffered reads may require resubmission, see\n-     * luring_resubmit_short_read().\n+     * Short reads/writes require resubmission, see\n+     * luring_resubmit_short_io().\n      */\n-    int total_read;\n+    int total_done;\n     QEMUIOVector resubmit_qiov;\n \n     CqeHandler cqe_handler;\n@@ -40,10 +40,14 @@ static void luring_prep_sqe(struct io_uring_sqe *sqe, void *opaque)\n {\n     LuringRequest *req = opaque;\n     QEMUIOVector *qiov = req->qiov;\n-    uint64_t offset = req->offset;\n+    uint64_t offset = req->offset + req->total_done;\n     int fd = req->fd;\n     BdrvRequestFlags flags = req->flags;\n \n+    if (req->resubmit_qiov.iov) {\n+        qiov = &req->resubmit_qiov;\n+    }\n+\n     switch (req->type) {\n     case QEMU_AIO_WRITE:\n     {\n@@ -73,17 +77,12 @@ static void luring_prep_sqe(struct io_uring_sqe *sqe, void *opaque)\n         break;\n     case QEMU_AIO_READ:\n     {\n-        if (req->resubmit_qiov.iov != NULL) {\n-            qiov = &req->resubmit_qiov;\n-        }\n         if (qiov->niov > 1) {\n-            io_uring_prep_readv(sqe, fd, qiov->iov, qiov->niov,\n-                                offset + req->total_read);\n+            io_uring_prep_readv(sqe, fd, qiov->iov, qiov->niov, offset);\n         } else {\n             /* The man page says non-vectored is faster than vectored */\n             struct iovec *iov = qiov->iov;\n-            io_uring_prep_read(sqe, fd, iov->iov_base, iov->iov_len,\n-                               offset + req->total_read);\n+            io_uring_prep_read(sqe, fd, iov->iov_base, iov->iov_len, offset);\n         }\n         break;\n     }\n@@ -98,21 +97,26 @@ static void luring_prep_sqe(struct io_uring_sqe *sqe, void *opaque)\n }\n \n /**\n- * luring_resubmit_short_read:\n+ * luring_resubmit_short_io:\n  *\n- * Short reads are rare but may occur. The remaining read request needs to be\n- * resubmitted.\n+ * Short reads and writes are rare but may occur.  The remaining request needs\n+ * to be resubmitted.\n+ *\n+ * For example, short reads can be reproduced by a FUSE export deliberately\n+ * executing short reads.  The tail of short writes is generally resubmitted by\n+ * io-uring in the kernel, but if that resubmission encounters an I/O error, the\n+ * already submitted portion will be returned as a short write.\n  */\n-static void luring_resubmit_short_read(LuringRequest *req, int nread)\n+static void luring_resubmit_short_io(LuringRequest *req, int ndone)\n {\n     QEMUIOVector *resubmit_qiov;\n     size_t remaining;\n \n-    trace_luring_resubmit_short_read(req, nread);\n+    trace_luring_resubmit_short_io(req, ndone);\n \n-    /* Update read position */\n-    req->total_read += nread;\n-    remaining = req->qiov->size - req->total_read;\n+    /* Update I/O position */\n+    req->total_done += ndone;\n+    remaining = req->qiov->size - req->total_done;\n \n     /* Shorten qiov */\n     resubmit_qiov = &req->resubmit_qiov;\n@@ -121,7 +125,7 @@ static void luring_resubmit_short_read(LuringRequest *req, int nread)\n     } else {\n         qemu_iovec_reset(resubmit_qiov);\n     }\n-    qemu_iovec_concat(resubmit_qiov, req->qiov, req->total_read, remaining);\n+    qemu_iovec_concat(resubmit_qiov, req->qiov, req->total_done, remaining);\n \n     aio_add_sqe(luring_prep_sqe, req, &req->cqe_handler);\n }\n@@ -153,31 +157,35 @@ static void luring_cqe_handler(CqeHandler *cqe_handler)\n             return;\n         }\n     } else if (req->qiov) {\n-        /* total_read is non-zero only for resubmitted read requests */\n-        int total_bytes = ret + req->total_read;\n+        /* total_done is non-zero only for resubmitted requests */\n+        int total_bytes = ret + req->total_done;\n \n         if (total_bytes == req->qiov->size) {\n             ret = 0;\n-        } else {\n+        } else if (ret > 0 && (req->type == QEMU_AIO_READ ||\n+                               req->type == QEMU_AIO_WRITE)) {\n             /* Short Read/Write */\n-            if (req->type == QEMU_AIO_READ) {\n-                if (ret > 0) {\n-                    luring_resubmit_short_read(req, ret);\n-                    return;\n-                }\n-\n-                /* Pad with zeroes */\n-                qemu_iovec_memset(req->qiov, total_bytes, 0,\n-                                  req->qiov->size - total_bytes);\n-                ret = 0;\n-            } else {\n-                ret = -ENOSPC;\n-            }\n+            luring_resubmit_short_io(req, ret);\n+            return;\n+        } else if (req->type == QEMU_AIO_READ) {\n+            /* Read ret == 0: EOF, pad with zeroes */\n+            qemu_iovec_memset(req->qiov, total_bytes, 0,\n+                              req->qiov->size - total_bytes);\n+            ret = 0;\n+        } else {\n+            /*\n+             * Normal write ret == 0 means ENOSPC.\n+             * For zone-append, we treat any 0 <= ret < qiov->size as ENOSPC,\n+             * too, because resubmitting the tail seems a little unsafe.\n+             */\n+            ret = -ENOSPC;\n         }\n     }\n \n     req->ret = ret;\n-    qemu_iovec_destroy(&req->resubmit_qiov);\n+    if (req->resubmit_qiov.iov) {\n+        qemu_iovec_destroy(&req->resubmit_qiov);\n+    }\n \n     /*\n      * If the coroutine is already entered it must be in luring_co_submit() and\ndiff --git a/block/trace-events b/block/trace-events\nindex d170fc96f15..950c82d4b80 100644\n--- a/block/trace-events\n+++ b/block/trace-events\n@@ -64,7 +64,7 @@ file_paio_submit(void *acb, void *opaque, int64_t offset, int count, int type) \"\n # io_uring.c\n luring_cqe_handler(void *req, int ret) \"req %p ret %d\"\n luring_co_submit(void *bs, void *req, int fd, uint64_t offset, size_t nbytes, int type) \"bs %p req %p fd %d offset %\" PRId64 \" nbytes %zd type %d\"\n-luring_resubmit_short_read(void *req, int nread) \"req %p nread %d\"\n+luring_resubmit_short_io(void *req, int ndone) \"req %p ndone %d\"\n \n # qcow2.c\n qcow2_add_task(void *co, void *bs, void *pool, const char *action, int cluster_type, uint64_t host_offset, uint64_t offset, uint64_t bytes, void *qiov, size_t qiov_offset) \"co %p bs %p pool %p: %s: cluster_type %d file_cluster_offset %\" PRIu64 \" offset %\" PRIu64 \" bytes %\" PRIu64 \" qiov %p qiov_offset %zu\"\n",
    "prefixes": [
        "PULL",
        "4/4"
    ]
}