Improve AHCI Enclosure Management and SES interoperation.

Since SES specs do not define mechanism to map enclosure slots to SATA
disks, AHCI EM code I written many years ago appeared quite useless,
that always bugged me.  I was thinking whether it was a good idea, but
if LSI HBAs do that, why I shouldn't?

This change introduces simple non-standard mechanism for the mapping
into both AHCI EM and SES code, that makes AHCI EM on capable controllers
(most of Intel's) a first-class SES citizen, allowing it to report disk
physical path to GEOM, show devices inserted into each enclosure slot in
`sesutil map` and `getencstat`, control locate and fault LEDs for specific
devices with `sesutil locate adaX on` and `sesutil fault adaX on`, etc.

I've successfully tested this on Supermicro X10DRH-i motherboard connected
with sideband cable of its S-SATA Mini-SAS connector to SAS815TQ backplane.
It can indicate with LEDs Locate, Fault and Rebuild/Remap SES statuses for
each disk identical to real SES of Supermicro SAS2 backplanes.

MFC after:	2 weeks
This commit is contained in:
Alexander Motin 2019-06-23 19:05:01 +00:00
parent 7a29e0bf96
commit 53f5ac1310
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=349321
10 changed files with 400 additions and 137 deletions

View File

@ -5574,6 +5574,7 @@ scsi_devid_is_naa_ieee_reg(uint8_t *bufp)
{
struct scsi_vpd_id_descriptor *descr;
struct scsi_vpd_id_naa_basic *naa;
int n;
descr = (struct scsi_vpd_id_descriptor *)bufp;
naa = (struct scsi_vpd_id_naa_basic *)descr->identifier;
@ -5581,7 +5582,8 @@ scsi_devid_is_naa_ieee_reg(uint8_t *bufp)
return 0;
if (descr->length < sizeof(struct scsi_vpd_id_naa_ieee_reg))
return 0;
if ((naa->naa >> SVPD_ID_NAA_NAA_SHIFT) != SVPD_ID_NAA_IEEE_REG)
n = naa->naa >> SVPD_ID_NAA_NAA_SHIFT;
if (n != SVPD_ID_NAA_LOCAL_REG && n != SVPD_ID_NAA_IEEE_REG)
return 0;
return 1;
}

View File

@ -89,6 +89,9 @@ int enc_verbose = 0;
SYSCTL_INT(_kern_cam_enc, OID_AUTO, verbose, CTLFLAG_RWTUN,
&enc_verbose, 0, "Enable verbose logging");
const char *elm_type_names[] = ELM_TYPE_NAMES;
CTASSERT(nitems(elm_type_names) - 1 == ELMTYP_LAST);
static struct periph_driver encdriver = {
enc_init, "ses",
TAILQ_HEAD_INITIALIZER(encdriver.units), /* generation */ 0
@ -240,11 +243,17 @@ enc_async(void *callback_arg, uint32_t code, struct cam_path *path, void *arg)
struct enc_softc *softc;
softc = (struct enc_softc *)periph->softc;
if (xpt_path_path_id(periph->path) != path_id
|| softc == NULL
|| (softc->enc_flags & ENC_FLAG_INITIALIZED)
== 0
|| softc->enc_vec.device_found == NULL)
/* Check this SEP is ready. */
if (softc == NULL || (softc->enc_flags &
ENC_FLAG_INITIALIZED) == 0 ||
softc->enc_vec.device_found == NULL)
continue;
/* Check this SEP may manage this device. */
if (xpt_path_path_id(periph->path) != path_id &&
(softc->enc_type != ENC_SEMB_SES ||
cgd->protocol != PROTO_ATA))
continue;
softc->enc_vec.device_found(softc);
@ -440,7 +449,7 @@ enc_ioctl(struct cdev *dev, u_long cmd, caddr_t arg_addr, int flag,
encioc_element_t kelm;
kelm.elm_idx = i;
kelm.elm_subenc_id = cache->elm_map[i].subenclosure;
kelm.elm_type = cache->elm_map[i].enctype;
kelm.elm_type = cache->elm_map[i].elm_type;
error = copyout(&kelm, &uelm[i], sizeof(kelm));
if (error)
break;

View File

@ -120,10 +120,42 @@ typedef enum {
ELMTYP_SCSI_INI = 0x15,
ELMTYP_SUBENC = 0x16,
ELMTYP_ARRAY_DEV = 0x17,
ELMTYP_SAS_EXP = 0x18, /* SAS expander */
ELMTYP_SAS_CONN = 0x19 /* SAS connector */
ELMTYP_SAS_EXP = 0x18, /* SAS Expander */
ELMTYP_SAS_CONN = 0x19, /* SAS Connector */
ELMTYP_LAST = ELMTYP_SAS_CONN
} elm_type_t;
#define ELM_TYPE_NAMES { \
"Unspecified", \
"Device Slot", \
"Power Supply", \
"Cooling", \
"Temperature Sensors", \
"Door", \
"Audible alarm", \
"Enclosure Services Controller Electronics", \
"SCC Controller Electronics", \
"Nonvolatile Cache", \
"Invalid Operation Reason", \
"Uninterruptible Power Supply", \
"Display", \
"Key Pad Entry", \
"Enclosure", \
"SCSI Port/Transceiver", \
"Language", \
"Communication Port", \
"Voltage Sensor", \
"Current Sensor", \
"SCSI Target Port", \
"SCSI Initiator Port", \
"Simple Subenclosure", \
"Array Device Slot", \
"SAS Expander", \
"SAS Connector" \
}
extern const char *elm_type_names[];
typedef struct encioc_element {
/* Element Index */
unsigned int elm_idx;

View File

@ -39,16 +39,12 @@
#include <sys/sysctl.h>
typedef struct enc_element {
uint32_t
enctype : 8, /* enclosure type */
subenclosure : 8, /* subenclosure id */
svalid : 1, /* enclosure information valid */
overall_status_elem: 1,/*
* This object represents generic
* status about all objects of this
* type.
*/
priv : 14; /* private data, per object */
uint8_t elm_idx; /* index of element */
uint8_t elm_type; /* element type */
uint8_t subenclosure; /* subenclosure id */
uint8_t type_elm_idx; /* index of element within type */
uint8_t svalid; /* enclosure information valid */
uint16_t priv; /* private data, per object */
uint8_t encstat[4]; /* state && stats */
uint8_t *physical_path; /* Device physical path data. */
u_int physical_path_len; /* Length of device path data. */

View File

@ -301,21 +301,21 @@ safte_process_config(enc_softc_t *enc, struct enc_fsm_state *state,
* in later fetches of status.
*/
for (i = 0; i < cfg->Nfans; i++)
enc->enc_cache.elm_map[r++].enctype = ELMTYP_FAN;
enc->enc_cache.elm_map[r++].elm_type = ELMTYP_FAN;
cfg->pwroff = (uint8_t) r;
for (i = 0; i < cfg->Npwr; i++)
enc->enc_cache.elm_map[r++].enctype = ELMTYP_POWER;
enc->enc_cache.elm_map[r++].elm_type = ELMTYP_POWER;
for (i = 0; i < cfg->DoorLock; i++)
enc->enc_cache.elm_map[r++].enctype = ELMTYP_DOORLOCK;
enc->enc_cache.elm_map[r++].elm_type = ELMTYP_DOORLOCK;
if (cfg->Nspkrs > 0)
enc->enc_cache.elm_map[r++].enctype = ELMTYP_ALARM;
enc->enc_cache.elm_map[r++].elm_type = ELMTYP_ALARM;
for (i = 0; i < cfg->Ntherm; i++)
enc->enc_cache.elm_map[r++].enctype = ELMTYP_THERM;
enc->enc_cache.elm_map[r++].elm_type = ELMTYP_THERM;
for (i = 0; i <= cfg->Ntstats; i++)
enc->enc_cache.elm_map[r++].enctype = ELMTYP_THERM;
enc->enc_cache.elm_map[r++].elm_type = ELMTYP_THERM;
cfg->slotoff = (uint8_t) r;
for (i = 0; i < cfg->Nslots; i++)
enc->enc_cache.elm_map[r++].enctype =
enc->enc_cache.elm_map[r++].elm_type =
emulate_array_devices ? ELMTYP_ARRAY_DEV :
ELMTYP_DEVICE;
@ -505,7 +505,7 @@ safte_process_status(enc_softc_t *enc, struct enc_fsm_state *state,
*/
for (i = 0; i < cfg->Nslots; i++) {
SAFT_BAIL(r, xfer_len);
if (cache->elm_map[cfg->slotoff + i].enctype == ELMTYP_DEVICE)
if (cache->elm_map[cfg->slotoff + i].elm_type == ELMTYP_DEVICE)
cache->elm_map[cfg->slotoff + i].encstat[1] = buf[r];
r++;
}
@ -678,7 +678,7 @@ safte_process_slotstatus(enc_softc_t *enc, struct enc_fsm_state *state,
oid = cfg->slotoff;
for (r = i = 0; i < cfg->Nslots; i++, r += 4) {
SAFT_BAIL(r+3, xfer_len);
if (cache->elm_map[oid].enctype == ELMTYP_ARRAY_DEV)
if (cache->elm_map[oid].elm_type == ELMTYP_ARRAY_DEV)
cache->elm_map[oid].encstat[1] = 0;
cache->elm_map[oid].encstat[2] &= SESCTL_RQSID;
cache->elm_map[oid].encstat[3] = 0;
@ -705,7 +705,7 @@ safte_process_slotstatus(enc_softc_t *enc, struct enc_fsm_state *state,
cache->elm_map[oid].encstat[3] |= SESCTL_RQSFLT;
if (buf[r+0] & 0x40)
cache->elm_map[oid].encstat[0] |= SESCTL_PRDFAIL;
if (cache->elm_map[oid].enctype == ELMTYP_ARRAY_DEV) {
if (cache->elm_map[oid].elm_type == ELMTYP_ARRAY_DEV) {
if (buf[r+0] & 0x01)
cache->elm_map[oid].encstat[1] |= 0x80;
if (buf[r+0] & 0x04)
@ -771,7 +771,7 @@ safte_fill_control_request(enc_softc_t *enc, struct enc_fsm_state *state,
} else {
ep = &enc->enc_cache.elm_map[idx];
switch (ep->enctype) {
switch (ep->elm_type) {
case ELMTYP_DEVICE:
case ELMTYP_ARRAY_DEV:
switch (cfg->current_request_stage) {
@ -781,7 +781,7 @@ safte_fill_control_request(enc_softc_t *enc, struct enc_fsm_state *state,
ep->priv |= 0x40;
if (req->elm_stat[3] & SESCTL_RQSFLT)
ep->priv |= 0x02;
if (ep->enctype == ELMTYP_ARRAY_DEV) {
if (ep->elm_type == ELMTYP_ARRAY_DEV) {
if (req->elm_stat[1] & 0x01)
ep->priv |= 0x200;
if (req->elm_stat[1] & 0x02)
@ -970,7 +970,7 @@ safte_process_control_request(enc_softc_t *enc, struct enc_fsm_state *state,
if (idx == SES_SETSTATUS_ENC_IDX)
type = -1;
else
type = enc->enc_cache.elm_map[idx].enctype;
type = enc->enc_cache.elm_map[idx].elm_type;
if (type == ELMTYP_DEVICE || type == ELMTYP_ARRAY_DEV)
enc_update_request(enc, SAFTE_UPDATE_READSLOTSTATUS);
else

View File

@ -102,6 +102,7 @@ typedef struct ses_addl_status {
union {
union ses_fcobj_hdr *fc;
union ses_elm_sas_hdr *sas;
struct ses_elm_ata_hdr *ata;
} proto_hdr;
union ses_addl_data proto_data; /* array sizes stored in header */
} ses_add_status_t;
@ -825,14 +826,6 @@ ses_devids_iter(enc_softc_t *enc, enc_element_t *elm,
elmpriv = elm->elm_private;
addl = &(elmpriv->addl);
/*
* Don't assume this object has additional status information, or
* that it is a SAS device, or that it is a device slot device.
*/
if (addl->hdr == NULL || addl->proto_hdr.sas == NULL
|| addl->proto_data.sasdev_phys == NULL)
return;
devid_record_size = SVPD_DEVICE_ID_DESC_HDR_LEN
+ sizeof(struct scsi_vpd_id_naa_ieee_reg);
for (i = 0; i < addl->proto_hdr.sas->base_hdr.num_phys; i++) {
@ -950,11 +943,40 @@ static void
ses_paths_iter(enc_softc_t *enc, enc_element_t *elm,
ses_path_callback_t *callback, void *callback_arg)
{
ses_path_iter_args_t args;
ses_element_t *elmpriv;
struct ses_addl_status *addl;
args.callback = callback;
args.callback_arg = callback_arg;
ses_devids_iter(enc, elm, ses_path_iter_devid_callback, &args);
elmpriv = elm->elm_private;
addl = &(elmpriv->addl);
if (addl->hdr == NULL)
return;
if (addl->proto_hdr.sas != NULL &&
addl->proto_data.sasdev_phys != NULL) {
ses_path_iter_args_t args;
args.callback = callback;
args.callback_arg = callback_arg;
ses_devids_iter(enc, elm, ses_path_iter_devid_callback, &args);
} else if (addl->proto_hdr.ata != NULL) {
struct cam_path *path;
struct ccb_getdev cgd;
if (xpt_create_path(&path, /*periph*/NULL,
scsi_4btoul(addl->proto_hdr.ata->bus),
scsi_4btoul(addl->proto_hdr.ata->target), 0)
!= CAM_REQ_CMP)
return;
xpt_setup_ccb(&cgd.ccb_h, path, CAM_PRIORITY_NORMAL);
cgd.ccb_h.func_code = XPT_GDEV_TYPE;
xpt_action((union ccb *)&cgd);
if (cgd.ccb_h.status == CAM_REQ_CMP)
callback(enc, elm, path, callback_arg);
xpt_free_path(path);
}
}
/**
@ -1059,6 +1081,10 @@ ses_set_physpath(enc_softc_t *enc, enc_element_t *elm,
ret = EIO;
devid = NULL;
elmpriv = elm->elm_private;
if (elmpriv->addl.hdr == NULL)
goto out;
/*
* Assemble the components of the physical path starting with
* the device ID of the enclosure itself.
@ -1091,7 +1117,6 @@ ses_set_physpath(enc_softc_t *enc, enc_element_t *elm,
scsi_8btou64(idd->identifier), iter->type_index,
iter->type_element_index);
/* Append the element descriptor if one exists */
elmpriv = elm->elm_private;
if (elmpriv->descr != NULL && elmpriv->descr_len > 0) {
sbuf_cat(&sb, "/elmdesc@");
for (i = 0, c = elmpriv->descr; i < elmpriv->descr_len;
@ -1457,9 +1482,10 @@ ses_process_config(enc_softc_t *enc, struct enc_fsm_state *state,
iter.global_element_index, iter.type_index, nelm,
iter.type_element_index);
thdr = ses_cache->ses_types[iter.type_index].hdr;
element->elm_idx = iter.global_element_index;
element->elm_type = thdr->etype_elm_type;
element->subenclosure = thdr->etype_subenc;
element->enctype = thdr->etype_elm_type;
element->overall_status_elem = iter.type_element_index == 0;
element->type_elm_idx = iter.type_element_index;
element->elm_private = malloc(sizeof(ses_element_t),
M_SCSIENC, M_WAITOK|M_ZERO);
ENC_DLOG(enc, "%s: creating elmpriv %d(%d,%d) subenc %d "
@ -1663,6 +1689,8 @@ static int ses_get_elm_addlstatus_fc(enc_softc_t *, enc_cache_t *,
uint8_t *, int);
static int ses_get_elm_addlstatus_sas(enc_softc_t *, enc_cache_t *, uint8_t *,
int, int, int, int);
static int ses_get_elm_addlstatus_ata(enc_softc_t *, enc_cache_t *, uint8_t *,
int, int, int, int);
/**
* \brief Parse the additional status element data for each object.
@ -1818,7 +1846,6 @@ ses_process_elm_addlstatus(enc_softc_t *enc, struct enc_fsm_state *state,
}
}
elmpriv = element->elm_private;
elmpriv->addl.hdr = elm_hdr;
ENC_DLOG(enc, "%s: global element index=%d, type index=%d "
"type element index=%d, offset=0x%x, "
"byte0=0x%x, length=0x%x\n", __func__,
@ -1842,6 +1869,7 @@ ses_process_elm_addlstatus(enc_softc_t *enc, struct enc_fsm_state *state,
offset += elm_hdr->length;
continue;
}
elmpriv->addl.hdr = elm_hdr;
/* Advance to the protocol data, skipping eip bytes if needed */
offset += (eip * SES_EIP_HDR_EXTRA_LEN);
@ -1865,6 +1893,13 @@ ses_process_elm_addlstatus(enc_softc_t *enc, struct enc_fsm_state *state,
eip, iter.type_index,
iter.global_element_index);
break;
case SPSP_PROTO_ATA:
ses_get_elm_addlstatus_ata(enc, enc_cache,
&buf[offset],
proto_info_len,
eip, iter.type_index,
iter.global_element_index);
break;
default:
ENC_VLOG(enc, "Element %d: Unknown Additional Element "
"Protocol 0x%x\n", iter.global_element_index,
@ -2193,18 +2228,16 @@ ses_get_elm_addlstatus_fc(enc_softc_t *enc, enc_cache_t *enc_cache,
}
#define SES_PRINT_PORTS(p, type) do { \
sbuf_printf(sbp, " %s(", type); \
if (((p) & SES_SASOBJ_DEV_PHY_PROTOMASK) == 0) \
sbuf_printf(sbp, " None"); \
else { \
if (((p) & SES_SASOBJ_DEV_PHY_PROTOMASK) != 0) { \
sbuf_printf(sbp, " %s (", type); \
if ((p) & SES_SASOBJ_DEV_PHY_SMP) \
sbuf_printf(sbp, " SMP"); \
if ((p) & SES_SASOBJ_DEV_PHY_STP) \
sbuf_printf(sbp, " STP"); \
if ((p) & SES_SASOBJ_DEV_PHY_SSP) \
sbuf_printf(sbp, " SSP"); \
sbuf_printf(sbp, " )"); \
} \
sbuf_printf(sbp, " )"); \
} while(0)
/**
@ -2214,11 +2247,10 @@ ses_get_elm_addlstatus_fc(enc_softc_t *enc, enc_cache_t *enc_cache,
* \param sesname SES device name associated with the object.
* \param sbp Sbuf to print to.
* \param obj The object to print the data for.
* \param periph_name Peripheral string associated with the object.
*/
static void
ses_print_addl_data_sas_type0(char *sesname, struct sbuf *sbp,
enc_element_t *obj, char *periph_name)
enc_element_t *obj)
{
int i;
ses_element_t *elmpriv;
@ -2227,16 +2259,12 @@ ses_print_addl_data_sas_type0(char *sesname, struct sbuf *sbp,
elmpriv = obj->elm_private;
addl = &(elmpriv->addl);
if (addl->proto_hdr.sas == NULL)
return;
sbuf_printf(sbp, "%s: %s: SAS Device Slot Element:",
sesname, periph_name);
sbuf_printf(sbp, " %d Phys", addl->proto_hdr.sas->base_hdr.num_phys);
sbuf_printf(sbp, ", SAS Slot: %d%s phys",
addl->proto_hdr.sas->base_hdr.num_phys,
ses_elm_sas_type0_not_all_phys(addl->proto_hdr.sas) ? "+" : "");
if (ses_elm_addlstatus_eip(addl->hdr))
sbuf_printf(sbp, " at Slot %d",
sbuf_printf(sbp, " at slot %d",
addl->proto_hdr.sas->type0_eip.dev_slot_num);
if (ses_elm_sas_type0_not_all_phys(addl->proto_hdr.sas))
sbuf_printf(sbp, ", Not All Phys");
sbuf_printf(sbp, "\n");
if (addl->proto_data.sasdev_phys == NULL)
return;
@ -2247,9 +2275,8 @@ ses_print_addl_data_sas_type0(char *sesname, struct sbuf *sbp,
/* Spec says all other fields are specific values */
sbuf_printf(sbp, " SATA device\n");
else {
sbuf_printf(sbp, " SAS device type %d id %d\n",
sbuf_printf(sbp, " SAS device type %d phy %d",
ses_elm_sas_dev_phy_dev_type(phy), phy->phy_id);
sbuf_printf(sbp, "%s: phy %d: protocols:", sesname, i);
SES_PRINT_PORTS(phy->initiator_ports, "Initiator");
SES_PRINT_PORTS(phy->target_ports, "Target");
sbuf_printf(sbp, "\n");
@ -2262,33 +2289,17 @@ ses_print_addl_data_sas_type0(char *sesname, struct sbuf *sbp,
}
#undef SES_PRINT_PORTS
/**
* \brief Report whether a given enclosure object is an expander.
*
* \param enc SES softc associated with object.
* \param obj Enclosure object to report for.
*
* \return 1 if true, 0 otherwise.
*/
static int
ses_obj_is_expander(enc_softc_t *enc, enc_element_t *obj)
{
return (obj->enctype == ELMTYP_SAS_EXP);
}
/**
* \brief Print the additional element status data for this object, for SAS
* type 1 objects. See SES2 r20 Sections 6.1.13.3.3 and 6.1.13.3.4.
*
* \param enc SES enclosure, needed for type identification.
* \param sesname SES device name associated with the object.
* \param sbp Sbuf to print to.
* \param obj The object to print the data for.
* \param periph_name Peripheral string associated with the object.
*/
static void
ses_print_addl_data_sas_type1(enc_softc_t *enc, char *sesname,
struct sbuf *sbp, enc_element_t *obj, char *periph_name)
ses_print_addl_data_sas_type1(char *sesname, struct sbuf *sbp,
enc_element_t *obj)
{
int i, num_phys;
ses_element_t *elmpriv;
@ -2298,12 +2309,10 @@ ses_print_addl_data_sas_type1(enc_softc_t *enc, char *sesname,
elmpriv = obj->elm_private;
addl = &(elmpriv->addl);
if (addl->proto_hdr.sas == NULL)
return;
sbuf_printf(sbp, "%s: %s: SAS ", sesname, periph_name);
if (ses_obj_is_expander(enc, obj)) {
sbuf_printf(sbp, ", SAS ");
if (obj->elm_type == ELMTYP_SAS_EXP) {
num_phys = addl->proto_hdr.sas->base_hdr.num_phys;
sbuf_printf(sbp, "Expander: %d Phys", num_phys);
sbuf_printf(sbp, "Expander: %d phys", num_phys);
if (addl->proto_data.sasexp_phys == NULL)
return;
for (i = 0;i < num_phys;i++) {
@ -2314,7 +2323,7 @@ ses_print_addl_data_sas_type1(enc_softc_t *enc, char *sesname,
}
} else {
num_phys = addl->proto_hdr.sas->base_hdr.num_phys;
sbuf_printf(sbp, "Port: %d Phys", num_phys);
sbuf_printf(sbp, "Port: %d phys", num_phys);
if (addl->proto_data.sasport_phys == NULL)
return;
for (i = 0;i < num_phys;i++) {
@ -2329,6 +2338,24 @@ ses_print_addl_data_sas_type1(enc_softc_t *enc, char *sesname,
}
}
/**
* \brief Print the additional element status data for this object, for
* ATA objects.
*
* \param sbp Sbuf to print to.
* \param obj The object to print the data for.
*/
static void
ses_print_addl_data_ata(struct sbuf *sbp, enc_element_t *obj)
{
ses_element_t *elmpriv = obj->elm_private;
struct ses_addl_status *addl = &elmpriv->addl;
struct ses_elm_ata_hdr *ata = addl->proto_hdr.ata;
sbuf_printf(sbp, ", SATA Slot: scbus%d target %d\n",
scsi_4btoul(ata->bus), scsi_4btoul(ata->target));
}
/**
* \brief Print the additional element status data for this object.
*
@ -2360,27 +2387,45 @@ ses_print_addl_data(enc_softc_t *enc, enc_element_t *obj)
sbuf_printf(&sesname, "%s%d", enc->periph->periph_name,
enc->periph->unit_number);
sbuf_finish(&sesname);
sbuf_printf(&out, "%s: %s in ", sbuf_data(&sesname), sbuf_data(&name));
if (elmpriv->descr != NULL)
sbuf_printf(&out, "%s: %s: Element descriptor: '%s'\n",
sbuf_data(&sesname), sbuf_data(&name), elmpriv->descr);
sbuf_printf(&out, "'%s'", elmpriv->descr);
else {
if (obj->elm_type <= ELMTYP_LAST)
sbuf_cat(&out, elm_type_names[obj->elm_type]);
else
sbuf_printf(&out, "<Type 0x%02x>", obj->elm_type);
sbuf_printf(&out, " %d", obj->type_elm_idx);
if (obj->subenclosure != 0)
sbuf_printf(&out, " of subenc %d", obj->subenclosure);
}
switch(ses_elm_addlstatus_proto(addl->hdr)) {
case SPSP_PROTO_FC:
goto noaddl; /* stubbed for now */
case SPSP_PROTO_SAS:
if (addl->proto_hdr.sas == NULL)
goto noaddl;
switch(ses_elm_sas_descr_type(addl->proto_hdr.sas)) {
case SES_SASOBJ_TYPE_SLOT:
ses_print_addl_data_sas_type0(sbuf_data(&sesname),
&out, obj, sbuf_data(&name));
&out, obj);
break;
case SES_SASOBJ_TYPE_OTHER:
ses_print_addl_data_sas_type1(enc, sbuf_data(&sesname),
&out, obj, sbuf_data(&name));
ses_print_addl_data_sas_type1(sbuf_data(&sesname),
&out, obj);
break;
default:
break;
goto noaddl;
}
break;
case SPSP_PROTO_FC: /* stubbed for now */
case SPSP_PROTO_ATA:
if (addl->proto_hdr.ata == NULL)
goto noaddl;
ses_print_addl_data_ata(&out, obj);
break;
default:
noaddl:
sbuf_cat(&out, "\n");
break;
}
sbuf_finish(&out);
@ -2485,7 +2530,7 @@ ses_get_elm_addlstatus_sas_type1(enc_softc_t *enc, enc_cache_t *enc_cache,
goto out;
/* Process expanders differently from other type1 cases */
if (ses_obj_is_expander(enc, obj)) {
if (obj->elm_type == ELMTYP_SAS_EXP) {
offset += sizeof(struct ses_elm_sas_type1_expander_hdr);
physz = addl->proto_hdr.sas->base_hdr.num_phys *
sizeof(struct ses_elm_sas_expander_phy);
@ -2593,6 +2638,53 @@ ses_get_elm_addlstatus_sas(enc_softc_t *enc, enc_cache_t *enc_cache,
return (err);
}
/**
* \brief Update the softc with the additional element status data for this
* object, for ATA objects.
*
* \param enc SES softc to be updated.
* \param buf The additional element status response buffer.
* \param bufsiz Size of the response buffer.
* \param eip The EIP bit value.
* \param tidx Type index for this object.
* \param nobj Number of objects attached to the SES softc.
*
* \return 0 on success, errno otherwise.
*/
static int
ses_get_elm_addlstatus_ata(enc_softc_t *enc, enc_cache_t *enc_cache,
uint8_t *buf, int bufsiz, int eip, int tidx,
int nobj)
{
int err;
ses_cache_t *ses_cache;
if (bufsiz < sizeof(struct ses_elm_ata_hdr)) {
err = EIO;
goto out;
}
ses_cache = enc_cache->private;
switch(ses_cache->ses_types[tidx].hdr->etype_elm_type) {
case ELMTYP_DEVICE:
case ELMTYP_ARRAY_DEV:
break;
default:
ENC_VLOG(enc, "Element %d has Additional Status, "
"invalid for SES element type 0x%x\n", nobj,
ses_cache->ses_types[tidx].hdr->etype_elm_type);
err = ENODEV;
goto out;
}
((ses_element_t *)enc_cache->elm_map[nobj].elm_private)
->addl.proto_hdr.ata = (struct ses_elm_ata_hdr *)buf;
err = 0;
out:
return (err);
}
static void
ses_softc_invalidate(enc_softc_t *enc)
{

View File

@ -2181,15 +2181,27 @@ struct ses_status_page_hdr {
#define SESCTL_DISABLE 0x20
#define SESCTL_RSTSWAP 0x10
/* Control bits, Device Elements, byte 2 */
#define SESCTL_DRVLCK 0x40 /* "DO NOT REMOVE" */
/* Control bits, Array Device Slot Elements, byte 1 */
#define SESCTL_RQSOK 0x80 /* RQST OK */
#define SESCTL_RQSRSV 0x40 /* RQST RSVD DEVICE */
#define SESCTL_RQSSPR 0x20 /* RQST HOT SPARE */
#define SESCTL_RQSCCH 0x10 /* RQST CONS CHECK */
#define SESCTL_RQSCRA 0x08 /* RQST IN CRIT ARRAY */
#define SESCTL_RQSFAA 0x04 /* RQST IN FAILED ARRAY */
#define SESCTL_RQSRR 0x02 /* RQST REBUI/REMAP */
#define SESCTL_RQSRRA 0x01 /* RQST R/R ABORT */
/* Control bits, [Array] Device Slot Elements, byte 2 */
#define SESCTL_RQSACT 0x80 /* RQST ACTIVE */
#define SESCTL_DRVLCK 0x40 /* DO NOT REMOVE */
#define SESCTL_RQSMSN 0x10 /* RQST MISSING */
#define SESCTL_RQSINS 0x08 /* RQST INSERT */
#define SESCTL_RQSRMV 0x04 /* RQST REMOVE */
#define SESCTL_RQSID 0x02 /* RQST IDENT */
/* Control bits, Device Elements, byte 3 */
/* Control bits, [Array] Device Slot Elements, byte 3 */
#define SESCTL_RQSFLT 0x20 /* RQST FAULT */
#define SESCTL_DEVOFF 0x10 /* DEVICE OFF */
#define SESCTL_ENBYPA 0x08 /* ENABLE BYP A */
#define SESCTL_ENBYPB 0x04 /* ENABLE BYP B */
/* Control bits, Generic, byte 3 */
#define SESCTL_RQSTFAIL 0x40
@ -2399,6 +2411,17 @@ union ses_elm_sas_hdr {
int ses_elm_sas_type0_not_all_phys(union ses_elm_sas_hdr *);
int ses_elm_sas_descr_type(union ses_elm_sas_hdr *);
/*
* This structure for SPSP_PROTO_ATA is not defined by SES specs,
* but purely my own design to make AHCI EM interoperate with SES.
* Since no other software I know can talk to SEMB, and we do not
* expose this this outside, it should be safe to do what we want.
*/
struct ses_elm_ata_hdr {
uint8_t bus[4];
uint8_t target[4];
};
struct ses_elm_addlstatus_base_hdr {
uint8_t byte0;
/*

View File

@ -186,6 +186,7 @@ ahci_attach(device_t dev)
ctlr->ccc = 0;
resource_int_value(device_get_name(dev),
device_get_unit(dev), "ccc", &ctlr->ccc);
mtx_init(&ctlr->ch_mtx, "AHCI channels lock", NULL, MTX_DEF);
/* Setup our own memory management for channels. */
ctlr->sc_iomem.rm_start = rman_get_start(ctlr->r_mem);
@ -379,6 +380,7 @@ ahci_detach(device_t dev)
/* Free memory. */
rman_fini(&ctlr->sc_iomem);
ahci_free_mem(dev);
mtx_destroy(&ctlr->ch_mtx);
return (0);
}
@ -666,6 +668,50 @@ ahci_get_dma_tag(device_t dev, device_t child)
return (ctlr->dma_tag);
}
void
ahci_attached(device_t dev, struct ahci_channel *ch)
{
struct ahci_controller *ctlr = device_get_softc(dev);
mtx_lock(&ctlr->ch_mtx);
ctlr->ch[ch->unit] = ch;
mtx_unlock(&ctlr->ch_mtx);
}
void
ahci_detached(device_t dev, struct ahci_channel *ch)
{
struct ahci_controller *ctlr = device_get_softc(dev);
mtx_lock(&ctlr->ch_mtx);
mtx_lock(&ch->mtx);
ctlr->ch[ch->unit] = NULL;
mtx_unlock(&ch->mtx);
mtx_unlock(&ctlr->ch_mtx);
}
struct ahci_channel *
ahci_getch(device_t dev, int n)
{
struct ahci_controller *ctlr = device_get_softc(dev);
struct ahci_channel *ch;
KASSERT(n >= 0 && n < AHCI_MAX_PORTS, ("Bad channel number %d", n));
mtx_lock(&ctlr->ch_mtx);
ch = ctlr->ch[n];
if (ch != NULL)
mtx_lock(&ch->mtx);
mtx_unlock(&ctlr->ch_mtx);
return (ch);
}
void
ahci_putch(struct ahci_channel *ch)
{
mtx_unlock(&ch->mtx);
}
static int
ahci_ch_probe(device_t dev)
{
@ -824,6 +870,7 @@ ahci_ch_attach(device_t dev)
ahci_ch_pm, ch);
}
mtx_unlock(&ch->mtx);
ahci_attached(device_get_parent(dev), ch);
ctx = device_get_sysctl_ctx(dev);
tree = device_get_sysctl_tree(dev);
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "disable_phy",
@ -849,6 +896,7 @@ ahci_ch_detach(device_t dev)
{
struct ahci_channel *ch = device_get_softc(dev);
ahci_detached(device_get_parent(dev), ch);
mtx_lock(&ch->mtx);
xpt_async(AC_LOST_DEVICE, ch->path, NULL);
/* Forget about reset. */

View File

@ -524,6 +524,8 @@ struct ahci_controller {
} interrupt[AHCI_MAX_PORTS];
void (*ch_start)(struct ahci_channel *);
int dma_coherent; /* DMA is cache-coherent */
struct mtx ch_mtx; /* Lock for attached channels */
struct ahci_channel *ch[AHCI_MAX_PORTS]; /* Attached channels */
};
enum ahci_err_type {
@ -654,5 +656,11 @@ int ahci_ctlr_reset(device_t dev);
int ahci_ctlr_setup(device_t dev);
void ahci_free_mem(device_t dev);
/* Functions to allow AHCI EM to access other channels. */
void ahci_attached(device_t dev, struct ahci_channel *ch);
void ahci_detached(device_t dev, struct ahci_channel *ch);
struct ahci_channel * ahci_getch(device_t dev, int n);
void ahci_putch(struct ahci_channel *ch);
extern devclass_t ahci_devclass;

View File

@ -294,14 +294,14 @@ ahci_em_setleds(device_t dev, int c)
enc = device_get_softc(dev);
val = 0;
if (enc->status[c][2] & 0x80) /* Activity */
if (enc->status[c][2] & SESCTL_RQSACT) /* Activity */
val |= (1 << 0);
if (enc->status[c][2] & SESCTL_RQSID) /* Identification */
if (enc->status[c][1] & SESCTL_RQSRR) /* Rebuild */
val |= (1 << 6) | (1 << 3);
else if (enc->status[c][2] & SESCTL_RQSID) /* Identification */
val |= (1 << 3);
else if (enc->status[c][3] & SESCTL_RQSFLT) /* Fault */
val |= (1 << 6);
else if (enc->status[c][1] & 0x02) /* Rebuild */
val |= (1 << 6) | (1 << 3);
timeout = 10000;
while (ATA_INL(enc->r_memc, 0) & (AHCI_EM_TM | AHCI_EM_RST) &&
@ -366,9 +366,12 @@ static void
ahci_em_emulate_ses_on_led(device_t dev, union ccb *ccb)
{
struct ahci_enclosure *enc;
struct ahci_channel *ch;
struct ses_status_page *page;
struct ses_status_array_dev_slot *ads, *ads0;
struct ses_elm_desc_hdr *elmd;
struct ses_elm_addlstatus_eip_hdr *elma;
struct ses_elm_ata_hdr *elmb;
uint8_t *buf;
int i;
@ -391,7 +394,7 @@ ahci_em_emulate_ses_on_led(device_t dev, union ccb *ccb)
strncpy(&buf[3], device_get_nameunit(dev), 7);
strncpy(&buf[10], "AHCI ", SID_VENDOR_SIZE);
strncpy(&buf[18], "SGPIO Enclosure ", SID_PRODUCT_SIZE);
strncpy(&buf[34], "1.00", SID_REVISION_SIZE);
strncpy(&buf[34], "2.00", SID_REVISION_SIZE);
strncpy(&buf[39], "0001", 4);
strncpy(&buf[43], "S-E-S ", 6);
strncpy(&buf[49], "2.00", 4);
@ -403,14 +406,15 @@ ahci_em_emulate_ses_on_led(device_t dev, union ccb *ccb)
page = (struct ses_status_page *)buf;
if (ccb->ataio.cmd.lba_low == 0x02 &&
ccb->ataio.cmd.features == 0x00 &&
ccb->ataio.cmd.sector_count >= 2) {
ccb->ataio.cmd.sector_count >= 3) {
bzero(buf, ccb->ataio.dxfer_len);
page->hdr.page_code = 0;
scsi_ulto2b(4, page->hdr.length);
buf[4] = 0;
buf[5] = 1;
buf[6] = 2;
buf[7] = 7;
scsi_ulto2b(5, page->hdr.length);
buf[4] = 0x00;
buf[5] = 0x01;
buf[6] = 0x02;
buf[7] = 0x07;
buf[8] = 0x0a;
ccb->ccb_h.status = CAM_REQ_CMP;
goto out;
}
@ -418,26 +422,30 @@ ahci_em_emulate_ses_on_led(device_t dev, union ccb *ccb)
/* SEMB RECEIVE DIAGNOSTIC RESULT (1) */
if (ccb->ataio.cmd.lba_low == 0x02 &&
ccb->ataio.cmd.features == 0x01 &&
ccb->ataio.cmd.sector_count >= 13) {
ccb->ataio.cmd.sector_count >= 16) {
struct ses_enc_desc *ed;
struct ses_elm_type_desc *td;
bzero(buf, ccb->ataio.dxfer_len);
page->hdr.page_code = 0x01;
scsi_ulto2b(4 + 4 + 36 + 4, page->hdr.length);
scsi_ulto2b(4 + sizeof(*ed) + sizeof(*td) + 11,
page->hdr.length);
ed = (struct ses_enc_desc *)&buf[8];
ed->byte0 = 0x11;
ed->subenc_id = 0;
ed->num_types = 1;
ed->length = 36;
ed->logical_id[0] = 0x30; /* NAA Locally Assigned. */
strncpy(&ed->logical_id[1], device_get_nameunit(dev), 7);
strncpy(ed->vendor_id, "AHCI ", SID_VENDOR_SIZE);
strncpy(ed->product_id, "SGPIO Enclosure ", SID_PRODUCT_SIZE);
strncpy(ed->product_rev, " ", SID_REVISION_SIZE);
strncpy(ed->product_rev, "2.00", SID_REVISION_SIZE);
td = (struct ses_elm_type_desc *)ses_enc_desc_next(ed);
td->etype_elm_type = 0x17;
td->etype_maxelt = enc->channels;
td->etype_subenc = 0;
td->etype_txt_len = 0;
td->etype_txt_len = 11;
snprintf((char *)(td + 1), 12, "Drive Slots");
ccb->ccb_h.status = CAM_REQ_CMP;
goto out;
}
@ -453,10 +461,22 @@ ahci_em_emulate_ses_on_led(device_t dev, union ccb *ccb)
for (i = 0; i < enc->channels; i++) {
ads = &page->elements[i + 1].array_dev_slot;
memcpy(ads, enc->status[i], 4);
ads->common.bytes[0] |=
(enc->ichannels & (1 << i)) ?
SES_OBJSTAT_UNKNOWN :
SES_OBJSTAT_NOTINSTALLED;
ch = ahci_getch(device_get_parent(dev), i);
if (ch == NULL) {
ads->common.bytes[0] |= SES_OBJSTAT_UNKNOWN;
continue;
}
if (ch->pm_present)
ads->common.bytes[0] |= SES_OBJSTAT_UNKNOWN;
else if (ch->devices)
ads->common.bytes[0] |= SES_OBJSTAT_OK;
else if (ch->disablephy)
ads->common.bytes[0] |= SES_OBJSTAT_NOTAVAIL;
else
ads->common.bytes[0] |= SES_OBJSTAT_NOTINSTALLED;
if (ch->disablephy)
ads->common.bytes[3] |= SESCTL_DEVOFF;
ahci_putch(ch);
}
ccb->ccb_h.status = CAM_REQ_CMP;
goto out;
@ -471,21 +491,21 @@ ahci_em_emulate_ses_on_led(device_t dev, union ccb *ccb)
ads = &page->elements[i + 1].array_dev_slot;
if (ads->common.bytes[0] & SESCTL_CSEL) {
enc->status[i][0] = 0;
enc->status[i][1] =
ads->bytes[0] & 0x02;
enc->status[i][2] =
ads->bytes[1] & (0x80 | SESCTL_RQSID);
enc->status[i][3] =
ads->bytes[2] & SESCTL_RQSFLT;
enc->status[i][1] = ads->bytes[0] &
SESCTL_RQSRR;
enc->status[i][2] = ads->bytes[1] &
(SESCTL_RQSACT | SESCTL_RQSID);
enc->status[i][3] = ads->bytes[2] &
SESCTL_RQSFLT;
ahci_em_setleds(dev, i);
} else if (ads0->common.bytes[0] & SESCTL_CSEL) {
enc->status[i][0] = 0;
enc->status[i][1] =
ads0->bytes[0] & 0x02;
enc->status[i][2] =
ads0->bytes[1] & (0x80 | SESCTL_RQSID);
enc->status[i][3] =
ads0->bytes[2] & SESCTL_RQSFLT;
enc->status[i][1] = ads0->bytes[0] &
SESCTL_RQSRR;
enc->status[i][2] = ads0->bytes[1] &
(SESCTL_RQSACT | SESCTL_RQSID);
enc->status[i][3] = ads0->bytes[2] &
SESCTL_RQSFLT;
ahci_em_setleds(dev, i);
}
}
@ -496,15 +516,48 @@ ahci_em_emulate_ses_on_led(device_t dev, union ccb *ccb)
/* SEMB RECEIVE DIAGNOSTIC RESULT (7) */
if (ccb->ataio.cmd.lba_low == 0x02 &&
ccb->ataio.cmd.features == 0x07 &&
ccb->ataio.cmd.sector_count >= (3 + 3 * enc->channels)) {
ccb->ataio.cmd.sector_count >= (6 + 3 * enc->channels)) {
bzero(buf, ccb->ataio.dxfer_len);
page->hdr.page_code = 0x07;
scsi_ulto2b(4 + 4 + 12 * enc->channels,
scsi_ulto2b(4 + 15 + 11 * enc->channels, page->hdr.length);
elmd = (struct ses_elm_desc_hdr *)&buf[8];
scsi_ulto2b(11, elmd->length);
snprintf((char *)(elmd + 1), 12, "Drive Slots");
for (i = 0; i < enc->channels; i++) {
elmd = (struct ses_elm_desc_hdr *)&buf[8 + 15 + 11 * i];
scsi_ulto2b(7, elmd->length);
snprintf((char *)(elmd + 1), 8, "Slot %02d", i);
}
ccb->ccb_h.status = CAM_REQ_CMP;
goto out;
}
/* SEMB RECEIVE DIAGNOSTIC RESULT (a) */
if (ccb->ataio.cmd.lba_low == 0x02 &&
ccb->ataio.cmd.features == 0x0a &&
ccb->ataio.cmd.sector_count >= (2 + 3 * enc->channels)) {
bzero(buf, ccb->ataio.dxfer_len);
page->hdr.page_code = 0x0a;
scsi_ulto2b(4 + (sizeof(*elma) + sizeof(*elmb)) * enc->channels,
page->hdr.length);
for (i = 0; i < enc->channels; i++) {
elmd = (struct ses_elm_desc_hdr *)&buf[8 + 4 + 12 * i];
scsi_ulto2b(8, elmd->length);
snprintf((char *)(elmd + 1), 9, "SLOT %03d", i);
elma = (struct ses_elm_addlstatus_eip_hdr *)&buf[
8 + (sizeof(*elma) + sizeof(*elmb)) * i];
elma->base.byte0 = 0x10 | SPSP_PROTO_ATA;
elma->base.length = 2 + sizeof(*elmb);
elma->byte2 = 0x01;
elma->element_index = 1 + i;
ch = ahci_getch(device_get_parent(dev), i);
if (ch == NULL) {
elma->base.byte0 |= 0x80;
continue;
}
if (ch->devices == 0 || ch->pm_present)
elma->base.byte0 |= 0x80;
elmb = (struct ses_elm_ata_hdr *)(elma + 1);
scsi_ulto4b(cam_sim_path(ch->sim), elmb->bus);
scsi_ulto4b(0, elmb->target);
ahci_putch(ch);
}
ccb->ccb_h.status = CAM_REQ_CMP;
goto out;