Block layer patches

-----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.22 (GNU/Linux)
 
 iQIcBAABAgAGBQJYEjZDAAoJEH8JsnLIjy/WSXQQALz5t/O6jnLkLMceuJvAdxzk
 1w3cwcc2I2VOSubqnUXuK9KCFK0R8ifN0UmYNENbh9DCNEhDpbD16hbkO5LvnRwk
 IaFpt1jAP7Y9epZ3GnJ0JdAsk+PDnqj3zQW6PSJLyzXVOnyVCE7aU+fjEv3Khoa7
 88ye3xBx8F9RDaQ4EBOSm55q/gEk0RHnFbgq/YurHzWg5go23VwunVBP7XALG6Bs
 2jm5/iEKXdoIoJ57dADQzUf2WWMgE73CO9kd/c9iaFmd2FWOHHRsuFyj7a38bKmn
 N7kRDBrC3MlOPxP6zB2jBNmAa70cdQO9Ktqm7geTTb1WWSxxUHBuX1TtAsish6d7
 aYVSZyNoaSdwcRSnNTnJkscNItldAUtoPvgrYCbniWVRU7YiY+yUXsQyWdmpKbTE
 JLy06p4mGZEuDR5RDMWZfaJbw+eNtmpiL9vMRBM+A9EzMIhuVm1hu34/SRyvcPSM
 fyzW5gAYsPA7E+nZT1Jkpw/f8jxxUo1vdhQpWWGijCMK5kpkumfWXZKnuSKwvRYz
 xcHdGE1nbLfWaBgyClZCLRMNb3CFuDEsr4NCrjwGr4xdLs9VbxXxvNqvFl8DOWR/
 amVsQd6fvTfcusDxY9hY6BOlgID/dqfi7wzvxowDhsTG6ewSsqwjYLzUwADzcKn0
 sAmLBX3P3sJNrf7Y9UaT
 =BtvM
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging

Block layer patches

# gpg: Signature made Thu 27 Oct 2016 18:15:47 BST
# gpg:                using RSA key 0x7F09B272C88F2FD6
# gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>"
# Primary key fingerprint: DC3D EB15 9A9A F95D 3D74  56FE 7F09 B272 C88F 2FD6

* remotes/kevin/tags/for-upstream: (23 commits)
  iotests: Add test for NBD's blockdev-add interface
  iotests: Add assert_json_filename_equal() method
  socket_scm_helper: Accept fd directly
  iotests.py: Allow concurrent qemu instances
  iotests.py: Add qemu_nbd function
  qapi: Allow blockdev-add for NBD
  block/nbd: Use SocketAddress options
  block/nbd: Accept SocketAddress
  block/nbd: Add nbd_has_filename_options_conflict()
  block/nbd: Use qdict_put()
  block/nbd: Default port in nbd_refresh_filename()
  block/nbd: Reject port parameter without host
  block/nbd: Drop trailing "." in error messages
  qemu-iotests: Fix typo for NFS with IMGOPTSSYNTAX
  block: Remove bdrv_aio_ioctl()
  raw: Implement .bdrv_co_ioctl instead of .bdrv_aio_ioctl
  block: Introduce .bdrv_co_ioctl() driver callback
  block: Remove bdrv_ioctl()
  raw-posix: Don't use bdrv_ioctl()
  block: Use blk_co_ioctl() for all BB level ioctls
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2016-10-28 12:06:41 +01:00
commit 9879b75873
18 changed files with 536 additions and 259 deletions

View File

@ -1099,26 +1099,36 @@ BlockAIOCB *blk_aio_pwritev(BlockBackend *blk, int64_t offset,
blk_aio_write_entry, flags, cb, opaque); blk_aio_write_entry, flags, cb, opaque);
} }
static void blk_aio_flush_entry(void *opaque)
{
BlkAioEmAIOCB *acb = opaque;
BlkRwCo *rwco = &acb->rwco;
rwco->ret = blk_co_flush(rwco->blk);
blk_aio_complete(acb);
}
BlockAIOCB *blk_aio_flush(BlockBackend *blk, BlockAIOCB *blk_aio_flush(BlockBackend *blk,
BlockCompletionFunc *cb, void *opaque) BlockCompletionFunc *cb, void *opaque)
{ {
if (!blk_is_available(blk)) { return blk_aio_prwv(blk, 0, 0, NULL, blk_aio_flush_entry, 0, cb, opaque);
return blk_abort_aio_request(blk, cb, opaque, -ENOMEDIUM); }
}
return bdrv_aio_flush(blk_bs(blk), cb, opaque); static void blk_aio_pdiscard_entry(void *opaque)
{
BlkAioEmAIOCB *acb = opaque;
BlkRwCo *rwco = &acb->rwco;
rwco->ret = blk_co_pdiscard(rwco->blk, rwco->offset, acb->bytes);
blk_aio_complete(acb);
} }
BlockAIOCB *blk_aio_pdiscard(BlockBackend *blk, BlockAIOCB *blk_aio_pdiscard(BlockBackend *blk,
int64_t offset, int count, int64_t offset, int count,
BlockCompletionFunc *cb, void *opaque) BlockCompletionFunc *cb, void *opaque)
{ {
int ret = blk_check_byte_request(blk, offset, count); return blk_aio_prwv(blk, offset, count, NULL, blk_aio_pdiscard_entry, 0,
if (ret < 0) { cb, opaque);
return blk_abort_aio_request(blk, cb, opaque, ret);
}
return bdrv_aio_pdiscard(blk_bs(blk), offset, count, cb, opaque);
} }
void blk_aio_cancel(BlockAIOCB *acb) void blk_aio_cancel(BlockAIOCB *acb)
@ -1131,23 +1141,50 @@ void blk_aio_cancel_async(BlockAIOCB *acb)
bdrv_aio_cancel_async(acb); bdrv_aio_cancel_async(acb);
} }
int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf) int blk_co_ioctl(BlockBackend *blk, unsigned long int req, void *buf)
{ {
if (!blk_is_available(blk)) { if (!blk_is_available(blk)) {
return -ENOMEDIUM; return -ENOMEDIUM;
} }
return bdrv_ioctl(blk_bs(blk), req, buf); return bdrv_co_ioctl(blk_bs(blk), req, buf);
}
static void blk_ioctl_entry(void *opaque)
{
BlkRwCo *rwco = opaque;
rwco->ret = blk_co_ioctl(rwco->blk, rwco->offset,
rwco->qiov->iov[0].iov_base);
}
int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf)
{
return blk_prw(blk, req, buf, 0, blk_ioctl_entry, 0);
}
static void blk_aio_ioctl_entry(void *opaque)
{
BlkAioEmAIOCB *acb = opaque;
BlkRwCo *rwco = &acb->rwco;
rwco->ret = blk_co_ioctl(rwco->blk, rwco->offset,
rwco->qiov->iov[0].iov_base);
blk_aio_complete(acb);
} }
BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf, BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf,
BlockCompletionFunc *cb, void *opaque) BlockCompletionFunc *cb, void *opaque)
{ {
if (!blk_is_available(blk)) { QEMUIOVector qiov;
return blk_abort_aio_request(blk, cb, opaque, -ENOMEDIUM); struct iovec iov;
}
return bdrv_aio_ioctl(blk_bs(blk), req, buf, cb, opaque); iov = (struct iovec) {
.iov_base = buf,
.iov_len = 0,
};
qemu_iovec_init_external(&qiov, &iov, 1);
return blk_aio_prwv(blk, req, 0, &qiov, blk_aio_ioctl_entry, 0, cb, opaque);
} }
int blk_co_pdiscard(BlockBackend *blk, int64_t offset, int count) int blk_co_pdiscard(BlockBackend *blk, int64_t offset, int count)
@ -1169,13 +1206,15 @@ int blk_co_flush(BlockBackend *blk)
return bdrv_co_flush(blk_bs(blk)); return bdrv_co_flush(blk_bs(blk));
} }
static void blk_flush_entry(void *opaque)
{
BlkRwCo *rwco = opaque;
rwco->ret = blk_co_flush(rwco->blk);
}
int blk_flush(BlockBackend *blk) int blk_flush(BlockBackend *blk)
{ {
if (!blk_is_available(blk)) { return blk_prw(blk, 0, NULL, 0, blk_flush_entry, 0);
return -ENOMEDIUM;
}
return bdrv_flush(blk_bs(blk));
} }
void blk_drain(BlockBackend *blk) void blk_drain(BlockBackend *blk)
@ -1555,14 +1594,15 @@ int blk_truncate(BlockBackend *blk, int64_t offset)
return bdrv_truncate(blk_bs(blk), offset); return bdrv_truncate(blk_bs(blk), offset);
} }
static void blk_pdiscard_entry(void *opaque)
{
BlkRwCo *rwco = opaque;
rwco->ret = blk_co_pdiscard(rwco->blk, rwco->offset, rwco->qiov->size);
}
int blk_pdiscard(BlockBackend *blk, int64_t offset, int count) int blk_pdiscard(BlockBackend *blk, int64_t offset, int count)
{ {
int ret = blk_check_byte_request(blk, offset, count); return blk_prw(blk, offset, NULL, count, blk_pdiscard_entry, 0);
if (ret < 0) {
return ret;
}
return bdrv_pdiscard(blk_bs(blk), offset, count);
} }
int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf, int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf,

