From patchwork Tue Jun 15 15:16:13 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hannes Reinecke X-Patchwork-Id: 55729 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 6D515B7D12 for ; Wed, 16 Jun 2010 01:48:39 +1000 (EST) Received: from localhost ([127.0.0.1]:34626 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1OOYNQ-0000f5-BY for incoming@patchwork.ozlabs.org; Tue, 15 Jun 2010 11:48:36 -0400 Received: from [140.186.70.92] (port=46552 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1OOXsD-0005U8-1G for qemu-devel@nongnu.org; Tue, 15 Jun 2010 11:16:22 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.69) (envelope-from ) id 1OOXs7-0002Hn-1Y for qemu-devel@nongnu.org; Tue, 15 Jun 2010 11:16:20 -0400 Received: from cantor2.suse.de ([195.135.220.15]:44902 helo=mx2.suse.de) by eggs.gnu.org with esmtp (Exim 4.69) (envelope-from ) id 1OOXs6-0002HY-8n for qemu-devel@nongnu.org; Tue, 15 Jun 2010 11:16:15 -0400 Received: from relay2.suse.de (charybdis-ext.suse.de [195.135.221.2]) by mx2.suse.de (Postfix) with ESMTP id 571CC89FC6; Tue, 15 Jun 2010 17:16:13 +0200 (CEST) Date: Tue, 15 Jun 2010 17:16:13 +0200 To: Nicholas A.Bellinger User-Agent: Heirloom mailx 12.2 01/07/07 MIME-Version: 1.0 Message-Id: <20100615151613.24B7E2B964@ochil.suse.de> From: hare@suse.de (Hannes Reinecke) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.4-2.6 Cc: qemu-devel@nongnu.org Subject: [Qemu-devel] [PATCH 2/7] megasas: Update to 1.02 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org This patchset updates the megasas HBA emulation to v1.02. Fixed issues; - Fixup frame context handling - Endianness fixes - Implement reset function - Improve DCMD bounds checking - Improve dump_frame() output - Sanitize I/O settings - Return correct SCSI status codes Signed-off-by: Hannes Reinecke --- hw/megasas.c | 155 +++++++++++++++++++++++++++++++++++----------------------- hw/mfi.h | 1 + 2 files changed, 95 insertions(+), 61 deletions(-) diff --git a/hw/megasas.c b/hw/megasas.c index 19569a8..753d5dc 100644 --- a/hw/megasas.c +++ b/hw/megasas.c @@ -40,11 +40,12 @@ do { fprintf(stderr, "megasas: error: " fmt , ## __VA_ARGS__);} while (0) #endif /* Static definitions */ -#define MEGASAS_VERSION "1.01" +#define MEGASAS_VERSION "1.02" #define MEGASAS_MAX_FRAMES 2048 /* Firmware limit at 65535 */ #define MEGASAS_DEFAULT_FRAMES 1000 /* Windows requires this */ #define MEGASAS_MAX_SGE 255 /* Firmware limit */ #define MEGASAS_DEFAULT_SGE 80 +#define MEGASAS_MAX_SECTORS 0xFFFF /* No real limit */ #define MEGASAS_MAX_ARRAYS 128 const char *mfi_frame_desc[] = { @@ -53,6 +54,7 @@ const char *mfi_frame_desc[] = { struct megasas_cmd_t { int index; + int context; target_phys_addr_t pa; union mfi_frame *frame; @@ -121,7 +123,8 @@ static void megasas_soft_reset(MPTState *s); static void megasas_map_sgl(struct megasas_cmd_t *cmd, int pa_offset) { int i; - int is_sgl64 = (cmd->frame->header.flags & MFI_FRAME_SGL64) ? 1 : 0; + uint16_t flags = le16_to_cpu(cmd->frame->header.flags); + int is_sgl64 = (flags & MFI_FRAME_SGL64) ? 1 : 0; int sgl_addr_size = is_sgl64 ? sizeof(uint64_t) : sizeof(uint32_t); qemu_sglist_init(&cmd->sg, cmd->frame->header.sge_count); @@ -152,7 +155,8 @@ static void megasas_build_sense(struct megasas_cmd_t *cmd, SCSISense sense) uint8_t *sense_ptr; uint8_t sense_len; target_phys_addr_t pa, pa_hi = 0, pa_lo; - int is_sense64 = (cmd->frame->header.flags & MFI_FRAME_SENSE64) ? 1 : 0; + uint16_t flags = le16_to_cpu(cmd->frame->header.flags); + int is_sense64 = (flags & MFI_FRAME_SENSE64) ? 1 : 0; sense_ptr = qemu_mallocz(cmd->frame->header.sense_len); sense_len = scsi_build_sense(SENSE_CODE(INVALID_OPCODE), sense_ptr, @@ -216,6 +220,22 @@ static uint64_t megasas_gen_sas_addr(unsigned long id) * Frame handling */ +static void megasas_dump_frame(struct megasas_cmd_t *cmd) +{ + DPRINTF("Frame %x: p%p context %x\n" + "\t%016lx %016lx\n\t%016lx %016lx\n" + "\t%016lx %016lx\n\t%016lx %016lx\n", + cmd->index, cmd->frame, cmd->context, + be64_to_cpu(cmd->frame->raw[0]), + be64_to_cpu(cmd->frame->raw[1]), + be64_to_cpu(cmd->frame->raw[2]), + be64_to_cpu(cmd->frame->raw[3]), + be64_to_cpu(cmd->frame->raw[4]), + be64_to_cpu(cmd->frame->raw[5]), + be64_to_cpu(cmd->frame->raw[6]), + be64_to_cpu(cmd->frame->raw[7])); +} + static inline int megasas_next_index(MPTState *s, int index) { index++; @@ -253,9 +273,11 @@ static inline struct megasas_cmd_t *megasas_next_frame(MPTState *s, cmd = megasas_lookup_frame(s, frame); if (cmd) { #ifdef DEBUG_MEGASAS_QUEUE - DPRINTF("Found mapped frame %x context %x pa %lx\n", cmd->index, - cmd->frame->header.context, cmd->pa); + DPRINTF("Found mapped frame %x pa %lx\n", cmd->index, cmd->pa); #endif + if (cmd->context != -1) + DPRINTF("Frame %x context %x not finished\n", + cmd->index, cmd->context); return cmd; } index = s->reply_queue_index; @@ -300,15 +322,18 @@ megasas_enqueue_frame(MPTState *s, target_phys_addr_t frame) } } - cmd->frame->header.context = le32_to_cpu(cmd->frame->header.context); - cmd->frame->header.flags = le16_to_cpu(cmd->frame->header.flags); - cmd->frame->header.timeout = le16_to_cpu(cmd->frame->header.timeout); - cmd->frame->header.data_len = le32_to_cpu(cmd->frame->header.data_len); + cmd->context = le32_to_cpu(cmd->frame->header.context); + if (cmd->context != cmd->index) { + DPRINTF("Non-matching context %x, correcting to %x\n", + cmd->context, cmd->index); + megasas_dump_frame(cmd); + cmd->context = cmd->index; + } s->busy++; #ifdef DEBUG_MEGASAS_QUEUE - DPRINTF("Enqueue frame context %x tail %d busy %d\n", - cmd->frame->header.context, s->reply_queue_index, s->busy); + DPRINTF("Enqueue frame %x context %x tail %x busy %d\n", + cmd->index, cmd->context, s->reply_queue_index, s->busy); #endif return cmd; @@ -334,7 +359,7 @@ static void megasas_dequeue_frame(MPTState *s, int context) s->reply_queue_index = megasas_next_index(s, tail); #ifdef DEBUG_MEGASAS_QUEUE - DPRINTF("Complete frame context %x tail %d busy %d doorbell %d\n", + DPRINTF("Complete frame context %x tail %x busy %d doorbell %d\n", context, tail, s->busy, s->doorbell); #endif @@ -345,14 +370,6 @@ static void megasas_dequeue_frame(MPTState *s, int context) } } -static void megasas_dump_frame(struct megasas_cmd_t *cmd) -{ - DPRINTF("Frame %d: cmd %x cdb %d sges %d ctx %x data_len %d\n", - cmd->index, cmd->frame->header.frame_cmd, - cmd->frame->header.cdb_len, cmd->frame->header.sge_count, - cmd->frame->header.context, cmd->frame->header.data_len); -} - static int megasas_finish_command(MPTState *s, struct megasas_cmd_t *cmd) { int context = -1; @@ -364,8 +381,8 @@ static int megasas_finish_command(MPTState *s, struct megasas_cmd_t *cmd) s->event_count++; return -1; } - if (cmd->frame) - context = cmd->frame->header.context; + context = cmd->context; + cmd->context = -1; return context; } @@ -395,6 +412,8 @@ static int megasas_init_firmware(MPTState *s, struct megasas_cmd_t *cmd) initq = cpu_physical_memory_map(iq_pa, &initq_size, 0); if (initq_size != sizeof(*initq)) { DPRINTF("MFI init firmware: failed to map queue mem\n"); + if (initq) + cpu_physical_memory_unmap(initq, initq_size, 0, 0); s->fw_state = MFI_FWSTATE_FAULT; s->event_count++; goto out; @@ -424,9 +443,22 @@ out: static int megasas_map_dcmd(struct megasas_cmd_t *cmd) { target_phys_addr_t iov_pa, iov_size_p; - uint32_t iov_size; - int is_sgl64 = (cmd->frame->header.flags & MFI_FRAME_SGL64) ? 1 : 0; + uint32_t iov_size = 0; + uint16_t flags = le16_to_cpu(cmd->frame->header.flags); + int is_sgl64 = (flags & MFI_FRAME_SGL64) ? 1 : 0; + if (cmd->frame->header.sge_count == 0) { +#ifdef DEBUG_MEGASAS_DCMD + DPRINTF("Zero DCMD sge count\n"); +#endif + cmd->iov_size = 0; + cmd->iov_buf = NULL; + return 0; + } else if (cmd->frame->header.sge_count > 1) { + DPRINTF("Invalid DCMD sge count %d\n", + cmd->frame->header.sge_count); + return -1; + } if (is_sgl64) { iov_pa = le64_to_cpu(cmd->frame->dcmd.sgl.sg64[0].addr); iov_size = le32_to_cpu(cmd->frame->dcmd.sgl.sg64[0].len); @@ -451,7 +483,8 @@ static int megasas_map_dcmd(struct megasas_cmd_t *cmd) static int megasas_finish_dcmd(struct megasas_cmd_t *cmd, uint32_t size) { - int is_sgl64 = (cmd->frame->header.flags & MFI_FRAME_SGL64) ? 1 : 0; + uint16_t flags = le16_to_cpu(cmd->frame->header.flags); + int is_sgl64 = (flags & MFI_FRAME_SGL64) ? 1 : 0; int sgl_addr_size = is_sgl64 ? sizeof(uint64_t) : sizeof(uint32_t); if (size > cmd->iov_size) { @@ -459,6 +492,9 @@ static int megasas_finish_dcmd(struct megasas_cmd_t *cmd, uint32_t size) size, cmd->iov_size); size = cmd->iov_size; } + if (!cmd->iov_buf) + return 0; + cpu_physical_memory_unmap(cmd->iov_buf, cmd->iov_size, 1, size); if (cmd->iov_size > size) stl_phys(cmd->pa + offsetof(struct mfi_dcmd_frame,sgl) + sgl_addr_size, size); @@ -512,7 +548,7 @@ static int megasas_ctrl_get_info(MPTState *s, struct megasas_cmd_t *cmd) info->max_lds = s->fw_luns; info->max_cmds = s->fw_cmds; info->max_sg_elements = s->fw_sge; - info->max_request_size = 8192; + info->max_request_size = MEGASAS_MAX_SECTORS; info->lds_present = num_ld_disks; info->pd_present = num_ld_disks + 1; info->pd_disks_present = num_ld_disks; @@ -529,9 +565,9 @@ static int megasas_ctrl_get_info(MPTState *s, struct megasas_cmd_t *cmd) MFI_INFO_LDOPS_IO_POLICY | MFI_INFO_LDOPS_WRITE_POLICY | MFI_INFO_LDOPS_READ_POLICY; - info->max_strips_per_io = 42; - info->stripe_sz_ops.min = 4; - info->stripe_sz_ops.max = 0xf; + info->max_strips_per_io = s->fw_sge; + info->stripe_sz_ops.min = 3; + info->stripe_sz_ops.max = ffs(MEGASAS_MAX_SECTORS + 1) - 1; info->properties.pred_fail_poll_interval = 300; info->properties.intr_throttle_cnt = 16; info->properties.intr_throttle_timeout = 50; @@ -564,7 +600,7 @@ static int megasas_mfc_get_defaults(MPTState *s, struct megasas_cmd_t *cmd) return MFI_STAT_INVALID_PARAMETER; } - info->stripe_size = 8; + info->stripe_size = 3; info->flush_time = 4; info->background_rate = 30; info->allow_mix_in_enclosure = 1; @@ -867,7 +903,7 @@ static int megasas_dcmd_ld_get_info(MPTState *s, struct megasas_cmd_t *cmd) info->ld_config.properties.ld.v.target_id = ld_id; if (sdev) { - info->ld_config.params.stripe_size = 64; + info->ld_config.params.stripe_size = 3; info->ld_config.params.num_drives = 1; info->ld_config.params.state = MFI_LD_STATE_OFFLINE; info->ld_config.params.is_consistent = 1; @@ -892,8 +928,7 @@ static int megasas_dcmd_get_properties(MPTState *s, struct megasas_cmd_t *cmd) } #ifdef DEBUG_MEGASAS_DCMD - DPRINTF("DCMD get properties: xfer_len %d sge_count %d\n", - cmd->frame->header.data_len, cmd->frame->header.sge_count); + DPRINTF("DCMD get properties\n"); #endif info->pred_fail_poll_interval = 300; info->intr_throttle_cnt = 16; @@ -934,23 +969,10 @@ static int megasas_ctrl_shutdown(MPTState *s, struct megasas_cmd_t *cmd) static int megasas_dcmd_set_properties(MPTState *s, struct megasas_cmd_t *cmd) { uint8_t *dummy; - target_phys_addr_t iov_pa; - uint32_t iov_size; - int is_sgl64 = (cmd->frame->header.flags & MFI_FRAME_SGL64) ? 1 : 0; #ifdef DEBUG_MEGASAS_DCMD - DPRINTF("DCMD get properties: xfer_len %d sge_count %d\n", - cmd->frame->header.data_len, cmd->frame->header.sge_count); + DPRINTF("DCMD set properties\n"); #endif - if (is_sgl64) { - iov_pa = le64_to_cpu(cmd->frame->dcmd.sgl.sg64[0].addr); - iov_size = le32_to_cpu(cmd->frame->dcmd.sgl.sg64[0].len); - } else { - iov_pa = le32_to_cpu(cmd->frame->dcmd.sgl.sg32[0].addr); - iov_size = le32_to_cpu(cmd->frame->dcmd.sgl.sg32[0].len); - } - - cpu_physical_memory_read(iov_pa, cmd->iov_buf, 64); dummy = cmd->iov_buf; DPRINTF("Properties dump\n"); DPRINTF("%02x %02x %02x %0x2 %02x %02x %02x %02x\n", @@ -983,10 +1005,9 @@ static int megasas_dcmd_set_properties(MPTState *s, struct megasas_cmd_t *cmd) static int megasas_dcmd_dummy(MPTState *s, struct megasas_cmd_t *cmd) { #ifdef DEBUG_MEGASAS_DCMD - DPRINTF("DCMD dummy: xfer_len %d sge_count %d\n", - cmd->frame->header.data_len, cmd->frame->header.sge_count); + DPRINTF("DCMD dummy: xfer_len %ld\n", cmd->iov_size); #endif - memset(cmd->iov_buf, 0, cmd->frame->header.data_len); + memset(cmd->iov_buf, 0, cmd->iov_size); return MFI_STAT_OK; } @@ -1146,7 +1167,7 @@ static int megasas_handle_scsi(MPTState *s, struct megasas_cmd_t *cmd, int is_lo cmd->frame->header.target_id, cmd->frame->header.lun_id, cmd->frame->header.cdb_len); megasas_build_sense(cmd, SENSE_CODE(INVALID_OPCODE)); - megasas_frame_set_scsi_status(cmd->pa, CHECK_CONDITION); + megasas_frame_set_scsi_status(cmd->pa, CHECK_CONDITION << 1); s->event_count++; return MFI_STAT_SCSI_DONE_WITH_ERROR; } @@ -1157,7 +1178,6 @@ static int megasas_handle_scsi(MPTState *s, struct megasas_cmd_t *cmd, int is_lo if (cmd->frame->header.data_len != cmd->req->cmd.xfer) { DPRINTF("xfer length mismatch, frame %u cdb %u\n", cmd->frame->header.data_len, (unsigned)cmd->req->cmd.xfer); - cmd->frame->header.data_len = cmd->req->cmd.xfer; s->event_count++; } @@ -1202,7 +1222,7 @@ static int megasas_handle_io(MPTState *s, struct megasas_cmd_t *cmd) cmd->frame->header.target_id, cmd->frame->header.lun_id, cmd->frame->header.cdb_len); megasas_build_sense(cmd, SENSE_CODE(INVALID_OPCODE)); - megasas_frame_set_scsi_status(cmd->pa, CHECK_CONDITION); + megasas_frame_set_scsi_status(cmd->pa, CHECK_CONDITION << 1); s->event_count++; return MFI_STAT_SCSI_DONE_WITH_ERROR; } @@ -1263,7 +1283,7 @@ static void megasas_command_complete(SCSIRequest *req) mfi_frame_desc[cmd->frame->header.frame_cmd], req, cmd, cmd->sdev, req->status, (unsigned)req->xferlen); #endif - if (req->status == CHECK_CONDITION) { + if (req->status == CHECK_CONDITION << 1) { megasas_build_sense(cmd, cmd->sdev->sense); cmd_status = MFI_STAT_SCSI_DONE_WITH_ERROR; scsi_dev_clear_sense(cmd->sdev); @@ -1317,7 +1337,7 @@ static void megasas_handle_frame(MPTState *s, target_phys_addr_t frame_addr, { uint8_t frame_cmd; uint8_t frame_status = MFI_STAT_INVALID_CMD; - uint32_t frame_context = 0; + uint32_t frame_context; struct megasas_cmd_t *cmd; frame_cmd = megasas_frame_get_cmd(frame_addr); @@ -1331,7 +1351,7 @@ static void megasas_handle_frame(MPTState *s, target_phys_addr_t frame_addr, cmd = megasas_enqueue_frame(s, frame_addr); if (!cmd) { /* reply queue full */ - megasas_frame_set_scsi_status(frame_addr, BUSY); + megasas_frame_set_scsi_status(frame_addr, BUSY << 1); frame_status = MFI_STAT_SCSI_DONE_WITH_ERROR; s->event_count++; goto frame_done; @@ -1466,7 +1486,7 @@ static void megasas_mmio_writel(void *opaque, target_phys_addr_t addr, case MFI_ODCR0: /* Update reply queue pointer */ #ifdef DEBUG_MEGASAS_QUEUE - DPRINTF("Update reply queue head %d busy %d\n", + DPRINTF("Update reply queue head %x busy %d\n", s->reply_queue_index, s->busy); #endif stl_phys(s->producer_pa, s->reply_queue_index); @@ -1651,6 +1671,17 @@ static void megasas_queue_mapfunc(PCIDevice *pci_dev, int region_num, s->event_count++; } +static void megasas_scsi_reset(DeviceState *dev) +{ + MPTState *s = DO_UPCAST(MPTState, dev.qdev, dev); + int i; + + for (i = 0; i <= s->fw_cmds; i++) + megasas_abort_command(&s->frames[i]); + + megasas_soft_reset(s); +} + static void megasas_scsi_save(QEMUFile *f, void *opaque) { MPTState *s = opaque; @@ -1748,12 +1779,11 @@ static int megasas_scsi_init(PCIDevice *dev) s->consumer_pa = 0; for (i = 0; i < s->fw_cmds; i++) { s->frames[i].index = i; + s->frames[i].context = -1; s->frames[i].pa = 0; s->frames[i].state = s; } - megasas_soft_reset(s); - scsi_bus_new(&s->bus, &dev->qdev, 1, s->fw_luns, megasas_command_complete); scsi_bus_legacy_handle_cmdline(&s->bus); register_savevm("megasas", -1, 0, megasas_scsi_save, megasas_scsi_load, s); @@ -1764,11 +1794,14 @@ static PCIDeviceInfo megasas_info = { .qdev.name = "LSI MegaRAID SAS 1078", .qdev.alias = "megasas", .qdev.size = sizeof(MPTState), + .qdev.reset = megasas_scsi_reset, .init = megasas_scsi_init, .exit = megasas_scsi_uninit, .qdev.props = (Property[]) { - DEFINE_PROP_UINT32("max_sge", MPTState, fw_sge, MEGASAS_DEFAULT_SGE), - DEFINE_PROP_UINT32("max_cmds", MPTState, fw_cmds, MEGASAS_DEFAULT_FRAMES), + DEFINE_PROP_UINT32("max_sge", MPTState, fw_sge, + MEGASAS_DEFAULT_SGE), + DEFINE_PROP_UINT32("max_cmds", MPTState, fw_cmds, + MEGASAS_DEFAULT_FRAMES), DEFINE_PROP_STRING("mode", MPTState, raid_mode_str), DEFINE_PROP_END_OF_LIST(), }, diff --git a/hw/mfi.h b/hw/mfi.h index 90334b1..cc71af3 100644 --- a/hw/mfi.h +++ b/hw/mfi.h @@ -489,6 +489,7 @@ union mfi_frame { struct mfi_abort_frame abort; struct mfi_smp_frame smp; struct mfi_stp_frame stp; + uint64_t raw[8]; uint8_t bytes[MFI_FRAME_SIZE]; };