diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2014-11-03 12:31:07 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2014-11-03 12:31:07 +0000 |
commit | f67d23b1ae32b97064fa8f05330efbb09e5d4b07 (patch) | |
tree | 303cea847d5dc30347b385cc156a860262e1c7c7 /hw | |
parent | 0a2923f8488498000eec54871456aa64a4391da4 (diff) | |
parent | ace386b4e0e088ed1d42fba697fbb68219aceee6 (diff) | |
download | qemu-f67d23b1ae32b97064fa8f05330efbb09e5d4b07.zip |
Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream' into staging
The last round of patches for soft freeze. Includes ivshmem bugfixes,
megasas 2108 emulation, and other small patches here and there.
# gpg: Signature made Fri 31 Oct 2014 17:17:54 GMT using RSA key ID 78C7AE83
# gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>"
# gpg: aka "Paolo Bonzini <pbonzini@redhat.com>"
# gpg: WARNING: This key is not certified with sufficiently trusted signatures!
# gpg: It is not certain that the signature belongs to the owner.
# Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4 E2F7 7E15 100C CD36 69B1
# Subkey fingerprint: F133 3857 4B66 2389 866C 7682 BFFB D25F 78C7 AE83
* remotes/bonzini/tags/for-upstream: (35 commits)
virtio-scsi: fix dataplane
ivshmem: use error_report
ivshmem: Fix fd leak on error
ivshmem: Fix potential OOB r/w access
ivshmem: validate incoming_posn value from server
ivshmem: Check ivshmem_read() size argument
i386: fix breakpoints handling in icount mode
kvm_stat: Add powerpc support
kvm_stat: Abstract ioctl numbers
kvm_stat: Rework platform detection
kvm_stat: Fix the non-x86 exit reasons
kvm_stat: Only consider online cpus
virtio-scsi: Fix num_queue input validation
scsi: devirtualize unrealize of SCSI devices
virtio-scsi: Fix memory leak when realize failed
iscsi: Refuse to open as writable if the LUN is write protected
kvmvapic: patch_instruction fix
vl.c: Fix Coverity complaining for vmstate_dump_file
Add skip_dump flag to ignore memory region during dump
-machine vmport=off: Allow disabling of VMWare ioport emulation
...
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw')
-rw-r--r-- | hw/i386/kvmvapic.c | 1 | ||||
-rw-r--r-- | hw/i386/pc.c | 19 | ||||
-rw-r--r-- | hw/i386/pc_piix.c | 4 | ||||
-rw-r--r-- | hw/i386/pc_q35.c | 3 | ||||
-rw-r--r-- | hw/misc/ivshmem.c | 118 | ||||
-rw-r--r-- | hw/misc/vfio.c | 1 | ||||
-rw-r--r-- | hw/scsi/megasas.c | 574 | ||||
-rw-r--r-- | hw/scsi/mfi.h | 16 | ||||
-rw-r--r-- | hw/scsi/scsi-bus.c | 71 | ||||
-rw-r--r-- | hw/scsi/scsi-disk.c | 18 | ||||
-rw-r--r-- | hw/scsi/scsi-generic.c | 7 | ||||
-rw-r--r-- | hw/scsi/virtio-scsi-dataplane.c | 3 | ||||
-rw-r--r-- | hw/scsi/virtio-scsi.c | 8 |
13 files changed, 595 insertions, 248 deletions
diff --git a/hw/i386/kvmvapic.c b/hw/i386/kvmvapic.c index 2dc362b88f..c6d34b2546 100644 --- a/hw/i386/kvmvapic.c +++ b/hw/i386/kvmvapic.c @@ -405,7 +405,6 @@ static void patch_instruction(VAPICROMState *s, X86CPU *cpu, target_ulong ip) } if (!kvm_enabled()) { - cpu_restore_state(cs, cs->mem_io_pc); cpu_get_tb_cpu_state(env, ¤t_pc, ¤t_cs_base, ¤t_flags); } diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 61aba9fc54..889e888c6a 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1688,6 +1688,20 @@ static void pc_machine_set_max_ram_below_4g(Object *obj, Visitor *v, pcms->max_ram_below_4g = value; } +static bool pc_machine_get_vmport(Object *obj, Error **errp) +{ + PCMachineState *pcms = PC_MACHINE(obj); + + return pcms->vmport; +} + +static void pc_machine_set_vmport(Object *obj, bool value, Error **errp) +{ + PCMachineState *pcms = PC_MACHINE(obj); + + pcms->vmport = value; +} + static void pc_machine_initfn(Object *obj) { PCMachineState *pcms = PC_MACHINE(obj); @@ -1700,6 +1714,11 @@ static void pc_machine_initfn(Object *obj) pc_machine_get_max_ram_below_4g, pc_machine_set_max_ram_below_4g, NULL, NULL, NULL); + pcms->vmport = !xen_enabled(); + object_property_add_bool(obj, PC_MACHINE_VMPORT, + pc_machine_get_vmport, + pc_machine_set_vmport, + NULL); } static void pc_machine_class_init(ObjectClass *oc, void *data) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 91a20cb8f4..1cda5dd068 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -234,8 +234,8 @@ static void pc_init1(MachineState *machine, pc_vga_init(isa_bus, pci_enabled ? pci_bus : NULL); /* init basic PC hardware */ - pc_basic_device_init(isa_bus, gsi, &rtc_state, &floppy, xen_enabled(), - 0x4); + pc_basic_device_init(isa_bus, gsi, &rtc_state, &floppy, + !pc_machine->vmport, 0x4); pc_nic_init(isa_bus, pci_bus); diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index e225c6d110..4d9e3cd69d 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -242,7 +242,8 @@ static void pc_q35_init(MachineState *machine) pc_register_ferr_irq(gsi[13]); /* init basic PC hardware */ - pc_basic_device_init(isa_bus, gsi, &rtc_state, &floppy, false, 0xff0104); + pc_basic_device_init(isa_bus, gsi, &rtc_state, &floppy, + !pc_machine->vmport, 0xff0104); /* connect pm stuff to lpc */ ich9_lpc_pm_init(lpc); diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c index bd9d7182af..5d272c84e9 100644 --- a/hw/misc/ivshmem.c +++ b/hw/misc/ivshmem.c @@ -24,10 +24,12 @@ #include "migration/migration.h" #include "qapi/qmp/qerror.h" #include "qemu/event_notifier.h" +#include "qemu/fifo8.h" #include "sysemu/char.h" #include <sys/mman.h> #include <sys/types.h> +#include <limits.h> #define PCI_VENDOR_ID_IVSHMEM PCI_VENDOR_ID_REDHAT_QUMRANET #define PCI_DEVICE_ID_IVSHMEM 0x1110 @@ -73,6 +75,7 @@ typedef struct IVShmemState { CharDriverState **eventfd_chr; CharDriverState *server_chr; + Fifo8 incoming_fifo; MemoryRegion ivshmem_mmio; /* We might need to register the BAR before we actually have the memory. @@ -130,7 +133,7 @@ static void ivshmem_update_irq(IVShmemState *s, int val) /* don't print ISR resets */ if (isr) { IVSHMEM_DPRINTF("Set IRQ to %d (%04x %04x)\n", - isr ? 1 : 0, s->intrstatus, s->intrmask); + isr ? 1 : 0, s->intrstatus, s->intrmask); } pci_set_irq(d, (isr != 0)); @@ -297,8 +300,8 @@ static CharDriverState* create_eventfd_chr_device(void * opaque, EventNotifier * chr = qemu_chr_open_eventfd(eventfd); if (chr == NULL) { - fprintf(stderr, "creating eventfd for eventfd %d failed\n", eventfd); - exit(-1); + error_report("creating eventfd for eventfd %d failed", eventfd); + exit(1); } qemu_chr_fe_claim_no_fail(chr); @@ -325,16 +328,15 @@ static int check_shm_size(IVShmemState *s, int fd) { struct stat buf; if (fstat(fd, &buf) < 0) { - fprintf(stderr, "ivshmem: exiting: fstat on fd %d failed: %s\n", - fd, strerror(errno)); + error_report("exiting: fstat on fd %d failed: %s", + fd, strerror(errno)); return -1; } if (s->ivshmem_size > buf.st_size) { - fprintf(stderr, - "IVSHMEM ERROR: Requested memory size greater" - " than shared object size (%" PRIu64 " > %" PRIu64")\n", - s->ivshmem_size, (uint64_t)buf.st_size); + error_report("Requested memory size greater" + " than shared object size (%" PRIu64 " > %" PRIu64")", + s->ivshmem_size, (uint64_t)buf.st_size); return -1; } else { return 0; @@ -387,6 +389,9 @@ static void close_guest_eventfds(IVShmemState *s, int posn) if (!ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) { return; } + if (posn < 0 || posn >= s->nb_peers) { + return; + } guest_curr_max = s->peers[posn].nb_eventfds; @@ -405,14 +410,24 @@ static void close_guest_eventfds(IVShmemState *s, int posn) /* this function increase the dynamic storage need to store data about other * guests */ -static void increase_dynamic_storage(IVShmemState *s, int new_min_size) { +static int increase_dynamic_storage(IVShmemState *s, int new_min_size) +{ int j, old_nb_alloc; + /* check for integer overflow */ + if (new_min_size >= INT_MAX / sizeof(Peer) - 1 || new_min_size <= 0) { + return -1; + } + old_nb_alloc = s->nb_peers; - while (new_min_size >= s->nb_peers) - s->nb_peers = s->nb_peers * 2; + if (new_min_size >= s->nb_peers) { + /* +1 because #new_min_size is used as last array index */ + s->nb_peers = new_min_size + 1; + } else { + return 0; + } IVSHMEM_DPRINTF("bumping storage to %d guests\n", s->nb_peers); s->peers = g_realloc(s->peers, s->nb_peers * sizeof(Peer)); @@ -422,23 +437,57 @@ static void increase_dynamic_storage(IVShmemState *s, int new_min_size) { s->peers[j].eventfds = NULL; s->peers[j].nb_eventfds = 0; } + + return 0; } -static void ivshmem_read(void *opaque, const uint8_t * buf, int flags) +static void ivshmem_read(void *opaque, const uint8_t *buf, int size) { IVShmemState *s = opaque; int incoming_fd, tmp_fd; int guest_max_eventfd; long incoming_posn; - memcpy(&incoming_posn, buf, sizeof(long)); + if (fifo8_is_empty(&s->incoming_fifo) && size == sizeof(incoming_posn)) { + memcpy(&incoming_posn, buf, size); + } else { + const uint8_t *p; + uint32_t num; + + IVSHMEM_DPRINTF("short read of %d bytes\n", size); + num = MAX(size, sizeof(long) - fifo8_num_used(&s->incoming_fifo)); + fifo8_push_all(&s->incoming_fifo, buf, num); + if (fifo8_num_used(&s->incoming_fifo) < sizeof(incoming_posn)) { + return; + } + size -= num; + buf += num; + p = fifo8_pop_buf(&s->incoming_fifo, sizeof(incoming_posn), &num); + g_assert(num == sizeof(incoming_posn)); + memcpy(&incoming_posn, p, sizeof(incoming_posn)); + if (size > 0) { + fifo8_push_all(&s->incoming_fifo, buf, size); + } + } + + if (incoming_posn < -1) { + IVSHMEM_DPRINTF("invalid incoming_posn %ld\n", incoming_posn); + return; + } + /* pick off s->server_chr->msgfd and store it, posn should accompany msg */ tmp_fd = qemu_chr_fe_get_msgfd(s->server_chr); IVSHMEM_DPRINTF("posn is %ld, fd is %d\n", incoming_posn, tmp_fd); /* make sure we have enough space for this guest */ if (incoming_posn >= s->nb_peers) { - increase_dynamic_storage(s, incoming_posn); + if (increase_dynamic_storage(s, incoming_posn) < 0) { + error_report("increase_dynamic_storage() failed"); + if (tmp_fd != -1) { + close(tmp_fd); + } + return; + } } if (tmp_fd == -1) { @@ -460,8 +509,8 @@ static void ivshmem_read(void *opaque, const uint8_t * buf, int flags) incoming_fd = dup(tmp_fd); if (incoming_fd == -1) { - fprintf(stderr, "could not allocate file descriptor %s\n", - strerror(errno)); + error_report("could not allocate file descriptor %s", strerror(errno)); + close(tmp_fd); return; } @@ -473,7 +522,7 @@ static void ivshmem_read(void *opaque, const uint8_t * buf, int flags) s->max_peer = 0; if (check_shm_size(s, incoming_fd) == -1) { - exit(-1); + exit(1); } /* mmap the region and map into the BAR2 */ @@ -484,7 +533,7 @@ static void ivshmem_read(void *opaque, const uint8_t * buf, int flags) vmstate_register_ram(&s->ivshmem, DEVICE(s)); IVSHMEM_DPRINTF("guest h/w addr = %p, size = %" PRIu64 "\n", - map_ptr, s->ivshmem_size); + map_ptr, s->ivshmem_size); memory_region_add_subregion(&s->bar, 0, &s->ivshmem); @@ -505,7 +554,7 @@ static void ivshmem_read(void *opaque, const uint8_t * buf, int flags) /* this is an eventfd for a particular guest VM */ IVSHMEM_DPRINTF("eventfds[%ld][%d] = %d\n", incoming_posn, - guest_max_eventfd, incoming_fd); + guest_max_eventfd, incoming_fd); event_notifier_init_fd(&s->peers[incoming_posn].eventfds[guest_max_eventfd], incoming_fd); @@ -567,13 +616,13 @@ static uint64_t ivshmem_get_size(IVShmemState * s) { value <<= 30; break; default: - fprintf(stderr, "qemu: invalid ram size: %s\n", s->sizearg); + error_report("invalid ram size: %s", s->sizearg); exit(1); } /* BARs must be a power of 2 */ if (!is_power_of_two(value)) { - fprintf(stderr, "ivshmem: size must be power of 2\n"); + error_report("size must be power of 2"); exit(1); } @@ -625,7 +674,7 @@ static int ivshmem_load(QEMUFile* f, void *opaque, int version_id) } if (proxy->role_val == IVSHMEM_PEER) { - fprintf(stderr, "ivshmem: 'peer' devices are not migratable\n"); + error_report("'peer' devices are not migratable"); return -EINVAL; } @@ -663,13 +712,15 @@ static int pci_ivshmem_init(PCIDevice *dev) s->ivshmem_size = ivshmem_get_size(s); } + fifo8_create(&s->incoming_fifo, sizeof(long)); + register_savevm(DEVICE(dev), "ivshmem", 0, 0, ivshmem_save, ivshmem_load, dev); /* IRQFD requires MSI */ if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD) && !ivshmem_has_feature(s, IVSHMEM_MSI)) { - fprintf(stderr, "ivshmem: ioeventfd/irqfd requires MSI\n"); + error_report("ioeventfd/irqfd requires MSI"); exit(1); } @@ -680,7 +731,7 @@ static int pci_ivshmem_init(PCIDevice *dev) } else if (strncmp(s->role, "master", 7) == 0) { s->role_val = IVSHMEM_MASTER; } else { - fprintf(stderr, "ivshmem: 'role' must be 'peer' or 'master'\n"); + error_report("'role' must be 'peer' or 'master'"); exit(1); } } else { @@ -720,12 +771,12 @@ static int pci_ivshmem_init(PCIDevice *dev) * to the ivshmem server to receive the memory region */ if (s->shmobj != NULL) { - fprintf(stderr, "WARNING: do not specify both 'chardev' " - "and 'shm' with ivshmem\n"); + error_report("WARNING: do not specify both 'chardev' " + "and 'shm' with ivshmem"); } IVSHMEM_DPRINTF("using shared memory server (socket = %s)\n", - s->server_chr->filename); + s->server_chr->filename); if (ivshmem_has_feature(s, IVSHMEM_MSI)) { ivshmem_setup_msi(s); @@ -749,7 +800,7 @@ static int pci_ivshmem_init(PCIDevice *dev) int fd; if (s->shmobj == NULL) { - fprintf(stderr, "Must specify 'chardev' or 'shm' to ivshmem\n"); + error_report("Must specify 'chardev' or 'shm' to ivshmem"); exit(1); } @@ -761,18 +812,18 @@ static int pci_ivshmem_init(PCIDevice *dev) S_IRWXU|S_IRWXG|S_IRWXO)) > 0) { /* truncate file to length PCI device's memory */ if (ftruncate(fd, s->ivshmem_size) != 0) { - fprintf(stderr, "ivshmem: could not truncate shared file\n"); + error_report("could not truncate shared file"); } } else if ((fd = shm_open(s->shmobj, O_CREAT|O_RDWR, S_IRWXU|S_IRWXG|S_IRWXO)) < 0) { - fprintf(stderr, "ivshmem: could not open shared file\n"); - exit(-1); + error_report("could not open shared file"); + exit(1); } if (check_shm_size(s, fd) == -1) { - exit(-1); + exit(1); } create_shared_memory_BAR(s, fd); @@ -796,6 +847,7 @@ static void pci_ivshmem_uninit(PCIDevice *dev) memory_region_del_subregion(&s->bar, &s->ivshmem); vmstate_unregister_ram(&s->ivshmem, DEVICE(dev)); unregister_savevm(DEVICE(dev), "ivshmem", s); + fifo8_destroy(&s->incoming_fifo); } static Property ivshmem_properties[] = { diff --git a/hw/misc/vfio.c b/hw/misc/vfio.c index b5e798173b..fd318a122d 100644 --- a/hw/misc/vfio.c +++ b/hw/misc/vfio.c @@ -2911,6 +2911,7 @@ static int vfio_mmap_bar(VFIODevice *vdev, VFIOBAR *bar, } memory_region_init_ram_ptr(submem, OBJECT(vdev), name, size, *map); + memory_region_set_skip_dump(submem); } else { empty_region: /* Create a zero sized sub-region to make cleanup easy. */ diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index 36a04f31bb..604252a198 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -31,9 +31,11 @@ #include "mfi.h" -#define MEGASAS_VERSION "1.70" +#define MEGASAS_VERSION_GEN1 "1.70" +#define MEGASAS_VERSION_GEN2 "1.80" #define MEGASAS_MAX_FRAMES 2048 /* Firmware limit at 65535 */ #define MEGASAS_DEFAULT_FRAMES 1000 /* Windows requires this */ +#define MEGASAS_GEN2_DEFAULT_FRAMES 1008 /* Windows requires this */ #define MEGASAS_MAX_SGE 128 /* Firmware limit */ #define MEGASAS_DEFAULT_SGE 80 #define MEGASAS_MAX_SECTORS 0xFFFF /* No real limit */ @@ -91,6 +93,8 @@ typedef struct MegasasState { int intr_mask; int doorbell; int busy; + int diag; + int adp_reset; MegasasCmd *event_cmd; int event_locale; @@ -111,14 +115,30 @@ typedef struct MegasasState { uint64_t producer_pa; MegasasCmd frames[MEGASAS_MAX_FRAMES]; - + DECLARE_BITMAP(frame_map, MEGASAS_MAX_FRAMES); SCSIBus bus; } MegasasState; -#define TYPE_MEGASAS "megasas" +typedef struct MegasasBaseClass { + PCIDeviceClass parent_class; + const char *product_name; + const char *product_version; + int mmio_bar; + int ioport_bar; + int osts; +} MegasasBaseClass; + +#define TYPE_MEGASAS_BASE "megasas-base" +#define TYPE_MEGASAS_GEN1 "megasas" +#define TYPE_MEGASAS_GEN2 "megasas-gen2" #define MEGASAS(obj) \ - OBJECT_CHECK(MegasasState, (obj), TYPE_MEGASAS) + OBJECT_CHECK(MegasasState, (obj), TYPE_MEGASAS_BASE) + +#define MEGASAS_DEVICE_CLASS(oc) \ + OBJECT_CLASS_CHECK(MegasasBaseClass, (oc), TYPE_MEGASAS_BASE) +#define MEGASAS_DEVICE_GET_CLASS(oc) \ + OBJECT_GET_CLASS(MegasasBaseClass, (oc), TYPE_MEGASAS_BASE) #define MEGASAS_INTR_DISABLED_MASK 0xFFFFFFFF @@ -443,34 +463,20 @@ static MegasasCmd *megasas_lookup_frame(MegasasState *s, return cmd; } -static MegasasCmd *megasas_next_frame(MegasasState *s, - hwaddr frame) +static void megasas_unmap_frame(MegasasState *s, MegasasCmd *cmd) { - MegasasCmd *cmd = NULL; - int num = 0, index; + PCIDevice *p = PCI_DEVICE(s); - cmd = megasas_lookup_frame(s, frame); - if (cmd) { - trace_megasas_qf_found(cmd->index, cmd->pa); - return cmd; - } - index = s->reply_queue_head; - num = 0; - while (num < s->fw_cmds) { - if (!s->frames[index].pa) { - cmd = &s->frames[index]; - break; - } - index = megasas_next_index(s, index, s->fw_cmds); - num++; - } - if (!cmd) { - trace_megasas_qf_failed(frame); - } - trace_megasas_qf_new(index, cmd); - return cmd; + pci_dma_unmap(p, cmd->frame, cmd->pa_size, 0, 0); + cmd->frame = NULL; + cmd->pa = 0; + clear_bit(cmd->index, s->frame_map); } +/* + * This absolutely needs to be locked if + * qemu ever goes multithreaded. + */ static MegasasCmd *megasas_enqueue_frame(MegasasState *s, hwaddr frame, uint64_t context, int count) { @@ -478,37 +484,50 @@ static MegasasCmd *megasas_enqueue_frame(MegasasState *s, MegasasCmd *cmd = NULL; int frame_size = MFI_FRAME_SIZE * 16; hwaddr frame_size_p = frame_size; + unsigned long index; - cmd = megasas_next_frame(s, frame); - /* All frames busy */ - if (!cmd) { + index = 0; + while (index < s->fw_cmds) { + index = find_next_zero_bit(s->frame_map, s->fw_cmds, index); + if (!s->frames[index].pa) + break; + /* Busy frame found */ + trace_megasas_qf_mapped(index); + } + if (index >= s->fw_cmds) { + /* All frames busy */ + trace_megasas_qf_busy(frame); return NULL; } - if (!cmd->pa) { - cmd->pa = frame; - /* Map all possible frames */ - cmd->frame = pci_dma_map(pcid, frame, &frame_size_p, 0); - if (frame_size_p != frame_size) { - trace_megasas_qf_map_failed(cmd->index, (unsigned long)frame); - if (cmd->frame) { - pci_dma_unmap(pcid, cmd->frame, frame_size_p, 0, 0); - cmd->frame = NULL; - cmd->pa = 0; - } - s->event_count++; - return NULL; - } - cmd->pa_size = frame_size_p; - cmd->context = context; - if (!megasas_use_queue64(s)) { - cmd->context &= (uint64_t)0xFFFFFFFF; + cmd = &s->frames[index]; + set_bit(index, s->frame_map); + trace_megasas_qf_new(index, frame); + + cmd->pa = frame; + /* Map all possible frames */ + cmd->frame = pci_dma_map(pcid, frame, &frame_size_p, 0); + if (frame_size_p != frame_size) { + trace_megasas_qf_map_failed(cmd->index, (unsigned long)frame); + if (cmd->frame) { + megasas_unmap_frame(s, cmd); } + s->event_count++; + return NULL; + } + cmd->pa_size = frame_size_p; + cmd->context = context; + if (!megasas_use_queue64(s)) { + cmd->context &= (uint64_t)0xFFFFFFFF; } cmd->count = count; s->busy++; + if (s->consumer_pa) { + s->reply_queue_tail = ldl_le_phys(&address_space_memory, + s->consumer_pa); + } trace_megasas_qf_enqueue(cmd->index, cmd->count, cmd->context, - s->reply_queue_head, s->busy); + s->reply_queue_head, s->reply_queue_tail, s->busy); return cmd; } @@ -520,39 +539,47 @@ static void megasas_complete_frame(MegasasState *s, uint64_t context) /* Decrement busy count */ s->busy--; - if (s->reply_queue_pa) { /* * Put command on the reply queue. * Context is opaque, but emulation is running in * little endian. So convert it. */ - tail = s->reply_queue_head; if (megasas_use_queue64(s)) { - queue_offset = tail * sizeof(uint64_t); + queue_offset = s->reply_queue_head * sizeof(uint64_t); stq_le_phys(&address_space_memory, s->reply_queue_pa + queue_offset, context); } else { - queue_offset = tail * sizeof(uint32_t); + queue_offset = s->reply_queue_head * sizeof(uint32_t); stl_le_phys(&address_space_memory, s->reply_queue_pa + queue_offset, context); } - s->reply_queue_head = megasas_next_index(s, tail, s->fw_cmds); - trace_megasas_qf_complete(context, tail, queue_offset, - s->busy, s->doorbell); + s->reply_queue_tail = ldl_le_phys(&address_space_memory, + s->consumer_pa); + trace_megasas_qf_complete(context, s->reply_queue_head, + s->reply_queue_tail, s->busy); } if (megasas_intr_enabled(s)) { + /* Update reply queue pointer */ + s->reply_queue_tail = ldl_le_phys(&address_space_memory, + s->consumer_pa); + tail = s->reply_queue_head; + s->reply_queue_head = megasas_next_index(s, tail, s->fw_cmds); + trace_megasas_qf_update(s->reply_queue_head, s->reply_queue_tail, + s->busy); + stl_le_phys(&address_space_memory, + s->producer_pa, s->reply_queue_head); /* Notify HBA */ - s->doorbell++; - if (s->doorbell == 1) { - if (msix_enabled(pci_dev)) { - trace_megasas_msix_raise(0); - msix_notify(pci_dev, 0); - } else if (msi_enabled(pci_dev)) { - trace_megasas_msi_raise(0); - msi_notify(pci_dev, 0); - } else { + if (msix_enabled(pci_dev)) { + trace_megasas_msix_raise(0); + msix_notify(pci_dev, 0); + } else if (msi_enabled(pci_dev)) { + trace_megasas_msi_raise(0); + msi_notify(pci_dev, 0); + } else { + s->doorbell++; + if (s->doorbell == 1) { trace_megasas_irq_raise(); pci_irq_assert(pci_dev); } @@ -564,18 +591,16 @@ static void megasas_complete_frame(MegasasState *s, uint64_t context) static void megasas_reset_frames(MegasasState *s) { - PCIDevice *pcid = PCI_DEVICE(s); int i; MegasasCmd *cmd; for (i = 0; i < s->fw_cmds; i++) { cmd = &s->frames[i]; if (cmd->pa) { - pci_dma_unmap(pcid, cmd->frame, cmd->pa_size, 0, 0); - cmd->frame = NULL; - cmd->pa = 0; + megasas_unmap_frame(s, cmd); } } + bitmap_zero(s->frame_map, MEGASAS_MAX_FRAMES); } static void megasas_abort_command(MegasasCmd *cmd) @@ -590,16 +615,19 @@ static int megasas_init_firmware(MegasasState *s, MegasasCmd *cmd) { PCIDevice *pcid = PCI_DEVICE(s); uint32_t pa_hi, pa_lo; - hwaddr iq_pa, initq_size; - struct mfi_init_qinfo *initq; + hwaddr iq_pa, initq_size = sizeof(struct mfi_init_qinfo); + struct mfi_init_qinfo *initq = NULL; uint32_t flags; int ret = MFI_STAT_OK; + if (s->reply_queue_pa) { + trace_megasas_initq_mapped(s->reply_queue_pa); + goto out; + } pa_lo = le32_to_cpu(cmd->frame->init.qinfo_new_addr_lo); pa_hi = le32_to_cpu(cmd->frame->init.qinfo_new_addr_hi); iq_pa = (((uint64_t) pa_hi << 32) | pa_lo); trace_megasas_init_firmware((uint64_t)iq_pa); - initq_size = sizeof(*initq); initq = pci_dma_map(pcid, iq_pa, &initq_size, 0); if (!initq || initq_size != sizeof(*initq)) { trace_megasas_initq_map_failed(cmd->index); @@ -686,11 +714,12 @@ static void megasas_finish_dcmd(MegasasCmd *cmd, uint32_t iov_size) static int megasas_ctrl_get_info(MegasasState *s, MegasasCmd *cmd) { PCIDevice *pci_dev = PCI_DEVICE(s); + PCIDeviceClass *pci_class = PCI_DEVICE_GET_CLASS(pci_dev); + MegasasBaseClass *base_class = MEGASAS_DEVICE_GET_CLASS(s); struct mfi_ctrl_info info; size_t dcmd_size = sizeof(info); BusChild *kid; - int num_ld_disks = 0; - uint16_t sdev_id; + int num_pd_disks = 0; memset(&info, 0x0, cmd->iov_size); if (cmd->iov_size < dcmd_size) { @@ -699,10 +728,10 @@ static int megasas_ctrl_get_info(MegasasState *s, MegasasCmd *cmd) return MFI_STAT_INVALID_PARAMETER; } - info.pci.vendor = cpu_to_le16(PCI_VENDOR_ID_LSI_LOGIC); - info.pci.device = cpu_to_le16(PCI_DEVICE_ID_LSI_SAS1078); - info.pci.subvendor = cpu_to_le16(PCI_VENDOR_ID_LSI_LOGIC); - info.pci.subdevice = cpu_to_le16(0x1013); + info.pci.vendor = cpu_to_le16(pci_class->vendor_id); + info.pci.device = cpu_to_le16(pci_class->device_id); + info.pci.subvendor = cpu_to_le16(pci_class->subsystem_vendor_id); + info.pci.subdevice = cpu_to_le16(pci_class->subsystem_id); /* * For some reason the firmware supports @@ -719,20 +748,22 @@ static int megasas_ctrl_get_info(MegasasState *s, MegasasCmd *cmd) info.device.port_count = 8; QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child); + uint16_t pd_id; - if (num_ld_disks < 8) { - sdev_id = ((sdev->id & 0xFF) >> 8) | (sdev->lun & 0xFF); - info.device.port_addr[num_ld_disks] = - cpu_to_le64(megasas_get_sata_addr(sdev_id)); + if (num_pd_disks < 8) { + pd_id = ((sdev->id & 0xFF) << 8) | (sdev->lun & 0xFF); + info.device.port_addr[num_pd_disks] = + cpu_to_le64(megasas_get_sata_addr(pd_id)); } - num_ld_disks++; + num_pd_disks++; } - memcpy(info.product_name, "MegaRAID SAS 8708EM2", 20); + memcpy(info.product_name, base_class->product_name, 24); snprintf(info.serial_number, 32, "%s", s->hba_serial); snprintf(info.package_version, 0x60, "%s-QEMU", QEMU_VERSION); memcpy(info.image_component[0].name, "APP", 3); - memcpy(info.image_component[0].version, MEGASAS_VERSION "-QEMU", 9); + snprintf(info.image_component[0].version, 10, "%s-QEMU", + base_class->product_version); memcpy(info.image_component[0].build_date, "Apr 1 2014", 11); memcpy(info.image_component[0].build_time, "12:34:56", 8); info.image_component_count = 1; @@ -751,13 +782,14 @@ static int megasas_ctrl_get_info(MegasasState *s, MegasasCmd *cmd) info.max_arms = 32; info.max_spans = 8; info.max_arrays = MEGASAS_MAX_ARRAYS; - info.max_lds = s->fw_luns; + info.max_lds = MFI_MAX_LD; info.max_cmds = cpu_to_le16(s->fw_cmds); info.max_sg_elements = cpu_to_le16(s->fw_sge); info.max_request_size = cpu_to_le32(MEGASAS_MAX_SECTORS); - info.lds_present = cpu_to_le16(num_ld_disks); - info.pd_present = cpu_to_le16(num_ld_disks); - info.pd_disks_present = cpu_to_le16(num_ld_disks); + if (!megasas_is_jbod(s)) + info.lds_present = cpu_to_le16(num_pd_disks); + info.pd_present = cpu_to_le16(num_pd_disks); + info.pd_disks_present = cpu_to_le16(num_pd_disks); info.hw_present = cpu_to_le32(MFI_INFO_HW_NVRAM | MFI_INFO_HW_MEM | MFI_INFO_HW_FLASH); @@ -916,7 +948,6 @@ static int megasas_dcmd_pd_get_list(MegasasState *s, MegasasCmd *cmd) size_t dcmd_size = sizeof(info); BusChild *kid; uint32_t offset, dcmd_limit, num_pd_disks = 0, max_pd_disks; - uint16_t sdev_id; memset(&info, 0, dcmd_size); offset = 8; @@ -928,22 +959,25 @@ static int megasas_dcmd_pd_get_list(MegasasState *s, MegasasCmd *cmd) } max_pd_disks = (cmd->iov_size - offset) / sizeof(struct mfi_pd_address); - if (max_pd_disks > s->fw_luns) { - max_pd_disks = s->fw_luns; + if (max_pd_disks > MFI_MAX_SYS_PDS) { + max_pd_disks = MFI_MAX_SYS_PDS; } - QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child); + uint16_t pd_id; + + if (num_pd_disks >= max_pd_disks) + break; - sdev_id = ((sdev->id & 0xFF) >> 8) | (sdev->lun & 0xFF); - info.addr[num_pd_disks].device_id = cpu_to_le16(sdev_id); + pd_id = ((sdev->id & 0xFF) << 8) | (sdev->lun & 0xFF); + info.addr[num_pd_disks].device_id = cpu_to_le16(pd_id); info.addr[num_pd_disks].encl_device_id = 0xFFFF; info.addr[num_pd_disks].encl_index = 0; - info.addr[num_pd_disks].slot_number = (sdev->id & 0xFF); + info.addr[num_pd_disks].slot_number = sdev->id & 0xFF; info.addr[num_pd_disks].scsi_dev_type = sdev->type; info.addr[num_pd_disks].connect_port_bitmap = 0x1; info.addr[num_pd_disks].sas_addr[0] = - cpu_to_le64(megasas_get_sata_addr(sdev_id)); + cpu_to_le64(megasas_get_sata_addr(pd_id)); num_pd_disks++; offset += sizeof(struct mfi_pd_address); } @@ -978,7 +1012,7 @@ static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun, struct mfi_pd_info *info = cmd->iov_buf; size_t dcmd_size = sizeof(struct mfi_pd_info); uint64_t pd_size; - uint16_t sdev_id = ((sdev->id & 0xFF) >> 8) | (lun & 0xFF); + uint16_t pd_id = ((sdev->id & 0xFF) << 8) | (lun & 0xFF); uint8_t cmdbuf[6]; SCSIRequest *req; size_t len, resid; @@ -1034,7 +1068,7 @@ static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun, info->fw_state = cpu_to_le16(MFI_PD_STATE_OFFLINE); } - info->ref.v.device_id = cpu_to_le16(sdev_id); + info->ref.v.device_id = cpu_to_le16(pd_id); info->state.ddf.pd_type = cpu_to_le16(MFI_PD_DDF_TYPE_IN_VD| MFI_PD_DDF_TYPE_INTF_SAS); blk_get_geometry(sdev->conf.blk, &pd_size); @@ -1045,7 +1079,7 @@ static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun, info->slot_number = (sdev->id & 0xFF); info->path_info.count = 1; info->path_info.sas_addr[0] = - cpu_to_le64(megasas_get_sata_addr(sdev_id)); + cpu_to_le64(megasas_get_sata_addr(pd_id)); info->connected_port_bitmap = 0x1; info->device_speed = 1; info->link_speed = 1; @@ -1060,6 +1094,7 @@ static int megasas_dcmd_pd_get_info(MegasasState *s, MegasasCmd *cmd) { size_t dcmd_size = sizeof(struct mfi_pd_info); uint16_t pd_id; + uint8_t target_id, lun_id; SCSIDevice *sdev = NULL; int retval = MFI_STAT_DEVICE_NOT_FOUND; @@ -1069,7 +1104,9 @@ static int megasas_dcmd_pd_get_info(MegasasState *s, MegasasCmd *cmd) /* mbox0 has the ID */ pd_id = le16_to_cpu(cmd->frame->dcmd.mbox[0]); - sdev = scsi_device_find(&s->bus, 0, pd_id, 0); + target_id = (pd_id >> 8) & 0xFF; + lun_id = pd_id & 0xFF; + sdev = scsi_device_find(&s->bus, 0, target_id, lun_id); trace_megasas_dcmd_pd_get_info(cmd->index, pd_id); if (sdev) { @@ -1084,20 +1121,24 @@ static int megasas_dcmd_ld_get_list(MegasasState *s, MegasasCmd *cmd) { struct mfi_ld_list info; size_t dcmd_size = sizeof(info), resid; - uint32_t num_ld_disks = 0, max_ld_disks = s->fw_luns; + uint32_t num_ld_disks = 0, max_ld_disks; uint64_t ld_size; BusChild *kid; memset(&info, 0, dcmd_size); - if (cmd->iov_size < dcmd_size) { + if (cmd->iov_size > dcmd_size) { trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size, dcmd_size); return MFI_STAT_INVALID_PARAMETER; } + max_ld_disks = (cmd->iov_size - 8) / 16; if (megasas_is_jbod(s)) { max_ld_disks = 0; } + if (max_ld_disks > MFI_MAX_LD) { + max_ld_disks = MFI_MAX_LD; + } QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child); @@ -1107,7 +1148,6 @@ static int megasas_dcmd_ld_get_list(MegasasState *s, MegasasCmd *cmd) /* Logical device size is in blocks */ blk_get_geometry(sdev->conf.blk, &ld_size); info.ld_list[num_ld_disks].ld.v.target_id = sdev->id; - info.ld_list[num_ld_disks].ld.v.lun_id = sdev->lun; info.ld_list[num_ld_disks].state = MFI_LD_STATE_OPTIMAL; info.ld_list[num_ld_disks].size = cpu_to_le64(ld_size); num_ld_disks++; @@ -1123,15 +1163,49 @@ static int megasas_dcmd_ld_get_list(MegasasState *s, MegasasCmd *cmd) static int megasas_dcmd_ld_list_query(MegasasState *s, MegasasCmd *cmd) { uint16_t flags; + struct mfi_ld_targetid_list info; + size_t dcmd_size = sizeof(info), resid; + uint32_t num_ld_disks = 0, max_ld_disks = s->fw_luns; + BusChild *kid; /* mbox0 contains flags */ flags = le16_to_cpu(cmd->frame->dcmd.mbox[0]); trace_megasas_dcmd_ld_list_query(cmd->index, flags); - if (flags == MR_LD_QUERY_TYPE_ALL || - flags == MR_LD_QUERY_TYPE_EXPOSED_TO_HOST) { - return megasas_dcmd_ld_get_list(s, cmd); + if (flags != MR_LD_QUERY_TYPE_ALL && + flags != MR_LD_QUERY_TYPE_EXPOSED_TO_HOST) { + max_ld_disks = 0; } + memset(&info, 0, dcmd_size); + if (cmd->iov_size < 12) { + trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size, + dcmd_size); + return MFI_STAT_INVALID_PARAMETER; + } + dcmd_size = sizeof(uint32_t) * 2 + 3; + max_ld_disks = cmd->iov_size - dcmd_size; + if (megasas_is_jbod(s)) { + max_ld_disks = 0; + } + if (max_ld_disks > MFI_MAX_LD) { + max_ld_disks = MFI_MAX_LD; + } + QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { + SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child); + + if (num_ld_disks >= max_ld_disks) { + break; + } + info.targetid[num_ld_disks] = sdev->lun; + num_ld_disks++; + dcmd_size++; + } + info.ld_count = cpu_to_le32(num_ld_disks); + info.size = dcmd_size; + trace_megasas_dcmd_ld_get_list(cmd->index, num_ld_disks, max_ld_disks); + + resid = dma_buf_read((uint8_t *)&info, dcmd_size, &cmd->qsg); + cmd->iov_size = dcmd_size - resid; return MFI_STAT_OK; } @@ -1143,7 +1217,7 @@ static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun, uint8_t cdb[6]; SCSIRequest *req; ssize_t len, resid; - uint16_t sdev_id = ((sdev->id & 0xFF) >> 8) | (lun & 0xFF); + uint16_t sdev_id = ((sdev->id & 0xFF) << 8) | (lun & 0xFF); uint64_t ld_size; if (!cmd->iov_buf) { @@ -1259,7 +1333,7 @@ static int megasas_dcmd_cfg_read(MegasasState *s, MegasasCmd *cmd) QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child); - uint16_t sdev_id = ((sdev->id & 0xFF) >> 8) | (sdev->lun & 0xFF); + uint16_t sdev_id = ((sdev->id & 0xFF) << 8) | (sdev->lun & 0xFF); struct mfi_array *array; struct mfi_ld_config *ld; uint64_t pd_size; @@ -1285,7 +1359,7 @@ static int megasas_dcmd_cfg_read(MegasasState *s, MegasasCmd *cmd) array_offset += sizeof(struct mfi_array); ld = (struct mfi_ld_config *)(data + ld_offset); memset(ld, 0, sizeof(struct mfi_ld_config)); - ld->properties.ld.v.target_id = (sdev->id & 0xFF); + ld->properties.ld.v.target_id = sdev->id; ld->properties.default_cache_policy = MR_LD_CACHE_READ_AHEAD | MR_LD_CACHE_READ_ADAPTIVE; ld->properties.current_cache_policy = MR_LD_CACHE_READ_AHEAD | @@ -1347,9 +1421,23 @@ static int megasas_ctrl_shutdown(MegasasState *s, MegasasCmd *cmd) return MFI_STAT_OK; } +/* Some implementations use CLUSTER RESET LD to simulate a device reset */ static int megasas_cluster_reset_ld(MegasasState *s, MegasasCmd *cmd) { - return MFI_STAT_INVALID_DCMD; + uint16_t target_id; + int i; + + /* mbox0 contains the device index */ + target_id = le16_to_cpu(cmd->frame->dcmd.mbox[0]); + trace_megasas_dcmd_reset_ld(cmd->index, target_id); + for (i = 0; i < s->fw_cmds; i++) { + MegasasCmd *tmp_cmd = &s->frames[i]; + if (tmp_cmd->req && tmp_cmd->req->dev->id == target_id) { + SCSIDevice *d = tmp_cmd->req->dev; + qdev_reset_all(&d->qdev); + } + } + return MFI_STAT_OK; } static int megasas_dcmd_set_properties(MegasasState *s, MegasasCmd *cmd) @@ -1566,16 +1654,23 @@ static int megasas_handle_scsi(MegasasState *s, MegasasCmd *cmd, bool is_logical) { uint8_t *cdb; - int len; bool is_write; struct SCSIDevice *sdev = NULL; cdb = cmd->frame->pass.cdb; - if (cmd->frame->header.target_id < s->fw_luns) { - sdev = scsi_device_find(&s->bus, 0, cmd->frame->header.target_id, - cmd->frame->header.lun_id); + if (is_logical) { + if (cmd->frame->header.target_id >= MFI_MAX_LD || + cmd->frame->header.lun_id != 0) { + trace_megasas_scsi_target_not_present( + mfi_frame_desc[cmd->frame->header.frame_cmd], is_logical, + cmd->frame->header.target_id, cmd->frame->header.lun_id); + return MFI_STAT_DEVICE_NOT_FOUND; + } } + sdev = scsi_device_find(&s->bus, 0, cmd->frame->header.target_id, + cmd->frame->header.lun_id); + cmd->iov_size = le32_to_cpu(cmd->frame->header.data_len); trace_megasas_handle_scsi(mfi_frame_desc[cmd->frame->header.frame_cmd], is_logical, cmd->frame->header.target_id, @@ -1619,16 +1714,16 @@ static int megasas_handle_scsi(MegasasState *s, MegasasCmd *cmd, } is_write = (cmd->req->cmd.mode == SCSI_XFER_TO_DEV); - len = megasas_enqueue_req(cmd, is_write); - if (len > 0) { + if (cmd->iov_size) { if (is_write) { - trace_megasas_scsi_write_start(cmd->index, len); + trace_megasas_scsi_write_start(cmd->index, cmd->iov_size); } else { - trace_megasas_scsi_read_start(cmd->index, len); + trace_megasas_scsi_read_start(cmd->index, cmd->iov_size); } } else { trace_megasas_scsi_nodata(cmd->index); } + megasas_enqueue_req(cmd, is_write); return MFI_STAT_INVALID_STATUS; } @@ -1646,7 +1741,8 @@ static int megasas_handle_io(MegasasState *s, MegasasCmd *cmd) lba_start_hi = le32_to_cpu(cmd->frame->io.lba_hi); lba_start = ((uint64_t)lba_start_hi << 32) | lba_start_lo; - if (cmd->frame->header.target_id < s->fw_luns) { + if (cmd->frame->header.target_id < MFI_MAX_LD && + cmd->frame->header.lun_id == 0) { sdev = scsi_device_find(&s->bus, 0, cmd->frame->header.target_id, cmd->frame->header.lun_id); } @@ -1797,6 +1893,7 @@ static void megasas_command_complete(SCSIRequest *req, uint32_t status, cmd->req = NULL; } cmd->frame->header.cmd_status = cmd_status; + megasas_unmap_frame(cmd->state, cmd); megasas_complete_frame(cmd->state, cmd->context); } @@ -1900,6 +1997,7 @@ static void megasas_handle_frame(MegasasState *s, uint64_t frame_addr, } else { megasas_frame_set_cmd_status(frame_addr, frame_status); } + megasas_unmap_frame(s, cmd); megasas_complete_frame(s, cmd->context); } } @@ -1908,38 +2006,55 @@ static uint64_t megasas_mmio_read(void *opaque, hwaddr addr, unsigned size) { MegasasState *s = opaque; + PCIDevice *pci_dev = PCI_DEVICE(s); + MegasasBaseClass *base_class = MEGASAS_DEVICE_GET_CLASS(s); uint32_t retval = 0; switch (addr) { case MFI_IDB: retval = 0; + trace_megasas_mmio_readl("MFI_IDB", retval); break; case MFI_OMSG0: case MFI_OSP0: - retval = (megasas_use_msix(s) ? MFI_FWSTATE_MSIX_SUPPORTED : 0) | + retval = (msix_present(pci_dev) ? MFI_FWSTATE_MSIX_SUPPORTED : 0) | (s->fw_state & MFI_FWSTATE_MASK) | ((s->fw_sge & 0xff) << 16) | (s->fw_cmds & 0xFFFF); + trace_megasas_mmio_readl(addr == MFI_OMSG0 ? "MFI_OMSG0" : "MFI_OSP0", + retval); break; case MFI_OSTS: if (megasas_intr_enabled(s) && s->doorbell) { - retval = MFI_1078_RM | 1; + retval = base_class->osts; } + trace_megasas_mmio_readl("MFI_OSTS", retval); break; case MFI_OMSK: retval = s->intr_mask; + trace_megasas_mmio_readl("MFI_OMSK", retval); break; case MFI_ODCR0: - retval = s->doorbell; + retval = s->doorbell ? 1 : 0; + trace_megasas_mmio_readl("MFI_ODCR0", retval); + break; + case MFI_DIAG: + retval = s->diag; + trace_megasas_mmio_readl("MFI_DIAG", retval); + break; + case MFI_OSP1: + retval = 15; + trace_megasas_mmio_readl("MFI_OSP1", retval); break; default: trace_megasas_mmio_invalid_readl(addr); break; } - trace_megasas_mmio_readl(addr, retval); return retval; } +static int adp_reset_seq[] = {0x00, 0x04, 0x0b, 0x02, 0x07, 0x0d}; + static void megasas_mmio_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { @@ -1949,9 +2064,9 @@ static void megasas_mmio_write(void *opaque, hwaddr addr, uint32_t frame_count; int i; - trace_megasas_mmio_writel(addr, val); switch (addr) { case MFI_IDB: + trace_megasas_mmio_writel("MFI_IDB", val); if (val & MFI_FWINIT_ABORT) { /* Abort all pending cmds */ for (i = 0; i < s->fw_cmds; i++) { @@ -1965,8 +2080,13 @@ static void megasas_mmio_write(void *opaque, hwaddr addr, if (val & MFI_FWINIT_MFIMODE) { /* discard MFIs */ } + if (val & MFI_FWINIT_STOP_ADP) { + /* Terminal error, stop processing */ + s->fw_state = MFI_FWSTATE_FAULT; + } break; case MFI_OMSK: + trace_megasas_mmio_writel("MFI_OMSK", val); s->intr_mask = val; if (!megasas_intr_enabled(s) && !msi_enabled(pci_dev) && @@ -1984,29 +2104,34 @@ static void megasas_mmio_write(void *opaque, hwaddr addr, } } else { trace_megasas_intr_disabled(); + megasas_soft_reset(s); } break; case MFI_ODCR0: + trace_megasas_mmio_writel("MFI_ODCR0", val); s->doorbell = 0; - if (s->producer_pa && megasas_intr_enabled(s)) { - /* Update reply queue pointer */ - trace_megasas_qf_update(s->reply_queue_head, s->busy); - stl_le_phys(&address_space_memory, - s->producer_pa, s->reply_queue_head); - if (!msix_enabled(pci_dev)) { + if (megasas_intr_enabled(s)) { + if (!msix_enabled(pci_dev) && !msi_enabled(pci_dev)) { trace_megasas_irq_lower(); pci_irq_deassert(pci_dev); } } break; case MFI_IQPH: + trace_megasas_mmio_writel("MFI_IQPH", val); /* Received high 32 bits of a 64 bit MFI frame address */ s->frame_hi = val; break; case MFI_IQPL: + trace_megasas_mmio_writel("MFI_IQPL", val); /* Received low 32 bits of a 64 bit MFI frame address */ + /* Fallthrough */ case MFI_IQP: - /* Received 32 bit MFI frame address */ + if (addr == MFI_IQP) { + trace_megasas_mmio_writel("MFI_IQP", val); + /* Received 64 bit MFI frame address */ + s->frame_hi = 0; + } frame_addr = (val & ~0x1F); /* Add possible 64 bit offset */ frame_addr |= ((uint64_t)s->frame_hi << 32); @@ -2014,6 +2139,30 @@ static void megasas_mmio_write(void *opaque, hwaddr addr, frame_count = (val >> 1) & 0xF; megasas_handle_frame(s, frame_addr, frame_count); break; + case MFI_SEQ: + trace_megasas_mmio_writel("MFI_SEQ", val); + /* Magic sequence to start ADP reset */ + if (adp_reset_seq[s->adp_reset] == val) { + s->adp_reset++; + } else { + s->adp_reset = 0; + s->diag = 0; + } + if (s->adp_reset == 6) { + s->diag = MFI_DIAG_WRITE_ENABLE; + } + break; + case MFI_DIAG: + trace_megasas_mmio_writel("MFI_DIAG", val); + /* ADP reset */ + if ((s->diag & MFI_DIAG_WRITE_ENABLE) && + (val & MFI_DIAG_RESET_ADP)) { + s->diag |= MFI_DIAG_RESET_ADP; + megasas_soft_reset(s); + s->adp_reset = 0; + s->diag = 0; + } + break; default: trace_megasas_mmio_invalid_writel(addr, val); break; @@ -2072,11 +2221,26 @@ static void megasas_soft_reset(MegasasState *s) int i; MegasasCmd *cmd; - trace_megasas_reset(); + trace_megasas_reset(s->fw_state); for (i = 0; i < s->fw_cmds; i++) { cmd = &s->frames[i]; megasas_abort_command(cmd); } + if (s->fw_state == MFI_FWSTATE_READY) { + BusChild *kid; + + /* + * The EFI firmware doesn't handle UA, + * so we need to clear the Power On/Reset UA + * after the initial reset. + */ + QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { + SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child); + + sdev->unit_attention = SENSE_CODE(NO_SENSE); + scsi_device_unit_attention_reported(sdev); + } + } megasas_reset_frames(s); s->reply_queue_len = s->fw_cmds; s->reply_queue_pa = 0; @@ -2098,7 +2262,7 @@ static void megasas_scsi_reset(DeviceState *dev) megasas_soft_reset(s); } -static const VMStateDescription vmstate_megasas = { +static const VMStateDescription vmstate_megasas_gen1 = { .name = "megasas", .version_id = 0, .minimum_version_id = 0, @@ -2116,6 +2280,25 @@ static const VMStateDescription vmstate_megasas = { } }; +static const VMStateDescription vmstate_megasas_gen2 = { + .name = "megasas-gen2", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField[]) { + VMSTATE_PCIE_DEVICE(parent_obj, MegasasState), + VMSTATE_MSIX(parent_obj, MegasasState), + + VMSTATE_INT32(fw_state, MegasasState), + VMSTATE_INT32(intr_mask, MegasasState), + VMSTATE_INT32(doorbell, MegasasState), + VMSTATE_UINT64(reply_queue_pa, MegasasState), + VMSTATE_UINT64(consumer_pa, MegasasState), + VMSTATE_UINT64(producer_pa, MegasasState), + VMSTATE_END_OF_LIST() + } +}; + static void megasas_scsi_uninit(PCIDevice *d) { MegasasState *s = MEGASAS(d); @@ -2143,6 +2326,7 @@ static int megasas_scsi_init(PCIDevice *dev) { DeviceState *d = DEVICE(dev); MegasasState *s = MEGASAS(dev); + MegasasBaseClass *b = MEGASAS_DEVICE_GET_CLASS(s); uint8_t *pci_conf; int i, bar_type; Error *err = NULL; @@ -2166,20 +2350,25 @@ static int megasas_scsi_init(PCIDevice *dev) s->flags &= ~MEGASAS_MASK_USE_MSI; } if (megasas_use_msix(s) && - msix_init(dev, 15, &s->mmio_io, 0, 0x2000, - &s->mmio_io, 0, 0x3800, 0x68)) { + msix_init(dev, 15, &s->mmio_io, b->mmio_bar, 0x2000, + &s->mmio_io, b->mmio_bar, 0x3800, 0x68)) { s->flags &= ~MEGASAS_MASK_USE_MSIX; } + if (pci_is_express(dev)) { + pcie_endpoint_cap_init(dev, 0xa0); + } bar_type = PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64; - pci_register_bar(dev, 0, bar_type, &s->mmio_io); - pci_register_bar(dev, 2, PCI_BASE_ADDRESS_SPACE_IO, &s->port_io); + pci_register_bar(dev, b->ioport_bar, + PCI_BASE_ADDRESS_SPACE_IO, &s->port_io); + pci_register_bar(dev, b->mmio_bar, bar_type, &s->mmio_io); pci_register_bar(dev, 3, bar_type, &s->queue_io); if (megasas_use_msix(s)) { msix_vector_use(dev, 0); } + s->fw_state = MFI_FWSTATE_READY; if (!s->sas_addr) { s->sas_addr = ((NAA_LOCALLY_ASSIGNED_ID << 24) | IEEE_COMPANY_LOCALLY_ASSIGNED) << 36; @@ -2202,8 +2391,12 @@ static int megasas_scsi_init(PCIDevice *dev) } trace_megasas_init(s->fw_sge, s->fw_cmds, megasas_is_jbod(s) ? "jbod" : "raid"); - s->fw_luns = (MFI_MAX_LD > MAX_SCSI_DEVS) ? - MAX_SCSI_DEVS : MFI_MAX_LD; + + if (megasas_is_jbod(s)) { + s->fw_luns = MFI_MAX_SYS_PDS; + } else { + s->fw_luns = MFI_MAX_LD; + } s->producer_pa = 0; s->consumer_pa = 0; for (i = 0; i < s->fw_cmds; i++) { @@ -2232,7 +2425,7 @@ megasas_write_config(PCIDevice *pci, uint32_t addr, uint32_t val, int len) msi_write_config(pci, addr, val, len); } -static Property megasas_properties[] = { +static Property megasas_properties_gen1[] = { DEFINE_PROP_UINT32("max_sge", MegasasState, fw_sge, MEGASAS_DEFAULT_SGE), DEFINE_PROP_UINT32("max_cmds", MegasasState, fw_cmds, @@ -2248,36 +2441,119 @@ static Property megasas_properties[] = { DEFINE_PROP_END_OF_LIST(), }; +static Property megasas_properties_gen2[] = { + DEFINE_PROP_UINT32("max_sge", MegasasState, fw_sge, + MEGASAS_DEFAULT_SGE), + DEFINE_PROP_UINT32("max_cmds", MegasasState, fw_cmds, + MEGASAS_GEN2_DEFAULT_FRAMES), + DEFINE_PROP_STRING("hba_serial", MegasasState, hba_serial), + DEFINE_PROP_UINT64("sas_address", MegasasState, sas_addr, 0), + DEFINE_PROP_BIT("use_msi", MegasasState, flags, + MEGASAS_FLAG_USE_MSI, true), + DEFINE_PROP_BIT("use_msix", MegasasState, flags, + MEGASAS_FLAG_USE_MSIX, true), + DEFINE_PROP_BIT("use_jbod", MegasasState, flags, + MEGASAS_FLAG_USE_JBOD, false), + DEFINE_PROP_END_OF_LIST(), +}; + +typedef struct MegasasInfo { + const char *name; + const char *desc; + const char *product_name; + const char *product_version; + uint16_t device_id; + uint16_t subsystem_id; + int ioport_bar; + int mmio_bar; + bool is_express; + int osts; + const VMStateDescription *vmsd; + Property *props; +} MegasasInfo; + +static struct MegasasInfo megasas_devices[] = { + { + .name = TYPE_MEGASAS_GEN1, + .desc = "LSI MegaRAID SAS 1078", + .product_name = "LSI MegaRAID SAS 8708EM2", + .product_version = MEGASAS_VERSION_GEN1, + .device_id = PCI_DEVICE_ID_LSI_SAS1078, + .subsystem_id = 0x1013, + .ioport_bar = 2, + .mmio_bar = 0, + .osts = MFI_1078_RM | 1, + .is_express = false, + .vmsd = &vmstate_megasas_gen1, + .props = megasas_properties_gen1, + },{ + .name = TYPE_MEGASAS_GEN2, + .desc = "LSI MegaRAID SAS 2108", + .product_name = "LSI MegaRAID SAS 9260-8i", + .product_version = MEGASAS_VERSION_GEN2, + .device_id = PCI_DEVICE_ID_LSI_SAS0079, + .subsystem_id = 0x9261, + .ioport_bar = 0, + .mmio_bar = 1, + .osts = MFI_GEN2_RM, + .is_express = true, + .vmsd = &vmstate_megasas_gen2, + .props = megasas_properties_gen2, + } +}; + static void megasas_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc); + MegasasBaseClass *e = MEGASAS_DEVICE_CLASS(oc); + const MegasasInfo *info = data; pc->init = megasas_scsi_init; pc->exit = megasas_scsi_uninit; pc->vendor_id = PCI_VENDOR_ID_LSI_LOGIC; - pc->device_id = PCI_DEVICE_ID_LSI_SAS1078; + pc->device_id = info->device_id; pc->subsystem_vendor_id = PCI_VENDOR_ID_LSI_LOGIC; - pc->subsystem_id = 0x1013; + pc->subsystem_id = info->subsystem_id; pc->class_id = PCI_CLASS_STORAGE_RAID; - dc->props = megasas_properties; + pc->is_express = info->is_express; + e->mmio_bar = info->mmio_bar; + e->ioport_bar = info->ioport_bar; + e->osts = info->osts; + e->product_name = info->product_name; + e->product_version = info->product_version; + dc->props = info->props; dc->reset = megasas_scsi_reset; - dc->vmsd = &vmstate_megasas; + dc->vmsd = info->vmsd; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->desc = "LSI MegaRAID SAS 1078"; + dc->desc = info->desc; pc->config_write = megasas_write_config; } static const TypeInfo megasas_info = { - .name = TYPE_MEGASAS, + .name = TYPE_MEGASAS_BASE, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(MegasasState), - .class_init = megasas_class_init, + .class_size = sizeof(MegasasBaseClass), + .abstract = true, }; static void megasas_register_types(void) { + int i; + type_register_static(&megasas_info); + for (i = 0; i < ARRAY_SIZE(megasas_devices); i++) { + const MegasasInfo *info = &megasas_devices[i]; + TypeInfo type_info = {}; + + type_info.name = info->name; + type_info.parent = TYPE_MEGASAS_BASE; + type_info.class_data = (void *)info; + type_info.class_init = megasas_class_init; + + type_register(&type_info); + } } type_init(megasas_register_types) diff --git a/hw/scsi/mfi.h b/hw/scsi/mfi.h index a3034f6239..29d41775d4 100644 --- a/hw/scsi/mfi.h +++ b/hw/scsi/mfi.h @@ -60,6 +60,7 @@ #define MFI_ODR0 0x9c /* outbound doorbell register0 */ #define MFI_ODCR0 0xa0 /* outbound doorbell clear register0 */ #define MFI_OSP0 0xb0 /* outbound scratch pad0 */ +#define MFI_OSP1 0xb4 /* outbound scratch pad1 */ #define MFI_IQPL 0xc0 /* Inbound queue port (low bytes) */ #define MFI_IQPH 0xc4 /* Inbound queue port (high bytes) */ #define MFI_DIAG 0xf8 /* Host diag */ @@ -116,6 +117,12 @@ #define MFI_FWINIT_STOP_ADP 0x00000020 /* Move to operational, stop */ #define MFI_FWINIT_ADP_RESET 0x00000040 /* Reset ADP */ +/* + * Control bits for the DIAG register + */ +#define MFI_DIAG_WRITE_ENABLE 0x00000080 +#define MFI_DIAG_RESET_ADP 0x00000004 + /* MFI Commands */ typedef enum { MFI_CMD_INIT = 0x00, @@ -1094,7 +1101,7 @@ struct mfi_pd_list { union mfi_ld_ref { struct { uint8_t target_id; - uint8_t lun_id; + uint8_t reserved; uint16_t seq; } v; uint32_t ref; @@ -1111,6 +1118,13 @@ struct mfi_ld_list { } ld_list[MFI_MAX_LD]; } QEMU_PACKED; +struct mfi_ld_targetid_list { + uint32_t size; + uint32_t ld_count; + uint8_t pad[3]; + uint8_t targetid[MFI_MAX_LD]; +} QEMU_PACKED; + enum mfi_ld_access { MFI_LD_ACCESS_RW = 0, MFI_LD_ACCSSS_RO = 2, diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index af7707cf27..24f7b74ae8 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -51,14 +51,6 @@ static void scsi_device_realize(SCSIDevice *s, Error **errp) } } -static void scsi_device_unrealize(SCSIDevice *s, Error **errp) -{ - SCSIDeviceClass *sc = SCSI_DEVICE_GET_CLASS(s); - if (sc->unrealize) { - sc->unrealize(s, errp); - } -} - int scsi_bus_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, uint8_t *buf, void *hba_private) { @@ -84,7 +76,7 @@ static SCSIRequest *scsi_device_alloc_req(SCSIDevice *s, uint32_t tag, uint32_t return NULL; } -static void scsi_device_unit_attention_reported(SCSIDevice *s) +void scsi_device_unit_attention_reported(SCSIDevice *s) { SCSIDeviceClass *sc = SCSI_DEVICE_GET_CLASS(s); if (sc->unit_attention_reported) { @@ -218,7 +210,9 @@ static void scsi_qdev_unrealize(DeviceState *qdev, Error **errp) if (dev->vmsentry) { qemu_del_vm_change_state_handler(dev->vmsentry); } - scsi_device_unrealize(dev, errp); + + scsi_device_purge_requests(dev, SENSE_CODE(NO_SENSE)); + blockdev_mark_auto_del(dev->conf.blk); } /* handle legacy '-drive if=scsi,...' cmd line args */ @@ -826,7 +820,7 @@ static int ata_passthrough_xfer_unit(SCSIDevice *dev, uint8_t *buf) return xfer_unit; } -static int ata_passthrough_12_xfer_size(SCSIDevice *dev, uint8_t *buf) +static int ata_passthrough_12_xfer(SCSIDevice *dev, uint8_t *buf) { int length = buf[2] & 0x3; int xfer; @@ -849,7 +843,7 @@ static int ata_passthrough_12_xfer_size(SCSIDevice *dev, uint8_t *buf) return xfer * unit; } -static int ata_passthrough_16_xfer_size(SCSIDevice *dev, uint8_t *buf) +static int ata_passthrough_16_xfer(SCSIDevice *dev, uint8_t *buf) { int extend = buf[1] & 0x1; int length = buf[2] & 0x3; @@ -875,16 +869,16 @@ static int ata_passthrough_16_xfer_size(SCSIDevice *dev, uint8_t *buf) return xfer * unit; } -uint32_t scsi_data_cdb_length(uint8_t *buf) +uint32_t scsi_data_cdb_xfer(uint8_t *buf) { if ((buf[0] >> 5) == 0 && buf[4] == 0) { return 256; } else { - return scsi_cdb_length(buf); + return scsi_cdb_xfer(buf); } } -uint32_t scsi_cdb_length(uint8_t *buf) +uint32_t scsi_cdb_xfer(uint8_t *buf) { switch (buf[0] >> 5) { case 0: @@ -905,9 +899,9 @@ uint32_t scsi_cdb_length(uint8_t *buf) } } -static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) +static int scsi_req_xfer(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) { - cmd->xfer = scsi_cdb_length(buf); + cmd->xfer = scsi_cdb_xfer(buf); switch (buf[0]) { case TEST_UNIT_READY: case REWIND: @@ -1038,17 +1032,17 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) /* BLANK command of MMC */ cmd->xfer = 0; } else { - cmd->xfer = ata_passthrough_12_xfer_size(dev, buf); + cmd->xfer = ata_passthrough_12_xfer(dev, buf); } break; case ATA_PASSTHROUGH_16: - cmd->xfer = ata_passthrough_16_xfer_size(dev, buf); + cmd->xfer = ata_passthrough_16_xfer(dev, buf); break; } return 0; } -static int scsi_req_stream_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) +static int scsi_req_stream_xfer(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) { switch (buf[0]) { /* stream commands */ @@ -1103,12 +1097,12 @@ static int scsi_req_stream_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *bu break; /* generic commands */ default: - return scsi_req_length(cmd, dev, buf); + return scsi_req_xfer(cmd, dev, buf); } return 0; } -static int scsi_req_medium_changer_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) +static int scsi_req_medium_changer_xfer(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) { switch (buf[0]) { /* medium changer commands */ @@ -1125,7 +1119,7 @@ static int scsi_req_medium_changer_length(SCSICommand *cmd, SCSIDevice *dev, uin /* generic commands */ default: - return scsi_req_length(cmd, dev, buf); + return scsi_req_xfer(cmd, dev, buf); } return 0; } @@ -1214,38 +1208,45 @@ static uint64_t scsi_cmd_lba(SCSICommand *cmd) return lba; } -int scsi_req_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, uint8_t *buf) -{ - int rc; +int scsi_cdb_length(uint8_t *buf) { + int cdb_len; - cmd->lba = -1; switch (buf[0] >> 5) { case 0: - cmd->len = 6; + cdb_len = 6; break; case 1: case 2: - cmd->len = 10; + cdb_len = 10; break; case 4: - cmd->len = 16; + cdb_len = 16; break; case 5: - cmd->len = 12; + cdb_len = 12; break; default: - return -1; + cdb_len = -1; } + return cdb_len; +} + +int scsi_req_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, uint8_t *buf) +{ + int rc; + + cmd->lba = -1; + cmd->len = scsi_cdb_length(buf); switch (dev->type) { case TYPE_TAPE: - rc = scsi_req_stream_length(cmd, dev, buf); + rc = scsi_req_stream_xfer(cmd, dev, buf); break; case TYPE_MEDIUM_CHANGER: - rc = scsi_req_medium_changer_length(cmd, dev, buf); + rc = scsi_req_medium_changer_xfer(cmd, dev, buf); break; default: - rc = scsi_req_length(cmd, dev, buf); + rc = scsi_req_xfer(cmd, dev, buf); break; } diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index 010eefd443..2f75d7d51c 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -1666,7 +1666,7 @@ static void scsi_disk_emulate_write_same(SCSIDiskReq *r, uint8_t *inbuf) { SCSIRequest *req = &r->req; SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); - uint32_t nb_sectors = scsi_data_cdb_length(r->req.cmd.buf); + uint32_t nb_sectors = scsi_data_cdb_xfer(r->req.cmd.buf); WriteSameCBData *data; uint8_t *buf; int i; @@ -2061,7 +2061,7 @@ static int32_t scsi_disk_dma_command(SCSIRequest *req, uint8_t *buf) return 0; } - len = scsi_data_cdb_length(r->req.cmd.buf); + len = scsi_data_cdb_xfer(r->req.cmd.buf); switch (command) { case READ_6: case READ_10: @@ -2138,14 +2138,6 @@ static void scsi_disk_reset(DeviceState *dev) s->tray_open = 0; } -static void scsi_unrealize(SCSIDevice *dev, Error **errp) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); - - scsi_device_purge_requests(&s->qdev, SENSE_CODE(NO_SENSE)); - blockdev_mark_auto_del(s->qdev.conf.blk); -} - static void scsi_disk_resize_cb(void *opaque) { SCSIDiskState *s = opaque; @@ -2396,7 +2388,7 @@ static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun, DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", lun, tag, buf[0]); { int i; - for (i = 1; i < req->cmd.len; i++) { + for (i = 1; i < scsi_cdb_length(buf); i++) { printf(" 0x%02x", buf[i]); } printf("\n"); @@ -2612,7 +2604,6 @@ static void scsi_hd_class_initfn(ObjectClass *klass, void *data) SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass); sc->realize = scsi_hd_realize; - sc->unrealize = scsi_unrealize; sc->alloc_req = scsi_new_request; sc->unit_attention_reported = scsi_disk_unit_attention_reported; dc->fw_name = "disk"; @@ -2643,7 +2634,6 @@ static void scsi_cd_class_initfn(ObjectClass *klass, void *data) SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass); sc->realize = scsi_cd_realize; - sc->unrealize = scsi_unrealize; sc->alloc_req = scsi_new_request; sc->unit_attention_reported = scsi_disk_unit_attention_reported; dc->fw_name = "disk"; @@ -2672,7 +2662,6 @@ static void scsi_block_class_initfn(ObjectClass *klass, void *data) SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass); sc->realize = scsi_block_realize; - sc->unrealize = scsi_unrealize; sc->alloc_req = scsi_block_new_request; sc->parse_cdb = scsi_block_parse_cdb; dc->fw_name = "disk"; @@ -2710,7 +2699,6 @@ static void scsi_disk_class_initfn(ObjectClass *klass, void *data) SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass); sc->realize = scsi_disk_realize; - sc->unrealize = scsi_unrealize; sc->alloc_req = scsi_new_request; sc->unit_attention_reported = scsi_disk_unit_attention_reported; dc->fw_name = "disk"; diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c index f2e53afd09..6b9e4e1ef9 100644 --- a/hw/scsi/scsi-generic.c +++ b/hw/scsi/scsi-generic.c @@ -368,12 +368,6 @@ static void scsi_generic_reset(DeviceState *dev) scsi_device_purge_requests(s, SENSE_CODE(RESET)); } -static void scsi_unrealize(SCSIDevice *s, Error **errp) -{ - scsi_device_purge_requests(s, SENSE_CODE(NO_SENSE)); - blockdev_mark_auto_del(s->conf.blk); -} - static void scsi_generic_realize(SCSIDevice *s, Error **errp) { int rc; @@ -478,7 +472,6 @@ static void scsi_generic_class_initfn(ObjectClass *klass, void *data) SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass); sc->realize = scsi_generic_realize; - sc->unrealize = scsi_unrealize; sc->alloc_req = scsi_new_request; sc->parse_cdb = scsi_generic_parse_cdb; dc->fw_name = "disk"; diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c index 3097d544b4..9651e6f274 100644 --- a/hw/scsi/virtio-scsi-dataplane.c +++ b/hw/scsi/virtio-scsi-dataplane.c @@ -241,9 +241,10 @@ void virtio_scsi_dataplane_start(VirtIOSCSI *s) } } - aio_context_release(s->ctx); s->dataplane_starting = false; s->dataplane_started = true; + aio_context_release(s->ctx); + return; fail_vrings: virtio_scsi_clear_aio(s); diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index 7d40eccb0c..fdcacfd79a 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -804,10 +804,12 @@ void virtio_scsi_common_realize(DeviceState *dev, Error **errp, virtio_init(vdev, "virtio-scsi", VIRTIO_ID_SCSI, sizeof(VirtIOSCSIConfig)); - if (s->conf.num_queues <= 0 || s->conf.num_queues > VIRTIO_PCI_QUEUE_MAX) { - error_setg(errp, "Invalid number of queues (= %" PRId32 "), " + if (s->conf.num_queues == 0 || + s->conf.num_queues > VIRTIO_PCI_QUEUE_MAX - 2) { + error_setg(errp, "Invalid number of queues (= %" PRIu32 "), " "must be a positive integer less than %d.", - s->conf.num_queues, VIRTIO_PCI_QUEUE_MAX); + s->conf.num_queues, VIRTIO_PCI_QUEUE_MAX - 2); + virtio_cleanup(vdev); return; } s->cmd_vqs = g_malloc0(s->conf.num_queues * sizeof(VirtQueue *)); |