fix qemu 2.9 drive mirroring to nbd target
cherry pick from qemu-kvm-ev-2.9.0-16.el7_4.11.1 https://cbs.centos.org/koji/buildinfo?buildID=21003 Tue Jun 13 2017 Miroslav Rezanina <mrezanin@redhat.com> - rhev-2.9.0-10.el7 - kvm-nbd-make-it-thread-safe-fix-qcow2-over-nbd.patch [bz#1454582] Tue Aug 15 2017 Miroslav Rezanina <mrezanin@redhat.com> - rhev-2.9.0-16.el7_4.4 - kvm-nbd-strict-nbd_wr_syncv.patch [bz#1467509] - kvm-nbd-read_sync-and-friends-return-0-on-success.patch [bz#1467509] - kvm-nbd-make-nbd_drop-public.patch [bz#1467509] - kvm-nbd-server-get-rid-of-nbd_negotiate_read-and-friends.patch [bz#1467509] Mon Oct 09 2017 Miroslav Rezanina <mrezanin@redhat.com> - rhev-2.9.0-16.el7_4.9 - kvm-nbd-client-Fix-regression-when-server-sends-garbage.patch [bz#1495474] - kvm-fix-build-failure-in-nbd_read_reply_entry.patch [bz#1495474] - kvm-nbd-client-avoid-spurious-qio_channel_yield-re-entry.patch [bz#1495474] - kvm-nbd-client-avoid-read_reply_co-entry-if-send-failed.patch [bz#1495474] - kvm-qemu-iotests-improve-nbd-fault-injector.py-startup-p.patch [bz#1495474] - kvm-qemu-iotests-test-NBD-over-UNIX-domain-sockets-in-08.patch [bz#1495474] - kvm-block-nbd-client-nbd_co_send_request-fix-return-code.patch [bz#1495474] - Resolves: bz#1495474
This commit is contained in:
		
							parent
							
								
									db2a3b4757
								
							
						
					
					
						commit
						b45e13fe5c
					
				
							
								
								
									
										136
									
								
								debian/patches/extra/0036-kvm-nbd-make-it-thread-safe-fix-qcow2-over-nbd.patch
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								debian/patches/extra/0036-kvm-nbd-make-it-thread-safe-fix-qcow2-over-nbd.patch
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,136 @@ | ||||
| From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 | ||||
| From: Eric Blake <eblake@redhat.com> | ||||
| Date: Sun, 11 Jun 2017 03:30:07 +0200 | ||||
| Subject: [PATCH] nbd: make it thread-safe, fix qcow2 over nbd | ||||
| 
 | ||||
| RH-Author: Eric Blake <eblake@redhat.com> | ||||
| Message-id: <20170611033007.399-1-eblake@redhat.com> | ||||
| Patchwork-id: 75581 | ||||
| O-Subject: [RHEV-7.4 qemu-kvm-rhev PATCH] nbd: make it thread-safe, fix qcow2 over nbd | ||||
| Bugzilla: 1454582 | ||||
| RH-Acked-by: Laurent Vivier <lvivier@redhat.com> | ||||
| RH-Acked-by: Max Reitz <mreitz@redhat.com> | ||||
| RH-Acked-by: Jeffrey Cody <jcody@redhat.com> | ||||
| 
 | ||||
| From: Paolo Bonzini <pbonzini@redhat.com> | ||||
| 
 | ||||
| NBD is not thread safe, because it accesses s->in_flight without | ||||
| a CoMutex.  Fixing this will be required for multiqueue. | ||||
| CoQueue doesn't have spurious wakeups but, when another coroutine can | ||||
| run between qemu_co_queue_next's wakeup and qemu_co_queue_wait's | ||||
| re-locking of the mutex, the wait condition can become false and | ||||
| a loop is necessary. | ||||
| 
 | ||||
| In fact, it turns out that the loop is necessary even without this | ||||
| multi-threaded scenario.  A particular sequence of coroutine wakeups | ||||
| is happening ~80% of the time when starting a guest with qcow2 image | ||||
| served over NBD (i.e. qemu-nbd --format=raw, and QEMU's -drive option | ||||
| has -format=qcow2).  This patch fixes that issue too. | ||||
| 
 | ||||
| Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> | ||||
| (cherry picked from commit 6bdcc018a6ed760b9dfe43539124e420aed83092) | ||||
| Signed-off-by: Eric Blake <eblake@redhat.com> | ||||
| Upstream-status: v6 pull request https://lists.gnu.org/archive/html/qemu-devel/2017-06/msg01841.html | ||||
| Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com> | ||||
| ---
 | ||||
|  block/nbd-client.c | 30 +++++++++--------------------- | ||||
|  1 file changed, 9 insertions(+), 21 deletions(-) | ||||
| 
 | ||||
| diff --git a/block/nbd-client.c b/block/nbd-client.c
 | ||||
| index 1e2952f..43e0292 100644
 | ||||
| --- a/block/nbd-client.c
 | ||||
| +++ b/block/nbd-client.c
 | ||||