View File

@ -2196,35 +2196,6 @@ BlockAIOCB *bdrv_aio_flush(BlockDriverState *bs,
return &acb->common; return &acb->common;
} }
static void coroutine_fn bdrv_aio_pdiscard_co_entry(void *opaque)
{
BlockAIOCBCoroutine *acb = opaque;
BlockDriverState *bs = acb->common.bs;
acb->req.error = bdrv_co_pdiscard(bs, acb->req.offset, acb->req.bytes);
bdrv_co_complete(acb);
}
BlockAIOCB *bdrv_aio_pdiscard(BlockDriverState *bs, int64_t offset, int count,
BlockCompletionFunc *cb, void *opaque)
{
Coroutine *co;
BlockAIOCBCoroutine *acb;
trace_bdrv_aio_pdiscard(bs, offset, count, opaque);
acb = qemu_aio_get(&bdrv_em_co_aiocb_info, bs, cb, opaque);
acb->need_bh = true;
acb->req.error = -EINPROGRESS;
acb->req.offset = offset;
acb->req.bytes = count;
co = qemu_coroutine_create(bdrv_aio_pdiscard_co_entry, acb);
qemu_coroutine_enter(co);
bdrv_co_maybe_schedule_bh(acb);
return &acb->common;
}
void *qemu_aio_get(const AIOCBInfo *aiocb_info, BlockDriverState *bs, void *qemu_aio_get(const AIOCBInfo *aiocb_info, BlockDriverState *bs,
BlockCompletionFunc *cb, void *opaque) BlockCompletionFunc *cb, void *opaque)
{ {
@ -2521,7 +2492,7 @@ int bdrv_pdiscard(BlockDriverState *bs, int64_t offset, int count)
return rwco.ret; return rwco.ret;
} }
static int bdrv_co_do_ioctl(BlockDriverState *bs, int req, void *buf) int bdrv_co_ioctl(BlockDriverState *bs, int req, void *buf)
{ {
BlockDriver *drv = bs->drv; BlockDriver *drv = bs->drv;
BdrvTrackedRequest tracked_req; BdrvTrackedRequest tracked_req;
@ -2531,86 +2502,26 @@ static int bdrv_co_do_ioctl(BlockDriverState *bs, int req, void *buf)
BlockAIOCB *acb; BlockAIOCB *acb;
tracked_request_begin(&tracked_req, bs, 0, 0, BDRV_TRACKED_IOCTL); tracked_request_begin(&tracked_req, bs, 0, 0, BDRV_TRACKED_IOCTL);
if (!drv || !drv->bdrv_aio_ioctl) { if (!drv || (!drv->bdrv_aio_ioctl && !drv->bdrv_co_ioctl)) {
co.ret = -ENOTSUP; co.ret = -ENOTSUP;
goto out; goto out;
} }
if (drv->bdrv_co_ioctl) {
co.ret = drv->bdrv_co_ioctl(bs, req, buf);
} else {
acb = drv->bdrv_aio_ioctl(bs, req, buf, bdrv_co_io_em_complete, &co); acb = drv->bdrv_aio_ioctl(bs, req, buf, bdrv_co_io_em_complete, &co);
if (!acb) { if (!acb) {
co.ret = -ENOTSUP; co.ret = -ENOTSUP;
goto out; goto out;
} }
qemu_coroutine_yield(); qemu_coroutine_yield();
}
out: out:
tracked_request_end(&tracked_req); tracked_request_end(&tracked_req);
return co.ret; return co.ret;
} }
typedef struct {
BlockDriverState *bs;
int req;
void *buf;
int ret;
} BdrvIoctlCoData;
static void coroutine_fn bdrv_co_ioctl_entry(void *opaque)
{
BdrvIoctlCoData *data = opaque;
data->ret = bdrv_co_do_ioctl(data->bs, data->req, data->buf);
}
/* needed for generic scsi interface */
int bdrv_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
{
BdrvIoctlCoData data = {
.bs = bs,
.req = req,
.buf = buf,
.ret = -EINPROGRESS,
};
if (qemu_in_coroutine()) {
/* Fast-path if already in coroutine context */
bdrv_co_ioctl_entry(&data);
} else {
Coroutine *co = qemu_coroutine_create(bdrv_co_ioctl_entry, &data);
qemu_coroutine_enter(co);
while (data.ret == -EINPROGRESS) {
aio_poll(bdrv_get_aio_context(bs), true);
}
}
return data.ret;
}
static void coroutine_fn bdrv_co_aio_ioctl_entry(void *opaque)
{
BlockAIOCBCoroutine *acb = opaque;
acb->req.error = bdrv_co_do_ioctl(acb->common.bs,
acb->req.req, acb->req.buf);
bdrv_co_complete(acb);
}
BlockAIOCB *bdrv_aio_ioctl(BlockDriverState *bs,
unsigned long int req, void *buf,
BlockCompletionFunc *cb, void *opaque)
{
BlockAIOCBCoroutine *acb = qemu_aio_get(&bdrv_em_co_aiocb_info,
bs, cb, opaque);
Coroutine *co;
acb->need_bh = true;
acb->req.error = -EINPROGRESS;
acb->req.req = req;
acb->req.buf = buf;
co = qemu_coroutine_create(bdrv_co_aio_ioctl_entry, acb);
qemu_coroutine_enter(co);
bdrv_co_maybe_schedule_bh(acb);
return &acb->common;
}
void *qemu_blockalign(BlockDriverState *bs, size_t size) void *qemu_blockalign(BlockDriverState *bs, size_t size)
{ {
return qemu_memalign(bdrv_opt_mem_align(bs), size); return qemu_memalign(bdrv_opt_mem_align(bs), size);

View File

@ -32,6 +32,9 @@
#include "qemu/uri.h" #include "qemu/uri.h"
#include "block/block_int.h" #include "block/block_int.h"
#include "qemu/module.h" #include "qemu/module.h"
#include "qapi-visit.h"
#include "qapi/qobject-input-visitor.h"
#include "qapi/qobject-output-visitor.h"
#include "qapi/qmp/qdict.h" #include "qapi/qmp/qdict.h"
#include "qapi/qmp/qjson.h" #include "qapi/qmp/qjson.h"
#include "qapi/qmp/qint.h" #include "qapi/qmp/qint.h"
@ -44,7 +47,8 @@ typedef struct BDRVNBDState {
NbdClientSession client; NbdClientSession client;
/* For nbd_refresh_filename() */ /* For nbd_refresh_filename() */
char *path, *host, *port, *export, *tlscredsid; SocketAddress *saddr;
char *export, *tlscredsid;
} BDRVNBDState; } BDRVNBDState;
static int nbd_parse_uri(const char *filename, QDict *options) static int nbd_parse_uri(const char *filename, QDict *options)
@ -90,9 +94,13 @@ static int nbd_parse_uri(const char *filename, QDict *options)
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
} }
qdict_put(options, "path", qstring_from_str(qp->p[0].value)); qdict_put(options, "server.type", qstring_from_str("unix"));
qdict_put(options, "server.data.path",
qstring_from_str(qp->p[0].value));
} else { } else {
QString *host; QString *host;
char *port_str;
/* nbd[+tcp]://host[:port]/export */ /* nbd[+tcp]://host[:port]/export */
if (!uri->server) { if (!uri->server) {
ret = -EINVAL; ret = -EINVAL;
@ -107,13 +115,13 @@ static int nbd_parse_uri(const char *filename, QDict *options)
host = qstring_from_str(uri->server); host = qstring_from_str(uri->server);
} }
qdict_put(options, "host", host); qdict_put(options, "server.type", qstring_from_str("inet"));
if (uri->port) { qdict_put(options, "server.data.host", host);
char* port_str = g_strdup_printf("%d", uri->port);
qdict_put(options, "port", qstring_from_str(port_str)); port_str = g_strdup_printf("%d", uri->port ?: NBD_DEFAULT_PORT);
qdict_put(options, "server.data.port", qstring_from_str(port_str));
g_free(port_str); g_free(port_str);
} }
}
out: out:
if (qp) { if (qp) {
@ -123,6 +131,26 @@ out:
return ret; return ret;
} }
static bool nbd_has_filename_options_conflict(QDict *options, Error **errp)
{
const QDictEntry *e;
for (e = qdict_first(options); e; e = qdict_next(options, e)) {
if (!strcmp(e->key, "host") ||
!strcmp(e->key, "port") ||
!strcmp(e->key, "path") ||
!strcmp(e->key, "export") ||
strstart(e->key, "server.", NULL))
{
error_setg(errp, "Option '%s' cannot be used with a file name",
e->key);
return true;
}
}
return false;
}
static void nbd_parse_filename(const char *filename, QDict *options, static void nbd_parse_filename(const char *filename, QDict *options,
Error **errp) Error **errp)
{ {
@ -131,12 +159,7 @@ static void nbd_parse_filename(const char *filename, QDict *options,
const char *host_spec; const char *host_spec;
const char *unixpath; const char *unixpath;
if (qdict_haskey(options, "host") if (nbd_has_filename_options_conflict(options, errp)) {
|| qdict_haskey(options, "port")
|| qdict_haskey(options, "path"))
{
error_setg(errp, "host/port/path and a file name may not be specified "
"at the same time");
return; return;
} }
@ -173,7 +196,8 @@ static void nbd_parse_filename(const char *filename, QDict *options,
/* are we a UNIX or TCP socket? */ /* are we a UNIX or TCP socket? */
if (strstart(host_spec, "unix:", &unixpath)) { if (strstart(host_spec, "unix:", &unixpath)) {
qdict_put(options, "path", qstring_from_str(unixpath)); qdict_put(options, "server.type", qstring_from_str("unix"));
qdict_put(options, "server.data.path", qstring_from_str(unixpath));
} else { } else {
InetSocketAddress *addr = NULL; InetSocketAddress *addr = NULL;
@ -182,8 +206,9 @@ static void nbd_parse_filename(const char *filename, QDict *options,
goto out; goto out;
} }
qdict_put(options, "host", qstring_from_str(addr->host)); qdict_put(options, "server.type", qstring_from_str("inet"));
qdict_put(options, "port", qstring_from_str(addr->port)); qdict_put(options, "server.data.host", qstring_from_str(addr->host));
qdict_put(options, "server.data.port", qstring_from_str(addr->port));
qapi_free_InetSocketAddress(addr); qapi_free_InetSocketAddress(addr);
} }
@ -191,47 +216,81 @@ out:
g_free(file); g_free(file);
} }
static SocketAddress *nbd_config(BDRVNBDState *s, QemuOpts *opts, Error **errp) static bool nbd_process_legacy_socket_options(QDict *output_options,
QemuOpts *legacy_opts,
Error **errp)
{ {
SocketAddress *saddr; const char *path = qemu_opt_get(legacy_opts, "path");
const char *host = qemu_opt_get(legacy_opts, "host");
const char *port = qemu_opt_get(legacy_opts, "port");
const QDictEntry *e;
s->path = g_strdup(qemu_opt_get(opts, "path")); if (!path && !host && !port) {
s->host = g_strdup(qemu_opt_get(opts, "host")); return true;
if (!s->path == !s->host) {
if (s->path) {
error_setg(errp, "path and host may not be used at the same time.");
} else {
error_setg(errp, "one of path and host must be specified.");
}
return NULL;
} }
saddr = g_new0(SocketAddress, 1); for (e = qdict_first(output_options); e; e = qdict_next(output_options, e))
{
if (s->path) { if (strstart(e->key, "server.", NULL)) {
UnixSocketAddress *q_unix; error_setg(errp, "Cannot use 'server' and path/host/port at the "
saddr->type = SOCKET_ADDRESS_KIND_UNIX; "same time");
q_unix = saddr->u.q_unix.data = g_new0(UnixSocketAddress, 1); return false;
q_unix->path = g_strdup(s->path);
} else {
InetSocketAddress *inet;
s->port = g_strdup(qemu_opt_get(opts, "port"));
saddr->type = SOCKET_ADDRESS_KIND_INET;
inet = saddr->u.inet.data = g_new0(InetSocketAddress, 1);
inet->host = g_strdup(s->host);
inet->port = g_strdup(s->port);
if (!inet->port) {
inet->port = g_strdup_printf("%d", NBD_DEFAULT_PORT);
} }
} }
if (path && host) {
error_setg(errp, "path and host may not be used at the same time");
return false;
} else if (path) {
if (port) {
error_setg(errp, "port may not be used without host");
return false;
}
qdict_put(output_options, "server.type", qstring_from_str("unix"));
qdict_put(output_options, "server.data.path", qstring_from_str(path));
} else if (host) {
qdict_put(output_options, "server.type", qstring_from_str("inet"));
qdict_put(output_options, "server.data.host", qstring_from_str(host));
qdict_put(output_options, "server.data.port",
qstring_from_str(port ?: stringify(NBD_DEFAULT_PORT)));
}
return true;
}
static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, Error **errp)
{
SocketAddress *saddr = NULL;
QDict *addr = NULL;
QObject *crumpled_addr = NULL;
Visitor *iv = NULL;
Error *local_err = NULL;
qdict_extract_subqdict(options, &addr, "server.");
if (!qdict_size(addr)) {
error_setg(errp, "NBD server address missing");
goto done;
}
crumpled_addr = qdict_crumple(addr, errp);
if (!crumpled_addr) {
goto done;
}
iv = qobject_input_visitor_new(crumpled_addr, true);
visit_type_SocketAddress(iv, NULL, &saddr, &local_err);
if (local_err) {
error_propagate(errp, local_err);
goto done;
}
s->client.is_unix = saddr->type == SOCKET_ADDRESS_KIND_UNIX; s->client.is_unix = saddr->type == SOCKET_ADDRESS_KIND_UNIX;
s->export = g_strdup(qemu_opt_get(opts, "export")); done:
QDECREF(addr);
qobject_decref(crumpled_addr);
visit_free(iv);
return saddr; return saddr;
} }
@ -332,7 +391,6 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
QemuOpts *opts = NULL; QemuOpts *opts = NULL;
Error *local_err = NULL; Error *local_err = NULL;
QIOChannelSocket *sioc = NULL; QIOChannelSocket *sioc = NULL;
SocketAddress *saddr = NULL;
QCryptoTLSCreds *tlscreds = NULL; QCryptoTLSCreds *tlscreds = NULL;
const char *hostname = NULL; const char *hostname = NULL;
int ret = -EINVAL; int ret = -EINVAL;
@ -344,12 +402,19 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
goto error; goto error;
} }
/* Pop the config into our state object. Exit if invalid. */ /* Translate @host, @port, and @path to a SocketAddress */
saddr = nbd_config(s, opts, errp); if (!nbd_process_legacy_socket_options(options, opts, errp)) {
if (!saddr) {
goto error; goto error;
} }
/* Pop the config into our state object. Exit if invalid. */
s->saddr = nbd_config(s, options, errp);
if (!s->saddr) {
goto error;
}
s->export = g_strdup(qemu_opt_get(opts, "export"));
s->tlscredsid = g_strdup(qemu_opt_get(opts, "tls-creds")); s->tlscredsid = g_strdup(qemu_opt_get(opts, "tls-creds"));
if (s->tlscredsid) { if (s->tlscredsid) {
tlscreds = nbd_get_tls_creds(s->tlscredsid, errp); tlscreds = nbd_get_tls_creds(s->tlscredsid, errp);
@ -357,17 +422,17 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
goto error; goto error;
} }
if (saddr->type != SOCKET_ADDRESS_KIND_INET) { if (s->saddr->type != SOCKET_ADDRESS_KIND_INET) {
error_setg(errp, "TLS only supported over IP sockets"); error_setg(errp, "TLS only supported over IP sockets");
goto error; goto error;
} }
hostname = saddr->u.inet.data->host; hostname = s->saddr->u.inet.data->host;
} }
/* establish TCP connection, return error if it fails /* establish TCP connection, return error if it fails
* TODO: Configurable retry-until-timeout behaviour. * TODO: Configurable retry-until-timeout behaviour.
*/ */
sioc = nbd_establish_connection(saddr, errp); sioc = nbd_establish_connection(s->saddr, errp);
if (!sioc) { if (!sioc) {
ret = -ECONNREFUSED; ret = -ECONNREFUSED;
goto error; goto error;
@ -384,13 +449,10 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
object_unref(OBJECT(tlscreds)); object_unref(OBJECT(tlscreds));
} }
if (ret < 0) { if (ret < 0) {
g_free(s->path); qapi_free_SocketAddress(s->saddr);
g_free(s->host);
g_free(s->port);
g_free(s->export); g_free(s->export);
g_free(s->tlscredsid); g_free(s->tlscredsid);
} }
qapi_free_SocketAddress(saddr);
qemu_opts_del(opts); qemu_opts_del(opts);
return ret; return ret;
} }
@ -412,9 +474,7 @@ static void nbd_close(BlockDriverState *bs)
nbd_client_close(bs); nbd_client_close(bs);
g_free(s->path); qapi_free_SocketAddress(s->saddr);
g_free(s->host);
g_free(s->port);
g_free(s->export); g_free(s->export);
g_free(s->tlscredsid); g_free(s->tlscredsid);
} }
@ -441,45 +501,51 @@ static void nbd_refresh_filename(BlockDriverState *bs, QDict *options)
{ {
BDRVNBDState *s = bs->opaque; BDRVNBDState *s = bs->opaque;
QDict *opts = qdict_new(); QDict *opts = qdict_new();
QObject *saddr_qdict;
Visitor *ov;
const char *host = NULL, *port = NULL, *path = NULL;
qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("nbd"))); if (s->saddr->type == SOCKET_ADDRESS_KIND_INET) {
const InetSocketAddress *inet = s->saddr->u.inet.data;
if (s->path && s->export) { if (!inet->has_ipv4 && !inet->has_ipv6 && !inet->has_to) {
snprintf(bs->exact_filename, sizeof(bs->exact_filename), host = inet->host;
"nbd+unix:///%s?socket=%s", s->export, s->path); port = inet->port;
} else if (s->path && !s->export) { }
snprintf(bs->exact_filename, sizeof(bs->exact_filename), } else if (s->saddr->type == SOCKET_ADDRESS_KIND_UNIX) {
"nbd+unix://?socket=%s", s->path); path = s->saddr->u.q_unix.data->path;
} else if (!s->path && s->export && s->port) {
snprintf(bs->exact_filename, sizeof(bs->exact_filename),
"nbd://%s:%s/%s", s->host, s->port, s->export);
} else if (!s->path && s->export && !s->port) {
snprintf(bs->exact_filename, sizeof(bs->exact_filename),
"nbd://%s/%s", s->host, s->export);
} else if (!s->path && !s->export && s->port) {
snprintf(bs->exact_filename, sizeof(bs->exact_filename),
"nbd://%s:%s", s->host, s->port);
} else if (!s->path && !s->export && !s->port) {
snprintf(bs->exact_filename, sizeof(bs->exact_filename),
"nbd://%s", s->host);
} }
if (s->path) { qdict_put(opts, "driver", qstring_from_str("nbd"));
qdict_put_obj(opts, "path", QOBJECT(qstring_from_str(s->path)));
} else if (s->port) { if (path && s->export) {
qdict_put_obj(opts, "host", QOBJECT(qstring_from_str(s->host))); snprintf(bs->exact_filename, sizeof(bs->exact_filename),
qdict_put_obj(opts, "port", QOBJECT(qstring_from_str(s->port))); "nbd+unix:///%s?socket=%s", s->export, path);
} else { } else if (path && !s->export) {
qdict_put_obj(opts, "host", QOBJECT(qstring_from_str(s->host))); snprintf(bs->exact_filename, sizeof(bs->exact_filename),
"nbd+unix://?socket=%s", path);
} else if (host && s->export) {
snprintf(bs->exact_filename, sizeof(bs->exact_filename),
"nbd://%s:%s/%s", host, port, s->export);
} else if (host && !s->export) {
snprintf(bs->exact_filename, sizeof(bs->exact_filename),
"nbd://%s:%s", host, port);
} }
ov = qobject_output_visitor_new(&saddr_qdict);
visit_type_SocketAddress(ov, NULL, &s->saddr, &error_abort);
visit_complete(ov, &saddr_qdict);
assert(qobject_type(saddr_qdict) == QTYPE_QDICT);
qdict_put_obj(opts, "server", saddr_qdict);
if (s->export) { if (s->export) {
qdict_put_obj(opts, "export", QOBJECT(qstring_from_str(s->export))); qdict_put(opts, "export", qstring_from_str(s->export));
} }
if (s->tlscredsid) { if (s->tlscredsid) {
qdict_put_obj(opts, "tls-creds", qdict_put(opts, "tls-creds", qstring_from_str(s->tlscredsid));
QOBJECT(qstring_from_str(s->tlscredsid)));
} }
qdict_flatten(opts);
bs->full_open_options = opts; bs->full_open_options = opts;
} }

