nbd patches for 2019-06-13

- add 'qemu-nbd --pid-file'
 - NBD-related iotest improvements
 - NBD code refactoring in preparation for reconnect
 -----BEGIN PGP SIGNATURE-----
 
 iQEcBAABCAAGBQJdAm3WAAoJEKeha0olJ0NqEdMH/iXo+ps7bZNkO9JwEh15VUqQ
 tMDt+lZi3hi1p9u2V8uF6flWsnUWurIQ55r6WXRQj7r2LgZfRzeLCcgbsFA+6cXM
 ChA4HI9Q/3NdXhdNcyGcXfoZ3jPLJZYkUzaE5CbsRxsOm2Wi5xxDsXf4lcp4aJdm
 R2sfccPOFuwIQp09d/6NEA0TCxmfJLEw6KRbcAZ5UKSMnK0VXKF7ZMegM6meDQn8
 mIjjkBAKhGN7q/8FOvluUmnkmGCp0uWfsAtmYCr8/qOSRoKaBFBlfzbAZu6jY9dZ
 yrEPs2RActPUHdYvFeLVOaEXlYTVOYG8NLdXM6Ilp0XDLuUAPkjZYP/VKPj/GzI=
 =JixR
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/ericb/tags/pull-nbd-2019-06-13' into staging

nbd patches for 2019-06-13

- add 'qemu-nbd --pid-file'
- NBD-related iotest improvements
- NBD code refactoring in preparation for reconnect

# gpg: Signature made Thu 13 Jun 2019 16:37:58 BST
# gpg:                using RSA key A7A16B4A2527436A
# gpg: Good signature from "Eric Blake <eblake@redhat.com>" [full]
# gpg:                 aka "Eric Blake (Free Software Programmer) <ebb9@byu.net>" [full]
# gpg:                 aka "[jpeg image of size 6874]" [full]
# Primary key fingerprint: 71C2 CC22 B1C4 6029 27D2  F3AA A7A1 6B4A 2527 436A

* remotes/ericb/tags/pull-nbd-2019-06-13:
  block/nbd: merge NBDClientSession struct back to BDRVNBDState
  block/nbd: merge nbd-client.* to nbd.c
  block/nbd-client: drop stale logout
  nbd/server: Nicer spelling of max BLOCK_STATUS reply length
  iotests: Let 233 run concurrently
  iotests: Use qemu-nbd's --pid-file
  qemu-nbd: Do not close stderr
  iotests.py: Add qemu_nbd_early_pipe()
  qemu-nbd: Add --pid-file option

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2019-06-13 18:17:32 +01:00
commit d1bf88e56f
13 changed files with 1338 additions and 1414 deletions

View File

@ -22,7 +22,7 @@ block-obj-y += null.o mirror.o commit.o io.o create.o
block-obj-y += throttle-groups.o
block-obj-$(CONFIG_LINUX) += nvme.o
block-obj-y += nbd.o nbd-client.o
block-obj-y += nbd.o
block-obj-$(CONFIG_SHEEPDOG) += sheepdog.o
block-obj-$(CONFIG_LIBISCSI) += iscsi.o
block-obj-$(if $(CONFIG_LIBISCSI),y,n) += iscsi-opts.o

File diff suppressed because it is too large Load Diff

View File

@ -1,71 +0,0 @@
#ifndef NBD_CLIENT_H
#define NBD_CLIENT_H
#include "block/nbd.h"
#include "block/block_int.h"
#include "io/channel-socket.h"
/* #define DEBUG_NBD */
#if defined(DEBUG_NBD)
#define logout(fmt, ...) \
fprintf(stderr, "nbd\t%-24s" fmt, __func__, ##__VA_ARGS__)
#else
#define logout(fmt, ...) ((void)0)
#endif
#define MAX_NBD_REQUESTS 16
typedef struct {
Coroutine *coroutine;
uint64_t offset; /* original offset of the request */
bool receiving; /* waiting for connection_co? */
} NBDClientRequest;
typedef struct NBDClientSession {
QIOChannelSocket *sioc; /* The master data channel */
QIOChannel *ioc; /* The current I/O channel which may differ (eg TLS) */
NBDExportInfo info;
CoMutex send_mutex;
CoQueue free_sema;
Coroutine *connection_co;
int in_flight;
NBDClientRequest requests[MAX_NBD_REQUESTS];
NBDReply reply;
BlockDriverState *bs;
bool quit;
} NBDClientSession;
NBDClientSession *nbd_get_client_session(BlockDriverState *bs);
int nbd_client_init(BlockDriverState *bs,
SocketAddress *saddr,
const char *export_name,
QCryptoTLSCreds *tlscreds,
const char *hostname,
const char *x_dirty_bitmap,
Error **errp);
void nbd_client_close(BlockDriverState *bs);
int nbd_client_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes);
int nbd_client_co_flush(BlockDriverState *bs);
int nbd_client_co_pwritev(BlockDriverState *bs, uint64_t offset,
uint64_t bytes, QEMUIOVector *qiov, int flags);
int nbd_client_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset,
int bytes, BdrvRequestFlags flags);
int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset,
uint64_t bytes, QEMUIOVector *qiov, int flags);
void nbd_client_detach_aio_context(BlockDriverState *bs);
void nbd_client_attach_aio_context(BlockDriverState *bs,
AioContext *new_context);
int coroutine_fn nbd_client_co_block_status(BlockDriverState *bs,
bool want_zero,
int64_t offset, int64_t bytes,
int64_t *pnum, int64_t *map,
BlockDriverState **file);
#endif /* NBD_CLIENT_H */