| @@ -114,6 +114,10 @@ static int nbd_co_send_request(BlockDriverState *bs,
 | ||||
|      int rc, ret, i; | ||||
|   | ||||
|      qemu_co_mutex_lock(&s->send_mutex); | ||||
| +    while (s->in_flight == MAX_NBD_REQUESTS) {
 | ||||
| +        qemu_co_queue_wait(&s->free_sema, &s->send_mutex);
 | ||||
| +    }
 | ||||
| +    s->in_flight++;
 | ||||
|   | ||||
|      for (i = 0; i < MAX_NBD_REQUESTS; i++) { | ||||
|          if (s->recv_coroutine[i] == NULL) { | ||||
| @@ -176,20 +180,6 @@ static void nbd_co_receive_reply(NBDClientSession *s,
 | ||||
|      } | ||||
|  } | ||||
|   | ||||
| -static void nbd_coroutine_start(NBDClientSession *s,
 | ||||
| -                                NBDRequest *request)
 | ||||
| -{
 | ||||
| -    /* Poor man semaphore.  The free_sema is locked when no other request
 | ||||
| -     * can be accepted, and unlocked after receiving one reply.  */
 | ||||
| -    if (s->in_flight == MAX_NBD_REQUESTS) {
 | ||||
| -        qemu_co_queue_wait(&s->free_sema, NULL);
 | ||||
| -        assert(s->in_flight < MAX_NBD_REQUESTS);
 | ||||
| -    }
 | ||||
| -    s->in_flight++;
 | ||||
| -
 | ||||
| -    /* s->recv_coroutine[i] is set as soon as we get the send_lock.  */
 | ||||
| -}
 | ||||
| -
 | ||||
|  static void nbd_coroutine_end(BlockDriverState *bs, | ||||
|                                NBDRequest *request) | ||||
|  { | ||||
| @@ -197,13 +187,16 @@ static void nbd_coroutine_end(BlockDriverState *bs,
 | ||||
|      int i = HANDLE_TO_INDEX(s, request->handle); | ||||
|   | ||||
|      s->recv_coroutine[i] = NULL; | ||||
| -    s->in_flight--;
 | ||||
| -    qemu_co_queue_next(&s->free_sema);
 | ||||
|   | ||||
|      /* Kick the read_reply_co to get the next reply.  */ | ||||
|      if (s->read_reply_co) { | ||||
|          aio_co_wake(s->read_reply_co); | ||||
|      } | ||||
| +
 | ||||
| +    qemu_co_mutex_lock(&s->send_mutex);
 | ||||
| +    s->in_flight--;
 | ||||
| +    qemu_co_queue_next(&s->free_sema);
 | ||||
| +    qemu_co_mutex_unlock(&s->send_mutex);
 | ||||
|  } | ||||
|   | ||||
|  int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset, | ||||
| @@ -221,7 +214,6 @@ int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset,
 | ||||
|      assert(bytes <= NBD_MAX_BUFFER_SIZE); | ||||
|      assert(!flags); | ||||
|   | ||||
| -    nbd_coroutine_start(client, &request);
 | ||||
|      ret = nbd_co_send_request(bs, &request, NULL); | ||||
|      if (ret < 0) { | ||||
|          reply.error = -ret; | ||||
| @@ -251,7 +243,6 @@ int nbd_client_co_pwritev(BlockDriverState *bs, uint64_t offset,
 | ||||
|   | ||||
|      assert(bytes <= NBD_MAX_BUFFER_SIZE); | ||||
|   | ||||
| -    nbd_coroutine_start(client, &request);
 | ||||
|      ret = nbd_co_send_request(bs, &request, qiov); | ||||
|      if (ret < 0) { | ||||
|          reply.error = -ret; | ||||
| @@ -286,7 +277,6 @@ int nbd_client_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset,
 | ||||
|          request.flags |= NBD_CMD_FLAG_NO_HOLE; | ||||
|      } | ||||
|   | ||||
| -    nbd_coroutine_start(client, &request);
 | ||||
|      ret = nbd_co_send_request(bs, &request, NULL); | ||||
|      if (ret < 0) { | ||||
|          reply.error = -ret; | ||||
| @@ -311,7 +301,6 @@ int nbd_client_co_flush(BlockDriverState *bs)
 | ||||
|      request.from = 0; | ||||
|      request.len = 0; | ||||
|   | ||||
| -    nbd_coroutine_start(client, &request);
 | ||||
|      ret = nbd_co_send_request(bs, &request, NULL); | ||||
|      if (ret < 0) { | ||||
|          reply.error = -ret; | ||||
| @@ -337,7 +326,6 @@ int nbd_client_co_pdiscard(BlockDriverState *bs, int64_t offset, int count)
 | ||||
|          return 0; | ||||
|      } | ||||
|   | ||||
| -    nbd_coroutine_start(client, &request);
 | ||||
|      ret = nbd_co_send_request(bs, &request, NULL); | ||||
|      if (ret < 0) { | ||||
|          reply.error = -ret; | ||||
| -- 
 | ||||
| 1.8.3.1 | ||||
| 
 | ||||
							
								
								
									
										66
									
								
								debian/patches/extra/0037-kvm-nbd-strict-nbd_wr_syncv.patch
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								debian/patches/extra/0037-kvm-nbd-strict-nbd_wr_syncv.patch
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,66 @@ | ||||
| From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 | ||||
| From: Eric Blake <eblake@redhat.com> | ||||
| Date: Wed, 19 Jul 2017 18:01:59 +0200 | ||||
| Subject: [PATCH] nbd: strict nbd_wr_syncv | ||||
| 
 | ||||
| RH-Author: Eric Blake <eblake@redhat.com> | ||||
| Message-id: <20170719180202.23329-2-eblake@redhat.com> | ||||
| Patchwork-id: 75817 | ||||
| O-Subject: [RHEV-7.4.z qemu-kvm-rhev PATCH 1/4] nbd: strict nbd_wr_syncv | ||||
| Bugzilla: 1467509 | ||||
| RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com> | ||||
| RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com> | ||||
| RH-Acked-by: Jeffrey Cody <jcody@redhat.com> | ||||
| 
 | ||||
| From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||||
| 
 | ||||
| nbd_wr_syncv is called either from coroutine or from client negotiation | ||||
| code, when socket is in blocking mode. So, -EAGAIN is impossible. | ||||
| 
 | ||||
| Furthermore, EAGAIN is confusing, as, what to read/write again? With | ||||
| EAGAIN as a return code we don't know how much data is already | ||||
| read or written by the function, so in case of EAGAIN the whole | ||||
| communication is broken. | ||||
| 
 | ||||
| Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||||
| Message-Id: <20170516094533.6160-2-vsementsov@virtuozzo.com> | ||||
| Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> | ||||
| (cherry picked from commit f250a42ddaee042ad2eb02022a3ebd18fcf987de) | ||||
| Signed-off-by: Eric Blake <eblake@redhat.com> | ||||
| Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com> | ||||
| ---
 | ||||
|  nbd/common.c | 11 ++++++----- | ||||
|  1 file changed, 6 insertions(+), 5 deletions(-) | ||||
| 
 | ||||
| diff --git a/nbd/common.c b/nbd/common.c
 | ||||
| index dccbb8e..4db45b3 100644
 | ||||
| --- a/nbd/common.c
 | ||||
| +++ b/nbd/common.c
 | ||||
| @@ -20,6 +20,10 @@
 | ||||
|  #include "qapi/error.h" | ||||
|  #include "nbd-internal.h" | ||||
|   | ||||
| +/* nbd_wr_syncv
 | ||||
| + * The function may be called from coroutine or from non-coroutine context.
 | ||||
| + * When called from non-coroutine context @ioc must be in blocking mode.
 | ||||
| + */
 | ||||
|  ssize_t nbd_wr_syncv(QIOChannel *ioc, | ||||
|                       struct iovec *iov, | ||||
|                       size_t niov, | ||||
| @@ -42,11 +46,8 @@ ssize_t nbd_wr_syncv(QIOChannel *ioc,
 | ||||
|              len = qio_channel_writev(ioc, local_iov, nlocal_iov, &local_err); | ||||
|          } | ||||
|          if (len == QIO_CHANNEL_ERR_BLOCK) { | ||||
| -            if (qemu_in_coroutine()) {
 | ||||
| -                qio_channel_yield(ioc, do_read ? G_IO_IN : G_IO_OUT);
 | ||||
| -            } else {
 | ||||
| -                return -EAGAIN;
 | ||||
| -            }
 | ||||
| +            assert(qemu_in_coroutine());
 | ||||
| +            qio_channel_yield(ioc, do_read ? G_IO_IN : G_IO_OUT);
 | ||||
|              continue; | ||||
|          } | ||||
|          if (len < 0) { | ||||
| -- 
 | ||||
| 1.8.3.1 | ||||
| 
 | ||||
							
								
								
									
										620
									
								
								debian/patches/extra/0038-kvm-nbd-read_sync-and-friends-return-0-on-success.patch
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										620
									
								
								debian/patches/extra/0038-kvm-nbd-read_sync-and-friends-return-0-on-success.patch
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,620 @@ | ||||
| From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 | ||||
| From: Eric Blake <eblake@redhat.com> | ||||
| Date: Wed, 19 Jul 2017 18:02:00 +0200 | ||||
| Subject: [PATCH] nbd: read_sync and friends: return 0 on success | ||||
| 
 | ||||
| RH-Author: Eric Blake <eblake@redhat.com> | ||||
| Message-id: <20170719180202.23329-3-eblake@redhat.com> | ||||
| Patchwork-id: 75818 | ||||
| O-Subject: [RHEV-7.4.z qemu-kvm-rhev PATCH 2/4] nbd: read_sync and friends: return 0 on success | ||||
| Bugzilla: 1467509 | ||||
| RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com> | ||||
| RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com> | ||||
| RH-Acked-by: Jeffrey Cody <jcody@redhat.com> | ||||
| 
 | ||||
| From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||||
| 
 | ||||
| functions read_sync, drop_sync, write_sync, and also | ||||
| nbd_negotiate_write, nbd_negotiate_read, nbd_negotiate_drop_sync | ||||
| returns number of processed bytes. But what this number can be, | ||||
| except requested number of bytes? | ||||
| 
 | ||||
| Actually, underlying nbd_wr_syncv function returns a value >= 0 and | ||||
| != requested_bytes only on eof on read operation. So, firstly, it is
 | ||||
| impossible on write (let's add an assert) and on read it actually | ||||
| means, that communication is broken (except nbd_receive_reply, see | ||||
| below). | ||||
| 
 | ||||
| Most of callers operate like this: | ||||
|    if (func(..., size) != size) { | ||||
|        /* error path */ | ||||
|    } | ||||
| , i.e.: | ||||
|   1. They are not interested in partial success | ||||
|   2. Extra duplications in code (especially bad are duplications of | ||||
|      magic numbers) | ||||
|   3. User doesn't see actual error message, as return code is lost. | ||||
|      (this patch doesn't fix this point, but it makes fixing easier) | ||||
| 
 | ||||
| Several callers handles ret >= 0 and != requested-size separately, by | ||||
| just returning EINVAL in this case. This patch makes read_sync and | ||||
| friends return EINVAL in this case, so final behavior is the same. | ||||
| 
 | ||||
| And only one caller - nbd_receive_reply() does something not so | ||||
| obvious. It returns EINVAL for ret > 0 and != requested-size, like | ||||
| previous group, but for ret == 0 it returns 0. The only caller of | ||||
| nbd_receive_reply() - nbd_read_reply_entry() handles ret == 0 in the | ||||
| same way as ret < 0, so for now it doesn't matter. However, in | ||||
| following commits error path handling will be improved and we'll need | ||||
| to distinguish success from fail in this case too. So, this patch adds | ||||
| separate helper for this case - read_sync_eof. | ||||
| 
 | ||||
| Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||||
| Message-Id: <20170516094533.6160-3-vsementsov@virtuozzo.com> | ||||
| Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> | ||||
| (cherry picked from commit f5d406fe86bb28da85824b6581e58980cc1a07f3) | ||||
| Signed-off-by: Eric Blake <eblake@redhat.com> | ||||
| Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com> | ||||
| ---
 | ||||
|  nbd/client.c       | 63 ++++++++++++++++------------------------ | ||||
|  nbd/nbd-internal.h | 34 +++++++++++++++++++--- | ||||
|  nbd/server.c       | 84 +++++++++++++++++++++--------------------------------- | ||||
|  3 files changed, 88 insertions(+), 93 deletions(-) | ||||
| 
 | ||||
| diff --git a/nbd/client.c b/nbd/client.c
 | ||||
| index a58fb02..6b74a62 100644
 | ||||
| --- a/nbd/client.c
 | ||||
| +++ b/nbd/client.c
 | ||||
| @@ -86,9 +86,9 @@ static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports);
 | ||||
|   | ||||
|  */ | ||||
|   | ||||
| -/* Discard length bytes from channel.  Return -errno on failure, or
 | ||||
| - * the amount of bytes consumed. */
 | ||||
| -static ssize_t drop_sync(QIOChannel *ioc, size_t size)
 | ||||
| +/* Discard length bytes from channel.  Return -errno on failure and 0 on
 | ||||
| + * success*/
 | ||||
| +static int drop_sync(QIOChannel *ioc, size_t size)
 | ||||
|  { | ||||
|      ssize_t ret = 0; | ||||
|      char small[1024]; | ||||
| @@ -96,14 +96,13 @@ static ssize_t drop_sync(QIOChannel *ioc, size_t size)
 | ||||
|   | ||||
|      buffer = sizeof(small) >= size ? small : g_malloc(MIN(65536, size)); | ||||
|      while (size > 0) { | ||||
| -        ssize_t count = read_sync(ioc, buffer, MIN(65536, size));
 | ||||
| +        ssize_t count = MIN(65536, size);
 | ||||
| +        ret = read_sync(ioc, buffer, MIN(65536, size));
 | ||||
|   | ||||
| -        if (count <= 0) {
 | ||||
| +        if (ret < 0) {
 | ||||
|              goto cleanup; | ||||
|          } | ||||
| -        assert(count <= size);
 | ||||
|          size -= count; | ||||
| -        ret += count;
 | ||||
|      } | ||||
|   | ||||
|   cleanup: | ||||
| @@ -136,12 +135,12 @@ static int nbd_send_option_request(QIOChannel *ioc, uint32_t opt,
 | ||||
|      stl_be_p(&req.option, opt); | ||||
|      stl_be_p(&req.length, len); | ||||
|   | ||||
| -    if (write_sync(ioc, &req, sizeof(req)) != sizeof(req)) {
 | ||||
| +    if (write_sync(ioc, &req, sizeof(req)) < 0) {
 | ||||
|          error_setg(errp, "Failed to send option request header"); | ||||
|          return -1; | ||||
|      } | ||||
|   | ||||
| -    if (len && write_sync(ioc, (char *) data, len) != len) {
 | ||||
| +    if (len && write_sync(ioc, (char *) data, len) < 0) {
 | ||||
|          error_setg(errp, "Failed to send option request data"); | ||||
|          return -1; | ||||
|      } | ||||
| @@ -170,7 +169,7 @@ static int nbd_receive_option_reply(QIOChannel *ioc, uint32_t opt,
 | ||||
|                                      nbd_opt_reply *reply, Error **errp) | ||||
|  { | ||||
|      QEMU_BUILD_BUG_ON(sizeof(*reply) != 20); | ||||
| -    if (read_sync(ioc, reply, sizeof(*reply)) != sizeof(*reply)) {
 | ||||
| +    if (read_sync(ioc, reply, sizeof(*reply)) < 0) {
 | ||||
|          error_setg(errp, "failed to read option reply"); | ||||
|          nbd_send_opt_abort(ioc); | ||||
|          return -1; | ||||
| @@ -219,7 +218,7 @@ static int nbd_handle_reply_err(QIOChannel *ioc, nbd_opt_reply *reply,
 | ||||
|              goto cleanup; | ||||
|          } | ||||
|          msg = g_malloc(reply->length + 1); | ||||
| -        if (read_sync(ioc, msg, reply->length) != reply->length) {
 | ||||
| +        if (read_sync(ioc, msg, reply->length) < 0) {
 | ||||
|              error_setg(errp, "failed to read option error message"); | ||||
|              goto cleanup; | ||||
|          } | ||||
| @@ -321,7 +320,7 @@ static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match,
 | ||||
|          nbd_send_opt_abort(ioc); | ||||
|          return -1; | ||||
|      } | ||||
| -    if (read_sync(ioc, &namelen, sizeof(namelen)) != sizeof(namelen)) {
 | ||||
| +    if (read_sync(ioc, &namelen, sizeof(namelen)) < 0) {
 | ||||
|          error_setg(errp, "failed to read option name length"); | ||||
|          nbd_send_opt_abort(ioc); | ||||
|          return -1; | ||||
| @@ -334,7 +333,7 @@ static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match,
 | ||||
|          return -1; | ||||
|      } | ||||
|      if (namelen != strlen(want)) { | ||||
| -        if (drop_sync(ioc, len) != len) {
 | ||||
| +        if (drop_sync(ioc, len) < 0) {
 | ||||
|              error_setg(errp, "failed to skip export name with wrong length"); | ||||
|              nbd_send_opt_abort(ioc); | ||||
|              return -1; | ||||
| @@ -343,14 +342,14 @@ static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match,
 | ||||
|      } | ||||
|   | ||||
|      assert(namelen < sizeof(name)); | ||||
| -    if (read_sync(ioc, name, namelen) != namelen) {
 | ||||
| +    if (read_sync(ioc, name, namelen) < 0) {
 | ||||
|          error_setg(errp, "failed to read export name"); | ||||
|          nbd_send_opt_abort(ioc); | ||||
|          return -1; | ||||
|      } | ||||
|      name[namelen] = '\0'; | ||||
|      len -= namelen; | ||||
| -    if (drop_sync(ioc, len) != len) {
 | ||||
| +    if (drop_sync(ioc, len) < 0) {
 | ||||
|          error_setg(errp, "failed to read export description"); | ||||
|          nbd_send_opt_abort(ioc); | ||||
|          return -1; | ||||
| @@ -477,7 +476,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags,
 | ||||
|          goto fail; | ||||
|      } | ||||
|   | ||||
| -    if (read_sync(ioc, buf, 8) != 8) {
 | ||||
| +    if (read_sync(ioc, buf, 8) < 0) {
 | ||||
|          error_setg(errp, "Failed to read data"); | ||||
|          goto fail; | ||||
|      } | ||||
| @@ -503,7 +502,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags,
 | ||||
|          goto fail; | ||||
|      } | ||||
|   | ||||
| -    if (read_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
 | ||||
| +    if (read_sync(ioc, &magic, sizeof(magic)) < 0) {
 | ||||
|          error_setg(errp, "Failed to read magic"); | ||||
|          goto fail; | ||||
|      } | ||||
| @@ -515,8 +514,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags,
 | ||||
|          uint16_t globalflags; | ||||
|          bool fixedNewStyle = false; | ||||
|   | ||||
| -        if (read_sync(ioc, &globalflags, sizeof(globalflags)) !=
 | ||||
| -            sizeof(globalflags)) {
 | ||||
| +        if (read_sync(ioc, &globalflags, sizeof(globalflags)) < 0) {
 | ||||
|              error_setg(errp, "Failed to read server flags"); | ||||
|              goto fail; | ||||
|          } | ||||
| @@ -534,8 +532,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags,
 | ||||
|          } | ||||
|          /* client requested flags */ | ||||
|          clientflags = cpu_to_be32(clientflags); | ||||
| -        if (write_sync(ioc, &clientflags, sizeof(clientflags)) !=
 | ||||
| -            sizeof(clientflags)) {
 | ||||
| +        if (write_sync(ioc, &clientflags, sizeof(clientflags)) < 0) {
 | ||||
|              error_setg(errp, "Failed to send clientflags field"); | ||||
|              goto fail; | ||||
|          } | ||||
| @@ -573,13 +570,13 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags,
 | ||||
|          } | ||||
|   | ||||
|          /* Read the response */ | ||||
| -        if (read_sync(ioc, &s, sizeof(s)) != sizeof(s)) {
 | ||||
| +        if (read_sync(ioc, &s, sizeof(s)) < 0) {
 | ||||
|              error_setg(errp, "Failed to read export length"); | ||||
|              goto fail; | ||||
|          } | ||||
|          *size = be64_to_cpu(s); | ||||
|   | ||||
| -        if (read_sync(ioc, flags, sizeof(*flags)) != sizeof(*flags)) {
 | ||||
| +        if (read_sync(ioc, flags, sizeof(*flags)) < 0) {
 | ||||
|              error_setg(errp, "Failed to read export flags"); | ||||
|              goto fail; | ||||
|          } | ||||
| @@ -596,14 +593,14 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags,
 | ||||
|              goto fail; | ||||
|          } | ||||
|   | ||||
| -        if (read_sync(ioc, &s, sizeof(s)) != sizeof(s)) {
 | ||||
| +        if (read_sync(ioc, &s, sizeof(s)) < 0) {
 | ||||
|              error_setg(errp, "Failed to read export length"); | ||||
|              goto fail; | ||||
|          } | ||||
|          *size = be64_to_cpu(s); | ||||
|          TRACE("Size is %" PRIu64, *size); | ||||
|   | ||||
| -        if (read_sync(ioc, &oldflags, sizeof(oldflags)) != sizeof(oldflags)) {
 | ||||
| +        if (read_sync(ioc, &oldflags, sizeof(oldflags)) < 0) {
 | ||||
|              error_setg(errp, "Failed to read export flags"); | ||||
|              goto fail; | ||||
|          } | ||||
| @@ -619,7 +616,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags,
 | ||||
|      } | ||||
|   | ||||
|      TRACE("Size is %" PRIu64 ", export flags %" PRIx16, *size, *flags); | ||||
| -    if (zeroes && drop_sync(ioc, 124) != 124) {
 | ||||
| +    if (zeroes && drop_sync(ioc, 124) < 0) {
 | ||||
|          error_setg(errp, "Failed to read reserved block"); | ||||
|          goto fail; | ||||
|      } | ||||
| @@ -744,7 +741,6 @@ int nbd_disconnect(int fd)
 | ||||
|  ssize_t nbd_send_request(QIOChannel *ioc, NBDRequest *request) | ||||
|  { | ||||
|      uint8_t buf[NBD_REQUEST_SIZE]; | ||||
| -    ssize_t ret;
 | ||||
|   | ||||
|      TRACE("Sending request to server: " | ||||
|            "{ .from = %" PRIu64", .len = %" PRIu32 ", .handle = %" PRIu64 | ||||
| @@ -759,16 +755,7 @@ ssize_t nbd_send_request(QIOChannel *ioc, NBDRequest *request)
 | ||||
|      stq_be_p(buf + 16, request->from); | ||||
|      stl_be_p(buf + 24, request->len); | ||||
|   | ||||
| -    ret = write_sync(ioc, buf, sizeof(buf));
 | ||||
| -    if (ret < 0) {
 | ||||
| -        return ret;
 | ||||
| -    }
 | ||||
| -
 | ||||
| -    if (ret != sizeof(buf)) {
 | ||||
| -        LOG("writing to socket failed");
 | ||||
| -        return -EINVAL;
 | ||||
| -    }
 | ||||
| -    return 0;
 | ||||
| +    return write_sync(ioc, buf, sizeof(buf));
 | ||||
|  } | ||||
|   | ||||
|  ssize_t nbd_receive_reply(QIOChannel *ioc, NBDReply *reply) | ||||
| @@ -777,7 +764,7 @@ ssize_t nbd_receive_reply(QIOChannel *ioc, NBDReply *reply)
 | ||||
|      uint32_t magic; | ||||
|      ssize_t ret; | ||||
|   | ||||
| -    ret = read_sync(ioc, buf, sizeof(buf));
 | ||||
| +    ret = read_sync_eof(ioc, buf, sizeof(buf));
 | ||||
|      if (ret <= 0) { | ||||
|          return ret; | ||||
|      } | ||||
| diff --git a/nbd/nbd-internal.h b/nbd/nbd-internal.h
 | ||||
| index f43d990..e6bbc7c 100644
 | ||||
| --- a/nbd/nbd-internal.h
 | ||||
| +++ b/nbd/nbd-internal.h
 | ||||
| @@ -94,7 +94,13 @@
 | ||||
|  #define NBD_ENOSPC     28 | ||||
|  #define NBD_ESHUTDOWN  108 | ||||
|   | ||||
| -static inline ssize_t read_sync(QIOChannel *ioc, void *buffer, size_t size)
 | ||||
| +/* read_sync_eof
 | ||||
| + * Tries to read @size bytes from @ioc. Returns number of bytes actually read.
 | ||||
| + * May return a value >= 0 and < size only on EOF, i.e. when iteratively called
 | ||||
| + * qio_channel_readv() returns 0. So, there are no needs to call read_sync_eof
 | ||||
| + * iteratively.
 | ||||
| + */
 | ||||
| +static inline ssize_t read_sync_eof(QIOChannel *ioc, void *buffer, size_t size)
 | ||||
|  { | ||||
|      struct iovec iov = { .iov_base = buffer, .iov_len = size }; | ||||
|      /* Sockets are kept in blocking mode in the negotiation phase.  After | ||||
| @@ -105,12 +111,32 @@ static inline ssize_t read_sync(QIOChannel *ioc, void *buffer, size_t size)
 | ||||
|      return nbd_wr_syncv(ioc, &iov, 1, size, true); | ||||
|  } | ||||
|   | ||||
| -static inline ssize_t write_sync(QIOChannel *ioc, const void *buffer,
 | ||||
| -                                 size_t size)
 | ||||
| +/* read_sync
 | ||||
| + * Reads @size bytes from @ioc. Returns 0 on success.
 | ||||
| + */
 | ||||
| +static inline int read_sync(QIOChannel *ioc, void *buffer, size_t size)
 | ||||
| +{
 | ||||
| +    ssize_t ret = read_sync_eof(ioc, buffer, size);
 | ||||
| +
 | ||||
| +    if (ret >= 0 && ret != size) {
 | ||||
| +        ret = -EINVAL;
 | ||||
| +    }
 | ||||
| +
 | ||||
| +    return ret < 0 ? ret : 0;
 | ||||
| +}
 | ||||
| +
 | ||||
| +/* write_sync
 | ||||
| + * Writes @size bytes to @ioc. Returns 0 on success.
 | ||||
| + */
 | ||||
| +static inline int write_sync(QIOChannel *ioc, const void *buffer, size_t size)
 | ||||
|  { | ||||
|      struct iovec iov = { .iov_base = (void *) buffer, .iov_len = size }; | ||||
|   | ||||
| -    return nbd_wr_syncv(ioc, &iov, 1, size, false);
 | ||||
| +    ssize_t ret = nbd_wr_syncv(ioc, &iov, 1, size, false);
 | ||||
| +
 | ||||
| +    assert(ret < 0 || ret == size);
 | ||||
| +
 | ||||
| +    return ret < 0 ? ret : 0;
 | ||||
|  } | ||||
|   | ||||
|  struct NBDTLSHandshakeData { | ||||
| diff --git a/nbd/server.c b/nbd/server.c
 | ||||
| index a98bb21..b44cbe6 100644
 | ||||
| --- a/nbd/server.c
 | ||||
| +++ b/nbd/server.c
 | ||||
| @@ -112,7 +112,7 @@ static gboolean nbd_negotiate_continue(QIOChannel *ioc,
 | ||||
|      return TRUE; | ||||
|  } | ||||
|   | ||||
| -static ssize_t nbd_negotiate_read(QIOChannel *ioc, void *buffer, size_t size)
 | ||||
| +static int nbd_negotiate_read(QIOChannel *ioc, void *buffer, size_t size)
 | ||||
|  { | ||||
|      ssize_t ret; | ||||
|      guint watch; | ||||
| @@ -130,8 +130,7 @@ static ssize_t nbd_negotiate_read(QIOChannel *ioc, void *buffer, size_t size)
 | ||||
|   | ||||
|  } | ||||
|   | ||||
| -static ssize_t nbd_negotiate_write(QIOChannel *ioc, const void *buffer,
 | ||||
| -                                   size_t size)
 | ||||
| +static int nbd_negotiate_write(QIOChannel *ioc, const void *buffer, size_t size)
 | ||||
|  { | ||||
|      ssize_t ret; | ||||
|      guint watch; | ||||
| @@ -148,24 +147,24 @@ static ssize_t nbd_negotiate_write(QIOChannel *ioc, const void *buffer,
 | ||||
|      return ret; | ||||
|  } | ||||
|   | ||||
| -static ssize_t nbd_negotiate_drop_sync(QIOChannel *ioc, size_t size)
 | ||||
| +static int nbd_negotiate_drop_sync(QIOChannel *ioc, size_t size)
 | ||||
|  { | ||||
| -    ssize_t ret, dropped = size;
 | ||||
| +    ssize_t ret;
 | ||||
|      uint8_t *buffer = g_malloc(MIN(65536, size)); | ||||
|   | ||||
|      while (size > 0) { | ||||
| -        ret = nbd_negotiate_read(ioc, buffer, MIN(65536, size));
 | ||||
| +        size_t count = MIN(65536, size);
 | ||||
| +        ret = nbd_negotiate_read(ioc, buffer, count);
 | ||||
|          if (ret < 0) { | ||||
|              g_free(buffer); | ||||
|              return ret; | ||||
|          } | ||||
|   | ||||
| -        assert(ret <= size);
 | ||||
| -        size -= ret;
 | ||||
| +        size -= count;
 | ||||
|      } | ||||
|   | ||||
|      g_free(buffer); | ||||
| -    return dropped;
 | ||||
| +    return 0;
 | ||||
|  } | ||||
|   | ||||
|  /* Basic flow for negotiation | ||||
| @@ -206,22 +205,22 @@ static int nbd_negotiate_send_rep_len(QIOChannel *ioc, uint32_t type,
 | ||||
|            type, opt, len); | ||||
|   | ||||
|      magic = cpu_to_be64(NBD_REP_MAGIC); | ||||
| -    if (nbd_negotiate_write(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
 | ||||
| +    if (nbd_negotiate_write(ioc, &magic, sizeof(magic)) < 0) {
 | ||||
|          LOG("write failed (rep magic)"); | ||||
|          return -EINVAL; | ||||
|      } | ||||
|      opt = cpu_to_be32(opt); | ||||
| -    if (nbd_negotiate_write(ioc, &opt, sizeof(opt)) != sizeof(opt)) {
 | ||||
| +    if (nbd_negotiate_write(ioc, &opt, sizeof(opt)) < 0) {
 | ||||
|          LOG("write failed (rep opt)"); | ||||
|          return -EINVAL; | ||||
|      } | ||||
|      type = cpu_to_be32(type); | ||||
| -    if (nbd_negotiate_write(ioc, &type, sizeof(type)) != sizeof(type)) {
 | ||||
| +    if (nbd_negotiate_write(ioc, &type, sizeof(type)) < 0) {
 | ||||
|          LOG("write failed (rep type)"); | ||||
|          return -EINVAL; | ||||
|      } | ||||
|      len = cpu_to_be32(len); | ||||
| -    if (nbd_negotiate_write(ioc, &len, sizeof(len)) != sizeof(len)) {
 | ||||
| +    if (nbd_negotiate_write(ioc, &len, sizeof(len)) < 0) {
 | ||||
|          LOG("write failed (rep data length)"); | ||||
|          return -EINVAL; | ||||
|      } | ||||
| @@ -256,7 +255,7 @@ nbd_negotiate_send_rep_err(QIOChannel *ioc, uint32_t type,
 | ||||
|      if (ret < 0) { | ||||
|          goto out; | ||||
|      } | ||||
| -    if (nbd_negotiate_write(ioc, msg, len) != len) {
 | ||||
| +    if (nbd_negotiate_write(ioc, msg, len) < 0) {
 | ||||
|          LOG("write failed (error message)"); | ||||
|          ret = -EIO; | ||||
|      } else { | ||||
| @@ -287,15 +286,15 @@ static int nbd_negotiate_send_rep_list(QIOChannel *ioc, NBDExport *exp)
 | ||||
|      } | ||||
|   | ||||
|      len = cpu_to_be32(name_len); | ||||
| -    if (nbd_negotiate_write(ioc, &len, sizeof(len)) != sizeof(len)) {
 | ||||
| +    if (nbd_negotiate_write(ioc, &len, sizeof(len)) < 0) {
 | ||||
|          LOG("write failed (name length)"); | ||||
|          return -EINVAL; | ||||
|      } | ||||
| -    if (nbd_negotiate_write(ioc, name, name_len) != name_len) {
 | ||||
| +    if (nbd_negotiate_write(ioc, name, name_len) < 0) {
 | ||||
|          LOG("write failed (name buffer)"); | ||||
|          return -EINVAL; | ||||
|      } | ||||
| -    if (nbd_negotiate_write(ioc, desc, desc_len) != desc_len) {
 | ||||
| +    if (nbd_negotiate_write(ioc, desc, desc_len) < 0) {
 | ||||
|          LOG("write failed (description buffer)"); | ||||
|          return -EINVAL; | ||||
|      } | ||||
| @@ -309,7 +308,7 @@ static int nbd_negotiate_handle_list(NBDClient *client, uint32_t length)
 | ||||
|      NBDExport *exp; | ||||
|   | ||||
|      if (length) { | ||||
| -        if (nbd_negotiate_drop_sync(client->ioc, length) != length) {
 | ||||
| +        if (nbd_negotiate_drop_sync(client->ioc, length) < 0) {
 | ||||
|              return -EIO; | ||||
|          } | ||||
|          return nbd_negotiate_send_rep_err(client->ioc, | ||||
| @@ -340,7 +339,7 @@ static int nbd_negotiate_handle_export_name(NBDClient *client, uint32_t length)
 | ||||
|          LOG("Bad length received"); | ||||
|          goto fail; | ||||
|      } | ||||
| -    if (nbd_negotiate_read(client->ioc, name, length) != length) {
 | ||||
| +    if (nbd_negotiate_read(client->ioc, name, length) < 0) {
 | ||||
|          LOG("read failed"); | ||||
|          goto fail; | ||||
|      } | ||||
| @@ -373,7 +372,7 @@ static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client,
 | ||||
|      TRACE("Setting up TLS"); | ||||
|      ioc = client->ioc; | ||||
|      if (length) { | ||||
| -        if (nbd_negotiate_drop_sync(ioc, length) != length) {
 | ||||
| +        if (nbd_negotiate_drop_sync(ioc, length) < 0) {
 | ||||
|              return NULL; | ||||
|          } | ||||
|          nbd_negotiate_send_rep_err(ioc, NBD_REP_ERR_INVALID, NBD_OPT_STARTTLS, | ||||
| @@ -437,8 +436,7 @@ static int nbd_negotiate_options(NBDClient *client)
 | ||||
|          ...           Rest of request | ||||
|      */ | ||||
|   | ||||
| -    if (nbd_negotiate_read(client->ioc, &flags, sizeof(flags)) !=
 | ||||
| -        sizeof(flags)) {
 | ||||
| +    if (nbd_negotiate_read(client->ioc, &flags, sizeof(flags)) < 0) {
 | ||||
|          LOG("read failed"); | ||||
|          return -EIO; | ||||
|      } | ||||
| @@ -464,8 +462,7 @@ static int nbd_negotiate_options(NBDClient *client)
 | ||||
|          uint32_t clientflags, length; | ||||
|          uint64_t magic; | ||||
|   | ||||
| -        if (nbd_negotiate_read(client->ioc, &magic, sizeof(magic)) !=
 | ||||
| -            sizeof(magic)) {
 | ||||
| +        if (nbd_negotiate_read(client->ioc, &magic, sizeof(magic)) < 0) {
 | ||||
|              LOG("read failed"); | ||||
|              return -EINVAL; | ||||
|          } | ||||
| @@ -476,14 +473,14 @@ static int nbd_negotiate_options(NBDClient *client)
 | ||||
|          } | ||||
|   | ||||
|          if (nbd_negotiate_read(client->ioc, &clientflags, | ||||
| -                               sizeof(clientflags)) != sizeof(clientflags)) {
 | ||||
| +                               sizeof(clientflags)) < 0)
 | ||||
| +        {
 | ||||
|              LOG("read failed"); | ||||
|              return -EINVAL; | ||||
|          } | ||||
|          clientflags = be32_to_cpu(clientflags); | ||||
|   | ||||
| -        if (nbd_negotiate_read(client->ioc, &length, sizeof(length)) !=
 | ||||
| -            sizeof(length)) {
 | ||||
| +        if (nbd_negotiate_read(client->ioc, &length, sizeof(length)) < 0) {
 | ||||
|              LOG("read failed"); | ||||
|              return -EINVAL; | ||||
|          } | ||||
| @@ -513,7 +510,7 @@ static int nbd_negotiate_options(NBDClient *client)
 | ||||
|                  return -EINVAL; | ||||
|   | ||||
|              default: | ||||
| -                if (nbd_negotiate_drop_sync(client->ioc, length) != length) {
 | ||||
| +                if (nbd_negotiate_drop_sync(client->ioc, length) < 0) {
 | ||||
|                      return -EIO; | ||||
|                  } | ||||
|                  ret = nbd_negotiate_send_rep_err(client->ioc, | ||||
| @@ -551,7 +548,7 @@ static int nbd_negotiate_options(NBDClient *client)
 | ||||
|                  return nbd_negotiate_handle_export_name(client, length); | ||||
|   | ||||
|              case NBD_OPT_STARTTLS: | ||||
| -                if (nbd_negotiate_drop_sync(client->ioc, length) != length) {
 | ||||
| +                if (nbd_negotiate_drop_sync(client->ioc, length) < 0) {
 | ||||
|                      return -EIO; | ||||
|                  } | ||||
|                  if (client->tlscreds) { | ||||
| @@ -570,7 +567,7 @@ static int nbd_negotiate_options(NBDClient *client)
 | ||||
|                  } | ||||
|                  break; | ||||
|              default: | ||||
| -                if (nbd_negotiate_drop_sync(client->ioc, length) != length) {
 | ||||
| +                if (nbd_negotiate_drop_sync(client->ioc, length) < 0) {
 | ||||
|                      return -EIO; | ||||
|                  } | ||||
|                  ret = nbd_negotiate_send_rep_err(client->ioc, | ||||
| @@ -659,12 +656,12 @@ static coroutine_fn int nbd_negotiate(NBDClientNewData *data)
 | ||||
|              TRACE("TLS cannot be enabled with oldstyle protocol"); | ||||
|              goto fail; | ||||
|          } | ||||
| -        if (nbd_negotiate_write(client->ioc, buf, sizeof(buf)) != sizeof(buf)) {
 | ||||
| +        if (nbd_negotiate_write(client->ioc, buf, sizeof(buf)) < 0) {
 | ||||
|              LOG("write failed"); | ||||
|              goto fail; | ||||
|          } | ||||
|      } else { | ||||
| -        if (nbd_negotiate_write(client->ioc, buf, 18) != 18) {
 | ||||
| +        if (nbd_negotiate_write(client->ioc, buf, 18) < 0) {
 | ||||
|              LOG("write failed"); | ||||
|              goto fail; | ||||
|          } | ||||
| @@ -679,7 +676,7 @@ static coroutine_fn int nbd_negotiate(NBDClientNewData *data)
 | ||||
|          stq_be_p(buf + 18, client->exp->size); | ||||
|          stw_be_p(buf + 26, client->exp->nbdflags | myflags); | ||||
|          len = client->no_zeroes ? 10 : sizeof(buf) - 18; | ||||
| -        if (nbd_negotiate_write(client->ioc, buf + 18, len) != len) {
 | ||||
| +        if (nbd_negotiate_write(client->ioc, buf + 18, len) < 0) {
 | ||||
|              LOG("write failed"); | ||||
|              goto fail; | ||||
|          } | ||||
| @@ -702,11 +699,6 @@ static ssize_t nbd_receive_request(QIOChannel *ioc, NBDRequest *request)
 | ||||
|          return ret; | ||||
|      } | ||||
|   | ||||
| -    if (ret != sizeof(buf)) {
 | ||||
| -        LOG("read failed");
 | ||||
| -        return -EINVAL;
 | ||||
| -    }
 | ||||
| -
 | ||||
|      /* Request | ||||
|         [ 0 ..  3]   magic   (NBD_REQUEST_MAGIC) | ||||
|         [ 4 ..  5]   flags   (NBD_CMD_FLAG_FUA, ...) | ||||
| @@ -737,7 +729,6 @@ static ssize_t nbd_receive_request(QIOChannel *ioc, NBDRequest *request)
 | ||||
|  static ssize_t nbd_send_reply(QIOChannel *ioc, NBDReply *reply) | ||||
|  { | ||||
|      uint8_t buf[NBD_REPLY_SIZE]; | ||||
| -    ssize_t ret;
 | ||||
|   | ||||
|      reply->error = system_errno_to_nbd_errno(reply->error); | ||||
|   | ||||
| @@ -754,16 +745,7 @@ static ssize_t nbd_send_reply(QIOChannel *ioc, NBDReply *reply)
 | ||||
|      stl_be_p(buf + 4, reply->error); | ||||
|      stq_be_p(buf + 8, reply->handle); | ||||
|   | ||||
| -    ret = write_sync(ioc, buf, sizeof(buf));
 | ||||
| -    if (ret < 0) {
 | ||||
| -        return ret;
 | ||||
| -    }
 | ||||
| -
 | ||||
| -    if (ret != sizeof(buf)) {
 | ||||
| -        LOG("writing to socket failed");
 | ||||
| -        return -EINVAL;
 | ||||
| -    }
 | ||||
| -    return 0;
 | ||||
| +    return write_sync(ioc, buf, sizeof(buf));
 | ||||
|  } | ||||
|   | ||||
|  #define MAX_NBD_REQUESTS 16 | ||||
| @@ -1067,7 +1049,7 @@ static ssize_t nbd_co_send_reply(NBDRequestData *req, NBDReply *reply,
 | ||||
|          rc = nbd_send_reply(client->ioc, reply); | ||||
|          if (rc >= 0) { | ||||
|              ret = write_sync(client->ioc, req->data, len); | ||||
| -            if (ret != len) {
 | ||||
| +            if (ret < 0) {
 | ||||
|                  rc = -EIO; | ||||
|              } | ||||
|          } | ||||
| @@ -1141,7 +1123,7 @@ static ssize_t nbd_co_receive_request(NBDRequestData *req,
 | ||||
|      if (request->type == NBD_CMD_WRITE) { | ||||
|          TRACE("Reading %" PRIu32 " byte(s)", request->len); | ||||
|   | ||||
| -        if (read_sync(client->ioc, req->data, request->len) != request->len) {
 | ||||
| +        if (read_sync(client->ioc, req->data, request->len) < 0) {
 | ||||
|              LOG("reading from socket failed"); | ||||
|              rc = -EIO; | ||||
|              goto out; | ||||
| -- 
 | ||||
| 1.8.3.1 | ||||
| 
 | ||||
							
								
								
									
										151
									
								
								debian/patches/extra/0039-kvm-nbd-make-nbd_drop-public.patch
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								debian/patches/extra/0039-kvm-nbd-make-nbd_drop-public.patch
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,151 @@ | ||||
| From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 | ||||
| From: Eric Blake <eblake@redhat.com> | ||||
| Date: Wed, 19 Jul 2017 18:02:01 +0200 | ||||
| Subject: [PATCH] nbd: make nbd_drop public | ||||
| 
 | ||||
| RH-Author: Eric Blake <eblake@redhat.com> | ||||
| Message-id: <20170719180202.23329-4-eblake@redhat.com> | ||||
| Patchwork-id: 75814 | ||||
| O-Subject: [RHEV-7.4.z qemu-kvm-rhev PATCH 3/4] nbd: make nbd_drop public | ||||
| Bugzilla: 1467509 | ||||
| RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com> | ||||
| RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com> | ||||
| RH-Acked-by: Jeffrey Cody <jcody@redhat.com> | ||||
| 
 | ||||
| From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||||
| 
 | ||||
| Following commit will reuse it for nbd server too. | ||||
| 
 | ||||
| Reviewed-by: Eric Blake <eblake@redhat.com> | ||||
| Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||||
| Message-Id: <20170602150150.258222-3-vsementsov@virtuozzo.com> | ||||
| Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> | ||||
| (cherry picked from commit 44298024d30ad36439707b89715a76333f58791b) | ||||
| 
 | ||||
|  Conflicts: | ||||
| 	nbd/client.c, nbd/nbd_internal.h, nbd/common.c - missing errp | ||||
| 	addition (e44ed99) and bulk rename (d1fdf25) | ||||
| 
 | ||||
| Signed-off-by: Eric Blake <eblake@redhat.com> | ||||
| Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com> | ||||
| ---
 | ||||
|  nbd/client.c       | 32 +++----------------------------- | ||||
|  nbd/common.c       | 26 ++++++++++++++++++++++++++ | ||||
|  nbd/nbd-internal.h |  2 ++ | ||||
|  3 files changed, 31 insertions(+), 29 deletions(-) | ||||
| 
 | ||||
| diff --git a/nbd/client.c b/nbd/client.c
 | ||||
| index 6b74a62..1652f28 100644
 | ||||
| --- a/nbd/client.c
 | ||||
| +++ b/nbd/client.c
 | ||||
| @@ -86,32 +86,6 @@ static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports);
 | ||||
|   | ||||
|  */ | ||||
|   | ||||
| -/* Discard length bytes from channel.  Return -errno on failure and 0 on
 | ||||
| - * success*/
 | ||||
| -static int drop_sync(QIOChannel *ioc, size_t size)
 | ||||
| -{
 | ||||
| -    ssize_t ret = 0;
 | ||||
| -    char small[1024];
 | ||||
| -    char *buffer;
 | ||||
| -
 | ||||
| -    buffer = sizeof(small) >= size ? small : g_malloc(MIN(65536, size));
 | ||||
| -    while (size > 0) {
 | ||||
| -        ssize_t count = MIN(65536, size);
 | ||||
| -        ret = read_sync(ioc, buffer, MIN(65536, size));
 | ||||
| -
 | ||||
| -        if (ret < 0) {
 | ||||
| -            goto cleanup;
 | ||||
| -        }
 | ||||
| -        size -= count;
 | ||||
| -    }
 | ||||
| -
 | ||||
| - cleanup:
 | ||||
| -    if (buffer != small) {
 | ||||
| -        g_free(buffer);
 | ||||
| -    }
 | ||||
| -    return ret;
 | ||||
| -}
 | ||||
| -
 | ||||
|  /* Send an option request. | ||||
|   * | ||||
|   * The request is for option @opt, with @data containing @len bytes of | ||||
| @@ -333,7 +307,7 @@ static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match,
 | ||||
|          return -1; | ||||
|      } | ||||
|      if (namelen != strlen(want)) { | ||||
| -        if (drop_sync(ioc, len) < 0) {
 | ||||
| +        if (nbd_drop(ioc, len) < 0) {
 | ||||
|              error_setg(errp, "failed to skip export name with wrong length"); | ||||
|              nbd_send_opt_abort(ioc); | ||||
|              return -1; | ||||
| @@ -349,7 +323,7 @@ static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match,
 | ||||
|      } | ||||
|      name[namelen] = '\0'; | ||||
|      len -= namelen; | ||||
| -    if (drop_sync(ioc, len) < 0) {
 | ||||
| +    if (nbd_drop(ioc, len) < 0) {
 | ||||
|          error_setg(errp, "failed to read export description"); | ||||
|          nbd_send_opt_abort(ioc); | ||||
|          return -1; | ||||
| @@ -616,7 +590,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags,
 | ||||
|      } | ||||
|   | ||||
|      TRACE("Size is %" PRIu64 ", export flags %" PRIx16, *size, *flags); | ||||
| -    if (zeroes && drop_sync(ioc, 124) < 0) {
 | ||||
| +    if (zeroes && nbd_drop(ioc, 124) < 0) {
 | ||||
|          error_setg(errp, "Failed to read reserved block"); | ||||
|          goto fail; | ||||
|      } | ||||
| diff --git a/nbd/common.c b/nbd/common.c
 | ||||
| index 4db45b3..9a54010 100644
 | ||||
| --- a/nbd/common.c
 | ||||
| +++ b/nbd/common.c
 | ||||
| @@ -71,6 +71,32 @@ ssize_t nbd_wr_syncv(QIOChannel *ioc,
 | ||||
|      return done; | ||||
|  } | ||||
|   | ||||
| +/* Discard length bytes from channel.  Return -errno on failure and 0 on
 | ||||
| + * success */
 | ||||
| +int nbd_drop(QIOChannel *ioc, size_t size)
 | ||||
| +{
 | ||||
| +    ssize_t ret = 0;
 | ||||
| +    char small[1024];
 | ||||
| +    char *buffer;
 | ||||
| +
 | ||||
| +    buffer = sizeof(small) >= size ? small : g_malloc(MIN(65536, size));
 | ||||
| +    while (size > 0) {
 | ||||
| +        ssize_t count = MIN(65536, size);
 | ||||
| +        ret = read_sync(ioc, buffer, MIN(65536, size));
 | ||||
| +
 | ||||
| +        if (ret < 0) {
 | ||||
| +            goto cleanup;
 | ||||
| +        }
 | ||||
| +        size -= count;
 | ||||
| +    }
 | ||||
| +
 | ||||
| + cleanup:
 | ||||
| +    if (buffer != small) {
 | ||||
| +        g_free(buffer);
 | ||||
| +    }
 | ||||
| +    return ret;
 | ||||
| +}
 | ||||
| +
 | ||||
|   | ||||
|  void nbd_tls_handshake(QIOTask *task, | ||||
|                         void *opaque) | ||||
| diff --git a/nbd/nbd-internal.h b/nbd/nbd-internal.h
 | ||||
| index e6bbc7c..c02378c 100644
 | ||||
| --- a/nbd/nbd-internal.h
 | ||||
| +++ b/nbd/nbd-internal.h
 | ||||
| @@ -149,4 +149,6 @@ struct NBDTLSHandshakeData {
 | ||||
|  void nbd_tls_handshake(QIOTask *task, | ||||
|                         void *opaque); | ||||
|   | ||||
| +int nbd_drop(QIOChannel *ioc, size_t size);
 | ||||
| +
 | ||||
|  #endif | ||||
| -- 
 | ||||
| 1.8.3.1 | ||||
| 
 | ||||
							
								
								
									
										292
									
								
								debian/patches/extra/0040-kvm-nbd-server-get-rid-of-nbd_negotiate_read-and-friends.patch
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										292
									
								
								debian/patches/extra/0040-kvm-nbd-server-get-rid-of-nbd_negotiate_read-and-friends.patch
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,292 @@ | ||||
| From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 | ||||
| From: Eric Blake <eblake@redhat.com> | ||||
| Date: Wed, 19 Jul 2017 18:02:02 +0200 | ||||
| Subject: [PATCH] nbd/server: get rid of nbd_negotiate_read and friends | ||||
| 
 | ||||
| RH-Author: Eric Blake <eblake@redhat.com> | ||||
| Message-id: <20170719180202.23329-5-eblake@redhat.com> | ||||
| Patchwork-id: 75816 | ||||
| O-Subject: [RHEV-7.4.z qemu-kvm-rhev PATCH 4/4] nbd/server: get rid of nbd_negotiate_read and friends | ||||
| Bugzilla: 1467509 | ||||
| RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com> | ||||
| RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com> | ||||
| RH-Acked-by: Jeffrey Cody <jcody@redhat.com> | ||||
| 
 | ||||
| From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||||
| 
 | ||||
| Functions nbd_negotiate_{read,write,drop_sync} were introduced in | ||||
| 1a6245a5b, when nbd_rwv (was nbd_wr_sync) was working through | ||||
| qemu_co_sendv_recvv (the path is nbd_wr_sync -> qemu_co_{recv/send} -> | ||||
| qemu_co_send_recv -> qemu_co_sendv_recvv), which just yields, without | ||||
| setting any handlers. But starting from ff82911cd nbd_rwv (was | ||||
| nbd_wr_syncv) works through qio_channel_yield() which sets handlers, so | ||||
| watchers are redundant in nbd_negotiate_{read,write,drop_sync}, then, | ||||
| let's just use nbd_{read,write,drop} functions. | ||||
| 
 | ||||
| Functions nbd_{read,write,drop} has errp parameter, which is unused in | ||||
| this patch. This will be fixed later. | ||||
| 
 | ||||
| Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||||
| Reviewed-by: Eric Blake <eblake@redhat.com> | ||||
| Message-Id: <20170602150150.258222-4-vsementsov@virtuozzo.com> | ||||
| Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> | ||||
| (cherry picked from commit 2b0bbc4f8809c972bad134bc1a2570dbb01dea0b) | ||||
| 
 | ||||
| Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com> | ||||
| 
 | ||||
| Conflicts: | ||||
| 	nbd/server.c - missing errp addition (e44ed99) and bulk | ||||
| 	rename (d1fdf25) | ||||
| Fixes CVE-2017-7539 | ||||
| 
 | ||||
| Signed-off-by: Eric Blake <eblake@redhat.com> | ||||
| ---
 | ||||
|  nbd/server.c | 106 ++++++++++++----------------------------------------------- | ||||
|  1 file changed, 21 insertions(+), 85 deletions(-) | ||||
| 
 | ||||
| diff --git a/nbd/server.c b/nbd/server.c
 | ||||
| index b44cbe6..8e3b8e5 100644
 | ||||
| --- a/nbd/server.c
 | ||||
| +++ b/nbd/server.c
 | ||||
| @@ -104,69 +104,6 @@ struct NBDClient {
 | ||||
|   | ||||
|  static void nbd_client_receive_next_request(NBDClient *client); | ||||
|   | ||||
| -static gboolean nbd_negotiate_continue(QIOChannel *ioc,
 | ||||
| -                                       GIOCondition condition,
 | ||||
| -                                       void *opaque)
 | ||||
| -{
 | ||||
| -    qemu_coroutine_enter(opaque);
 | ||||
| -    return TRUE;
 | ||||
| -}
 | ||||
| -
 | ||||
| -static int nbd_negotiate_read(QIOChannel *ioc, void *buffer, size_t size)
 | ||||
| -{
 | ||||
| -    ssize_t ret;
 | ||||
| -    guint watch;
 | ||||
| -
 | ||||
| -    assert(qemu_in_coroutine());
 | ||||
| -    /* Negotiation are always in main loop. */
 | ||||
| -    watch = qio_channel_add_watch(ioc,
 | ||||
| -                                  G_IO_IN,
 | ||||
| -                                  nbd_negotiate_continue,
 | ||||
| -                                  qemu_coroutine_self(),
 | ||||
| -                                  NULL);
 | ||||
| -    ret = read_sync(ioc, buffer, size);
 | ||||
| -    g_source_remove(watch);
 | ||||
| -    return ret;
 | ||||
| -
 | ||||
| -}
 | ||||
| -
 | ||||
| -static int nbd_negotiate_write(QIOChannel *ioc, const void *buffer, size_t size)
 | ||||
| -{
 | ||||
| -    ssize_t ret;
 | ||||
| -    guint watch;
 | ||||
| -
 | ||||
| -    assert(qemu_in_coroutine());
 | ||||
| -    /* Negotiation are always in main loop. */
 | ||||
| -    watch = qio_channel_add_watch(ioc,
 | ||||
| -                                  G_IO_OUT,
 | ||||
| -                                  nbd_negotiate_continue,
 | ||||
| -                                  qemu_coroutine_self(),
 | ||||
| -                                  NULL);
 | ||||
| -    ret = write_sync(ioc, buffer, size);
 | ||||
| -    g_source_remove(watch);
 | ||||
| -    return ret;
 | ||||
| -}
 | ||||
| -
 | ||||
| -static int nbd_negotiate_drop_sync(QIOChannel *ioc, size_t size)
 | ||||
| -{
 | ||||
| -    ssize_t ret;
 | ||||
| -    uint8_t *buffer = g_malloc(MIN(65536, size));
 | ||||
| -
 | ||||
| -    while (size > 0) {
 | ||||
| -        size_t count = MIN(65536, size);
 | ||||
| -        ret = nbd_negotiate_read(ioc, buffer, count);
 | ||||
| -        if (ret < 0) {
 | ||||
| -            g_free(buffer);
 | ||||
| -            return ret;
 | ||||
| -        }
 | ||||
| -
 | ||||
| -        size -= count;
 | ||||
| -    }
 | ||||
| -
 | ||||
| -    g_free(buffer);
 | ||||
| -    return 0;
 | ||||
| -}
 | ||||
| -
 | ||||
|  /* Basic flow for negotiation | ||||
|   | ||||
|     Server         Client | ||||
| @@ -205,22 +142,22 @@ static int nbd_negotiate_send_rep_len(QIOChannel *ioc, uint32_t type,
 | ||||
|            type, opt, len); | ||||
|   | ||||
|      magic = cpu_to_be64(NBD_REP_MAGIC); | ||||
| -    if (nbd_negotiate_write(ioc, &magic, sizeof(magic)) < 0) {
 | ||||
| +    if (write_sync(ioc, &magic, sizeof(magic)) < 0) {
 | ||||
|          LOG("write failed (rep magic)"); | ||||
|          return -EINVAL; | ||||
|      } | ||||
|      opt = cpu_to_be32(opt); | ||||
| -    if (nbd_negotiate_write(ioc, &opt, sizeof(opt)) < 0) {
 | ||||
| +    if (write_sync(ioc, &opt, sizeof(opt)) < 0) {
 | ||||
|          LOG("write failed (rep opt)"); | ||||
|          return -EINVAL; | ||||
|      } | ||||
|      type = cpu_to_be32(type); | ||||
| -    if (nbd_negotiate_write(ioc, &type, sizeof(type)) < 0) {
 | ||||
| +    if (write_sync(ioc, &type, sizeof(type)) < 0) {
 | ||||
|          LOG("write failed (rep type)"); | ||||
|          return -EINVAL; | ||||
|      } | ||||
|      len = cpu_to_be32(len); | ||||
| -    if (nbd_negotiate_write(ioc, &len, sizeof(len)) < 0) {
 | ||||
| +    if (write_sync(ioc, &len, sizeof(len)) < 0) {
 | ||||
|          LOG("write failed (rep data length)"); | ||||
|          return -EINVAL; | ||||
|      } | ||||
| @@ -255,7 +192,7 @@ nbd_negotiate_send_rep_err(QIOChannel *ioc, uint32_t type,
 | ||||
|      if (ret < 0) { | ||||
|          goto out; | ||||
|      } | ||||
| -    if (nbd_negotiate_write(ioc, msg, len) < 0) {
 | ||||
| +    if (write_sync(ioc, msg, len) < 0) {
 | ||||
|          LOG("write failed (error message)"); | ||||
|          ret = -EIO; | ||||
|      } else { | ||||
| @@ -286,15 +223,15 @@ static int nbd_negotiate_send_rep_list(QIOChannel *ioc, NBDExport *exp)
 | ||||
|      } | ||||
|   | ||||
|      len = cpu_to_be32(name_len); | ||||
| -    if (nbd_negotiate_write(ioc, &len, sizeof(len)) < 0) {
 | ||||
| +    if (write_sync(ioc, &len, sizeof(len)) < 0) {
 | ||||
|          LOG("write failed (name length)"); | ||||
|          return -EINVAL; | ||||
|      } | ||||
| -    if (nbd_negotiate_write(ioc, name, name_len) < 0) {
 | ||||
| +    if (write_sync(ioc, name, name_len) < 0) {
 | ||||
|          LOG("write failed (name buffer)"); | ||||
|          return -EINVAL; | ||||
|      } | ||||
| -    if (nbd_negotiate_write(ioc, desc, desc_len) < 0) {
 | ||||
| +    if (write_sync(ioc, desc, desc_len) < 0) {
 | ||||
|          LOG("write failed (description buffer)"); | ||||
|          return -EINVAL; | ||||
|      } | ||||
| @@ -308,7 +245,7 @@ static int nbd_negotiate_handle_list(NBDClient *client, uint32_t length)
 | ||||
|      NBDExport *exp; | ||||
|   | ||||
|      if (length) { | ||||
| -        if (nbd_negotiate_drop_sync(client->ioc, length) < 0) {
 | ||||
| +        if (nbd_drop(client->ioc, length) < 0) {
 | ||||
|              return -EIO; | ||||
|          } | ||||
|          return nbd_negotiate_send_rep_err(client->ioc, | ||||
| @@ -339,7 +276,7 @@ static int nbd_negotiate_handle_export_name(NBDClient *client, uint32_t length)
 | ||||
|          LOG("Bad length received"); | ||||
|          goto fail; | ||||
|      } | ||||
| -    if (nbd_negotiate_read(client->ioc, name, length) < 0) {
 | ||||
| +    if (read_sync(client->ioc, name, length) < 0) {
 | ||||
|          LOG("read failed"); | ||||
|          goto fail; | ||||
|      } | ||||
| @@ -372,7 +309,7 @@ static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client,
 | ||||
|      TRACE("Setting up TLS"); | ||||
|      ioc = client->ioc; | ||||
|      if (length) { | ||||
| -        if (nbd_negotiate_drop_sync(ioc, length) < 0) {
 | ||||
| +        if (nbd_drop(ioc, length) < 0) {
 | ||||
|              return NULL; | ||||
|          } | ||||
|          nbd_negotiate_send_rep_err(ioc, NBD_REP_ERR_INVALID, NBD_OPT_STARTTLS, | ||||
| @@ -436,7 +373,7 @@ static int nbd_negotiate_options(NBDClient *client)
 | ||||
|          ...           Rest of request | ||||
|      */ | ||||
|   | ||||
| -    if (nbd_negotiate_read(client->ioc, &flags, sizeof(flags)) < 0) {
 | ||||
| +    if (read_sync(client->ioc, &flags, sizeof(flags)) < 0) {
 | ||||
|          LOG("read failed"); | ||||
|          return -EIO; | ||||
|      } | ||||
| @@ -462,7 +399,7 @@ static int nbd_negotiate_options(NBDClient *client)
 | ||||
|          uint32_t clientflags, length; | ||||
|          uint64_t magic; | ||||
|   | ||||
| -        if (nbd_negotiate_read(client->ioc, &magic, sizeof(magic)) < 0) {
 | ||||
| +        if (read_sync(client->ioc, &magic, sizeof(magic)) < 0) {
 | ||||
|              LOG("read failed"); | ||||
|              return -EINVAL; | ||||
|          } | ||||
| @@ -472,15 +409,14 @@ static int nbd_negotiate_options(NBDClient *client)
 | ||||
|              return -EINVAL; | ||||
|          } | ||||
|   | ||||
| -        if (nbd_negotiate_read(client->ioc, &clientflags,
 | ||||
| -                               sizeof(clientflags)) < 0)
 | ||||
| +        if (read_sync(client->ioc, &clientflags, sizeof(clientflags)) < 0)
 | ||||
|          { | ||||
|              LOG("read failed"); | ||||
|              return -EINVAL; | ||||
|          } | ||||
|          clientflags = be32_to_cpu(clientflags); | ||||
|   | ||||
| -        if (nbd_negotiate_read(client->ioc, &length, sizeof(length)) < 0) {
 | ||||
| +        if (read_sync(client->ioc, &length, sizeof(length)) < 0) {
 | ||||
|              LOG("read failed"); | ||||
|              return -EINVAL; | ||||
|          } | ||||
| @@ -510,7 +446,7 @@ static int nbd_negotiate_options(NBDClient *client)
 | ||||
|                  return -EINVAL; | ||||
|   | ||||
|              default: | ||||
| -                if (nbd_negotiate_drop_sync(client->ioc, length) < 0) {
 | ||||
| +                if (nbd_drop(client->ioc, length) < 0) {
 | ||||
|                      return -EIO; | ||||
|                  } | ||||
|                  ret = nbd_negotiate_send_rep_err(client->ioc, | ||||
| @@ -548,7 +484,7 @@ static int nbd_negotiate_options(NBDClient *client)
 | ||||
|                  return nbd_negotiate_handle_export_name(client, length); | ||||
|   | ||||
|              case NBD_OPT_STARTTLS: | ||||
| -                if (nbd_negotiate_drop_sync(client->ioc, length) < 0) {
 | ||||
| +                if (nbd_drop(client->ioc, length) < 0) {
 | ||||
|                      return -EIO; | ||||
|                  } | ||||
|                  if (client->tlscreds) { | ||||
| @@ -567,7 +503,7 @@ static int nbd_negotiate_options(NBDClient *client)
 | ||||
|                  } | ||||
|                  break; | ||||
|              default: | ||||
| -                if (nbd_negotiate_drop_sync(client->ioc, length) < 0) {
 | ||||
| +                if (nbd_drop(client->ioc, length) < 0) {
 | ||||
|                      return -EIO; | ||||
|                  } | ||||
|                  ret = nbd_negotiate_send_rep_err(client->ioc, | ||||
| @@ -656,12 +592,12 @@ static coroutine_fn int nbd_negotiate(NBDClientNewData *data)
 | ||||
|              TRACE("TLS cannot be enabled with oldstyle protocol"); | ||||
|              goto fail; | ||||
|          } | ||||
| -        if (nbd_negotiate_write(client->ioc, buf, sizeof(buf)) < 0) {
 | ||||
| +        if (write_sync(client->ioc, buf, sizeof(buf)) < 0) {
 | ||||
|              LOG("write failed"); | ||||
|              goto fail; | ||||
|          } | ||||
|      } else { | ||||
| -        if (nbd_negotiate_write(client->ioc, buf, 18) < 0) {
 | ||||
| +        if (write_sync(client->ioc, buf, 18) < 0) {
 | ||||
|              LOG("write failed"); | ||||
|              goto fail; | ||||
|          } | ||||
| @@ -676,7 +612,7 @@ static coroutine_fn int nbd_negotiate(NBDClientNewData *data)
 | ||||
|          stq_be_p(buf + 18, client->exp->size); | ||||
|          stw_be_p(buf + 26, client->exp->nbdflags | myflags); | ||||
|          len = client->no_zeroes ? 10 : sizeof(buf) - 18; | ||||
| -        if (nbd_negotiate_write(client->ioc, buf + 18, len) < 0) {
 | ||||
| +        if (write_sync(client->ioc, buf + 18, len) < 0) {
 | ||||
|              LOG("write failed"); | ||||
|              goto fail; | ||||
|          } | ||||
| -- 
 | ||||
| 1.8.3.1 | ||||
| 
 | ||||
							
								
								
									
										153
									
								
								debian/patches/extra/0041-kvm-nbd-client-Fix-regression-when-server-sends-garbage.patch
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								debian/patches/extra/0041-kvm-nbd-client-Fix-regression-when-server-sends-garbage.patch
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,153 @@ | ||||
| From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 | ||||
| From: Eric Blake <eblake@redhat.com> | ||||
| Date: Wed, 27 Sep 2017 17:57:19 +0200 | ||||
| Subject: [PATCH] nbd-client: Fix regression when server sends garbage | ||||
| 
 | ||||
| RH-Author: Eric Blake <eblake@redhat.com> | ||||
| Message-id: <20170927175725.20023-2-eblake@redhat.com> | ||||
| Patchwork-id: 76672 | ||||
| O-Subject: [RHEV-7.4.z qemu-kvm-rhev PATCH 1/7] nbd-client: Fix regression when server sends garbage | ||||
| Bugzilla: 1495474 | ||||
| RH-Acked-by: Max Reitz <mreitz@redhat.com> | ||||
| RH-Acked-by: Jeffrey Cody <jcody@redhat.com> | ||||
| RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com> | ||||
| 
 | ||||
| When we switched NBD to use coroutines for qemu 2.9 (in particular, | ||||
| commit a12a712a), we introduced a regression: if a server sends us | ||||
| garbage (such as a corrupted magic number), we quit the read loop | ||||
| but do not stop sending further queued commands, resulting in the | ||||
| client hanging when it never reads the response to those additional | ||||
| commands.  In qemu 2.8, we properly detected that the server is no | ||||
| longer reliable, and cancelled all existing pending commands with | ||||
| EIO, then tore down the socket so that all further command attempts | ||||
| get EPIPE. | ||||
| 
 | ||||
| Restore the proper behavior of quitting (almost) all communication | ||||
| with a broken server: Once we know we are out of sync or otherwise | ||||
| can't trust the server, we must assume that any further incoming | ||||
| data is unreliable and therefore end all pending commands with EIO, | ||||
| and quit trying to send any further commands.  As an exception, we | ||||
| still (try to) send NBD_CMD_DISC to let the server know we are going | ||||
| away (in part, because it is easier to do that than to further | ||||
| refactor nbd_teardown_connection, and in part because it is the | ||||
| only command where we do not have to wait for a reply). | ||||
| 
 | ||||
| Based on a patch by Vladimir Sementsov-Ogievskiy. | ||||
| 
 | ||||
| A malicious server can be created with the following hack, | ||||
| followed by setting NBD_SERVER_DEBUG to a non-zero value in the | ||||
| environment when running qemu-nbd: | ||||
| 
 | ||||
| | --- a/nbd/server.c | ||||
| | +++ b/nbd/server.c | ||||
| | @@ -919,6 +919,17 @@ static int nbd_send_reply(QIOChannel *ioc, NBDReply *reply, Error **errp) | ||||
| |      stl_be_p(buf + 4, reply->error); | ||||
| |      stq_be_p(buf + 8, reply->handle); | ||||
| | | ||||
| | +    static int debug; | ||||
| | +    static int count; | ||||
| | +    if (!count++) { | ||||
| | +        const char *str = getenv("NBD_SERVER_DEBUG"); | ||||
| | +        if (str) { | ||||
| | +            debug = atoi(str); | ||||
| | +        } | ||||
| | +    } | ||||
| | +    if (debug && !(count % debug)) { | ||||
| | +        buf[0] = 0; | ||||
| | +    } | ||||
| |      return nbd_write(ioc, buf, sizeof(buf), errp); | ||||
| |  } | ||||
| 
 | ||||
| Reported-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||||
| Signed-off-by: Eric Blake <eblake@redhat.com> | ||||
| Message-Id: <20170814213426.24681-1-eblake@redhat.com> | ||||
| Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | ||||
| (cherry picked from commit 72b6ffc76653214b69a94a7b1643ff80df134486) | ||||
| Signed-off-by: Eric Blake <eblake@redhat.com> | ||||
| Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com> | ||||
| 
 | ||||
| Conflicts: | ||||
| 	block/nbd-client.c | ||||
| ---
 | ||||
|  block/nbd-client.c | 17 +++++++++++++---- | ||||
|  block/nbd-client.h |  1 + | ||||
|  2 files changed, 14 insertions(+), 4 deletions(-) | ||||
| 
 | ||||
| diff --git a/block/nbd-client.c b/block/nbd-client.c
 | ||||
| index 282679b..701b4ce 100644
 | ||||
| --- a/block/nbd-client.c
 | ||||
| +++ b/block/nbd-client.c
 | ||||
| @@ -71,7 +71,7 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque)
 | ||||
|      uint64_t i; | ||||
|      int ret; | ||||
|   | ||||
| -    for (;;) {
 | ||||
| +    while (!s->quit) {
 | ||||
|          assert(s->reply.handle == 0); | ||||
|          ret = nbd_receive_reply(s->ioc, &s->reply); | ||||
|          if (ret <= 0) { | ||||
| @@ -102,6 +102,9 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque)
 | ||||
|          qemu_coroutine_yield(); | ||||
|      } | ||||
|   | ||||
| +    if (ret < 0) {
 | ||||
| +        s->quit = true;
 | ||||
| +    }
 | ||||
|      nbd_recv_coroutines_enter_all(s); | ||||
|      s->read_reply_co = NULL; | ||||
|  } | ||||
| @@ -130,6 +133,10 @@ static int nbd_co_send_request(BlockDriverState *bs,
 | ||||
|      assert(i < MAX_NBD_REQUESTS); | ||||
|      request->handle = INDEX_TO_HANDLE(s, i); | ||||
|   | ||||
| +    if (s->quit) {
 | ||||
| +        qemu_co_mutex_unlock(&s->send_mutex);
 | ||||
| +        return -EIO;
 | ||||
| +    }
 | ||||
|      if (!s->ioc) { | ||||
|          qemu_co_mutex_unlock(&s->send_mutex); | ||||
|          return -EPIPE; | ||||
| @@ -138,7 +145,7 @@ static int nbd_co_send_request(BlockDriverState *bs,
 | ||||
|      if (qiov) { | ||||
|          qio_channel_set_cork(s->ioc, true); | ||||
|          rc = nbd_send_request(s->ioc, request); | ||||
| -        if (rc >= 0) {
 | ||||
| +        if (rc >= 0 && !s->quit) {
 | ||||
|              ret = nbd_wr_syncv(s->ioc, qiov->iov, qiov->niov, request->len, | ||||
|                                 false); | ||||
|              if (ret != request->len) { | ||||
| @@ -149,6 +156,9 @@ static int nbd_co_send_request(BlockDriverState *bs,
 | ||||
|      } else { | ||||
|          rc = nbd_send_request(s->ioc, request); | ||||
|      } | ||||
| +    if (rc < 0) {
 | ||||
| +        s->quit = true;
 | ||||
| +    }
 | ||||
|      qemu_co_mutex_unlock(&s->send_mutex); | ||||
|      return rc; | ||||
|  } | ||||
| @@ -163,8 +173,7 @@ static void nbd_co_receive_reply(NBDClientSession *s,
 | ||||
|      /* Wait until we're woken up by nbd_read_reply_entry.  */ | ||||
|      qemu_coroutine_yield(); | ||||
|      *reply = s->reply; | ||||
| -    if (reply->handle != request->handle ||
 | ||||
| -        !s->ioc) {
 | ||||
| +    if (reply->handle != request->handle || !s->ioc || s->quit) {
 | ||||
|          reply->error = EIO; | ||||
|      } else { | ||||
|          if (qiov && reply->error == 0) { | ||||
| diff --git a/block/nbd-client.h b/block/nbd-client.h
 | ||||
| index 891ba44..9774a8e 100644
 | ||||
| --- a/block/nbd-client.h
 | ||||
| +++ b/block/nbd-client.h
 | ||||
| @@ -30,6 +30,7 @@ typedef struct NBDClientSession {
 | ||||
|   | ||||
|      Coroutine *recv_coroutine[MAX_NBD_REQUESTS]; | ||||
|      NBDReply reply; | ||||
| +    bool quit;
 | ||||
|  } NBDClientSession; | ||||
|   | ||||
|  NBDClientSession *nbd_get_client_session(BlockDriverState *bs); | ||||
| -- 
 | ||||
| 1.8.3.1 | ||||
| 
 | ||||
							
								
								
									
										55
									
								
								debian/patches/extra/0042-kvm-fix-build-failure-in-nbd_read_reply_entry.patch
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								debian/patches/extra/0042-kvm-fix-build-failure-in-nbd_read_reply_entry.patch
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,55 @@ | ||||
| From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 | ||||
| From: Eric Blake <eblake@redhat.com> | ||||
| Date: Wed, 27 Sep 2017 17:57:20 +0200 | ||||
| Subject: [PATCH] fix build failure in nbd_read_reply_entry() | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
| 
 | ||||
| RH-Author: Eric Blake <eblake@redhat.com> | ||||
| Message-id: <20170927175725.20023-3-eblake@redhat.com> | ||||
| Patchwork-id: 76668 | ||||
| O-Subject: [RHEV-7.4.z qemu-kvm-rhev PATCH 2/7] fix build failure in nbd_read_reply_entry() | ||||
| Bugzilla: 1495474 | ||||
| RH-Acked-by: Max Reitz <mreitz@redhat.com> | ||||
| RH-Acked-by: Jeffrey Cody <jcody@redhat.com> | ||||
| RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com> | ||||
| 
 | ||||
| From: Igor Mammedov <imammedo@redhat.com> | ||||
| 
 | ||||
| travis builds fail at HEAD at rc3 master with | ||||
| 
 | ||||
|   block/nbd-client.c: In function ‘nbd_read_reply_entry’: | ||||
|   block/nbd-client.c:110:8: error: ‘ret’ may be used uninitialized in this function [-Werror=uninitialized] | ||||
| 
 | ||||
| fix it by initializing 'ret' to 0 | ||||
| 
 | ||||
| Signed-off-by: Igor Mammedov <imammedo@redhat.com> | ||||
| Reviewed-by: Eric Blake <eblake@redhat.com> | ||||
| Signed-off-by: Peter Maydell <peter.maydell@linaro.org> | ||||
| (cherry picked from commit d0a180131c6655487b47ea72e7da0a909a479a3c) | ||||
| Signed-off-by: Eric Blake <eblake@redhat.com> | ||||
| Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com> | ||||
| 
 | ||||
| Conflicts: | ||||
| 	block/nbd-client.c - context | ||||
| ---
 | ||||
|  block/nbd-client.c | 2 +- | ||||
|  1 file changed, 1 insertion(+), 1 deletion(-) | ||||
| 
 | ||||
| diff --git a/block/nbd-client.c b/block/nbd-client.c
 | ||||
| index 701b4ce..256dabe 100644
 | ||||
| --- a/block/nbd-client.c
 | ||||
| +++ b/block/nbd-client.c
 | ||||
| @@ -69,7 +69,7 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque)
 | ||||
|  { | ||||
|      NBDClientSession *s = opaque; | ||||
|      uint64_t i; | ||||
| -    int ret;
 | ||||
| +    int ret = 0;
 | ||||
|   | ||||
|      while (!s->quit) { | ||||
|          assert(s->reply.handle == 0); | ||||
| -- 
 | ||||
| 1.8.3.1 | ||||
| 
 | ||||
							
								
								
									
										184
									
								
								debian/patches/extra/0043-kvm-nbd-client-avoid-spurious-qio_channel_yield-re-entry.patch
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										184
									
								
								debian/patches/extra/0043-kvm-nbd-client-avoid-spurious-qio_channel_yield-re-entry.patch
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,184 @@ | ||||
| From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 | ||||
| From: Eric Blake <eblake@redhat.com> | ||||
| Date: Wed, 27 Sep 2017 17:57:21 +0200 | ||||
| Subject: [PATCH] nbd-client: avoid spurious qio_channel_yield() re-entry | ||||
| 
 | ||||
| RH-Author: Eric Blake <eblake@redhat.com> | ||||
| Message-id: <20170927175725.20023-4-eblake@redhat.com> | ||||
| Patchwork-id: 76671 | ||||
| O-Subject: [RHEV-7.4.z qemu-kvm-rhev PATCH 3/7] nbd-client: avoid spurious qio_channel_yield() re-entry | ||||
| Bugzilla: 1495474 | ||||
| RH-Acked-by: Max Reitz <mreitz@redhat.com> | ||||
| RH-Acked-by: Jeffrey Cody <jcody@redhat.com> | ||||
| RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com> | ||||
| 
 | ||||
| From: Stefan Hajnoczi <stefanha@redhat.com> | ||||
| 
 | ||||
| The following scenario leads to an assertion failure in | ||||
| qio_channel_yield(): | ||||
| 
 | ||||
| 1. Request coroutine calls qio_channel_yield() successfully when sending | ||||
|    would block on the socket.  It is now yielded. | ||||
| 2. nbd_read_reply_entry() calls nbd_recv_coroutines_enter_all() because | ||||
|    nbd_receive_reply() failed. | ||||
| 3. Request coroutine is entered and returns from qio_channel_yield(). | ||||
|    Note that the socket fd handler has not fired yet so | ||||
|    ioc->write_coroutine is still set. | ||||
| 4. Request coroutine attempts to send the request body with nbd_rwv() | ||||
|    but the socket would still block.  qio_channel_yield() is called | ||||
|    again and assert(!ioc->write_coroutine) is hit. | ||||
| 
 | ||||
| The problem is that nbd_read_reply_entry() does not distinguish between | ||||
| request coroutines that are waiting to receive a reply and those that | ||||
| are not. | ||||
| 
 | ||||
| This patch adds a per-request bool receiving flag so | ||||
| nbd_read_reply_entry() can avoid spurious aio_wake() calls. | ||||
| 
 | ||||
| Reported-by: Dr. David Alan Gilbert <dgilbert@redhat.com> | ||||
| Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||||
| Message-Id: <20170822125113.5025-1-stefanha@redhat.com> | ||||
| Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||||
| Tested-by: Eric Blake <eblake@redhat.com> | ||||
| Reviewed-by: Paolo Bonzini <pbonzini@redhat.com> | ||||
| Signed-off-by: Eric Blake <eblake@redhat.com> | ||||
| (cherry picked from commit 40f4a21895b5a7eae4011593837069f63460d983) | ||||
| Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com> | ||||
| ---
 | ||||
|  block/nbd-client.c | 35 ++++++++++++++++++++++------------- | ||||
|  block/nbd-client.h |  7 ++++++- | ||||
|  2 files changed, 28 insertions(+), 14 deletions(-) | ||||
| 
 | ||||
| diff --git a/block/nbd-client.c b/block/nbd-client.c
 | ||||
| index 256dabe..f7bca3f 100644
 | ||||
| --- a/block/nbd-client.c
 | ||||
| +++ b/block/nbd-client.c
 | ||||
| @@ -38,8 +38,10 @@ static void nbd_recv_coroutines_enter_all(NBDClientSession *s)
 | ||||
|      int i; | ||||
|   | ||||
|      for (i = 0; i < MAX_NBD_REQUESTS; i++) { | ||||
| -        if (s->recv_coroutine[i]) {
 | ||||
| -            aio_co_wake(s->recv_coroutine[i]);
 | ||||
| +        NBDClientRequest *req = &s->requests[i];
 | ||||
| +
 | ||||
| +        if (req->coroutine && req->receiving) {
 | ||||
| +            aio_co_wake(req->coroutine);
 | ||||
|          } | ||||
|      } | ||||
|  } | ||||
| @@ -83,28 +85,28 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque)
 | ||||
|           * one coroutine is called until the reply finishes. | ||||
|           */ | ||||
|          i = HANDLE_TO_INDEX(s, s->reply.handle); | ||||
| -        if (i >= MAX_NBD_REQUESTS || !s->recv_coroutine[i]) {
 | ||||
| +        if (i >= MAX_NBD_REQUESTS ||
 | ||||
| +            !s->requests[i].coroutine ||
 | ||||
| +            !s->requests[i].receiving) {
 | ||||
|              break; | ||||
|          } | ||||
|   | ||||
| -        /* We're woken up by the recv_coroutine itself.  Note that there
 | ||||
| +        /* We're woken up again by the request itself.  Note that there
 | ||||
|           * is no race between yielding and reentering read_reply_co.  This | ||||
|           * is because: | ||||
|           * | ||||
| -         * - if recv_coroutine[i] runs on the same AioContext, it is only
 | ||||
| +         * - if the request runs on the same AioContext, it is only
 | ||||
|           *   entered after we yield | ||||
|           * | ||||
| -         * - if recv_coroutine[i] runs on a different AioContext, reentering
 | ||||
| +         * - if the request runs on a different AioContext, reentering
 | ||||
|           *   read_reply_co happens through a bottom half, which can only | ||||
|           *   run after we yield. | ||||
|           */ | ||||
| -        aio_co_wake(s->recv_coroutine[i]);
 | ||||
| +        aio_co_wake(s->requests[i].coroutine);
 | ||||
|          qemu_coroutine_yield(); | ||||
|      } | ||||
|   | ||||
| -    if (ret < 0) {
 | ||||
| -        s->quit = true;
 | ||||
| -    }
 | ||||
| +    s->quit = true;
 | ||||
|      nbd_recv_coroutines_enter_all(s); | ||||
|      s->read_reply_co = NULL; | ||||
|  } | ||||
| @@ -123,14 +125,17 @@ static int nbd_co_send_request(BlockDriverState *bs,
 | ||||
|      s->in_flight++; | ||||
|   | ||||
|      for (i = 0; i < MAX_NBD_REQUESTS; i++) { | ||||
| -        if (s->recv_coroutine[i] == NULL) {
 | ||||
| -            s->recv_coroutine[i] = qemu_coroutine_self();
 | ||||
| +        if (s->requests[i].coroutine == NULL) {
 | ||||
|              break; | ||||
|          } | ||||
|      } | ||||
|   | ||||
|      g_assert(qemu_in_coroutine()); | ||||
|      assert(i < MAX_NBD_REQUESTS); | ||||
| +
 | ||||
| +    s->requests[i].coroutine = qemu_coroutine_self();
 | ||||
| +    s->requests[i].receiving = false;
 | ||||
| +
 | ||||
|      request->handle = INDEX_TO_HANDLE(s, i); | ||||
|   | ||||
|      if (s->quit) { | ||||
| @@ -168,10 +173,13 @@ static void nbd_co_receive_reply(NBDClientSession *s,
 | ||||
|                                   NBDReply *reply, | ||||
|                                   QEMUIOVector *qiov) | ||||
|  { | ||||
| +    int i = HANDLE_TO_INDEX(s, request->handle);
 | ||||
|      int ret; | ||||
|   | ||||
|      /* Wait until we're woken up by nbd_read_reply_entry.  */ | ||||
| +    s->requests[i].receiving = true;
 | ||||
|      qemu_coroutine_yield(); | ||||
| +    s->requests[i].receiving = false;
 | ||||
|      *reply = s->reply; | ||||
|      if (reply->handle != request->handle || !s->ioc || s->quit) { | ||||
|          reply->error = EIO; | ||||
| @@ -181,6 +189,7 @@ static void nbd_co_receive_reply(NBDClientSession *s,
 | ||||
|                                 true); | ||||
|              if (ret != request->len) { | ||||
|                  reply->error = EIO; | ||||
| +                s->quit = true;
 | ||||
|              } | ||||
|          } | ||||
|   | ||||
| @@ -195,7 +204,7 @@ static void nbd_coroutine_end(BlockDriverState *bs,
 | ||||
|      NBDClientSession *s = nbd_get_client_session(bs); | ||||
|      int i = HANDLE_TO_INDEX(s, request->handle); | ||||
|   | ||||
| -    s->recv_coroutine[i] = NULL;
 | ||||
| +    s->requests[i].coroutine = NULL;
 | ||||
|   | ||||
|      /* Kick the read_reply_co to get the next reply.  */ | ||||
|      if (s->read_reply_co) { | ||||
| diff --git a/block/nbd-client.h b/block/nbd-client.h
 | ||||
| index 9774a8e..f97792f 100644
 | ||||
| --- a/block/nbd-client.h
 | ||||
| +++ b/block/nbd-client.h
 | ||||
| @@ -17,6 +17,11 @@
 | ||||
|   | ||||
|  #define MAX_NBD_REQUESTS    16 | ||||
|   | ||||
| +typedef struct {
 | ||||
| +    Coroutine *coroutine;
 | ||||
| +    bool receiving;         /* waiting for read_reply_co? */
 | ||||
| +} NBDClientRequest;
 | ||||
| +
 | ||||
|  typedef struct NBDClientSession { | ||||
|      QIOChannelSocket *sioc; /* The master data channel */ | ||||
|      QIOChannel *ioc; /* The current I/O channel which may differ (eg TLS) */ | ||||
| @@ -28,7 +33,7 @@ typedef struct NBDClientSession {
 | ||||
|      Coroutine *read_reply_co; | ||||
|      int in_flight; | ||||
|   | ||||
| -    Coroutine *recv_coroutine[MAX_NBD_REQUESTS];
 | ||||
| +    NBDClientRequest requests[MAX_NBD_REQUESTS];
 | ||||
|      NBDReply reply; | ||||
|      bool quit; | ||||
|  } NBDClientSession; | ||||
| -- 
 | ||||
| 1.8.3.1 | ||||
| 
 | ||||
							
								
								
									
										160
									
								
								debian/patches/extra/0044-kvm-nbd-client-avoid-read_reply_co-entry-if-send-failed.patch
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								debian/patches/extra/0044-kvm-nbd-client-avoid-read_reply_co-entry-if-send-failed.patch
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,160 @@ | ||||
| From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 | ||||
| From: Eric Blake <eblake@redhat.com> | ||||
| Date: Wed, 27 Sep 2017 17:57:22 +0200 | ||||
| Subject: [PATCH] nbd-client: avoid read_reply_co entry if send failed | ||||
| 
 | ||||
| RH-Author: Eric Blake <eblake@redhat.com> | ||||
| Message-id: <20170927175725.20023-5-eblake@redhat.com> | ||||
| Patchwork-id: 76674 | ||||
| O-Subject: [RHEV-7.4.z qemu-kvm-rhev PATCH 4/7] nbd-client: avoid read_reply_co entry if send failed | ||||
| Bugzilla: 1495474 | ||||
| RH-Acked-by: Max Reitz <mreitz@redhat.com> | ||||
| RH-Acked-by: Jeffrey Cody <jcody@redhat.com> | ||||
| RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com> | ||||
| 
 | ||||
| From: Stefan Hajnoczi <stefanha@redhat.com> | ||||
| 
 | ||||
| The following segfault is encountered if the NBD server closes the UNIX | ||||
| domain socket immediately after negotiation: | ||||
| 
 | ||||
|   Program terminated with signal SIGSEGV, Segmentation fault. | ||||
|   #0  aio_co_schedule (ctx=0x0, co=0xd3c0ff2ef0) at util/async.c:441 | ||||
|   441       QSLIST_INSERT_HEAD_ATOMIC(&ctx->scheduled_coroutines, | ||||
|   (gdb) bt | ||||
|   #0  0x000000d3c01a50f8 in aio_co_schedule (ctx=0x0, co=0xd3c0ff2ef0) at util/async.c:441 | ||||
|   #1  0x000000d3c012fa90 in nbd_coroutine_end (bs=bs@entry=0xd3c0fec650, request=<optimized out>) at block/nbd-client.c:207 | ||||
|   #2  0x000000d3c012fb58 in nbd_client_co_preadv (bs=0xd3c0fec650, offset=0, bytes=<optimized out>, qiov=0x7ffc10a91b20, flags=0) at block/nbd-client.c:237 | ||||
|   #3  0x000000d3c0128e63 in bdrv_driver_preadv (bs=bs@entry=0xd3c0fec650, offset=offset@entry=0, bytes=bytes@entry=512, qiov=qiov@entry=0x7ffc10a91b20, flags=0) at block/io.c:836 | ||||
|   #4  0x000000d3c012c3e0 in bdrv_aligned_preadv (child=child@entry=0xd3c0ff51d0, req=req@entry=0x7f31885d6e90, offset=offset@entry=0, bytes=bytes@entry=512, align=align@entry=1, qiov=qiov@entry=0x7ffc10a91b20, f | ||||
| +lags=0) at block/io.c:1086
 | ||||
|   #5  0x000000d3c012c6b8 in bdrv_co_preadv (child=0xd3c0ff51d0, offset=offset@entry=0, bytes=bytes@entry=512, qiov=qiov@entry=0x7ffc10a91b20, flags=flags@entry=0) at block/io.c:1182 | ||||
|   #6  0x000000d3c011cc17 in blk_co_preadv (blk=0xd3c0ff4f80, offset=0, bytes=512, qiov=0x7ffc10a91b20, flags=0) at block/block-backend.c:1032 | ||||
|   #7  0x000000d3c011ccec in blk_read_entry (opaque=0x7ffc10a91b40) at block/block-backend.c:1079 | ||||
|   #8  0x000000d3c01bbb96 in coroutine_trampoline (i0=<optimized out>, i1=<optimized out>) at util/coroutine-ucontext.c:79 | ||||
|   #9  0x00007f3196cb8600 in __start_context () at /lib64/libc.so.6 | ||||
| 
 | ||||
| The problem is that nbd_client_init() uses | ||||
| nbd_client_attach_aio_context() -> aio_co_schedule(new_context, | ||||
| client->read_reply_co).  Execution of read_reply_co is deferred to a BH | ||||
| which doesn't run until later. | ||||
| 
 | ||||
| In the mean time blk_co_preadv() can be called and nbd_coroutine_end() | ||||
| calls aio_wake() on read_reply_co.  At this point in time | ||||
| read_reply_co's ctx isn't set because it has never been entered yet. | ||||
| 
 | ||||
| This patch simplifies the nbd_co_send_request() -> | ||||
| nbd_co_receive_reply() -> nbd_coroutine_end() lifecycle to just | ||||
| nbd_co_send_request() -> nbd_co_receive_reply().  The request is "ended" | ||||
| if an error occurs at any point.  Callers no longer have to invoke | ||||
| nbd_coroutine_end(). | ||||
| 
 | ||||
| This cleanup also eliminates the segfault because we don't call | ||||
| aio_co_schedule() to wake up s->read_reply_co if sending the request | ||||
| failed.  It is only necessary to wake up s->read_reply_co if a reply was | ||||
| received. | ||||
| 
 | ||||
| Note this only happens with UNIX domain sockets on Linux.  It doesn't | ||||
| seem possible to reproduce this with TCP sockets. | ||||
| 
 | ||||
| Suggested-by: Paolo Bonzini <pbonzini@redhat.com> | ||||
| Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||||
| Message-Id: <20170829122745.14309-2-stefanha@redhat.com> | ||||
| Signed-off-by: Eric Blake <eblake@redhat.com> | ||||
| (cherry picked from commit 3c2d5183f9fa4eac3d17d841e26da65a0181ae7b) | ||||
| Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com> | ||||
| ---
 | ||||
|  block/nbd-client.c | 25 +++++++++---------------- | ||||
|  1 file changed, 9 insertions(+), 16 deletions(-) | ||||
| 
 | ||||
| diff --git a/block/nbd-client.c b/block/nbd-client.c
 | ||||
| index f7bca3f..434acf6 100644
 | ||||
| --- a/block/nbd-client.c
 | ||||
| +++ b/block/nbd-client.c
 | ||||
| @@ -139,12 +139,12 @@ static int nbd_co_send_request(BlockDriverState *bs,
 | ||||
|      request->handle = INDEX_TO_HANDLE(s, i); | ||||
|   | ||||
|      if (s->quit) { | ||||
| -        qemu_co_mutex_unlock(&s->send_mutex);
 | ||||
| -        return -EIO;
 | ||||
| +        rc = -EIO;
 | ||||
| +        goto err;
 | ||||
|      } | ||||
|      if (!s->ioc) { | ||||
| -        qemu_co_mutex_unlock(&s->send_mutex);
 | ||||
| -        return -EPIPE;
 | ||||
| +        rc = -EPIPE;
 | ||||
| +        goto err;
 | ||||
|      } | ||||
|   | ||||
|      if (qiov) { | ||||
| @@ -161,8 +161,13 @@ static int nbd_co_send_request(BlockDriverState *bs,
 | ||||
|      } else { | ||||
|          rc = nbd_send_request(s->ioc, request); | ||||
|      } | ||||
| +
 | ||||
| +err:
 | ||||
|      if (rc < 0) { | ||||
|          s->quit = true; | ||||
| +        s->requests[i].coroutine = NULL;
 | ||||
| +        s->in_flight--;
 | ||||
| +        qemu_co_queue_next(&s->free_sema);
 | ||||
|      } | ||||
|      qemu_co_mutex_unlock(&s->send_mutex); | ||||
|      return rc; | ||||
| @@ -196,13 +201,6 @@ static void nbd_co_receive_reply(NBDClientSession *s,
 | ||||
|          /* Tell the read handler to read another header.  */ | ||||
|          s->reply.handle = 0; | ||||
|      } | ||||
| -}
 | ||||
| -
 | ||||
| -static void nbd_coroutine_end(BlockDriverState *bs,
 | ||||
| -                              NBDRequest *request)
 | ||||
| -{
 | ||||
| -    NBDClientSession *s = nbd_get_client_session(bs);
 | ||||
| -    int i = HANDLE_TO_INDEX(s, request->handle);
 | ||||
|   | ||||
|      s->requests[i].coroutine = NULL; | ||||
|   | ||||
| @@ -238,7 +236,6 @@ int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset,
 | ||||
|      } else { | ||||
|          nbd_co_receive_reply(client, &request, &reply, qiov); | ||||
|      } | ||||
| -    nbd_coroutine_end(bs, &request);
 | ||||
|      return -reply.error; | ||||
|  } | ||||
|   | ||||
| @@ -267,7 +264,6 @@ int nbd_client_co_pwritev(BlockDriverState *bs, uint64_t offset,
 | ||||
|      } else { | ||||
|          nbd_co_receive_reply(client, &request, &reply, NULL); | ||||
|      } | ||||
| -    nbd_coroutine_end(bs, &request);
 | ||||
|      return -reply.error; | ||||
|  } | ||||
|   | ||||
| @@ -301,7 +297,6 @@ int nbd_client_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset,
 | ||||
|      } else { | ||||
|          nbd_co_receive_reply(client, &request, &reply, NULL); | ||||
|      } | ||||
| -    nbd_coroutine_end(bs, &request);
 | ||||
|      return -reply.error; | ||||
|  } | ||||
|   | ||||
| @@ -325,7 +320,6 @@ int nbd_client_co_flush(BlockDriverState *bs)
 | ||||
|      } else { | ||||
|          nbd_co_receive_reply(client, &request, &reply, NULL); | ||||
|      } | ||||
| -    nbd_coroutine_end(bs, &request);
 | ||||
|      return -reply.error; | ||||
|  } | ||||
|   | ||||
| @@ -350,7 +344,6 @@ int nbd_client_co_pdiscard(BlockDriverState *bs, int64_t offset, int count)
 | ||||
|      } else { | ||||
|          nbd_co_receive_reply(client, &request, &reply, NULL); | ||||
|      } | ||||
| -    nbd_coroutine_end(bs, &request);
 | ||||
|      return -reply.error; | ||||
|   | ||||
|  } | ||||
| -- 
 | ||||
| 1.8.3.1 | ||||
| 
 | ||||
							
								
								
									
										61
									
								
								debian/patches/extra/0045-kvm-qemu-iotests-improve-nbd-fault-injector.py-startup-p.patch
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								debian/patches/extra/0045-kvm-qemu-iotests-improve-nbd-fault-injector.py-startup-p.patch
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,61 @@ | ||||
| From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 | ||||
| From: Eric Blake <eblake@redhat.com> | ||||
| Date: Wed, 27 Sep 2017 17:57:23 +0200 | ||||
| Subject: [PATCH] qemu-iotests: improve nbd-fault-injector.py startup | ||||
|  protocol | ||||
| 
 | ||||
| RH-Author: Eric Blake <eblake@redhat.com> | ||||
| Message-id: <20170927175725.20023-6-eblake@redhat.com> | ||||
| Patchwork-id: 76675 | ||||
| O-Subject: [RHEV-7.4.z qemu-kvm-rhev PATCH 5/7] qemu-iotests: improve nbd-fault-injector.py startup protocol | ||||
| Bugzilla: 1495474 | ||||
| RH-Acked-by: Max Reitz <mreitz@redhat.com> | ||||
| RH-Acked-by: Jeffrey Cody <jcody@redhat.com> | ||||
| RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com> | ||||
| 
 | ||||
| From: Stefan Hajnoczi <stefanha@redhat.com> | ||||
| 
 | ||||
| Currently 083 waits for the nbd-fault-injector.py server to start up by | ||||
| looping until netstat shows the TCP listen socket. | ||||
| 
 | ||||
| The startup protocol can be simplified by passing a 0 port number to | ||||
| nbd-fault-injector.py.  The kernel will allocate a port in bind(2) and | ||||
| the final port number can be printed by nbd-fault-injector.py. | ||||
| 
 | ||||
| This should make it slightly nicer and less TCP-specific to wait for | ||||
| server startup.  This patch changes nbd-fault-injector.py, the next one | ||||
| will rewrite server startup in 083. | ||||
| 
 | ||||
| Reviewed-by: Eric Blake <eblake@redhat.com> | ||||
| Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||||
| Message-Id: <20170829122745.14309-3-stefanha@redhat.com> | ||||
| Signed-off-by: Eric Blake <eblake@redhat.com> | ||||
| (cherry picked from commit 6e592fc92234a58c7156c385840633c17dedd24f) | ||||
| Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com> | ||||
| ---
 | ||||
|  tests/qemu-iotests/nbd-fault-injector.py | 4 ++++ | ||||
|  1 file changed, 4 insertions(+) | ||||
| 
 | ||||
| diff --git a/tests/qemu-iotests/nbd-fault-injector.py b/tests/qemu-iotests/nbd-fault-injector.py
 | ||||
| index 6c07191..1c10dcb 100755
 | ||||
| --- a/tests/qemu-iotests/nbd-fault-injector.py
 | ||||
| +++ b/tests/qemu-iotests/nbd-fault-injector.py
 | ||||
| @@ -235,11 +235,15 @@ def open_socket(path):
 | ||||
|          sock = socket.socket() | ||||
|          sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | ||||
|          sock.bind((host, int(port))) | ||||
| +
 | ||||
| +        # If given port was 0 the final port number is now available
 | ||||
| +        path = '%s:%d' % sock.getsockname()
 | ||||
|      else: | ||||
|          sock = socket.socket(socket.AF_UNIX) | ||||
|          sock.bind(path) | ||||
|      sock.listen(0) | ||||
|      print 'Listening on %s' % path | ||||
| +    sys.stdout.flush() # another process may be waiting, show message now
 | ||||
|      return sock | ||||
|   | ||||
|  def usage(args): | ||||
| -- 
 | ||||
| 1.8.3.1 | ||||
| 
 | ||||
							
								
								
									
										454
									
								
								debian/patches/extra/0046-kvm-qemu-iotests-test-NBD-over-UNIX-domain-sockets-in-08.patch
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										454
									
								
								debian/patches/extra/0046-kvm-qemu-iotests-test-NBD-over-UNIX-domain-sockets-in-08.patch
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,454 @@ | ||||
| From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 | ||||
| From: Eric Blake <eblake@redhat.com> | ||||
| Date: Wed, 27 Sep 2017 17:57:24 +0200 | ||||
| Subject: [PATCH] qemu-iotests: test NBD over UNIX domain sockets in 083 | ||||
| 
 | ||||
| RH-Author: Eric Blake <eblake@redhat.com> | ||||
| Message-id: <20170927175725.20023-7-eblake@redhat.com> | ||||
| Patchwork-id: 76670 | ||||
| O-Subject: [RHEV-7.4.z qemu-kvm-rhev PATCH 6/7] qemu-iotests: test NBD over UNIX domain sockets in 083 | ||||
| Bugzilla: 1495474 | ||||
| RH-Acked-by: Max Reitz <mreitz@redhat.com> | ||||
| RH-Acked-by: Jeffrey Cody <jcody@redhat.com> | ||||
| RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com> | ||||
| 
 | ||||
| From: Stefan Hajnoczi <stefanha@redhat.com> | ||||
| 
 | ||||
| 083 only tests TCP.  Some failures might be specific to UNIX domain | ||||
| sockets. | ||||
| 
 | ||||
| A few adjustments are necessary: | ||||
| 
 | ||||
| 1. Generating a port number and waiting for server startup is | ||||
|    TCP-specific.  Use the new nbd-fault-injector.py startup protocol to | ||||
|    fetch the address.  This is a little more elegant because we don't | ||||
|    need netstat anymore. | ||||
| 
 | ||||
| 2. The NBD filter does not work for the UNIX domain sockets URIs we | ||||
|    generate and must be extended. | ||||
| 
 | ||||
| 3. Run all tests twice: once for TCP and once for UNIX domain sockets. | ||||
| 
 | ||||
| Reviewed-by: Eric Blake <eblake@redhat.com> | ||||
| Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||||
| Message-Id: <20170829122745.14309-4-stefanha@redhat.com> | ||||
| Signed-off-by: Eric Blake <eblake@redhat.com> | ||||
| (cherry picked from commit 02d2d860d25e439f0e88658c701668ab684568fb) | ||||
| Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com> | ||||
| 
 | ||||
| Conflicts: | ||||
| 	tests/qemu-iotests/083.out - error message improvements not backported | ||||
| 
 | ||||
| Signed-off-by: Eric Blake <eblake@redhat.com> | ||||
| ---
 | ||||
|  tests/qemu-iotests/083           | 136 +++++++++++++++++++++++-------------- | ||||
|  tests/qemu-iotests/083.out       | 143 ++++++++++++++++++++++++++++++++++----- | ||||
|  tests/qemu-iotests/common.filter |   4 +- | ||||
|  3 files changed, 212 insertions(+), 71 deletions(-) | ||||
| 
 | ||||
| diff --git a/tests/qemu-iotests/083 b/tests/qemu-iotests/083
 | ||||
| index bff9360..0306f11 100755
 | ||||
| --- a/tests/qemu-iotests/083
 | ||||
| +++ b/tests/qemu-iotests/083
 | ||||
| @@ -27,6 +27,14 @@ echo "QA output created by $seq"
 | ||||
|  here=`pwd` | ||||
|  status=1	# failure is the default! | ||||
|   | ||||
| +_cleanup()
 | ||||
| +{
 | ||||
| +	rm -f nbd.sock
 | ||||
| +	rm -f nbd-fault-injector.out
 | ||||
| +	rm -f nbd-fault-injector.conf
 | ||||
| +}
 | ||||
| +trap "_cleanup; exit \$status" 0 1 2 3 15
 | ||||
| +
 | ||||
|  # get standard environment, filters and checks | ||||
|  . ./common.rc | ||||
|  . ./common.filter | ||||
| @@ -35,81 +43,105 @@ _supported_fmt generic
 | ||||
|  _supported_proto nbd | ||||
|  _supported_os Linux | ||||
|   | ||||
| -# Pick a TCP port based on our pid.  This way multiple instances of this test
 | ||||
| -# can run in parallel without conflicting.
 | ||||
| -choose_tcp_port() {
 | ||||
| -	echo $((($$ % 31744) + 1024)) # 1024 <= port < 32768
 | ||||
| -}
 | ||||
| -
 | ||||
| -wait_for_tcp_port() {
 | ||||
| -	while ! (netstat --tcp --listening --numeric | \
 | ||||
| -		 grep "$1.*0\\.0\\.0\\.0:\\*.*LISTEN") >/dev/null 2>&1; do
 | ||||
| -		sleep 0.1
 | ||||
| +check_disconnect() {
 | ||||
| +	local event export_name=foo extra_args nbd_addr nbd_url proto when
 | ||||
| +
 | ||||
| +	while true; do
 | ||||
| +		case $1 in
 | ||||
| +		--classic-negotiation)
 | ||||
| +			shift
 | ||||
| +			extra_args=--classic-negotiation
 | ||||
| +			export_name=
 | ||||
| +			;;
 | ||||
| +		--tcp)
 | ||||
| +			shift
 | ||||
| +			proto=tcp
 | ||||
| +			;;
 | ||||
| +		--unix)
 | ||||
| +			shift
 | ||||
| +			proto=unix
 | ||||
| +			;;
 | ||||
| +		*)
 | ||||
| +			break
 | ||||
| +			;;
 | ||||
| +		esac
 | ||||
|  	done | ||||
| -}
 | ||||
|   | ||||
| -check_disconnect() {
 | ||||
|  	event=$1 | ||||
|  	when=$2 | ||||
| -	negotiation=$3
 | ||||
|  	echo "=== Check disconnect $when $event ===" | ||||
|  	echo | ||||
|   | ||||
| -	port=$(choose_tcp_port)
 | ||||
| -
 | ||||
|  	cat > "$TEST_DIR/nbd-fault-injector.conf" <<EOF | ||||
|  [inject-error] | ||||
|  event=$event | ||||
|  when=$when | ||||
|  EOF | ||||
|   | ||||
| -	if [ "$negotiation" = "--classic-negotiation" ]; then
 | ||||
| -		extra_args=--classic-negotiation
 | ||||
| -		nbd_url="nbd:127.0.0.1:$port"
 | ||||
| +	if [ "$proto" = "tcp" ]; then
 | ||||
| +		nbd_addr="127.0.0.1:0"
 | ||||
|  	else | ||||
| -		nbd_url="nbd:127.0.0.1:$port:exportname=foo"
 | ||||
| +		nbd_addr="$TEST_DIR/nbd.sock"
 | ||||
| +	fi
 | ||||
| +
 | ||||
| +	rm -f "$TEST_DIR/nbd.sock"
 | ||||
| +
 | ||||
| +	$PYTHON nbd-fault-injector.py $extra_args "$nbd_addr" "$TEST_DIR/nbd-fault-injector.conf" >"$TEST_DIR/nbd-fault-injector.out" 2>&1 &
 | ||||
| +
 | ||||
| +	# Wait for server to be ready
 | ||||
| +	while ! grep -q 'Listening on ' "$TEST_DIR/nbd-fault-injector.out"; do
 | ||||
| +		sleep 0.1
 | ||||
| +	done
 | ||||
| +
 | ||||
| +	# Extract the final address (port number has now been assigned in tcp case)
 | ||||
| +	nbd_addr=$(sed 's/Listening on \(.*\)$/\1/' "$TEST_DIR/nbd-fault-injector.out")
 | ||||
| +
 | ||||
| +	if [ "$proto" = "tcp" ]; then
 | ||||
| +		nbd_url="nbd+tcp://$nbd_addr/$export_name"
 | ||||
| +	else
 | ||||
| +		nbd_url="nbd+unix:///$export_name?socket=$nbd_addr"
 | ||||
|  	fi | ||||
|   | ||||
| -	$PYTHON nbd-fault-injector.py $extra_args "127.0.0.1:$port" "$TEST_DIR/nbd-fault-injector.conf" >/dev/null 2>&1 &
 | ||||
| -	wait_for_tcp_port "127\\.0\\.0\\.1:$port"
 | ||||
|  	$QEMU_IO -c "read 0 512" "$nbd_url" 2>&1 | _filter_qemu_io | _filter_nbd | ||||
|   | ||||
|  	echo | ||||
|  } | ||||
|   | ||||
| -for event in neg1 "export" neg2 request reply data; do
 | ||||
| -	for when in before after; do
 | ||||
| -		check_disconnect "$event" "$when"
 | ||||
| -	done
 | ||||
| -
 | ||||
| -	# Also inject short replies from the NBD server
 | ||||
| -	case "$event" in
 | ||||
| -	neg1)
 | ||||
| -		for when in 8 16; do
 | ||||
| -			check_disconnect "$event" "$when"
 | ||||
| -		done
 | ||||
| -		;;
 | ||||
| -	"export")
 | ||||
| -		for when in 4 12 16; do
 | ||||
| -			check_disconnect "$event" "$when"
 | ||||
| +for proto in tcp unix; do
 | ||||
| +	for event in neg1 "export" neg2 request reply data; do
 | ||||
| +		for when in before after; do
 | ||||
| +			check_disconnect "--$proto" "$event" "$when"
 | ||||
|  		done | ||||
| -		;;
 | ||||
| -	neg2)
 | ||||
| -		for when in 8 10; do
 | ||||
| -			check_disconnect "$event" "$when"
 | ||||
| -		done
 | ||||
| -		;;
 | ||||
| -	reply)
 | ||||
| -		for when in 4 8; do
 | ||||
| -			check_disconnect "$event" "$when"
 | ||||
| -		done
 | ||||
| -		;;
 | ||||
| -	esac
 | ||||
| -done
 | ||||
|   | ||||
| -# Also check classic negotiation without export information
 | ||||
| -for when in before 8 16 24 28 after; do
 | ||||
| -	check_disconnect "neg-classic" "$when" --classic-negotiation
 | ||||
| +		# Also inject short replies from the NBD server
 | ||||
| +		case "$event" in
 | ||||
| +		neg1)
 | ||||
| +			for when in 8 16; do
 | ||||
| +				check_disconnect "--$proto" "$event" "$when"
 | ||||
| +			done
 | ||||
| +			;;
 | ||||
| +		"export")
 | ||||
| +			for when in 4 12 16; do
 | ||||
| +				check_disconnect "--$proto" "$event" "$when"
 | ||||
| +			done
 | ||||
| +			;;
 | ||||
| +		neg2)
 | ||||
| +			for when in 8 10; do
 | ||||
| +				check_disconnect "--$proto" "$event" "$when"
 | ||||
| +			done
 | ||||
| +			;;
 | ||||
| +		reply)
 | ||||
| +			for when in 4 8; do
 | ||||
| +				check_disconnect "--$proto" "$event" "$when"
 | ||||
| +			done
 | ||||
| +			;;
 | ||||
| +		esac
 | ||||
| +	done
 | ||||
| +
 | ||||
| +	# Also check classic negotiation without export information
 | ||||
| +	for when in before 8 16 24 28 after; do
 | ||||
| +		check_disconnect "--$proto" --classic-negotiation "neg-classic" "$when"
 | ||||
| +	done
 | ||||
|  done | ||||
|   | ||||
|  # success, all done | ||||
| diff --git a/tests/qemu-iotests/083.out b/tests/qemu-iotests/083.out
 | ||||
| index 0c13888..7419722 100644
 | ||||
| --- a/tests/qemu-iotests/083.out
 | ||||
| +++ b/tests/qemu-iotests/083.out
 | ||||
| @@ -1,43 +1,43 @@
 | ||||
|  QA output created by 083 | ||||
|  === Check disconnect before neg1 === | ||||
|   | ||||
| -can't open device nbd:127.0.0.1:PORT:exportname=foo
 | ||||
| +can't open device nbd+tcp://127.0.0.1:PORT/foo
 | ||||
|   | ||||
|  === Check disconnect after neg1 === | ||||
|   | ||||
| -can't open device nbd:127.0.0.1:PORT:exportname=foo
 | ||||
| +can't open device nbd+tcp://127.0.0.1:PORT/foo
 | ||||
|   | ||||
|  === Check disconnect 8 neg1 === | ||||
|   | ||||
| -can't open device nbd:127.0.0.1:PORT:exportname=foo
 | ||||
| +can't open device nbd+tcp://127.0.0.1:PORT/foo
 | ||||
|   | ||||
|  === Check disconnect 16 neg1 === | ||||
|   | ||||
| -can't open device nbd:127.0.0.1:PORT:exportname=foo
 | ||||
| +can't open device nbd+tcp://127.0.0.1:PORT/foo
 | ||||
|   | ||||
|  === Check disconnect before export === | ||||
|   | ||||
| -can't open device nbd:127.0.0.1:PORT:exportname=foo
 | ||||
| +can't open device nbd+tcp://127.0.0.1:PORT/foo
 | ||||
|   | ||||
|  === Check disconnect after export === | ||||
|   | ||||
| -can't open device nbd:127.0.0.1:PORT:exportname=foo
 | ||||
| +can't open device nbd+tcp://127.0.0.1:PORT/foo
 | ||||
|   | ||||
|  === Check disconnect 4 export === | ||||
|   | ||||
| -can't open device nbd:127.0.0.1:PORT:exportname=foo
 | ||||
| +can't open device nbd+tcp://127.0.0.1:PORT/foo
 | ||||
|   | ||||
|  === Check disconnect 12 export === | ||||
|   | ||||
| -can't open device nbd:127.0.0.1:PORT:exportname=foo
 | ||||
| +can't open device nbd+tcp://127.0.0.1:PORT/foo
 | ||||
|   | ||||
|  === Check disconnect 16 export === | ||||
|   | ||||
| -can't open device nbd:127.0.0.1:PORT:exportname=foo
 | ||||
| +can't open device nbd+tcp://127.0.0.1:PORT/foo
 | ||||
|   | ||||
|  === Check disconnect before neg2 === | ||||
|   | ||||
| -can't open device nbd:127.0.0.1:PORT:exportname=foo
 | ||||
| +can't open device nbd+tcp://127.0.0.1:PORT/foo
 | ||||
|   | ||||
|  === Check disconnect after neg2 === | ||||
|   | ||||
| @@ -45,11 +45,11 @@ read failed: Input/output error
 | ||||
|   | ||||
|  === Check disconnect 8 neg2 === | ||||
|   | ||||
| -can't open device nbd:127.0.0.1:PORT:exportname=foo
 | ||||
| +can't open device nbd+tcp://127.0.0.1:PORT/foo
 | ||||
|   | ||||
|  === Check disconnect 10 neg2 === | ||||
|   | ||||
| -can't open device nbd:127.0.0.1:PORT:exportname=foo
 | ||||
| +can't open device nbd+tcp://127.0.0.1:PORT/foo
 | ||||
|   | ||||
|  === Check disconnect before request === | ||||
|   | ||||
| @@ -86,23 +86,132 @@ read 512/512 bytes at offset 0
 | ||||
|   | ||||
|  === Check disconnect before neg-classic === | ||||
|   | ||||
| -can't open device nbd:127.0.0.1:PORT
 | ||||
| +can't open device nbd+tcp://127.0.0.1:PORT/
 | ||||
|   | ||||
|  === Check disconnect 8 neg-classic === | ||||
|   | ||||
| -can't open device nbd:127.0.0.1:PORT
 | ||||
| +can't open device nbd+tcp://127.0.0.1:PORT/
 | ||||
|   | ||||
|  === Check disconnect 16 neg-classic === | ||||
|   | ||||
| -can't open device nbd:127.0.0.1:PORT
 | ||||
| +can't open device nbd+tcp://127.0.0.1:PORT/
 | ||||
|   | ||||
|  === Check disconnect 24 neg-classic === | ||||
|   | ||||
| -can't open device nbd:127.0.0.1:PORT
 | ||||
| +can't open device nbd+tcp://127.0.0.1:PORT/
 | ||||
|   | ||||
|  === Check disconnect 28 neg-classic === | ||||
|   | ||||
| -can't open device nbd:127.0.0.1:PORT
 | ||||
| +can't open device nbd+tcp://127.0.0.1:PORT/
 | ||||
| +
 | ||||
| +=== Check disconnect after neg-classic ===
 | ||||
| +
 | ||||
| +read failed: Input/output error
 | ||||
| +
 | ||||
| +=== Check disconnect before neg1 ===
 | ||||
| +
 | ||||
| +can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock
 | ||||
| +
 | ||||
| +=== Check disconnect after neg1 ===
 | ||||
| +
 | ||||
| +can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock
 | ||||
| +
 | ||||
| +=== Check disconnect 8 neg1 ===
 | ||||
| +
 | ||||
| +can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock
 | ||||
| +
 | ||||
| +=== Check disconnect 16 neg1 ===
 | ||||
| +
 | ||||
| +can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock
 | ||||
| +
 | ||||
| +=== Check disconnect before export ===
 | ||||
| +
 | ||||
| +can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock
 | ||||
| +
 | ||||
| +=== Check disconnect after export ===
 | ||||
| +
 | ||||
| +can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock
 | ||||
| +
 | ||||
| +=== Check disconnect 4 export ===
 | ||||
| +
 | ||||
| +can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock
 | ||||
| +
 | ||||
| +=== Check disconnect 12 export ===
 | ||||
| +
 | ||||
| +can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock
 | ||||
| +
 | ||||
| +=== Check disconnect 16 export ===
 | ||||
| +
 | ||||
| +can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock
 | ||||
| +
 | ||||
| +=== Check disconnect before neg2 ===
 | ||||
| +
 | ||||
| +can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock
 | ||||
| +
 | ||||
| +=== Check disconnect after neg2 ===
 | ||||
| +
 | ||||
| +read failed: Input/output error
 | ||||
| +
 | ||||
| +=== Check disconnect 8 neg2 ===
 | ||||
| +
 | ||||
| +can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock
 | ||||
| +
 | ||||
| +=== Check disconnect 10 neg2 ===
 | ||||
| +
 | ||||
| +can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock
 | ||||
| +
 | ||||
| +=== Check disconnect before request ===
 | ||||
| +
 | ||||
| +read failed: Input/output error
 | ||||
| +
 | ||||
| +=== Check disconnect after request ===
 | ||||
| +
 | ||||
| +read failed: Input/output error
 | ||||
| +
 | ||||
| +=== Check disconnect before reply ===
 | ||||
| +
 | ||||
| +read failed: Input/output error
 | ||||
| +
 | ||||
| +=== Check disconnect after reply ===
 | ||||
| +
 | ||||
| +read failed: Input/output error
 | ||||
| +
 | ||||
| +=== Check disconnect 4 reply ===
 | ||||
| +
 | ||||
| +read failed: Input/output error
 | ||||
| +
 | ||||
| +=== Check disconnect 8 reply ===
 | ||||
| +
 | ||||
| +read failed: Input/output error
 | ||||
| +
 | ||||
| +=== Check disconnect before data ===
 | ||||
| +
 | ||||
| +read failed: Input/output error
 | ||||
| +
 | ||||
| +=== Check disconnect after data ===
 | ||||
| +
 | ||||
| +read 512/512 bytes at offset 0
 | ||||
| +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
 | ||||
| +
 | ||||
| +=== Check disconnect before neg-classic ===
 | ||||
| +
 | ||||
| +can't open device nbd+unix:///?socket=TEST_DIR/nbd.sock
 | ||||
| +
 | ||||
| +=== Check disconnect 8 neg-classic ===
 | ||||
| +
 | ||||
| +can't open device nbd+unix:///?socket=TEST_DIR/nbd.sock
 | ||||
| +
 | ||||
| +=== Check disconnect 16 neg-classic ===
 | ||||
| +
 | ||||
| +can't open device nbd+unix:///?socket=TEST_DIR/nbd.sock
 | ||||
| +
 | ||||
| +=== Check disconnect 24 neg-classic ===
 | ||||
| +
 | ||||
| +can't open device nbd+unix:///?socket=TEST_DIR/nbd.sock
 | ||||
| +
 | ||||
| +=== Check disconnect 28 neg-classic ===
 | ||||
| +
 | ||||
| +can't open device nbd+unix:///?socket=TEST_DIR/nbd.sock
 | ||||
|   | ||||
|  === Check disconnect after neg-classic === | ||||
|   | ||||
| diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter
 | ||||
| index c9a2d5c..f06d4dc 100644
 | ||||
| --- a/tests/qemu-iotests/common.filter
 | ||||
| +++ b/tests/qemu-iotests/common.filter
 | ||||
| @@ -154,9 +154,9 @@ _filter_nbd()
 | ||||
|      # | ||||
|      # Filter out the TCP port number since this changes between runs. | ||||
|      sed -e '/nbd\/.*\.c:/d' \ | ||||
| -        -e 's#nbd:\(//\)\?127\.0\.0\.1:[0-9]*#nbd:\1127.0.0.1:PORT#g' \
 | ||||
| +        -e 's#127\.0\.0\.1:[0-9]*#127.0.0.1:PORT#g' \
 | ||||
|          -e "s#?socket=$TEST_DIR#?socket=TEST_DIR#g" \ | ||||
| -        -e 's#\(exportname=foo\|PORT\): Failed to .*$#\1#'
 | ||||
| +        -e 's#\(foo\|PORT/\?\|.sock\): Failed to .*$#\1#'
 | ||||
|  } | ||||
|   | ||||
|  # make sure this script returns success | ||||
| -- 
 | ||||
| 1.8.3.1 | ||||
| 
 | ||||
							
								
								
									
										45
									
								
								debian/patches/extra/0047-kvm-block-nbd-client-nbd_co_send_request-fix-return-code.patch
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								debian/patches/extra/0047-kvm-block-nbd-client-nbd_co_send_request-fix-return-code.patch
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,45 @@ | ||||
| From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 | ||||
| From: Eric Blake <eblake@redhat.com> | ||||
| Date: Wed, 27 Sep 2017 17:57:25 +0200 | ||||
| Subject: [PATCH] block/nbd-client: nbd_co_send_request: fix return code | ||||
| 
 | ||||
| RH-Author: Eric Blake <eblake@redhat.com> | ||||
| Message-id: <20170927175725.20023-8-eblake@redhat.com> | ||||
| Patchwork-id: 76673 | ||||
| O-Subject: [RHEV-7.4.z qemu-kvm-rhev PATCH 7/7] block/nbd-client: nbd_co_send_request: fix return code | ||||
| Bugzilla: 1495474 | ||||
| RH-Acked-by: Max Reitz <mreitz@redhat.com> | ||||
| RH-Acked-by: Jeffrey Cody <jcody@redhat.com> | ||||
| RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com> | ||||
| 
 | ||||
| From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||||
| 
 | ||||
| It's incorrect to return success rc >= 0 if we skip qio_channel_writev_all() | ||||
| call due to s->quit. | ||||
| 
 | ||||
| Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||||
| Reviewed-by: Eric Blake <eblake@redhat.com> | ||||
| Message-Id: <20170920124507.18841-4-vsementsov@virtuozzo.com> | ||||
| Signed-off-by: Eric Blake <eblake@redhat.com> | ||||
| (cherry picked from commit a693437037328a95d815ad5aec37ac2f8e130e58) | ||||
| Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com> | ||||
| ---
 | ||||
|  block/nbd-client.c | 2 ++ | ||||
|  1 file changed, 2 insertions(+) | ||||
| 
 | ||||
| diff --git a/block/nbd-client.c b/block/nbd-client.c
 | ||||
| index 434acf6..76789c1 100644
 | ||||
| --- a/block/nbd-client.c
 | ||||
| +++ b/block/nbd-client.c
 | ||||
| @@ -156,6 +156,8 @@ static int nbd_co_send_request(BlockDriverState *bs,
 | ||||
|              if (ret != request->len) { | ||||
|                  rc = -EIO; | ||||
|              } | ||||
| +        } else if (rc >= 0) {
 | ||||
| +            rc = -EIO;
 | ||||
|          } | ||||
|          qio_channel_set_cork(s->ioc, false); | ||||
|      } else { | ||||
| -- 
 | ||||
| 1.8.3.1 | ||||
| 
 | ||||
							
								
								
									
										12
									
								
								debian/patches/series
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								debian/patches/series
									
									
									
									
										vendored
									
									
								
							| @ -62,3 +62,15 @@ extra/0032-i386-Add-spec-ctrl-CPUID-bit.patch | ||||
| extra/0033-i386-Add-FEAT_8000_0008_EBX-CPUID-feature-word.patch | ||||
| extra/0034-i386-Add-new-IBRS-versions-of-Intel-CPU-models.patch | ||||
| extra/0035-ratelimit-don-t-align-wait-time-with-slices.patch | ||||
| extra/0036-kvm-nbd-make-it-thread-safe-fix-qcow2-over-nbd.patch | ||||
| extra/0037-kvm-nbd-strict-nbd_wr_syncv.patch | ||||
| extra/0038-kvm-nbd-read_sync-and-friends-return-0-on-success.patch | ||||
| extra/0039-kvm-nbd-make-nbd_drop-public.patch | ||||
| extra/0040-kvm-nbd-server-get-rid-of-nbd_negotiate_read-and-friends.patch | ||||
| extra/0041-kvm-nbd-client-Fix-regression-when-server-sends-garbage.patch | ||||
| extra/0042-kvm-fix-build-failure-in-nbd_read_reply_entry.patch | ||||
| extra/0043-kvm-nbd-client-avoid-spurious-qio_channel_yield-re-entry.patch | ||||
| extra/0044-kvm-nbd-client-avoid-read_reply_co-entry-if-send-failed.patch | ||||
| extra/0045-kvm-qemu-iotests-improve-nbd-fault-injector.py-startup-p.patch | ||||
| extra/0046-kvm-qemu-iotests-test-NBD-over-UNIX-domain-sockets-in-08.patch | ||||
| extra/0047-kvm-block-nbd-client-nbd_co_send_request-fix-return-code.patch | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Alexandre Derumier
						Alexandre Derumier