thebeanogamer / rpms / qemu-kvm

Forked from rpms/qemu-kvm 6 months ago
Clone

Blame SOURCES/kvm-nbd-server-Allow-MULTI_CONN-for-shared-writable-expo.patch

29b115
From 4a9ddf42788d3f924bdad7746f7aca615f03d7c1 Mon Sep 17 00:00:00 2001
29b115
From: Eric Blake <eblake@redhat.com>
29b115
Date: Wed, 11 May 2022 19:49:24 -0500
29b115
Subject: [PATCH 2/2] nbd/server: Allow MULTI_CONN for shared writable exports
29b115
MIME-Version: 1.0
29b115
Content-Type: text/plain; charset=UTF-8
29b115
Content-Transfer-Encoding: 8bit
29b115
29b115
RH-Author: Eric Blake <eblake@redhat.com>
29b115
RH-MergeRequest: 90: Advertise MULTI_CONN on writeable NBD servers
29b115
RH-Commit: [2/2] 53f0e885a5ed7f6e4bb14e74fe8e7957e6afe90f (ebblake/centos-qemu-kvm)
29b115
RH-Bugzilla: 1708300
29b115
RH-Acked-by: Nir Soffer <nsoffer@redhat.com>
29b115
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>
29b115
RH-Acked-by: Daniel P. Berrangé <berrange@redhat.com>
29b115
29b115
According to the NBD spec, a server that advertises
29b115
NBD_FLAG_CAN_MULTI_CONN promises that multiple client connections will
29b115
not see any cache inconsistencies: when properly separated by a single
29b115
flush, actions performed by one client will be visible to another
29b115
client, regardless of which client did the flush.
29b115
29b115
We always satisfy these conditions in qemu - even when we support
29b115
multiple clients, ALL clients go through a single point of reference
29b115
into the block layer, with no local caching.  The effect of one client
29b115
is instantly visible to the next client.  Even if our backend were a
29b115
network device, we argue that any multi-path caching effects that
29b115
would cause inconsistencies in back-to-back actions not seeing the
29b115
effect of previous actions would be a bug in that backend, and not the
29b115
fault of caching in qemu.  As such, it is safe to unconditionally
29b115
advertise CAN_MULTI_CONN for any qemu NBD server situation that
29b115
supports parallel clients.
29b115
29b115
Note, however, that we don't want to advertise CAN_MULTI_CONN when we
29b115
know that a second client cannot connect (for historical reasons,
29b115
qemu-nbd defaults to a single connection while nbd-server-add and QMP
29b115
commands default to unlimited connections; but we already have
29b115
existing means to let either style of NBD server creation alter those
29b115
defaults).  This is visible by no longer advertising MULTI_CONN for
29b115
'qemu-nbd -r' without -e, as in the iotest nbd-qemu-allocation.
29b115
29b115
The harder part of this patch is setting up an iotest to demonstrate
29b115
behavior of multiple NBD clients to a single server.  It might be
29b115
possible with parallel qemu-io processes, but I found it easier to do
29b115
in python with the help of libnbd, and help from Nir and Vladimir in
29b115
writing the test.
29b115
29b115
Signed-off-by: Eric Blake <eblake@redhat.com>
29b115
Suggested-by: Nir Soffer <nsoffer@redhat.com>
29b115
Suggested-by: Vladimir Sementsov-Ogievskiy <v.sementsov-og@mail.ru>
29b115
Message-Id: <20220512004924.417153-3-eblake@redhat.com>
29b115
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
29b115
29b115
(cherry picked from commit 58a6fdcc9efb2a7c1ef4893dca4aa5e8020ca3dc)
29b115
Conflicts:
29b115
	nbd/server.c - context, e5fb29d5 not backported
29b115
Signed-off-by: Eric Blake <eblake@redhat.com>
29b115
---
29b115
 MAINTAINERS                                   |   1 +
29b115
 blockdev-nbd.c                                |   5 +
29b115
 docs/interop/nbd.txt                          |   1 +
29b115
 docs/tools/qemu-nbd.rst                       |   3 +-
29b115
 include/block/nbd.h                           |   3 +-