File diff suppressed because it is too large Load Diff

View File

@ -160,11 +160,13 @@ nvme_cmd_map_qiov_iov(void *s, int i, void *page, int pages) "s %p iov[%d] %p pa
# iscsi.c
iscsi_xcopy(void *src_lun, uint64_t src_off, void *dst_lun, uint64_t dst_off, uint64_t bytes, int ret) "src_lun %p offset %"PRIu64" dst_lun %p offset %"PRIu64" bytes %"PRIu64" ret %d"
# nbd-client.c
# nbd.c
nbd_parse_blockstatus_compliance(const char *err) "ignoring extra data from non-compliant server: %s"
nbd_structured_read_compliance(const char *type) "server sent non-compliant unaligned read %s chunk"
nbd_read_reply_entry_fail(int ret, const char *err) "ret = %d, err: %s"
nbd_co_request_fail(uint64_t from, uint32_t len, uint64_t handle, uint16_t flags, uint16_t type, const char *name, int ret, const char *err) "Request failed { .from = %" PRIu64", .len = %" PRIu32 ", .handle = %" PRIu64 ", .flags = 0x%" PRIx16 ", .type = %" PRIu16 " (%s) } ret = %d, err: %s"
nbd_client_connect(const char *export_name) "export '%s'"
nbd_client_connect_success(const char *export_name) "export '%s'"
# ssh.c
ssh_restart_coroutine(void *co) "co=%p"

View File