View File

@ -2069,13 +2069,23 @@ static bool hdev_is_sg(BlockDriverState *bs)
#if defined(__linux__) #if defined(__linux__)
BDRVRawState *s = bs->opaque;
struct stat st; struct stat st;
struct sg_scsi_id scsiid; struct sg_scsi_id scsiid;
int sg_version; int sg_version;
int ret;
if (stat(bs->filename, &st) >= 0 && S_ISCHR(st.st_mode) && if (stat(bs->filename, &st) < 0 || !S_ISCHR(st.st_mode)) {
!bdrv_ioctl(bs, SG_GET_VERSION_NUM, &sg_version) && return false;
!bdrv_ioctl(bs, SG_GET_SCSI_ID, &scsiid)) { }
ret = ioctl(s->fd, SG_GET_VERSION_NUM, &sg_version);
if (ret < 0) {
return false;
}
ret = ioctl(s->fd, SG_GET_SCSI_ID, &scsiid);
if (ret >= 0) {
DPRINTF("SG device found: type=%d, version=%d\n", DPRINTF("SG device found: type=%d, version=%d\n",
scsiid.scsi_type, sg_version); scsiid.scsi_type, sg_version);
return true; return true;

View File

@ -176,12 +176,9 @@ static void raw_lock_medium(BlockDriverState *bs, bool locked)
bdrv_lock_medium(bs->file->bs, locked); bdrv_lock_medium(bs->file->bs, locked);
} }
static BlockAIOCB *raw_aio_ioctl(BlockDriverState *bs, static int raw_co_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
unsigned long int req, void *buf,
BlockCompletionFunc *cb,
void *opaque)
{ {
return bdrv_aio_ioctl(bs->file->bs, req, buf, cb, opaque); return bdrv_co_ioctl(bs->file->bs, req, buf);
} }
static int raw_has_zero_init(BlockDriverState *bs) static int raw_has_zero_init(BlockDriverState *bs)
@ -261,7 +258,7 @@ BlockDriver bdrv_raw = {
.bdrv_media_changed = &raw_media_changed, .bdrv_media_changed = &raw_media_changed,
.bdrv_eject = &raw_eject, .bdrv_eject = &raw_eject,
.bdrv_lock_medium = &raw_lock_medium, .bdrv_lock_medium = &raw_lock_medium,
.bdrv_aio_ioctl = &raw_aio_ioctl, .bdrv_co_ioctl = &raw_co_ioctl,
.create_opts = &raw_create_opts, .create_opts = &raw_create_opts,
.bdrv_has_zero_init = &raw_has_zero_init .bdrv_has_zero_init = &raw_has_zero_init
}; };