29b115
 nbd/server.c                                  |  10 +-
29b115
 qapi/block-export.json                        |   8 +-
29b115
 tests/qemu-iotests/tests/nbd-multiconn        | 145 ++++++++++++++++++
29b115
 tests/qemu-iotests/tests/nbd-multiconn.out    |   5 +
29b115
 .../tests/nbd-qemu-allocation.out             |   2 +-
29b115
 10 files changed, 172 insertions(+), 11 deletions(-)
29b115
 create mode 100755 tests/qemu-iotests/tests/nbd-multiconn
29b115
 create mode 100644 tests/qemu-iotests/tests/nbd-multiconn.out
29b115
29b115
diff --git a/MAINTAINERS b/MAINTAINERS
29b115
index 4ad2451e03..2fe20a49ab 100644
29b115
--- a/MAINTAINERS
29b115
+++ b/MAINTAINERS
29b115
@@ -3370,6 +3370,7 @@ F: qemu-nbd.*
29b115
 F: blockdev-nbd.c
29b115
 F: docs/interop/nbd.txt
29b115
 F: docs/tools/qemu-nbd.rst
29b115
+F: tests/qemu-iotests/tests/*nbd*
29b115
 T: git https://repo.or.cz/qemu/ericb.git nbd
29b115
 T: git https://src.openvz.org/scm/~vsementsov/qemu.git nbd
29b115
 
29b115
diff --git a/blockdev-nbd.c b/blockdev-nbd.c
29b115
index add41a23af..c6d9b0324c 100644
29b115
--- a/blockdev-nbd.c
29b115
+++ b/blockdev-nbd.c
29b115
@@ -44,6 +44,11 @@ bool nbd_server_is_running(void)
29b115
     return nbd_server || qemu_nbd_connections >= 0;
29b115
 }
29b115
 
29b115
+int nbd_server_max_connections(void)
29b115
+{
29b115
+    return nbd_server ? nbd_server->max_connections : qemu_nbd_connections;
29b115
+}
29b115
+
29b115
 static void nbd_blockdev_client_closed(NBDClient *client, bool ignored)
29b115
 {
29b115
     nbd_client_put(client);
29b115
diff --git a/docs/interop/nbd.txt b/docs/interop/nbd.txt
29b115
index bdb0f2a41a..f5ca25174a 100644
29b115
--- a/docs/interop/nbd.txt
29b115
+++ b/docs/interop/nbd.txt
29b115
@@ -68,3 +68,4 @@ NBD_CMD_BLOCK_STATUS for "qemu:dirty-bitmap:", NBD_CMD_CACHE
29b115
 * 4.2: NBD_FLAG_CAN_MULTI_CONN for shareable read-only exports,
29b115
 NBD_CMD_FLAG_FAST_ZERO
29b115
 * 5.2: NBD_CMD_BLOCK_STATUS for "qemu:allocation-depth"
29b115
+* 7.1: NBD_FLAG_CAN_MULTI_CONN for shareable writable exports
29b115
diff --git a/docs/tools/qemu-nbd.rst b/docs/tools/qemu-nbd.rst
29b115
index 4c950f6199..8e08a29e89 100644
29b115
--- a/docs/tools/qemu-nbd.rst
29b115
+++ b/docs/tools/qemu-nbd.rst
29b115
@@ -139,8 +139,7 @@ driver options if :option:`--image-opts` is specified.
29b115
 .. option:: -e, --shared=NUM
29b115
 
29b115
   Allow up to *NUM* clients to share the device (default
29b115
-  ``1``), 0 for unlimited. Safe for readers, but for now,
29b115
-  consistency is not guaranteed between multiple writers.
29b115
+  ``1``), 0 for unlimited.
29b115
 
29b115
 .. option:: -t, --persistent
29b115
 
29b115
diff --git a/include/block/nbd.h b/include/block/nbd.h
29b115
index c5a29ce1c6..c74b7a9d2e 100644
29b115
--- a/include/block/nbd.h
29b115
+++ b/include/block/nbd.h
29b115
@@ -1,5 +1,5 @@
29b115
 /*
29b115
- *  Copyright (C) 2016-2020 Red Hat, Inc.
29b115
+ *  Copyright (C) 2016-2022 Red Hat, Inc.
29b115
  *  Copyright (C) 2005  Anthony Liguori <anthony@codemonkey.ws>
29b115
  *
29b115
  *  Network Block Device
29b115
@@ -346,6 +346,7 @@ void nbd_client_put(NBDClient *client);
29b115
 
29b115
 void nbd_server_is_qemu_nbd(int max_connections);
29b115
 bool nbd_server_is_running(void);
29b115
+int nbd_server_max_connections(void);
29b115
 void nbd_server_start(SocketAddress *addr, const char *tls_creds,
29b115
                       const char *tls_authz, uint32_t max_connections,
29b115
                       Error **errp);
29b115
diff --git a/nbd/server.c b/nbd/server.c
29b115
index c5644fd3f6..6e2157acfa 100644
29b115
--- a/nbd/server.c
29b115
+++ b/nbd/server.c
29b115
@@ -1,5 +1,5 @@
29b115
 /*
29b115
- *  Copyright (C) 2016-2021 Red Hat, Inc.
29b115
+ *  Copyright (C) 2016-2022 Red Hat, Inc.
29b115
  *  Copyright (C) 2005  Anthony Liguori <anthony@codemonkey.ws>
29b115
  *
29b115
  *  Network Block Device Server Side
29b115
@@ -1642,7 +1642,6 @@ static int nbd_export_create(BlockExport *blk_exp, BlockExportOptions *exp_args,
29b115
     int64_t size;
29b115
     uint64_t perm, shared_perm;
29b115
     bool readonly = !exp_args->writable;
29b115
-    bool shared = !exp_args->writable;
29b115
     strList *bitmaps;
29b115
     size_t i;
29b115
     int ret;
29b115
@@ -1693,11 +1692,12 @@ static int nbd_export_create(BlockExport *blk_exp, BlockExportOptions *exp_args,
29b115
     exp->description = g_strdup(arg->description);
29b115
     exp->nbdflags = (NBD_FLAG_HAS_FLAGS | NBD_FLAG_SEND_FLUSH |
29b115
                      NBD_FLAG_SEND_FUA | NBD_FLAG_SEND_CACHE);
29b115
+
29b115
+    if (nbd_server_max_connections() != 1) {
29b115
+        exp->nbdflags |= NBD_FLAG_CAN_MULTI_CONN;
29b115
+    }
29b115
     if (readonly) {
29b115
         exp->nbdflags |= NBD_FLAG_READ_ONLY;
29b115
-        if (shared) {
29b115
-            exp->nbdflags |= NBD_FLAG_CAN_MULTI_CONN;
29b115
-        }
29b115
     } else {
29b115
         exp->nbdflags |= (NBD_FLAG_SEND_TRIM | NBD_FLAG_SEND_WRITE_ZEROES |
29b115
                           NBD_FLAG_SEND_FAST_ZERO);
29b115
diff --git a/qapi/block-export.json b/qapi/block-export.json
29b115
index 1e34927f85..755ccc89b1 100644
29b115
--- a/qapi/block-export.json
29b115
+++ b/qapi/block-export.json
29b115
@@ -21,7 +21,9 @@
29b115
 #             recreated on the fly while the NBD server is active.
29b115
 #             If missing, it will default to denying access (since 4.0).
29b115
 # @max-connections: The maximum number of connections to allow at the same
29b115
-#                   time, 0 for unlimited. (since 5.2; default: 0)
29b115
+#                   time, 0 for unlimited. Setting this to 1 also stops
29b115
+#                   the server from advertising multiple client support
29b115
+#                   (since 5.2; default: 0)
29b115
 #
29b115
 # Since: 4.2
29b115
 ##
29b115
@@ -50,7 +52,9 @@
29b115
 #             recreated on the fly while the NBD server is active.
29b115
 #             If missing, it will default to denying access (since 4.0).
29b115
 # @max-connections: The maximum number of connections to allow at the same
29b115
-#                   time, 0 for unlimited. (since 5.2; default: 0)
29b115
+#                   time, 0 for unlimited. Setting this to 1 also stops
29b115
+#                   the server from advertising multiple client support
29b115
+#                   (since 5.2; default: 0).
29b115
 #
29b115
 # Returns: error if the server is already running.
29b115
 #
29b115
diff --git a/tests/qemu-iotests/tests/nbd-multiconn b/tests/qemu-iotests/tests/nbd-multiconn
29b115
new file mode 100755
29b115
index 0000000000..b121f2e363
29b115
--- /dev/null
29b115
+++ b/tests/qemu-iotests/tests/nbd-multiconn
29b115
@@ -0,0 +1,145 @@
29b115
+#!/usr/bin/env python3
29b115
+# group: rw auto quick
29b115
+#
29b115
+# Test cases for NBD multi-conn advertisement
29b115
+#
29b115
+# Copyright (C) 2022 Red Hat, Inc.
29b115
+#
29b115
+# This program is free software; you can redistribute it and/or modify
29b115
+# it under the terms of the GNU General Public License as published by
29b115
+# the Free Software Foundation; either version 2 of the License, or
29b115
+# (at your option) any later version.
29b115
+#
29b115
+# This program is distributed in the hope that it will be useful,
29b115
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
29b115
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
29b115
+# GNU General Public License for more details.
29b115
+#
29b115
+# You should have received a copy of the GNU General Public License
29b115
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
29b115
+
29b115
+import os
29b115
+from contextlib import contextmanager
29b115
+import iotests
29b115
+from iotests import qemu_img_create, qemu_io
29b115
+
29b115
+
29b115
+disk = os.path.join(iotests.test_dir, 'disk')
29b115
+size = '4M'
29b115
+nbd_sock = os.path.join(iotests.sock_dir, 'nbd_sock')
29b115
+nbd_uri = 'nbd+unix:///{}?socket=' + nbd_sock
29b115
+
29b115
+
29b115
+@contextmanager
29b115
+def open_nbd(export_name):
29b115
+    h = nbd.NBD()
29b115
+    try:
29b115
+        h.connect_uri(nbd_uri.format(export_name))
29b115
+        yield h
29b115
+    finally:
29b115
+        h.shutdown()
29b115
+
29b115
+class TestNbdMulticonn(iotests.QMPTestCase):
29b115
+    def setUp(self):
29b115
+        qemu_img_create('-f', iotests.imgfmt, disk, size)
29b115
+        qemu_io('-c', 'w -P 1 0 2M', '-c', 'w -P 2 2M 2M', disk)
29b115
+
29b115
+        self.vm = iotests.VM()
29b115
+        self.vm.launch()
29b115
+        result = self.vm.qmp('blockdev-add', {
29b115
+            'driver': 'qcow2',
29b115
+            'node-name': 'n',
29b115
+            'file': {'driver': 'file', 'filename': disk}
29b115
+        })
29b115
+        self.assert_qmp(result, 'return', {})
29b115
+
29b115
+    def tearDown(self):
29b115
+        self.vm.shutdown()
29b115
+        os.remove(disk)
29b115
+        try:
29b115
+            os.remove(nbd_sock)
29b115
+        except OSError:
29b115
+            pass
29b115
+
29b115
+    @contextmanager
29b115
+    def run_server(self, max_connections=None):
29b115
+        args = {
29b115
+            'addr': {
29b115
+                'type': 'unix',
29b115
+                'data': {'path': nbd_sock}
29b115
+            }
29b115
+        }
29b115
+        if max_connections is not None:
29b115
+            args['max-connections'] = max_connections
29b115
+
29b115
+        result = self.vm.qmp('nbd-server-start', args)
29b115
+        self.assert_qmp(result, 'return', {})
29b115
+        yield
29b115
+
29b115
+        result = self.vm.qmp('nbd-server-stop')
29b115
+        self.assert_qmp(result, 'return', {})
29b115
+
29b115
+    def add_export(self, name, writable=None):
29b115
+        args = {
29b115
+            'type': 'nbd',
29b115
+            'id': name,
29b115
+            'node-name': 'n',
29b115
+            'name': name,
29b115
+        }
29b115
+        if writable is not None:
29b115
+            args['writable'] = writable
29b115
+
29b115
+        result = self.vm.qmp('block-export-add', args)
29b115
+        self.assert_qmp(result, 'return', {})
29b115
+
29b115
+    def test_default_settings(self):
29b115
+        with self.run_server():
29b115
+            self.add_export('r')
29b115
+            self.add_export('w', writable=True)
29b115
+            with open_nbd('r') as h:
29b115
+                self.assertTrue(h.can_multi_conn())
29b115
+            with open_nbd('w') as h:
29b115
+                self.assertTrue(h.can_multi_conn())
29b115
+
29b115
+    def test_limited_connections(self):
29b115
+        with self.run_server(max_connections=1):
29b115
+            self.add_export('r')
29b115
+            self.add_export('w', writable=True)
29b115
+            with open_nbd('r') as h:
29b115
+                self.assertFalse(h.can_multi_conn())
29b115
+            with open_nbd('w') as h:
29b115
+                self.assertFalse(h.can_multi_conn())
29b115
+
29b115
+    def test_parallel_writes(self):
29b115
+        with self.run_server():
29b115
+            self.add_export('w', writable=True)
29b115
+
29b115
+            clients = [nbd.NBD() for _ in range(3)]
29b115
+            for c in clients:
29b115
+                c.connect_uri(nbd_uri.format('w'))
29b115
+                self.assertTrue(c.can_multi_conn())
29b115
+
29b115
+            initial_data = clients[0].pread(1024 * 1024, 0)
29b115
+            self.assertEqual(initial_data, b'\x01' * 1024 * 1024)
29b115
+
29b115
+            updated_data = b'\x03' * 1024 * 1024
29b115
+            clients[1].pwrite(updated_data, 0)
29b115
+            clients[2].flush()
29b115
+            current_data = clients[0].pread(1024 * 1024, 0)
29b115
+
29b115
+            self.assertEqual(updated_data, current_data)
29b115
+
29b115
+            for i in range(3):
29b115
+                clients[i].shutdown()
29b115
+
29b115
+
29b115
+if __name__ == '__main__':
29b115
+    try:
29b115
+        # Easier to use libnbd than to try and set up parallel
29b115
+        # 'qemu-nbd --list' or 'qemu-io' processes, but not all systems
29b115
+        # have libnbd installed.
29b115
+        import nbd  # type: ignore
29b115
+
29b115
+        iotests.main(supported_fmts=['qcow2'])
29b115
+    except ImportError:
29b115
+        iotests.notrun('libnbd not installed')
29b115
diff --git a/tests/qemu-iotests/tests/nbd-multiconn.out b/tests/qemu-iotests/tests/nbd-multiconn.out
29b115
new file mode 100644
29b115
index 0000000000..8d7e996700
29b115
--- /dev/null
29b115
+++ b/tests/qemu-iotests/tests/nbd-multiconn.out
29b115
@@ -0,0 +1,5 @@
29b115
+...
29b115
+----------------------------------------------------------------------
29b115
+Ran 3 tests
29b115
+
29b115
+OK
29b115
diff --git a/tests/qemu-iotests/tests/nbd-qemu-allocation.out b/tests/qemu-iotests/tests/nbd-qemu-allocation.out
29b115
index 0bf1abb063..9d938db24e 100644
29b115
--- a/tests/qemu-iotests/tests/nbd-qemu-allocation.out
29b115
+++ b/tests/qemu-iotests/tests/nbd-qemu-allocation.out
29b115
@@ -17,7 +17,7 @@ wrote 2097152/2097152 bytes at offset 1048576
29b115
 exports available: 1
29b115
  export: ''
29b115
   size:  4194304
29b115
-  flags: 0x58f ( readonly flush fua df multi cache )
29b115
+  flags: 0x48f ( readonly flush fua df cache )
29b115
   min block: 1
29b115
   opt block: 4096
29b115
   max block: 33554432
29b115
-- 
29b115
2.31.1
29b115