summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoshua M. Clulow <jmc@joyent.com>2016-03-07 11:59:15 +0000
committerJoshua M. Clulow <jmc@joyent.com>2016-07-06 16:25:32 +0000
commit90a7d022958606b544f5c442c92439fbdfaf5522 (patch)
tree42eb610ffa006201405c658600503ef519f352da
parent4555d02e8b95eba019119f4b350177a3bb821649 (diff)
downloadillumos-joyent-90a7d022958606b544f5c442c92439fbdfaf5522.tar.gz
XXX extensive refactoring to support aborting commands that time out
-rw-r--r--usr/src/uts/common/Makefile.files3
-rw-r--r--usr/src/uts/common/io/cpqary3/cpqary3.c196
-rw-r--r--usr/src/uts/common/io/cpqary3/cpqary3.h244
-rw-r--r--usr/src/uts/common/io/cpqary3/cpqary3_ciss.c697
-rw-r--r--usr/src/uts/common/io/cpqary3/cpqary3_ciss_simple.c38
-rw-r--r--usr/src/uts/common/io/cpqary3/cpqary3_commands.c57
-rw-r--r--usr/src/uts/common/io/cpqary3/cpqary3_hba.c400
-rw-r--r--usr/src/uts/common/io/cpqary3/cpqary3_logvol.c133
-rw-r--r--usr/src/uts/common/io/cpqary3/cpqary3_scsi.c82
-rw-r--r--usr/src/uts/common/io/cpqary3/cpqary3_scsi.h5
-rw-r--r--usr/src/uts/common/io/cpqary3/cpqary3_transport.c106
-rw-r--r--usr/src/uts/common/io/cpqary3/cpqary3_user.c120
-rw-r--r--usr/src/uts/common/io/cpqary3/cpqary3_util.c243
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);
-}