@samitouri / QOSamiQemu / commits / 37d10da587

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