)]}'
{
  "commit": "cf9cdaea6e24d13dfdf8402f6829d2ca4dca864b",
  "tree": "ad75b1529f5c5efb91fb0ff005915c34b57bfd08",
  "parents": [
    "7eca3d4883be8d328377001a9ea7ae9882b00f3c"
  ],
  "author": {
    "name": "Hanna Czenczek",
    "email": "hreitz@redhat.com",
    "time": "Tue Mar 24 09:43:36 2026 +0100"
  },
  "committer": {
    "name": "Kevin Wolf",
    "email": "kwolf@redhat.com",
    "time": "Tue Mar 24 19:55:09 2026 +0100"
  },
  "message": "io-uring: Resubmit tails of short writes\n\nShort writes can happen, too, not just short reads.  The difference to\naio\u003dnative 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 \u003d MIN(size, 4096);\n+    if (error_size \u003d\u003d size) {\n+        error_size \u003d 0;\n+        return -EIO;\n+    } else if (size \u003e 4096) {\n+        error_size \u003d size - 4096;\n+        size \u003d 4096;\n+    }\n\n     /* Limited by max_read, should not happen */\n     if (size \u003e 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 \u003d MIN(size, 4096);\n+    if (error_size \u003d\u003d size) {\n+        error_size \u003d 0;\n+        return -EIO;\n+    } else if (size \u003e 4096) {\n+        error_size \u003d size - 4096;\n+        size \u003d 4096;\n+    }\n\n     QEMU_BUILD_BUG_ON(FUSE_MAX_WRITE_BYTES \u003e 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 \u0027test.raw\u0027, fmt\u003draw size\u003d8192\n$ ./qemu-io -f raw -c \u0027write -P 42 0 8k\u0027 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\u003dtest,filename\u003dtest.raw \\\n    --export fuse,id\u003dexp,node-name\u003dtest,mountpoint\u003dtest.raw,writable\u003dtrue\n\n$ ./qemu-io --image-opts -c \u0027read -P 23 0 8k\u0027 \\\n    driver\u003dfile,filename\u003dtest.raw,cache.direct\u003don,aio\u003dio_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 \u0027write -P 23 0 8k\u0027 \\\n    driver\u003dfile,filename\u003dtest.raw,cache.direct\u003don,aio\u003dio_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-\u003eresubmit_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 \u003ckwolf@redhat.com\u003e\nSigned-off-by: Hanna Czenczek \u003chreitz@redhat.com\u003e\nMessage-ID: \u003c20260324084338.37453-4-hreitz@redhat.com\u003e\nSigned-off-by: Kevin Wolf \u003ckwolf@redhat.com\u003e\n",
  "tree_diff": [
    {
      "type": "modify",
      "old_id": "cb131d3b8b5bc38c4fec7327963550b1c785740b",
      "old_mode": 33188,
      "old_path": "block/io_uring.c",
      "new_id": "c48a72d37ebe842f8eb177df33177bfb14e56088",
      "new_mode": 33188,
      "new_path": "block/io_uring.c"
    },
    {
      "type": "modify",
      "old_id": "d170fc96f15f4c0bf8453ca0aac02210ae60698b",
      "old_mode": 33188,
      "old_path": "block/trace-events",
      "new_id": "950c82d4b80c7d277c496ffdfe2e618f7b2273eb",
      "new_mode": 33188,
      "new_path": "block/trace-events"
    }
  ]
}
