diff options
author | Joshua M. Clulow <jmc@joyent.com> | 2016-03-07 11:59:15 +0000 |
---|---|---|
committer | Joshua M. Clulow <jmc@joyent.com> | 2016-07-06 16:25:32 +0000 |
commit | 90a7d022958606b544f5c442c92439fbdfaf5522 (patch) | |
tree | 42eb610ffa006201405c658600503ef519f352da | |
parent | 4555d02e8b95eba019119f4b350177a3bb821649 (diff) | |
download | illumos-joyent-90a7d022958606b544f5c442c92439fbdfaf5522.tar.gz |
XXX extensive refactoring to support aborting commands that time out
-rw-r--r-- | usr/src/uts/common/Makefile.files | 3 | ||||
-rw-r--r-- | usr/src/uts/common/io/cpqary3/cpqary3.c | 196 | ||||
-rw-r--r-- | usr/src/uts/common/io/cpqary3/cpqary3.h | 244 | ||||
-rw-r--r-- | usr/src/uts/common/io/cpqary3/cpqary3_ciss.c | 697 | ||||
-rw-r--r-- | usr/src/uts/common/io/cpqary3/cpqary3_ciss_simple.c | 38 | ||||
-rw-r--r-- | usr/src/uts/common/io/cpqary3/cpqary3_commands.c | 57 | ||||
-rw-r--r-- | usr/src/uts/common/io/cpqary3/cpqary3_hba.c | 400 | ||||
-rw-r--r-- | usr/src/uts/common/io/cpqary3/cpqary3_logvol.c | 133 | ||||
-rw-r--r-- | usr/src/uts/common/io/cpqary3/cpqary3_scsi.c | 82 | ||||
-rw-r--r-- | usr/src/uts/common/io/cpqary3/cpqary3_scsi.h | 5 | ||||
-rw-r--r-- | usr/src/uts/common/io/cpqary3/cpqary3_transport.c | 106 | ||||
-rw-r--r-- | usr/src/uts/common/io/cpqary3/cpqary3_user.c | 120 | ||||
-rw-r--r-- | usr/src/uts/common/io/cpqary3/cpqary3_util.c | 243 |
13 files changed, 1447 insertions, 877 deletions
diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files index 7a135acb5e..e663767986 100644 --- a/usr/src/uts/common/Makefile.files +++ b/usr/src/uts/common/Makefile.files @@ -2155,13 +2155,14 @@ DR_SAS_OBJS = dr_sas.o # XXX disabled cpqary3_noe.o cpqary3_ioctl.o CPQARY3_OBJS = cpqary3.o \ cpqary3_transport.o cpqary3_mem.o \ - cpqary3_scsi.o cpqary3_util.o \ + cpqary3_scsi.o \ cpqary3_bd.o \ cpqary3_device.o \ cpqary3_interrupts.o \ cpqary3_commands.o \ cpqary3_logvol.o \ cpqary3_hba.o \ + cpqary3_user.o \ cpqary3_ciss_simple.o \ cpqary3_ciss.o \ diff --git a/usr/src/uts/common/io/cpqary3/cpqary3.c b/usr/src/uts/common/io/cpqary3/cpqary3.c index c97361fab1..c2fd5a39d3 100644 --- a/usr/src/uts/common/io/cpqary3/cpqary3.c +++ b/usr/src/uts/common/io/cpqary3/cpqary3.c @@ -17,54 +17,22 @@ #include <sys/policy.h> #include "cpqary3.h" -/* - * Local Autoconfiguration Function Prototype Declations - */ static int cpqary3_attach(dev_info_t *, ddi_attach_cmd_t); static int cpqary3_detach(dev_info_t *, ddi_detach_cmd_t); static int cpqary3_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); - -/* - * Local Functions Definitions - */ - static void cpqary3_cleanup(cpqary3_t *); -static uint8_t cpqary3_update_ctlrdetails(cpqary3_t *, uint32_t *); static int cpqary3_command_comparator(const void *, const void *); /* - * Per-controller soft state object. + * Controller soft state. Each entry is an object of type "cpqary3_t". */ void *cpqary3_state; /* - * HBA minor number schema - * - * The minor numbers for any minor device nodes that we create are - * governed by the SCSA framework. We use the macros below to - * fabricate minor numbers for nodes that we own. - * - * See sys/impl/transport.h for more info. + * DMA attributes template. Each controller will make a copy of this template + * with appropriate customisations; e.g., the Scatter/Gather List Length. */ - -/* Macro to extract interface from minor number */ -#define CPQARY3_MINOR2INTERFACE(_x) ((_x) & (TRAN_MINOR_MASK)) - -/* Base of range assigned to HBAs: */ -#define SCSA_MINOR_HBABASE (32) - -/* Our minor nodes: */ -#define CPQARY3_MINOR (0 + SCSA_MINOR_HBABASE) - -/* Convenience macros to convert device instances to minor numbers */ -#define CPQARY3_INST2x(_i, _x) (((_i) << INST_MINOR_SHIFT) | (_x)) -#define CPQARY3_INST2CPQARY3(_i) CPQARY3_INST2x(_i, CPQARY3_MINOR) - -/* - * The Driver DMA Limit structure. - * Data used for SMART Integrated Array Controller shall be used. - */ -ddi_dma_attr_t cpqary3_dma_attr = { +static ddi_dma_attr_t cpqary3_dma_attr_template = { DMA_ATTR_V0, /* ddi_dma_attr version */ 0, /* Low Address */ 0xFFFFFFFFFFFFFFFF, /* High Address */ @@ -80,14 +48,14 @@ ddi_dma_attr_t cpqary3_dma_attr = { * in the Surge ASIC, with earlier FW versions. */ 0xFFFFFFFF, - CPQARY3_SG_CNT, /* Scatter/Gather List Length */ + 1, /* Scatter/Gather List Length */ 512, /* Device Granularity */ 0 /* DMA flags */ }; /* - * DMA access for both device control registers and for command block - * allocation. + * Device memory access attributes for both device control registers and for + * command block allocation. */ ddi_device_acc_attr_t cpqary3_dev_attributes = { DDI_DEVICE_ATTR_V0, @@ -158,11 +126,7 @@ _init() { int r; - /* - * Allocate Soft State Resources; if failure, return. - */ - VERIFY0(ddi_soft_state_init(&cpqary3_state, sizeof (cpqary3_t), - MAX_CTLRS)); + VERIFY0(ddi_soft_state_init(&cpqary3_state, sizeof (cpqary3_t), 0)); if ((r = scsi_hba_init(&cpqary3_modlinkage)) != 0) { goto fail; @@ -183,41 +147,27 @@ fail: int _fini() { - int retvalue; - - /* Unload the Driver(loadable module) */ - - if ((retvalue = mod_remove(&cpqary3_modlinkage)) == 0) { + int r; - /* Cancel the registeration for the HBA Interface */ + if ((r = mod_remove(&cpqary3_modlinkage)) == 0) { scsi_hba_fini(&cpqary3_modlinkage); - - /* dealloacte soft state resources of the driver */ ddi_soft_state_fini(&cpqary3_state); } - return (retvalue); + return (r); } int _info(struct modinfo *modinfop) { - /* - * Get the module information. - */ return (mod_info(&cpqary3_modlinkage, modinfop)); } static int cpqary3_attach(dev_info_t *dip, ddi_attach_cmd_t attach_cmd) { - int8_t minor_node_name[14]; - uint32_t instance; - uint32_t retvalue; - cpqary3_t *cpq; /* per-controller */ - ddi_dma_attr_t tmp_dma_attr; - uint_t (*hw_isr)(caddr_t); - uint_t (*sw_isr)(caddr_t); + uint32_t instance; + cpqary3_t *cpq; if (attach_cmd != DDI_ATTACH) { return (DDI_FAILURE); @@ -248,6 +198,8 @@ cpqary3_attach(dev_info_t *dip, ddi_attach_cmd_t attach_cmd) offsetof(cpqary3_command_t, cpcm_link)); list_create(&cpq->cpq_finishq, sizeof (cpqary3_command_t), offsetof(cpqary3_command_t, cpcm_link_finish)); + list_create(&cpq->cpq_abortq, sizeof (cpqary3_command_t), + offsetof(cpqary3_command_t, cpcm_link_abort)); list_create(&cpq->cpq_volumes, sizeof (cpqary3_volume_t), offsetof(cpqary3_volume_t, cplv_link)); list_create(&cpq->cpq_targets, sizeof (cpqary3_target_t), @@ -280,6 +232,22 @@ cpqary3_attach(dev_info_t *dip, ddi_attach_cmd_t attach_cmd) } /* + * Each controller may have a different Scatter/Gather Element count. + * Configure a per-controller set of DMA attributes with the + * appropriate S/G size. + */ + VERIFY(cpq->cpq_sg_cnt > 0); + cpq->cpq_dma_attr = cpqary3_dma_attr_template; + cpq->cpq_dma_attr.dma_attr_sgllen = cpq->cpq_sg_cnt; + + /* + * From this point forward, the controller is able to accept commands + * and (at least by polling) return command submissions. Setting this + * flag allows the rest of the driver to interact with the device. + */ + cpq->cpq_status |= CPQARY3_CTLR_STATUS_RUNNING; + + /* * Now that we have selected a Transport Method, we can configure * the appropriate interrupt handlers. */ @@ -288,41 +256,16 @@ cpqary3_attach(dev_info_t *dip, ddi_attach_cmd_t attach_cmd) goto fail; } - /* - * Allocate HBA transport structure - */ - if ((cpq->cpq_hba_tran = scsi_hba_tran_alloc(dip, - SCSI_HBA_CANSLEEP)) == NULL) { - dev_err(dip, CE_WARN, "scsi_hba_tran_alloc failed"); + if (cpqary3_hba_setup(cpq) != DDI_SUCCESS) { + dev_err(dip, CE_WARN, "SCSI framework setup failed"); goto fail; } - cpq->cpq_init_level |= CPQARY3_INITLEVEL_HBA_ALLOC; /* - * XXX This function should do _all_ of the SCSA HBA driver - * init work. + * Set the appropriate Interrupt Mask Register bits to start + * command completion interrupts from the controller. */ - cpqary3_hba_setup(cpq); - - tmp_dma_attr = cpqary3_dma_attr; - tmp_dma_attr.dma_attr_sgllen = cpq->cpq_sg_cnt; - - /* - * Register the DMA attributes and the transport vectors - * of each instance of the HBA device. - */ - if (scsi_hba_attach_setup(dip, &tmp_dma_attr, cpq->cpq_hba_tran, - SCSI_HBA_TRAN_CLONE) == DDI_FAILURE) { - dev_err(dip, CE_WARN, "scsi_hba_attach_setup failed"); - goto fail; - } - cpq->cpq_init_level |= CPQARY3_INITLEVEL_HBA_ATTACH; - - /* Enable the Controller Interrupt */ cpqary3_intr_set(cpq, B_TRUE); - if (cpq->cpq_host_support & 0x4) { - cpqary3_lockup_intr_set(cpq, B_TRUE); - } /* * Register a periodic function to be called every 15 seconds. @@ -332,14 +275,19 @@ cpqary3_attach(dev_info_t *dip, ddi_attach_cmd_t attach_cmd) 1 * NANOSEC, DDI_IPL_0); cpq->cpq_init_level |= CPQARY3_INITLEVEL_PERIODIC; - /* Report that an Instance of the Driver is Attached Successfully */ - ddi_report_dev(dip); - + /* + * Discover the set of logical volumes attached to this controller: + */ if (cpqary3_discover_logical_volumes(cpq, 30) != 0) { dev_err(dip, CE_WARN, "could not discover logical volumes"); goto fail; } + /* + * Announce the attachment of this controller. + */ + ddi_report_dev(dip); + return (DDI_SUCCESS); fail: @@ -350,33 +298,45 @@ fail: static int cpqary3_detach(dev_info_t *dip, ddi_detach_cmd_t detach_cmd) { - cpqary3_t *cpqary3p; - scsi_hba_tran_t *hba_tran; + scsi_hba_tran_t *tran = (scsi_hba_tran_t *)ddi_get_driver_private(dip); + cpqary3_t *cpq = (cpqary3_t *)tran->tran_hba_private; - /* Return failure, If Command is not DDI_DETACH */ - - if (DDI_DETACH != detach_cmd) { + if (detach_cmd != DDI_DETACH) { return (DDI_FAILURE); } /* - * Get scsi_hba_tran structure. - * Get per controller structure. + * First, check to make sure that all SCSI framework targets have + * detached. */ + mutex_enter(&cpq->cpq_mutex); + if (!list_is_empty(&cpq->cpq_targets)) { + mutex_exit(&cpq->cpq_mutex); + dev_err(cpq->dip, CE_WARN, "cannot detach; targets still " + "using HBA"); + return (DDI_FAILURE); + } - hba_tran = (scsi_hba_tran_t *)ddi_get_driver_private(dip); - cpqary3p = (cpqary3_t *)hba_tran->tran_hba_private; - - /* Flush the cache */ - - cpqary3_flush_cache(cpqary3p); + /* + * Prevent new targets from attaching now: + */ + cpq->cpq_status |= CPQARY3_CTLR_STATUS_DETACHING; + mutex_exit(&cpq->cpq_mutex); - /* Undo cpqary3_attach */ +#if 0 + /* + * Attempt to have the controller flush its write cache out to disk. + * XXX Check for failure? + */ + cpqary3_flush_cache(cpq); +#endif - cpqary3_cleanup(cpqary3p); + /* + * Clean up all remaining resources. + */ + cpqary3_cleanup(cpq); return (DDI_SUCCESS); - } static int @@ -384,7 +344,6 @@ cpqary3_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rval) { cpqary3_t *cpq; - minor_t cpqary3_minor_num; int inst = MINOR2INST(getminor(dev)); int status; @@ -400,6 +359,9 @@ cpqary3_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, } switch (cmd) { + case CPQARY3_IOCTL_PASSTHROUGH: + status = cpqary3_ioctl_passthrough(cpq, arg, mode, rval); + break; default: status = scsi_hba_ioctl(dev, cmd, arg, mode, credp, rval); break; @@ -418,15 +380,9 @@ cpqary3_cleanup(cpqary3_t *cpq) cpq->cpq_init_level &= ~CPQARY3_INITLEVEL_PERIODIC; } - if (cpq->cpq_init_level & CPQARY3_INITLEVEL_HBA_ATTACH) { - (void) scsi_hba_detach(cpq->dip); - cpq->cpq_init_level &= ~CPQARY3_INITLEVEL_HBA_ATTACH; - } + cpqary3_ctlr_teardown(cpq); - if (cpq->cpq_init_level & CPQARY3_INITLEVEL_HBA_ALLOC) { - scsi_hba_tran_free(cpq->cpq_hba_tran); - cpq->cpq_init_level &= ~CPQARY3_INITLEVEL_HBA_ALLOC; - } + cpqary3_hba_teardown(cpq); if (cpq->cpq_init_level & CPQARY3_INITLEVEL_BASIC) { mutex_destroy(&cpq->cpq_mutex); diff --git a/usr/src/uts/common/io/cpqary3/cpqary3.h b/usr/src/uts/common/io/cpqary3/cpqary3.h index 421709a60f..ddb570ec3f 100644 --- a/usr/src/uts/common/io/cpqary3/cpqary3.h +++ b/usr/src/uts/common/io/cpqary3/cpqary3.h @@ -35,23 +35,12 @@ #include <cpqary3_ciss.h> #include <cpqary3_bd.h> +#include <cpqary3_ioctl.h> #ifdef __cplusplus extern "C" { #endif -/* - * Ioctl Commands - */ -#define CPQARY3_IOCTL_CMD ('c' << 4) -#define CPQARY3_IOCTL_BMIC_PASS CPQARY3_IOCTL_CMD | 0x04 -#define CPQARY3_IOCTL_SCSI_PASS CPQARY3_IOCTL_CMD | 0x08 - -/* Some Useful definations */ -#define CPQARY3_FAILURE 0 -#define CPQARY3_SUCCESS 1 - -#define CTLR_SCSI_ID 7 #define CPQARY3_LD_FAILED 1 typedef enum cpqary3_init_level { @@ -62,24 +51,23 @@ typedef enum cpqary3_init_level { CPQARY3_INITLEVEL_INT_ALLOC = (0x1 << 4), CPQARY3_INITLEVEL_INT_ADDED = (0x1 << 5), CPQARY3_INITLEVEL_INT_ENABLED = (0x1 << 6), - CPQARY3_INITLEVEL_HBA_ALLOC = (0x1 << 7), - CPQARY3_INITLEVEL_HBA_ATTACH = (0x1 << 8), + CPQARY3_INITLEVEL_SCSA = (0x1 << 7), } cpqary3_init_level_t; /* - * Commands issued to the controller carry a (generally 32-bit, though with two - * reserved signalling bits) identifying tag number. In order to avoid having - * the controller confuse us by double-reporting the completion of a particular - * tag, we try to reuse them as infrequently as possible. In practice, this - * means looping through a range of values. The minimum and maximum value are - * defined below. + * Commands issued to the controller carry a (generally 32-bit, though with + * two reserved signalling bits) identifying tag number. In order to avoid + * having the controller confuse us by double-reporting the completion of a + * particular tag, we try to reuse them as infrequently as possible. In + * practice, this means looping through a range of values. The minimum and + * maximum value are defined below. */ #define CPQARY3_MIN_TAG_NUMBER 0x00000100 #define CPQARY3_MAX_TAG_NUMBER 0x0fffffff /* * Definitions to support waiting for the controller to converge on a - * particular state; ready or not ready. These are used with + * particular state: ready or not ready. These are used with * cpqary3_ctlr_wait_for_state(). */ #define CPQARY3_WAIT_DELAY_SECONDS 120 @@ -98,7 +86,13 @@ typedef enum cpqary3_ctlr_mode { */ #define MAX_LOGDRV 64 /* Max supported Logical Drivers */ #define MAX_CTLRS 8 /* Max supported Controllers */ -#define MAX_TAPE 28 + +/* + * In addition to Logical Volumes, we also expose the controller at a + * pseudo target address on the SCSI bus we are essentially pretending to be. + */ +#define CPQARY3_CONTROLLER_TARGET 128 + /* * NOTE: When changing the below two entries, Max SG count in cpqary3_ciss.h * should also be changed. @@ -157,31 +151,12 @@ typedef enum cpqary3_ctlr_mode { #define CPQARY3_SWAP(val) ((val >> 8) | ((val & 0xff) << 8)) #define CPQARY3_SEC2HZ(x) drv_usectohz((x) * 1000000) -/* - * Convenient macros for reading/writing Configuration table registers - */ -#define DDI_GET8(ctlr, regp) \ - ddi_get8((ctlr)->ct_handle, (uint8_t *)(regp)) -#define DDI_PUT8(ctlr, regp, value) \ - ddi_put8((ctlr)->ct_handle, (uint8_t *)(regp), (value)) -#define DDI_GET16(ctlr, regp) \ - ddi_get16((ctlr)->ct_handle, (uint16_t *)(regp)) -#define DDI_PUT16(ctlr, regp, value) \ - ddi_put16((ctlr)->ct_handle, (uint16_t *)(regp), (value)) -#define DDI_GET32(ctlr, regp) \ - ddi_get32((ctlr)->ct_handle, (uint32_t *)(regp)) -#define DDI_PUT32(ctlr, regp, value) \ - ddi_put32((ctlr)->ct_handle, (uint32_t *)(regp), (value)) -#define DDI_PUT32_CP(ctlr, regp, value) \ - ddi_put32((ctlr)->cp_handle, (uint32_t *)(regp), (value)) - #define CPQARY3_BUFFER_ERROR_CLEAR 0x0 /* to be used with bioerror */ #define CPQARY3_DMA_NO_CALLBACK 0x0 /* to be used with DMA calls */ #define CPQARY3_DMA_ALLOC_HANDLE_DONE 0x01 #define CPQARY3_DMA_ALLOC_MEM_DONE 0x02 #define CPQARY3_DMA_BIND_ADDR_DONE 0x04 #define CPQARY3_FREE_PHYCTG_MEM 0x07 -#define CPQARY3_SYNCCMD_SEND_WAITSIG (0x0001) /* * Include the driver specific relevant header files here. @@ -262,6 +237,30 @@ struct cpqary3_target { #define INTR_SIMPLE_5I_MASK 0x00000004l #define INTR_SIMPLE_5I_LOCKUP_MASK 0x0000000cl +typedef enum cpqary3_controller_status { + /* + * A Logical Volume discovery is currently occuring. + */ + CPQARY3_CTLR_STATUS_DISCOVERY = (0x1 << 0), + + /* + * An attempt is being made to detach the controller instance. + */ + CPQARY3_CTLR_STATUS_DETACHING = (0x1 << 1), + + /* + * The controller is believed to be functioning correctly. The driver + * is to allow command submission, process interrupts, and perform + * periodic background maintenance. + */ + CPQARY3_CTLR_STATUS_RUNNING = (0x1 << 2), + + /* + * The controller is currently being reset. + */ + CPQARY3_CTLR_STATUS_RESETTING = (0x1 << 3), +} cpqary3_controller_status_t; + /* * Per Controller Structure */ @@ -269,6 +268,7 @@ typedef struct cpqary3 cpqary3_t; struct cpqary3 { dev_info_t *dip; int cpq_instance; + cpqary3_controller_status_t cpq_status; /* * Controller model-specific data. @@ -311,7 +311,11 @@ struct cpqary3 { * Controller Heartbeat Tracking */ uint32_t cpq_last_heartbeat; - clock_t cpq_last_heartbeat_lbolt; + hrtime_t cpq_last_heartbeat_time; + + hrtime_t cpq_last_discovery; + hrtime_t cpq_last_reset_start; + hrtime_t cpq_last_reset_finish; /* * Command object tracking. These lists, and all commands within the @@ -321,6 +325,7 @@ struct cpqary3 { avl_tree_t cpq_inflight; list_t cpq_commands; /* List of all commands. */ list_t cpq_finishq; /* List of completed commands. */ + list_t cpq_abortq; /* List of commands to abort. */ /* * Controller interrupt handler registration. @@ -332,6 +337,8 @@ struct cpqary3 { scsi_hba_tran_t *cpq_hba_tran; + ddi_dma_attr_t cpq_dma_attr; + /* * Access to the I2O Registers: */ @@ -396,47 +403,118 @@ typedef struct cpqary3_command_scsa cpqary3_command_scsa_t; typedef struct cpqary3_pkt cpqary3_pkt_t; typedef enum cpqary3_command_status { + /* + * When a command is submitted to the controller, it is marked USED + * to avoid accidental reuse of the command without reinitialising + * critical fields. The submitted command is also marked INFLIGHT + * to reflect its inclusion in the "cpq_inflight" AVL tree. When + * the command is completed by the controller, INFLIGHT is unset. + */ CPQARY3_CMD_STATUS_USED = (0x1 << 0), CPQARY3_CMD_STATUS_INFLIGHT = (0x1 << 1), + CPQARY3_CMD_STATUS_TIMEOUT = (0x1 << 2), CPQARY3_CMD_STATUS_ERROR = (0x1 << 3), + + /* + * This command has been abandoned by the original submitter. This + * could happen if the command did not complete in a timely fashion. + * When it reaches the finish queue it will be freed without further + * processing. + */ CPQARY3_CMD_STATUS_ABANDONED = (0x1 << 4), + + /* + * This command has made it through the completion queue and had final + * processing performed. + */ CPQARY3_CMD_STATUS_COMPLETE = (0x1 << 5), + + /* + * A polled message will be ignored by the regular processing of the + * completion queue. The blocking function doing the polling is + * responsible for watching the command on which it has set the POLLED + * flag. Regular completion queue processing (which might happen in + * the polling function, or it might happen in the interrupt handler) + * will set POLL_COMPLETE once it is out of the finish queue + * altogether. + */ CPQARY3_CMD_STATUS_POLLED = (0x1 << 6), CPQARY3_CMD_STATUS_POLL_COMPLETE = (0x1 << 7), + + /* + * An abort message has been sent to the controller in an attempt to + * cancel this command. + */ CPQARY3_CMD_STATUS_ABORT_SENT = (0x1 << 8), + + /* + * This command has been passed to our tran_start(9E) handler. + */ CPQARY3_CMD_STATUS_TRAN_START = (0x1 << 9), + + /* + * This command was for a SCSI command that we are explicitly avoiding + * sending to the controller. + */ CPQARY3_CMD_STATUS_TRAN_IGNORED = (0x1 << 10), + + /* + * This command has been submitted once, and subsequently passed to + * cpqary3_command_reuse(). + */ CPQARY3_CMD_STATUS_REUSED = (0x1 << 11), + + /* + * A controller reset has been issued, so a response for this command + * is not expected. If one arrives before the controller reset has + * taken effect, it likely cannot be trusted. + */ + CPQARY3_CMD_STATUS_RESET_SENT = (0x1 << 12), } cpqary3_command_status_t; typedef enum cpqary3_command_type { - CPQARY3_CMDTYPE_NONE = 0, - CPQARY3_CMDTYPE_OS, - CPQARY3_CMDTYPE_SYNCCMD, + CPQARY3_CMDTYPE_INTERNAL = 1, + CPQARY3_CMDTYPE_ABORTQ, + CPQARY3_CMDTYPE_SCSA, } cpqary3_command_type_t; struct cpqary3_command { uint32_t cpcm_tag; cpqary3_command_type_t cpcm_type; + cpqary3_command_status_t cpcm_status; cpqary3_t *cpcm_ctlr; cpqary3_target_t *cpcm_target; list_node_t cpcm_link; /* Linkage for allocated list. */ list_node_t cpcm_link_finish; /* Linkage for completion list. */ + list_node_t cpcm_link_abort; /* Linkage for abort list. */ avl_node_t cpcm_node; /* Inflight AVL membership. */ - clock_t cpcm_time_submit; - clock_t cpcm_time_abort; + hrtime_t cpcm_time_submit; + hrtime_t cpcm_time_complete; - cpqary3_command_status_t cpcm_status; + hrtime_t cpcm_expiry; + + /* + * The time at which an abort message was sent to try and terminate + * this command, as well as the tag of the abort message itself: + */ + hrtime_t cpcm_abort_time; + uint32_t cpcm_abort_tag; + /* + * Ancillary data objects. Only one of these will be allocated for any + * given command, but we nonetheless resist the temptation to use a + * union of pointers in order to make incorrect usage obvious. + */ cpqary3_command_scsa_t *cpcm_scsa; cpqary3_command_internal_t *cpcm_internal; /* - * Physical allocation tracking: + * Physical allocation tracking for the actual command to send to the + * controller. */ cpqary3_phyctg_t cpcm_phyctg; @@ -471,38 +549,6 @@ struct cpqary3_command_scsa { }; -#if 0 -/* cmd_flags */ -#define CFLAG_DMASEND 0x01 -#define CFLAG_CMDIOPB 0x02 -#define CFLAG_DMAVALID 0x04 - -/* - * Driver Private Packet - */ -struct cpqary3_pkt { - struct scsi_pkt *scsi_cmd_pkt; - ddi_dma_win_t prev_winp; - ddi_dma_seg_t prev_segp; - ddi_dma_cookie_t cmd_dmacookies[MAX_PERF_SG_CNT]; - uint32_t cmd_ncookies; - uint32_t cmd_cookie; - uint32_t cmd_cookiecnt; - uint32_t cmd_nwin; - uint32_t cmd_curwin; - off_t cmd_dma_offset; - size_t cmd_dma_len; - size_t cmd_dmacount; - struct buf *bf; - ddi_dma_handle_t cmd_dmahandle; - uint32_t bytes; - uint32_t cmd_flags; - uint32_t cdb_len; - uint32_t scb_len; - cpqary3_command_t *cmd_command; -}; -#endif - /* Driver function definitions */ void cpqary3_periodic(void *); @@ -523,7 +569,7 @@ void cpqary3_build_cmdlist(cpqary3_command_t *cpqary3_cmdpvtp, void cpqary3_lockup_check(cpqary3_t *); void cpqary3_oscmd_complete(cpqary3_command_t *); -void cpqary3_poll_for(cpqary3_t *, cpqary3_command_t *); +int cpqary3_poll_for(cpqary3_t *, cpqary3_command_t *); /* * Memory management. @@ -533,13 +579,6 @@ caddr_t cpqary3_alloc_phyctgs_mem(cpqary3_t *, size_t, uint32_t *, void cpqary3_free_phyctgs_mem(cpqary3_phyctg_t *, uint8_t); /* - * Synchronous command routines. - */ -cpqary3_command_t *cpqary3_synccmd_alloc(cpqary3_t *, size_t, int); -void cpqary3_synccmd_free(cpqary3_t *, cpqary3_command_t *); -int cpqary3_synccmd_send(cpqary3_t *, cpqary3_command_t *, clock_t, int); - -/* * Interrupt service routines. */ int cpqary3_interrupts_setup(cpqary3_t *); @@ -550,19 +589,23 @@ uint32_t cpqary3_isr_hw_simple(caddr_t, caddr_t); * Interrupt enable/disable routines. */ void cpqary3_intr_set(cpqary3_t *, boolean_t); -void cpqary3_lockup_intr_set(cpqary3_t *, boolean_t); /* * Controller initialisation routines. */ int cpqary3_ctlr_init(cpqary3_t *); +void cpqary3_ctlr_teardown(cpqary3_t *); +int cpqary3_ctlr_reset(cpqary3_t *); +int cpqary3_ctlr_ping(cpqary3_t *, int); int cpqary3_ctlr_wait_for_state(cpqary3_t *, cpqary3_wait_state_t); int cpqary3_ctlr_init_simple(cpqary3_t *); +void cpqary3_ctlr_teardown_simple(cpqary3_t *); int cpqary3_cfgtbl_flush(cpqary3_t *); int cpqary3_cfgtbl_transport_has_support(cpqary3_t *, int); void cpqary3_cfgtbl_transport_set(cpqary3_t *, int); int cpqary3_cfgtbl_transport_confirm(cpqary3_t *, int); uint32_t cpqary3_ctlr_get_cmdsoutmax(cpqary3_t *); +uint32_t cpqary3_ctlr_get_maxsgelements(cpqary3_t *); /* * Device enumeration routines. @@ -579,22 +622,32 @@ cpqary3_target_t *cpqary3_target_from_addr(cpqary3_t *, struct scsi_address *); int cpqary3_setcap(struct scsi_address *, char *, int, int); int cpqary3_getcap(struct scsi_address *, char *, int); -void cpqary3_hba_setup(cpqary3_t *); +int cpqary3_hba_setup(cpqary3_t *); +void cpqary3_hba_teardown(cpqary3_t *); void cpqary3_process_finishq(cpqary3_t *); +void cpqary3_process_abortq(cpqary3_t *); /* * Command object management. */ cpqary3_command_t *cpqary3_command_alloc(cpqary3_t *, cpqary3_command_type_t, int); -cpqary3_command_internal_t *cpqary3_command_internal_alloc(cpqary3_t *, - size_t, int); +int cpqary3_command_attach_internal(cpqary3_t *, cpqary3_command_t *, size_t, + int); void cpqary3_command_free(cpqary3_command_t *); cpqary3_command_t *cpqary3_lookup_inflight(cpqary3_t *, uint32_t); void cpqary3_command_reuse(cpqary3_command_t *); /* + * XXX + */ +void cpqary3_write_lun_addr_phys(LUNAddr_t *, boolean_t, unsigned, unsigned); +void cpqary3_write_message_abort_one(cpqary3_command_t *, uint32_t); +void cpqary3_write_message_abort_all(cpqary3_command_t *, LogDevAddr_t *); +void cpqary3_write_message_nop(cpqary3_command_t *, int); + +/* * Device management routines. */ int cpqary3_device_setup(cpqary3_t *); @@ -602,6 +655,11 @@ void cpqary3_device_teardown(cpqary3_t *); uint32_t cpqary3_get32(cpqary3_t *, offset_t); void cpqary3_put32(cpqary3_t *, offset_t, uint32_t); +/* + * Routines for ioctl(2) handling. + */ +int cpqary3_ioctl_passthrough(cpqary3_t *, intptr_t, int, int *); + #ifdef __cplusplus } diff --git a/usr/src/uts/common/io/cpqary3/cpqary3_ciss.c b/usr/src/uts/common/io/cpqary3/cpqary3_ciss.c index b598910f6b..7d425bf637 100644 --- a/usr/src/uts/common/io/cpqary3/cpqary3_ciss.c +++ b/usr/src/uts/common/io/cpqary3/cpqary3_ciss.c @@ -15,6 +15,112 @@ #include "cpqary3.h" + +void +cpqary3_write_lun_addr_phys(LUNAddr_t *lun, boolean_t masked, unsigned bus, + unsigned target) +{ + lun->PhysDev.Mode = masked ? MASK_PERIPHERIAL_DEV_ADDR : + PERIPHERIAL_DEV_ADDR; + + lun->PhysDev.TargetId = target; + lun->PhysDev.Bus = bus; + + bzero(&lun->PhysDev.Target, sizeof (lun->PhysDev.Target)); +} + +void +cpqary3_write_message_common(cpqary3_command_t *cpcm, int type, + int timeout_secs) +{ + switch (type) { + case CISS_MSG_ABORT: + case CISS_MSG_RESET: + case CISS_MSG_NOP: + break; + + default: + panic("unknown message type"); + } + + cpcm->cpcm_va_cmd->Request.Type.Type = CISS_TYPE_MSG; + cpcm->cpcm_va_cmd->Request.Type.Attribute = CISS_ATTR_HEADOFQUEUE; + cpcm->cpcm_va_cmd->Request.Type.Direction = CISS_XFER_NONE; + cpcm->cpcm_va_cmd->Request.Timeout = timeout_secs; + cpcm->cpcm_va_cmd->Request.CDBLen = 16; + cpcm->cpcm_va_cmd->Request.CDB[0] = type; +} + +void +cpqary3_write_message_abort_one(cpqary3_command_t *cpcm, uint32_t tag) +{ + cpqary3_tag_t cisstag; + + /* + * When aborting a particular command, the request is addressed + * to the controller. + */ + cpqary3_write_lun_addr_phys(&cpcm->cpcm_va_cmd->Header.LUN, + B_TRUE, 0, 0); + + cpqary3_write_message_common(cpcm, CISS_MSG_ABORT, 0); + + /* + * Abort a single command. + */ + cpcm->cpcm_va_cmd->Request.CDB[1] = CISS_ABORT_TASK; + + /* + * The CISS Specification says that the tag value for a task-level + * abort should be in the CDB in bytes 4-11. + */ + bzero(&cisstag, sizeof (cisstag)); + cisstag.tag_value = tag; + bcopy(&cisstag, &cpcm->cpcm_va_cmd->Request.CDB[4], + sizeof (cisstag)); +} + +void +cpqary3_write_message_abort_all(cpqary3_command_t *cpcm, LogDevAddr_t *addr) +{ + /* + * When aborting all tasks for a particular Logical Volume, + * the command is addressed not to the controller but to + * the Volume itself. + */ + cpcm->cpcm_va_cmd->Header.LUN.LogDev = *addr; + + cpqary3_write_message_common(cpcm, CISS_MSG_ABORT, 0); + + /* + * Abort all commands for a particular Logical Volume. + */ + cpcm->cpcm_va_cmd->Request.CDB[1] = CISS_ABORT_TASKSET; +} + +void +cpqary3_write_message_reset_ctlr(cpqary3_command_t *cpcm) +{ + cpqary3_write_lun_addr_phys(&cpcm->cpcm_va_cmd->Header.LUN, + B_TRUE, 0, 0); + + cpqary3_write_message_common(cpcm, CISS_MSG_RESET, 0); + + cpcm->cpcm_va_cmd->Request.CDB[1] = CISS_RESET_CTLR; +} + +void +cpqary3_write_message_nop(cpqary3_command_t *cpcm, int timeout_secs) +{ + /* + * No-op messages are always sent to the controller. + */ + cpqary3_write_lun_addr_phys(&cpcm->cpcm_va_cmd->Header.LUN, + B_TRUE, 0, 0); + + cpqary3_write_message_common(cpcm, CISS_MSG_NOP, timeout_secs); +} + /* * This routine is executed every 15 seconds via ddi_periodic_add(9F). It * checks the health of the controller and looks for submitted commands that @@ -24,10 +130,17 @@ void cpqary3_periodic(void *arg) { cpqary3_t *cpq = arg; - clock_t now; cpqary3_command_t *cpcm, *cpcm_next; mutex_enter(&cpq->cpq_mutex); + if (!(cpq->cpq_status & CPQARY3_CTLR_STATUS_RUNNING)) { + /* + * The device is currently not active, e.g. due to an + * in-progress controller reset. + */ + mutex_exit(&cpq->cpq_mutex); + return; + } /* * Check on the health of the controller firmware. Note that if the @@ -44,55 +157,47 @@ cpqary3_periodic(void *arg) /* * Check inflight commands to see if they have timed out. */ - now = ddi_get_lbolt(); for (cpcm = avl_first(&cpq->cpq_inflight); cpcm != NULL; cpcm = cpcm_next) { - cpqary3_command_scsa_t *cpcms; - struct scsi_pkt *pkt; - clock_t exp; - /* * Save the next entry now, in case we need to remove this one * from the AVL tree. */ cpcm_next = AVL_NEXT(&cpq->cpq_inflight, cpcm); - /* - * We only need to process command timeout for commands - * issued on behalf of SCSA. - */ - if (cpcm->cpcm_type != CPQARY3_CMDTYPE_OS || - (cpcms = cpcm->cpcm_scsa) == NULL || - (pkt = cpcms->cpcms_pkt) == NULL) { + if (cpcm->cpcm_status & CPQARY3_CMD_STATUS_POLLED) { + /* + * Polled commands are timed out by the polling + * routine. + */ continue; } - if (pkt->pkt_time == 0) { + if (cpcm->cpcm_status & CPQARY3_CMD_STATUS_ABORT_SENT) { /* - * This SCSI command has no timeout. + * This command has been aborted; either it will + * complete or the controller will be reset. */ continue; } - if (cpcm->cpcm_status & CPQARY3_CMD_STATUS_POLLED) { + if (cpcm->cpcm_expiry == 0) { /* - * Polled commands are timed out by the polling - * routine. + * This command has no expiry time. */ continue; } - exp = CPQARY3_SEC2HZ(pkt->pkt_time) + cpcm->cpcm_time_submit; - if (exp < now) { - /* - * This command has timed out. Set the appropriate - * status bit and push it onto the completion - * queue. - */ + if (gethrtime() > cpcm->cpcm_expiry) { + if (list_link_active(&cpcm->cpcm_link_abort)) { + /* + * Already on the abort queue. + */ + continue; + } + + list_insert_tail(&cpq->cpq_abortq, cpcm); cpcm->cpcm_status |= CPQARY3_CMD_STATUS_TIMEOUT; - cpcm->cpcm_status &= ~CPQARY3_CMD_STATUS_INFLIGHT; - avl_remove(&cpq->cpq_inflight, cpcm); - list_insert_tail(&cpq->cpq_finishq, cpcm); } } @@ -101,6 +206,11 @@ cpqary3_periodic(void *arg) */ (void) cpqary3_process_finishq(cpq); + /* + * Process the abort queue. + */ + (void) cpqary3_process_abortq(cpq); + mutex_exit(&cpq->cpq_mutex); } @@ -130,6 +240,14 @@ cpqary3_submit(cpqary3_t *cpq, cpqary3_command_t *cpcm) VERIFY(MUTEX_HELD(&cpq->cpq_mutex)); /* + * If the controller is currently being reset, do not allow command + * submission. + */ + if (!(cpq->cpq_status & CPQARY3_CTLR_STATUS_RUNNING)) { + return (EIO); + } + + /* * Do not allow submission of more concurrent commands than the * controller supports. */ @@ -138,7 +256,7 @@ cpqary3_submit(cpqary3_t *cpq, cpqary3_command_t *cpcm) } /* - * XXX Synchronise the Command Block DMA resources to ensure that the + * Synchronise the Command Block DMA resources to ensure that the * device has a consistent view before we pass it the address. */ if (ddi_dma_sync(cpcm->cpcm_phyctg.cpqary3_dmahandle, 0, @@ -167,41 +285,53 @@ cpqary3_submit(cpqary3_t *cpq, cpqary3_command_t *cpcm) VERIFY(!(cpcm->cpcm_status & CPQARY3_CMD_STATUS_INFLIGHT)); cpcm->cpcm_status |= CPQARY3_CMD_STATUS_INFLIGHT; - cpcm->cpcm_time_submit = ddi_get_lbolt(); + cpcm->cpcm_time_submit = gethrtime(); switch (cpq->cpq_ctlr_mode) { case CPQARY3_CTLR_MODE_SIMPLE: cpqary3_submit_simple(cpq, cpcm); - break; + return (0); - default: - panic("unknown controller mode"); + case CPQARY3_CTLR_MODE_UNKNOWN: + break; } - - return (0); + panic("unknown controller mode"); } static void cpqary3_process_finishq_one(cpqary3_command_t *cpcm) { + cpqary3_t *cpq = cpcm->cpcm_ctlr; + + VERIFY(!(cpcm->cpcm_status & CPQARY3_CMD_STATUS_COMPLETE)); cpcm->cpcm_status |= CPQARY3_CMD_STATUS_COMPLETE; switch (cpcm->cpcm_type) { - case CPQARY3_CMDTYPE_SYNCCMD: + case CPQARY3_CMDTYPE_INTERNAL: cv_broadcast(&cpcm->cpcm_ctlr->cpq_cv_finishq); return; - case CPQARY3_CMDTYPE_OS: + case CPQARY3_CMDTYPE_SCSA: cpqary3_oscmd_complete(cpcm); return; - default: - break; + case CPQARY3_CMDTYPE_ABORTQ: + /* + * Abort messages sent as part of abort queue processing + * do not require any completion activity. + */ + mutex_exit(&cpq->cpq_mutex); + cpqary3_command_free(cpcm); + mutex_enter(&cpq->cpq_mutex); + return; } panic("unknown command type"); } +/* + * Process commands in the completion queue. + */ void cpqary3_process_finishq(cpqary3_t *cpq) { @@ -211,6 +341,19 @@ cpqary3_process_finishq(cpqary3_t *cpq) while ((cpcm = list_remove_head(&cpq->cpq_finishq)) != NULL) { /* + * Check if this command was in line to be aborted. + */ + if (list_link_active(&cpcm->cpcm_link_abort)) { + /* + * This command was in line, but the controller + * subsequently completed the command before we + * were able to do so. + */ + list_remove(&cpq->cpq_abortq, cpcm); + cpcm->cpcm_status &= ~CPQARY3_CMD_STATUS_TIMEOUT; + } + + /* * Check if this command has been abandoned by the original * submitter. If it has, free it now to avoid a leak. */ @@ -222,8 +365,9 @@ cpqary3_process_finishq(cpqary3_t *cpq) } /* - * XXX Synchronise the Command Block before we read things - * from it? + * Synchronise the Command Block before we read from it, + * to ensure a consistent view of whatever the controller + * left for us. */ if (ddi_dma_sync(cpcm->cpcm_phyctg.cpqary3_dmahandle, 0, cpcm->cpcm_phyctg.real_size, @@ -253,19 +397,132 @@ cpqary3_process_finishq(cpqary3_t *cpq) cv_broadcast(&cpq->cpq_cv_finishq); } +/* + * Process commands in the abort queue. + */ void +cpqary3_process_abortq(cpqary3_t *cpq) +{ + cpqary3_command_t *cpcm; + cpqary3_command_t *abort_cpcm = NULL; + + VERIFY(MUTEX_HELD(&cpq->cpq_mutex)); + + if (list_is_empty(&cpq->cpq_abortq)) { + goto out; + } + +another: + mutex_exit(&cpq->cpq_mutex); + if ((abort_cpcm = cpqary3_command_alloc(cpq, CPQARY3_CMDTYPE_ABORTQ, + KM_NOSLEEP)) == NULL) { + /* + * No resources available to send abort messages. We will + * try again the next time around. + */ + mutex_enter(&cpq->cpq_mutex); + goto out; + } + mutex_enter(&cpq->cpq_mutex); + + while ((cpcm = list_remove_head(&cpq->cpq_abortq)) != NULL) { + if (!(cpcm->cpcm_status & CPQARY3_CMD_STATUS_INFLIGHT)) { + /* + * This message is not currently inflight, so + * no abort is needed. + */ + continue; + } + + if (cpcm->cpcm_status & CPQARY3_CMD_STATUS_ABORT_SENT) { + /* + * An abort message has already been sent for + * this command. + */ + continue; + } + + /* + * Send an abort message for the command. + */ + cpqary3_write_message_abort_one(abort_cpcm, cpcm->cpcm_tag); + if (cpqary3_submit(cpq, abort_cpcm) != 0) { + /* + * The command could not be submitted to the + * controller. Put it back in the abort queue + * and give up for now. + */ + list_insert_head(&cpq->cpq_abortq, cpcm); + goto out; + } + cpcm->cpcm_status |= CPQARY3_CMD_STATUS_ABORT_SENT; + + /* + * Record some debugging information about the abort we + * sent: + */ + cpcm->cpcm_abort_time = gethrtime(); + cpcm->cpcm_abort_tag = abort_cpcm->cpcm_tag; + + /* + * The abort message was sent. Release it and + * allocate another command. + */ + abort_cpcm = NULL; + goto another; + } + +out: + cv_broadcast(&cpq->cpq_cv_finishq); + if (abort_cpcm != NULL) { + mutex_exit(&cpq->cpq_mutex); + cpqary3_command_free(abort_cpcm); + mutex_enter(&cpq->cpq_mutex); + } +} + +int cpqary3_poll_for(cpqary3_t *cpq, cpqary3_command_t *cpcm) { VERIFY(MUTEX_HELD(&cpq->cpq_mutex)); VERIFY(cpcm->cpcm_status & CPQARY3_CMD_STATUS_POLLED); while (!(cpcm->cpcm_status & CPQARY3_CMD_STATUS_POLL_COMPLETE)) { - cv_wait(&cpq->cpq_cv_finishq, &cpq->cpq_mutex); + if (cpcm->cpcm_expiry != 0) { + /* + * This command has an expiry time. Check to see + * if it has already passed: + */ + if (cpcm->cpcm_expiry < gethrtime()) { + return (ETIMEDOUT); + } + } + + if (ddi_in_panic()) { + /* + * When the system is panicking, there are no + * interrupts or other threads. Drive the polling + * loop on our own. + */ + (void) cpqary3_retrieve(cpq); + cpqary3_process_finishq(cpq); + drv_usecwait(100); + continue; + } /* - * XXX If we are ddi_in_panic(), we should drive the - * polling ourselves with a spinloop. + * Wait for command completion to return through the regular + * interrupt handling path. */ + if (cpcm->cpcm_expiry == 0) { + cv_wait(&cpq->cpq_cv_finishq, &cpq->cpq_mutex); + } else { + /* + * Wait only until the expiry time for this command. + */ + (void) cv_timedwait_sig_hrtime(&cpq->cpq_cv_finishq, + &cpq->cpq_mutex, cpcm->cpcm_expiry); + } } /* @@ -274,6 +531,8 @@ cpqary3_poll_for(cpqary3_t *cpq, cpqary3_command_t *cpcm) * referenced again once this call returns. */ cpqary3_process_finishq_one(cpcm); + + return (0); } void @@ -305,27 +564,6 @@ cpqary3_intr_set(cpqary3_t *cpq, boolean_t enabled) cpqary3_put32(cpq, CISS_I2O_INTERRUPT_MASK, imr); } -void -cpqary3_lockup_intr_set(cpqary3_t *cpq, boolean_t enabled) -{ - /* - * Read the Interrupt Mask Register. - */ - uint32_t imr = cpqary3_get32(cpq, CISS_I2O_INTERRUPT_MASK); - - /* - * Enable or disable firmware lockup interrupts from the controller - * based on the flag. - */ - if (enabled) { - imr &= ~cpq->cpq_board->bd_lockup_intrmask; - } else { - imr |= cpq->cpq_board->bd_lockup_intrmask; - } - - cpqary3_put32(cpq, CISS_I2O_INTERRUPT_MASK, imr); -} - /* * Signal to the controller that we have updated the Configuration Table by * writing to the Inbound Doorbell Register. The controller will, after some @@ -439,35 +677,20 @@ cpqary3_cfgtbl_transport_confirm(cpqary3_t *cpq, int xport) } uint32_t -cpqary3_ctlr_get_cmdsoutmax(cpqary3_t *cpq) +cpqary3_ctlr_get_maxsgelements(cpqary3_t *cpq) { - uint32_t val; + return (ddi_get32(cpq->cpq_ct_handle, &cpq->cpq_ct->MaxSGElements)); +} +uint32_t +cpqary3_ctlr_get_cmdsoutmax(cpqary3_t *cpq) +{ return (ddi_get32(cpq->cpq_ct_handle, &cpq->cpq_ct->CmdsOutMax)); } static uint32_t cpqary3_ctlr_get_hostdrvsup(cpqary3_t *cpq) { - if (!cpq->cpq_board->bd_is_e200 && !cpq->cpq_board->bd_is_ssll) { - uint32_t val = ddi_get32(cpq->cpq_ct_handle, - &cpq->cpq_ct->HostDrvrSupport); - - /* - * XXX This is "bit 2" in the "Host Driver Specific Support" - * field of the Configuration Table. According to the CISS - * spec, this is "Interrupt Host upon Controller Lockup" - * Enable. - * - * It's not clear why we _set_ this bit, but then it's not yet - * clear how this table entry is even supposed to work. - */ - val |= 0x04; - - ddi_put32(cpq->cpq_ct_handle, &cpq->cpq_ct->HostDrvrSupport, - val); - } - return (ddi_get32(cpq->cpq_ct_handle, &cpq->cpq_ct->HostDrvrSupport)); } @@ -475,7 +698,6 @@ int cpqary3_ctlr_init(cpqary3_t *cpq) { uint8_t signature[4] = { 'C', 'I', 'S', 'S' }; - CfgTable_t *ctp = cpq->cpq_ct; int e; if ((e = cpqary3_ctlr_wait_for_state(cpq, @@ -497,6 +719,10 @@ cpqary3_ctlr_init(cpqary3_t *cpq) } } + /* + * Initialise an appropriate Transport Method. For now, this driver + * only supports the "Simple" method. + */ if ((e = cpqary3_ctlr_init_simple(cpq)) != 0) { return (e); } @@ -514,14 +740,34 @@ cpqary3_ctlr_init(cpqary3_t *cpq) */ cpq->cpq_last_heartbeat = ddi_get32(cpq->cpq_ct_handle, &cpq->cpq_ct->HeartBeat); - cpq->cpq_last_heartbeat_lbolt = ddi_get_lbolt(); + cpq->cpq_last_heartbeat_time = gethrtime(); - return (0); + return (DDI_SUCCESS); +} + +void +cpqary3_ctlr_teardown(cpqary3_t *cpq) +{ + cpq->cpq_status &= ~CPQARY3_CTLR_STATUS_RUNNING; + + switch (cpq->cpq_ctlr_mode) { + case CPQARY3_CTLR_MODE_SIMPLE: + cpqary3_ctlr_teardown_simple(cpq); + return; + + case CPQARY3_CTLR_MODE_UNKNOWN: + return; + } + + panic("unknown controller mode"); } int cpqary3_ctlr_wait_for_state(cpqary3_t *cpq, cpqary3_wait_state_t state) { + unsigned wait_usec = 100 * 1000; + unsigned wait_count = CPQARY3_WAIT_DELAY_SECONDS * 1000000 / wait_usec; + VERIFY(state == CPQARY3_WAIT_STATE_READY || state == CPQARY3_WAIT_STATE_UNREADY); @@ -534,7 +780,7 @@ cpqary3_ctlr_wait_for_state(cpqary3_t *cpq, cpqary3_wait_state_t state) * second and try again. If the device has not become ready in 300 * seconds, give up. */ - for (unsigned i = 0; i < CPQARY3_WAIT_DELAY_SECONDS; i++) { + for (unsigned i = 0; i < wait_count; i++) { uint32_t spr = cpqary3_get32(cpq, CISS_I2O_SCRATCHPAD); switch (state) { @@ -551,10 +797,18 @@ cpqary3_ctlr_wait_for_state(cpqary3_t *cpq, cpqary3_wait_state_t state) break; } - /* - * Wait for a second and try again. - */ - delay(drv_usectohz(1000000)); + if (ddi_in_panic()) { + /* + * There is no sleep for the panicking, so we + * must spin wait: + */ + drv_usecwait(wait_usec); + } else { + /* + * Wait for a quarter second and try again. + */ + delay(drv_usectohz(wait_usec)); + } } dev_err(cpq->dip, CE_WARN, "time out waiting for controller " @@ -562,3 +816,258 @@ cpqary3_ctlr_wait_for_state(cpqary3_t *cpq, cpqary3_wait_state_t state) "ready": "unready"); return (DDI_FAILURE); } + +void +cpqary3_lockup_check(cpqary3_t *cpq) +{ + /* + * Read the current controller heartbeat value. + */ + uint32_t heartbeat = ddi_get32(cpq->cpq_ct_handle, + &cpq->cpq_ct->HeartBeat); + + VERIFY(MUTEX_HELD(&cpq->cpq_mutex)); + + /* + * Check to see if the value is the same as last time we looked: + */ + if (heartbeat != cpq->cpq_last_heartbeat) { + /* + * The heartbeat value has changed, which suggests that the + * firmware in the controller has not yet come to a complete + * stop. Record the new value, as well as the current time. + */ + cpq->cpq_last_heartbeat = heartbeat; + cpq->cpq_last_heartbeat_time = gethrtime(); + return; + } + + /* + * The controller _might_ have been able to signal to us that is + * has locked up. This is a truly unfathomable state of affairs: + * If the firmware can tell it has flown off the rails, why not + * simply reset the controller? + */ + uint32_t odr = cpqary3_get32(cpq, CISS_I2O_OUTBOUND_DOORBELL_STATUS); + uint32_t spr = cpqary3_get32(cpq, CISS_I2O_SCRATCHPAD); + if ((odr & CISS_ODR_BIT_LOCKUP) != 0) { + dev_err(cpq->dip, CE_PANIC, "HP SmartArray firmware has " + "reported a critical fault (odr %08x spr %08x)", + odr, spr); + } + + if (gethrtime() > cpq->cpq_last_heartbeat_time + 60 * NANOSEC) { + dev_err(cpq->dip, CE_PANIC, "HP SmartArray firmware has " + "stopped responding (odr %08x spr %08x)", + odr, spr); + } +} + +int +cpqary3_ctlr_reset(cpqary3_t *cpq) +{ + cpqary3_command_t *cpcm, *cpcm_nop; + int r; + + /* + * Allocate two commands: one for the soft reset message, which we + * cannot free until the controller has reset; and one for the ping we + * will use to determine when it is once again functional. + */ + if ((cpcm = cpqary3_command_alloc(cpq, CPQARY3_CMDTYPE_INTERNAL, + KM_NOSLEEP)) == NULL) { + return (ENOMEM); + } + if ((cpcm_nop = cpqary3_command_alloc(cpq, CPQARY3_CMDTYPE_INTERNAL, + KM_NOSLEEP)) == NULL) { + cpqary3_command_free(cpcm); + return (ENOMEM); + } + + if (ddi_in_panic()) { + goto skip_check; + } + + mutex_enter(&cpq->cpq_mutex); + if (cpq->cpq_status & CPQARY3_CTLR_STATUS_RESETTING) { + /* + * Don't pile on. One reset is enough. Wait until + * it's complete, and then return success. + */ + while (!(cpq->cpq_status & CPQARY3_CTLR_STATUS_RUNNING)) { + cv_wait(&cpq->cpq_cv_finishq, &cpq->cpq_mutex); + } + mutex_exit(&cpq->cpq_mutex); + cpqary3_command_free(cpcm); + cpqary3_command_free(cpcm_nop); + return (0); + } + cpq->cpq_status |= CPQARY3_CTLR_STATUS_RESETTING; + cpq->cpq_last_reset_start = gethrtime(); + mutex_exit(&cpq->cpq_mutex); + +skip_check: + + /* + * Send a soft reset command to the controller. If this command + * succeeds, there will likely be no completion notification. Instead, + * the device should become unavailable for some period of time and + * then become available again. Once available again, we know the soft + * reset has completed and should abort all in-flight commands. XXX + */ + cpqary3_write_message_reset_ctlr(cpcm); + + mutex_enter(&cpq->cpq_mutex); + + /* + * Disable interrupts now. + */ + cpqary3_intr_set(cpq, B_FALSE); + + dev_err(cpq->dip, CE_WARN, "SENDING SOFT RESET MESSAGE"); + cpcm->cpcm_status |= CPQARY3_CMD_STATUS_POLLED; + if ((r = cpqary3_submit(cpq, cpcm)) != 0) { + dev_err(cpq->dip, CE_PANIC, "could not complete soft reset " + ": submit failed (%d)", r); + } + + /* + * Mark every currently inflight command as being reset, including the + * soft reset command we just sent. Once we confirm the reset works, + * we can safely report that these commands have failed. + */ + for (cpqary3_command_t *t = avl_first(&cpq->cpq_inflight); + t != NULL; t = AVL_NEXT(&cpq->cpq_inflight, t)) { + t->cpcm_status |= CPQARY3_CMD_STATUS_RESET_SENT; + } + + /* + * Now that we have submitted our soft reset command, prevent + * the rest of the driver from interacting with the controller. + */ + cpq->cpq_status &= ~CPQARY3_CTLR_STATUS_RUNNING; + + /* + * We do not expect a completion from the controller for our soft + * reset command, but we also cannot remove it from the inflight + * list until we know the controller has actually reset. To do + * otherwise would potentially allow the controller to scribble + * on the memory we were using. + */ + cpcm->cpcm_status |= CPQARY3_CMD_STATUS_ABANDONED; + + dev_err(cpq->dip, CE_WARN, "WAITING FOR DEVICE TO GO AWAY"); + if (cpqary3_ctlr_wait_for_state(cpq, CPQARY3_WAIT_STATE_UNREADY) != + DDI_SUCCESS) { + dev_err(cpq->dip, CE_PANIC, "could not complete soft reset " + ": controller did not go offline"); + } + + dev_err(cpq->dip, CE_WARN, "WAITING FOR DEVICE TO COME BACK"); + if (cpqary3_ctlr_wait_for_state(cpq, CPQARY3_WAIT_STATE_READY) != + DDI_SUCCESS) { + dev_err(cpq->dip, CE_PANIC, "could not complete soft reset " + ": controller did not come back online"); + } + + /* + * In at least the Smart Array P420i, the controller can take 30-45 + * seconds after the scratchpad register shows it as being available + * before it is ready to receive commands. In order to avoid hitting + * it too early with our post-reset ping, we will sleep for 10 seconds + * here. + */ + dev_err(cpq->dip, CE_WARN, "SLEEPING FOR CONTROLLER TO COME BACK"); + if (ddi_in_panic()) { + drv_usecwait(10 * MICROSEC); + } else { + delay(drv_usectohz(10 * MICROSEC)); + } + + dev_err(cpq->dip, CE_WARN, "REINIT DEVICE"); + cpqary3_ctlr_teardown(cpq); + if (cpqary3_ctlr_init(cpq) != DDI_SUCCESS) { + dev_err(cpq->dip, CE_PANIC, "could not complete soft reset " + ": controller transport could not be configured"); + } + + dev_err(cpq->dip, CE_WARN, "SEND NOP MESSAGE"); + cpqary3_write_message_nop(cpcm_nop, 0); + cpcm_nop->cpcm_status |= CPQARY3_CMD_STATUS_POLLED; + cpq->cpq_status |= CPQARY3_CTLR_STATUS_RUNNING; + if ((r = cpqary3_submit(cpq, cpcm_nop)) != 0) { + dev_err(cpq->dip, CE_PANIC, "could not complete soft reset " + ": post-reset ping could not be submitted (%d)", r); + } + cpq->cpq_status &= ~CPQARY3_CTLR_STATUS_RUNNING; + + dev_err(cpq->dip, CE_WARN, "WAITING TO SEE IF WE GET A COMPLETION"); + for (int i = 0; i < 500; i++) { + /* + * Interrupts are still masked at this stage. The controller + * should start up in the Simple Transport mode, so poll + * manually for a bit: + */ + dev_err(cpq->dip, CE_WARN, "TRY #%d...", i); + cpqary3_retrieve_simple(cpq); + cpqary3_process_finishq(cpq); + if (cpcm_nop->cpcm_status & CPQARY3_CMD_STATUS_POLL_COMPLETE) { + cpqary3_process_finishq_one(cpcm_nop); + dev_err(cpq->dip, CE_WARN, "GOT A COMPLETION!"); + + VERIFY(cpcm_nop->cpcm_status & + CPQARY3_CMD_STATUS_COMPLETE); + if (cpcm_nop->cpcm_status & CPQARY3_CMD_STATUS_ERROR) { + dev_err(cpq->dip, CE_WARN, "BUT, AN ERROR!"); + } + break; + } + drv_usecwait(100 * 1000); + } + + if (!(cpcm_nop->cpcm_status & CPQARY3_CMD_STATUS_COMPLETE)) { + dev_err(cpq->dip, CE_PANIC, "could not complete soft reset " + ": post-reset ping was not returned"); + } + + /* + * Now that the controller is working again, we can abort any + * commands that were inflight during the reset. + */ + cpqary3_command_t *nt; + for (cpqary3_command_t *t = avl_first(&cpq->cpq_inflight); + t != NULL; t = nt) { + nt = AVL_NEXT(&cpq->cpq_inflight, t); + + if (t->cpcm_status & CPQARY3_CMD_STATUS_RESET_SENT) { + avl_remove(&cpq->cpq_inflight, t); + t->cpcm_status &= ~CPQARY3_CMD_STATUS_INFLIGHT; + + list_insert_tail(&cpq->cpq_finishq, t); + } + } + + /* + * Re-enable interrupts, mark the controller running and + * the reset as complete.... + */ + cpqary3_intr_set(cpq, B_TRUE); + cpq->cpq_status |= CPQARY3_CTLR_STATUS_RUNNING; + cpq->cpq_status &= ~CPQARY3_CTLR_STATUS_RESETTING; + cpq->cpq_last_reset_finish = gethrtime(); + + /* + * Process the completion queue one last time before we let go + * of the mutex. + */ + cpqary3_process_finishq(cpq); + + /* + * Wake anybody that was waiting for the reset to complete. + */ + cv_broadcast(&cpq->cpq_cv_finishq); + mutex_exit(&cpq->cpq_mutex); + + cpqary3_command_free(cpcm_nop); + return (0); +} diff --git a/usr/src/uts/common/io/cpqary3/cpqary3_ciss_simple.c b/usr/src/uts/common/io/cpqary3/cpqary3_ciss_simple.c index d16b2530cb..578d361367 100644 --- a/usr/src/uts/common/io/cpqary3/cpqary3_ciss_simple.c +++ b/usr/src/uts/common/io/cpqary3/cpqary3_ciss_simple.c @@ -22,6 +22,14 @@ cpqary3_isr_hw_simple(caddr_t arg1, caddr_t arg2) uint32_t isr = cpqary3_get32(cpq, CISS_I2O_INTERRUPT_STATUS); mutex_enter(&cpq->cpq_mutex); + if (!(cpq->cpq_status & CPQARY3_CTLR_STATUS_RUNNING)) { + /* + * We should not be receiving interrupts from the controller + * while the driver is not running. + */ + mutex_exit(&cpq->cpq_mutex); + return (DDI_INTR_UNCLAIMED); + } /* * Check to see if this interrupt came from the device: @@ -78,6 +86,7 @@ cpqary3_retrieve_simple(cpqary3_t *cpq) if (CISS_OPQ_READ_ERROR(opq) != 0) { cpcm->cpcm_status |= CPQARY3_CMD_STATUS_ERROR; } + cpcm->cpcm_time_complete = gethrtime(); /* * Push this command onto the completion queue. @@ -119,9 +128,21 @@ cpqary3_ctlr_init_simple(cpqary3_t *cpq) } /* - * XXX ? + * Determine the number of Scatter/Gather List entries this controller + * supports. The maximum number we allow is CISS_MAXSGENTRIES: the + * number of elements in the static struct we use for command + * submission. */ - cpq->cpq_sg_cnt = CPQARY3_SG_CNT; + if ((cpq->cpq_sg_cnt = cpqary3_ctlr_get_maxsgelements(cpq)) == 0) { + /* + * The CISS specification states that if this value is + * zero, we should assume a value of 31 for compatibility + * with older firmware. + */ + cpq->cpq_sg_cnt = 31; + } else if (cpq->cpq_sg_cnt > CISS_MAXSGENTRIES) { + cpq->cpq_sg_cnt = CISS_MAXSGENTRIES; + } /* * Zero the upper 32 bits of the address in the Controller. @@ -156,3 +177,16 @@ cpqary3_ctlr_init_simple(cpqary3_t *cpq) return (DDI_SUCCESS); } + +void +cpqary3_ctlr_teardown_simple(cpqary3_t *cpq) +{ + VERIFY(cpq->cpq_ctlr_mode == CPQARY3_CTLR_MODE_SIMPLE); + + /* + * Due to the nominal simplicity of the simple mode, we have no + * particular teardown to perform as we do not allocate anything + * on the way up. + */ + cpq->cpq_ctlr_mode = CPQARY3_CTLR_MODE_UNKNOWN; +} diff --git a/usr/src/uts/common/io/cpqary3/cpqary3_commands.c b/usr/src/uts/common/io/cpqary3/cpqary3_commands.c index 23658c652e..19971e099f 100644 --- a/usr/src/uts/common/io/cpqary3/cpqary3_commands.c +++ b/usr/src/uts/common/io/cpqary3/cpqary3_commands.c @@ -37,6 +37,23 @@ cpqary3_set_new_tag(cpqary3_t *cpq, cpqary3_command_t *cpcm) } } +static int +cpqary3_check_command_type(cpqary3_command_type_t type) +{ + /* + * Note that we leave out the default case in order to utilise + * compiler warnings about missed enum values. + */ + switch (type) { + case CPQARY3_CMDTYPE_ABORTQ: + case CPQARY3_CMDTYPE_SCSA: + case CPQARY3_CMDTYPE_INTERNAL: + return (type); + } + + panic("unexpected command type"); +} + cpqary3_command_t * cpqary3_command_alloc(cpqary3_t *cpq, cpqary3_command_type_t type, int kmflags) @@ -50,15 +67,7 @@ cpqary3_command_alloc(cpqary3_t *cpq, cpqary3_command_type_t type, } cpcm->cpcm_ctlr = cpq; - - switch (type) { - case CPQARY3_CMDTYPE_OS: - case CPQARY3_CMDTYPE_SYNCCMD: - break; - default: - panic("unexpected type"); - } - cpcm->cpcm_type = type; + cpcm->cpcm_type = cpqary3_check_command_type(type); mutex_enter(&cpq->cpq_mutex); cpqary3_set_new_tag(cpq, cpcm); @@ -120,26 +129,34 @@ cpqary3_command_alloc(cpqary3_t *cpq, cpqary3_command_type_t type, return (cpcm); } -cpqary3_command_internal_t * -cpqary3_command_internal_alloc(cpqary3_t *cpq, size_t len, int kmflags) +int +cpqary3_command_attach_internal(cpqary3_t *cpq, cpqary3_command_t *cpcm, + size_t len, int kmflags) { cpqary3_command_internal_t *cpcmi; VERIFY(kmflags == KM_SLEEP || kmflags == KM_NOSLEEP); if ((cpcmi = kmem_zalloc(sizeof (*cpcmi), kmflags)) == NULL) { - return (NULL); + return (ENOMEM); } if ((cpcmi->cpcmi_va = (void *)cpqary3_alloc_phyctgs_mem(cpq, len, &cpcmi->cpcmi_pa, &cpcmi->cpcmi_phyctg, kmflags)) == NULL) { kmem_free(cpcmi, sizeof (*cpcmi)); - return (NULL); + return (ENOMEM); } bzero(cpcmi->cpcmi_va, cpcmi->cpcmi_len); - return (cpcmi); + cpcm->cpcm_internal = cpcmi; + + cpcm->cpcm_va_cmd->SG[0].Addr = cpcmi->cpcmi_pa; + cpcm->cpcm_va_cmd->SG[0].Len = len; + cpcm->cpcm_va_cmd->Header.SGList = 1; + cpcm->cpcm_va_cmd->Header.SGTotal = 1; + + return (0); } void @@ -193,3 +210,15 @@ cpqary3_command_free(cpqary3_command_t *cpcm) kmem_free(cpcm, sizeof (*cpcm)); } + +cpqary3_command_t * +cpqary3_lookup_inflight(cpqary3_t *cpq, uint32_t tag) +{ + VERIFY(MUTEX_HELD(&cpq->cpq_mutex)); + + cpqary3_command_t srch; + + srch.cpcm_tag = tag; + + return (avl_find(&cpq->cpq_inflight, &srch, NULL)); +} diff --git a/usr/src/uts/common/io/cpqary3/cpqary3_hba.c b/usr/src/uts/common/io/cpqary3/cpqary3_hba.c index c8b83c8aa3..53089a3f7c 100644 --- a/usr/src/uts/common/io/cpqary3/cpqary3_hba.c +++ b/usr/src/uts/common/io/cpqary3/cpqary3_hba.c @@ -15,6 +15,13 @@ #include "cpqary3.h" +static boolean_t +cpqary3_device_is_controller(struct scsi_device *sd) +{ + return (sd->sd_address.a_target == CPQARY3_CONTROLLER_TARGET && + sd->sd_address.a_lun == 0); +} + static int cpqary3_tran_tgt_init(dev_info_t *hba_dip, dev_info_t *tgt_dip, scsi_hba_tran_t *hba_tran, struct scsi_device *sd) @@ -24,7 +31,7 @@ cpqary3_tran_tgt_init(dev_info_t *hba_dip, dev_info_t *tgt_dip, cpqary3_target_t *cptg; /* - * XXX Check to see if new logical volumes are available. + * Check to see if new logical volumes are available. */ if (cpqary3_discover_logical_volumes(cpq, 15) != 0) { dev_err(cpq->dip, CE_WARN, "discover logical volumes failure"); @@ -37,20 +44,30 @@ cpqary3_tran_tgt_init(dev_info_t *hba_dip, dev_info_t *tgt_dip, return (DDI_FAILURE); } + mutex_enter(&cpq->cpq_mutex); + + if (cpq->cpq_status & CPQARY3_CTLR_STATUS_DETACHING) { + /* + * We are detaching. Do not accept any more requests to + * attach targets from the framework. + */ + mutex_exit(&cpq->cpq_mutex); + kmem_free(cptg, sizeof (*cptg)); + return (DDI_FAILURE); + } + /* - * XXX Expose the controller as a target... SIGH + * Check to see if this is the SCSI address of the pseudo target + * representing the Smart Array controller itself. */ - if (sd->sd_address.a_target == 7 && sd->sd_address.a_lun == 0) { + if (cpqary3_device_is_controller(sd)) { cptg->cptg_controller_target = B_TRUE; - mutex_enter(&cpq->cpq_mutex); - list_insert_tail(&cpq->cpq_targets, cptg); - goto skip; + goto skip_logvol; } /* * Look for a logical volume for the SCSI address of this target. */ - mutex_enter(&cpq->cpq_mutex); if ((cplv = cpqary3_lookup_volume_by_addr(cpq, &sd->sd_address)) == NULL) { mutex_exit(&cpq->cpq_mutex); @@ -61,27 +78,24 @@ cpqary3_tran_tgt_init(dev_info_t *hba_dip, dev_info_t *tgt_dip, cptg->cptg_volume = cplv; list_insert_tail(&cplv->cplv_targets, cptg); -skip: +skip_logvol: + /* + * Link this target object to the controller: + */ + cptg->cptg_ctlr = cpq; + list_insert_tail(&cpq->cpq_targets, cptg); + cptg->cptg_scsi_dev = sd; - VERIFY(sd->sd_dev == tgt_dip); /* XXX */ + VERIFY(sd->sd_dev == tgt_dip); /* * We passed SCSI_HBA_TRAN_CLONE to scsi_hba_attach(9F), so * we can stash our target-specific data structure on the - * (cloned) "hba_tran" without affecting the HBA-level - * private data pointer. + * (cloned) "hba_tran" without affecting the private + * private data pointers of the HBA or of other targets. */ hba_tran->tran_tgt_private = cptg; - /* - * XXX - * Note that we used to turn on these caps: - * CPQARY3_CAP_DISCON_ENABLED - * CPQARY3_CAP_SYNC_ENABLED - * CPQARY3_CAP_WIDE_XFER_ENABLED - * CPQARY3_CAP_ARQ_ENABLED - */ - mutex_exit(&cpq->cpq_mutex); return (DDI_SUCCESS); } @@ -96,20 +110,21 @@ cpqary3_tran_tgt_free(dev_info_t *hba_dip, dev_info_t *tgt_dip, VERIFY(cptg->cptg_scsi_dev == sd); + mutex_enter(&cpq->cpq_mutex); + /* * XXX Make sure that there are no outstanding commands for this * target. */ - mutex_enter(&cpq->cpq_mutex); - if (cptg->cptg_controller_target) { - /* - * XXX - */ - list_remove(&cpq->cpq_targets, cptg); - } else { + /* + * Remove this target from the tracking lists: + */ + if (!cptg->cptg_controller_target) { list_remove(&cplv->cplv_targets, cptg); } + list_remove(&cpq->cpq_targets, cptg); + mutex_exit(&cpq->cpq_mutex); kmem_free(cptg, sizeof (*cptg)); @@ -154,23 +169,23 @@ cpqary3_tran_setup_pkt(struct scsi_pkt *pkt, int (*callback)(caddr_t), * The SCB is the "SenseInfo[]" member of the "ErrorInfo_t". * This is statically allocated; make sure it is big enough. */ - dev_err(cpq->dip, CE_WARN, "oversize SENSE BYTES: had %u, " - "needed %u", CISS_SENSEINFOBYTES, pkt->pkt_scblen); + dev_err(cpq->dip, CE_WARN, "oversize SCB: had %u, needed %u", + CISS_SENSEINFOBYTES, pkt->pkt_scblen); return (-1); } /* * Allocate our command block: */ - if ((cpcm = cpqary3_command_alloc(cpq, CPQARY3_CMDTYPE_OS, kmflags)) == - NULL) { + if ((cpcm = cpqary3_command_alloc(cpq, CPQARY3_CMDTYPE_SCSA, + kmflags)) == NULL) { return (-1); } cpcm->cpcm_scsa = cpcms; cpcms->cpcms_command = cpcm; cpcms->cpcms_pkt = pkt; - pkt->pkt_cdbp = cpcm->cpcm_va_cmd->Request.CDB; + pkt->pkt_cdbp = &cpcm->cpcm_va_cmd->Request.CDB[0]; cpcm->cpcm_va_cmd->Request.CDBLen = pkt->pkt_cdblen; pkt->pkt_scbp = (uchar_t *)&cpcm->cpcm_va_err->SenseInfo; @@ -190,8 +205,6 @@ cpqary3_tran_setup_pkt(struct scsi_pkt *pkt, int (*callback)(caddr_t), static void cpqary3_tran_teardown_pkt(struct scsi_pkt *pkt) { - scsi_hba_tran_t *tran = pkt->pkt_address.a_hba_tran; - cpqary3_t *cpq = (cpqary3_t *)tran->tran_hba_private; cpqary3_command_scsa_t *cpcms = (cpqary3_command_scsa_t *) pkt->pkt_ha_private; cpqary3_command_t *cpcm = cpcms->cpcms_command; @@ -255,8 +268,10 @@ cpqary3_tran_start(struct scsi_address *sa, struct scsi_pkt *pkt) case SCMD_MODE_SELECT: case SCMD_PERSISTENT_RESERVE_IN: cpcm->cpcm_status |= CPQARY3_CMD_STATUS_TRAN_IGNORED; + dev_err(cpq->dip, CE_WARN, "ignored SCSI cmd %02x", (unsigned)pkt->pkt_cdbp[0]); /* XXX */ + cpqary3_set_arq_data(pkt, KEY_ILLEGAL_REQUEST); pkt->pkt_reason = CMD_BADMSG; pkt->pkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET | @@ -296,15 +311,11 @@ cpqary3_tran_start(struct scsi_address *sa, struct scsi_pkt *pkt) if (cpcm->cpcm_target->cptg_controller_target) { /* - * XXX use cpqary3_write_lun_addr_phys() + * The controller is, according to the CISS Specification, + * always LUN 0 in the peripheral device addressing mode. */ - LUNAddr_t *lun = &cpcm->cpcm_va_cmd->Header.LUN; - - lun->PhysDev.Mode = MASK_PERIPHERIAL_DEV_ADDR; - lun->PhysDev.TargetId = 0; - lun->PhysDev.Bus = 0; - - bzero(&lun->PhysDev.Target, sizeof (lun->PhysDev.Target)); + cpqary3_write_lun_addr_phys(&cpcm->cpcm_va_cmd->Header.LUN, + B_TRUE, 0, 0); } else { /* * Copy logical volume address from the target object: @@ -353,25 +364,15 @@ cpqary3_tran_start(struct scsi_address *sa, struct scsi_pkt *pkt) pkt->pkt_state = 0; /* - * XXX Synchronise DMA for device to see changes to the command - * block...? XXX this is being done in cpqary3_submit() now ... + * If this SCSI packet has a timeout, configure an appropriate + * expiry time: */ -#if 0 - if (ddi_dma_sync(cpcm->cpcm_phyctg->cpqary3_dmahandle, 0, - sizeof (CommandList_t), DDI_DMA_SYNC_FORDEV) != DDI_SUCCESS) { - dev_err(cpq->cpq, CE_WARN, "DMA sync failure"); - return (TRAN_FATAL_ERROR); + if (pkt->pkt_time != 0) { + cpcm->cpcm_expiry = gethrtime() + pkt->pkt_time * NANOSEC; } -#endif /* - * XXX I don't _think_ we need to synchronise the DMA stuff we - * were _passed_ (in the SCSI packet). Need to make sure, though. - * I think the documentation could be clearer about this... - */ - - /* - * Submit the command to the controller! + * Submit the command to the controller. */ mutex_enter(&cpq->cpq_mutex); if ((r = cpqary3_submit(cpq, cpcm)) != 0) { @@ -407,75 +408,109 @@ cpqary3_tran_start(struct scsi_address *sa, struct scsi_pkt *pkt) return (TRAN_ACCEPT); } - -#if 0 -static struct scsi_pkt * -cpqary3_tran_init_pkt(struct scsi_address *sa, struct scsi_pkt *pkt, - struct buf *bp, int cmdlen, int statuslen, int tgtlen, - int flags, int (*callback)(), caddr_t arg) +static int +cpqary3_tran_reset(struct scsi_address *sa, int level) { - cpqary3_t *cpq = (cpqary3_t *)sa->a_hba_tran->tran_hba_private; - cpqary3_target_t *cptg = (cpqary3_target_t *)sa->a_hba_tran-> - tran_tgt_private; - boolean_t allocated_packet = B_FALSE; - cpqary3_command_scsa_t *cpcms; + scsi_hba_tran_t *tran = sa->a_hba_tran; + cpqary3_t *cpq = (cpqary3_t *)tran->tran_hba_private; + int r; + cpqary3_command_t *cpcm; - if (pkt == NULL) { + /* + * The framework has requested some kind of SCSI reset. A + * controller-level soft reset can take a very long time -- often on + * the order of 30-60 seconds -- but might well be our only option if + * the controller is non-responsive. + * + * First, check if the controller is responding to pings. + */ +again: + if ((cpcm = cpqary3_command_alloc(cpq, CPQARY3_CMDTYPE_INTERNAL, + KM_NOSLEEP)) == NULL) { + return (0); + } + + cpqary3_write_message_nop(cpcm, 15); + + mutex_enter(&cpq->cpq_mutex); + if (ddi_in_panic()) { + goto skip_check; + } + + if (cpq->cpq_status & CPQARY3_CTLR_STATUS_RESETTING) { /* - * The framework requires we allocate a new packet - * structure via scsi_hba_pkt_alloc(9F). + * The controller is already resetting. Wait for that + * to finish. */ - if ((pkt = scsi_hba_pkt_alloc(cpq->dip, sa, cmdlen, statuslen, - tgtlen, sizeof (*cpcms), callback, arg)) == NULL) { - return (NULL); + while (cpq->cpq_status & CPQARY3_CTLR_STATUS_RESETTING) { + cv_wait(&cpq->cpq_cv_finishq, &cpq->cpq_mutex); } - allocated_packet = B_TRUE; } - cpcms = (cpqary3_command_scsa_t *)pkt->pkt_ha_private; - if (allocated_packet) { - /* - * Our private SCSI packet object was just allocated by - * scsi_hba_pkt_alloc(9F). Initialise it: - */ - cpcms->cpcms_pkt = pkt; +skip_check: + /* + * Submit our ping to the controller. + */ + cpcm->cpcm_status |= CPQARY3_CMD_STATUS_POLLED; + cpcm->cpcm_expiry = gethrtime() + 15 * NANOSEC; + if (cpqary3_submit(cpq, cpcm) != 0) { + mutex_exit(&cpq->cpq_mutex); + cpqary3_command_free(cpcm); + return (0); } - if (bp != NULL && allocated_packet) { + if ((r = cpqary3_poll_for(cpq, cpcm)) == 0 && + !(cpcm->cpcm_status & CPQARY3_CMD_STATUS_ERROR) && + !(cpcm->cpcm_status & CPQARY3_CMD_STATUS_RESET_SENT)) { /* - * This is a new packet with an associated buffer. The - * framework requires us to allocate appropriate DMA - * resources. + * The controller is responsive, and a full soft reset would be + * extremely disruptive to the system. Given our spotty + * support for some SCSI commands (which can upset the target + * drivers) and the historically lax behaviour of the "cpqary3" + * driver, we grit our teeth and pretend we were able to + * perform a reset. */ - if (cpqary3_dma_alloc(cpq, pkt, bp, flags, callback) != - DDI_SUCCESS) { - scsi_hba_pkt_free(sa, scsi_pktp); - return (NULL); - } - } else if (bp != NULL && !allocated_packet && - (flags & PKT_DMA_PARTIAL) != 0) { + mutex_exit(&cpq->cpq_mutex); + cpqary3_command_free(cpcm); + return (1); + } + + if (!(cpcm->cpcm_status & CPQARY3_CMD_STATUS_POLL_COMPLETE)) { + cpcm->cpcm_status |= CPQARY3_CMD_STATUS_ABANDONED; + } else { + mutex_exit(&cpq->cpq_mutex); + cpqary3_command_free(cpcm); + mutex_enter(&cpq->cpq_mutex); + } + + /* + * If a reset has been initiated in the last 90 seconds, try + * another ping. + */ + if (gethrtime() < cpq->cpq_last_reset_start + 90 * NANOSEC) { + dev_err(cpq->dip, CE_WARN, "controller ping failed, but was " + "recently reset; retrying ping"); + mutex_exit(&cpq->cpq_mutex); + /* - * This is not a new packet, but a buffer was passed in and we - * had previously allocated DMA resources. This is a request - * from the framework to move the DMA resources. + * Sleep for a second first. */ - if (cpqary3_dma_move(scsi_pktp, bp, cpq) != DDI_SUCCESS) { - return (NULL); + if (ddi_in_panic()) { + drv_usecwait(1 * MICROSEC); + } else { + delay(drv_usectohz(1 * MICROSEC)); } + goto again; } - return (scsi_pktp); -} -#endif + dev_err(cpq->dip, CE_WARN, "controller ping failed; " + "resetting controller"); + if (cpqary3_ctlr_reset(cpq) != 0) { + dev_err(cpq->dip, CE_WARN, "controller reset failure"); + return (0); + } -static int -cpqary3_tran_reset(struct scsi_address *sa, int level) -{ - /* - * We currently have no earthly idea how to reset the controller. - * Signal our universal, abject failure to the SCSI framework. - */ - return (0); + return (1); } static int @@ -483,31 +518,128 @@ cpqary3_tran_abort(struct scsi_address *sa, struct scsi_pkt *pkt) { scsi_hba_tran_t *tran = sa->a_hba_tran; cpqary3_t *cpq = (cpqary3_t *)tran->tran_hba_private; - cpqary3_target_t *cptg = (cpqary3_target_t *)tran->tran_tgt_private; cpqary3_command_t *cpcm = NULL; - int r; + cpqary3_command_t *abort_cpcm; + if ((abort_cpcm = cpqary3_command_alloc(cpq, CPQARY3_CMDTYPE_INTERNAL, + KM_NOSLEEP)) == NULL) { + /* + * No resources available to send an abort message. + */ + return (0); + } + + mutex_enter(&cpq->cpq_mutex); if (pkt != NULL) { + /* + * The framework wants us to abort a specific SCSI packet. + */ cpqary3_command_scsa_t *cpcms = (cpqary3_command_scsa_t *) pkt->pkt_ha_private; cpcm = cpcms->cpcms_command; + + if (!(cpcm->cpcm_status & CPQARY3_CMD_STATUS_INFLIGHT)) { + /* + * This message is not currently inflight, so we + * cannot abort it. + */ + goto fail; + } + + if (cpcm->cpcm_status & CPQARY3_CMD_STATUS_ABORT_SENT) { + /* + * An abort message for this command has already been + * sent to the controller. Return failure. + */ + goto fail; + } + + cpqary3_write_message_abort_one(abort_cpcm, cpcm->cpcm_tag); + } else { + /* + * The framework wants us to abort every inflight command + * for the target with this address. + */ + cpqary3_target_t *cptg = (cpqary3_target_t *)tran-> + tran_tgt_private; + + if (cptg->cptg_volume == NULL) { + /* + * We currently do not support sending an abort + * to anything but a Logical Volume. + */ + goto fail; + } + + cpqary3_write_message_abort_all(abort_cpcm, + &cptg->cptg_volume->cplv_addr); } - mutex_enter(&cpq->cpq_mutex); /* - * XXX + * Submit the abort message to the controller. + */ + abort_cpcm->cpcm_status |= CPQARY3_CMD_STATUS_POLLED; + if (cpqary3_submit(cpq, abort_cpcm) != 0) { + goto fail; + } + + if (pkt != NULL) { + /* + * Record some debugging information about the abort we + * sent: + */ + cpcm->cpcm_abort_time = gethrtime(); + cpcm->cpcm_abort_tag = abort_cpcm->cpcm_tag; + + /* + * Mark the command as aborted so that we do not send + * a second abort message: + */ + cpcm->cpcm_status |= CPQARY3_CMD_STATUS_ABORT_SENT; + } + + /* + * Poll for completion of the abort message. Note that this function + * only fails if we set a timeout on the command, which we have not + * done. + */ + VERIFY0(cpqary3_poll_for(cpq, abort_cpcm)); + + if ((abort_cpcm->cpcm_status & CPQARY3_CMD_STATUS_RESET_SENT) || + (abort_cpcm->cpcm_status & CPQARY3_CMD_STATUS_ERROR)) { + /* + * Either the controller was reset or the abort command + * failed. + */ + goto fail; + } + + /* + * The command was successfully aborted. */ - r = cpqary3_send_abortcmd(cpq, cptg, cpcm); mutex_exit(&cpq->cpq_mutex); + cpqary3_command_free(abort_cpcm); + return (1); - return (r == 0 ? 1 : 0); +fail: + mutex_exit(&cpq->cpq_mutex); + cpqary3_command_free(abort_cpcm); + return (0); } -void +int cpqary3_hba_setup(cpqary3_t *cpq) { - scsi_hba_tran_t *tran = cpq->cpq_hba_tran; + scsi_hba_tran_t *tran; + if ((tran = scsi_hba_tran_alloc(cpq->dip, SCSI_HBA_CANSLEEP)) == + NULL) { + dev_err(cpq->dip, CE_WARN, "could not allocate SCSA " + "resources"); + return (DDI_FAILURE); + } + + cpq->cpq_hba_tran = tran; tran->tran_hba_private = cpq; tran->tran_tgt_init = cpqary3_tran_tgt_init; @@ -524,26 +656,34 @@ cpqary3_hba_setup(cpqary3_t *cpq) tran->tran_getcap = cpqary3_getcap; tran->tran_setcap = cpqary3_setcap; -#if 0 - /* - * XXX Old style: - */ - tran->tran_init_pkt = XXX; - tran->tran_destroy_pkt = XXX; - tran->tran_dmafree = XXX; - tran->tran_sync_pkt = XXX; -#else - /* - * XXX New style: - */ tran->tran_setup_pkt = cpqary3_tran_setup_pkt; tran->tran_teardown_pkt = cpqary3_tran_teardown_pkt; tran->tran_hba_len = sizeof (cpqary3_command_scsa_t); -#endif /* * XXX We should set "tran_interconnect_type" appropriately. * e.g. to INTERCONNECT_SAS for SAS controllers. How to tell? * Who knows. */ + + if (scsi_hba_attach_setup(cpq->dip, &cpq->cpq_dma_attr, tran, + SCSI_HBA_TRAN_CLONE) != DDI_SUCCESS) { + dev_err(cpq->dip, CE_WARN, "could not attach to SCSA " + "framework"); + scsi_hba_tran_free(tran); + return (DDI_FAILURE); + } + + cpq->cpq_init_level |= CPQARY3_INITLEVEL_SCSA; + return (DDI_SUCCESS); +} + +void +cpqary3_hba_teardown(cpqary3_t *cpq) +{ + if (cpq->cpq_init_level & CPQARY3_INITLEVEL_SCSA) { + VERIFY(scsi_hba_detach(cpq->dip) != DDI_FAILURE); + scsi_hba_tran_free(cpq->cpq_hba_tran); + cpq->cpq_init_level &= ~CPQARY3_INITLEVEL_SCSA; + } } diff --git a/usr/src/uts/common/io/cpqary3/cpqary3_logvol.c b/usr/src/uts/common/io/cpqary3/cpqary3_logvol.c index b1d121e1f0..b653ad4470 100644 --- a/usr/src/uts/common/io/cpqary3/cpqary3_logvol.c +++ b/usr/src/uts/common/io/cpqary3/cpqary3_logvol.c @@ -42,19 +42,6 @@ cpqary3_lookup_volume_by_addr(cpqary3_t *cpq, struct scsi_address *sa) return (cpqary3_lookup_volume_by_id(cpq, sa->a_target)); } -static void -cpqary3_write_lun_addr_phys(LUNAddr_t *lun, boolean_t masked, unsigned bus, - unsigned target) -{ - lun->PhysDev.Mode = masked ? MASK_PERIPHERIAL_DEV_ADDR : - PERIPHERIAL_DEV_ADDR; - - lun->PhysDev.TargetId = target; - lun->PhysDev.Bus = bus; - - bzero(&lun->PhysDev.Target, sizeof (lun->PhysDev.Target)); -} - static int cpqary3_read_logvols(cpqary3_t *cpq, cpqary3_report_logical_lun_t *cprll) { @@ -182,33 +169,60 @@ cpqary3_discover_logical_volumes(cpqary3_t *cpq, int timeout) { cpqary3_command_t *cpcm; cpqary3_report_logical_lun_t *cprll; - CommandList_t *cl; cpqary3_report_logical_lun_req_t cprllr = { 0 }; int r; + if (!ddi_in_panic()) { + mutex_enter(&cpq->cpq_mutex); + while (cpq->cpq_status & CPQARY3_CTLR_STATUS_DISCOVERY) { + /* + * A discovery is already occuring. Wait for + * completion. + */ + cv_wait(&cpq->cpq_cv_finishq, &cpq->cpq_mutex); + } + + if (gethrtime() < cpq->cpq_last_discovery + 5 * NANOSEC) { + /* + * A discovery completed successfully within the + * last five seconds. Just use the existing data. + */ + mutex_exit(&cpq->cpq_mutex); + return (0); + } + + cpq->cpq_status |= CPQARY3_CTLR_STATUS_DISCOVERY; + mutex_exit(&cpq->cpq_mutex); + } + /* * Allocate the command to send to the device, including buffer space * for the returned list of Logical Volumes. */ - if ((cpcm = cpqary3_synccmd_alloc(cpq, - sizeof (cpqary3_report_logical_lun_t), KM_NOSLEEP)) == NULL) { - return (ENOMEM); + if ((cpcm = cpqary3_command_alloc(cpq, CPQARY3_CMDTYPE_INTERNAL, + KM_NOSLEEP)) == NULL || + cpqary3_command_attach_internal(cpq, cpcm, + sizeof (cpqary3_report_logical_lun_t), KM_NOSLEEP) != 0) { + r = ENOMEM; + mutex_enter(&cpq->cpq_mutex); + goto out; } + cprll = cpcm->cpcm_internal->cpcmi_va; - cl = cpcm->cpcm_va_cmd; /* * According to the CISS Specification, the Report Logical LUNs * command is sent to the controller itself. The Masked Peripheral * Device addressing mode is used, with LUN of 0. */ - cpqary3_write_lun_addr_phys(&cl->Header.LUN, B_TRUE, 0, 0); + cpqary3_write_lun_addr_phys(&cpcm->cpcm_va_cmd->Header.LUN, B_TRUE, + 0, 0); - cl->Request.CDBLen = 12; - cl->Request.Timeout = 0; - cl->Request.Type.Type = CISS_TYPE_CMD; - cl->Request.Type.Attribute = CISS_ATTR_ORDERED; - cl->Request.Type.Direction = CISS_XFER_READ; + cpcm->cpcm_va_cmd->Request.CDBLen = 12; + cpcm->cpcm_va_cmd->Request.Timeout = timeout; + cpcm->cpcm_va_cmd->Request.Type.Type = CISS_TYPE_CMD; + cpcm->cpcm_va_cmd->Request.Type.Attribute = CISS_ATTR_ORDERED; + cpcm->cpcm_va_cmd->Request.Type.Direction = CISS_XFER_READ; /* * The Report Logical LUNs command is essentially a vendor-specific @@ -218,36 +232,79 @@ cpqary3_discover_logical_volumes(cpqary3_t *cpq, int timeout) cprllr.cprllr_opcode = CISS_SCMD_REPORT_LOGICAL_LUNS; cprllr.cprllr_extflag = 1; cprllr.cprllr_datasize = htonl(sizeof (cpqary3_report_logical_lun_t)); - bcopy(&cprllr, cl->Request.CDB, 16); + bcopy(&cprllr, &cpcm->cpcm_va_cmd->Request.CDB[0], 16); - if (cpqary3_synccmd_send(cpq, cpcm, timeout * 1000, - CPQARY3_SYNCCMD_SEND_WAITSIG) != 0) { - cpqary3_synccmd_free(cpq, cpcm); - return (EIO); + mutex_enter(&cpq->cpq_mutex); + + /* + * Send the command to the device. + */ + cpcm->cpcm_status |= CPQARY3_CMD_STATUS_POLLED; + if (cpqary3_submit(cpq, cpcm) != 0) { + r = EIO; + goto out; + } + + /* + * Poll for completion. + */ + cpcm->cpcm_expiry = gethrtime() + timeout * NANOSEC; + if ((r = cpqary3_poll_for(cpq, cpcm)) != 0) { + VERIFY(r == ETIMEDOUT); + VERIFY0(cpcm->cpcm_status & CPQARY3_CMD_STATUS_POLL_COMPLETE); + + /* + * The command timed out; abandon it now. Remove the POLLED + * flag so that the periodic routine will send an abort to + * clean it up next time around. + */ + cpcm->cpcm_status |= CPQARY3_CMD_STATUS_ABANDONED; + cpcm->cpcm_status &= ~CPQARY3_CMD_STATUS_POLLED; + cpcm = NULL; + goto out; + } + + if (cpcm->cpcm_status & CPQARY3_CMD_STATUS_RESET_SENT) { + /* + * The controller was reset while we were trying to discover + * logical volumes. Report failure. + */ + r = EIO; + goto out; } if (cpcm->cpcm_status & CPQARY3_CMD_STATUS_ERROR) { ErrorInfo_t *ei = cpcm->cpcm_va_err; - dev_err(cpq->dip, CE_WARN, "RLL ERROR: %x", ei->CommandStatus); if (ei->CommandStatus != CISS_CMD_DATA_UNDERRUN) { - /* - * XXX This is fatal, then... - */ - cpqary3_synccmd_free(cpq, cpcm); - return (EIO); + dev_err(cpq->dip, CE_WARN, "logical volume discovery" + "error: status 0x%x", ei->CommandStatus); + r = EIO; + goto out; } } - mutex_enter(&cpq->cpq_mutex); if ((cprll->cprll_extflag & 0x1) != 0) { r = cpqary3_read_logvols_ext(cpq, cprll); } else { r = cpqary3_read_logvols(cpq, cprll); } - mutex_exit(&cpq->cpq_mutex); - cpqary3_synccmd_free(cpq, cpcm); + if (r == 0) { + /* + * Update the time of the last successful Logical Volume + * discovery: + */ + cpq->cpq_last_discovery = gethrtime(); + } + +out: + cpq->cpq_status &= ~CPQARY3_CTLR_STATUS_DISCOVERY; + cv_broadcast(&cpq->cpq_cv_finishq); + mutex_exit(&cpq->cpq_mutex); + if (cpcm != NULL) { + cpqary3_command_free(cpcm); + } return (r); } diff --git a/usr/src/uts/common/io/cpqary3/cpqary3_scsi.c b/usr/src/uts/common/io/cpqary3/cpqary3_scsi.c index dd022e5c24..9ab0cc9ccd 100644 --- a/usr/src/uts/common/io/cpqary3/cpqary3_scsi.c +++ b/usr/src/uts/common/io/cpqary3/cpqary3_scsi.c @@ -17,83 +17,7 @@ #include <sys/sdt.h> #include "cpqary3.h" -uint8_t -cpqary3_send_abortcmd(cpqary3_t *cpq, cpqary3_target_t *tgtp, - cpqary3_command_t *abort_cpcm) -{ - CommandList_t *cmdlistp; - cpqary3_tag_t *cpqary3_tagp; - cpqary3_command_t *cpcm; - - VERIFY(MUTEX_HELD(&cpq->cpq_mutex)); - - /* - * Occupy the Command List - * Update the Command List accordingly - * Submit the command and wait for a signal - */ - if ((cpcm = cpqary3_synccmd_alloc(cpq, 0, KM_NOSLEEP)) == NULL) { - return (ENOMEM); - } - - cmdlistp = cpcm->cpcm_va_cmd; - - cmdlistp->Header.LUN.PhysDev.TargetId = 0; - cmdlistp->Header.LUN.PhysDev.Bus = 0; - cmdlistp->Header.LUN.PhysDev.Mode = PERIPHERIAL_DEV_ADDR; - - cmdlistp->Request.Type.Type = CISS_TYPE_MSG; - cmdlistp->Request.Type.Attribute = CISS_ATTR_HEADOFQUEUE; - cmdlistp->Request.Type.Direction = CISS_XFER_NONE; - cmdlistp->Request.Timeout = CISS_NO_TIMEOUT; - cmdlistp->Request.CDBLen = CPQARY3_CDBLEN_16; - cmdlistp->Request.CDB[0] = CISS_MSG_ABORT; - - if (abort_cpcm != NULL) { - /* - * Abort the specified task. - */ - cmdlistp->Request.CDB[1] = CISS_ABORT_TASK; - - cpqary3_tagp = (cpqary3_tag_t *)&cmdlistp->Request.CDB[4]; - cpqary3_tagp->tag_value = abort_cpcm->cpcm_tag; - - if (abort_cpcm->cpcm_time_abort != 0) { - abort_cpcm->cpcm_time_abort = ddi_get_lbolt(); - } - abort_cpcm->cpcm_status |= CPQARY3_CMD_STATUS_ABORT_SENT; - - } else { - /* - * Abort all tasks for the controller. - * XXX Does this cause the controller to fire completion - * of all inflight tasks, but marked aborted? - */ - cmdlistp->Request.CDB[1] = CISS_ABORT_TASKSET; - - cmdlistp->Header.LUN.LogDev = tgtp->cptg_volume->cplv_addr; #if 0 - cmdlistp->Header.LUN.LogDev.Mode = LOGICAL_VOL_ADDR; - cmdlistp->Header.LUN.LogDev.VolId = tgtp->logical_id; -#endif - } - - if (cpqary3_synccmd_send(cpq, cpcm, 30000, - CPQARY3_SYNCCMD_SEND_WAITSIG) != 0) { - cpqary3_synccmd_free(cpq, cpcm); - return (CPQARY3_FAILURE); - } - - if (cpcm->cpcm_status & CPQARY3_CMD_STATUS_ERROR) { - cpqary3_synccmd_free(cpq, cpcm); - return (CPQARY3_FAILURE); - } - - cpqary3_synccmd_free(cpq, cpcm); - - return (CPQARY3_SUCCESS); -} - int cpqary3_flush_cache(cpqary3_t *cpqary3p) { @@ -129,6 +53,12 @@ cpqary3_flush_cache(cpqary3_t *cpqary3p) return (ETIMEDOUT); } + if (cpcm->cpcm_status & CPQARY3_CMD_STATUS_RESET_SENT) { + cpqary3_synccmd_free(cpqary3p, cpcm); + return (EIO); + } + cpqary3_synccmd_free(cpqary3p, cpcm); return (0); } +#endif diff --git a/usr/src/uts/common/io/cpqary3/cpqary3_scsi.h b/usr/src/uts/common/io/cpqary3/cpqary3_scsi.h index f7f18e6267..3bf3cf4e21 100644 --- a/usr/src/uts/common/io/cpqary3/cpqary3_scsi.h +++ b/usr/src/uts/common/io/cpqary3/cpqary3_scsi.h @@ -11,6 +11,7 @@ /* * Copyright (C) 2013 Hewlett-Packard Development Company, L.P. + * Copyright 2016 Joyent, Inc. */ #ifndef _CPQARY3_SCSI_H @@ -71,7 +72,11 @@ extern "C" { #define CISS_MSG_RESET 0x1 #define CISS_RESET_CTLR 0x0 +#define CISS_RESET_BUS 0x1 #define CISS_RESET_TGT 0x3 +#define CISS_RESET_LUN 0x4 + +#define CISS_MSG_NOP 0x3 #define CPQARY3_CDBLEN_12 12 #define CPQARY3_CDBLEN_16 16 diff --git a/usr/src/uts/common/io/cpqary3/cpqary3_transport.c b/usr/src/uts/common/io/cpqary3/cpqary3_transport.c index 4739aaf33d..c13ac63fc9 100644 --- a/usr/src/uts/common/io/cpqary3/cpqary3_transport.c +++ b/usr/src/uts/common/io/cpqary3/cpqary3_transport.c @@ -29,41 +29,12 @@ static boolean_t cpqary3_is_scsi_read_write(struct scsi_pkt *scsi_pktp); extern ddi_dma_attr_t cpqary3_dma_attr; -static void -cpqary3_set_arq_data(struct scsi_pkt *pkt, uchar_t key) -{ - struct scsi_arq_status *arqstat; - - arqstat = (struct scsi_arq_status *)(pkt->pkt_scbp); - - arqstat->sts_status.sts_chk = 1; /* CHECK CONDITION */ - arqstat->sts_rqpkt_reason = CMD_CMPLT; - arqstat->sts_rqpkt_resid = 0; - arqstat->sts_rqpkt_state = STATE_GOT_BUS | STATE_GOT_TARGET | - STATE_SENT_CMD | STATE_XFERRED_DATA; - arqstat->sts_rqpkt_statistics = 0; - arqstat->sts_sensedata.es_valid = 1; - arqstat->sts_sensedata.es_class = CLASS_EXTENDED_SENSE; - arqstat->sts_sensedata.es_key = key; -} - -/* - * Function : cpqary3_getcap - * Description : This routine is called to get the current value of a - * capability.(SCSI transport capability) - * Called By : kernel - * Parameters : SCSI address, capability identifier, target(s) affected - * Calls : None - * Return Values: current value of capability / -1 (if unsupported) - */ int cpqary3_getcap(struct scsi_address *sa, char *capstr, int tgtonly) { + scsi_hba_tran_t *tran = sa->a_hba_tran; + cpqary3_t *cpq = (cpqary3_t *)tran->tran_hba_private; int index; -#if 0 - cpqary3_t *cpq = SA2CTLR(sa); - cpqary3_tgt_t *tgtp = cpqary3_target_from_addr(cpq, sa); -#endif /* * If requested Capability is not supported, return -1. @@ -88,30 +59,20 @@ cpqary3_getcap(struct scsi_address *sa, char *capstr, int tgtonly) case SCSI_CAP_CDB_LEN: return (16); case SCSI_CAP_DMA_MAX: - return ((int)cpqary3_dma_attr.dma_attr_maxxfer); + return ((int)cpq->cpq_dma_attr.dma_attr_maxxfer); case SCSI_CAP_DISCONNECT: case SCSI_CAP_SYNCHRONOUS: case SCSI_CAP_WIDE_XFER: case SCSI_CAP_ARQ: return (1); -#if 0 - case SCSI_CAP_DISCONNECT: - return (tgtp->ctlr_flags & CPQARY3_CAP_DISCON_ENABLED); - case SCSI_CAP_SYNCHRONOUS: - return (tgtp->ctlr_flags & CPQARY3_CAP_SYNC_ENABLED); - case SCSI_CAP_WIDE_XFER: - return (tgtp->ctlr_flags & CPQARY3_CAP_WIDE_XFER_ENABLED); - case SCSI_CAP_ARQ: - return ((tgtp->ctlr_flags & CPQARY3_CAP_ARQ_ENABLED) ? 1 : 0); -#endif case SCSI_CAP_INITIATOR_ID: - return (7); /* XXX */ + return (CPQARY3_CONTROLLER_TARGET); case SCSI_CAP_UNTAGGED_QING: return (1); case SCSI_CAP_TAGGED_QING: return (1); case SCSI_CAP_SECTOR_SIZE: - return (cpqary3_dma_attr.dma_attr_granular); + return ((int)cpq->cpq_dma_attr.dma_attr_granular); case SCSI_CAP_TOTAL_SECTORS: return (CAP_NOT_DEFINED); #if 0 @@ -125,16 +86,6 @@ cpqary3_getcap(struct scsi_address *sa, char *capstr, int tgtonly) } } -/* - * Function : cpqary3_setcap - * Description : This routine is called to set the current value of a - * capability.(SCSI transport capability) - * Called By : kernel - * Parameters : SCSI address, capability identifier, - * new capability value, target(s) affected - * Calls : None - * Return Values: SUCCESS / FAILURE / -1 (if capability is unsupported) - */ /* ARGSUSED */ int cpqary3_setcap(struct scsi_address *sa, char *capstr, int value, int tgtonly) @@ -167,17 +118,18 @@ cpqary3_setcap(struct scsi_address *sa, char *capstr, int value, int tgtonly) case SCSI_CAP_DISCONNECT: case SCSI_CAP_SYNCHRONOUS: case SCSI_CAP_WIDE_XFER: - return (CAP_CHG_NOT_ALLOWED); case SCSI_CAP_ARQ: - case SCSI_CAP_UNTAGGED_QING: - case SCSI_CAP_TAGGED_QING: - return (1); case SCSI_CAP_INITIATOR_ID: case SCSI_CAP_SECTOR_SIZE: case SCSI_CAP_TOTAL_SECTORS: + case SCSI_CAP_UNTAGGED_QING: + case SCSI_CAP_TAGGED_QING: +#if 0 case SCSI_CAP_GEOMETRY: +#endif case SCSI_CAP_RESET_NOTIFICATION: return (CAP_CHG_NOT_ALLOWED); + default: return (CAP_NOT_DEFINED); } @@ -188,15 +140,20 @@ cpqary3_oscmd_complete(cpqary3_command_t *cpcm) { cpqary3_t *cpqary3p = cpcm->cpcm_ctlr; ErrorInfo_t *errorinfop = cpcm->cpcm_va_err; - CommandList_t *cmdlistp = cpcm->cpcm_va_cmd; struct scsi_pkt *scsi_pktp = cpcm->cpcm_scsa->cpcms_pkt; VERIFY(MUTEX_HELD(&cpqary3p->cpq_mutex)); - VERIFY(cpcm->cpcm_type == CPQARY3_CMDTYPE_OS); - - if (cpcm->cpcm_status & CPQARY3_CMD_STATUS_TIMEOUT) { - scsi_pktp->pkt_reason = CMD_TIMEOUT; - scsi_pktp->pkt_statistics |= STAT_TIMEOUT; + VERIFY(cpcm->cpcm_type == CPQARY3_CMDTYPE_SCSA); + + if (cpcm->cpcm_status & CPQARY3_CMD_STATUS_RESET_SENT) { + if (scsi_pktp->pkt_reason != CMD_CMPLT) { + /* + * If another error status was previously written, + * do not overwrite it. + */ + scsi_pktp->pkt_reason = CMD_RESET; + } + scsi_pktp->pkt_statistics |= STAT_BUS_RESET | STAT_DEV_RESET; goto finish; } @@ -228,16 +185,33 @@ cpqary3_oscmd_complete(cpqary3_command_t *cpcm) scsi_pktp->pkt_state = 0; /* XXX ? */ break; + /* + * The controller has reported completion for a command in response + * to an abort message. + */ case CISS_CMD_ABORTED: case CISS_CMD_UNSOLICITED_ABORT: - scsi_pktp->pkt_reason = CMD_ABORTED; + if (cpcm->cpcm_status & CPQARY3_CMD_STATUS_TIMEOUT) { + /* + * This abort was arranged by the periodic routine + * in response to an elapsed timeout. + */ + scsi_pktp->pkt_reason = CMD_TIMEOUT; + scsi_pktp->pkt_statistics |= STAT_TIMEOUT; + } else { + scsi_pktp->pkt_reason = CMD_ABORTED; + } scsi_pktp->pkt_statistics |= STAT_ABORTED; - scsi_pktp->pkt_state = STATE_XFERRED_DATA | STATE_GOT_STATUS; + scsi_pktp->pkt_state |= STATE_XFERRED_DATA | STATE_GOT_STATUS; break; case CISS_CMD_ABORT_FAILED: break; + /* + * The controller suggests that the timeout we specified in the + * SCSI packet has expired. + */ case CISS_CMD_TIMEOUT: scsi_pktp->pkt_reason = CMD_TIMEOUT; scsi_pktp->pkt_statistics |= STAT_TIMEOUT; diff --git a/usr/src/uts/common/io/cpqary3/cpqary3_user.c b/usr/src/uts/common/io/cpqary3/cpqary3_user.c new file mode 100644 index 0000000000..39164ea312 --- /dev/null +++ b/usr/src/uts/common/io/cpqary3/cpqary3_user.c @@ -0,0 +1,120 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2016 Joyent, Inc. + */ + +#include "cpqary3.h" + +#include <sys/file.h> + +int +cpqary3_ioctl_passthrough(cpqary3_t *cpq, intptr_t udata, int mode, + int *rval) +{ + return (EINVAL); +#if 0 + int status = 0; + void *ubufp; + uint32_t ubufsz; + unsigned cdblen; + boolean_t for_read; + cpqary3_command_t *cpcm; + STRUCT_DECL(cpqary3_ioctl_req, cppt); + + if ((mode & FWRITE) == 0) { + return (EACCES); + } + + STRUCT_INIT(cppt, get_udatamodel()); + + if (ddi_copyin((void *)udata, STRUCT_BUF(cppt), STRUCT_SIZE(cppt), + mode) != 0) { + return (EFAULT); + } + + for_read = STRUCT_FGET(cppt, cppt_for_read) == 0 ? B_FALSE : B_TRUE; + ubufp = STRUCT_FGETP(cppt, cppt_bufp); + ubufsz = STRUCT_FGET(cppt, cppt_bufsz); + + /* + * XXX better checks? + */ + if (ubufsz > 1024 * 1024) { + return (EINVAL); + } + if ((cdblen = STRUCT_FGET(cppt, cppt_cdblen)) > 16) { + return (EINVAL); + } + + /* + * Allocate a sync command to send to the controller. + */ + if ((cpcm = cpqary3_synccmd_alloc(cpq, ubufsz, KM_NOSLEEP)) == + NULL) { + return (ENOMEM); + } + + /* + * Load data buffer from request into the command internal buffer. + */ + if (ddi_copyin(ubufp, cpcm->cpcm_internal->cpcmi_va, ubufsz, + mode) != 0) { + cpqary3_synccmd_free(cpq, cpcm); + return (EFAULT); + } + + /* + * XXX These pass-through requests target the controller LUN for + * now... + */ + LUNAddr_t *lun = &cpcm->cpcm_va_cmd->Header.LUN; + lun->PhysDev.Mode = MASK_PERIPHERIAL_DEV_ADDR; + lun->PhysDev.TargetId = 0; + lun->PhysDev.Bus = 0; + bzero(&lun->PhysDev.Target, sizeof (lun->PhysDev.Target)); + + bcopy(STRUCT_FGET(cppt, cppt_cdb), cpcm->cpcm_va_cmd->Request.CDB, + cdblen); + cpcm->cpcm_va_cmd->Request.CDBLen = cdblen; + + cpcm->cpcm_va_cmd->Request.Type.Type = CISS_TYPE_CMD; + cpcm->cpcm_va_cmd->Request.Type.Attribute = CISS_ATTR_SIMPLE; + cpcm->cpcm_va_cmd->Request.Type.Direction = for_read ? + CISS_XFER_READ : CISS_XFER_WRITE; + cpcm->cpcm_va_cmd->Request.Timeout = 30; + + /* + * Submit command to controller! + */ + if (cpqary3_synccmd_send(cpq, cpcm, 30 * 1000, + CPQARY3_SYNCCMD_SEND_WAITSIG) != 0) { + status = EIO; + } else { + /* + * We succeeded in submitting the command to the controller, + * so copy the data back to the user. + */ + if (ddi_copyout(cpcm->cpcm_internal->cpcmi_va, + ubufp, ubufsz, mode) != 0) { + status = EFAULT; + } + } + + cpqary3_synccmd_free(cpq, cpcm); + + if (status == 0) { + *rval = 0; + } + return (status); +#endif +} diff --git a/usr/src/uts/common/io/cpqary3/cpqary3_util.c b/usr/src/uts/common/io/cpqary3/cpqary3_util.c deleted file mode 100644 index 0fbbc6d685..0000000000 --- a/usr/src/uts/common/io/cpqary3/cpqary3_util.c +++ /dev/null @@ -1,243 +0,0 @@ -/* - * This file and its contents are supplied under the terms of the - * Common Development and Distribution License ("CDDL"), version 1.0. - * You may only use this file in accordance with the terms of version - * 1.0 of the CDDL. - * - * A full copy of the text of the CDDL should have accompanied this - * source. A copy of the CDDL is also available via the Internet at - * http://www.illumos.org/license/CDDL. - */ - -/* - * Copyright (C) 2013 Hewlett-Packard Development Company, L.P. - * Copyright 2016 Joyent, Inc. - */ - -#include <sys/sdt.h> -#include "cpqary3.h" - -void -cpqary3_lockup_check(cpqary3_t *cpq) -{ - /* - * Read the current controller heartbeat value. - */ - uint32_t heartbeat = ddi_get32(cpq->cpq_ct_handle, - &cpq->cpq_ct->HeartBeat); - - VERIFY(MUTEX_HELD(&cpq->cpq_mutex)); - - /* - * Check to see if the value is the same as last time we looked: - */ - if (heartbeat != cpq->cpq_last_heartbeat) { - /* - * The heartbeat value has changed, which suggests that the - * firmware in the controller has not yet come to a complete - * stop. Record the new value, as well as the current time. - */ - cpq->cpq_last_heartbeat = heartbeat; - cpq->cpq_last_heartbeat_lbolt = ddi_get_lbolt(); - return; - } - - /* - * The controller _might_ have been able to signal to us that is - * has locked up. This is a truly unfathomable state of affairs: - * If the firmware can tell it has flown off the rails, why not - * simply reset the controller? - */ - uint32_t odr = cpqary3_get32(cpq, CISS_I2O_OUTBOUND_DOORBELL_STATUS); - uint32_t spr = cpqary3_get32(cpq, CISS_I2O_SCRATCHPAD); - if ((odr & CISS_ODR_BIT_LOCKUP) != 0) { - dev_err(cpq->dip, CE_PANIC, "HP SmartArray firmware has " - "reported a critical fault (odr %08x spr %08x)", - odr, spr); - } - - clock_t expiry = cpq->cpq_last_heartbeat_lbolt + CPQARY3_SEC2HZ(60); - if (ddi_get_lbolt() >= expiry) { - dev_err(cpq->dip, CE_PANIC, "HP SmartArray firmware has " - "stopped responding (odr %08x spr %08x)", - odr, spr); - } -} - -cpqary3_command_t * -cpqary3_lookup_inflight(cpqary3_t *cpq, uint32_t tag) -{ - VERIFY(MUTEX_HELD(&cpq->cpq_mutex)); - - cpqary3_command_t srch; - - srch.cpcm_tag = tag; - - return (avl_find(&cpq->cpq_inflight, &srch, NULL)); -} - -#if 0 -cpqary3_target_t * -cpqary3_target_from_id(cpqary3_t *cpq, unsigned id) -{ - VERIFY(MUTEX_HELD(&cpq->cpq_mutex)); - - if (id >= CPQARY3_MAX_TGT) { - return (NULL); - } - - return (cpq->cpq_targets[id]); -} - -cpqary3_tgt_t * -cpqary3_target_from_addr(cpqary3_t *cpq, struct scsi_address *sa) -{ - VERIFY(MUTEX_HELD(&cpq->cpq_mutex)); - - return (cpqary3_target_from_id(cpq, sa->a_target)); -} -#endif - -cpqary3_command_t * -cpqary3_synccmd_alloc(cpqary3_t *cpq, size_t bufsz, int kmflags) -{ - cpqary3_command_t *cpcm; - - if ((cpcm = cpqary3_command_alloc(cpq, CPQARY3_CMDTYPE_SYNCCMD, - kmflags)) == NULL) { - return (NULL); - } - - if (bufsz == 0) { - return (cpcm); - } - - if ((cpcm->cpcm_internal = cpqary3_command_internal_alloc(cpq, - bufsz, kmflags)) == NULL) { - cpqary3_command_free(cpcm); - return (NULL); - } - - cpcm->cpcm_va_cmd->SG[0].Addr = cpcm->cpcm_internal->cpcmi_pa; - cpcm->cpcm_va_cmd->SG[0].Len = bufsz; - cpcm->cpcm_va_cmd->Header.SGList = 1; - cpcm->cpcm_va_cmd->Header.SGTotal = 1; - - return (cpcm); -} - -void -cpqary3_synccmd_free(cpqary3_t *cpq, cpqary3_command_t *cpcm) -{ - /* - * so, the user is done with this command packet. - * we have three possible scenarios here: - * - * 1) the command was never submitted to the controller - * - * or - * - * 2) the command has completed at the controller and has - * been fully processed by the interrupt processing - * mechanism and is no longer on the submitted or - * retrieve queues. - * - * or - * - * 3) the command is not yet complete at the controller, - * and/or hasn't made it through cpqary3_process_pkt() - * yet. - * - * For cases (1) and (2), we can go ahead and free the - * command and the associated resources. For case (3), we - * must mark the command as no longer needed, and let - * cpqary3_process_pkt() clean it up instead. - */ - - mutex_enter(&cpq->cpq_mutex); - if (cpcm->cpcm_status & CPQARY3_CMD_STATUS_INFLIGHT) { - cpcm->cpcm_status |= CPQARY3_CMD_STATUS_ABANDONED; - mutex_exit(&cpq->cpq_mutex); - return; - } - mutex_exit(&cpq->cpq_mutex); - - /* - * command was either never submitted or has completed - * (cases #1 and #2 above). so, clean it up. - */ - cpqary3_command_free(cpcm); -} - -int -cpqary3_synccmd_send(cpqary3_t *cpqary3p, cpqary3_command_t *cpcm, - clock_t timeoutms, int flags) -{ - clock_t absto = 0; /* absolute timeout */ - boolean_t waitsig = B_FALSE; - int rc = 0; - - VERIFY(cpcm->cpcm_type == CPQARY3_CMDTYPE_SYNCCMD); - - /* compute absolute timeout, if necessary */ - if (timeoutms > 0) { - absto = ddi_get_lbolt() + drv_usectohz(timeoutms * 1000); - } - - /* heed signals during wait? */ - if (flags & CPQARY3_SYNCCMD_SEND_WAITSIG) { - waitsig = B_TRUE; - } - - /* acquire the sw mutex for our wait */ - mutex_enter(&cpqary3p->cpq_mutex); - - if (cpqary3_submit(cpqary3p, cpcm) != 0) { - mutex_exit(&cpqary3p->cpq_mutex); - return (-1); - } - - /* wait for command completion, timeout, or signal */ - while (!(cpcm->cpcm_status & CPQARY3_CMD_STATUS_COMPLETE)) { - kmutex_t *mt = &cpqary3p->cpq_mutex; - kcondvar_t *cv = &cpqary3p->cpq_cv_finishq; - - /* wait with the request behavior */ - if (absto) { - clock_t crc; - if (waitsig) { - crc = cv_timedwait_sig(cv, mt, absto); - } else { - crc = cv_timedwait(cv, mt, absto); - } - if (crc > 0) { - rc = 0; - } else { - rc = -1; - } - } else { - if (waitsig) { - rc = cv_wait_sig(cv, mt); - if (rc > 0) { - rc = 0; - } else { - rc = -1; - } - } else { - cv_wait(cv, mt); - rc = 0; - } - } - - /* - * if our wait was interrupted (timeout), - * then break here - */ - if (rc) { - break; - } - } - - mutex_exit(&cpqary3p->cpq_mutex); - return (rc); -} |