@ -21,15 +21,18 @@
#include "qapi/error.h"
#include "trace.h"
#include "nbd-internal.h"
#include "qemu/units.h"
#define NBD_META_ID_BASE_ALLOCATION 0
#define NBD_META_ID_DIRTY_BITMAP 1
/* NBD_MAX_BITMAP_EXTENTS: 1 mb of extents data. An empirical
/*
* NBD_MAX_BLOCK_STATUS_EXTENTS: 1 MiB of extents data. An empirical
* constant. If an increase is needed, note that the NBD protocol
* recommends no larger than 32 mb, so that the client won't consider
* the reply as a denial of service attack. */
#define NBD_MAX_BITMAP_EXTENTS (0x100000 / 8)
* the reply as a denial of service attack.
*/
#define NBD_MAX_BLOCK_STATUS_EXTENTS (1 * MiB / 8)
static int system_errno_to_nbd_errno(int err)
{
@ -1960,7 +1963,7 @@ static int nbd_co_send_block_status(NBDClient *client, uint64_t handle,
Error **errp)
{
int ret;
unsigned int nb_extents = dont_fragment ? 1 : NBD_MAX_BITMAP_EXTENTS;
unsigned int nb_extents = dont_fragment ? 1 : NBD_MAX_BLOCK_STATUS_EXTENTS;
NBDExtent *extents = g_new(NBDExtent, nb_extents);
uint64_t final_length = length;
@ -2045,7 +2048,7 @@ static int nbd_co_send_bitmap(NBDClient *client, uint64_t handle,
uint32_t context_id, Error **errp)
{
int ret;
unsigned int nb_extents = dont_fragment ? 1 : NBD_MAX_BITMAP_EXTENTS;
unsigned int nb_extents = dont_fragment ? 1 : NBD_MAX_BLOCK_STATUS_EXTENTS;
NBDExtent *extents = g_new(NBDExtent, nb_extents);
uint64_t final_length = length;

View File

@ -61,6 +61,7 @@
#define QEMU_NBD_OPT_IMAGE_OPTS 262
#define QEMU_NBD_OPT_FORK 263
#define QEMU_NBD_OPT_TLSAUTHZ 264
#define QEMU_NBD_OPT_PID_FILE 265
#define MBR_SIZE 512
@ -113,6 +114,7 @@ static void usage(const char *name)
" specify tracing options\n"
" --fork fork off the server process and exit the parent\n"
" once the server is running\n"
" --pid-file=PATH store the server's process ID in the given file\n"
#if HAVE_NBD_DEVICE
"\n"
"Kernel NBD client support:\n"
@ -641,6 +643,7 @@ int main(int argc, char **argv)
{ "image-opts", no_argument, NULL, QEMU_NBD_OPT_IMAGE_OPTS },
{ "trace", required_argument, NULL, 'T' },
{ "fork", no_argument, NULL, QEMU_NBD_OPT_FORK },
{ "pid-file", required_argument, NULL, QEMU_NBD_OPT_PID_FILE },
{ NULL, 0, NULL, 0 }
};
int ch;
@ -667,6 +670,7 @@ int main(int argc, char **argv)
bool list = false;
int old_stderr = -1;
unsigned socket_activation;
const char *pid_file_name = NULL;
/* The client thread uses SIGTERM to interrupt the server. A signal
* handler ensures that "qemu-nbd -v -c" exits with a nice status code.
@ -866,6 +870,9 @@ int main(int argc, char **argv)
case 'L':
list = true;
break;
case QEMU_NBD_OPT_PID_FILE:
pid_file_name = optarg;
break;
}
}
@ -997,10 +1004,11 @@ int main(int argc, char **argv)
exit(EXIT_FAILURE);
} else if (pid == 0) {
close(stderr_fd[0]);
old_stderr = dup(STDERR_FILENO);
ret = qemu_daemon(1, 0);
/* Temporarily redirect stderr to the parent's pipe... */
old_stderr = dup(STDERR_FILENO);
dup2(stderr_fd[1], STDERR_FILENO);
if (ret < 0) {
error_report("Failed to daemonize: %s", strerror(errno));
@ -1186,6 +1194,10 @@ int main(int argc, char **argv)
nbd_update_server_watch();
if (pid_file_name) {
qemu_write_pidfile(pid_file_name, &error_fatal);
}
/* now when the initialization is (almost) complete, chdir("/")
* to free any busy filesystems */
if (chdir("/") < 0) {

View File

@ -117,6 +117,8 @@ option; or provide the credentials needed for connecting as a client
in list mode.
@item --fork
Fork off the server process and exit the parent once the server is running.
@item --pid-file=PATH
Store the server's process ID in the given file.
@item --tls-authz=ID
Specify the ID of a qauthz object previously created with the
--object option. This will be used to authorize connecting users

View File

@ -24,7 +24,7 @@ import socket
import stat
import time
import iotests
from iotests import cachemode, imgfmt, qemu_img, qemu_nbd, qemu_nbd_pipe
from iotests import cachemode, imgfmt, qemu_img, qemu_nbd, qemu_nbd_early_pipe
NBD_PORT_START = 32768
NBD_PORT_END = NBD_PORT_START + 1024
@ -93,7 +93,7 @@ class QemuNBD(NBDBlockdevAddBase):
pass
def _try_server_up(self, *args):
status, msg = qemu_nbd_pipe('-f', imgfmt, test_img, *args)
status, msg = qemu_nbd_early_pipe('-f', imgfmt, test_img, *args)
if status == 0:
return True
if 'Address already in use' in msg:

View File

@ -49,7 +49,6 @@ _supported_proto file
# If porting to non-Linux, consider using socat instead of ss in common.nbd
_require_command QEMU_NBD
nbd_server_set_tcp_port
tls_x509_init
echo

View File

@ -22,6 +22,11 @@
nbd_unix_socket="${TEST_DIR}/qemu-nbd.sock"
nbd_tcp_addr="127.0.0.1"
nbd_pid_file="${TEST_DIR}/qemu-nbd.pid"
nbd_stderr_fifo="${TEST_DIR}/qemu-nbd.fifo"
# If bash version is >= 4.1, this will be overwritten by a dynamically
# assigned file descriptor value.
nbd_fifo_fd=10
nbd_server_stop()
{
@ -33,77 +38,62 @@ nbd_server_stop()
kill "$NBD_PID"
fi
fi
rm -f "$nbd_unix_socket"
}
nbd_server_wait_for_unix_socket()
{
pid=$1
for ((i = 0; i < 300; i++))
do
if [ -r "$nbd_unix_socket" ]; then
return
fi
kill -s 0 $pid 2>/dev/null
if test $? != 0
then
echo "qemu-nbd unexpectedly quit"
exit 1
fi
sleep 0.1
done
echo "Failed in check of unix socket created by qemu-nbd"
exit 1
rm -f "$nbd_unix_socket" "$nbd_stderr_fifo"
}
nbd_server_start_unix_socket()
{
nbd_server_stop
$QEMU_NBD -v -t -k "$nbd_unix_socket" "$@" &
nbd_server_wait_for_unix_socket $!
}
nbd_server_set_tcp_port()
{
(ss --help) >/dev/null 2>&1 || _notrun "ss utility not found, skipping test"
for ((port = 10809; port <= 10909; port++))
do
if ! ss -tln | grep -sqE ":$port\b"; then
nbd_tcp_port=$port
return
fi
done
echo "Cannot find free TCP port for nbd in range 10809-10909"
exit 1
}
nbd_server_wait_for_tcp_socket()
{
pid=$1
for ((i = 0; i < 300; i++))
do
if ss -tln | grep -sqE ":$nbd_tcp_port\b"; then
return
fi
kill -s 0 $pid 2>/dev/null
if test $? != 0
then
echo "qemu-nbd unexpectedly quit"
exit 1
fi
sleep 0.1
done
echo "Failed in check of TCP socket created by qemu-nbd"
exit 1
$QEMU_NBD -v -t -k "$nbd_unix_socket" --fork "$@"
}
nbd_server_start_tcp_socket()
{
nbd_server_stop
$QEMU_NBD -v -t -b $nbd_tcp_addr -p $nbd_tcp_port "$@" &
nbd_server_wait_for_tcp_socket $!
mkfifo "$nbd_stderr_fifo"
for ((port = 10809; port <= 10909; port++))
do
# Redirect stderr to FIFO, so we can later decide whether we
# want to read it or to redirect it to our stderr, depending
# on whether the command fails or not
$QEMU_NBD -v -t -b $nbd_tcp_addr -p $port --fork "$@" \
2> "$nbd_stderr_fifo" &
# Taken from common.qemu
if [[ "${BASH_VERSINFO[0]}" -ge "5" ||
("${BASH_VERSINFO[0]}" -ge "4" && "${BASH_VERSINFO[1]}" -ge "1") ]]
then
exec {nbd_fifo_fd}<"$nbd_stderr_fifo"
else
let _nbd_fifo_fd++
eval "exec ${_nbd_fifo_fd}<'$nbd_stderr_fifo'"
fi
wait $!
if test $? == 0
then
# Success, redirect qemu-nbd's stderr to our stderr
nbd_tcp_port=$port
(cat <&$nbd_fifo_fd >&2) &
eval "exec $nbd_fifo_fd>&-"
return
fi
# Failure, read the output
output=$(cat <&$nbd_fifo_fd)
eval "exec $nbd_fifo_fd>&-"
if ! echo "$output" | grep -q "Address already in use"
then
# Unknown error, print it
echo "$output" >&2
rm -f "$nbd_stderr_fifo"
exit 1
fi
done
echo "Cannot find free TCP port for nbd in range 10809-10909"
rm -f "$nbd_stderr_fifo"
exit 1
}

View File

@ -105,10 +105,8 @@ _qemu_io_wrapper()
_qemu_nbd_wrapper()
{
(
echo $BASHPID > "${QEMU_TEST_DIR}/qemu-nbd.pid"
exec "$QEMU_NBD_PROG" $QEMU_NBD_OPTIONS "$@"
)
"$QEMU_NBD_PROG" --pid-file="${QEMU_TEST_DIR}/qemu-nbd.pid" \
$QEMU_NBD_OPTIONS "$@"
}
_qemu_vxhs_wrapper()

View File

@ -209,9 +209,9 @@ def qemu_nbd(*args):
'''Run qemu-nbd in daemon mode and return the parent's exit code'''
return subprocess.call(qemu_nbd_args + ['--fork'] + list(args))
def qemu_nbd_pipe(*args):
def qemu_nbd_early_pipe(*args):
'''Run qemu-nbd in daemon mode and return both the parent's exit code
and its output'''
and its output in case of an error'''
subp = subprocess.Popen(qemu_nbd_args + ['--fork'] + list(args),
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
@ -221,7 +221,10 @@ def qemu_nbd_pipe(*args):
sys.stderr.write('qemu-nbd received signal %i: %s\n' %
(-exitcode,
' '.join(qemu_nbd_args + ['--fork'] + list(args))))
return exitcode, subp.communicate()[0]
if exitcode == 0:
return exitcode, ''
else:
return exitcode, subp.communicate()[0]
def compare_images(img1, img2, fmt1=imgfmt, fmt2=imgfmt):
'''Return True if two image files are identical'''