View File

@ -9,7 +9,6 @@ blk_co_preadv(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags
blk_co_pwritev(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags %x" blk_co_pwritev(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags %x"
# block/io.c # block/io.c
bdrv_aio_pdiscard(void *bs, int64_t offset, int count, void *opaque) "bs %p offset %"PRId64" count %d opaque %p"
bdrv_aio_flush(void *bs, void *opaque) "bs %p opaque %p" bdrv_aio_flush(void *bs, void *opaque) "bs %p opaque %p"
bdrv_aio_readv(void *bs, int64_t sector_num, int nb_sectors, void *opaque) "bs %p sector_num %"PRId64" nb_sectors %d opaque %p" bdrv_aio_readv(void *bs, int64_t sector_num, int nb_sectors, void *opaque) "bs %p sector_num %"PRId64" nb_sectors %d opaque %p"
bdrv_aio_writev(void *bs, int64_t sector_num, int nb_sectors, void *opaque) "bs %p sector_num %"PRId64" nb_sectors %d opaque %p" bdrv_aio_writev(void *bs, int64_t sector_num, int nb_sectors, void *opaque) "bs %p sector_num %"PRId64" nb_sectors %d opaque %p"

View File

@ -314,17 +314,11 @@ BlockAIOCB *bdrv_aio_writev(BdrvChild *child, int64_t sector_num,
BlockCompletionFunc *cb, void *opaque); BlockCompletionFunc *cb, void *opaque);
BlockAIOCB *bdrv_aio_flush(BlockDriverState *bs, BlockAIOCB *bdrv_aio_flush(BlockDriverState *bs,
BlockCompletionFunc *cb, void *opaque); BlockCompletionFunc *cb, void *opaque);
BlockAIOCB *bdrv_aio_pdiscard(BlockDriverState *bs,
int64_t offset, int count,
BlockCompletionFunc *cb, void *opaque);
void bdrv_aio_cancel(BlockAIOCB *acb); void bdrv_aio_cancel(BlockAIOCB *acb);
void bdrv_aio_cancel_async(BlockAIOCB *acb); void bdrv_aio_cancel_async(BlockAIOCB *acb);
/* sg packet commands */ /* sg packet commands */
int bdrv_ioctl(BlockDriverState *bs, unsigned long int req, void *buf); int bdrv_co_ioctl(BlockDriverState *bs, int req, void *buf);
BlockAIOCB *bdrv_aio_ioctl(BlockDriverState *bs,
unsigned long int req, void *buf,
BlockCompletionFunc *cb, void *opaque);
/* Invalidate any cached metadata used by image formats */ /* Invalidate any cached metadata used by image formats */
void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp); void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp);

