summaryrefslogtreecommitdiffstats
path: root/sys/dev/ciss
diff options
context:
space:
mode:
authorps <ps@FreeBSD.org>2004-04-16 23:00:01 +0000
committerps <ps@FreeBSD.org>2004-04-16 23:00:01 +0000
commita7a31583b738b898e6e5fc0e113160519768d0b8 (patch)
treee2f6cfb86187409cf53dcfda78756032100e4037 /sys/dev/ciss
parent914d5fc9b4fc8f56a7ae7565ed67757fcfd84358 (diff)
downloadFreeBSD-src-a7a31583b738b898e6e5fc0e113160519768d0b8.zip
FreeBSD-src-a7a31583b738b898e6e5fc0e113160519768d0b8.tar.gz
Add support for the HP Modular Smart Array 20 & 500 storage arrays.
Logical volumes on these devices show up as LUNs behind another controller (also known as proxy controller). In order to issue firmware commands for a volume on a proxy controller, they must be targeted at the address of the proxy controller it is attached to, not the Host/PCI controller. A proxy controller is defined as a device listed in the INQUIRY PHYSICAL LUNS command who's L2 and L3 SCSI addresses are zero. The corresponding address returned defines which "bus" the controller lives on and we use this to create a virtual CAM bus. A logical volume's addresses first byte defines the logical drive number. The second byte defines the bus that it is attached to which corresponds to the BUS of the proxy controller's found or the Host/PCI controller. Change event notification to be handled in its own kernel thread. This is needed since some events may require the driver to sleep on some operations and this cannot be done during interrupt context. With this change, it is now possible to create and destroy logical volumes from FreeBSD, but it requires a native application to construct the proper firmware commands which is not publicly available. Special thanks to John Cagle @ HP for providing remote access to all the hardware and beating on the storage engineers at HP to answer my questions.
Diffstat (limited to 'sys/dev/ciss')
-rw-r--r--sys/dev/ciss/ciss.c559
-rw-r--r--sys/dev/ciss/cissreg.h9
-rw-r--r--sys/dev/ciss/cissvar.h32
3 files changed, 462 insertions, 138 deletions
diff --git a/sys/dev/ciss/ciss.c b/sys/dev/ciss/ciss.c
index 8980bb5..f060da7 100644
--- a/sys/dev/ciss/ciss.c
+++ b/sys/dev/ciss/ciss.c
@@ -1,5 +1,6 @@
/*-
* Copyright (c) 2001 Michael Smith
+ * Copyright (c) 2004 Paul Saab
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -66,6 +67,11 @@
* Thanks must go to Fred Harris and Darryl DeVinney at Compaq, as
* well as Paul Saab at Yahoo! for their assistance in making this
* driver happen.
+ *
+ * More thanks must go to John Cagle at HP for the countless hours
+ * spent making this driver "work" with the MSA* series storage
+ * enclosures. Without his help (and nagging), this driver could not
+ * be used with these enclosures.
*/
#include <sys/param.h>
@@ -75,6 +81,8 @@
#include <sys/bus.h>
#include <sys/conf.h>
#include <sys/stat.h>
+#include <sys/kthread.h>
+#include <sys/queue.h>
#include <cam/cam.h>
#include <cam/cam_ccb.h>
@@ -116,12 +124,16 @@ static void ciss_command_map_helper(void *arg, bus_dma_segment_t *segs,
int nseg, int error);
static int ciss_identify_adapter(struct ciss_softc *sc);
static int ciss_init_logical(struct ciss_softc *sc);
+static int ciss_init_physical(struct ciss_softc *sc);
static int ciss_identify_logical(struct ciss_softc *sc, struct ciss_ldrive *ld);
static int ciss_get_ldrive_status(struct ciss_softc *sc, struct ciss_ldrive *ld);
static int ciss_update_config(struct ciss_softc *sc);
-static int ciss_accept_media(struct ciss_softc *sc, int ldrive, int async);
+static int ciss_accept_media(struct ciss_softc *sc, struct ciss_ldrive *ld,
+ int async);
static void ciss_accept_media_complete(struct ciss_request *cr);
static void ciss_free(struct ciss_softc *sc);
+static void ciss_spawn_notify_thread(struct ciss_softc *sc);
+static void ciss_kill_notify_thread(struct ciss_softc *sc);
/* request submission/completion */
static int ciss_start(struct ciss_request *cr);
@@ -155,7 +167,8 @@ static void ciss_unmap_request(struct ciss_request *cr);
/* CAM interface */
static int ciss_cam_init(struct ciss_softc *sc);
-static void ciss_cam_rescan_target(struct ciss_softc *sc, int target);
+static void ciss_cam_rescan_target(struct ciss_softc *sc,
+ int bus, int target);
static void ciss_cam_rescan_all(struct ciss_softc *sc);
static void ciss_cam_rescan_callback(struct cam_periph *periph, union ccb *ccb);
static void ciss_cam_action(struct cam_sim *sim, union ccb *ccb);
@@ -164,8 +177,9 @@ static int ciss_cam_emulate(struct ciss_softc *sc, struct ccb_scsiio *csio);
static void ciss_cam_poll(struct cam_sim *sim);
static void ciss_cam_complete(struct ciss_request *cr);
static void ciss_cam_complete_fixup(struct ciss_softc *sc, struct ccb_scsiio *csio);
-static struct cam_periph *ciss_find_periph(struct ciss_softc *sc, int target);
-static int ciss_name_device(struct ciss_softc *sc, int target);
+static struct cam_periph *ciss_find_periph(struct ciss_softc *sc,
+ int bus, int target);
+static int ciss_name_device(struct ciss_softc *sc, int bus, int target);
/* periodic status monitoring */
static void ciss_periodic(void *arg);
@@ -363,6 +377,7 @@ ciss_attach(device_t dev)
ciss_initq_free(sc);
ciss_initq_busy(sc);
ciss_initq_complete(sc);
+ ciss_initq_notify(sc);
/*
* Initialise command/request pool.
@@ -377,6 +392,12 @@ ciss_attach(device_t dev)
goto out;
/*
+ * Find all the physical devices.
+ */
+ if ((error = ciss_init_physical(sc)) != 0)
+ goto out;
+
+ /*
* Build our private table of logical devices.
*/
if ((error = ciss_init_logical(sc)) != 0)
@@ -412,6 +433,8 @@ ciss_attach(device_t dev)
*/
sc->ciss_flags |= CISS_FLAG_RUNNING;
+ ciss_spawn_notify_thread(sc);
+
error = 0;
out:
if (error != 0)
@@ -853,9 +876,11 @@ ciss_identify_adapter(struct ciss_softc *sc)
/* print information */
if (bootverbose) {
+#if 0 /* XXX proxy volumes??? */
ciss_printf(sc, " %d logical drive%s configured\n",
sc->ciss_id->configured_logical_drives,
(sc->ciss_id->configured_logical_drives == 1) ? "" : "s");
+#endif
ciss_printf(sc, " firmware %4.4s\n", sc->ciss_id->running_firmware_revision);
ciss_printf(sc, " %d SCSI channels\n", sc->ciss_id->scsi_bus_count);
@@ -893,19 +918,18 @@ out:
}
/************************************************************************
- * Find logical drives on the adapter.
+ * Helper routine for generating a list of logical and physical luns.
*/
-static int
-ciss_init_logical(struct ciss_softc *sc)
+static struct ciss_lun_report *
+ciss_report_luns(struct ciss_softc *sc, int opcode, int nunits)
{
struct ciss_request *cr;
struct ciss_command *cc;
struct ciss_report_cdb *crc;
struct ciss_lun_report *cll;
- int error, i;
- size_t report_size;
- int ndrives;
int command_status;
+ int report_size;
+ int error = 0;
debug_called(1);
@@ -917,15 +941,15 @@ ciss_init_logical(struct ciss_softc *sc)
*/
if ((error = ciss_get_request(sc, &cr)) != 0)
goto out;
- report_size = sizeof(*cll) + CISS_MAX_LOGICAL * sizeof(union ciss_device_address);
+ report_size = sizeof(*cll) + nunits * sizeof(union ciss_device_address);
if ((cll = malloc(report_size, CISS_MALLOC_CLASS, M_NOWAIT | M_ZERO)) == NULL) {
- ciss_printf(sc, "can't allocate memory for logical drive list\n");
+ ciss_printf(sc, "can't allocate memory for lun report\n");
error = ENOMEM;
goto out;
}
/*
- * Build the Report Logical LUNs command.
+ * Build the Report Logical/Physical LUNs command.
*/
cc = CISS_FIND_COMMAND(cr);
cr->cr_data = cll;
@@ -943,7 +967,7 @@ ciss_init_logical(struct ciss_softc *sc)
crc = (struct ciss_report_cdb *)&(cc->cdb.cdb[0]);
bzero(crc, sizeof(*crc));
- crc->opcode = CISS_OPCODE_REPORT_LOGICAL_LUNS;
+ crc->opcode = opcode;
crc->length = htonl(report_size); /* big-endian field */
cll->list_size = htonl(report_size - sizeof(*cll)); /* big-endian field */
@@ -952,7 +976,7 @@ ciss_init_logical(struct ciss_softc *sc)
* here should be much greater than above)
*/
if ((error = ciss_synch_request(cr, 60 * 1000)) != 0) {
- ciss_printf(sc, "error sending Report Logical LUNs command (%d)\n", error);
+ ciss_printf(sc, "error sending %d LUN command (%d)\n", opcode, error);
goto out;
}
@@ -965,7 +989,7 @@ ciss_init_logical(struct ciss_softc *sc)
case CISS_CMD_STATUS_DATA_UNDERRUN: /* buffer too large, not bad */
break;
case CISS_CMD_STATUS_DATA_OVERRUN:
- ciss_printf(sc, "WARNING: more logical drives than driver limit (%d), adjust CISS_MAX_LOGICAL\n",
+ ciss_printf(sc, "WARNING: more units than driver limit (%d)\n",
CISS_MAX_LOGICAL);
break;
default:
@@ -977,49 +1001,162 @@ ciss_init_logical(struct ciss_softc *sc)
ciss_release_request(cr);
cr = NULL;
+out:
+ if (cr != NULL)
+ ciss_release_request(cr);
+ if (error && cll != NULL) {
+ free(cll, CISS_MALLOC_CLASS);
+ cll = NULL;
+ }
+ return(cll);
+}
+
+/************************************************************************
+ * Find logical drives on the adapter.
+ */
+static int
+ciss_init_logical(struct ciss_softc *sc)
+{
+ struct ciss_lun_report *cll;
+ int error = 0, i, j;
+ int ndrives;
+
+ debug_called(1);
+
+ cll = ciss_report_luns(sc, CISS_OPCODE_REPORT_LOGICAL_LUNS,
+ CISS_MAX_LOGICAL);
+ if (cll == NULL) {
+ error = ENXIO;
+ goto out;
+ }
+
/* sanity-check reply */
ndrives = (ntohl(cll->list_size) / sizeof(union ciss_device_address));
if ((ndrives < 0) || (ndrives >= CISS_MAX_LOGICAL)) {
ciss_printf(sc, "adapter claims to report absurd number of logical drives (%d > %d)\n",
ndrives, CISS_MAX_LOGICAL);
- return(ENXIO);
+ error = ENXIO;
+ goto out;
}
/*
* Save logical drive information.
*/
- if (bootverbose)
- ciss_printf(sc, "%d logical drive%s\n", ndrives, (ndrives > 1) ? "s" : "");
- if (ndrives != sc->ciss_id->configured_logical_drives)
- ciss_printf(sc, "logical drive map claims %d drives, but adapter claims %d\n",
- ndrives, sc->ciss_id->configured_logical_drives);
+ if (bootverbose) {
+ ciss_printf(sc, "%d logical drive%s\n",
+ ndrives, (ndrives > 1 || ndrives == 0) ? "s" : "");
+ }
+
+ sc->ciss_logical =
+ malloc(sc->ciss_max_bus_number * sizeof(struct ciss_ldrive *),
+ CISS_MALLOC_CLASS, M_NOWAIT | M_ZERO);
+ if (sc->ciss_logical == NULL) {
+ error = ENXIO;
+ goto out;
+ }
+
+ for (i = 0; i <= sc->ciss_max_bus_number; i++) {
+ sc->ciss_logical[i] =
+ malloc(CISS_MAX_LOGICAL * sizeof(struct ciss_ldrive),
+ CISS_MALLOC_CLASS, M_NOWAIT | M_ZERO);
+ if (sc->ciss_logical[i] == NULL) {
+ error = ENXIO;
+ goto out;
+ }
+
+ for (j = 0; j < CISS_MAX_LOGICAL; j++)
+ sc->ciss_logical[i][j].cl_status = CISS_LD_NONEXISTENT;
+ }
+
+
for (i = 0; i < CISS_MAX_LOGICAL; i++) {
if (i < ndrives) {
- sc->ciss_logical[i].cl_address = cll->lun[i]; /* XXX endianness? */
- if (ciss_identify_logical(sc, &sc->ciss_logical[i]) != 0)
+ struct ciss_ldrive *ld;
+ int bus, target;
+
+ bus = CISS_LUN_TO_BUS(cll->lun[i].logical.lun);
+ target = CISS_LUN_TO_TARGET(cll->lun[i].logical.lun);
+ ld = &sc->ciss_logical[bus][target];
+
+ ld->cl_address = cll->lun[i];
+ ld->cl_controller = &sc->ciss_controllers[bus];
+ if (ciss_identify_logical(sc, ld) != 0)
continue;
/*
* If the drive has had media exchanged, we should bring it online.
*/
- if (sc->ciss_logical[i].cl_lstatus->media_exchanged)
- ciss_accept_media(sc, i, 0);
+ if (ld->cl_lstatus->media_exchanged)
+ ciss_accept_media(sc, ld, 0);
- } else {
- sc->ciss_logical[i].cl_status = CISS_LD_NONEXISTENT;
}
}
- error = 0;
out:
+ if (cll != NULL)
+ free(cll, CISS_MALLOC_CLASS);
+ return(error);
+}
+
+static int
+ciss_init_physical(struct ciss_softc *sc)
+{
+ struct ciss_lun_report *cll;
+ int error = 0, i;
+ int nphys;
+
+ debug_called(1);
+
+ cll = ciss_report_luns(sc, CISS_OPCODE_REPORT_PHYSICAL_LUNS,
+ CISS_MAX_PHYSICAL);
+ if (cll == NULL) {
+ error = ENXIO;
+ goto out;
+ }
+
+ nphys = (ntohl(cll->list_size) / sizeof(union ciss_device_address));
+
+ if (bootverbose) {
+ ciss_printf(sc, "%d physical device%s\n",
+ nphys, (nphys > 1 || nphys == 0) ? "s" : "");
+ }
+
/*
- * Note that if the error is a timeout, we are taking a slight
- * risk here and assuming that the adapter will not respond at a
- * later time, scribbling over host memory.
+ * If the L2 and L3 SCSI addresses are 0, this signifies a proxy
+ * controller. A proxy controller is another physical controller
+ * behind the primary PCI controller. We need to know about this
+ * so that BMIC commands can be properly targeted. There can be
+ * proxy controllers attached to a single PCI controller, so
+ * find the highest numbered one so the array can be properly
+ * sized.
*/
- if (cr != NULL)
- ciss_release_request(cr);
+ sc->ciss_max_bus_number = 1;
+ for (i = 0; i < nphys; i++) {
+ if (cll->lun[i].physical.extra_address == 0) {
+ sc->ciss_max_bus_number = cll->lun[i].physical.bus + 1;
+ }
+ }
+
+ sc->ciss_controllers =
+ malloc(sc->ciss_max_bus_number * sizeof (union ciss_device_address),
+ CISS_MALLOC_CLASS, M_NOWAIT | M_ZERO);
+
+ if (sc->ciss_controllers == NULL) {
+ ciss_printf(sc, "Could not allocate memory for controller map\n");
+ error = ENOMEM;
+ goto out;
+ }
+
+ /* setup a map of controller addresses */
+ for (i = 0; i < nphys; i++) {
+ if (cll->lun[i].physical.extra_address == 0) {
+ sc->ciss_controllers[cll->lun[i].physical.bus] = cll->lun[i];
+ }
+ }
+
+out:
if (cll != NULL)
free(cll, CISS_MALLOC_CLASS);
+
return(error);
}
@@ -1031,10 +1168,8 @@ ciss_inquiry_logical(struct ciss_softc *sc, struct ciss_ldrive *ld)
struct scsi_inquiry *inq;
int error;
int command_status;
- int lun;
cr = NULL;
- lun = ld->cl_address.logical.lun;
bzero(&ld->cl_geometry, sizeof(ld->cl_geometry));
@@ -1046,8 +1181,7 @@ ciss_inquiry_logical(struct ciss_softc *sc, struct ciss_ldrive *ld)
cr->cr_length = sizeof(ld->cl_geometry);
cr->cr_flags = CISS_REQ_DATAIN;
- cc->header.address.logical.mode = CISS_HDR_ADDRESS_MODE_LOGICAL;
- cc->header.address.logical.lun = lun;
+ cc->header.address = ld->cl_address;
cc->cdb.cdb_length = 6;
cc->cdb.type = CISS_CDB_TYPE_COMMAND;
cc->cdb.attribute = CISS_CDB_ATTRIBUTE_SIMPLE;
@@ -1107,8 +1241,9 @@ ciss_identify_logical(struct ciss_softc *sc, struct ciss_ldrive *ld)
sizeof(*ld->cl_ldrive))) != 0)
goto out;
cc = CISS_FIND_COMMAND(cr);
+ cc->header.address = *ld->cl_controller; /* target controller */
cbc = (struct ciss_bmic_cdb *)&(cc->cdb.cdb[0]);
- cbc->log_drive = ld->cl_address.logical.lun;
+ cbc->log_drive = CISS_LUN_TO_TARGET(ld->cl_address.logical.lun);
/*
* Submit the request and wait for it to complete.
@@ -1153,8 +1288,9 @@ ciss_identify_logical(struct ciss_softc *sc, struct ciss_ldrive *ld)
* Print the drive's basic characteristics.
*/
if (bootverbose) {
- ciss_printf(sc, "logical drive %d: %s, %dMB ",
- ld->cl_address.logical.lun,
+ ciss_printf(sc, "logical drive (b%dt%d): %s, %dMB ",
+ CISS_LUN_TO_BUS(ld->cl_address.logical.lun),
+ CISS_LUN_TO_TARGET(ld->cl_address.logical.lun),
ciss_name_ldrive_org(ld->cl_ldrive->fault_tolerance),
((ld->cl_ldrive->blocks_available / (1024 * 1024)) *
ld->cl_ldrive->block_size));
@@ -1201,8 +1337,9 @@ ciss_get_ldrive_status(struct ciss_softc *sc, struct ciss_ldrive *ld)
sizeof(*ld->cl_lstatus))) != 0)
goto out;
cc = CISS_FIND_COMMAND(cr);
+ cc->header.address = *ld->cl_controller; /* target controller */
cbc = (struct ciss_bmic_cdb *)&(cc->cdb.cdb[0]);
- cbc->log_drive = ld->cl_address.logical.lun;
+ cbc->log_drive = CISS_LUN_TO_TARGET(ld->cl_address.logical.lun);
/*
* Submit the request and wait for it to complete.
@@ -1271,7 +1408,7 @@ ciss_update_config(struct ciss_softc *sc)
* could make sure it's not open right now.
*/
static int
-ciss_accept_media(struct ciss_softc *sc, int ldrive, int async)
+ciss_accept_media(struct ciss_softc *sc, struct ciss_ldrive *ld, int async)
{
struct ciss_request *cr;
struct ciss_command *cc;
@@ -1288,8 +1425,9 @@ ciss_accept_media(struct ciss_softc *sc, int ldrive, int async)
NULL, 0)) != 0)
goto out;
cc = CISS_FIND_COMMAND(cr);
+ cc->header.address = *ld->cl_controller; /* target controller */
cbc = (struct ciss_bmic_cdb *)&(cc->cdb.cdb[0]);
- cbc->log_drive = ldrive;
+ cbc->log_drive = CISS_LUN_TO_TARGET(ld->cl_address.logical.lun);
/*
* Dispatch the request asynchronously if we can't sleep waiting
@@ -1350,6 +1488,7 @@ static void
ciss_free(struct ciss_softc *sc)
{
struct ciss_request *cr;
+ int i;
debug_called(1);
@@ -1362,6 +1501,8 @@ ciss_free(struct ciss_softc *sc)
/* cancel the Event Notify chain */
ciss_notify_abort(sc);
+ ciss_kill_notify_thread(sc);
+
/* free the controller data */
if (sc->ciss_id != NULL)
free(sc->ciss_id, CISS_MALLOC_CLASS);
@@ -1398,12 +1539,25 @@ ciss_free(struct ciss_softc *sc)
/* disconnect from CAM */
if (sc->ciss_cam_sim) {
- xpt_bus_deregister(cam_sim_path(sc->ciss_cam_sim));
- cam_sim_free(sc->ciss_cam_sim, 0);
+ for (i = 0; i < sc->ciss_max_bus_number; i++) {
+ if (sc->ciss_cam_sim[i]) {
+ xpt_bus_deregister(cam_sim_path(sc->ciss_cam_sim[i]));
+ cam_sim_free(sc->ciss_cam_sim[i], 0);
+ }
+ }
+ free(sc->ciss_cam_sim, CISS_MALLOC_CLASS);
}
if (sc->ciss_cam_devq)
cam_simq_free(sc->ciss_cam_devq);
- /* XXX what about ciss_cam_path? */
+
+ if (sc->ciss_logical) {
+ for (i = 0; i < sc->ciss_max_bus_number; i++)
+ free(sc->ciss_logical[i], CISS_MALLOC_CLASS);
+ free(sc->ciss_logical, CISS_MALLOC_CLASS);
+ }
+
+ if (sc->ciss_controllers)
+ free(sc->ciss_controllers, CISS_MALLOC_CLASS);
}
/************************************************************************
@@ -1636,6 +1790,9 @@ ciss_report_request(struct ciss_request *cr, int *command_status, int *scsi_stat
ce->additional_error_info.invalid_command.offense_value);
}
}
+#if 0
+ ciss_print_request(cr);
+#endif
return(1);
}
@@ -2097,6 +2254,7 @@ out:
static int
ciss_cam_init(struct ciss_softc *sc)
{
+ int i;
debug_called(1);
@@ -2112,21 +2270,32 @@ ciss_cam_init(struct ciss_softc *sc)
/*
* Create a SIM.
*/
- if ((sc->ciss_cam_sim = cam_sim_alloc(ciss_cam_action, ciss_cam_poll, "ciss", sc,
- device_get_unit(sc->ciss_dev),
- sc->ciss_max_requests - 2,
- 1,
- sc->ciss_cam_devq)) == NULL) {
- ciss_printf(sc, "can't allocate CAM SIM\n");
+ sc->ciss_cam_sim = malloc(sc->ciss_max_bus_number * sizeof(struct cam_sim *),
+ CISS_MALLOC_CLASS, M_NOWAIT | M_ZERO);
+ if (sc->ciss_cam_sim == NULL) {
+ ciss_printf(sc, "can't allocate memory for controller SIM\n");
return(ENOMEM);
}
- /*
- * Register bus 0 (the 'logical drives' bus) with this SIM.
- */
- if (xpt_bus_register(sc->ciss_cam_sim, 0) != 0) {
- ciss_printf(sc, "can't register SCSI bus 0\n");
- return(ENXIO);
+ for (i = 0; i < sc->ciss_max_bus_number; i++) {
+ if ((sc->ciss_cam_sim[i] = cam_sim_alloc(ciss_cam_action, ciss_cam_poll,
+ "ciss", sc,
+ device_get_unit(sc->ciss_dev),
+ sc->ciss_max_requests - 2,
+ 1,
+ sc->ciss_cam_devq)) == NULL) {
+ ciss_printf(sc, "can't allocate CAM SIM for controller %d\n", i);
+ return(ENOMEM);
+ }
+
+ /*
+ * Register bus with this SIM.
+ */
+ if ((i == 0 || sc->ciss_controllers[i].physical.bus != 0) &&
+ xpt_bus_register(sc->ciss_cam_sim[i], i) != 0) {
+ ciss_printf(sc, "can't register SCSI bus %d\n", i);
+ return(ENXIO);
+ }
}
/*
@@ -2141,9 +2310,10 @@ ciss_cam_init(struct ciss_softc *sc)
* Initiate a rescan of the 'logical devices' SIM
*/
static void
-ciss_cam_rescan_target(struct ciss_softc *sc, int target)
+ciss_cam_rescan_target(struct ciss_softc *sc, int bus, int target)
{
- union ccb *ccb;
+ struct cam_path *path;
+ union ccb *ccb;
debug_called(1);
@@ -2152,14 +2322,14 @@ ciss_cam_rescan_target(struct ciss_softc *sc, int target)
return;
}
- if (xpt_create_path(&sc->ciss_cam_path, xpt_periph, cam_sim_path(sc->ciss_cam_sim), target, 0)
- != CAM_REQ_CMP) {
+ if (xpt_create_path(&path, xpt_periph, cam_sim_path(sc->ciss_cam_sim[bus]),
+ target, CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
ciss_printf(sc, "rescan failed (can't create path)\n");
free(ccb, M_TEMP);
return;
}
- xpt_setup_ccb(&ccb->ccb_h, sc->ciss_cam_path, 5/*priority (low)*/);
+ xpt_setup_ccb(&ccb->ccb_h, path, 5/*priority (low)*/);
ccb->ccb_h.func_code = XPT_SCAN_BUS;
ccb->ccb_h.cbfcnp = ciss_cam_rescan_callback;
ccb->crcn.flags = CAM_FLAG_NONE;
@@ -2171,7 +2341,10 @@ ciss_cam_rescan_target(struct ciss_softc *sc, int target)
static void
ciss_cam_rescan_all(struct ciss_softc *sc)
{
- ciss_cam_rescan_target(sc, 0);
+ int i;
+
+ for (i = 0; i < sc->ciss_max_bus_number; i++)
+ ciss_cam_rescan_target(sc, i, CAM_TARGET_WILDCARD);
}
static void
@@ -2189,9 +2362,10 @@ ciss_cam_action(struct cam_sim *sim, union ccb *ccb)
{
struct ciss_softc *sc;
struct ccb_scsiio *csio;
- int target;
+ int bus, target;
sc = cam_sim_softc(sim);
+ bus = cam_sim_bus(sim);
csio = (struct ccb_scsiio *)&ccb->csio;
target = csio->ccb_h.target_id;
@@ -2207,7 +2381,7 @@ ciss_cam_action(struct cam_sim *sim, union ccb *ccb)
case XPT_CALC_GEOMETRY:
{
struct ccb_calc_geometry *ccg = &ccb->ccg;
- struct ciss_ldrive *ld = &sc->ciss_logical[target];
+ struct ciss_ldrive *ld = &sc->ciss_logical[bus][target];
debug(1, "XPT_CALC_GEOMETRY %d:%d:%d", cam_sim_bus(sim), ccb->ccb_h.target_id, ccb->ccb_h.target_lun);
@@ -2301,13 +2475,6 @@ ciss_cam_action_io(struct cam_sim *sim, struct ccb_scsiio *csio)
debug(2, "XPT_SCSI_IO %d:%d:%d", bus, target, csio->ccb_h.target_lun);
- /* check for I/O attempt to nonexistent device */
- if ((bus != 0) ||
- (target >= CISS_MAX_LOGICAL)) {
- debug(3, " device does not exist");
- csio->ccb_h.status = CAM_REQ_CMP_ERR;
- }
-
/* firmware does not support commands > 10 bytes */
if (csio->cdb_len > 12/*CISS_CDB_BUFFER_SIZE*/) {
debug(3, " command too large (%d > %d)", csio->cdb_len, CISS_CDB_BUFFER_SIZE);
@@ -2348,7 +2515,7 @@ ciss_cam_action_io(struct cam_sim *sim, struct ccb_scsiio *csio)
* request completes.
*/
if ((error = ciss_get_request(sc, &cr)) != 0) {
- xpt_freeze_simq(sc->ciss_cam_sim, 1);
+ xpt_freeze_simq(sc->ciss_cam_sim[bus], 1);
csio->ccb_h.status |= CAM_REQUEUE_REQ;
return(error);
}
@@ -2362,8 +2529,10 @@ ciss_cam_action_io(struct cam_sim *sim, struct ccb_scsiio *csio)
cr->cr_complete = ciss_cam_complete;
cr->cr_private = csio;
- cc->header.address.logical.mode = CISS_HDR_ADDRESS_MODE_LOGICAL;
- cc->header.address.logical.lun = target;
+ /*
+ * Target the right logical volume.
+ */
+ cc->header.address = sc->ciss_logical[bus][target].cl_address;
cc->cdb.cdb_length = csio->cdb_len;
cc->cdb.type = CISS_CDB_TYPE_COMMAND;
cc->cdb.attribute = CISS_CDB_ATTRIBUTE_SIMPLE; /* XXX ordered tags? */
@@ -2392,7 +2561,7 @@ ciss_cam_action_io(struct cam_sim *sim, struct ccb_scsiio *csio)
* if the adapter rejects the command).
*/
if ((error = ciss_start(cr)) != 0) {
- xpt_freeze_simq(sc->ciss_cam_sim, 1);
+ xpt_freeze_simq(sc->ciss_cam_sim[bus], 1);
if (error == EINPROGRESS) {
csio->ccb_h.status |= CAM_RELEASE_SIMQ;
error = 0;
@@ -2412,10 +2581,11 @@ ciss_cam_action_io(struct cam_sim *sim, struct ccb_scsiio *csio)
static int
ciss_cam_emulate(struct ciss_softc *sc, struct ccb_scsiio *csio)
{
- int target;
+ int bus, target;
u_int8_t opcode;
target = csio->ccb_h.target_id;
+ bus = cam_sim_bus(xpt_path_sim(csio->ccb_h.path));
opcode = (csio->ccb_h.flags & CAM_CDB_POINTER) ?
*(u_int8_t *)csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes[0];
@@ -2424,7 +2594,7 @@ ciss_cam_emulate(struct ciss_softc *sc, struct ccb_scsiio *csio)
* is slightly better than an illegal request. Other errors might be
* better.
*/
- if (sc->ciss_logical[target].cl_status == CISS_LD_NONEXISTENT) {
+ if (sc->ciss_logical[bus][target].cl_status == CISS_LD_NONEXISTENT) {
csio->ccb_h.status = CAM_SEL_TIMEOUT;
xpt_done((union ccb *)csio);
return(1);
@@ -2435,7 +2605,7 @@ ciss_cam_emulate(struct ciss_softc *sc, struct ccb_scsiio *csio)
*
* I/O operations should fail, everything else should work.
*/
- if (sc->ciss_logical[target].cl_status == CISS_LD_OFFLINE) {
+ if (sc->ciss_logical[bus][target].cl_status == CISS_LD_OFFLINE) {
switch(opcode) {
case READ_6:
case READ_10:
@@ -2524,7 +2694,7 @@ ciss_cam_complete(struct ciss_request *cr)
/* check condition, sense data included */
case SCSI_STATUS_CHECK_COND: /* CISS_SCSI_STATUS_CHECK_CONDITION */
- debug(0, "SCSI_STATUS_CHECK_COND sense size %d resid %d",
+ debug(0, "SCSI_STATUS_CHECK_COND sense size %d resid %d\n",
ce->sense_length, ce->residual_count);
bzero(&csio->sense_data, SSD_FULL_SIZE);
bcopy(&ce->sense_info[0], &csio->sense_data, ce->sense_length);
@@ -2568,14 +2738,15 @@ ciss_cam_complete_fixup(struct ciss_softc *sc, struct ccb_scsiio *csio)
{
struct scsi_inquiry_data *inq;
struct ciss_ldrive *cl;
- int target;
+ int bus, target;
if (((csio->ccb_h.flags & CAM_CDB_POINTER) ?
*(u_int8_t *)csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes[0]) == INQUIRY) {
inq = (struct scsi_inquiry_data *)csio->data_ptr;
target = csio->ccb_h.target_id;
- cl = &sc->ciss_logical[target];
+ bus = cam_sim_bus(xpt_path_sim(csio->ccb_h.path));
+ cl = &sc->ciss_logical[bus][target];
padstr(inq->vendor, "COMPAQ", 8);
padstr(inq->product, ciss_name_ldrive_org(cl->cl_ldrive->fault_tolerance), 8);
@@ -2588,13 +2759,14 @@ ciss_cam_complete_fixup(struct ciss_softc *sc, struct ccb_scsiio *csio)
* Find a peripheral attached at (target)
*/
static struct cam_periph *
-ciss_find_periph(struct ciss_softc *sc, int target)
+ciss_find_periph(struct ciss_softc *sc, int bus, int target)
{
struct cam_periph *periph;
struct cam_path *path;
int status;
- status = xpt_create_path(&path, NULL, cam_sim_path(sc->ciss_cam_sim), target, 0);
+ status = xpt_create_path(&path, NULL, cam_sim_path(sc->ciss_cam_sim[bus]),
+ target, 0);
if (status == CAM_REQ_CMP) {
periph = cam_periph_find(path, NULL);
xpt_free_path(path);
@@ -2610,15 +2782,15 @@ ciss_find_periph(struct ciss_softc *sc, int target)
* XXX is this strictly correct?
*/
static int
-ciss_name_device(struct ciss_softc *sc, int target)
+ciss_name_device(struct ciss_softc *sc, int bus, int target)
{
struct cam_periph *periph;
- if ((periph = ciss_find_periph(sc, target)) != NULL) {
- sprintf(sc->ciss_logical[target].cl_name, "%s%d", periph->periph_name, periph->unit_number);
+ if ((periph = ciss_find_periph(sc, bus, target)) != NULL) {
+ sprintf(sc->ciss_logical[bus][target].cl_name, "%s%d", periph->periph_name, periph->unit_number);
return(0);
}
- sc->ciss_logical[target].cl_name[0] = 0;
+ sc->ciss_logical[bus][target].cl_name[0] = 0;
return(ENOENT);
}
@@ -2764,7 +2936,6 @@ ciss_notify_complete(struct ciss_request *cr)
struct ciss_softc *sc;
int scsi_status;
int command_status;
-
debug_called(1);
cc = CISS_FIND_COMMAND(cr);
@@ -2801,26 +2972,6 @@ ciss_notify_complete(struct ciss_request *cr)
cn->class, cn->subclass, cn->detail);
/*
- * If there's room, save the event for a user-level tool.
- */
- if (((sc->ciss_notify_head + 1) % CISS_MAX_EVENTS) != sc->ciss_notify_tail) {
- sc->ciss_notify[sc->ciss_notify_head] = *cn;
- sc->ciss_notify_head = (sc->ciss_notify_head + 1) % CISS_MAX_EVENTS;
- }
-
- /*
- * Some events are directly of interest to us.
- */
- switch (cn->class) {
- case CISS_NOTIFY_LOGICAL:
- ciss_notify_logical(sc, cn);
- break;
- case CISS_NOTIFY_PHYSICAL:
- ciss_notify_physical(sc, cn);
- break;
- }
-
- /*
* If the response indicates that the notifier has been aborted,
* release the notifier command.
*/
@@ -2832,8 +2983,13 @@ ciss_notify_complete(struct ciss_request *cr)
ciss_release_request(cr);
sc->ciss_periodic_notify = NULL;
wakeup(&sc->ciss_periodic_notify);
+ } else {
+ /* Handle notify events in a kernel thread */
+ ciss_enqueue_notify(cr);
+ sc->ciss_periodic_notify = NULL;
+ wakeup(&sc->ciss_periodic_notify);
+ wakeup(&sc->ciss_notify);
}
-
/*
* Send a new notify event command, if we're not aborting.
*/
@@ -3030,6 +3186,75 @@ out:
}
/************************************************************************
+ * Handle rescanning all the logical volumes when a notify event
+ * causes the drives to come online or offline.
+ */
+static void
+ciss_notify_rescan_logical(struct ciss_softc *sc)
+{
+ struct ciss_lun_report *cll;
+ struct ciss_ldrive *ld;
+ int i, j, ndrives;
+
+ /*
+ * We must rescan all logical volumes to get the right logical
+ * drive address.
+ */
+ cll = ciss_report_luns(sc, CISS_OPCODE_REPORT_LOGICAL_LUNS,
+ CISS_MAX_LOGICAL);
+ if (cll == NULL)
+ return;
+
+ ndrives = (ntohl(cll->list_size) / sizeof(union ciss_device_address));
+
+ /*
+ * Delete any of the drives which were destroyed by the
+ * firmware.
+ */
+ for (i = 0; i < sc->ciss_max_bus_number; i++) {
+ for (j = 0; j < CISS_MAX_LOGICAL; j++) {
+ ld = &sc->ciss_logical[i][j];
+
+ if (ld->cl_update == 0)
+ continue;
+
+ if (ld->cl_status != CISS_LD_ONLINE) {
+ ciss_cam_rescan_target(sc, i, j);
+ ld->cl_update = 0;
+ if (ld->cl_ldrive)
+ free(ld->cl_ldrive, CISS_MALLOC_CLASS);
+ if (ld->cl_lstatus)
+ free(ld->cl_lstatus, CISS_MALLOC_CLASS);
+
+ ld->cl_ldrive = NULL;
+ ld->cl_lstatus = NULL;
+ }
+ }
+ }
+
+ /*
+ * Scan for new drives.
+ */
+ for (i = 0; i < ndrives; i++) {
+ int bus, target;
+
+ bus = CISS_LUN_TO_BUS(cll->lun[i].logical.lun);
+ target = CISS_LUN_TO_TARGET(cll->lun[i].logical.lun);
+ ld = &sc->ciss_logical[bus][target];
+
+ if (ld->cl_update == 0)
+ continue;
+
+ ld->cl_address = cll->lun[i];
+ ld->cl_controller = &sc->ciss_controllers[bus];
+ if (ciss_identify_logical(sc, ld) == 0) {
+ ciss_cam_rescan_target(sc, bus, target);
+ }
+ }
+ free(cll, CISS_MALLOC_CLASS);
+}
+
+/************************************************************************
* Handle a notify event relating to the status of a logical drive.
*
* XXX need to be able to defer some of these to properly handle
@@ -3040,17 +3265,19 @@ static void
ciss_notify_logical(struct ciss_softc *sc, struct ciss_notify *cn)
{
struct ciss_ldrive *ld;
- int ostatus;
+ int ostatus, bus, target;
debug_called(2);
- ld = &sc->ciss_logical[cn->data.logical_status.logical_drive];
+ bus = cn->device.physical.bus;
+ target = cn->data.logical_status.logical_drive;
+ ld = &sc->ciss_logical[bus][target];
switch (cn->subclass) {
case CISS_NOTIFY_LOGICAL_STATUS:
switch (cn->detail) {
case 0:
- ciss_name_device(sc, cn->data.logical_status.logical_drive);
+ ciss_name_device(sc, bus, target);
ciss_printf(sc, "logical drive %d (%s) changed status %s->%s, spare status 0x%b\n",
cn->data.logical_status.logical_drive, ld->cl_name,
ciss_name_ldrive_status(cn->data.logical_status.previous_state),
@@ -3066,28 +3293,28 @@ ciss_notify_logical(struct ciss_softc *sc, struct ciss_notify *cn)
if (ld->cl_lstatus != NULL)
ld->cl_lstatus->status = cn->data.logical_status.new_state;
-#if 0
/*
* Have CAM rescan the drive if its status has changed.
*/
- if (ostatus != ld->cl_status)
- ciss_cam_rescan_target(sc, cn->data.logical_status.logical_drive);
-#endif
+ if (ostatus != ld->cl_status) {
+ ld->cl_update = 1;
+ ciss_notify_rescan_logical(sc);
+ }
break;
case 1: /* logical drive has recognised new media, needs Accept Media Exchange */
- ciss_name_device(sc, cn->data.logical_status.logical_drive);
+ ciss_name_device(sc, bus, target);
ciss_printf(sc, "logical drive %d (%s) media exchanged, ready to go online\n",
cn->data.logical_status.logical_drive, ld->cl_name);
- ciss_accept_media(sc, cn->data.logical_status.logical_drive, 1);
+ ciss_accept_media(sc, ld, 1);
break;
case 2:
case 3:
ciss_printf(sc, "rebuild of logical drive %d (%s) failed due to %s error\n",
cn->data.rebuild_aborted.logical_drive,
- sc->ciss_logical[cn->data.rebuild_aborted.logical_drive].cl_name,
+ ld->cl_name,
(cn->detail == 2) ? "read" : "write");
break;
}
@@ -3097,7 +3324,7 @@ ciss_notify_logical(struct ciss_softc *sc, struct ciss_notify *cn)
if (cn->detail == 0) {
ciss_printf(sc, "FATAL I/O ERROR on logical drive %d (%s), SCSI port %d ID %d\n",
cn->data.io_error.logical_drive,
- sc->ciss_logical[cn->data.io_error.logical_drive].cl_name,
+ ld->cl_name,
cn->data.io_error.failure_bus,
cn->data.io_error.failure_drive);
/* XXX should we take the drive down at this point, or will we be told? */
@@ -3108,7 +3335,7 @@ ciss_notify_logical(struct ciss_softc *sc, struct ciss_notify *cn)
if (cn->detail == 0)
ciss_printf(sc, "logical drive %d (%s) completed consistency initialisation\n",
cn->data.consistency_completed.logical_drive,
- sc->ciss_logical[cn->data.consistency_completed.logical_drive].cl_name);
+ ld->cl_name);
break;
}
}
@@ -3119,7 +3346,91 @@ ciss_notify_logical(struct ciss_softc *sc, struct ciss_notify *cn)
static void
ciss_notify_physical(struct ciss_softc *sc, struct ciss_notify *cn)
{
+}
+
+/************************************************************************
+ * Handle deferred processing of notify events. Notify events may need
+ * sleep which is unsafe during an interrupt.
+ */
+static void
+ciss_notify_thread(void *arg)
+{
+ struct ciss_softc *sc;
+ struct ciss_request *cr;
+ struct ciss_notify *cn;
+ int s;
+
+#if __FreeBSD_version >= 500000
+ mtx_lock(&Giant);
+#endif
+ sc = (struct ciss_softc *)arg;
+
+ s = splcam();
+ for (;;) {
+ if (TAILQ_EMPTY(&sc->ciss_notify) != 0 &&
+ (sc->ciss_flags & CISS_FLAG_THREAD_SHUT) == 0) {
+ tsleep(&sc->ciss_notify, PUSER, "idle", 0);
+ }
+
+ if (sc->ciss_flags & CISS_FLAG_THREAD_SHUT)
+ break;
+
+ cr = ciss_dequeue_notify(sc);
+ splx(s);
+
+ if (cr == NULL)
+ panic("cr null");
+ cn = (struct ciss_notify *)cr->cr_data;
+
+ switch (cn->class) {
+ case CISS_NOTIFY_LOGICAL:
+ ciss_notify_logical(sc, cn);
+ break;
+ case CISS_NOTIFY_PHYSICAL:
+ ciss_notify_physical(sc, cn);
+ break;
+ }
+
+ ciss_release_request(cr);
+
+ s = splcam();
+ }
+ sc->ciss_notify_thread = NULL;
+ wakeup(&sc->ciss_notify_thread);
+ splx(s);
+
+#if __FreeBSD_version >= 500000
+ mtx_unlock(&Giant);
+#endif
+ kthread_exit(0);
+}
+
+/************************************************************************
+ * Start the notification kernel thread.
+ */
+static void
+ciss_spawn_notify_thread(struct ciss_softc *sc)
+{
+
+ if (kthread_create((void(*)(void *))ciss_notify_thread, sc,
+ &sc->ciss_notify_thread, 0, 0, "ciss_notify%d",
+ device_get_unit(sc->ciss_dev)))
+ panic("Could not create notify thread\n");
+}
+
+/************************************************************************
+ * Kill the notification kernel thread.
+ */
+static void
+ciss_kill_notify_thread(struct ciss_softc *sc)
+{
+
+ if (sc->ciss_notify_thread == NULL)
+ return;
+ sc->ciss_flags |= CISS_FLAG_THREAD_SHUT;
+ wakeup(&sc->ciss_notify);
+ tsleep(&sc->ciss_notify_thread, PUSER, "thtrm", 0);
}
/************************************************************************
diff --git a/sys/dev/ciss/cissreg.h b/sys/dev/ciss/cissreg.h
index 3924698..b2d50d2 100644
--- a/sys/dev/ciss/cissreg.h
+++ b/sys/dev/ciss/cissreg.h
@@ -463,6 +463,15 @@ struct ciss_config_table
#define CISS_BIG_MAP_ENTRIES 128 /* number of entries in a BIG_MAP */
/*
+ * In the device address of a logical volume, the bus number
+ * is encoded into the logical lun volume number starting
+ * at the second byte, with the first byte defining the
+ * logical drive number.
+ */
+#define CISS_LUN_TO_BUS(x) (((x) >> 16) & 0xFF)
+#define CISS_LUN_TO_TARGET(x) ((x) & 0xFF)
+
+/*
* BMIC CDB
*
* Note that the phys_drive/res1 field is nominally the 32-bit
diff --git a/sys/dev/ciss/cissvar.h b/sys/dev/ciss/cissvar.h
index 6274b9d..717351f 100644
--- a/sys/dev/ciss/cissvar.h
+++ b/sys/dev/ciss/cissvar.h
@@ -47,6 +47,11 @@
#define CISS_MAX_LOGICAL 15
/*
+ * Maximum number of physical devices we support.
+ */
+#define CISS_MAX_PHYSICAL 1024
+
+/*
* Interrupt reduction can be controlled by tuning the interrupt
* coalesce delay and count paramters. The delay (in microseconds)
* defers delivery of interrupts to increase the chance of there being
@@ -66,11 +71,6 @@
*/
#define CISS_HEARTBEAT_RATE 10
-/*
- * Maximum number of events we will queue for a monitoring utility.
- */
-#define CISS_MAX_EVENTS 32
-
/************************************************************************
* Compatibility with older versions of FreeBSD
*/
@@ -86,7 +86,8 @@ typedef struct proc d_thread_t;
#define CISSQ_FREE 0
#define CISSQ_BUSY 1
#define CISSQ_COMPLETE 2
-#define CISSQ_COUNT 3
+#define CISSQ_NOTIFY 3
+#define CISSQ_COUNT 4
struct ciss_qstat
{
@@ -150,12 +151,14 @@ struct ciss_request
struct ciss_ldrive
{
union ciss_device_address cl_address;
-
+ union ciss_device_address *cl_controller;
int cl_status;
#define CISS_LD_NONEXISTENT 0
#define CISS_LD_ONLINE 1
#define CISS_LD_OFFLINE 2
+ int cl_update;
+
struct ciss_bmic_id_ldrive *cl_ldrive;
struct ciss_bmic_id_lstatus *cl_lstatus;
struct ciss_ldrive_geometry cl_geometry;
@@ -202,19 +205,18 @@ struct ciss_softc
TAILQ_HEAD(,ciss_request) ciss_free; /* requests available for reuse */
TAILQ_HEAD(,ciss_request) ciss_busy; /* requests in the adapter */
TAILQ_HEAD(,ciss_request) ciss_complete; /* requests which have been returned by the adapter */
+ TAILQ_HEAD(,ciss_request) ciss_notify; /* requests which are defered for processing */
+ struct proc *ciss_notify_thread;
struct callout_handle ciss_periodic; /* periodic event handling */
struct ciss_request *ciss_periodic_notify; /* notify callback request */
- struct ciss_notify ciss_notify[CISS_MAX_EVENTS];
- int ciss_notify_head; /* saved-event ringbuffer */
- int ciss_notify_tail;
-
- struct ciss_ldrive ciss_logical[CISS_MAX_LOGICAL];
+ struct ciss_ldrive **ciss_logical;
+ union ciss_device_address *ciss_controllers; /* controller address */
+ int ciss_max_bus_number; /* maximum bus number */
struct cam_devq *ciss_cam_devq;
- struct cam_sim *ciss_cam_sim;
- struct cam_path *ciss_cam_path;
+ struct cam_sim **ciss_cam_sim;
int ciss_flags;
#define CISS_FLAG_NOTIFY_OK (1<<0) /* notify command running OK */
@@ -224,6 +226,7 @@ struct ciss_softc
#define CISS_FLAG_FAKE_SYNCH (1<<16) /* needs SYNCHRONISE_CACHE faked */
#define CISS_FLAG_BMIC_ABORT (1<<17) /* use BMIC command to abort Notify on Event */
+#define CISS_FLAG_THREAD_SHUT (1<<20) /* shutdown the kthread */
struct ciss_qstat ciss_qstat[CISSQ_COUNT]; /* queue statistics */
};
@@ -367,6 +370,7 @@ struct hack
CISSQ_REQUEST_QUEUE(free, CISSQ_FREE);
CISSQ_REQUEST_QUEUE(busy, CISSQ_BUSY);
CISSQ_REQUEST_QUEUE(complete, CISSQ_COMPLETE);
+CISSQ_REQUEST_QUEUE(notify, CISSQ_NOTIFY);
/********************************************************************************
* space-fill a character string
OpenPOWER on IntegriCloud