block/nfs: add support for libnfs v6
libnfs v6 added a new api structure for read and write requests. This effectively also adds zero copy read support for cases where the qiov coming from the block layer has only one vector. The .brdv_refresh_limits implementation is needed because libnfs v6 silently dropped support for splitting large read/write request into chunks. Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com> Signed-off-by: Peter Lieven <pl@dlhnet.de> Message-ID: <20260306142840.72923-1-pl@dlhnet.de> Reviewed-by: Kevin Wolf <kwolf@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Peter Lieven committed
Mar 6, 2026 at 14:28 UTC
37d10da587d5d241520c502706192da5806cc370
2 files changed
+50
-2
block/nfs.c
+49
-1
@@ -69,7 +69,9 @@ typedef struct NFSClient {
69
typedef struct NFSRPC {
70
BlockDriverState *bs;
71
int ret;
72
+#ifndef LIBNFS_API_V2
73
QEMUIOVector *iov;
74
+#endif
75
struct stat *st;
76
Coroutine *co;
77
NFSClient *client;
@@ -237,6 +239,7 @@ nfs_co_generic_cb(int ret, struct nfs_context *nfs, void *data,
239
NFSRPC *task = private_data;
240
task->ret = ret;
241
assert(!task->st);
242
+#ifndef LIBNFS_API_V2
243
if (task->ret > 0 && task->iov) {
244
if (task->ret <= task->iov->size) {
245
qemu_iovec_from_buf(task->iov, 0, data, task->ret);
@@ -244,6 +247,7 @@ nfs_co_generic_cb(int ret, struct nfs_context *nfs, void *data,
247
task->ret = -EIO;
248
}
249
}
250
+#endif
251
if (task->ret < 0) {
252
error_report("NFS Error: %s", nfs_get_error(nfs));
253
}
@@ -266,13 +270,36 @@ static int coroutine_fn nfs_co_preadv(BlockDriverState *bs, int64_t offset,
270
{
271
NFSClient *client = bs->opaque;
272
NFSRPC task;
273
+ char *buf = NULL;
274
+ bool my_buffer = false;
275
276
nfs_co_init_task(bs, &task);
271
- task.iov = iov;
277
+
278
+#ifdef LIBNFS_API_V2
279
+ if (iov->niov != 1) {
280
+ buf = g_try_malloc(bytes);
281
+ if (bytes && buf == NULL) {
282
+ return -ENOMEM;
283
+ }
284
+ my_buffer = true;
285
+ } else {
286
+ buf = iov->iov[0].iov_base;
287
+ }
288
+#endif
289
290
WITH_QEMU_LOCK_GUARD(&client->mutex) {
291
+#ifdef LIBNFS_API_V2
292
+ if (nfs_pread_async(client->context, client->fh,
293
+ buf, bytes, offset,
294
+ nfs_co_generic_cb, &task) != 0) {
295
+#else
296
+ task.iov = iov;
297
if (nfs_pread_async(client->context, client->fh,
298
offset, bytes, nfs_co_generic_cb, &task) != 0) {
299
+#endif
300
+ if (my_buffer) {
301
+ g_free(buf);
302
+ }
303
return -ENOMEM;
304
}
305
@@ -280,6 +307,13 @@ static int coroutine_fn nfs_co_preadv(BlockDriverState *bs, int64_t offset,
307
}
308
qemu_coroutine_yield();
309
310
+ if (my_buffer) {
311
+ if (task.ret > 0) {
312
+ qemu_iovec_from_buf(iov, 0, buf, task.ret);
313
+ }
314
+ g_free(buf);
315
+ }
316
+
317
if (task.ret < 0) {
318
return task.ret;
319
}
@@ -315,9 +349,15 @@ static int coroutine_fn nfs_co_pwritev(BlockDriverState *bs, int64_t offset,
349
}
350
351
WITH_QEMU_LOCK_GUARD(&client->mutex) {
352
+#ifdef LIBNFS_API_V2
353
+ if (nfs_pwrite_async(client->context, client->fh,
354
+ buf, bytes, offset,
355
+ nfs_co_generic_cb, &task) != 0) {
356
+#else
357
if (nfs_pwrite_async(client->context, client->fh,
358
offset, bytes, buf,
359
nfs_co_generic_cb, &task) != 0) {
360
+#endif
361
if (my_buffer) {
362
g_free(buf);
363
}
@@ -856,6 +896,13 @@ static void coroutine_fn nfs_co_invalidate_cache(BlockDriverState *bs,
896
}
897
#endif
898
899
+static void nfs_refresh_limits(BlockDriverState *bs, Error **errp)
900
+{
901
+ NFSClient *client = bs->opaque;
902
+ bs->bl.max_transfer = MIN((uint32_t)nfs_get_readmax(client->context),
903
+ (uint32_t)nfs_get_writemax(client->context));
904
+}
905
+
906
static const char *nfs_strong_runtime_opts[] = {
907
"path",
908
"user",
@@ -893,6 +940,7 @@ static BlockDriver bdrv_nfs = {
940
.bdrv_detach_aio_context = nfs_detach_aio_context,
941
.bdrv_attach_aio_context = nfs_attach_aio_context,
942
.bdrv_refresh_filename = nfs_refresh_filename,
943
+ .bdrv_refresh_limits = nfs_refresh_limits,
944
.bdrv_dirname = nfs_dirname,
945
946
.strong_runtime_opts = nfs_strong_runtime_opts,
meson.build
+1
-1
@@ -1157,7 +1157,7 @@ endif
1157
1158
libnfs = not_found
1159
if not get_option('libnfs').auto() or have_block
1160
- libnfs = dependency('libnfs', version: ['>=1.9.3', '<6.0.0'],
1160
+ libnfs = dependency('libnfs', version: '>=1.9.3',
1161
required: get_option('libnfs'),
1162
method: 'pkg-config')
1163
endif