View File

@ -244,6 +244,8 @@ struct BlockDriver {
BlockAIOCB *(*bdrv_aio_ioctl)(BlockDriverState *bs, BlockAIOCB *(*bdrv_aio_ioctl)(BlockDriverState *bs,
unsigned long int req, void *buf, unsigned long int req, void *buf,
BlockCompletionFunc *cb, void *opaque); BlockCompletionFunc *cb, void *opaque);
int coroutine_fn (*bdrv_co_ioctl)(BlockDriverState *bs,
unsigned long int req, void *buf);
/* List of options for creating images, terminated by name == NULL */ /* List of options for creating images, terminated by name == NULL */
QemuOptsList *create_opts; QemuOptsList *create_opts;

View File

@ -146,6 +146,7 @@ BlockAIOCB *blk_aio_pdiscard(BlockBackend *blk, int64_t offset, int count,
BlockCompletionFunc *cb, void *opaque); BlockCompletionFunc *cb, void *opaque);
void blk_aio_cancel(BlockAIOCB *acb); void blk_aio_cancel(BlockAIOCB *acb);
void blk_aio_cancel_async(BlockAIOCB *acb); void blk_aio_cancel_async(BlockAIOCB *acb);
int blk_co_ioctl(BlockBackend *blk, unsigned long int req, void *buf);
int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf); int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf);
BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf, BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf,
BlockCompletionFunc *cb, void *opaque); BlockCompletionFunc *cb, void *opaque);

