summaryrefslogtreecommitdiffstats
path: root/sys/cam
diff options
context:
space:
mode:
authorscottl <scottl@FreeBSD.org>2016-04-14 21:55:42 +0000
committerscottl <scottl@FreeBSD.org>2016-04-14 21:55:42 +0000
commit58e70e70a564e31d110f5b052c080cfca3ae9f03 (patch)
tree6ffb7328ecf955ae35b0a8d3fd3f11a161dddf74 /sys/cam
parentc1f7aad42e892ceef31024a3ad2f07b56cbb28b6 (diff)
downloadFreeBSD-src-58e70e70a564e31d110f5b052c080cfca3ae9f03.zip
FreeBSD-src-58e70e70a564e31d110f5b052c080cfca3ae9f03.tar.gz
Add a devctl/devd notification conduit for CAM errors that happen at the
periph level. When a relevant error is reported to the periph, some amplifying information is gathered, and the error and information are fed to devctl with the attributes / keys system=CAM, subsystem=periph. The 'type' key will be either 'error' or 'timeout', and based on this, various other keys are also populated. The purpose of this is to provide a concise mechanism for error reporting that is less noisy than the system console but higher in resolution and fidelity than simple sysctl counters. We will be using it at Netflix to populate a structured log and database to track errors and error trends across our world-wide population of drives. Submitted by: imp, scottl Approved by: kenm MFC after: 3 days Sponsored by: Netflix Differential Revision: D5943
Diffstat (limited to 'sys/cam')
-rw-r--r--sys/cam/cam_periph.c102
1 files changed, 100 insertions, 2 deletions
diff --git a/sys/cam/cam_periph.c b/sys/cam/cam_periph.c
index 91cb45d..299b4ec 100644
--- a/sys/cam/cam_periph.c
+++ b/sys/cam/cam_periph.c
@@ -86,6 +86,7 @@ static int camperiphscsisenseerror(union ccb *ccb,
u_int32_t *timeout,
u_int32_t *action,
const char **action_string);
+static void cam_periph_devctl_notify(union ccb *ccb);
static int nperiph_drivers;
static int initialized = 0;
@@ -1615,7 +1616,7 @@ cam_periph_error(union ccb *ccb, cam_flags camflags,
struct cam_periph *periph;
const char *action_string;
cam_status status;
- int frozen, error, openings;
+ int frozen, error, openings, devctl_err;
u_int32_t action, relsim_flags, timeout;
action = SSQ_PRINT_SENSE;
@@ -1624,9 +1625,26 @@ cam_periph_error(union ccb *ccb, cam_flags camflags,
status = ccb->ccb_h.status;
frozen = (status & CAM_DEV_QFRZN) != 0;
status &= CAM_STATUS_MASK;
- openings = relsim_flags = timeout = 0;
+ devctl_err = openings = relsim_flags = timeout = 0;
orig_ccb = ccb;
+ /* Filter the errors that should be reported via devctl */
+ switch (ccb->ccb_h.status & CAM_STATUS_MASK) {
+ case CAM_CMD_TIMEOUT:
+ case CAM_REQ_ABORTED:
+ case CAM_REQ_CMP_ERR:
+ case CAM_REQ_TERMIO:
+ case CAM_UNREC_HBA_ERROR:
+ case CAM_DATA_RUN_ERR:
+ case CAM_SCSI_STATUS_ERROR:
+ case CAM_ATA_STATUS_ERROR:
+ case CAM_SMP_STATUS_ERROR:
+ devctl_err++;
+ break;
+ default:
+ break;
+ }
+
switch (status) {
case CAM_REQ_CMP:
error = 0;
@@ -1754,6 +1772,9 @@ cam_periph_error(union ccb *ccb, cam_flags camflags,
xpt_print(ccb->ccb_h.path, "Retrying command\n");
}
+ if (devctl_err)
+ cam_periph_devctl_notify(orig_ccb);
+
if ((action & SSQ_LOST) != 0) {
lun_id_t lun_id;
@@ -1824,3 +1845,80 @@ cam_periph_error(union ccb *ccb, cam_flags camflags,
return (error);
}
+
+#define CAM_PERIPH_DEVD_MSG_SIZE 256
+
+static void
+cam_periph_devctl_notify(union ccb *ccb)
+{
+ struct cam_periph *periph;
+ struct ccb_getdev *cgd;
+ struct sbuf sb;
+ int serr, sk, asc, ascq;
+ char *sbmsg, *type;
+
+ sbmsg = malloc(CAM_PERIPH_DEVD_MSG_SIZE, M_CAMPERIPH, M_NOWAIT);
+ if (sbmsg == NULL)
+ return;
+
+ sbuf_new(&sb, sbmsg, CAM_PERIPH_DEVD_MSG_SIZE, SBUF_FIXEDLEN);
+
+ periph = xpt_path_periph(ccb->ccb_h.path);
+ sbuf_printf(&sb, "device=%s%d ", periph->periph_name,
+ periph->unit_number);
+
+ sbuf_printf(&sb, "serial=\"");
+ if ((cgd = (struct ccb_getdev *)xpt_alloc_ccb_nowait()) != NULL) {
+ xpt_setup_ccb(&cgd->ccb_h, ccb->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)
+ sbuf_bcat(&sb, cgd->serial_num, cgd->serial_num_len);
+ }
+ sbuf_printf(&sb, "\" ");
+ sbuf_printf(&sb, "cam_status=\"0x%x\" ", ccb->ccb_h.status);
+
+ switch (ccb->ccb_h.status & CAM_STATUS_MASK) {
+ case CAM_CMD_TIMEOUT:
+ sbuf_printf(&sb, "timeout=%d ", ccb->ccb_h.timeout);
+ type = "timeout";
+ break;
+ case CAM_SCSI_STATUS_ERROR:
+ sbuf_printf(&sb, "scsi_status=%d ", ccb->csio.scsi_status);
+ if (scsi_extract_sense_ccb(ccb, &serr, &sk, &asc, &ascq))
+ sbuf_printf(&sb, "scsi_sense=\"%02x %02x %02x %02x\" ",
+ serr, sk, asc, ascq);
+ type = "error";
+ break;
+ case CAM_ATA_STATUS_ERROR:
+ sbuf_printf(&sb, "RES=\"");
+ ata_res_sbuf(&ccb->ataio.res, &sb);
+ sbuf_printf(&sb, "\" ");
+ type = "error";
+ break;
+ default:
+ type = "error";
+ break;
+ }
+
+ if (ccb->ccb_h.func_code == XPT_SCSI_IO) {
+ sbuf_printf(&sb, "CDB=\"");
+ if ((ccb->ccb_h.flags & CAM_CDB_POINTER) != 0)
+ scsi_cdb_sbuf(ccb->csio.cdb_io.cdb_ptr, &sb);
+ else
+ scsi_cdb_sbuf(ccb->csio.cdb_io.cdb_bytes, &sb);
+ sbuf_printf(&sb, "\" ");
+ } else if (ccb->ccb_h.func_code == XPT_ATA_IO) {
+ sbuf_printf(&sb, "ACB=\"");
+ ata_cmd_sbuf(&ccb->ataio.cmd, &sb);
+ sbuf_printf(&sb, "\" ");
+ }
+
+ if (sbuf_finish(&sb) == 0)
+ devctl_notify("CAM", "periph", type, sbuf_data(&sb));
+ sbuf_delete(&sb);
+ free(sbmsg, M_CAMPERIPH);
+}
+
OpenPOWER on IntegriCloud