diff options
-rw-r--r-- | block/nbd.c | 11 | ||||
-rw-r--r-- | blockdev-nbd.c | 38 | ||||
-rw-r--r-- | hmp-commands.hx | 26 | ||||
-rw-r--r-- | hmp.c | 20 | ||||
-rw-r--r-- | hmp.h | 1 | ||||
-rw-r--r-- | hw/input/trace-events | 1 | ||||
-rw-r--r-- | hw/pci-host/grackle.c | 13 | ||||
-rw-r--r-- | hw/pci-host/trace-events | 9 | ||||
-rw-r--r-- | hw/pci-host/uninorth.c | 24 | ||||
-rw-r--r-- | hw/ppc/ppc405_boards.c | 14 | ||||
-rw-r--r-- | hw/ppc/ppc440_bamboo.c | 9 | ||||
-rw-r--r-- | hw/ppc/spapr.c | 6 | ||||
-rw-r--r-- | hw/ppc/spapr_caps.c | 124 | ||||
-rw-r--r-- | hw/ppc/spapr_hcall.c | 58 | ||||
-rw-r--r-- | hw/ppc/spapr_pci.c | 61 | ||||
-rw-r--r-- | hw/ppc/virtex_ml507.c | 8 | ||||
-rw-r--r-- | include/block/nbd.h | 1 | ||||
-rw-r--r-- | include/hw/ppc/pnv_xscom.h | 2 | ||||
-rw-r--r-- | include/hw/ppc/spapr.h | 28 | ||||
-rw-r--r-- | nbd/server.c | 13 | ||||
-rw-r--r-- | qapi/block.json | 50 | ||||
-rw-r--r-- | qemu-doc.texi | 6 | ||||
-rw-r--r-- | target/ppc/kvm.c | 58 | ||||
-rw-r--r-- | target/ppc/kvm_ppc.h | 18 | ||||
-rwxr-xr-x | tests/qemu-iotests/147 | 68 | ||||
-rw-r--r-- | tests/qemu-iotests/147.out | 4 | ||||
-rw-r--r-- | tests/qemu-iotests/205 | 156 | ||||
-rw-r--r-- | tests/qemu-iotests/205.out | 5 | ||||
-rw-r--r-- | tests/qemu-iotests/group | 1 | ||||
-rw-r--r-- | tests/qemu-iotests/iotests.py | 38 |
30 files changed, 788 insertions, 83 deletions
diff --git a/block/nbd.c b/block/nbd.c index 8b8ba56cdd..94220f6d14 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -566,6 +566,14 @@ static void nbd_refresh_filename(BlockDriverState *bs, QDict *options) bs->full_open_options = opts; } +static int nbd_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +{ + if (bs->supported_zero_flags & BDRV_REQ_MAY_UNMAP) { + bdi->can_write_zeroes_with_unmap = true; + } + return 0; +} + static BlockDriver bdrv_nbd = { .format_name = "nbd", .protocol_name = "nbd", @@ -583,6 +591,7 @@ static BlockDriver bdrv_nbd = { .bdrv_detach_aio_context = nbd_detach_aio_context, .bdrv_attach_aio_context = nbd_attach_aio_context, .bdrv_refresh_filename = nbd_refresh_filename, + .bdrv_get_info = nbd_get_info, }; static BlockDriver bdrv_nbd_tcp = { @@ -602,6 +611,7 @@ static BlockDriver bdrv_nbd_tcp = { .bdrv_detach_aio_context = nbd_detach_aio_context, .bdrv_attach_aio_context = nbd_attach_aio_context, .bdrv_refresh_filename = nbd_refresh_filename, + .bdrv_get_info = nbd_get_info, }; static BlockDriver bdrv_nbd_unix = { @@ -621,6 +631,7 @@ static BlockDriver bdrv_nbd_unix = { .bdrv_detach_aio_context = nbd_detach_aio_context, .bdrv_attach_aio_context = nbd_attach_aio_context, .bdrv_refresh_filename = nbd_refresh_filename, + .bdrv_get_info = nbd_get_info, }; static void bdrv_nbd_init(void) diff --git a/blockdev-nbd.c b/blockdev-nbd.c index 9e3c22109c..a9f79c6778 100644 --- a/blockdev-nbd.c +++ b/blockdev-nbd.c @@ -140,8 +140,8 @@ void qmp_nbd_server_start(SocketAddressLegacy *addr, qapi_free_SocketAddress(addr_flat); } -void qmp_nbd_server_add(const char *device, bool has_writable, bool writable, - Error **errp) +void qmp_nbd_server_add(const char *device, bool has_name, const char *name, + bool has_writable, bool writable, Error **errp) { BlockDriverState *bs = NULL; BlockBackend *on_eject_blk; @@ -152,8 +152,12 @@ void qmp_nbd_server_add(const char *device, bool has_writable, bool writable, return; } - if (nbd_export_find(device)) { - error_setg(errp, "NBD server already exporting device '%s'", device); + if (!has_name) { + name = device; + } + + if (nbd_export_find(name)) { + error_setg(errp, "NBD server already has export named '%s'", name); return; } @@ -177,7 +181,7 @@ void qmp_nbd_server_add(const char *device, bool has_writable, bool writable, return; } - nbd_export_set_name(exp, device); + nbd_export_set_name(exp, name); /* The list of named exports has a strong reference to this export now and * our only way of accessing it is through nbd_export_find(), so we can drop @@ -185,6 +189,30 @@ void qmp_nbd_server_add(const char *device, bool has_writable, bool writable, nbd_export_put(exp); } +void qmp_nbd_server_remove(const char *name, + bool has_mode, NbdServerRemoveMode mode, + Error **errp) +{ + NBDExport *exp; + + if (!nbd_server) { + error_setg(errp, "NBD server not running"); + return; + } + + exp = nbd_export_find(name); + if (exp == NULL) { + error_setg(errp, "Export '%s' is not found", name); + return; + } + + if (!has_mode) { + mode = NBD_SERVER_REMOVE_MODE_SAFE; + } + + nbd_export_remove(exp, mode, errp); +} + void qmp_nbd_server_stop(Error **errp) { nbd_export_close_all(); diff --git a/hmp-commands.hx b/hmp-commands.hx index 45eebf27e6..15620c94d3 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1553,17 +1553,35 @@ ETEXI { .name = "nbd_server_add", - .args_type = "writable:-w,device:B", - .params = "nbd_server_add [-w] device", + .args_type = "writable:-w,device:B,name:s?", + .params = "nbd_server_add [-w] device [name]", .help = "export a block device via NBD", .cmd = hmp_nbd_server_add, }, STEXI -@item nbd_server_add @var{device} +@item nbd_server_add @var{device} [ @var{name} ] @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. +exported device writable too. The export name is controlled by @var{name}, +defaulting to @var{device}. +ETEXI + + { + .name = "nbd_server_remove", + .args_type = "force:-f,name:s", + .params = "nbd_server_remove [-f] name", + .help = "remove an export previously exposed via NBD", + .cmd = hmp_nbd_server_remove, + }, +STEXI +@item nbd_server_remove [-f] @var{name} +@findex nbd_server_remove +Stop exporting a block device through QEMU's NBD server, which was +previously started with @command{nbd_server_add}. The @option{-f} +option forces the server to drop the export immediately even if +clients are connected; otherwise the command fails unless there are no +clients. ETEXI { @@ -2203,7 +2203,8 @@ void hmp_nbd_server_start(Monitor *mon, const QDict *qdict) continue; } - qmp_nbd_server_add(info->value->device, true, writable, &local_err); + qmp_nbd_server_add(info->value->device, false, NULL, + true, writable, &local_err); if (local_err != NULL) { qmp_nbd_server_stop(NULL); @@ -2220,14 +2221,23 @@ exit: void hmp_nbd_server_add(Monitor *mon, const QDict *qdict) { const char *device = qdict_get_str(qdict, "device"); + const char *name = qdict_get_try_str(qdict, "name"); bool writable = qdict_get_try_bool(qdict, "writable", false); Error *local_err = NULL; - qmp_nbd_server_add(device, true, writable, &local_err); + qmp_nbd_server_add(device, !!name, name, true, writable, &local_err); + hmp_handle_error(mon, &local_err); +} - if (local_err != NULL) { - hmp_handle_error(mon, &local_err); - } +void hmp_nbd_server_remove(Monitor *mon, const QDict *qdict) +{ + const char *name = qdict_get_str(qdict, "name"); + bool force = qdict_get_try_bool(qdict, "force", false); + Error *err = NULL; + + /* Rely on NBD_SERVER_REMOVE_MODE_SAFE being the default */ + qmp_nbd_server_remove(name, force, NBD_SERVER_REMOVE_MODE_HARD, &err); + hmp_handle_error(mon, &err); } void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict) @@ -101,6 +101,7 @@ void hmp_sendkey(Monitor *mon, const QDict *qdict); void hmp_screendump(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_remove(Monitor *mon, const QDict *qdict); void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict); void hmp_chardev_add(Monitor *mon, const QDict *qdict); void hmp_chardev_change(Monitor *mon, const QDict *qdict); diff --git a/hw/input/trace-events b/hw/input/trace-events index a8d46cb766..5affabc81d 100644 --- a/hw/input/trace-events +++ b/hw/input/trace-events @@ -4,6 +4,7 @@ adb_kbd_no_key(void) "Ignoring NO_KEY" adb_kbd_writereg(int reg, uint8_t val) "reg %d val 0x%2.2x" adb_kbd_readreg(int reg, uint8_t val0, uint8_t val1) "reg %d obuf[0] 0x%2.2x obuf[1] 0x%2.2x" + # hw/input/adb-mouse.c adb_mouse_writereg(int reg, uint8_t val) "reg %d val 0x%2.2x" adb_mouse_readreg(int reg, uint8_t val0, uint8_t val1) "reg %d obuf[0] 0x%2.2x obuf[1] 0x%2.2x" diff --git a/hw/pci-host/grackle.c b/hw/pci-host/grackle.c index 3caf1ccb37..033588b7d2 100644 --- a/hw/pci-host/grackle.c +++ b/hw/pci-host/grackle.c @@ -27,16 +27,7 @@ #include "hw/pci/pci_host.h" #include "hw/ppc/mac.h" #include "hw/pci/pci.h" - -/* debug Grackle */ -//#define DEBUG_GRACKLE - -#ifdef DEBUG_GRACKLE -#define GRACKLE_DPRINTF(fmt, ...) \ - do { printf("GRACKLE: " fmt , ## __VA_ARGS__); } while (0) -#else -#define GRACKLE_DPRINTF(fmt, ...) -#endif +#include "trace.h" #define GRACKLE_PCI_HOST_BRIDGE(obj) \ OBJECT_CHECK(GrackleState, (obj), TYPE_GRACKLE_PCI_HOST_BRIDGE) @@ -58,7 +49,7 @@ static void pci_grackle_set_irq(void *opaque, int irq_num, int level) { qemu_irq *pic = opaque; - GRACKLE_DPRINTF("set_irq num %d level %d\n", irq_num, level); + trace_grackle_set_irq(irq_num, level); qemu_set_irq(pic[irq_num + 0x15], level); } diff --git a/hw/pci-host/trace-events b/hw/pci-host/trace-events index 32dfc84692..341a87a702 100644 --- a/hw/pci-host/trace-events +++ b/hw/pci-host/trace-events @@ -1,5 +1,8 @@ # See docs/devel/tracing.txt for syntax documentation. +# hw/pci-host/grackle.c +grackle_set_irq(int irq_num, int level) "set_irq num %d level %d" + # hw/pci-host/sabre.c sabre_set_request(int irq_num) "request irq %d" sabre_clear_request(int irq_num) "clear request irq %d" @@ -9,3 +12,9 @@ sabre_pci_config_write(uint64_t addr, uint64_t val) "addr 0x%"PRIx64" val 0x%"PR sabre_pci_config_read(uint64_t addr, uint64_t val) "addr 0x%"PRIx64" val 0x%"PRIx64 sabre_pci_set_irq(int irq_num, int level) "set irq_in %d level %d" sabre_pci_set_obio_irq(int irq_num, int level) "set irq %d level %d" + +# hw/pci-host/uninorth.c +unin_set_irq(int irq_num, int level) "setting INT %d = %d" +unin_get_config_reg(uint32_t reg, uint32_t addr, uint32_t retval) "converted config space accessor 0x%"PRIx32 "/0x%"PRIx32 " -> 0x%"PRIx32 +unin_data_write(uint64_t addr, unsigned len, uint64_t val) "write addr 0x%"PRIx64 " len %d val 0x%"PRIx64 +unin_data_read(uint64_t addr, unsigned len, uint64_t val) "read addr 0x%"PRIx64 " len %d val 0x%"PRIx64 diff --git a/hw/pci-host/uninorth.c b/hw/pci-host/uninorth.c index 5d8ccaa711..66991da975 100644 --- a/hw/pci-host/uninorth.c +++ b/hw/pci-host/uninorth.c @@ -26,16 +26,7 @@ #include "hw/ppc/mac.h" #include "hw/pci/pci.h" #include "hw/pci/pci_host.h" - -/* debug UniNorth */ -//#define DEBUG_UNIN - -#ifdef DEBUG_UNIN -#define UNIN_DPRINTF(fmt, ...) \ - do { printf("UNIN: " fmt , ## __VA_ARGS__); } while (0) -#else -#define UNIN_DPRINTF(fmt, ...) -#endif +#include "trace.h" static const int unin_irq_line[] = { 0x1b, 0x1c, 0x1d, 0x1e }; @@ -69,8 +60,7 @@ static void pci_unin_set_irq(void *opaque, int irq_num, int level) { qemu_irq *pic = opaque; - UNIN_DPRINTF("%s: setting INT %d = %d\n", __func__, - unin_irq_line[irq_num], level); + trace_unin_set_irq(unin_irq_line[irq_num], level); qemu_set_irq(pic[unin_irq_line[irq_num]], level); } @@ -103,9 +93,7 @@ static uint32_t unin_get_config_reg(uint32_t reg, uint32_t addr) retval |= func << 8; } - - UNIN_DPRINTF("Converted config space accessor %08x/%08x -> %08x\n", - reg, addr, retval); + trace_unin_get_config_reg(reg, addr, retval); return retval; } @@ -115,8 +103,7 @@ static void unin_data_write(void *opaque, hwaddr addr, { UNINState *s = opaque; PCIHostState *phb = PCI_HOST_BRIDGE(s); - UNIN_DPRINTF("write addr " TARGET_FMT_plx " len %d val %"PRIx64"\n", - addr, len, val); + trace_unin_data_write(addr, len, val); pci_data_write(phb->bus, unin_get_config_reg(phb->config_reg, addr), val, len); @@ -132,8 +119,7 @@ static uint64_t unin_data_read(void *opaque, hwaddr addr, val = pci_data_read(phb->bus, unin_get_config_reg(phb->config_reg, addr), len); - UNIN_DPRINTF("read addr " TARGET_FMT_plx " len %d val %x\n", - addr, len, val); + trace_unin_data_read(addr, len, val); return val; } diff --git a/hw/ppc/ppc405_boards.c b/hw/ppc/ppc405_boards.c index e92db2c66a..6f7f2ee168 100644 --- a/hw/ppc/ppc405_boards.c +++ b/hw/ppc/ppc405_boards.c @@ -202,6 +202,13 @@ static void ref405ep_init(MachineState *machine) DriveInfo *dinfo; MemoryRegion *sysmem = get_system_memory(); +#ifdef TARGET_PPCEMB + if (!qtest_enabled()) { + warn_report("qemu-system-ppcemb is deprecated, " + "please use qemu-system-ppc instead."); + } +#endif + /* XXX: fix this */ memory_region_allocate_system_memory(&ram_memories[0], NULL, "ef405ep.ram", 0x08000000); @@ -497,6 +504,13 @@ static void taihu_405ep_init(MachineState *machine) int fl_idx, fl_sectors; DriveInfo *dinfo; +#ifdef TARGET_PPCEMB + if (!qtest_enabled()) { + warn_report("qemu-system-ppcemb is deprecated, " + "please use qemu-system-ppc instead."); + } +#endif + /* RAM is soldered to the board so the size cannot be changed */ ram_size = 0x08000000; memory_region_allocate_system_memory(ram, NULL, "taihu_405ep.ram", diff --git a/hw/ppc/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c index 693c215108..a299206fd4 100644 --- a/hw/ppc/ppc440_bamboo.c +++ b/hw/ppc/ppc440_bamboo.c @@ -13,6 +13,7 @@ #include "qemu/osdep.h" #include "qemu-common.h" +#include "qemu/error-report.h" #include "net/net.h" #include "hw/hw.h" #include "hw/pci/pci.h" @@ -27,6 +28,7 @@ #include "hw/ppc/ppc.h" #include "ppc405.h" #include "sysemu/sysemu.h" +#include "sysemu/qtest.h" #include "hw/sysbus.h" #define BINARY_DEVICE_TREE_FILE "bamboo.dtb" @@ -191,6 +193,13 @@ static void bamboo_init(MachineState *machine) exit(1); } +#ifdef TARGET_PPCEMB + if (!qtest_enabled()) { + warn_report("qemu-system-ppcemb is deprecated, " + "please use qemu-system-ppc instead."); + } +#endif + qemu_register_reset(main_cpu_reset, cpu); ppc_booke_timers_init(cpu, 400000000, 0); ppc_dcr_init(env, NULL, NULL); diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 88a78d31eb..32a876be56 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -1791,6 +1791,9 @@ static const VMStateDescription vmstate_spapr = { &vmstate_spapr_cap_htm, &vmstate_spapr_cap_vsx, &vmstate_spapr_cap_dfp, + &vmstate_spapr_cap_cfpc, + &vmstate_spapr_cap_sbbc, + &vmstate_spapr_cap_ibs, NULL } }; @@ -3881,6 +3884,9 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) smc->default_caps.caps[SPAPR_CAP_HTM] = SPAPR_CAP_OFF; smc->default_caps.caps[SPAPR_CAP_VSX] = SPAPR_CAP_ON; smc->default_caps.caps[SPAPR_CAP_DFP] = SPAPR_CAP_ON; + smc->default_caps.caps[SPAPR_CAP_CFPC] = SPAPR_CAP_BROKEN; + smc->default_caps.caps[SPAPR_CAP_SBBC] = SPAPR_CAP_BROKEN; + smc->default_caps.caps[SPAPR_CAP_IBS] = SPAPR_CAP_BROKEN; spapr_caps_add_properties(smc, &error_abort); } diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c index 5d52969bd5..62efdaee38 100644 --- a/hw/ppc/spapr_caps.c +++ b/hw/ppc/spapr_caps.c @@ -73,6 +73,66 @@ static void spapr_cap_set_bool(Object *obj, Visitor *v, const char *name, spapr->eff.caps[cap->index] = value ? SPAPR_CAP_ON : SPAPR_CAP_OFF; } +static void spapr_cap_get_tristate(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + sPAPRCapabilityInfo *cap = opaque; + sPAPRMachineState *spapr = SPAPR_MACHINE(obj); + char *val = NULL; + uint8_t value = spapr_get_cap(spapr, cap->index); + + switch (value) { + case SPAPR_CAP_BROKEN: + val = g_strdup("broken"); + break; + case SPAPR_CAP_WORKAROUND: + val = g_strdup("workaround"); + break; + case SPAPR_CAP_FIXED: + val = g_strdup("fixed"); + break; + default: + error_setg(errp, "Invalid value (%d) for cap-%s", value, cap->name); + return; + } + + visit_type_str(v, name, &val, errp); + g_free(val); +} + +static void spapr_cap_set_tristate(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + sPAPRCapabilityInfo *cap = opaque; + sPAPRMachineState *spapr = SPAPR_MACHINE(obj); + char *val; + Error *local_err = NULL; + uint8_t value; + + visit_type_str(v, name, &val, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + if (!strcasecmp(val, "broken")) { + value = SPAPR_CAP_BROKEN; + } else if (!strcasecmp(val, "workaround")) { + value = SPAPR_CAP_WORKAROUND; + } else if (!strcasecmp(val, "fixed")) { + value = SPAPR_CAP_FIXED; + } else { + error_setg(errp, "Invalid capability mode \"%s\" for cap-%s", val, + cap->name); + goto out; + } + + spapr->cmd_line_caps[cap->index] = true; + spapr->eff.caps[cap->index] = value; +out: + g_free(val); +} + static void cap_htm_apply(sPAPRMachineState *spapr, uint8_t val, Error **errp) { if (!val) { @@ -120,6 +180,40 @@ static void cap_dfp_apply(sPAPRMachineState *spapr, uint8_t val, Error **errp) } } +static void cap_safe_cache_apply(sPAPRMachineState *spapr, uint8_t val, + Error **errp) +{ + if (tcg_enabled() && val) { + /* TODO - for now only allow broken for TCG */ + error_setg(errp, "Requested safe cache capability level not supported by tcg, try a different value for cap-cfpc"); + } else if (kvm_enabled() && (val > kvmppc_get_cap_safe_cache())) { + error_setg(errp, "Requested safe cache capability level not supported by kvm, try a different value for cap-cfpc"); + } +} + +static void cap_safe_bounds_check_apply(sPAPRMachineState *spapr, uint8_t val, + Error **errp) +{ + if (tcg_enabled() && val) { + /* TODO - for now only allow broken for TCG */ + error_setg(errp, "Requested safe bounds check capability level not supported by tcg, try a different value for cap-sbbc"); + } else if (kvm_enabled() && (val > kvmppc_get_cap_safe_bounds_check())) { + error_setg(errp, "Requested safe bounds check capability level not supported by kvm, try a different value for cap-sbbc"); + } +} + +static void cap_safe_indirect_branch_apply(sPAPRMachineState *spapr, + uint8_t val, Error **errp) +{ + if (tcg_enabled() && val) { + /* TODO - for now only allow broken for TCG */ + error_setg(errp, "Requested safe indirect branch capability level not supported by tcg, try a different value for cap-ibs"); + } else if (kvm_enabled() && (val > kvmppc_get_cap_safe_indirect_branch())) { + error_setg(errp, "Requested safe indirect branch capability level not supported by kvm, try a different value for cap-ibs"); + } +} + +#define VALUE_DESC_TRISTATE " (broken, workaround, fixed)" sPAPRCapabilityInfo capability_table[SPAPR_CAP_NUM] = { [SPAPR_CAP_HTM] = { @@ -149,6 +243,33 @@ sPAPRCapabilityInfo capability_table[SPAPR_CAP_NUM] = { .type = "bool", .apply = cap_dfp_apply, }, + [SPAPR_CAP_CFPC] = { + .name = "cfpc", + .description = "Cache Flush on Privilege Change" VALUE_DESC_TRISTATE, + .index = SPAPR_CAP_CFPC, + .get = spapr_cap_get_tristate, + .set = spapr_cap_set_tristate, + .type = "string", + .apply = cap_safe_cache_apply, + }, + [SPAPR_CAP_SBBC] = { + .name = "sbbc", + .description = "Speculation Barrier Bounds Checking" VALUE_DESC_TRISTATE, + .index = SPAPR_CAP_SBBC, + .get = spapr_cap_get_tristate, + .set = spapr_cap_set_tristate, + .type = "string", + .apply = cap_safe_bounds_check_apply, + }, + [SPAPR_CAP_IBS] = { + .name = "ibs", + .description = "Indirect Branch Serialisation" VALUE_DESC_TRISTATE, + .index = SPAPR_CAP_IBS, + .get = spapr_cap_get_tristate, + .set = spapr_cap_set_tristate, + .type = "string", + .apply = cap_safe_indirect_branch_apply, + }, }; static sPAPRCapabilities default_caps_with_cpu(sPAPRMachineState *spapr, @@ -254,6 +375,9 @@ const VMStateDescription vmstate_spapr_cap_##cap = { \ SPAPR_CAP_MIG_STATE(htm, HTM); SPAPR_CAP_MIG_STATE(vsx, VSX); SPAPR_CAP_MIG_STATE(dfp, DFP); +SPAPR_CAP_MIG_STATE(cfpc, CFPC); +SPAPR_CAP_MIG_STATE(sbbc, SBBC); +SPAPR_CAP_MIG_STATE(ibs, IBS); void spapr_caps_reset(sPAPRMachineState *spapr) { diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 51eba52e86..4d0e6eb0cf 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -1654,6 +1654,60 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, return H_SUCCESS; } +static target_ulong h_get_cpu_characteristics(PowerPCCPU *cpu, + sPAPRMachineState *spapr, + target_ulong opcode, + target_ulong *args) +{ + uint64_t characteristics = H_CPU_CHAR_HON_BRANCH_HINTS & + ~H_CPU_CHAR_THR_RECONF_TRIG; + uint64_t behaviour = H_CPU_BEHAV_FAVOUR_SECURITY; + uint8_t safe_cache = spapr_get_cap(spapr, SPAPR_CAP_CFPC); + uint8_t safe_bounds_check = spapr_get_cap(spapr, SPAPR_CAP_SBBC); + uint8_t safe_indirect_branch = spapr_get_cap(spapr, SPAPR_CAP_IBS); + + switch (safe_cache) { + case SPAPR_CAP_WORKAROUND: + characteristics |= H_CPU_CHAR_L1D_FLUSH_ORI30; + characteristics |= H_CPU_CHAR_L1D_FLUSH_TRIG2; + characteristics |= H_CPU_CHAR_L1D_THREAD_PRIV; + behaviour |= H_CPU_BEHAV_L1D_FLUSH_PR; + break; + case SPAPR_CAP_FIXED: + break; + default: /* broken */ + assert(safe_cache == SPAPR_CAP_BROKEN); + behaviour |= H_CPU_BEHAV_L1D_FLUSH_PR; + break; + } + + switch (safe_bounds_check) { + case SPAPR_CAP_WORKAROUND: + characteristics |= H_CPU_CHAR_SPEC_BAR_ORI31; + behaviour |= H_CPU_BEHAV_BNDS_CHK_SPEC_BAR; + break; + case SPAPR_CAP_FIXED: + break; + default: /* broken */ + assert(safe_bounds_check == SPAPR_CAP_BROKEN); + behaviour |= H_CPU_BEHAV_BNDS_CHK_SPEC_BAR; + break; + } + + switch (safe_indirect_branch) { + case SPAPR_CAP_FIXED: + characteristics |= H_CPU_CHAR_BCCTRL_SERIALISED; + default: /* broken */ + assert(safe_indirect_branch == SPAPR_CAP_BROKEN); + break; + } + + args[0] = characteristics; + args[1] = behaviour; + + return H_SUCCESS; +} + static spapr_hcall_fn papr_hypercall_table[(MAX_HCALL_OPCODE / 4) + 1]; static spapr_hcall_fn kvmppc_hypercall_table[KVMPPC_HCALL_MAX - KVMPPC_HCALL_BASE + 1]; @@ -1733,6 +1787,10 @@ static void hypercall_register_types(void) spapr_register_hypercall(H_INVALIDATE_PID, h_invalidate_pid); spapr_register_hypercall(H_REGISTER_PROC_TBL, h_register_process_table); + /* hcall-get-cpu-characteristics */ + spapr_register_hypercall(H_GET_CPU_CHARACTERISTICS, + h_get_cpu_characteristics); + /* "debugger" hcalls (also used by SLOF). Note: We do -not- differenciate * here between the "CI" and the "CACHE" variants, they will use whatever * mapping attributes qemu is using. When using KVM, the kernel will diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 37f18b3d32..39a14980d3 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -280,13 +280,42 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr, int *config_addr_key; Error *err = NULL; + /* Fins sPAPRPHBState */ + phb = spapr_pci_find_phb(spapr, buid); + if (phb) { + pdev = spapr_pci_find_dev(spapr, buid, config_addr); + } + if (!phb || !pdev) { + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); + return; + } + switch (func) { - case RTAS_CHANGE_MSI_FN: case RTAS_CHANGE_FN: - ret_intr_type = RTAS_TYPE_MSI; + if (msi_present(pdev)) { + ret_intr_type = RTAS_TYPE_MSI; + } else if (msix_present(pdev)) { + ret_intr_type = RTAS_TYPE_MSIX; + } else { + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); + return; + } + break; + case RTAS_CHANGE_MSI_FN: + if (msi_present(pdev)) { + ret_intr_type = RTAS_TYPE_MSI; + } else { + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); + return; + } break; case RTAS_CHANGE_MSIX_FN: - ret_intr_type = RTAS_TYPE_MSIX; + if (msix_present(pdev)) { + ret_intr_type = RTAS_TYPE_MSIX; + } else { + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); + return; + } break; default: error_report("rtas_ibm_change_msi(%u) is not implemented", func); @@ -294,16 +323,6 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr, return; } - /* Fins sPAPRPHBState */ - phb = spapr_pci_find_phb(spapr, buid); - if (phb) { - pdev = spapr_pci_find_dev(spapr, buid, config_addr); - } - if (!phb || !pdev) { - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; - } - msi = (spapr_pci_msi *) g_hash_table_lookup(phb->msi, &config_addr); /* Releasing MSIs */ @@ -1286,13 +1305,17 @@ static void spapr_populate_pci_child_dt(PCIDevice *dev, void *fdt, int offset, _FDT(fdt_setprop_cell(fdt, offset, "#size-cells", RESOURCE_CELLS_SIZE)); - max_msi = msi_nr_vectors_allocated(dev); - if (max_msi) { - _FDT(fdt_setprop_cell(fdt, offset, "ibm,req#msi", max_msi)); + if (msi_present(dev)) { + max_msi = msi_nr_vectors_allocated(dev); + if (max_msi) { + _FDT(fdt_setprop_cell(fdt, offset, "ibm,req#msi", max_msi)); + } } - max_msix = dev->msix_entries_nr; - if (max_msix) { - _FDT(fdt_setprop_cell(fdt, offset, "ibm,req#msi-x", max_msix)); + if (msix_present(dev)) { + max_msix = dev->msix_entries_nr; + if (max_msix) { + _FDT(fdt_setprop_cell(fdt, offset, "ibm,req#msi-x", max_msix)); + } } populate_resource_props(dev, &rp); diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c index 5ac4f76613..9fe7655074 100644 --- a/hw/ppc/virtex_ml507.c +++ b/hw/ppc/virtex_ml507.c @@ -29,6 +29,7 @@ #include "hw/char/serial.h" #include "hw/block/flash.h" #include "sysemu/sysemu.h" +#include "sysemu/qtest.h" #include "hw/devices.h" #include "hw/boards.h" #include "sysemu/device_tree.h" @@ -210,6 +211,13 @@ static void virtex_init(MachineState *machine) int kernel_size; int i; +#ifdef TARGET_PPCEMB + if (!qtest_enabled()) { + warn_report("qemu-system-ppcemb is deprecated, " + "please use qemu-system-ppc instead."); + } +#endif + /* init CPUs */ cpu = ppc440_init_xilinx(&ram_size, 1, machine->cpu_type, 400000000); env = &cpu->env; diff --git a/include/block/nbd.h b/include/block/nbd.h index 978e443366..ee74ec391a 100644 --- a/include/block/nbd.h +++ b/include/block/nbd.h @@ -261,6 +261,7 @@ NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size, bool writethrough, BlockBackend *on_eject_blk, Error **errp); void nbd_export_close(NBDExport *exp); +void nbd_export_remove(NBDExport *exp, NbdServerRemoveMode mode, Error **errp); void nbd_export_get(NBDExport *exp); void nbd_export_put(NBDExport *exp); diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h index fb1bd5df09..255b26a5aa 100644 --- a/include/hw/ppc/pnv_xscom.h +++ b/include/hw/ppc/pnv_xscom.h @@ -21,8 +21,6 @@ #include "qom/object.h" -typedef struct PnvChip PnvChip; - typedef struct PnvXScomInterface { Object parent; } PnvXScomInterface; diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 0f5628f22e..62c077ac20 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -60,8 +60,14 @@ typedef enum { #define SPAPR_CAP_VSX 0x01 /* Decimal Floating Point */ #define SPAPR_CAP_DFP 0x02 +/* Cache Flush on Privilege Change */ +#define SPAPR_CAP_CFPC 0x03 +/* Speculation Barrier Bounds Checking */ +#define SPAPR_CAP_SBBC 0x04 +/* Indirect Branch Serialisation */ +#define SPAPR_CAP_IBS 0x05 /* Num Caps */ -#define SPAPR_CAP_NUM (SPAPR_CAP_DFP + 1) +#define SPAPR_CAP_NUM (SPAPR_CAP_IBS + 1) /* * Capability Values @@ -69,6 +75,10 @@ typedef enum { /* Bool Caps */ #define SPAPR_CAP_OFF 0x00 #define SPAPR_CAP_ON 0x01 +/* Broken | Workaround | Fixed Caps */ +#define SPAPR_CAP_BROKEN 0x00 +#define SPAPR_CAP_WORKAROUND 0x01 +#define SPAPR_CAP_FIXED 0x02 typedef struct sPAPRCapabilities sPAPRCapabilities; struct sPAPRCapabilities { @@ -295,6 +305,18 @@ struct sPAPRMachineState { #define H_DABRX_KERNEL (1ULL<<(63-62)) #define H_DABRX_USER (1ULL<<(63-63)) +/* Values for KVM_PPC_GET_CPU_CHAR & H_GET_CPU_CHARACTERISTICS */ +#define H_CPU_CHAR_SPEC_BAR_ORI31 PPC_BIT(0) +#define H_CPU_CHAR_BCCTRL_SERIALISED PPC_BIT(1) +#define H_CPU_CHAR_L1D_FLUSH_ORI30 PPC_BIT(2) +#define H_CPU_CHAR_L1D_FLUSH_TRIG2 PPC_BIT(3) +#define H_CPU_CHAR_L1D_THREAD_PRIV PPC_BIT(4) +#define H_CPU_CHAR_HON_BRANCH_HINTS PPC_BIT(5) +#define H_CPU_CHAR_THR_RECONF_TRIG PPC_BIT(6) +#define H_CPU_BEHAV_FAVOUR_SECURITY PPC_BIT(0) +#define H_CPU_BEHAV_L1D_FLUSH_PR PPC_BIT(1) +#define H_CPU_BEHAV_BNDS_CHK_SPEC_BAR PPC_BIT(2) + /* Each control block has to be on a 4K boundary */ #define H_CB_ALIGNMENT 4096 @@ -382,6 +404,7 @@ struct sPAPRMachineState { #define H_GET_HCA_INFO 0x1B8 #define H_GET_PERF_COUNT 0x1BC #define H_MANAGE_TRACE 0x1C0 +#define H_GET_CPU_CHARACTERISTICS 0x1C8 #define H_FREE_LOGICAL_LAN_BUFFER 0x1D4 #define H_QUERY_INT_STATE 0x1E4 #define H_POLL_PENDING 0x1D8 @@ -763,6 +786,9 @@ int spapr_caps_pre_save(void *opaque); extern const VMStateDescription vmstate_spapr_cap_htm; extern const VMStateDescription vmstate_spapr_cap_vsx; extern const VMStateDescription vmstate_spapr_cap_dfp; +extern const VMStateDescription vmstate_spapr_cap_cfpc; +extern const VMStateDescription vmstate_spapr_cap_sbbc; +extern const VMStateDescription vmstate_spapr_cap_ibs; static inline uint8_t spapr_get_cap(sPAPRMachineState *spapr, int cap) { diff --git a/nbd/server.c b/nbd/server.c index 6caa8d17be..112e3f69df 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -1177,6 +1177,19 @@ void nbd_export_close(NBDExport *exp) nbd_export_put(exp); } +void nbd_export_remove(NBDExport *exp, NbdServerRemoveMode mode, Error **errp) +{ + if (mode == NBD_SERVER_REMOVE_MODE_HARD || QTAILQ_EMPTY(&exp->clients)) { + nbd_export_close(exp); + return; + } + + assert(mode == NBD_SERVER_REMOVE_MODE_SAFE); + + error_setg(errp, "export '%s' still in use", exp->name); + error_append_hint(errp, "Use mode='hard' to force client disconnect\n"); +} + void nbd_export_get(NBDExport *exp) { assert(exp->refcount > 0); diff --git a/qapi/block.json b/qapi/block.json index f093fa3f27..c694524002 100644 --- a/qapi/block.json +++ b/qapi/block.json @@ -213,14 +213,60 @@ # # @device: The device name or node name of the node to be exported # +# @name: Export name. If unspecified, the @device parameter is used as the +# export name. (Since 2.12) +# # @writable: Whether clients should be able to write to the device via the # NBD connection (default false). # -# Returns: error if the device is already marked for export. +# Returns: error if the server is not running, or export with the same name +# already exists. # # Since: 1.3.0 ## -{ 'command': 'nbd-server-add', 'data': {'device': 'str', '*writable': 'bool'} } +{ 'command': 'nbd-server-add', + 'data': {'device': 'str', '*name': 'str', '*writable': 'bool'} } + +## +# @NbdServerRemoveMode: +# +# Mode for removing an NBD export. +# +# @safe: Remove export if there are no existing connections, fail otherwise. +# +# @hard: Drop all connections immediately and remove export. +# +# Potential additional modes to be added in the future: +# +# hide: Just hide export from new clients, leave existing connections as is. +# Remove export after all clients are disconnected. +# +# soft: Hide export from new clients, answer with ESHUTDOWN for all further +# requests from existing clients. +# +# Since: 2.12 +## +{'enum': 'NbdServerRemoveMode', 'data': ['safe', 'hard']} + +## +# @nbd-server-remove: +# +# Remove NBD export by name. +# +# @name: Export name. +# +# @mode: Mode of command operation. See @NbdServerRemoveMode description. +# Default is 'safe'. +# +# Returns: error if +# - the server is not running +# - export is not found +# - mode is 'safe' and there are existing connections +# +# Since: 2.12 +## +{ 'command': 'nbd-server-remove', + 'data': {'name': 'str', '*mode': 'NbdServerRemoveMode'} } ## # @nbd-server-stop: diff --git a/qemu-doc.texi b/qemu-doc.texi index 79d08b3f04..19a82bfea3 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -2773,6 +2773,12 @@ The ``host_net_remove'' command is replaced by the ``netdev_del'' command. The ``ivshmem'' device type is replaced by either the ``ivshmem-plain'' or ``ivshmem-doorbell`` device types. +@subsection Page size support < 4k for embedded PowerPC CPUs (since 2.12.0) + +qemu-system-ppcemb will be removed. qemu-system-ppc (or qemu-system-ppc64) +should be used instead. That means that embedded 4xx PowerPC CPUs will not +support page sizes < 4096 any longer. + @section System emulator machines @subsection Xilinx EP108 (since 2.11.0) diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 914be687e7..84284d5957 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -89,6 +89,9 @@ static int cap_mmu_radix; static int cap_mmu_hash_v3; static int cap_resize_hpt; static int cap_ppc_pvr_compat; +static int cap_ppc_safe_cache; +static int cap_ppc_safe_bounds_check; +static int cap_ppc_safe_indirect_branch; static uint32_t debug_inst_opcode; @@ -121,6 +124,7 @@ static bool kvmppc_is_pr(KVMState *ks) } static int kvm_ppc_register_host_cpu_type(MachineState *ms); +static void kvmppc_get_cpu_characteristics(KVMState *s); int kvm_arch_init(MachineState *ms, KVMState *s) { @@ -147,6 +151,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s) cap_mmu_radix = kvm_vm_check_extension(s, KVM_CAP_PPC_MMU_RADIX); cap_mmu_hash_v3 = kvm_vm_check_extension(s, KVM_CAP_PPC_MMU_HASH_V3); cap_resize_hpt = kvm_vm_check_extension(s, KVM_CAP_SPAPR_RESIZE_HPT); + kvmppc_get_cpu_characteristics(s); /* * Note: setting it to false because there is not such capability * in KVM at this moment. @@ -2456,6 +2461,59 @@ bool kvmppc_has_cap_mmu_hash_v3(void) return cap_mmu_hash_v3; } +static void kvmppc_get_cpu_characteristics(KVMState *s) +{ + struct kvm_ppc_cpu_char c; + int ret; + + /* Assume broken */ + cap_ppc_safe_cache = 0; + cap_ppc_safe_bounds_check = 0; + cap_ppc_safe_indirect_branch = 0; + + ret = kvm_vm_check_extension(s, KVM_CAP_PPC_GET_CPU_CHAR); + if (!ret) { + return; + } + ret = kvm_vm_ioctl(s, KVM_PPC_GET_CPU_CHAR, &c); + if (ret < 0) { + return; + } + /* Parse and set cap_ppc_safe_cache */ + if (~c.behaviour & c.behaviour_mask & H_CPU_BEHAV_L1D_FLUSH_PR) { + cap_ppc_safe_cache = 2; + } else if ((c.character & c.character_mask & H_CPU_CHAR_L1D_THREAD_PRIV) && + (c.character & c.character_mask + & (H_CPU_CHAR_L1D_FLUSH_ORI30 | H_CPU_CHAR_L1D_FLUSH_TRIG2))) { + cap_ppc_safe_cache = 1; + } + /* Parse and set cap_ppc_safe_bounds_check */ + if (~c.behaviour & c.behaviour_mask & H_CPU_BEHAV_BNDS_CHK_SPEC_BAR) { + cap_ppc_safe_bounds_check = 2; + } else if (c.character & c.character_mask & H_CPU_CHAR_SPEC_BAR_ORI31) { + cap_ppc_safe_bounds_check = 1; + } + /* Parse and set cap_ppc_safe_indirect_branch */ + if (c.character & H_CPU_CHAR_BCCTRL_SERIALISED) { + cap_ppc_safe_indirect_branch = 2; + } +} + +int kvmppc_get_cap_safe_cache(void) +{ + return cap_ppc_safe_cache; +} + +int kvmppc_get_cap_safe_bounds_check(void) +{ + return cap_ppc_safe_bounds_check; +} + +int kvmppc_get_cap_safe_indirect_branch(void) +{ + return cap_ppc_safe_indirect_branch; +} + PowerPCCPUClass *kvm_ppc_get_host_cpu_class(void) { uint32_t host_pvr = mfpvr(); diff --git a/target/ppc/kvm_ppc.h b/target/ppc/kvm_ppc.h index ecb55493cc..39830baa77 100644 --- a/target/ppc/kvm_ppc.h +++ b/target/ppc/kvm_ppc.h @@ -59,6 +59,9 @@ bool kvmppc_has_cap_fixup_hcalls(void); bool kvmppc_has_cap_htm(void); bool kvmppc_has_cap_mmu_radix(void); bool kvmppc_has_cap_mmu_hash_v3(void); +int kvmppc_get_cap_safe_cache(void); +int kvmppc_get_cap_safe_bounds_check(void); +int kvmppc_get_cap_safe_indirect_branch(void); int kvmppc_enable_hwrng(void); int kvmppc_put_books_sregs(PowerPCCPU *cpu); PowerPCCPUClass *kvm_ppc_get_host_cpu_class(void); @@ -290,6 +293,21 @@ static inline bool kvmppc_has_cap_mmu_hash_v3(void) return false; } +static inline int kvmppc_get_cap_safe_cache(void) +{ + return 0; +} + +static inline int kvmppc_get_cap_safe_bounds_check(void) +{ + return 0; +} + +static inline int kvmppc_get_cap_safe_indirect_branch(void) +{ + return 0; +} + static inline int kvmppc_enable_hwrng(void) { return -1; diff --git a/tests/qemu-iotests/147 b/tests/qemu-iotests/147 index 90f40ed245..d2081df84b 100755 --- a/tests/qemu-iotests/147 +++ b/tests/qemu-iotests/147 @@ -38,8 +38,8 @@ def flatten_sock_addr(crumpled_address): class NBDBlockdevAddBase(iotests.QMPTestCase): - def blockdev_add_options(self, address, export=None): - options = { 'node-name': 'nbd-blockdev', + def blockdev_add_options(self, address, export, node_name): + options = { 'node-name': node_name, 'driver': 'raw', 'file': { 'driver': 'nbd', @@ -50,23 +50,28 @@ class NBDBlockdevAddBase(iotests.QMPTestCase): options['file']['export'] = export return options - def client_test(self, filename, address, export=None): - bao = self.blockdev_add_options(address, export) + def client_test(self, filename, address, export=None, + node_name='nbd-blockdev', delete=True): + bao = self.blockdev_add_options(address, export, node_name) result = self.vm.qmp('blockdev-add', **bao) self.assert_qmp(result, 'return', {}) + found = False result = self.vm.qmp('query-named-block-nodes') for node in result['return']: - if node['node-name'] == 'nbd-blockdev': + if node['node-name'] == node_name: + found = True if isinstance(filename, str): self.assert_qmp(node, 'image/filename', filename) else: self.assert_json_filename_equal(node['image']['filename'], filename) break + self.assertTrue(found) - result = self.vm.qmp('blockdev-del', node_name='nbd-blockdev') - self.assert_qmp(result, 'return', {}) + if delete: + result = self.vm.qmp('blockdev-del', node_name=node_name) + self.assert_qmp(result, 'return', {}) class QemuNBD(NBDBlockdevAddBase): @@ -125,26 +130,63 @@ class BuiltinNBD(NBDBlockdevAddBase): except OSError: pass - def _server_up(self, address): + def _server_up(self, address, export_name=None, export_name2=None): result = self.server.qmp('nbd-server-start', addr=address) self.assert_qmp(result, 'return', {}) - result = self.server.qmp('nbd-server-add', device='nbd-export') + if export_name is None: + result = self.server.qmp('nbd-server-add', device='nbd-export') + else: + result = self.server.qmp('nbd-server-add', device='nbd-export', + name=export_name) self.assert_qmp(result, 'return', {}) + if export_name2 is not None: + result = self.server.qmp('nbd-server-add', device='nbd-export', + name=export_name2) + 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): + def do_test_inet(self, export_name=None): address = { 'type': 'inet', 'data': { 'host': 'localhost', 'port': str(NBD_PORT) } } - self._server_up(address) - self.client_test('nbd://localhost:%i/nbd-export' % NBD_PORT, - flatten_sock_addr(address), 'nbd-export') + self._server_up(address, export_name) + export_name = export_name or 'nbd-export' + self.client_test('nbd://localhost:%i/%s' % (NBD_PORT, export_name), + flatten_sock_addr(address), export_name) + self._server_down() + + def test_inet_default_export_name(self): + self.do_test_inet() + + def test_inet_same_export_name(self): + self.do_test_inet('nbd-export') + + def test_inet_different_export_name(self): + self.do_test_inet('shadow') + + def test_inet_two_exports(self): + address = { 'type': 'inet', + 'data': { + 'host': 'localhost', + 'port': str(NBD_PORT) + } } + self._server_up(address, 'exp1', 'exp2') + self.client_test('nbd://localhost:%i/%s' % (NBD_PORT, 'exp1'), + flatten_sock_addr(address), 'exp1', 'node1', False) + self.client_test('nbd://localhost:%i/%s' % (NBD_PORT, 'exp2'), + flatten_sock_addr(address), 'exp2', 'node2', False) + result = self.vm.qmp('blockdev-del', node_name='node1') + self.assert_qmp(result, 'return', {}) + result = self.vm.qmp('blockdev-del', node_name='node2') + self.assert_qmp(result, 'return', {}) self._server_down() def test_inet6(self): diff --git a/tests/qemu-iotests/147.out b/tests/qemu-iotests/147.out index 3f8a935a08..dae404e278 100644 --- a/tests/qemu-iotests/147.out +++ b/tests/qemu-iotests/147.out @@ -1,5 +1,5 @@ -...... +......... ---------------------------------------------------------------------- -Ran 6 tests +Ran 9 tests OK diff --git a/tests/qemu-iotests/205 b/tests/qemu-iotests/205 new file mode 100644 index 0000000000..10388920dc --- /dev/null +++ b/tests/qemu-iotests/205 @@ -0,0 +1,156 @@ +#!/usr/bin/env python +# +# Tests for qmp command nbd-server-remove. +# +# Copyright (c) 2017 Virtuozzo International GmbH +# +# 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 sys +import iotests +import time +from iotests import qemu_img, qemu_io, filter_qemu_io, QemuIoInteractive + +nbd_sock = 'nbd_sock' +nbd_uri = 'nbd+unix:///exp?socket=' + nbd_sock +disk = os.path.join(iotests.test_dir, 'disk') + + +class TestNbdServerRemove(iotests.QMPTestCase): + def setUp(self): + qemu_img('create', '-f', iotests.imgfmt, disk, '1M') + + self.vm = iotests.VM().add_drive(disk) + self.vm.launch() + + address = { + 'type': 'unix', + 'data': { + 'path': nbd_sock + } + } + + result = self.vm.qmp('nbd-server-start', addr=address) + self.assert_qmp(result, 'return', {}) + result = self.vm.qmp('nbd-server-add', device='drive0', name='exp') + self.assert_qmp(result, 'return', {}) + + def tearDown(self): + self.vm.shutdown() + os.remove(nbd_sock) + os.remove(disk) + + def remove_export(self, name, mode=None): + if mode is None: + return self.vm.qmp('nbd-server-remove', name=name) + else: + return self.vm.qmp('nbd-server-remove', name=name, mode=mode) + + def assertExportNotFound(self, name): + result = self.vm.qmp('nbd-server-remove', name=name) + self.assert_qmp(result, 'error/desc', "Export 'exp' is not found") + + def assertExistingClients(self, result): + self.assert_qmp(result, 'error/desc', "export 'exp' still in use") + + def assertReadOk(self, qemu_io_output): + self.assertEqual( + filter_qemu_io(qemu_io_output).strip(), + 'read 512/512 bytes at offset 0\n' + + '512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)') + + def assertReadFailed(self, qemu_io_output): + self.assertEqual(filter_qemu_io(qemu_io_output).strip(), + 'read failed: Input/output error') + + def assertConnectFailed(self, qemu_io_output): + self.assertEqual(filter_qemu_io(qemu_io_output).strip(), + "can't open device " + nbd_uri + + ": Requested export not available\n" + "server reported: export 'exp' not present") + + def do_test_connect_after_remove(self, mode=None): + args = ('-r', '-f', 'raw', '-c', 'read 0 512', nbd_uri) + self.assertReadOk(qemu_io(*args)) + + result = self.remove_export('exp', mode) + self.assert_qmp(result, 'return', {}) + + self.assertExportNotFound('exp') + self.assertConnectFailed(qemu_io(*args)) + + def test_connect_after_remove_default(self): + self.do_test_connect_after_remove() + + def test_connect_after_remove_safe(self): + self.do_test_connect_after_remove('safe') + + def test_connect_after_remove_force(self): + self.do_test_connect_after_remove('hard') + + def do_test_remove_during_connect_safe(self, mode=None): + qio = QemuIoInteractive('-r', '-f', 'raw', nbd_uri) + self.assertReadOk(qio.cmd('read 0 512')) + + result = self.remove_export('exp', mode) + self.assertExistingClients(result) + + self.assertReadOk(qio.cmd('read 0 512')) + + qio.close() + + result = self.remove_export('exp', mode) + self.assert_qmp(result, 'return', {}) + + self.assertExportNotFound('exp') + + def test_remove_during_connect_default(self): + self.do_test_remove_during_connect_safe() + + def test_remove_during_connect_safe(self): + self.do_test_remove_during_connect_safe('safe') + + def test_remove_during_connect_hard(self): + qio = QemuIoInteractive('-r', '-f', 'raw', nbd_uri) + self.assertReadOk(qio.cmd('read 0 512')) + + result = self.remove_export('exp', 'hard') + self.assert_qmp(result, 'return', {}) + + self.assertReadFailed(qio.cmd('read 0 512')) + self.assertExportNotFound('exp') + + qio.close() + + def test_remove_during_connect_safe_hard(self): + qio = QemuIoInteractive('-r', '-f', 'raw', nbd_uri) + self.assertReadOk(qio.cmd('read 0 512')) + + result = self.remove_export('exp', 'safe') + self.assertExistingClients(result) + + self.assertReadOk(qio.cmd('read 0 512')) + + result = self.remove_export('exp', 'hard') + self.assert_qmp(result, 'return', {}) + + self.assertExportNotFound('exp') + self.assertReadFailed(qio.cmd('read 0 512')) + qio.close() + + +if __name__ == '__main__': + iotests.main() diff --git a/tests/qemu-iotests/205.out b/tests/qemu-iotests/205.out new file mode 100644 index 0000000000..2f7d3902f2 --- /dev/null +++ b/tests/qemu-iotests/205.out @@ -0,0 +1,5 @@ +....... +---------------------------------------------------------------------- +Ran 7 tests + +OK diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 8fc4f62cca..a2dfe79d86 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -201,3 +201,4 @@ 202 rw auto quick 203 rw auto 204 rw auto quick +205 rw auto quick diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index 44477e9295..5a10b2d534 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -93,6 +93,44 @@ def qemu_io(*args): sys.stderr.write('qemu-io received signal %i: %s\n' % (-exitcode, ' '.join(args))) return subp.communicate()[0] + +class QemuIoInteractive: + def __init__(self, *args): + self.args = qemu_io_args + list(args) + self._p = subprocess.Popen(self.args, stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + assert self._p.stdout.read(9) == 'qemu-io> ' + + def close(self): + self._p.communicate('q\n') + + def _read_output(self): + pattern = 'qemu-io> ' + n = len(pattern) + pos = 0 + s = [] + while pos != n: + c = self._p.stdout.read(1) + # check unexpected EOF + assert c != '' + s.append(c) + if c == pattern[pos]: + pos += 1 + else: + pos = 0 + + return ''.join(s[:-n]) + + def cmd(self, cmd): + # quit command is in close(), '\n' is added automatically + assert '\n' not in cmd + cmd = cmd.strip() + assert cmd != 'q' and cmd != 'quit' + self._p.stdin.write(cmd + '\n') + return self._read_output() + + 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)) |