View File

@ -1703,14 +1703,15 @@
# #
# @host_device, @host_cdrom: Since 2.1 # @host_device, @host_cdrom: Since 2.1
# @gluster: Since 2.7 # @gluster: Since 2.7
# @nbd: Since 2.8
# #
# Since: 2.0 # Since: 2.0
## ##
{ 'enum': 'BlockdevDriver', { 'enum': 'BlockdevDriver',
'data': [ 'archipelago', 'blkdebug', 'blkverify', 'bochs', 'cloop', 'data': [ 'archipelago', 'blkdebug', 'blkverify', 'bochs', 'cloop',
'dmg', 'file', 'ftp', 'ftps', 'gluster', 'host_cdrom', 'dmg', 'file', 'ftp', 'ftps', 'gluster', 'host_cdrom',
'host_device', 'http', 'https', 'luks', 'null-aio', 'null-co', 'host_device', 'http', 'https', 'luks', 'nbd', 'null-aio',
'parallels', 'qcow', 'qcow2', 'qed', 'quorum', 'raw', 'null-co', 'parallels', 'qcow', 'qcow2', 'qed', 'quorum', 'raw',
'replication', 'tftp', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat' ] } 'replication', 'tftp', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat' ] }
## ##
@ -2219,6 +2220,24 @@
{ 'struct': 'BlockdevOptionsCurl', { 'struct': 'BlockdevOptionsCurl',
'data': { 'filename': 'str' } } 'data': { 'filename': 'str' } }
##
# @BlockdevOptionsNbd
#
# Driver specific block device options for NBD.
#
# @server: NBD server address
#
# @export: #optional export name
#
# @tls-creds: #optional TLS credentials ID
#
# Since: 2.8
##
{ 'struct': 'BlockdevOptionsNbd',
'data': { 'server': 'SocketAddress',
'*export': 'str',
'*tls-creds': 'str' } }
## ##
# @BlockdevOptions # @BlockdevOptions
# #
@ -2264,7 +2283,7 @@
'https': 'BlockdevOptionsCurl', 'https': 'BlockdevOptionsCurl',
# TODO iscsi: Wait for structured options # TODO iscsi: Wait for structured options
'luks': 'BlockdevOptionsLUKS', 'luks': 'BlockdevOptionsLUKS',
# TODO nbd: Should take InetSocketAddress for 'host'? 'nbd': 'BlockdevOptionsNbd',
# TODO nfs: Wait for structured options # TODO nfs: Wait for structured options
'null-aio': 'BlockdevOptionsNull', 'null-aio': 'BlockdevOptionsNull',
'null-co': 'BlockdevOptionsNull', 'null-co': 'BlockdevOptionsNull',

View File

@ -222,7 +222,7 @@ Testing: -drive driver=file
QEMU_PROG: -drive driver=file: The 'file' block driver requires a file name QEMU_PROG: -drive driver=file: The 'file' block driver requires a file name
Testing: -drive driver=nbd Testing: -drive driver=nbd
QEMU_PROG: -drive driver=nbd: one of path and host must be specified. QEMU_PROG: -drive driver=nbd: NBD server address missing
Testing: -drive driver=raw Testing: -drive driver=raw
QEMU_PROG: -drive driver=raw: Can't use 'raw' as a block driver for the protocol level QEMU_PROG: -drive driver=raw: Can't use 'raw' as a block driver for the protocol level
@ -231,7 +231,7 @@ Testing: -drive file.driver=file
QEMU_PROG: -drive file.driver=file: The 'file' block driver requires a file name QEMU_PROG: -drive file.driver=file: The 'file' block driver requires a file name
Testing: -drive file.driver=nbd Testing: -drive file.driver=nbd
QEMU_PROG: -drive file.driver=nbd: one of path and host must be specified. QEMU_PROG: -drive file.driver=nbd: NBD server address missing
Testing: -drive file.driver=raw Testing: -drive file.driver=raw
QEMU_PROG: -drive file.driver=raw: Can't use 'raw' as a block driver for the protocol level QEMU_PROG: -drive file.driver=raw: Can't use 'raw' as a block driver for the protocol level

View File

@ -316,7 +316,7 @@ Testing: -drive driver=file
QEMU_PROG: -drive driver=file: The 'file' block driver requires a file name QEMU_PROG: -drive driver=file: The 'file' block driver requires a file name
Testing: -drive driver=nbd Testing: -drive driver=nbd
QEMU_PROG: -drive driver=nbd: one of path and host must be specified. QEMU_PROG: -drive driver=nbd: NBD server address missing
Testing: -drive driver=raw Testing: -drive driver=raw
QEMU_PROG: -drive driver=raw: Can't use 'raw' as a block driver for the protocol level QEMU_PROG: -drive driver=raw: Can't use 'raw' as a block driver for the protocol level
@ -325,7 +325,7 @@ Testing: -drive file.driver=file
QEMU_PROG: -drive file.driver=file: The 'file' block driver requires a file name QEMU_PROG: -drive file.driver=file: The 'file' block driver requires a file name
Testing: -drive file.driver=nbd Testing: -drive file.driver=nbd
QEMU_PROG: -drive file.driver=nbd: one of path and host must be specified. QEMU_PROG: -drive file.driver=nbd: NBD server address missing
Testing: -drive file.driver=raw Testing: -drive file.driver=raw
QEMU_PROG: -drive file.driver=raw: Can't use 'raw' as a block driver for the protocol level QEMU_PROG: -drive file.driver=raw: Can't use 'raw' as a block driver for the protocol level

195
tests/qemu-iotests/147 Executable file
View File

@ -0,0 +1,195 @@
#!/usr/bin/env python
#
# Test case for NBD's blockdev-add interface
#
# Copyright (C) 2016 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import os
import socket
import stat
import time
import iotests
from iotests import cachemode, imgfmt, qemu_img, qemu_nbd
NBD_PORT = 10811
test_img = os.path.join(iotests.test_dir, 'test.img')
unix_socket = os.path.join(iotests.test_dir, 'nbd.socket')
class NBDBlockdevAddBase(iotests.QMPTestCase):
def blockdev_add_options(self, address, export=None):
options = { 'node-name': 'nbd-blockdev',
'driver': 'raw',
'file': {
'driver': 'nbd',
'server': address
} }
if export is not None:
options['file']['export'] = export
return options
def client_test(self, filename, address, export=None):
bao = self.blockdev_add_options(address, export)
result = self.vm.qmp('blockdev-add', **bao)
self.assert_qmp(result, 'return', {})
result = self.vm.qmp('query-named-block-nodes')
for node in result['return']:
if node['node-name'] == 'nbd-blockdev':
if isinstance(filename, str):
self.assert_qmp(node, 'image/filename', filename)
else:
self.assert_json_filename_equal(node['image']['filename'],
filename)
break
result = self.vm.qmp('x-blockdev-del', node_name='nbd-blockdev')
self.assert_qmp(result, 'return', {})
class QemuNBD(NBDBlockdevAddBase):
def setUp(self):
qemu_img('create', '-f', iotests.imgfmt, test_img, '64k')
self.vm = iotests.VM()
self.vm.launch()
def tearDown(self):
self.vm.shutdown()
os.remove(test_img)
try:
os.remove(unix_socket)
except OSError:
pass
def _server_up(self, *args):
self.assertEqual(qemu_nbd('-f', imgfmt, test_img, *args), 0)
def test_inet(self):
self._server_up('-p', str(NBD_PORT))
address = { 'type': 'inet',
'data': {
'host': 'localhost',
'port': str(NBD_PORT)
} }
self.client_test('nbd://localhost:%i' % NBD_PORT, address)
def test_unix(self):
self._server_up('-k', unix_socket)
address = { 'type': 'unix',
'data': { 'path': unix_socket } }
self.client_test('nbd+unix://?socket=' + unix_socket, address)
class BuiltinNBD(NBDBlockdevAddBase):
def setUp(self):
qemu_img('create', '-f', iotests.imgfmt, test_img, '64k')
self.vm = iotests.VM()
self.vm.launch()
self.server = iotests.VM('.server')
self.server.add_drive_raw('if=none,id=nbd-export,' +
'file=%s,' % test_img +
'format=%s,' % imgfmt +
'cache=%s' % cachemode)
self.server.launch()
def tearDown(self):
self.vm.shutdown()
self.server.shutdown()
os.remove(test_img)
try:
os.remove(unix_socket)
except OSError:
pass
def _server_up(self, address):
result = self.server.qmp('nbd-server-start', addr=address)
self.assert_qmp(result, 'return', {})
result = self.server.qmp('nbd-server-add', device='nbd-export')
self.assert_qmp(result, 'return', {})
def _server_down(self):
result = self.server.qmp('nbd-server-stop')
self.assert_qmp(result, 'return', {})
def test_inet(self):
address = { 'type': 'inet',
'data': {
'host': 'localhost',
'port': str(NBD_PORT)
} }
self._server_up(address)
self.client_test('nbd://localhost:%i/nbd-export' % NBD_PORT,
address, 'nbd-export')
self._server_down()
def test_inet6(self):
address = { 'type': 'inet',
'data': {
'host': '::1',
'port': str(NBD_PORT),
'ipv4': False,
'ipv6': True
} }
filename = { 'driver': 'raw',
'file': {
'driver': 'nbd',
'export': 'nbd-export',
'server': address
} }
self._server_up(address)
self.client_test(filename, address, 'nbd-export')
self._server_down()
def test_unix(self):
address = { 'type': 'unix',
'data': { 'path': unix_socket } }
self._server_up(address)
self.client_test('nbd+unix:///nbd-export?socket=' + unix_socket,
address, 'nbd-export')
self._server_down()
def test_fd(self):
self._server_up({ 'type': 'unix',
'data': { 'path': unix_socket } })
sockfd = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sockfd.connect(unix_socket)
result = self.vm.send_fd_scm(str(sockfd.fileno()))
self.assertEqual(result, 0, 'Failed to send socket FD')
result = self.vm.qmp('getfd', fdname='nbd-fifo')
self.assert_qmp(result, 'return', {})
address = { 'type': 'fd',
'data': { 'str': 'nbd-fifo' } }
filename = { 'driver': 'raw',
'file': {
'driver': 'nbd',
'export': 'nbd-export',
'server': address
} }
self.client_test(filename, address, 'nbd-export')
self._server_down()
if __name__ == '__main__':
# Need to support image creation
iotests.main(supported_fmts=['vpc', 'parallels', 'qcow', 'vdi', 'qcow2',
'vmdk', 'raw', 'vhdx', 'qed'])

View File

@ -0,0 +1,5 @@
......
----------------------------------------------------------------------
Ran 6 tests
OK

View File

@ -69,7 +69,7 @@ if [ "$IMGOPTSSYNTAX" = "true" ]; then
TEST_IMG="$DRIVER,file.driver=ssh,file.host=127.0.0.1,file.path=$TEST_IMG_FILE" TEST_IMG="$DRIVER,file.driver=ssh,file.host=127.0.0.1,file.path=$TEST_IMG_FILE"
elif [ "$IMGPROTO" = "nfs" ]; then elif [ "$IMGPROTO" = "nfs" ]; then
TEST_DIR="$DRIVER,file.driver=nfs,file.filename=nfs://127.0.0.1/$TEST_DIR" TEST_DIR="$DRIVER,file.driver=nfs,file.filename=nfs://127.0.0.1/$TEST_DIR"
TEST_IMG=$TEST_DIR_OPTS/t.$IMGFMT TEST_IMG=$TEST_DIR/t.$IMGFMT
elif [ "$IMGPROTO" = "archipelago" ]; then elif [ "$IMGPROTO" = "archipelago" ]; then
TEST_IMG="$DRIVER,file.driver=archipelago,file.volume=:at.$IMGFMT" TEST_IMG="$DRIVER,file.driver=archipelago,file.volume=:at.$IMGFMT"
else else

View File

@ -149,6 +149,7 @@
144 rw auto quick 144 rw auto quick
145 auto quick 145 auto quick
146 auto quick 146 auto quick
147 auto
148 rw auto quick 148 rw auto quick
149 rw auto sudo 149 rw auto sudo
150 rw auto quick 150 rw auto quick

View File

@ -39,6 +39,10 @@ qemu_io_args = [os.environ.get('QEMU_IO_PROG', 'qemu-io')]
if os.environ.get('QEMU_IO_OPTIONS'): if os.environ.get('QEMU_IO_OPTIONS'):
qemu_io_args += os.environ['QEMU_IO_OPTIONS'].strip().split(' ') qemu_io_args += os.environ['QEMU_IO_OPTIONS'].strip().split(' ')
qemu_nbd_args = [os.environ.get('QEMU_NBD_PROG', 'qemu-nbd')]
if os.environ.get('QEMU_NBD_OPTIONS'):
qemu_nbd_args += os.environ['QEMU_NBD_OPTIONS'].strip().split(' ')
qemu_prog = os.environ.get('QEMU_PROG', 'qemu') qemu_prog = os.environ.get('QEMU_PROG', 'qemu')
qemu_opts = os.environ.get('QEMU_OPTIONS', '').strip().split(' ') qemu_opts = os.environ.get('QEMU_OPTIONS', '').strip().split(' ')
@ -87,6 +91,10 @@ def qemu_io(*args):
sys.stderr.write('qemu-io received signal %i: %s\n' % (-exitcode, ' '.join(args))) sys.stderr.write('qemu-io received signal %i: %s\n' % (-exitcode, ' '.join(args)))
return subp.communicate()[0] return subp.communicate()[0]
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 compare_images(img1, img2, fmt1=imgfmt, fmt2=imgfmt): def compare_images(img1, img2, fmt1=imgfmt, fmt2=imgfmt):
'''Return True if two image files are identical''' '''Return True if two image files are identical'''
return qemu_img('compare', '-f', fmt1, return qemu_img('compare', '-f', fmt1,
@ -132,8 +140,10 @@ def log(msg, filters=[]):
class VM(qtest.QEMUQtestMachine): class VM(qtest.QEMUQtestMachine):
'''A QEMU VM''' '''A QEMU VM'''
def __init__(self): def __init__(self, path_suffix=''):
super(VM, self).__init__(qemu_prog, qemu_opts, test_dir=test_dir, name = "qemu%s-%d" % (path_suffix, os.getpid())
super(VM, self).__init__(qemu_prog, qemu_opts, name=name,
test_dir=test_dir,
socket_scm_helper=socket_scm_helper) socket_scm_helper=socket_scm_helper)
if debug: if debug:
self._debug = True self._debug = True
@ -212,6 +222,19 @@ class QMPTestCase(unittest.TestCase):
self.fail('invalid index "%s" in path "%s" in "%s"' % (idx, path, str(d))) self.fail('invalid index "%s" in path "%s" in "%s"' % (idx, path, str(d)))
return d return d
def flatten_qmp_object(self, obj, output=None, basestr=''):
if output is None:
output = dict()
if isinstance(obj, list):
for i in range(len(obj)):
self.flatten_qmp_object(obj[i], output, basestr + str(i) + '.')
elif isinstance(obj, dict):
for key in obj:
self.flatten_qmp_object(obj[key], output, basestr + key + '.')
else:
output[basestr[:-1]] = obj # Strip trailing '.'
return output
def assert_qmp_absent(self, d, path): def assert_qmp_absent(self, d, path):
try: try:
result = self.dictpath(d, path) result = self.dictpath(d, path)
@ -242,6 +265,13 @@ class QMPTestCase(unittest.TestCase):
self.assertTrue(False, "Cannot find %s %s in result:\n%s" % \ self.assertTrue(False, "Cannot find %s %s in result:\n%s" % \
(node_name, file_name, result)) (node_name, file_name, result))
def assert_json_filename_equal(self, json_filename, reference):
'''Asserts that the given filename is a json: filename and that its
content is equal to the given reference object'''
self.assertEqual(json_filename[:5], 'json:')
self.assertEqual(self.flatten_qmp_object(json.loads(json_filename[5:])),
self.flatten_qmp_object(reference))
def cancel_and_wait(self, drive='drive0', force=False, resume=False): def cancel_and_wait(self, drive='drive0', force=False, resume=False):
'''Cancel a block job and wait for it to finish, returning the event''' '''Cancel a block job and wait for it to finish, returning the event'''
result = self.vm.qmp('block-job-cancel', device=drive, force=force) result = self.vm.qmp('block-job-cancel', device=drive, force=force)

View File

@ -60,7 +60,7 @@ static int send_fd(int fd, int fd_to_send)
} }
/* Convert string to fd number. */ /* Convert string to fd number. */
static int get_fd_num(const char *fd_str) static int get_fd_num(const char *fd_str, bool silent)
{ {
int sock; int sock;
char *err; char *err;
@ -68,12 +68,16 @@ static int get_fd_num(const char *fd_str)
errno = 0; errno = 0;
sock = strtol(fd_str, &err, 10); sock = strtol(fd_str, &err, 10);
if (errno) { if (errno) {
if (!silent) {
fprintf(stderr, "Failed in strtol for socket fd, reason: %s\n", fprintf(stderr, "Failed in strtol for socket fd, reason: %s\n",
strerror(errno)); strerror(errno));
}
return -1; return -1;
} }
if (!*fd_str || *err || sock < 0) { if (!*fd_str || *err || sock < 0) {
if (!silent) {
fprintf(stderr, "bad numerical value for socket fd '%s'\n", fd_str); fprintf(stderr, "bad numerical value for socket fd '%s'\n", fd_str);
}
return -1; return -1;
} }
@ -104,19 +108,22 @@ int main(int argc, char **argv, char **envp)
} }
sock = get_fd_num(argv[1]); sock = get_fd_num(argv[1], false);
if (sock < 0) { if (sock < 0) {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
/* Now only open a file in readonly mode for test purpose. If more precise fd = get_fd_num(argv[2], true);
control is needed, use python script in file operation, which is if (fd < 0) {
supposed to fork and exec this program. */ /* Now only open a file in readonly mode for test purpose. If more
precise control is needed, use python script in file operation, which
is supposed to fork and exec this program. */
fd = open(argv[2], O_RDONLY); fd = open(argv[2], O_RDONLY);
if (fd < 0) { if (fd < 0) {
fprintf(stderr, "Failed to open file '%s'\n", argv[2]); fprintf(stderr, "Failed to open file '%s'\n", argv[2]);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
}
ret = send_fd(sock, fd); ret = send_fd(sock, fd);
if (ret < 0) { if (ret < 0) {