提交 de148eb7 编写于 作者: A Anthony Liguori

Merge remote-tracking branch 'bonzini/nbd-next' into staging

* bonzini/nbd-next:
  nbd: fixes to read-only handling
  hmp: add NBD server commands
  nbd: disallow nbd-server-add before nbd-server-start
  nbd: force read-only export for read-only devices
  nbd: fix nbd_server_stop crash when no server was running
  nbd: accept URIs
  nbd: accept relative path to Unix socket
  qemu-nbd: initialize main loop before block layer
Signed-off-by: NAnthony Liguori <aliguori@us.ibm.com>
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "qemu-common.h" #include "qemu-common.h"
#include "nbd.h" #include "nbd.h"
#include "uri.h"
#include "block_int.h" #include "block_int.h"
#include "module.h" #include "module.h"
#include "qemu_socket.h" #include "qemu_socket.h"
...@@ -55,7 +56,6 @@ typedef struct BDRVNBDState { ...@@ -55,7 +56,6 @@ typedef struct BDRVNBDState {
uint32_t nbdflags; uint32_t nbdflags;
off_t size; off_t size;
size_t blocksize; size_t blocksize;
char *export_name; /* An NBD server may export several devices */
CoMutex send_mutex; CoMutex send_mutex;
CoMutex free_sema; CoMutex free_sema;
...@@ -65,13 +65,75 @@ typedef struct BDRVNBDState { ...@@ -65,13 +65,75 @@ typedef struct BDRVNBDState {
Coroutine *recv_coroutine[MAX_NBD_REQUESTS]; Coroutine *recv_coroutine[MAX_NBD_REQUESTS];
struct nbd_reply reply; struct nbd_reply reply;
/* If it begins with '/', this is a UNIX domain socket. Otherwise, int is_unix;
* it's a string of the form <hostname|ip4|\[ip6\]>:port
*/
char *host_spec; char *host_spec;
char *export_name; /* An NBD server may export several devices */
} BDRVNBDState; } BDRVNBDState;
static int nbd_config(BDRVNBDState *s, const char *filename, int flags) static int nbd_parse_uri(BDRVNBDState *s, const char *filename)
{
URI *uri;
const char *p;
QueryParams *qp = NULL;
int ret = 0;
uri = uri_parse(filename);
if (!uri) {
return -EINVAL;
}
/* transport */
if (!strcmp(uri->scheme, "nbd")) {
s->is_unix = false;
} else if (!strcmp(uri->scheme, "nbd+tcp")) {
s->is_unix = false;
} else if (!strcmp(uri->scheme, "nbd+unix")) {
s->is_unix = true;
} else {
ret = -EINVAL;
goto out;
}
p = uri->path ? uri->path : "/";
p += strspn(p, "/");
if (p[0]) {
s->export_name = g_strdup(p);
}
qp = query_params_parse(uri->query);
if (qp->n > 1 || (s->is_unix && !qp->n) || (!s->is_unix && qp->n)) {
ret = -EINVAL;
goto out;
}
if (s->is_unix) {
/* nbd+unix:///export?socket=path */
if (uri->server || uri->port || strcmp(qp->p[0].name, "socket")) {
ret = -EINVAL;
goto out;
}
s->host_spec = g_strdup(qp->p[0].value);
} else {
/* nbd[+tcp]://host:port/export */
if (!uri->server) {
ret = -EINVAL;
goto out;
}
if (!uri->port) {
uri->port = NBD_DEFAULT_PORT;
}
s->host_spec = g_strdup_printf("%s:%d", uri->server, uri->port);
}
out:
if (qp) {
query_params_free(qp);
}
uri_free(uri);
return ret;
}
static int nbd_config(BDRVNBDState *s, const char *filename)
{ {
char *file; char *file;
char *export_name; char *export_name;
...@@ -79,6 +141,10 @@ static int nbd_config(BDRVNBDState *s, const char *filename, int flags) ...@@ -79,6 +141,10 @@ static int nbd_config(BDRVNBDState *s, const char *filename, int flags)
const char *unixpath; const char *unixpath;
int err = -EINVAL; int err = -EINVAL;
if (strstr(filename, "://")) {
return nbd_parse_uri(s, filename);
}
file = g_strdup(filename); file = g_strdup(filename);
export_name = strstr(file, EN_OPTSTR); export_name = strstr(file, EN_OPTSTR);
...@@ -98,11 +164,10 @@ static int nbd_config(BDRVNBDState *s, const char *filename, int flags) ...@@ -98,11 +164,10 @@ static int nbd_config(BDRVNBDState *s, const char *filename, int flags)
/* 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)) {
if (unixpath[0] != '/') { /* We demand an absolute path*/ s->is_unix = true;
goto out;
}
s->host_spec = g_strdup(unixpath); s->host_spec = g_strdup(unixpath);
} else { } else {
s->is_unix = false;
s->host_spec = g_strdup(host_spec); s->host_spec = g_strdup(host_spec);
} }
...@@ -262,7 +327,7 @@ static int nbd_establish_connection(BlockDriverState *bs) ...@@ -262,7 +327,7 @@ static int nbd_establish_connection(BlockDriverState *bs)
off_t size; off_t size;
size_t blocksize; size_t blocksize;
if (s->host_spec[0] == '/') { if (s->is_unix) {
sock = unix_socket_outgoing(s->host_spec); sock = unix_socket_outgoing(s->host_spec);
} else { } else {
sock = tcp_socket_outgoing_spec(s->host_spec); sock = tcp_socket_outgoing_spec(s->host_spec);
...@@ -320,7 +385,7 @@ static int nbd_open(BlockDriverState *bs, const char* filename, int flags) ...@@ -320,7 +385,7 @@ static int nbd_open(BlockDriverState *bs, const char* filename, int flags)
qemu_co_mutex_init(&s->free_sema); qemu_co_mutex_init(&s->free_sema);
/* Pop the config into our state object. Exit if invalid. */ /* Pop the config into our state object. Exit if invalid. */
result = nbd_config(s, filename, flags); result = nbd_config(s, filename);
if (result != 0) { if (result != 0) {
return result; return result;
} }
...@@ -498,6 +563,33 @@ static int64_t nbd_getlength(BlockDriverState *bs) ...@@ -498,6 +563,33 @@ static int64_t nbd_getlength(BlockDriverState *bs)
static BlockDriver bdrv_nbd = { static BlockDriver bdrv_nbd = {
.format_name = "nbd", .format_name = "nbd",
.protocol_name = "nbd",
.instance_size = sizeof(BDRVNBDState),
.bdrv_file_open = nbd_open,
.bdrv_co_readv = nbd_co_readv,
.bdrv_co_writev = nbd_co_writev,
.bdrv_close = nbd_close,
.bdrv_co_flush_to_os = nbd_co_flush,
.bdrv_co_discard = nbd_co_discard,
.bdrv_getlength = nbd_getlength,
};
static BlockDriver bdrv_nbd_tcp = {
.format_name = "nbd",
.protocol_name = "nbd+tcp",
.instance_size = sizeof(BDRVNBDState),
.bdrv_file_open = nbd_open,
.bdrv_co_readv = nbd_co_readv,
.bdrv_co_writev = nbd_co_writev,
.bdrv_close = nbd_close,
.bdrv_co_flush_to_os = nbd_co_flush,
.bdrv_co_discard = nbd_co_discard,
.bdrv_getlength = nbd_getlength,
};
static BlockDriver bdrv_nbd_unix = {
.format_name = "nbd",
.protocol_name = "nbd+unix",
.instance_size = sizeof(BDRVNBDState), .instance_size = sizeof(BDRVNBDState),
.bdrv_file_open = nbd_open, .bdrv_file_open = nbd_open,
.bdrv_co_readv = nbd_co_readv, .bdrv_co_readv = nbd_co_readv,
...@@ -506,12 +598,13 @@ static BlockDriver bdrv_nbd = { ...@@ -506,12 +598,13 @@ static BlockDriver bdrv_nbd = {
.bdrv_co_flush_to_os = nbd_co_flush, .bdrv_co_flush_to_os = nbd_co_flush,
.bdrv_co_discard = nbd_co_discard, .bdrv_co_discard = nbd_co_discard,
.bdrv_getlength = nbd_getlength, .bdrv_getlength = nbd_getlength,
.protocol_name = "nbd",
}; };
static void bdrv_nbd_init(void) static void bdrv_nbd_init(void)
{ {
bdrv_register(&bdrv_nbd); bdrv_register(&bdrv_nbd);
bdrv_register(&bdrv_nbd_tcp);
bdrv_register(&bdrv_nbd_unix);
} }
block_init(bdrv_nbd_init); block_init(bdrv_nbd_init);
...@@ -82,6 +82,11 @@ void qmp_nbd_server_add(const char *device, bool has_writable, bool writable, ...@@ -82,6 +82,11 @@ void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,
NBDExport *exp; NBDExport *exp;
NBDCloseNotifier *n; NBDCloseNotifier *n;
if (server_fd == -1) {
error_setg(errp, "NBD server not running");
return;
}
if (nbd_export_find(device)) { if (nbd_export_find(device)) {
error_setg(errp, "NBD server already exporting device '%s'", device); error_setg(errp, "NBD server already exporting device '%s'", device);
return; return;
...@@ -93,6 +98,13 @@ void qmp_nbd_server_add(const char *device, bool has_writable, bool writable, ...@@ -93,6 +98,13 @@ void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,
return; return;
} }
if (!has_writable) {
writable = true;
}
if (bdrv_is_read_only(bs)) {
writable = false;
}
exp = nbd_export_new(bs, 0, -1, writable ? 0 : NBD_FLAG_READ_ONLY, exp = nbd_export_new(bs, 0, -1, writable ? 0 : NBD_FLAG_READ_ONLY,
nbd_server_put_ref); nbd_server_put_ref);
...@@ -113,7 +125,9 @@ void qmp_nbd_server_stop(Error **errp) ...@@ -113,7 +125,9 @@ void qmp_nbd_server_stop(Error **errp)
nbd_close_notifier(&cn->n, nbd_export_get_blockdev(cn->exp)); nbd_close_notifier(&cn->n, nbd_export_get_blockdev(cn->exp));
} }
qemu_set_fd_handler2(server_fd, NULL, NULL, NULL, NULL); if (server_fd != -1) {
close(server_fd); qemu_set_fd_handler2(server_fd, NULL, NULL, NULL, NULL);
server_fd = -1; close(server_fd);
server_fd = -1;
}
} }
...@@ -1310,6 +1310,51 @@ Remove all matches from the access control list, and set the default ...@@ -1310,6 +1310,51 @@ Remove all matches from the access control list, and set the default
policy back to @code{deny}. policy back to @code{deny}.
ETEXI ETEXI
{
.name = "nbd_server_start",
.args_type = "all:-a,writable:-w,uri:s",
.params = "nbd_server_start [-a] [-w] host:port",
.help = "serve block devices on the given host and port",
.mhandler.cmd = hmp_nbd_server_start,
},
STEXI
@item nbd_server_start @var{host}:@var{port}
@findex nbd_server_start
Start an NBD server on the given host and/or port. If the @option{-a}
option is included, all of the virtual machine's block devices that
have an inserted media on them are automatically exported; in this case,
the @option{-w} option makes the devices writable too.
ETEXI
{
.name = "nbd_server_add",
.args_type = "writable:-w,device:B",
.params = "nbd_server_add [-w] device",
.help = "export a block device via NBD",
.mhandler.cmd = hmp_nbd_server_add,
},
STEXI
@item nbd_server_add @var{device}
@findex nbd_server_add
Export a block device through QEMU's NBD server, which must be started
beforehand with @command{nbd_server_start}. The @option{-w} option makes the
exported device writable too.
ETEXI
{
.name = "nbd_server_stop",
.args_type = "",
.params = "nbd_server_stop",
.help = "stop serving block devices using the NBD protocol",
.mhandler.cmd = hmp_nbd_server_stop,
},
STEXI
@item nbd_server_stop
@findex nbd_server_stop
Stop the QEMU embedded NBD server.
ETEXI
#if defined(TARGET_I386) #if defined(TARGET_I386)
{ {
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "qemu-option.h" #include "qemu-option.h"
#include "qemu-timer.h" #include "qemu-timer.h"
#include "qmp-commands.h" #include "qmp-commands.h"
#include "qemu_socket.h"
#include "monitor.h" #include "monitor.h"
#include "console.h" #include "console.h"
...@@ -1259,3 +1260,78 @@ void hmp_screen_dump(Monitor *mon, const QDict *qdict) ...@@ -1259,3 +1260,78 @@ void hmp_screen_dump(Monitor *mon, const QDict *qdict)
qmp_screendump(filename, &err); qmp_screendump(filename, &err);
hmp_handle_error(mon, &err); hmp_handle_error(mon, &err);
} }
void hmp_nbd_server_start(Monitor *mon, const QDict *qdict)
{
const char *uri = qdict_get_str(qdict, "uri");
int writable = qdict_get_try_bool(qdict, "writable", 0);
int all = qdict_get_try_bool(qdict, "all", 0);
Error *local_err = NULL;
BlockInfoList *block_list, *info;
SocketAddress *addr;
if (writable && !all) {
error_setg(&local_err, "-w only valid together with -a");
goto exit;
}
/* First check if the address is valid and start the server. */
addr = socket_parse(uri, &local_err);
if (local_err != NULL) {
goto exit;
}
qmp_nbd_server_start(addr, &local_err);
qapi_free_SocketAddress(addr);
if (local_err != NULL) {
goto exit;
}
if (!all) {
return;
}
/* Then try adding all block devices. If one fails, close all and
* exit.
*/
block_list = qmp_query_block(NULL);
for (info = block_list; info; info = info->next) {
if (!info->value->has_inserted) {
continue;
}
qmp_nbd_server_add(info->value->device, true, writable, &local_err);
if (local_err != NULL) {
qmp_nbd_server_stop(NULL);
break;
}
}
qapi_free_BlockInfoList(block_list);
exit:
hmp_handle_error(mon, &local_err);
}
void hmp_nbd_server_add(Monitor *mon, const QDict *qdict)
{
const char *device = qdict_get_str(qdict, "device");
int writable = qdict_get_try_bool(qdict, "writable", 0);
Error *local_err = NULL;
qmp_nbd_server_add(device, true, writable, &local_err);
if (local_err != NULL) {
hmp_handle_error(mon, &local_err);
}
}
void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict)
{
Error *errp = NULL;
qmp_nbd_server_stop(&errp);
hmp_handle_error(mon, &errp);
}
...@@ -77,5 +77,8 @@ void hmp_getfd(Monitor *mon, const QDict *qdict); ...@@ -77,5 +77,8 @@ void hmp_getfd(Monitor *mon, const QDict *qdict);
void hmp_closefd(Monitor *mon, const QDict *qdict); void hmp_closefd(Monitor *mon, const QDict *qdict);
void hmp_send_key(Monitor *mon, const QDict *qdict); void hmp_send_key(Monitor *mon, const QDict *qdict);
void hmp_screen_dump(Monitor *mon, const QDict *qdict); void hmp_screen_dump(Monitor *mon, const QDict *qdict);
void hmp_nbd_server_start(Monitor *mon, const QDict *qdict);
void hmp_nbd_server_add(Monitor *mon, const QDict *qdict);
void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict);
#endif #endif
...@@ -596,24 +596,23 @@ int nbd_init(int fd, int csock, uint32_t flags, off_t size, size_t blocksize) ...@@ -596,24 +596,23 @@ int nbd_init(int fd, int csock, uint32_t flags, off_t size, size_t blocksize)
return -serrno; return -serrno;
} }
if (flags & NBD_FLAG_READ_ONLY) { if (ioctl(fd, NBD_SET_FLAGS, flags) < 0) {
int read_only = 1; if (errno == ENOTTY) {
TRACE("Setting readonly attribute"); int read_only = (flags & NBD_FLAG_READ_ONLY) != 0;
TRACE("Setting readonly attribute");
if (ioctl(fd, BLKROSET, (unsigned long) &read_only) < 0) {
if (ioctl(fd, BLKROSET, (unsigned long) &read_only) < 0) {
int serrno = errno;
LOG("Failed setting read-only attribute");
return -serrno;
}
} else {
int serrno = errno; int serrno = errno;
LOG("Failed setting read-only attribute"); LOG("Failed setting flags");
return -serrno; return -serrno;
} }
} }
if (ioctl(fd, NBD_SET_FLAGS, flags) < 0
&& errno != ENOTTY) {
int serrno = errno;
LOG("Failed setting flags");
return -serrno;
}
TRACE("Negotiation ended"); TRACE("Negotiation ended");
return 0; return 0;
......
...@@ -610,14 +610,14 @@ QEMU can access directly to block device exported using the Network Block Device ...@@ -610,14 +610,14 @@ QEMU can access directly to block device exported using the Network Block Device
protocol. protocol.
@example @example
qemu-system-i386 linux.img -hdb nbd:my_nbd_server.mydomain.org:1024 qemu-system-i386 linux.img -hdb nbd://my_nbd_server.mydomain.org:1024/
@end example @end example
If the NBD server is located on the same host, you can use an unix socket instead If the NBD server is located on the same host, you can use an unix socket instead
of an inet socket: of an inet socket:
@example @example
qemu-system-i386 linux.img -hdb nbd:unix:/tmp/my_socket qemu-system-i386 linux.img -hdb nbd+unix://?socket=/tmp/my_socket
@end example @end example
In this case, the block device must be exported using qemu-nbd: In this case, the block device must be exported using qemu-nbd:
...@@ -631,17 +631,26 @@ The use of qemu-nbd allows to share a disk between several guests: ...@@ -631,17 +631,26 @@ The use of qemu-nbd allows to share a disk between several guests:
qemu-nbd --socket=/tmp/my_socket --share=2 my_disk.qcow2 qemu-nbd --socket=/tmp/my_socket --share=2 my_disk.qcow2
@end example @end example
@noindent
and then you can use it with two guests: and then you can use it with two guests:
@example @example
qemu-system-i386 linux1.img -hdb nbd:unix:/tmp/my_socket qemu-system-i386 linux1.img -hdb nbd+unix://?socket=/tmp/my_socket
qemu-system-i386 linux2.img -hdb nbd:unix:/tmp/my_socket qemu-system-i386 linux2.img -hdb nbd+unix://?socket=/tmp/my_socket
@end example @end example
If the nbd-server uses named exports (since NBD 2.9.18), you must use the If the nbd-server uses named exports (supported since NBD 2.9.18, or with QEMU's
"exportname" option: own embedded NBD server), you must specify an export name in the URI:
@example @example
qemu-system-i386 -cdrom nbd:localhost:exportname=debian-500-ppc-netinst qemu-system-i386 -cdrom nbd://localhost/debian-500-ppc-netinst
qemu-system-i386 -cdrom nbd:localhost:exportname=openSUSE-11.1-ppc-netinst qemu-system-i386 -cdrom nbd://localhost/openSUSE-11.1-ppc-netinst
@end example
The URI syntax for NBD is supported since QEMU 1.3. An alternative syntax is
also available. Here are some example of the older syntax:
@example
qemu-system-i386 linux.img -hdb nbd:my_nbd_server.mydomain.org:1024
qemu-system-i386 linux2.img -hdb nbd:unix:/tmp/my_socket
qemu-system-i386 -cdrom nbd:localhost:10809:exportname=debian-500-ppc-netinst
@end example @end example
@node disk_images_sheepdog @node disk_images_sheepdog
......
...@@ -539,6 +539,7 @@ int main(int argc, char **argv) ...@@ -539,6 +539,7 @@ int main(int argc, char **argv)
snprintf(sockpath, 128, SOCKET_PATH, basename(device)); snprintf(sockpath, 128, SOCKET_PATH, basename(device));
} }
qemu_init_main_loop();
bdrv_init(); bdrv_init();
atexit(bdrv_close_all); atexit(bdrv_close_all);
...@@ -584,7 +585,6 @@ int main(int argc, char **argv) ...@@ -584,7 +585,6 @@ int main(int argc, char **argv)
memset(&client_thread, 0, sizeof(client_thread)); memset(&client_thread, 0, sizeof(client_thread));
} }
qemu_init_main_loop();
qemu_set_fd_handler2(fd, nbd_can_accept, nbd_accept, NULL, qemu_set_fd_handler2(fd, nbd_can_accept, nbd_accept, NULL,
(void *)(uintptr_t)fd); (void *)(uintptr_t)fd);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册