From 2f2e8d68346597074ad2d1b05d301171d3301119 Mon Sep 17 00:00:00 2001 From: "Justin T. Gibbs" Date: Mon, 18 Jul 2011 18:56:50 +0000 Subject: [PATCH 01/89] sys/cam/scsi/scsi_all.h: o Apply const qualifiers to in scsi_(2,3,4,8)btoul(). o While touching these APIs, also convert them to use stdint.h types. --- sys/cam/scsi/scsi_all.h | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/sys/cam/scsi/scsi_all.h b/sys/cam/scsi/scsi_all.h index 93b11d5a680..25442f1484a 100644 --- a/sys/cam/scsi/scsi_all.h +++ b/sys/cam/scsi/scsi_all.h @@ -1504,11 +1504,11 @@ static __inline void scsi_ulto2b(u_int32_t val, u_int8_t *bytes); static __inline void scsi_ulto3b(u_int32_t val, u_int8_t *bytes); static __inline void scsi_ulto4b(u_int32_t val, u_int8_t *bytes); static __inline void scsi_u64to8b(u_int64_t val, u_int8_t *bytes); -static __inline u_int32_t scsi_2btoul(u_int8_t *bytes); -static __inline u_int32_t scsi_3btoul(u_int8_t *bytes); -static __inline int32_t scsi_3btol(u_int8_t *bytes); -static __inline u_int32_t scsi_4btoul(u_int8_t *bytes); -static __inline u_int64_t scsi_8btou64(u_int8_t *bytes); +static __inline uint32_t scsi_2btoul(const uint8_t *bytes); +static __inline uint32_t scsi_3btoul(const uint8_t *bytes); +static __inline int32_t scsi_3btol(const uint8_t *bytes); +static __inline uint32_t scsi_4btoul(const uint8_t *bytes); +static __inline uint64_t scsi_8btou64(const uint8_t *bytes); static __inline void *find_mode_page_6(struct scsi_mode_header_6 *mode_header); static __inline void *find_mode_page_10(struct scsi_mode_header_10 *mode_header); @@ -1563,20 +1563,20 @@ scsi_u64to8b(u_int64_t val, u_int8_t *bytes) bytes[7] = val & 0xff; } -static __inline u_int32_t -scsi_2btoul(u_int8_t *bytes) +static __inline uint32_t +scsi_2btoul(const uint8_t *bytes) { - u_int32_t rv; + uint32_t rv; rv = (bytes[0] << 8) | bytes[1]; return (rv); } -static __inline u_int32_t -scsi_3btoul(u_int8_t *bytes) +static __inline uint32_t +scsi_3btoul(const uint8_t *bytes) { - u_int32_t rv; + uint32_t rv; rv = (bytes[0] << 16) | (bytes[1] << 8) | @@ -1585,9 +1585,9 @@ scsi_3btoul(u_int8_t *bytes) } static __inline int32_t -scsi_3btol(u_int8_t *bytes) +scsi_3btol(const uint8_t *bytes) { - u_int32_t rc = scsi_3btoul(bytes); + uint32_t rc = scsi_3btoul(bytes); if (rc & 0x00800000) rc |= 0xff000000; @@ -1595,10 +1595,10 @@ scsi_3btol(u_int8_t *bytes) return (int32_t) rc; } -static __inline u_int32_t -scsi_4btoul(u_int8_t *bytes) +static __inline uint32_t +scsi_4btoul(const uint8_t *bytes) { - u_int32_t rv; + uint32_t rv; rv = (bytes[0] << 24) | (bytes[1] << 16) | @@ -1608,7 +1608,7 @@ scsi_4btoul(u_int8_t *bytes) } static __inline uint64_t -scsi_8btou64(uint8_t *bytes) +scsi_8btou64(const uint8_t *bytes) { uint64_t rv; From 01acaa308bc94c818b40c43f6052a0e7c44aae7e Mon Sep 17 00:00:00 2001 From: "Justin T. Gibbs" Date: Mon, 18 Jul 2011 19:26:16 +0000 Subject: [PATCH 02/89] Revamp the CAM enclosure services driver, renaming it to "enc" from "ses" in the process. This updated driver uses an in-kernel daemon to track state changes and publishes physical path location information for disk elements into the CAM device database. share/examples/ses/Makefile.inc: share/examples/ses/srcs/eltsub.c: share/examples/ses/srcs/sesd.c: share/examples/ses/srcs/getencstat.c: share/examples/ses/srcs/setobjstat.c: share/examples/ses/srcs/inienc.c: share/examples/ses/srcs/getobjstat.c: share/examples/ses/srcs/getnobj.c: share/examples/ses/srcs/getobjmap.c: share/examples/ses/srcs/setencstat.c: Update for changes in driver name and API. The ioctl interface is largely unchanged and could use additional refinement. It would be nice to be able to fetch the status of all elements in a single ioctl call and to have the ioctls that return variable length data allow you to query the necessary allocation size by passing in a zero length buffer. sys/sparc64/conf/GENERIC: sys/ia64/conf/GENERIC: sys/mips/conf/OCTEON1: sys/pc98/conf/GENERIC: sys/i386/conf/GENERIC: sys/amd64/conf/GENERIC: ses -> enc sys/conf/files: sys/modules/cam/Makefile: sys/cam/scsi/scsi_enc_internal.h sys/cam/scsi/scsi_enc_ses.c sys/cam/scsi/scsi_enc_safte.c sys/cam/scsi/scsi_ses.c sys/cam/scsi/scsi_enc.c Split the enc driver into a generic driver file and one file each for the SES and SAF-TE personalities. sys/cam/scsi/scsi_ses.h: o Retain this header, but use it to only hold structures derived from the T10 SES spec. The driver interface can be found in scsi_enc.h. o Add definitions for most SES pages. sys/cam/scsi/scsi_enc.c sys/cam/scsi/scsi_enc.h o Use a function vector table to allow interaction between the generic and protocol specific portions of this driver. o Provide a generic mechanism allowing personalities to define a finite state machine that is executed from a daemon thread context. o Track CAM device arrival events and pass these on to personalities that have registered an interest in them. These notifications are used to trigger physical path updates in the CAM EDT. sys/cam/scsi/scsi_enc_safte.c: The SAF-TE personality. This module is largely untouched by this update. To achieve the same level of support as we have for SES, it will need to define an FSM and code to determine the physical paths of elements within the enclosure. sys/cam/scsi/scsi_enc_ses.c: o Implement a state machine to fetch configuration, status, element descriptors, and additional element status. o Build a "element map" that indexes into the config and status data retrieved from a SES device. Use this to simplify our responses to ioctls. o Add support for using SAS domain/phy WWN data to determine the physical path (ence@/type@/slot@) of an element. Stubs are in place for FC, but both FC and SPI will need additional work in order to be supported. Sponsored by: Spectra Logic Corporation Submitted by: gibbs, will --- share/examples/ses/Makefile.inc | 1 - share/examples/ses/srcs/eltsub.c | 62 +- share/examples/ses/srcs/getencstat.c | 84 +- share/examples/ses/srcs/getnobj.c | 5 +- share/examples/ses/srcs/getobjmap.c | 5 +- share/examples/ses/srcs/getobjstat.c | 5 +- share/examples/ses/srcs/inienc.c | 5 +- share/examples/ses/srcs/sesd.c | 17 +- share/examples/ses/srcs/setencstat.c | 13 +- share/examples/ses/srcs/setobjstat.c | 13 +- sys/amd64/conf/GENERIC | 2 +- sys/cam/scsi/scsi_enc.c | 975 ++++++++++ sys/cam/scsi/scsi_enc.h | 219 +++ sys/cam/scsi/scsi_enc_internal.h | 226 +++ sys/cam/scsi/scsi_enc_safte.c | 1041 ++++++++++ sys/cam/scsi/scsi_enc_ses.c | 2703 ++++++++++++++++++++++++++ sys/cam/scsi/scsi_ses.c | 2535 ------------------------ sys/cam/scsi/scsi_ses.h | 2448 ++++++++++++++++++++++- sys/conf/files | 4 +- sys/i386/conf/GENERIC | 2 +- sys/ia64/conf/GENERIC | 2 +- sys/mips/conf/OCTEON1 | 2 +- sys/modules/cam/Makefile | 6 +- sys/pc98/conf/GENERIC | 2 +- sys/sparc64/conf/GENERIC | 2 +- 25 files changed, 7665 insertions(+), 2714 deletions(-) create mode 100644 sys/cam/scsi/scsi_enc.c create mode 100644 sys/cam/scsi/scsi_enc.h create mode 100644 sys/cam/scsi/scsi_enc_internal.h create mode 100644 sys/cam/scsi/scsi_enc_safte.c create mode 100644 sys/cam/scsi/scsi_enc_ses.c delete mode 100644 sys/cam/scsi/scsi_ses.c diff --git a/share/examples/ses/Makefile.inc b/share/examples/ses/Makefile.inc index 5ff1cd43048..2b1e85874d1 100644 --- a/share/examples/ses/Makefile.inc +++ b/share/examples/ses/Makefile.inc @@ -32,7 +32,6 @@ # mjacob@feral.com # -CFLAGS+= -I/usr/include/cam/scsi -DSESINC="" BINDIR?= /usr/sbin CLEANFILES+= ${MAN} diff --git a/share/examples/ses/srcs/eltsub.c b/share/examples/ses/srcs/eltsub.c index 8500fff041a..67f49a74c95 100644 --- a/share/examples/ses/srcs/eltsub.c +++ b/share/examples/ses/srcs/eltsub.c @@ -33,10 +33,13 @@ */ #include +#include +#include #include #include #include -#include SESINC +#include +#include #include "eltsub.h" @@ -46,80 +49,83 @@ geteltnm(int type) static char rbuf[132]; switch (type) { - case SESTYP_UNSPECIFIED: + case ELMTYP_UNSPECIFIED: sprintf(rbuf, "Unspecified"); break; - case SESTYP_DEVICE: + case ELMTYP_DEVICE: sprintf(rbuf, "Device"); break; - case SESTYP_POWER: + case ELMTYP_POWER: sprintf(rbuf, "Power supply"); break; - case SESTYP_FAN: + case ELMTYP_FAN: sprintf(rbuf, "Cooling element"); break; - case SESTYP_THERM: + case ELMTYP_THERM: sprintf(rbuf, "Temperature sensors"); break; - case SESTYP_DOORLOCK: + case ELMTYP_DOORLOCK: sprintf(rbuf, "Door Lock"); break; - case SESTYP_ALARM: + case ELMTYP_ALARM: sprintf(rbuf, "Audible alarm"); break; - case SESTYP_ESCC: + case ELMTYP_ESCC: sprintf(rbuf, "Enclosure services controller electronics"); break; - case SESTYP_SCC: + case ELMTYP_SCC: sprintf(rbuf, "SCC controller electronics"); break; - case SESTYP_NVRAM: + case ELMTYP_NVRAM: sprintf(rbuf, "Nonvolatile cache"); break; - case SESTYP_UPS: + case ELMTYP_INV_OP_REASON: + sprintf(rbuf, "Invalid Operation Reason"); + break; + case ELMTYP_UPS: sprintf(rbuf, "Uninterruptible power supply"); break; - case SESTYP_DISPLAY: + case ELMTYP_DISPLAY: sprintf(rbuf, "Display"); break; - case SESTYP_KEYPAD: + case ELMTYP_KEYPAD: sprintf(rbuf, "Key pad entry device"); break; - case SESTYP_ENCLOSURE: + case ELMTYP_ENCLOSURE: sprintf(rbuf, "Enclosure"); break; - case SESTYP_SCSIXVR: + case ELMTYP_SCSIXVR: sprintf(rbuf, "SCSI port/transceiver"); break; - case SESTYP_LANGUAGE: + case ELMTYP_LANGUAGE: sprintf(rbuf, "Language"); break; - case SESTYP_COMPORT: + case ELMTYP_COMPORT: sprintf(rbuf, "Communication Port"); break; - case SESTYP_VOM: + case ELMTYP_VOM: sprintf(rbuf, "Voltage Sensor"); break; - case SESTYP_AMMETER: + case ELMTYP_AMMETER: sprintf(rbuf, "Current Sensor"); break; - case SESTYP_SCSI_TGT: + case ELMTYP_SCSI_TGT: sprintf(rbuf, "SCSI target port"); break; - case SESTYP_SCSI_INI: + case ELMTYP_SCSI_INI: sprintf(rbuf, "SCSI initiator port"); break; - case SESTYP_SUBENC: + case ELMTYP_SUBENC: sprintf(rbuf, "Simple sub-enclosure"); break; - case SESTYP_ARRAY: + case ELMTYP_ARRAY_DEV: sprintf(rbuf, "Array device"); break; - case SESTYP_SASEXPANDER: - sprintf(rbuf, "SAS Expander"); + case ELMTYP_SAS_EXP: + sprintf(rbuf, "SAS expander"); break; - case SESTYP_SASCONNECTOR: - sprintf(rbuf, "SAS Connector"); + case ELMTYP_SAS_CONN: + sprintf(rbuf, "SAS connector"); break; default: (void) sprintf(rbuf, "", type); diff --git a/share/examples/ses/srcs/getencstat.c b/share/examples/ses/srcs/getencstat.c index 3514fe4e77c..1a70eb16360 100644 --- a/share/examples/ses/srcs/getencstat.c +++ b/share/examples/ses/srcs/getencstat.c @@ -33,20 +33,25 @@ */ #include +#include +#include #include #include #include #include #include -#include SESINC +#include +#include #include "eltsub.h" int main(int a, char **v) { - ses_object *objp; - ses_objstat ob; + encioc_element_t *objp; + encioc_elm_status_t ob; + encioc_elm_desc_t objd; + encioc_elm_devnames_t objdn; int fd, nobj, f, i, verbose, quiet, errors; u_char estat; @@ -73,13 +78,13 @@ main(int a, char **v) perror(*v); continue; } - if (ioctl(fd, SESIOC_GETNOBJ, (caddr_t) &nobj) < 0) { - perror("SESIOC_GETNOBJ"); + if (ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj) < 0) { + perror("ENCIOC_GETNELM"); (void) close(fd); continue; } - if (ioctl(fd, SESIOC_GETENCSTAT, (caddr_t) &estat) < 0) { - perror("SESIOC_GETENCSTAT"); + if (ioctl(fd, ENCIOC_GETENCSTAT, (caddr_t) &estat) < 0) { + perror("ENCIOC_GETENCSTAT"); (void) close(fd); continue; } @@ -113,38 +118,67 @@ main(int a, char **v) } } fprintf(stdout, ">\n"); - objp = calloc(nobj, sizeof (ses_object)); + objp = calloc(nobj, sizeof (encioc_element_t)); if (objp == NULL) { perror("calloc"); (void) close(fd); continue; } - if (ioctl(fd, SESIOC_GETOBJMAP, (caddr_t) objp) < 0) { - perror("SESIOC_GETOBJMAP"); + if (ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) objp) < 0) { + perror("ENCIOC_GETELMMAP"); (void) close(fd); continue; } for (i = 0; i < nobj; i++) { - ob.obj_id = objp[i].obj_id; - if (ioctl(fd, SESIOC_GETOBJSTAT, (caddr_t) &ob) < 0) { - perror("SESIOC_GETOBJSTAT"); + ob.elm_idx = objp[i].elm_idx; + if (ioctl(fd, ENCIOC_GETELMSTAT, (caddr_t) &ob) < 0) { + perror("ENCIOC_GETELMSTAT"); (void) close(fd); break; } - if ((ob.cstat[0] & 0xf) == SES_OBJSTAT_OK) { - if (verbose) { - fprintf(stdout, - "Element 0x%x: %s OK (%s)\n", - ob.obj_id, - geteltnm(objp[i].object_type), - stat2ascii(objp[i].object_type, - ob.cstat)); - } + bzero(&objd, sizeof(objd)); + objd.elm_idx = objp[i].elm_idx; + objd.elm_desc_len = UINT16_MAX; + objd.elm_desc_str = calloc(UINT16_MAX, sizeof(char)); + if (objd.elm_desc_str == NULL) { + perror("calloc"); + (void) close(fd); continue; } - fprintf(stdout, "Element 0x%x: %s, %s\n", - ob.obj_id, geteltnm(objp[i].object_type), - stat2ascii(objp[i].object_type, ob.cstat)); + if (ioctl(fd, ENCIOC_GETELMDESC, (caddr_t)&objd) < 0) { + perror("ENCIOC_GETELMDESC"); + (void) close(fd); + break; + } + bzero(&objdn, sizeof(objdn)); + objdn.elm_idx = objp[i].elm_idx; + objdn.elm_names_size = 128; + objdn.elm_devnames = calloc(128, sizeof(char)); + if (objdn.elm_devnames == NULL) { + perror("calloc"); + (void) close(fd); + break; + } + /* + * This ioctl isn't critical and has a good chance + * of returning -1. + */ + (void)ioctl(fd, ENCIOC_GETELMDEVNAMES, (caddr_t)&objdn); + fprintf(stdout, "Element 0x%x: %s", ob.elm_idx, + geteltnm(objp[i].elm_type)); + if ((ob.cstat[0] & 0xf) == SES_OBJSTAT_OK) + fprintf(stdout, ", OK (%s)", + stat2ascii(objp[i].elm_type, ob.cstat)); + else + fprintf(stdout, ", %s", + stat2ascii(objp[i].elm_type, ob.cstat)); + fprintf(stdout, ", descriptor: '%s'", + objd.elm_desc_str); + if (objdn.elm_names_len > 0) + fprintf(stdout, ", dev: '%s'", + objdn.elm_devnames); + fprintf(stdout, "\n"); + free(objdn.elm_devnames); } free(objp); (void) close(fd); diff --git a/share/examples/ses/srcs/getnobj.c b/share/examples/ses/srcs/getnobj.c index 17a26c62bd9..92d3458dec6 100644 --- a/share/examples/ses/srcs/getnobj.c +++ b/share/examples/ses/srcs/getnobj.c @@ -33,12 +33,15 @@ */ #include +#include +#include #include #include #include #include #include -#include SESINC +#include +#include int main(int argc, char **argv) diff --git a/share/examples/ses/srcs/getobjmap.c b/share/examples/ses/srcs/getobjmap.c index 9798b4c87ba..fbcc12e63f3 100644 --- a/share/examples/ses/srcs/getobjmap.c +++ b/share/examples/ses/srcs/getobjmap.c @@ -33,11 +33,14 @@ */ #include +#include +#include #include #include #include #include -#include SESINC +#include +#include #include "eltsub.h" diff --git a/share/examples/ses/srcs/getobjstat.c b/share/examples/ses/srcs/getobjstat.c index 99fb1854238..d49f6f97d2f 100644 --- a/share/examples/ses/srcs/getobjstat.c +++ b/share/examples/ses/srcs/getobjstat.c @@ -32,11 +32,14 @@ * mjacob@feral.com */ #include +#include +#include #include #include #include #include -#include SESINC +#include +#include int main(int a, char **v) diff --git a/share/examples/ses/srcs/inienc.c b/share/examples/ses/srcs/inienc.c index 7d6cc220b51..f418787b303 100644 --- a/share/examples/ses/srcs/inienc.c +++ b/share/examples/ses/srcs/inienc.c @@ -33,11 +33,14 @@ */ #include +#include +#include #include #include #include #include -#include SESINC +#include +#include int main(int a, char **v) diff --git a/share/examples/ses/srcs/sesd.c b/share/examples/ses/srcs/sesd.c index 0793077d210..88627e9ad5a 100644 --- a/share/examples/ses/srcs/sesd.c +++ b/share/examples/ses/srcs/sesd.c @@ -32,6 +32,8 @@ * mjacob@feral.com */ #include +#include +#include #include #include #include @@ -39,7 +41,8 @@ #include #include #include -#include SESINC +#include +#include #define ALLSTAT (SES_ENCSTAT_UNRECOV | SES_ENCSTAT_CRITICAL | \ SES_ENCSTAT_NONCRITICAL | SES_ENCSTAT_INFO) @@ -54,7 +57,7 @@ main(int a, char **v) static const char *usage = "usage: %s [ -d ] [ -t pollinterval ] device [ device ]\n"; int fd, polltime, dev, devbase, nodaemon; - ses_encstat stat, *carray; + encioc_enc_status_t stat, *carray; if (a < 2) { fprintf(stderr, usage, *v); @@ -83,7 +86,7 @@ main(int a, char **v) return (1); } for (dev = devbase; dev < a; dev++) - carray[dev] = (ses_encstat) -1; + carray[dev] = (encioc_enc_status_t) -1; /* * Check to make sure we can open all devices @@ -94,8 +97,8 @@ main(int a, char **v) perror(v[dev]); return (1); } - if (ioctl(fd, SESIOC_INIT, NULL) < 0) { - fprintf(stderr, "%s: SESIOC_INIT fails- %s\n", + if (ioctl(fd, ENCIOC_INIT, NULL) < 0) { + fprintf(stderr, "%s: ENCIOC_INIT fails- %s\n", v[dev], strerror(errno)); return (1); } @@ -122,9 +125,9 @@ main(int a, char **v) /* * Get the actual current enclosure status. */ - if (ioctl(fd, SESIOC_GETENCSTAT, (caddr_t) &stat) < 0) { + if (ioctl(fd, ENCIOC_GETENCSTAT, (caddr_t) &stat) < 0) { syslog(LOG_ERR, - "%s: SESIOC_GETENCSTAT- %m", v[dev]); + "%s: ENCIOC_GETENCSTAT- %m", v[dev]); (void) close(fd); continue; } diff --git a/share/examples/ses/srcs/setencstat.c b/share/examples/ses/srcs/setencstat.c index 127f68fa4cf..87a7fa26304 100644 --- a/share/examples/ses/srcs/setencstat.c +++ b/share/examples/ses/srcs/setencstat.c @@ -33,18 +33,21 @@ */ #include +#include +#include #include #include #include #include -#include SESINC +#include +#include int main(int a, char **v) { int fd; long val; - ses_encstat stat; + encioc_enc_status_t stat; if (a != 3) { fprintf(stderr, "usage: %s device enclosure_status\n", *v); @@ -57,9 +60,9 @@ main(int a, char **v) } val = strtol(v[2], NULL, 0); - stat = (ses_encstat) val; - if (ioctl(fd, SESIOC_SETENCSTAT, (caddr_t) &stat) < 0) { - perror("SESIOC_SETENCSTAT"); + stat = (encioc_enc_status_t)val; + if (ioctl(fd, ENCIOC_SETENCSTAT, (caddr_t) &stat) < 0) { + perror("ENCIOC_SETENCSTAT"); } (void) close(fd); return (0); diff --git a/share/examples/ses/srcs/setobjstat.c b/share/examples/ses/srcs/setobjstat.c index 08fdb7b4822..26a5dd19ebc 100644 --- a/share/examples/ses/srcs/setobjstat.c +++ b/share/examples/ses/srcs/setobjstat.c @@ -33,18 +33,21 @@ */ #include +#include +#include #include #include #include #include -#include SESINC +#include +#include int main(int a, char **v) { int fd; int i; - ses_objstat obj; + encioc_elm_status_t obj; long cvt; char *x; @@ -64,7 +67,7 @@ usage: if (x == v[2]) { goto usage; } - obj.obj_id = cvt; + obj.elm_idx = cvt; for (i = 0; i < 4; i++) { x = v[3 + i]; cvt = strtol(v[3 + i], &x, 0); @@ -73,8 +76,8 @@ usage: } obj.cstat[i] = cvt; } - if (ioctl(fd, SESIOC_SETOBJSTAT, (caddr_t) &obj) < 0) { - perror("SESIOC_SETOBJSTAT"); + if (ioctl(fd, ENCIOC_SETELMSTAT, (caddr_t) &obj) < 0) { + perror("ENCIOC_SETELMSTAT"); } (void) close(fd); return (0); diff --git a/sys/amd64/conf/GENERIC b/sys/amd64/conf/GENERIC index 4bfcfa328ce..2cea4ef44ba 100644 --- a/sys/amd64/conf/GENERIC +++ b/sys/amd64/conf/GENERIC @@ -126,7 +126,7 @@ device da # Direct Access (disks) device sa # Sequential Access (tape etc) device cd # CD device pass # Passthrough device (direct ATA/SCSI access) -device ses # SCSI Environmental Services (and SAF-TE) +device enc # Enclosure Services (SES and SAF-TE) # RAID controllers interfaced to the SCSI subsystem device amr # AMI MegaRAID diff --git a/sys/cam/scsi/scsi_enc.c b/sys/cam/scsi/scsi_enc.c new file mode 100644 index 00000000000..993a344c8c5 --- /dev/null +++ b/sys/cam/scsi/scsi_enc.c @@ -0,0 +1,975 @@ +/*- + * Copyright (c) 2000 Matthew Jacob + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +MALLOC_DEFINE(M_SCSIENC, "SCSI ENC", "SCSI ENC buffers"); + +/* Enclosure type independent driver */ + +#define SEN_ID "UNISYS SUN_SEN" +#define SEN_ID_LEN 24 + +static d_open_t enc_open; +static d_close_t enc_close; +static d_ioctl_t enc_ioctl; +static periph_init_t enc_init; +static periph_ctor_t enc_ctor; +static periph_oninv_t enc_oninvalidate; +static periph_dtor_t enc_dtor; +static periph_start_t enc_start; + +static void enc_async(void *, uint32_t, struct cam_path *, void *); +static enctyp enc_type(void *, int); + +static struct periph_driver encdriver = { + enc_init, "enc", + TAILQ_HEAD_INITIALIZER(encdriver.units), /* generation */ 0 +}; + +PERIPHDRIVER_DECLARE(enc, encdriver); + +static struct cdevsw enc_cdevsw = { + .d_version = D_VERSION, + .d_open = enc_open, + .d_close = enc_close, + .d_ioctl = enc_ioctl, + .d_name = "enc", + .d_flags = 0, +}; + +static void +enc_init(void) +{ + cam_status status; + + /* + * Install a global async callback. This callback will + * receive async callbacks like "new device found". + */ + status = xpt_register_async(AC_FOUND_DEVICE, enc_async, NULL, NULL); + + if (status != CAM_REQ_CMP) { + printf("enc: Failed to attach master async callback " + "due to status 0x%x!\n", status); + } +} + +static void +enc_oninvalidate(struct cam_periph *periph) +{ + struct enc_softc *enc; + + enc = periph->softc; + + /* If the sub-driver has an invalidate routine, call it */ + if (enc->enc_vec.softc_invalidate != NULL) + enc->enc_vec.softc_invalidate(periph); + + /* + * Unregister any async callbacks. + */ + xpt_register_async(0, enc_async, periph, periph->path); + + /* + * Shutdown our daemon. + */ + enc->enc_flags |= ENC_FLAG_SHUTDOWN; + if (enc->enc_daemon != NULL) { + /* Signal and wait for the ses daemon to terminate. */ + wakeup(enc->enc_daemon); + /* + * We're called with the SIM mutex held, but we're dropping + * the update mutex here on sleep. So we have to manually + * drop the SIM mutex. + */ + cam_periph_sleep(enc->periph, enc->enc_daemon, + PUSER, "thtrm", 0); + } + callout_drain(&enc->status_updater); + + enc->enc_flags |= ENC_FLAG_INVALID; + + xpt_print(periph->path, "lost device\n"); +} + +static void +enc_dtor(struct cam_periph *periph) +{ + struct enc_softc *enc; + + enc = periph->softc; + + xpt_print(periph->path, "removing device entry\n"); + cam_periph_unlock(periph); + destroy_dev(enc->enc_dev); + cam_periph_lock(periph); + + /* If the sub-driver has a cleanup routine, call it */ + if (enc->enc_vec.softc_cleanup != NULL) + enc->enc_vec.softc_cleanup(periph); + + if (enc->enc_boot_hold_ch.ich_func != NULL) { + config_intrhook_disestablish(&enc->enc_boot_hold_ch); + enc->enc_boot_hold_ch.ich_func = NULL; + } + + ENC_FREE(enc); +} + +static void +enc_async(void *callback_arg, uint32_t code, struct cam_path *path, void *arg) +{ + struct cam_periph *periph; + + periph = (struct cam_periph *)callback_arg; + + switch(code) { + case AC_FOUND_DEVICE: + { + struct ccb_getdev *cgd; + cam_status status; + int inq_len; + path_id_t path_id; + + cgd = (struct ccb_getdev *)arg; + if (arg == NULL) { + break; + } + + if (cgd->protocol != PROTO_SCSI) + break; + + inq_len = cgd->inq_data.additional_length + 4; + + /* + * PROBLEM: WE NEED TO LOOK AT BYTES 48-53 TO SEE IF THIS + * PROBLEM: IS A SAF-TE DEVICE. + */ + switch (enc_type(&cgd->inq_data, inq_len)) { + case ENC_SES: + case ENC_SES_SCSI2: + case ENC_SES_PASSTHROUGH: + case ENC_SEN: + case ENC_SAFT: + break; + default: + /* + * Schedule announcement of the ENC bindings for + * this device if it is managed by a SEP. + */ + path_id = xpt_path_path_id(path); + xpt_lock_buses(); + TAILQ_FOREACH(periph, &encdriver.units, unit_links) { + 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) + continue; + + softc->enc_vec.device_found(softc); + } + xpt_unlock_buses(); + return; + } + + status = cam_periph_alloc(enc_ctor, enc_oninvalidate, + enc_dtor, enc_start, "enc", CAM_PERIPH_BIO, + cgd->ccb_h.path, enc_async, AC_FOUND_DEVICE, cgd); + + if (status != CAM_REQ_CMP && status != CAM_REQ_INPROG) { + printf("enc_async: Unable to probe new device due to " + "status 0x%x\n", status); + } + break; + } + default: + cam_periph_async(periph, code, path, arg); + break; + } +} + +static int +enc_open(struct cdev *dev, int flags, int fmt, struct thread *td) +{ + struct cam_periph *periph; + struct enc_softc *softc; + int error = 0; + + periph = (struct cam_periph *)dev->si_drv1; + if (periph == NULL) { + return (ENXIO); + } + + if (cam_periph_acquire(periph) != CAM_REQ_CMP) { + cam_periph_unlock(periph); + return (ENXIO); + } + + cam_periph_lock(periph); + + softc = (struct enc_softc *)periph->softc; + + if ((softc->enc_flags & ENC_FLAG_INITIALIZED) == 0) { + error = ENXIO; + goto out; + } + if (softc->enc_flags & ENC_FLAG_INVALID) { + error = ENXIO; + goto out; + } + +out: + cam_periph_unlock(periph); + if (error) { + cam_periph_release(periph); + } + return (error); +} + +static int +enc_close(struct cdev *dev, int flag, int fmt, struct thread *td) +{ + struct cam_periph *periph; + struct enc_softc *softc; + int error; + + error = 0; + + periph = (struct cam_periph *)dev->si_drv1; + if (periph == NULL) + return (ENXIO); + + cam_periph_lock(periph); + + softc = (struct enc_softc *)periph->softc; + + cam_periph_unlock(periph); + cam_periph_release(periph); + + return (0); +} + +static void +enc_start(struct cam_periph *p, union ccb *sccb) +{ + struct enc_softc *enc; + + enc = p->softc; + ENC_DLOG(enc, "%s enter imm=%d prio=%d\n", + __func__, p->immediate_priority, p->pinfo.priority); + if (p->immediate_priority <= p->pinfo.priority) { + SLIST_INSERT_HEAD(&p->ccb_list, &sccb->ccb_h, periph_links.sle); + p->immediate_priority = CAM_PRIORITY_NONE; + wakeup(&p->ccb_list); + } else + xpt_release_ccb(sccb); + ENC_DLOG(enc, "%s exit\n", __func__); +} + +void +enc_done(struct cam_periph *periph, union ccb *dccb) +{ + wakeup(&dccb->ccb_h.cbfcnp); +} + +int +enc_error(union ccb *ccb, uint32_t cflags, uint32_t sflags) +{ + struct enc_softc *softc; + struct cam_periph *periph; + + periph = xpt_path_periph(ccb->ccb_h.path); + softc = (struct enc_softc *)periph->softc; + + return (cam_periph_error(ccb, cflags, sflags, &softc->saved_ccb)); +} + +static int +enc_ioctl(struct cdev *dev, u_long cmd, caddr_t arg_addr, int flag, + struct thread *td) +{ + struct cam_periph *periph; + encioc_enc_status_t tmp; + encioc_string_t sstr; + encioc_elm_status_t elms; + encioc_elm_desc_t elmd; + encioc_elm_devnames_t elmdn; + encioc_element_t *uelm; + enc_softc_t *enc; + enc_cache_t *cache; + void *addr; + int error, i; + + + if (arg_addr) + addr = *((caddr_t *) arg_addr); + else + addr = NULL; + + periph = (struct cam_periph *)dev->si_drv1; + if (periph == NULL) + return (ENXIO); + + CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering encioctl\n")); + + cam_periph_lock(periph); + enc = (struct enc_softc *)periph->softc; + cache = &enc->enc_cache; + + /* + * Now check to see whether we're initialized or not. + * This actually should never fail as we're not supposed + * to get past enc_open w/o successfully initializing + * things. + */ + if ((enc->enc_flags & ENC_FLAG_INITIALIZED) == 0) { + cam_periph_unlock(periph); + return (ENXIO); + } + cam_periph_unlock(periph); + + error = 0; + + CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, + ("trying to do ioctl %#lx\n", cmd)); + + /* + * If this command can change the device's state, + * we must have the device open for writing. + * + * For commands that get information about the + * device- we don't need to lock the peripheral + * if we aren't running a command. The periph + * also can't go away while a user process has + * it open. + */ + switch (cmd) { + case ENCIOC_GETNELM: + case ENCIOC_GETELMMAP: + case ENCIOC_GETENCSTAT: + case ENCIOC_GETELMSTAT: + case ENCIOC_GETELMDESC: + case ENCIOC_GETELMDEVNAMES: + break; + default: + if ((flag & FWRITE) == 0) { + return (EBADF); + } + } + + /* + * XXX The values read here are only valid for the current + * configuration generation. We need these ioctls + * to also pass in/out a generation number. + */ + sx_slock(&enc->enc_cache_lock); + switch (cmd) { + case ENCIOC_GETNELM: + error = copyout(&cache->nelms, addr, sizeof (cache->nelms)); + break; + + case ENCIOC_GETELMMAP: + for (uelm = addr, i = 0; i != cache->nelms; i++) { + 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; + error = copyout(&kelm, &uelm[i], sizeof(kelm)); + if (error) + break; + } + break; + + case ENCIOC_GETENCSTAT: + cam_periph_lock(periph); + error = enc->enc_vec.get_enc_status(enc, 1); + if (error) { + cam_periph_unlock(periph); + break; + } + tmp = cache->enc_status & ~ENCI_SVALID; + cam_periph_unlock(periph); + error = copyout(&tmp, addr, sizeof(tmp)); + cache->enc_status = tmp; + break; + + case ENCIOC_SETENCSTAT: + error = copyin(addr, &tmp, sizeof(tmp)); + if (error) + break; + cam_periph_lock(periph); + error = enc->enc_vec.set_enc_status(enc, tmp, 1); + cam_periph_unlock(periph); + break; + + case ENCIOC_GETSTRING: + case ENCIOC_SETSTRING: + if (enc->enc_vec.handle_string == NULL) { + error = EINVAL; + break; + } + error = copyin(addr, &sstr, sizeof(sstr)); + if (error) + break; + cam_periph_lock(periph); + error = enc->enc_vec.handle_string(enc, &sstr, cmd); + cam_periph_unlock(periph); + break; + + case ENCIOC_GETELMSTAT: + error = copyin(addr, &elms, sizeof(elms)); + if (error) + break; + if (elms.elm_idx >= cache->nelms) { + error = EINVAL; + break; + } + cam_periph_lock(periph); + error = enc->enc_vec.get_elm_status(enc, &elms, 1); + cam_periph_unlock(periph); + if (error) + break; + error = copyout(&elms, addr, sizeof(elms)); + break; + + case ENCIOC_GETELMDESC: + if (enc->enc_vec.get_elm_desc == NULL) { + error = EINVAL; + break; + } + error = copyin(addr, &elmd, sizeof(elmd)); + if (error) + break; + if (elmd.elm_idx >= cache->nelms) { + error = EINVAL; + break; + } + error = enc->enc_vec.get_elm_desc(enc, &elmd); + if (error) + break; + error = copyout(&elmd, addr, sizeof(elmd)); + break; + + case ENCIOC_GETELMDEVNAMES: + if (enc->enc_vec.get_elm_devnames == NULL) { + error = EINVAL; + break; + } + error = copyin(addr, &elmdn, sizeof(elmdn)); + if (error) + break; + if (elmdn.elm_idx >= cache->nelms) { + error = EINVAL; + break; + } + cam_periph_lock(periph); + error = (*enc->enc_vec.get_elm_devnames)(enc, &elmdn); + cam_periph_unlock(periph); + if (error) + break; + error = copyout(&elmdn, addr, sizeof(elmdn)); + break; + + case ENCIOC_SETELMSTAT: + error = copyin(addr, &elms, sizeof(elms)); + if (error) + break; + + if (elms.elm_idx >= cache->nelms) { + error = EINVAL; + break; + } + cam_periph_lock(periph); + error = enc->enc_vec.set_elm_status(enc, &elms, 1); + cam_periph_unlock(periph); + + break; + + case ENCIOC_INIT: + + cam_periph_lock(periph); + error = enc->enc_vec.init_enc(enc); + cam_periph_unlock(periph); + break; + + default: + cam_periph_lock(periph); + error = cam_periph_ioctl(periph, cmd, arg_addr, enc_error); + cam_periph_unlock(periph); + break; + } + sx_sunlock(&enc->enc_cache_lock); + return (error); +} + +int +enc_runcmd(struct enc_softc *enc, char *cdb, int cdbl, char *dptr, int *dlenp) +{ + int error, dlen; + ccb_flags ddf; + union ccb *ccb; + + CAM_DEBUG(enc->periph->path, CAM_DEBUG_TRACE, + ("entering enc_runcmd\n")); + if (dptr) { + if ((dlen = *dlenp) < 0) { + dlen = -dlen; + ddf = CAM_DIR_OUT; + } else { + ddf = CAM_DIR_IN; + } + } else { + dlen = 0; + ddf = CAM_DIR_NONE; + } + + if (cdbl > IOCDBLEN) { + cdbl = IOCDBLEN; + } + + ccb = cam_periph_getccb(enc->periph, 1); + cam_fill_csio(&ccb->csio, 0, enc_done, ddf, MSG_SIMPLE_Q_TAG, dptr, + dlen, sizeof (struct scsi_sense_data), cdbl, 60 * 1000); + bcopy(cdb, ccb->csio.cdb_io.cdb_bytes, cdbl); + + error = cam_periph_runccb(ccb, enc_error, ENC_CFLAGS, ENC_FLAGS, NULL); + if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) + cam_release_devq(ccb->ccb_h.path, 0, 0, 0, FALSE); + if (error) { + if (dptr) { + *dlenp = dlen; + } + } else { + if (dptr) { + *dlenp = ccb->csio.resid; + } + } + xpt_release_ccb(ccb); + CAM_DEBUG(enc->periph->path, CAM_DEBUG_SUBTRACE, + ("exiting enc_runcmd: *dlenp = %d\n", *dlenp)); + return (error); +} + +void +enc_log(struct enc_softc *enc, const char *fmt, ...) +{ + va_list ap; + + printf("%s%d: ", enc->periph->periph_name, enc->periph->unit_number); + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); +} + +/* + * The code after this point runs on many platforms, + * so forgive the slightly awkward and nonconforming + * appearance. + */ + +/* + * Is this a device that supports enclosure services? + * + * It's a a pretty simple ruleset- if it is device type 0x0D (13), it's + * an ENC device. If it happens to be an old UNISYS SEN device, we can + * handle that too. + */ + +#define SAFTE_START 44 +#define SAFTE_END 50 +#define SAFTE_LEN SAFTE_END-SAFTE_START + +static enctyp +enc_type(void *buf, int buflen) +{ + unsigned char *iqd = buf; + + if (buflen < 8+SEN_ID_LEN) + return (ENC_NONE); + + if ((iqd[0] & 0x1f) == T_ENCLOSURE) { + if (STRNCMP(&iqd[8], SEN_ID, SEN_ID_LEN) == 0) { + return (ENC_SEN); + } else if ((iqd[2] & 0x7) > 2) { + return (ENC_SES); + } else { + return (ENC_SES_SCSI2); + } + return (ENC_NONE); + } + +#ifdef ENC_ENABLE_PASSTHROUGH + if ((iqd[6] & 0x40) && (iqd[2] & 0x7) >= 2) { + /* + * PassThrough Device. + */ + return (ENC_ENC_PASSTHROUGH); + } +#endif + + /* + * The comparison is short for a reason- + * some vendors were chopping it short. + */ + + if (buflen < SAFTE_END - 2) { + return (ENC_NONE); + } + + if (STRNCMP((char *)&iqd[SAFTE_START], "SAF-TE", SAFTE_LEN - 2) == 0) { + return (ENC_SAFT); + } + return (ENC_NONE); +} + +/*================== Enclosure Monitoring/Processing Daemon ==================*/ +/** + * \brief Queue an update request for a given action, if needed. + * + * \param enc SES softc to queue the request for. + * \param action Action requested. + */ +void +enc_update_request(enc_softc_t *enc, uint32_t action) +{ + if ((enc->pending_actions & (0x1 << action)) == 0) { + enc->pending_actions |= (0x1 << action); + ENC_DLOG(enc, "%s: queing requested action %d\n", + __func__, action); + if (enc->current_action == ENC_UPDATE_NONE) + wakeup(enc->enc_daemon); + } else { + ENC_DLOG(enc, "%s: ignoring requested action %d - " + "Already queued\n", __func__, action); + } +} + +/** + * \brief Invoke the handler of the highest priority pending + * state in the SES state machine. + * + * \param enc The SES instance invoking the state machine. + */ +static void +enc_fsm_step(enc_softc_t *enc) +{ + union ccb *ccb; + uint8_t *buf; + struct enc_fsm_state *cur_state; + int error; + + ENC_DLOG(enc, "%s enter %p\n", __func__, enc); + + enc->current_action = ffs(enc->pending_actions) - 1; + enc->pending_actions &= ~(0x1 << enc->current_action); + + cur_state = &enc->enc_fsm_states[enc->current_action]; + + buf = NULL; + if (cur_state->buf_size != 0) + buf = malloc(cur_state->buf_size, M_SCSIENC, M_WAITOK|M_ZERO); + + error = 0; + ccb = NULL; + if (cur_state->fill != NULL) { + ccb = cam_periph_getccb(enc->periph, CAM_PRIORITY_NORMAL); + + error = cur_state->fill(enc, cur_state, ccb, buf); + if (error == 0) { + error = cam_periph_runccb(ccb, cur_state->error, + ENC_CFLAGS, + ENC_FLAGS|SF_QUIET_IR, NULL); + } + } + + + if (error == 0) { + uint32_t resid; + + resid = 0; + if (ccb != NULL) + resid = ccb->csio.dxfer_len - ccb->csio.resid; + + cam_periph_unlock(enc->periph); + cur_state->done(enc, cur_state, ccb, &buf, resid); + cam_periph_lock(enc->periph); + } + + ENC_DLOG(enc, "%s exit - result %d\n", __func__, error); + ENC_FREE_AND_NULL(buf); + if (ccb != NULL) + xpt_release_ccb(ccb); +} + +/** + * \invariant Called with cam_periph mutex held. + */ +static void +enc_status_updater(void *arg) +{ + enc_softc_t *enc; + + enc = arg; + if (enc->enc_vec.poll_status != NULL) + enc->enc_vec.poll_status(enc); +} + +static void +enc_daemon(void *arg) +{ + enc_softc_t *enc; + + enc = arg; + + cam_periph_lock(enc->periph); + while ((enc->enc_flags & ENC_FLAG_SHUTDOWN) == 0) { + if (enc->pending_actions == 0) { + struct intr_config_hook *hook; + + /* + * Reset callout and msleep, or + * issue timed task completion + * status command. + */ + enc->current_action = ENC_UPDATE_NONE; + + /* + * We've been through our state machine at least + * once. Allow the transition to userland. + */ + hook = &enc->enc_boot_hold_ch; + if (hook->ich_func != NULL) { + config_intrhook_disestablish(hook); + hook->ich_func = NULL; + } + + callout_reset(&enc->status_updater, 60*hz, + enc_status_updater, enc); + + cam_periph_sleep(enc->periph, enc->enc_daemon, + PUSER, "idle", 0); + } else { + enc_fsm_step(enc); + } + } + enc->enc_daemon = NULL; + cam_periph_unlock(enc->periph); + kproc_exit(0); +} + +static int +enc_kproc_init(enc_softc_t *enc) +{ + int result; + + callout_init_mtx(&enc->status_updater, enc->periph->sim->mtx, 0); + + result = kproc_create(enc_daemon, enc, &enc->enc_daemon, /*flags*/0, + /*stackpgs*/0, "enc_daemon%d", + enc->periph->unit_number); + if (result == 0) { + /* Do an initial load of all page data. */ + cam_periph_lock(enc->periph); + enc->enc_vec.poll_status(enc); + cam_periph_unlock(enc->periph); + } + return (result); +} + +/** + * \brief Interrupt configuration hook callback associated with + * enc_boot_hold_ch. + * + * Since interrupts are always functional at the time of enclosure + * configuration, there is nothing to be done when the callback occurs. + * This hook is only registered to hold up boot processing while initial + * eclosure processing occurs. + * + * \param arg The enclosure softc, but currently unused in this callback. + */ +static void +enc_nop_confighook_cb(void *arg __unused) +{ +} + +static cam_status +enc_ctor(struct cam_periph *periph, void *arg) +{ + cam_status status = CAM_REQ_CMP_ERR; + int err; + enc_softc_t *enc; + struct ccb_getdev *cgd; + char *tname; + + cgd = (struct ccb_getdev *)arg; + if (periph == NULL) { + printf("enc_ctor: periph was NULL!!\n"); + goto out; + } + + if (cgd == NULL) { + printf("enc_ctor: no getdev CCB, can't register device\n"); + goto out; + } + + enc = ENC_MALLOCZ(sizeof(*enc)); + if (enc == NULL) { + printf("enc_ctor: Unable to probe new device. " + "Unable to allocate enc\n"); + goto out; + } + enc->periph = periph; + enc->current_action = ENC_UPDATE_NONE; + + enc->enc_type = enc_type(&cgd->inq_data, sizeof (cgd->inq_data)); + sx_init(&enc->enc_cache_lock, "enccache"); + + switch (enc->enc_type) { + case ENC_SES: + case ENC_SES_SCSI2: + case ENC_SES_PASSTHROUGH: + err = ses_softc_init(enc, 1); + break; + case ENC_SAFT: + err = safte_softc_init(enc, 1); + break; + case ENC_SEN: + case ENC_NONE: + default: + ENC_FREE(enc); + return (CAM_REQ_CMP_ERR); + } + + if (err) { + xpt_print(periph->path, "error %d initializing\n", err); + goto out; + } + + /* + * Hold off userland until we have made at least one pass + * through our state machine so that physical path data is + * present. + */ + enc->enc_boot_hold_ch.ich_func = enc_nop_confighook_cb; + enc->enc_boot_hold_ch.ich_arg = enc; + config_intrhook_establish(&enc->enc_boot_hold_ch); + + /* + * The softc field is set only once the enc is fully initialized + * so that we can rely on this field to detect partially + * initialized periph objects in the AC_FOUND_DEVICE handler. + */ + periph->softc = enc; + + cam_periph_unlock(periph); + if (enc->enc_vec.poll_status != NULL) { + err = enc_kproc_init(enc); + if (err) { + xpt_print(periph->path, + "error %d string enc_daemon\n", err); + goto out; + } + } + enc->enc_dev = make_dev(&enc_cdevsw, periph->unit_number, + UID_ROOT, GID_OPERATOR, 0600, "%s%d", + periph->periph_name, periph->unit_number); + cam_periph_lock(periph); + enc->enc_dev->si_drv1 = periph; + + enc->enc_flags |= ENC_FLAG_INITIALIZED; + + /* + * Add an async callback so that we get notified if this + * device goes away. + */ + xpt_register_async(AC_LOST_DEVICE, enc_async, periph, periph->path); + + switch (enc->enc_type) { + default: + case ENC_NONE: + tname = "No ENC device"; + break; + case ENC_SES_SCSI2: + tname = "SCSI-2 ENC Device"; + break; + case ENC_SES: + tname = "SCSI-3 ENC Device"; + break; + case ENC_SES_PASSTHROUGH: + tname = "ENC Passthrough Device"; + break; + case ENC_SEN: + tname = "UNISYS SEN Device (NOT HANDLED YET)"; + break; + case ENC_SAFT: + tname = "SAF-TE Compliant Device"; + break; + } + xpt_announce_periph(periph, tname); + status = CAM_REQ_CMP; + +out: + if (status != CAM_REQ_CMP) + enc_dtor(periph); + return (status); +} + diff --git a/sys/cam/scsi/scsi_enc.h b/sys/cam/scsi/scsi_enc.h new file mode 100644 index 00000000000..e73172ce143 --- /dev/null +++ b/sys/cam/scsi/scsi_enc.h @@ -0,0 +1,219 @@ +/* $FreeBSD: head/sys/cam/scsi/scsi_enc.h 154504 2006-01-18 08:37:27Z mjacob $ */ +/*- + * Copyright (c) 2000 by Matthew Jacob + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * the GNU Public License ("GPL"). + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ +#ifndef _SCSI_ENC_H_ +#define _SCSI_ENC_H_ + +#include + +#define ENCIOC ('s' - 040) +#define ENCIOC_GETNELM _IO(ENCIOC, 1) +#define ENCIOC_GETELMMAP _IO(ENCIOC, 2) +#define ENCIOC_GETENCSTAT _IO(ENCIOC, 3) +#define ENCIOC_SETENCSTAT _IO(ENCIOC, 4) +#define ENCIOC_GETELMSTAT _IO(ENCIOC, 5) +#define ENCIOC_SETELMSTAT _IO(ENCIOC, 6) +#define ENCIOC_GETTEXT _IO(ENCIOC, 7) +#define ENCIOC_INIT _IO(ENCIOC, 8) +#define ENCIOC_GETELMDESC _IO(ENCIOC, 9) +#define ENCIOC_GETELMDEVNAMES _IO(ENCIOC, 10) +#define ENCIOC_GETSTRING _IO(ENCIOC, 11) +#define ENCIOC_SETSTRING _IO(ENCIOC, 12) + +/* + * Platform Independent Definitions for enclosure devices. + */ +/* + * SCSI Based Environmental Services Application Defines + * + * Based almost entirely on SCSI-3 ENC Revision 8A specification, + * but slightly abstracted as the underlying device may in fact + * be a SAF-TE or vendor unique device. + */ +/* + * ENC Driver Operations: + * (The defines themselves are platform and access method specific) + * + * ENCIOC_GETNELM + * ENCIOC_GETELMMAP + * ENCIOC_GETENCSTAT + * ENCIOC_SETENCSTAT + * ENCIOC_GETELMSTAT + * ENCIOC_SETELMSTAT + * ENCIOC_INIT + * + * + * An application finds out how many elements an enclosure instance + * is managing by performing a ENCIOC_GETNELM operation. It then + * performs a ENCIOC_GETELMMAP to get the map that contains the + * elment identifiers for all elements (see encioc_element_t below). + * This information is static. + * + * The application may perform ENCIOC_GETELMSTAT operations to retrieve + * status on an element (see the enc_elm_status_t structure below), + * ENCIOC_SETELMSTAT operations to set status for an element. + * + * Similarly, overall enclosure status me be fetched or set via + * ENCIOC_GETENCSTAT or ENCIOC_SETENCSTAT operations (see encioc_enc_status_t + * below). + * + * Readers should note that there is nothing that requires either a set + * or a clear operation to actually latch and do anything in the target. + * + * A ENCIOC_INIT operation causes the enclosure to be initialized. + */ + +/* Element Types */ +typedef enum { + ELMTYP_UNSPECIFIED = 0x00, + ELMTYP_DEVICE = 0x01, + ELMTYP_POWER = 0x02, + ELMTYP_FAN = 0x03, + ELMTYP_THERM = 0x04, + ELMTYP_DOORLOCK = 0x05, + ELMTYP_ALARM = 0x06, + ELMTYP_ESCC = 0x07, /* Enclosure SCC */ + ELMTYP_SCC = 0x08, /* SCC */ + ELMTYP_NVRAM = 0x09, + ELMTYP_INV_OP_REASON = 0x0a, + ELMTYP_UPS = 0x0b, + ELMTYP_DISPLAY = 0x0c, + ELMTYP_KEYPAD = 0x0d, + ELMTYP_ENCLOSURE = 0x0e, + ELMTYP_SCSIXVR = 0x0f, + ELMTYP_LANGUAGE = 0x10, + ELMTYP_COMPORT = 0x11, + ELMTYP_VOM = 0x12, + ELMTYP_AMMETER = 0x13, + ELMTYP_SCSI_TGT = 0x14, + ELMTYP_SCSI_INI = 0x15, + ELMTYP_SUBENC = 0x16, + ELMTYP_ARRAY_DEV = 0x17, + ELMTYP_SAS_EXP = 0x18, /* SAS expander */ + ELMTYP_SAS_CONN = 0x19 /* SAS connector */ +} elm_type_t; + +typedef struct encioc_element { + /* Element Index */ + unsigned int elm_idx; + + /* ID of SubEnclosure containing Element*/ + unsigned int elm_subenc_id; + + /* Element Type */ + elm_type_t elm_type; +} encioc_element_t; + +/* + * Overall Enclosure Status + */ +typedef unsigned char encioc_enc_status_t; + +/* + * Element Status + */ +typedef struct encioc_elm_status { + unsigned int elm_idx; + unsigned char cstat[4]; +} encioc_elm_status_t; + +/* + * ENC String structure, for StringIn and StringOut commands; use this with + * the ENCIOC_GETSTRING and ENCIOC_SETSTRING ioctls. + */ +typedef struct encioc_string { + size_t bufsiz; /* IN/OUT: length of string provided/returned */ +#define ENC_STRING_MAX 0xffff + uint8_t *buf; /* IN/OUT: string */ +} encioc_string_t; + +/*============================================================================*/ + +/* + * SES v2 r20 6.1.10 (pg 39) - Element Descriptor diagnostic page + * Tables 21, 22, and 23 + */ +typedef struct encioc_elm_desc { + unsigned int elm_idx; /* IN: elment requested */ + uint16_t elm_desc_len; /* IN: buffer size; OUT: bytes written */ + char *elm_desc_str; /* IN/OUT: buffer for descriptor data */ +} encioc_elm_desc_t; + +/* + * ENCIOC_GETELMDEVNAMES: + * ioctl structure to get an element's device names, if available + */ +typedef struct encioc_elm_devnames { + unsigned int elm_idx; /* IN: element index */ + size_t elm_names_size;/* IN: size of elm_devnames */ + size_t elm_names_len; /* OUT: actual size returned */ + /* + * IN/OUT: comma separated list of peripheral driver + * instances servicing this element. + */ + char *elm_devnames; +} encioc_elm_devnames_t; + +/* ioctl structure for requesting FC info for a port */ +typedef struct encioc_elm_fc_port { + unsigned int elm_idx; + unsigned int port_idx; + struct ses_elm_fc_port port_data; +} encioc_elm_fc_port_t; + +/* ioctl structure for requesting SAS info for element phys */ +typedef struct encioc_elm_sas_device_phy { + unsigned int elm_idx; + unsigned int phy_idx; + struct ses_elm_sas_device_phy phy_data; +} enioc_elm_sas_phy_t; + +/* ioctl structure for requesting SAS info for an expander phy */ +typedef struct encioc_elm_sas_expander_phy { + unsigned int elm_idx; + unsigned int phy_idx; + struct ses_elm_sas_expander_phy phy_data; +} encioc_elm_sas_expander_phy_t; + +/* ioctl structure for requesting SAS info for a port phy */ +typedef struct encioc_elm_sas_port_phy { + unsigned int elm_idx; + unsigned int phy_idx; + struct ses_elm_sas_port_phy phy_data; +} enioc_elm_sas_port_phy_t; + +/* ioctl structure for requesting additional status for an element */ +typedef struct encioc_addl_status { + unsigned int elm_idx; + union ses_elm_addlstatus_descr_hdr addl_hdr; + union ses_elm_addlstatus_proto_hdr proto_hdr; +} enioc_addl_status_t; + +#endif /* _SCSI_ENC_H_ */ diff --git a/sys/cam/scsi/scsi_enc_internal.h b/sys/cam/scsi/scsi_enc_internal.h new file mode 100644 index 00000000000..e2a8b005a72 --- /dev/null +++ b/sys/cam/scsi/scsi_enc_internal.h @@ -0,0 +1,226 @@ +/*- + * Copyright (c) 2000 Matthew Jacob + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * This file contains definitions only intended for use within + * sys/cam/scsi/scsi_enc*.c, and not in other kernel components. + */ + +#ifndef __SCSI_ENC_INTERNAL_H__ +#define __SCSI_ENC_INTERNAL_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 encstat[4]; /* state && stats */ + uint8_t *physical_path; /* Device physical path data. */ + u_int physical_path_len; /* Length of device path data. */ + void *elm_private; /* per-type object data */ +} enc_element_t; + +typedef enum { + ENC_NONE, + ENC_SES_SCSI2, + ENC_SES, + ENC_SES_PASSTHROUGH, + ENC_SEN, + ENC_SAFT +} enctyp; + +/* Platform Independent Driver Internal Definitions for enclosure devices. */ +typedef struct enc_softc enc_softc_t; + +struct enc_fsm_state; +typedef int fsm_fill_handler_t(enc_softc_t *ssc, + struct enc_fsm_state *state, + union ccb *ccb, + uint8_t *buf); +typedef int fsm_error_handler_t(union ccb *ccb, uint32_t cflags, + uint32_t sflags); +typedef int fsm_done_handler_t(enc_softc_t *ssc, + struct enc_fsm_state *state, union ccb *ccb, + uint8_t **bufp, int xfer_len); + +struct enc_fsm_state { + const char *name; + int page_code; + size_t buf_size; + uint32_t timeout; + fsm_fill_handler_t *fill; + fsm_done_handler_t *done; + fsm_error_handler_t *error; +}; + +typedef int (enc_softc_init_t)(enc_softc_t *, int); +typedef void (enc_softc_invalidate_t)(struct cam_periph *); +typedef void (enc_softc_cleanup_t)(struct cam_periph *); +typedef int (enc_init_enc_t)(enc_softc_t *); +typedef int (enc_get_enc_status_t)(enc_softc_t *, int); +typedef int (enc_set_enc_status_t)(enc_softc_t *, encioc_enc_status_t, int); +typedef int (enc_get_elm_status_t)(enc_softc_t *, encioc_elm_status_t *, int); +typedef int (enc_set_elm_status_t)(enc_softc_t *, encioc_elm_status_t *, int); +typedef int (enc_get_elm_desc_t)(enc_softc_t *, encioc_elm_desc_t *); +typedef int (enc_get_elm_devnames_t)(enc_softc_t *, encioc_elm_devnames_t *); +typedef int (enc_handle_string_t)(enc_softc_t *, encioc_string_t *, int); +typedef void (enc_device_found_t)(enc_softc_t *); +typedef void (enc_poll_status_t)(enc_softc_t *); + +struct enc_vec { + enc_softc_invalidate_t *softc_invalidate; + enc_softc_cleanup_t *softc_cleanup; + enc_init_enc_t *init_enc; + enc_get_enc_status_t *get_enc_status; + enc_set_enc_status_t *set_enc_status; + enc_get_elm_status_t *get_elm_status; + enc_set_elm_status_t *set_elm_status; + enc_get_elm_desc_t *get_elm_desc; + enc_get_elm_devnames_t *get_elm_devnames; + enc_handle_string_t *handle_string; + enc_device_found_t *device_found; + enc_poll_status_t *poll_status; +}; + +typedef struct enc_cache { + enc_element_t *elm_map; /* objects */ + int nelms; /* number of objects */ + encioc_enc_status_t enc_status; /* overall status */ + void *private; /* per-type private data */ +} enc_cache_t; + +/* Enclosure instance toplevel structure */ +struct enc_softc { + enctyp enc_type; /* type of enclosure */ + struct enc_vec enc_vec; /* vector to handlers */ + void *enc_private; /* per-type private data */ + + /** + * "Published" configuration and state data available to + * external consumers. + */ + enc_cache_t enc_cache; + + /** + * Configuration and state data being actively updated + * by the enclosure daemon. + */ + enc_cache_t enc_daemon_cache; + + struct sx enc_cache_lock; +#define ENCI_SVALID 0x80 + uint8_t enc_flags; +#define ENC_FLAG_INVALID 0x01 +#define ENC_FLAG_INITIALIZED 0x02 +#define ENC_FLAG_SHUTDOWN 0x04 + union ccb saved_ccb; + struct cdev *enc_dev; + struct cam_periph *periph; + + /* Bitmap of pending operations. */ + uint32_t pending_actions; + + /* The action on which the state machine is currently working. */ + uint32_t current_action; +#define ENC_UPDATE_NONE 0x00 + + /* Callout for auto-updating enclosure status */ + struct callout status_updater; + + struct proc *enc_daemon; + + struct enc_fsm_state *enc_fsm_states; + + struct intr_config_hook enc_boot_hold_ch; +}; + +static inline enc_cache_t * +enc_other_cache(enc_softc_t *enc, enc_cache_t *primary) +{ + return (primary == &enc->enc_cache + ? &enc->enc_daemon_cache : &enc->enc_cache); +} + +/* SES Management mode page - SES2r20 Table 59 */ +struct ses_mgmt_mode_page { + struct scsi_mode_header_6 header; + struct scsi_mode_blk_desc blk_desc; + uint8_t byte0; /* ps : 1, spf : 1, page_code : 6 */ +#define SES_MGMT_MODE_PAGE_CODE 0x14 + uint8_t length; +#define SES_MGMT_MODE_PAGE_LEN 6 + uint8_t reserved[3]; + uint8_t byte5; /* reserved : 7, enbltc : 1 */ +#define SES_MGMT_TIMED_COMP_EN 0x1 + uint8_t max_comp_time[2]; +}; + +/* Enclosure core interface for sub-drivers */ +int enc_runcmd(struct enc_softc *, char *, int, char *, int *); +void enc_log(struct enc_softc *, const char *, ...); +void enc_done(struct cam_periph *, union ccb *); +int enc_error(union ccb *, uint32_t, uint32_t); +void enc_update_request(enc_softc_t *, uint32_t); + +/* SES Native interface */ +enc_softc_init_t ses_softc_init; + +/* SAF-TE interface */ +enc_softc_init_t safte_softc_init; + +/* Helper macros */ +MALLOC_DECLARE(M_SCSIENC); +#define ENC_CFLAGS CAM_RETRY_SELTO +#define ENC_FLAGS SF_NO_PRINT | SF_RETRY_UA +#define STRNCMP strncmp +#define PRINTF printf +#define ENC_LOG enc_log +#if defined(DEBUG) || defined(ENC_DEBUG) +#define ENC_DLOG enc_log +#else +#define ENC_DLOG if (0) enc_log +#endif +#define ENC_VLOG if (bootverbose) enc_log +#define ENC_MALLOC(amt) malloc(amt, M_SCSIENC, M_NOWAIT) +#define ENC_MALLOCZ(amt) malloc(amt, M_SCSIENC, M_ZERO|M_NOWAIT) +/* Cast away const avoiding GCC warnings. */ +#define ENC_FREE(ptr) free((void *)((uintptr_t)ptr), M_SCSIENC) +#define ENC_FREE_AND_NULL(ptr) do { \ + if (ptr != NULL) { \ + ENC_FREE(ptr); \ + ptr = NULL; \ + } \ +} while(0) +#define MEMZERO bzero +#define MEMCPY(dest, src, amt) bcopy(src, dest, amt) + +#endif /* __SCSI_ENC_INTERNAL_H__ */ diff --git a/sys/cam/scsi/scsi_enc_safte.c b/sys/cam/scsi/scsi_enc_safte.c new file mode 100644 index 00000000000..b3caff7c055 --- /dev/null +++ b/sys/cam/scsi/scsi_enc_safte.c @@ -0,0 +1,1041 @@ +/*- + * Copyright (c) 2000 Matthew Jacob + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD: head/sys/cam/scsi/scsi_ses.c 201758 2010-01-07 21:01:37Z mbr $"); + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +/* + * SAF-TE Type Device Emulation + */ + +static int safte_getconfig(enc_softc_t *); +static int safte_rdstat(enc_softc_t *, int); +static int set_elm_status_sel(enc_softc_t *, encioc_elm_status_t *, int); +static int wrbuf16(enc_softc_t *, uint8_t, uint8_t, uint8_t, uint8_t, int); +static void wrslot_stat(enc_softc_t *, int); +static int perf_slotop(enc_softc_t *, uint8_t, uint8_t, int); + +#define ALL_ENC_STAT (SES_ENCSTAT_CRITICAL | SES_ENCSTAT_UNRECOV | \ + SES_ENCSTAT_NONCRITICAL | SES_ENCSTAT_INFO) +/* + * SAF-TE specific defines- Mandatory ones only... + */ + +/* + * READ BUFFER ('get' commands) IDs- placed in offset 2 of cdb + */ +#define SAFTE_RD_RDCFG 0x00 /* read enclosure configuration */ +#define SAFTE_RD_RDESTS 0x01 /* read enclosure status */ +#define SAFTE_RD_RDDSTS 0x04 /* read drive slot status */ + +/* + * WRITE BUFFER ('set' commands) IDs- placed in offset 0 of databuf + */ +#define SAFTE_WT_DSTAT 0x10 /* write device slot status */ +#define SAFTE_WT_SLTOP 0x12 /* perform slot operation */ +#define SAFTE_WT_FANSPD 0x13 /* set fan speed */ +#define SAFTE_WT_ACTPWS 0x14 /* turn on/off power supply */ +#define SAFTE_WT_GLOBAL 0x15 /* send global command */ + + +#define SAFT_SCRATCH 64 +#define NPSEUDO_THERM 16 +#define NPSEUDO_ALARM 1 +struct scfg { + /* + * Cached Configuration + */ + uint8_t Nfans; /* Number of Fans */ + uint8_t Npwr; /* Number of Power Supplies */ + uint8_t Nslots; /* Number of Device Slots */ + uint8_t DoorLock; /* Door Lock Installed */ + uint8_t Ntherm; /* Number of Temperature Sensors */ + uint8_t Nspkrs; /* Number of Speakers */ + uint8_t Nalarm; /* Number of Alarms (at least one) */ + /* + * Cached Flag Bytes for Global Status + */ + uint8_t flag1; + uint8_t flag2; + /* + * What object index ID is where various slots start. + */ + uint8_t pwroff; + uint8_t slotoff; +#define SAFT_ALARM_OFFSET(cc) (cc)->slotoff - 1 +}; + +#define SAFT_FLG1_ALARM 0x1 +#define SAFT_FLG1_GLOBFAIL 0x2 +#define SAFT_FLG1_GLOBWARN 0x4 +#define SAFT_FLG1_ENCPWROFF 0x8 +#define SAFT_FLG1_ENCFANFAIL 0x10 +#define SAFT_FLG1_ENCPWRFAIL 0x20 +#define SAFT_FLG1_ENCDRVFAIL 0x40 +#define SAFT_FLG1_ENCDRVWARN 0x80 + +#define SAFT_FLG2_LOCKDOOR 0x4 +#define SAFT_PRIVATE sizeof (struct scfg) + +static char *safte_2little = "Too Little Data Returned (%d) at line %d\n"; +#define SAFT_BAIL(r, x, k) \ + if ((r) >= (x)) { \ + ENC_LOG(ssc, safte_2little, x, __LINE__);\ + ENC_FREE((k)); \ + return (EIO); \ + } + +static int +safte_getconfig(enc_softc_t *ssc) +{ + struct scfg *cfg; + int err, amt; + char *sdata; + static char cdb[10] = + { READ_BUFFER, 1, SAFTE_RD_RDCFG, 0, 0, 0, 0, 0, SAFT_SCRATCH, 0 }; + + cfg = ssc->enc_private; + if (cfg == NULL) + return (ENXIO); + + sdata = ENC_MALLOC(SAFT_SCRATCH); + if (sdata == NULL) + return (ENOMEM); + + amt = SAFT_SCRATCH; + err = enc_runcmd(ssc, cdb, 10, sdata, &amt); + if (err) { + ENC_FREE(sdata); + return (err); + } + amt = SAFT_SCRATCH - amt; + if (amt < 6) { + ENC_LOG(ssc, "too little data (%d) for configuration\n", amt); + ENC_FREE(sdata); + return (EIO); + } + ENC_VLOG(ssc, "Nfans %d Npwr %d Nslots %d Lck %d Ntherm %d Nspkrs %d\n", + sdata[0], sdata[1], sdata[2], sdata[3], sdata[4], sdata[5]); + cfg->Nfans = sdata[0]; + cfg->Npwr = sdata[1]; + cfg->Nslots = sdata[2]; + cfg->DoorLock = sdata[3]; + cfg->Ntherm = sdata[4]; + cfg->Nspkrs = sdata[5]; + cfg->Nalarm = NPSEUDO_ALARM; + ENC_FREE(sdata); + return (0); +} + +static int +safte_rdstat(enc_softc_t *ssc, int slpflg) +{ + int err, oid, r, i, hiwater, nitems, amt; + uint16_t tempflags; + size_t buflen; + uint8_t status, oencstat; + char *sdata, cdb[10]; + struct scfg *cc = ssc->enc_private; + enc_cache_t *cache = &ssc->enc_cache; + + + /* + * The number of objects overstates things a bit, + * both for the bogus 'thermometer' entries and + * the drive status (which isn't read at the same + * time as the enclosure status), but that's okay. + */ + buflen = 4 * cc->Nslots; + if (cache->nelms > buflen) + buflen = cache->nelms; + sdata = ENC_MALLOC(buflen); + if (sdata == NULL) + return (ENOMEM); + + cdb[0] = READ_BUFFER; + cdb[1] = 1; + cdb[2] = SAFTE_RD_RDESTS; + cdb[3] = 0; + cdb[4] = 0; + cdb[5] = 0; + cdb[6] = 0; + cdb[7] = (buflen >> 8) & 0xff; + cdb[8] = buflen & 0xff; + cdb[9] = 0; + amt = buflen; + err = enc_runcmd(ssc, cdb, 10, sdata, &amt); + if (err) { + ENC_FREE(sdata); + return (err); + } + hiwater = buflen - amt; + + + /* + * invalidate all status bits. + */ + for (i = 0; i < cache->nelms; i++) + cache->elm_map[i].svalid = 0; + oencstat = cache->enc_status & ALL_ENC_STAT; + ssc->enc_cache.enc_status = 0; + + + /* + * Now parse returned buffer. + * If we didn't get enough data back, + * that's considered a fatal error. + */ + oid = r = 0; + + for (nitems = i = 0; i < cc->Nfans; i++) { + SAFT_BAIL(r, hiwater, sdata); + /* + * 0 = Fan Operational + * 1 = Fan is malfunctioning + * 2 = Fan is not present + * 0x80 = Unknown or Not Reportable Status + */ + cache->elm_map[oid].encstat[1] = 0; /* resvd */ + cache->elm_map[oid].encstat[2] = 0; /* resvd */ + switch ((int)(uint8_t)sdata[r]) { + case 0: + nitems++; + cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK; + /* + * We could get fancier and cache + * fan speeds that we have set, but + * that isn't done now. + */ + cache->elm_map[oid].encstat[3] = 7; + break; + + case 1: + cache->elm_map[oid].encstat[0] = + SES_OBJSTAT_CRIT; + /* + * FAIL and FAN STOPPED synthesized + */ + cache->elm_map[oid].encstat[3] = 0x40; + /* + * Enclosure marked with CRITICAL error + * if only one fan or no thermometers, + * else the NONCRITICAL error is set. + */ + if (cc->Nfans == 1 || cc->Ntherm == 0) + cache->enc_status |= SES_ENCSTAT_CRITICAL; + else + cache->enc_status |= SES_ENCSTAT_NONCRITICAL; + break; + case 2: + cache->elm_map[oid].encstat[0] = + SES_OBJSTAT_NOTINSTALLED; + cache->elm_map[oid].encstat[3] = 0; + /* + * Enclosure marked with CRITICAL error + * if only one fan or no thermometers, + * else the NONCRITICAL error is set. + */ + if (cc->Nfans == 1) + cache->enc_status |= SES_ENCSTAT_CRITICAL; + else + cache->enc_status |= SES_ENCSTAT_NONCRITICAL; + break; + case 0x80: + cache->elm_map[oid].encstat[0] = SES_OBJSTAT_UNKNOWN; + cache->elm_map[oid].encstat[3] = 0; + cache->enc_status |= SES_ENCSTAT_INFO; + break; + default: + cache->elm_map[oid].encstat[0] = SES_OBJSTAT_UNSUPPORTED; + ENC_LOG(ssc, "Unknown fan%d status 0x%x\n", i, + sdata[r] & 0xff); + break; + } + cache->elm_map[oid++].svalid = 1; + r++; + } + + /* + * No matter how you cut it, no cooling elements when there + * should be some there is critical. + */ + if (cc->Nfans && nitems == 0) { + cache->enc_status |= SES_ENCSTAT_CRITICAL; + } + + + for (i = 0; i < cc->Npwr; i++) { + SAFT_BAIL(r, hiwater, sdata); + cache->elm_map[oid].encstat[0] = SES_OBJSTAT_UNKNOWN; + cache->elm_map[oid].encstat[1] = 0; /* resvd */ + cache->elm_map[oid].encstat[2] = 0; /* resvd */ + cache->elm_map[oid].encstat[3] = 0x20; /* requested on */ + switch ((uint8_t)sdata[r]) { + case 0x00: /* pws operational and on */ + cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK; + break; + case 0x01: /* pws operational and off */ + cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK; + cache->elm_map[oid].encstat[3] = 0x10; + cache->enc_status |= SES_ENCSTAT_INFO; + break; + case 0x10: /* pws is malfunctioning and commanded on */ + cache->elm_map[oid].encstat[0] = SES_OBJSTAT_CRIT; + cache->elm_map[oid].encstat[3] = 0x61; + cache->enc_status |= SES_ENCSTAT_NONCRITICAL; + break; + + case 0x11: /* pws is malfunctioning and commanded off */ + cache->elm_map[oid].encstat[0] = SES_OBJSTAT_NONCRIT; + cache->elm_map[oid].encstat[3] = 0x51; + cache->enc_status |= SES_ENCSTAT_NONCRITICAL; + break; + case 0x20: /* pws is not present */ + cache->elm_map[oid].encstat[0] = + SES_OBJSTAT_NOTINSTALLED; + cache->elm_map[oid].encstat[3] = 0; + cache->enc_status |= SES_ENCSTAT_INFO; + break; + case 0x21: /* pws is present */ + /* + * This is for enclosures that cannot tell whether the + * device is on or malfunctioning, but know that it is + * present. Just fall through. + */ + /* FALLTHROUGH */ + case 0x80: /* Unknown or Not Reportable Status */ + cache->elm_map[oid].encstat[0] = SES_OBJSTAT_UNKNOWN; + cache->elm_map[oid].encstat[3] = 0; + cache->enc_status |= SES_ENCSTAT_INFO; + break; + default: + ENC_LOG(ssc, "unknown power supply %d status (0x%x)\n", + i, sdata[r] & 0xff); + break; + } + ssc->enc_cache.elm_map[oid++].svalid = 1; + r++; + } + + /* + * Skip over Slot SCSI IDs + */ + r += cc->Nslots; + + /* + * We always have doorlock status, no matter what, + * but we only save the status if we have one. + */ + SAFT_BAIL(r, hiwater, sdata); + if (cc->DoorLock) { + /* + * 0 = Door Locked + * 1 = Door Unlocked, or no Lock Installed + * 0x80 = Unknown or Not Reportable Status + */ + cache->elm_map[oid].encstat[1] = 0; + cache->elm_map[oid].encstat[2] = 0; + switch ((uint8_t)sdata[r]) { + case 0: + cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK; + cache->elm_map[oid].encstat[3] = 0; + break; + case 1: + cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK; + cache->elm_map[oid].encstat[3] = 1; + break; + case 0x80: + cache->elm_map[oid].encstat[0] = SES_OBJSTAT_UNKNOWN; + cache->elm_map[oid].encstat[3] = 0; + cache->enc_status |= SES_ENCSTAT_INFO; + break; + default: + cache->elm_map[oid].encstat[0] = + SES_OBJSTAT_UNSUPPORTED; + ENC_LOG(ssc, "unknown lock status 0x%x\n", + sdata[r] & 0xff); + break; + } + cache->elm_map[oid++].svalid = 1; + } + r++; + + /* + * We always have speaker status, no matter what, + * but we only save the status if we have one. + */ + SAFT_BAIL(r, hiwater, sdata); + if (cc->Nspkrs) { + cache->elm_map[oid].encstat[1] = 0; + cache->elm_map[oid].encstat[2] = 0; + if (sdata[r] == 1) { + /* + * We need to cache tone urgency indicators. + * Someday. + */ + cache->elm_map[oid].encstat[0] = SES_OBJSTAT_NONCRIT; + cache->elm_map[oid].encstat[3] = 0x8; + cache->enc_status |= SES_ENCSTAT_NONCRITICAL; + } else if (sdata[r] == 0) { + cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK; + cache->elm_map[oid].encstat[3] = 0; + } else { + cache->elm_map[oid].encstat[0] = SES_OBJSTAT_UNSUPPORTED; + cache->elm_map[oid].encstat[3] = 0; + ENC_LOG(ssc, "unknown spkr status 0x%x\n", + sdata[r] & 0xff); + } + cache->elm_map[oid++].svalid = 1; + } + r++; + + for (i = 0; i < cc->Ntherm; i++) { + SAFT_BAIL(r, hiwater, sdata); + /* + * Status is a range from -10 to 245 deg Celsius, + * which we need to normalize to -20 to -245 according + * to the latest SCSI spec, which makes little + * sense since this would overflow an 8bit value. + * Well, still, the base normalization is -20, + * not -10, so we have to adjust. + * + * So what's over and under temperature? + * Hmm- we'll state that 'normal' operating + * is 10 to 40 deg Celsius. + */ + + /* + * Actually.... All of the units that people out in the world + * seem to have do not come even close to setting a value that + * complies with this spec. + * + * The closest explanation I could find was in an + * LSI-Logic manual, which seemed to indicate that + * this value would be set by whatever the I2C code + * would interpolate from the output of an LM75 + * temperature sensor. + * + * This means that it is impossible to use the actual + * numeric value to predict anything. But we don't want + * to lose the value. So, we'll propagate the *uncorrected* + * value and set SES_OBJSTAT_NOTAVAIL. We'll depend on the + * temperature flags for warnings. + */ + cache->elm_map[oid].encstat[0] = SES_OBJSTAT_NOTAVAIL; + cache->elm_map[oid].encstat[1] = 0; + cache->elm_map[oid].encstat[2] = sdata[r]; + cache->elm_map[oid].encstat[3] = 0; + cache->elm_map[oid++].svalid = 1; + r++; + } + + /* + * Now, for "pseudo" thermometers, we have two bytes + * of information in enclosure status- 16 bits. Actually, + * the MSB is a single TEMP ALERT flag indicating whether + * any other bits are set, but, thanks to fuzzy thinking, + * in the SAF-TE spec, this can also be set even if no + * other bits are set, thus making this really another + * binary temperature sensor. + */ + + SAFT_BAIL(r, hiwater, sdata); + tempflags = sdata[r++]; + SAFT_BAIL(r, hiwater, sdata); + tempflags |= (tempflags << 8) | sdata[r++]; + + for (i = 0; i < NPSEUDO_THERM; i++) { + cache->elm_map[oid].encstat[1] = 0; + if (tempflags & (1 << (NPSEUDO_THERM - i - 1))) { + cache->elm_map[oid].encstat[0] = SES_OBJSTAT_CRIT; + cache->elm_map[4].encstat[2] = 0xff; + /* + * Set 'over temperature' failure. + */ + cache->elm_map[oid].encstat[3] = 8; + cache->enc_status |= SES_ENCSTAT_CRITICAL; + } else { + /* + * We used to say 'not available' and synthesize a + * nominal 30 deg (C)- that was wrong. Actually, + * Just say 'OK', and use the reserved value of + * zero. + */ + cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK; + cache->elm_map[oid].encstat[2] = 0; + cache->elm_map[oid].encstat[3] = 0; + } + cache->elm_map[oid++].svalid = 1; + } + + /* + * Get alarm status. + */ + cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK; + cache->elm_map[oid].encstat[3] = cache->elm_map[oid].priv; + cache->elm_map[oid++].svalid = 1; + + /* + * Now get drive slot status + */ + cdb[2] = SAFTE_RD_RDDSTS; + amt = buflen; + err = enc_runcmd(ssc, cdb, 10, sdata, &amt); + if (err) { + ENC_FREE(sdata); + return (err); + } + hiwater = buflen - amt; + for (r = i = 0; i < cc->Nslots; i++, r += 4) { + SAFT_BAIL(r+3, hiwater, sdata); + cache->elm_map[oid].encstat[0] = SES_OBJSTAT_UNSUPPORTED; + cache->elm_map[oid].encstat[1] = (uint8_t) i; + cache->elm_map[oid].encstat[2] = 0; + cache->elm_map[oid].encstat[3] = 0; + status = sdata[r+3]; + if ((status & 0x1) == 0) { /* no device */ + cache->elm_map[oid].encstat[0] = + SES_OBJSTAT_NOTINSTALLED; + } else { + cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK; + } + if (status & 0x2) { + cache->elm_map[oid].encstat[2] = 0x8; + } + if ((status & 0x4) == 0) { + cache->elm_map[oid].encstat[3] = 0x10; + } + cache->elm_map[oid++].svalid = 1; + } + /* see comment below about sticky enclosure status */ + cache->enc_status |= ENCI_SVALID | oencstat; + ENC_FREE(sdata); + return (0); +} + +static int +set_elm_status_sel(enc_softc_t *ssc, encioc_elm_status_t *elms, int slp) +{ + int idx; + enc_element_t *ep; + struct scfg *cc = ssc->enc_private; + + if (cc == NULL) + return (0); + + idx = (int)elms->elm_idx; + ep = &ssc->enc_cache.elm_map[idx]; + + switch (ep->enctype) { + case ELMTYP_DEVICE: + if (elms->cstat[0] & SESCTL_PRDFAIL) { + ep->priv |= 0x40; + } + /* SESCTL_RSTSWAP has no correspondence in SAF-TE */ + if (elms->cstat[0] & SESCTL_DISABLE) { + ep->priv |= 0x80; + /* + * Hmm. Try to set the 'No Drive' flag. + * Maybe that will count as a 'disable'. + */ + } + if (ep->priv & 0xc6) { + ep->priv &= ~0x1; + } else { + ep->priv |= 0x1; /* no errors */ + } + wrslot_stat(ssc, slp); + break; + case ELMTYP_POWER: + /* + * Okay- the only one that makes sense here is to + * do the 'disable' for a power supply. + */ + if (elms->cstat[0] & SESCTL_DISABLE) { + (void) wrbuf16(ssc, SAFTE_WT_ACTPWS, + idx - cc->pwroff, 0, 0, slp); + } + break; + case ELMTYP_FAN: + /* + * Okay- the only one that makes sense here is to + * set fan speed to zero on disable. + */ + if (elms->cstat[0] & SESCTL_DISABLE) { + /* remember- fans are the first items, so idx works */ + (void) wrbuf16(ssc, SAFTE_WT_FANSPD, idx, 0, 0, slp); + } + break; + case ELMTYP_DOORLOCK: + /* + * Well, we can 'disable' the lock. + */ + if (elms->cstat[0] & SESCTL_DISABLE) { + cc->flag2 &= ~SAFT_FLG2_LOCKDOOR; + (void) wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, + cc->flag2, 0, slp); + } + break; + case ELMTYP_ALARM: + /* + * Well, we can 'disable' the alarm. + */ + if (elms->cstat[0] & SESCTL_DISABLE) { + cc->flag2 &= ~SAFT_FLG1_ALARM; + ep->priv |= 0x40; /* Muted */ + (void) wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, + cc->flag2, 0, slp); + } + break; + default: + break; + } + ep->svalid = 0; + return (0); +} + +/* + * This function handles all of the 16 byte WRITE BUFFER commands. + */ +static int +wrbuf16(enc_softc_t *ssc, uint8_t op, uint8_t b1, uint8_t b2, + uint8_t b3, int slp) +{ + int err, amt; + char *sdata; + struct scfg *cc = ssc->enc_private; + static char cdb[10] = { WRITE_BUFFER, 1, 0, 0, 0, 0, 0, 0, 16, 0 }; + + if (cc == NULL) + return (0); + + sdata = ENC_MALLOCZ(16); + if (sdata == NULL) + return (ENOMEM); + + ENC_DLOG(ssc, "saf_wrbuf16 %x %x %x %x\n", op, b1, b2, b3); + + sdata[0] = op; + sdata[1] = b1; + sdata[2] = b2; + sdata[3] = b3; + amt = -16; + err = enc_runcmd(ssc, cdb, 10, sdata, &amt); + ENC_FREE(sdata); + return (err); +} + +/* + * This function updates the status byte for the device slot described. + * + * Since this is an optional SAF-TE command, there's no point in + * returning an error. + */ +static void +wrslot_stat(enc_softc_t *ssc, int slp) +{ + int i, amt; + enc_element_t *ep; + char cdb[10], *sdata; + struct scfg *cc = ssc->enc_private; + + if (cc == NULL) + return; + + ENC_DLOG(ssc, "saf_wrslot\n"); + cdb[0] = WRITE_BUFFER; + cdb[1] = 1; + cdb[2] = 0; + cdb[3] = 0; + cdb[4] = 0; + cdb[5] = 0; + cdb[6] = 0; + cdb[7] = 0; + cdb[8] = cc->Nslots * 3 + 1; + cdb[9] = 0; + + sdata = ENC_MALLOCZ(cc->Nslots * 3 + 1); + if (sdata == NULL) + return; + + sdata[0] = SAFTE_WT_DSTAT; + for (i = 0; i < cc->Nslots; i++) { + ep = &ssc->enc_cache.elm_map[cc->slotoff + i]; + ENC_DLOG(ssc, "saf_wrslot %d <- %x\n", i, ep->priv & 0xff); + sdata[1 + (3 * i)] = ep->priv & 0xff; + } + amt = -(cc->Nslots * 3 + 1); + (void) enc_runcmd(ssc, cdb, 10, sdata, &amt); + ENC_FREE(sdata); +} + +/* + * This function issues the "PERFORM SLOT OPERATION" command. + */ +static int +perf_slotop(enc_softc_t *ssc, uint8_t slot, uint8_t opflag, int slp) +{ + int err, amt; + char *sdata; + struct scfg *cc = ssc->enc_private; + static char cdb[10] = + { WRITE_BUFFER, 1, 0, 0, 0, 0, 0, 0, SAFT_SCRATCH, 0 }; + + if (cc == NULL) + return (0); + + sdata = ENC_MALLOCZ(SAFT_SCRATCH); + if (sdata == NULL) + return (ENOMEM); + + sdata[0] = SAFTE_WT_SLTOP; + sdata[1] = slot; + sdata[2] = opflag; + ENC_DLOG(ssc, "saf_slotop slot %d op %x\n", slot, opflag); + amt = -SAFT_SCRATCH; + err = enc_runcmd(ssc, cdb, 10, sdata, &amt); + ENC_FREE(sdata); + return (err); +} + +static void +safte_softc_cleanup(struct cam_periph *periph) +{ + enc_softc_t *ssc; + + ssc = periph->softc; + ENC_FREE_AND_NULL(ssc->enc_cache.elm_map); + ENC_FREE_AND_NULL(ssc->enc_private); + ssc->enc_cache.nelms = 0; +} + +static int +safte_init_enc(enc_softc_t *ssc) +{ + int err; + static char cdb0[6] = { SEND_DIAGNOSTIC }; + + err = enc_runcmd(ssc, cdb0, 6, NULL, 0); + if (err) { + return (err); + } + DELAY(5000); + err = wrbuf16(ssc, SAFTE_WT_GLOBAL, 0, 0, 0, 1); + return (err); +} + +static int +safte_get_enc_status(enc_softc_t *ssc, int slpflg) +{ + return (safte_rdstat(ssc, slpflg)); +} + +static int +safte_set_enc_status(enc_softc_t *ssc, uint8_t encstat, int slpflg) +{ + struct scfg *cc = ssc->enc_private; + if (cc == NULL) + return (0); + /* + * Since SAF-TE devices aren't necessarily sticky in terms + * of state, make our soft copy of enclosure status 'sticky'- + * that is, things set in enclosure status stay set (as implied + * by conditions set in reading object status) until cleared. + */ + ssc->enc_cache.enc_status &= ~ALL_ENC_STAT; + ssc->enc_cache.enc_status |= (encstat & ALL_ENC_STAT); + ssc->enc_cache.enc_status |= ENCI_SVALID; + cc->flag1 &= ~(SAFT_FLG1_ALARM|SAFT_FLG1_GLOBFAIL|SAFT_FLG1_GLOBWARN); + if ((encstat & (SES_ENCSTAT_CRITICAL|SES_ENCSTAT_UNRECOV)) != 0) { + cc->flag1 |= SAFT_FLG1_ALARM|SAFT_FLG1_GLOBFAIL; + } else if ((encstat & SES_ENCSTAT_NONCRITICAL) != 0) { + cc->flag1 |= SAFT_FLG1_GLOBWARN; + } + return (wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, cc->flag2, 0, slpflg)); +} + +static int +safte_get_elm_status(enc_softc_t *ssc, encioc_elm_status_t *elms, int slpflg) +{ + int i = (int)elms->elm_idx; + + if ((ssc->enc_cache.enc_status & ENCI_SVALID) == 0 || + (ssc->enc_cache.elm_map[i].svalid) == 0) { + int err = safte_rdstat(ssc, slpflg); + if (err) + return (err); + } + elms->cstat[0] = ssc->enc_cache.elm_map[i].encstat[0]; + elms->cstat[1] = ssc->enc_cache.elm_map[i].encstat[1]; + elms->cstat[2] = ssc->enc_cache.elm_map[i].encstat[2]; + elms->cstat[3] = ssc->enc_cache.elm_map[i].encstat[3]; + return (0); +} + + +static int +safte_set_elm_status(enc_softc_t *ssc, encioc_elm_status_t *elms, int slp) +{ + int idx, err; + enc_element_t *ep; + struct scfg *cc; + + + ENC_DLOG(ssc, "safte_set_objstat(%d): %x %x %x %x\n", + (int)elms->elm_idx, elms->cstat[0], elms->cstat[1], elms->cstat[2], + elms->cstat[3]); + + /* + * If this is clear, we don't do diddly. + */ + if ((elms->cstat[0] & SESCTL_CSEL) == 0) { + return (0); + } + + err = 0; + /* + * Check to see if the common bits are set and do them first. + */ + if (elms->cstat[0] & ~SESCTL_CSEL) { + err = set_elm_status_sel(ssc, elms, slp); + if (err) + return (err); + } + + cc = ssc->enc_private; + if (cc == NULL) + return (0); + + idx = (int)elms->elm_idx; + ep = &ssc->enc_cache.elm_map[idx]; + + switch (ep->enctype) { + case ELMTYP_DEVICE: + { + uint8_t slotop = 0; + /* + * XXX: I should probably cache the previous state + * XXX: of SESCTL_DEVOFF so that when it goes from + * XXX: true to false I can then set PREPARE FOR OPERATION + * XXX: flag in PERFORM SLOT OPERATION write buffer command. + */ + if (elms->cstat[2] & (SESCTL_RQSINS|SESCTL_RQSRMV)) { + slotop |= 0x2; + } + if (elms->cstat[2] & SESCTL_RQSID) { + slotop |= 0x4; + } + err = perf_slotop(ssc, (uint8_t) idx - (uint8_t) cc->slotoff, + slotop, slp); + if (err) + return (err); + if (elms->cstat[3] & SESCTL_RQSFLT) { + ep->priv |= 0x2; + } else { + ep->priv &= ~0x2; + } + if (ep->priv & 0xc6) { + ep->priv &= ~0x1; + } else { + ep->priv |= 0x1; /* no errors */ + } + wrslot_stat(ssc, slp); + break; + } + case ELMTYP_POWER: + if (elms->cstat[3] & SESCTL_RQSTFAIL) { + cc->flag1 |= SAFT_FLG1_ENCPWRFAIL; + } else { + cc->flag1 &= ~SAFT_FLG1_ENCPWRFAIL; + } + err = wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, + cc->flag2, 0, slp); + if (err) + return (err); + if (elms->cstat[3] & SESCTL_RQSTON) { + (void) wrbuf16(ssc, SAFTE_WT_ACTPWS, + idx - cc->pwroff, 0, 0, slp); + } else { + (void) wrbuf16(ssc, SAFTE_WT_ACTPWS, + idx - cc->pwroff, 0, 1, slp); + } + break; + case ELMTYP_FAN: + if (elms->cstat[3] & SESCTL_RQSTFAIL) { + cc->flag1 |= SAFT_FLG1_ENCFANFAIL; + } else { + cc->flag1 &= ~SAFT_FLG1_ENCFANFAIL; + } + err = wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, + cc->flag2, 0, slp); + if (err) + return (err); + if (elms->cstat[3] & SESCTL_RQSTON) { + uint8_t fsp; + if ((elms->cstat[3] & 0x7) == 7) { + fsp = 4; + } else if ((elms->cstat[3] & 0x7) == 6) { + fsp = 3; + } else if ((elms->cstat[3] & 0x7) == 4) { + fsp = 2; + } else { + fsp = 1; + } + (void) wrbuf16(ssc, SAFTE_WT_FANSPD, idx, fsp, 0, slp); + } else { + (void) wrbuf16(ssc, SAFTE_WT_FANSPD, idx, 0, 0, slp); + } + break; + case ELMTYP_DOORLOCK: + if (elms->cstat[3] & 0x1) { + cc->flag2 &= ~SAFT_FLG2_LOCKDOOR; + } else { + cc->flag2 |= SAFT_FLG2_LOCKDOOR; + } + (void) wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, + cc->flag2, 0, slp); + break; + case ELMTYP_ALARM: + /* + * On all nonzero but the 'muted' bit, we turn on the alarm, + */ + elms->cstat[3] &= ~0xa; + if (elms->cstat[3] & 0x40) { + cc->flag2 &= ~SAFT_FLG1_ALARM; + } else if (elms->cstat[3] != 0) { + cc->flag2 |= SAFT_FLG1_ALARM; + } else { + cc->flag2 &= ~SAFT_FLG1_ALARM; + } + ep->priv = elms->cstat[3]; + (void) wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, + cc->flag2, 0, slp); + break; + default: + break; + } + ep->svalid = 0; + return (0); +} + +static struct enc_vec safte_enc_vec = +{ + .softc_cleanup = safte_softc_cleanup, + .init_enc = safte_init_enc, + .get_enc_status = safte_get_enc_status, + .set_enc_status = safte_set_enc_status, + .get_elm_status = safte_get_elm_status, + .set_elm_status = safte_set_elm_status +}; + +int +safte_softc_init(enc_softc_t *ssc, int doinit) +{ + int err, i, r; + struct scfg *cc; + + if (doinit == 0) { + safte_softc_cleanup(ssc->periph); + return (0); + } + + ssc->enc_vec = safte_enc_vec; + + if (ssc->enc_private == NULL) { + ssc->enc_private = ENC_MALLOCZ(SAFT_PRIVATE); + if (ssc->enc_private == NULL) { + return (ENOMEM); + } + } + + ssc->enc_cache.nelms = 0; + ssc->enc_cache.enc_status = 0; + + if ((err = safte_getconfig(ssc)) != 0) { + return (err); + } + + /* + * The number of objects here, as well as that reported by the + * READ_BUFFER/GET_CONFIG call, are the over-temperature flags (15) + * that get reported during READ_BUFFER/READ_ENC_STATUS. + */ + cc = ssc->enc_private; + ssc->enc_cache.nelms = cc->Nfans + cc->Npwr + cc->Nslots + + cc->DoorLock + cc->Ntherm + cc->Nspkrs + NPSEUDO_THERM + + NPSEUDO_ALARM; + ssc->enc_cache.elm_map = + ENC_MALLOCZ(ssc->enc_cache.nelms * sizeof(enc_element_t)); + if (ssc->enc_cache.elm_map == NULL) { + return (ENOMEM); + } + + r = 0; + /* + * Note that this is all arranged for the convenience + * in later fetches of status. + */ + for (i = 0; i < cc->Nfans; i++) + ssc->enc_cache.elm_map[r++].enctype = ELMTYP_FAN; + cc->pwroff = (uint8_t) r; + for (i = 0; i < cc->Npwr; i++) + ssc->enc_cache.elm_map[r++].enctype = ELMTYP_POWER; + for (i = 0; i < cc->DoorLock; i++) + ssc->enc_cache.elm_map[r++].enctype = ELMTYP_DOORLOCK; + for (i = 0; i < cc->Nspkrs; i++) + ssc->enc_cache.elm_map[r++].enctype = ELMTYP_ALARM; + for (i = 0; i < cc->Ntherm; i++) + ssc->enc_cache.elm_map[r++].enctype = ELMTYP_THERM; + for (i = 0; i < NPSEUDO_THERM; i++) + ssc->enc_cache.elm_map[r++].enctype = ELMTYP_THERM; + ssc->enc_cache.elm_map[r++].enctype = ELMTYP_ALARM; + cc->slotoff = (uint8_t) r; + for (i = 0; i < cc->Nslots; i++) + ssc->enc_cache.elm_map[r++].enctype = ELMTYP_DEVICE; + return (0); +} + diff --git a/sys/cam/scsi/scsi_enc_ses.c b/sys/cam/scsi/scsi_enc_ses.c new file mode 100644 index 00000000000..852a387d1c5 --- /dev/null +++ b/sys/cam/scsi/scsi_enc_ses.c @@ -0,0 +1,2703 @@ +/*- + * Copyright (c) 2000 Matthew Jacob + * Copyright (c) 2010 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/** + * \file scsi_enc_ses.c + * + * Structures and routines specific && private to SES only + */ + +#include +__FBSDID("$FreeBSD: head/sys/cam/scsi/scsi_ses.c 201758 2010-01-07 21:01:37Z mbr $"); + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +/* SES Native Type Device Support */ + +/* SES Diagnostic Page Codes */ +typedef enum { + SesConfigPage = 0x1, + SesControlPage = 0x2, + SesStatusPage = SesControlPage, + SesHelpTxt = 0x3, + SesStringOut = 0x4, + SesStringIn = SesStringOut, + SesThresholdOut = 0x5, + SesThresholdIn = SesThresholdOut, + SesArrayControl = 0x6, /* Obsolete in SES v2 */ + SesArrayStatus = SesArrayControl, + SesElementDescriptor = 0x7, + SesShortStatus = 0x8, + SesEnclosureBusy = 0x9, + SesAddlElementStatus = 0xa +} SesDiagPageCodes; + +typedef struct ses_type { + const struct ses_elm_type_desc *hdr; + const char *text; +} ses_type_t; + +typedef struct ses_comstat { + uint8_t comstatus; + uint8_t comstat[3]; +} ses_comstat_t; + +typedef union ses_addl_data { + struct ses_elm_sas_device_phy *sasdev_phys; + struct ses_elm_sas_expander_phy *sasexp_phys; + struct ses_elm_sas_port_phy *sasport_phys; + struct ses_fcobj_port *fc_ports; +} ses_add_data_t; + +typedef struct ses_addl_status { + struct ses_elm_addlstatus_base_hdr *hdr; + union { + union ses_fcobj_hdr *fc; + union ses_elm_sas_hdr *sas; + } proto_hdr; + union ses_addl_data proto_data; /* array sizes stored in header */ +} ses_add_status_t; + +typedef struct ses_element { + uint8_t eip; /* eip bit is set */ + uint16_t descr_len; /* length of the descriptor */ + char *descr; /* descriptor for this object */ + struct ses_addl_status addl; /* additional status info */ +} ses_element_t; + +typedef struct ses_control_request { + int elm_idx; + ses_comstat_t elm_stat; + int result; + TAILQ_ENTRY(ses_control_request) links; +} ses_control_request_t; +TAILQ_HEAD(ses_control_reqlist, ses_control_request); +typedef struct ses_control_reqlist ses_control_reqlist_t; +enum { + SES_SETSTATUS_ENC_IDX = -1 +}; + +static void +ses_terminate_control_requests(ses_control_reqlist_t *reqlist, int result) +{ + ses_control_request_t *req; + + while ((req = TAILQ_FIRST(reqlist)) != NULL) { + TAILQ_REMOVE(reqlist, req, links); + req->result = result; + wakeup(req); + } +} + +enum ses_iter_index_values { + /** + * \brief Value of an initialized but invalid index + * in a ses_iterator object. + * + * This value is used for the individual_element_index of + * overal status elements and for all index types when + * an iterator is first initialized. + */ + ITERATOR_INDEX_INVALID = -1, + + /** + * \brief Value of an index in a ses_iterator object + * when the iterator has traversed past the last + * valid element.. + */ + ITERATOR_INDEX_END = INT_MAX +}; + +/** + * \brief Structure encapsulating all data necessary to traverse the + * elements of a SES configuration. + * + * The ses_iterator object simplifies the task of iterating through all + * elements detected via the SES configuration page by tracking the numerous + * element indexes that, instead of memoizing in the softc, we calculate + * on the fly during the traversal of the element objects. The various + * indexes are necessary due to the varying needs of matching objects in + * the different SES pages. Some pages (e.g. Status/Control) contain all + * elements, while others (e.g. Additional Element Status) only contain + * individual elements (no overal status elements) of particular types. + * + * To use an iterator, initialize it with ses_iter_init(), and then + * use ses_iter_next() to traverse the elements (including the first) in + * the configuration. Once an iterator is initiailized with ses_iter_init(), + * you may also seek to any particular element by either it's global or + * individual element index via the ses_iter_seek_to() function. You may + * also return an iterator to the position just before the first element + * (i.e. the same state as after an ses_iter_init()), with ses_iter_reset(). + */ +struct ses_iterator { + /** + * \brief Backlink to the overal software configuration structure. + * + * This is included for convenience so the iteration functions + * need only take a single, struct ses_iterator *, argument. + */ + enc_softc_t *enc; + + enc_cache_t *cache; + + /** + * \brief Index of the type of the current element within the + * ses_cache's ses_types array. + */ + int type_index; + + /** + * \brief The position (0 based) of this element relative to all other + * elements of this type. + * + * This index resets to zero every time the iterator transitions + * to elements of a new type in the configuration. + */ + int type_element_index; + + /** + * \brief The position (0 based) of this element relative to all + * other individual status elements in the configuration. + * + * This index ranges from 0 through the number of individual + * elements in the configuration. When the iterator returns + * an overall status element, individual_element_index is + * set to ITERATOR_INDEX_INVALID, to indicate that it does + * not apply to the current element. + */ + int individual_element_index; + + /** + * \brief The position (0 based) of this element relative to + * all elements in the configration. + * + * This index is appropriate for indexing into enc->ses_elm_map. + */ + int global_element_index; + + /** + * \brief The last valid individual element index of this + * iterator. + * + * When an iterator traverses an overal status element, the + * individual element index is reset to ITERATOR_INDEX_INVALID + * to prevent unintential use of the individual_element_index + * field. The saved_individual_element_index allows the iterator + * to restore it's position in the individual elements upon + * reaching the next individual element. + */ + int saved_individual_element_index; +}; + +typedef enum { + SES_UPDATE_NONE, + SES_UPDATE_GETCONFIG, + SES_UPDATE_GETSTATUS, + SES_UPDATE_GETELMDESCS, + SES_UPDATE_GETELMADDLSTATUS, + SES_PROCESS_CONTROL_REQS, + SES_PUBLISH_PHYSPATHS, + SES_PUBLISH_CACHE, + SES_NUM_UPDATE_STATES +} ses_update_action; + +static enc_softc_cleanup_t ses_softc_cleanup; + +#define SCSZ 0x8000 + +static fsm_fill_handler_t ses_fill_rcv_diag_csio; +static fsm_fill_handler_t ses_fill_control_request; +static fsm_done_handler_t ses_process_config; +static fsm_done_handler_t ses_process_status; +static fsm_done_handler_t ses_process_elm_descs; +static fsm_done_handler_t ses_process_elm_addlstatus; +static fsm_done_handler_t ses_process_control_request; +static fsm_done_handler_t ses_publish_physpaths; +static fsm_done_handler_t ses_publish_cache; + +struct enc_fsm_state enc_fsm_states[SES_NUM_UPDATE_STATES] = +{ + { "SES_UPDATE_NONE", 0, 0, 0, NULL, NULL, NULL }, + { + "SES_UPDATE_GETCONFIG", + SesConfigPage, + SCSZ, + 60 * 1000, + ses_fill_rcv_diag_csio, + ses_process_config, + enc_error + }, + { + "SES_UPDATE_GETSTATUS", + SesStatusPage, + SCSZ, + 60 * 1000, + ses_fill_rcv_diag_csio, + ses_process_status, + enc_error + }, + { + "SES_UPDATE_GETELMDESCS", + SesElementDescriptor, + SCSZ, + 60 * 1000, + ses_fill_rcv_diag_csio, + ses_process_elm_descs, + enc_error + }, + { + "SES_UPDATE_GETELMADDLSTATUS", + SesAddlElementStatus, + SCSZ, + 60 * 1000, + ses_fill_rcv_diag_csio, + ses_process_elm_addlstatus, + enc_error + }, + { + "SES_PROCESS_CONTROL_REQS", + SesControlPage, + SCSZ, + 60 * 1000, + ses_fill_control_request, + ses_process_control_request, + enc_error + }, + { + "SES_PUBLISH_PHYSPATHS", + 0, + 0, + 0, + NULL, + ses_publish_physpaths, + NULL + }, + { + "SES_PUBLISH_CACHE", + 0, + 0, + 0, + NULL, + ses_publish_cache, + NULL + } +}; + +typedef struct ses_cache { + /* Source for all the configuration data pointers */ + const struct ses_cfg_page *cfg_page; + + /* References into the config page. */ + const struct ses_enc_desc * const *subencs; + uint8_t ses_ntypes; + const ses_type_t *ses_types; + + /* Source for all the status pointers */ + const struct ses_status_page *status_page; + + /* Source for all the object descriptor pointers */ + const struct ses_elem_descr_page *elm_descs_page; + + /* Source for all the additional object status pointers */ + const struct ses_addl_elem_status_page *elm_addlstatus_page; + +} ses_cache_t; + +typedef struct ses_softc { + uint32_t ses_flags; +#define SES_FLAG_TIMEDCOMP 0x01 + + ses_control_reqlist_t ses_requests; + ses_control_reqlist_t ses_pending_requests; +} ses_softc_t; + +/** + * \brief Reset a SES iterator to just before the first element + * in the configuration. + * + * \param iter The iterator object to reset. + * + * The indexes within a reset iterator are invalid and will only + * become valid upon completion of a ses_iter_seek_to() or a + * ses_iter_next(). + */ +static void +ses_iter_reset(struct ses_iterator *iter) +{ + /* + * Set our indexes to just before the first valid element + * of the first type (ITERATOR_INDEX_INVALID == -1). This + * simplifies the implementation of ses_iter_next(). + */ + iter->type_index = 0; + iter->type_element_index = ITERATOR_INDEX_INVALID; + iter->global_element_index = ITERATOR_INDEX_INVALID; + iter->individual_element_index = ITERATOR_INDEX_INVALID; + iter->saved_individual_element_index = ITERATOR_INDEX_INVALID; +} + +/** + * \brief Initialize the storage of a SES iterator and reset it to + * the position just before the first element of the + * configuration. + * + * \param enc The SES softc for the SES instance whose configuration + * will be enumerated by this iterator. + * \param iter The iterator object to initialize. + */ +static void +ses_iter_init(enc_softc_t *enc, enc_cache_t *cache, struct ses_iterator *iter) +{ + iter->enc = enc; + iter->cache = cache; + ses_iter_reset(iter); +} + +/** + * \brief Traverse the provided SES iterator to the next element + * within the configuraiton. + * + * \param iter The iterator to move. + * + * \return If a valid next element exists, a pointer to it's enc_element_t. + * Otherwise NULL. + */ +static enc_element_t * +ses_iter_next(struct ses_iterator *iter) +{ + ses_cache_t *ses_cache; + const ses_type_t *element_type; + + ses_cache = iter->cache->private; + + /* + * Note: Treat nelms as signed, so we will hit this case + * and immediately terminate the iteration if the + * configuration has 0 objects. + */ + if (iter->global_element_index >= (int)iter->cache->nelms - 1) { + + /* Elements exhausted. */ + iter->type_index = ITERATOR_INDEX_END; + iter->type_element_index = ITERATOR_INDEX_END; + iter->global_element_index = ITERATOR_INDEX_END; + iter->individual_element_index = ITERATOR_INDEX_END; + return (NULL); + } + + KASSERT((iter->type_index < ses_cache->ses_ntypes), + ("Corrupted element iterator. %d not less than %d", + iter->type_index, ses_cache->ses_ntypes)); + + element_type = &ses_cache->ses_types[iter->type_index]; + iter->global_element_index++; + iter->type_element_index++; + + /* + * There is an object for overal type status in addition + * to one for each allowed element, but only if the element + * count is non-zero. + */ + if (iter->type_element_index > element_type->hdr->etype_maxelt) { + + /* + * We've exhausted the elements of this type. + * This next element belongs to the next type. + */ + iter->type_index++; + iter->type_element_index = 0; + iter->saved_individual_element_index + = iter->individual_element_index; + iter->individual_element_index = ITERATOR_INDEX_INVALID; + } + + if (iter->type_element_index > 0) { + if (iter->type_element_index == 1) { + iter->individual_element_index + = iter->saved_individual_element_index; + } + iter->individual_element_index++; + } + + return (&iter->cache->elm_map[iter->global_element_index]); +} + +/** + * Element index types tracked by a SES iterator. + */ +typedef enum { + /** + * Index relative to all elements (overall and individual) + * in the system. + */ + SES_ELEM_INDEX_GLOBAL, + + /** + * \brief Index relative to all individual elements in the system. + * + * This index counts only individual elements, skipping overall + * status elements. This is the index space of the additional + * element status page (page 0xa). + */ + SES_ELEM_INDEX_INDIVIDUAL +} ses_elem_index_type_t; + +/** + * \brief Move the provided iterator forwards or backwards to the object + * having the give index. + * + * \param iter The iterator on which to perform the seek. + * \param element_index The index of the element to find. + * \param index_type The type (global or individual) of element_index. + * + * \return If the element is found, a pointer to it's enc_element_t. + * Otherwise NULL. + */ +static enc_element_t * +ses_iter_seek_to(struct ses_iterator *iter, int element_index, + ses_elem_index_type_t index_type) +{ + enc_element_t *element; + int *cur_index; + + if (index_type == SES_ELEM_INDEX_GLOBAL) + cur_index = &iter->global_element_index; + else + cur_index = &iter->individual_element_index; + + if (*cur_index == element_index) { + /* Already there. */ + return (&iter->cache->elm_map[iter->global_element_index]); + } + + ses_iter_reset(iter); + while ((element = ses_iter_next(iter)) != NULL + && *cur_index != element_index) + ; + + if (*cur_index != element_index) + return (NULL); + + return (element); +} + +#if 0 +static int ses_encode(enc_softc_t *, uint8_t *, int, int, + struct ses_comstat *); +#endif +static int ses_set_timed_completion(enc_softc_t *, uint8_t); +#if 0 +static int ses_putstatus(enc_softc_t *, int, struct ses_comstat *); +#endif + +static void ses_print_addl_data(enc_softc_t *, enc_element_t *); + +/*=========================== SES cleanup routines ===========================*/ + +static void +ses_cache_free_elm_addlstatus(enc_softc_t *enc, enc_cache_t *cache) +{ + ses_cache_t *ses_cache; + ses_cache_t *other_ses_cache; + enc_element_t *cur_elm; + enc_element_t *last_elm; + + ENC_DLOG(enc, "%s: enter\n", __func__); + ses_cache = cache->private; + if (ses_cache->elm_addlstatus_page == NULL) + return; + + for (cur_elm = cache->elm_map, + last_elm = &cache->elm_map[cache->nelms - 1]; + cur_elm <= last_elm; cur_elm++) { + ses_element_t *elmpriv; + + elmpriv = cur_elm->elm_private; + + /* Clear references to the additional status page. */ + bzero(&elmpriv->addl, sizeof(elmpriv->addl)); + } + + other_ses_cache = enc_other_cache(enc, cache)->private; + if (other_ses_cache->elm_addlstatus_page + != ses_cache->elm_addlstatus_page) + ENC_FREE(ses_cache->elm_addlstatus_page); + ses_cache->elm_addlstatus_page = NULL; +} + +static void +ses_cache_free_elm_descs(enc_softc_t *enc, enc_cache_t *cache) +{ + ses_cache_t *ses_cache; + ses_cache_t *other_ses_cache; + enc_element_t *cur_elm; + enc_element_t *last_elm; + + ENC_DLOG(enc, "%s: enter\n", __func__); + ses_cache = cache->private; + if (ses_cache->elm_descs_page == NULL) + return; + + for (cur_elm = cache->elm_map, + last_elm = &cache->elm_map[cache->nelms - 1]; + cur_elm <= last_elm; cur_elm++) { + ses_element_t *elmpriv; + + elmpriv = cur_elm->elm_private; + elmpriv->descr_len = 0; + elmpriv->descr = NULL; + } + + other_ses_cache = enc_other_cache(enc, cache)->private; + if (other_ses_cache->elm_descs_page + != ses_cache->elm_descs_page) + ENC_FREE(ses_cache->elm_descs_page); + ses_cache->elm_descs_page = NULL; +} + +static void +ses_cache_free_status(enc_softc_t *enc, enc_cache_t *cache) +{ + ses_cache_t *ses_cache; + ses_cache_t *other_ses_cache; + + ENC_DLOG(enc, "%s: enter\n", __func__); + ses_cache = cache->private; + if (ses_cache->status_page == NULL) + return; + + other_ses_cache = enc_other_cache(enc, cache)->private; + if (other_ses_cache->status_page != ses_cache->status_page) + ENC_FREE(ses_cache->status_page); + ses_cache->status_page = NULL; +} + +static void +ses_cache_free_elm_map(enc_softc_t *enc, enc_cache_t *cache) +{ + enc_element_t *cur_elm; + enc_element_t *last_elm; + + ENC_DLOG(enc, "%s: enter\n", __func__); + if (cache->elm_map == NULL) + return; + + ses_cache_free_elm_descs(enc, cache); + ses_cache_free_elm_addlstatus(enc, cache); + for (cur_elm = cache->elm_map, + last_elm = &cache->elm_map[cache->nelms - 1]; + cur_elm <= last_elm; cur_elm++) { + + ENC_FREE_AND_NULL(cur_elm->elm_private); + } + ENC_FREE_AND_NULL(cache->elm_map); + cache->nelms = 0; + ENC_DLOG(enc, "%s: exit\n", __func__); +} + +static void +ses_cache_free(enc_softc_t *enc, enc_cache_t *cache) +{ + ses_cache_t *other_ses_cache; + ses_cache_t *ses_cache; + + ENC_DLOG(enc, "%s: enter\n", __func__); + ses_cache_free_elm_addlstatus(enc, cache); + ses_cache_free_status(enc, cache); + ses_cache_free_elm_map(enc, cache); + + ses_cache = cache->private; + ses_cache->ses_ntypes = 0; + + other_ses_cache = enc_other_cache(enc, cache)->private; + if (other_ses_cache->subencs != ses_cache->subencs) + ENC_FREE(ses_cache->subencs); + ses_cache->subencs = NULL; + + if (other_ses_cache->ses_types != ses_cache->ses_types) + ENC_FREE(ses_cache->ses_types); + ses_cache->ses_types = NULL; + + if (other_ses_cache->cfg_page != ses_cache->cfg_page) + ENC_FREE(ses_cache->cfg_page); + ses_cache->cfg_page = NULL; + + ENC_DLOG(enc, "%s: exit\n", __func__); +} + +static void +ses_cache_clone(enc_softc_t *enc, enc_cache_t *src, enc_cache_t *dst) +{ + ses_cache_t *dst_ses_cache; + ses_cache_t *src_ses_cache; + enc_element_t *src_elm; + enc_element_t *dst_elm; + enc_element_t *last_elm; + + ses_cache_free(enc, dst); + src_ses_cache = src->private; + dst_ses_cache = dst->private; + + /* + * The cloned enclosure cache and ses specific cache are + * mostly identical to the source. + */ + *dst = *src; + *dst_ses_cache = *src_ses_cache; + + /* + * But the ses cache storage is still independent. Restore + * the pointer that was clobbered by the structure copy above. + */ + dst->private = dst_ses_cache; + + /* + * The element map is independent even though it starts out + * pointing to the same constant page data. + */ + dst->elm_map = ENC_MALLOCZ(dst->nelms * sizeof(enc_element_t)); + memcpy(dst->elm_map, src->elm_map, dst->nelms * sizeof(enc_element_t)); + for (dst_elm = dst->elm_map, src_elm = src->elm_map, + last_elm = &src->elm_map[src->nelms - 1]; + src_elm <= last_elm; src_elm++, dst_elm++) { + + dst_elm->elm_private = ENC_MALLOCZ(sizeof(ses_element_t)); + memcpy(dst_elm->elm_private, src_elm->elm_private, + sizeof(ses_element_t)); + } +} + +/* Structure accessors. These are strongly typed to avoid errors. */ + +int +ses_elm_sas_descr_type(union ses_elm_sas_hdr *obj) +{ + return ((obj)->base_hdr.byte1 >> 6); +} +int +ses_elm_addlstatus_proto(struct ses_elm_addlstatus_base_hdr *hdr) +{ + return ((hdr)->byte0 & 0xf); +} +int +ses_elm_addlstatus_eip(struct ses_elm_addlstatus_base_hdr *hdr) +{ + return ((hdr)->byte0 >> 4) & 0x1; +} +int +ses_elm_addlstatus_invalid(struct ses_elm_addlstatus_base_hdr *hdr) +{ + return ((hdr)->byte0 >> 7); +} +int +ses_elm_sas_type0_not_all_phys(union ses_elm_sas_hdr *hdr) +{ + return ((hdr)->type0_noneip.byte1 & 0x1); +} +int +ses_elm_sas_dev_phy_sata_dev(struct ses_elm_sas_device_phy *phy) +{ + return ((phy)->target_ports & 0x1); +} +int +ses_elm_sas_dev_phy_sata_port(struct ses_elm_sas_device_phy *phy) +{ + return ((phy)->target_ports >> 7); +} +int +ses_elm_sas_dev_phy_dev_type(struct ses_elm_sas_device_phy *phy) +{ + return (((phy)->byte0 >> 4) & 0x7); +} + +/** + * \brief Verify that the cached configuration data in our softc + * is valid for processing the page data corresponding to + * the provided page header. + * + * \param ses_cache The SES cache to validate. + * \param gen_code The 4 byte generation code from a SES diagnostic + * page header. + * + * \return non-zero if true, 0 if false. + */ +static int +ses_config_cache_valid(ses_cache_t *ses_cache, const uint8_t *gen_code) +{ + uint32_t cache_gc; + uint32_t cur_gc; + + if (ses_cache->cfg_page == NULL) + return (0); + + cache_gc = scsi_4btoul(ses_cache->cfg_page->hdr.gen_code); + cur_gc = scsi_4btoul(gen_code); + return (cache_gc == cur_gc); +} + +/** + * Function signature for consumers of the ses_devids_iter() interface. + */ +typedef void ses_devid_callback_t(enc_softc_t *, enc_element_t *, + struct scsi_vpd_id_descriptor *, void *); + +/** + * \brief Iterate over and create vpd device id records from the + * additional element status data for elm, passing that data + * to the provided callback. + * + * \param enc SES instance containing elm + * \param elm Element for which to extract device ID data. + * \param callback The callback function to invoke on each generated + * device id descriptor for elm. + * \param callback_arg Argument passed through to callback on each invocation. + */ +static void +ses_devids_iter(enc_softc_t *enc, enc_element_t *elm, + ses_devid_callback_t *callback, void *callback_arg) +{ + ses_element_t *elmpriv; + struct ses_addl_status *addl; + u_int i; + size_t devid_record_size; + + 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++) { + uint8_t devid_buf[devid_record_size]; + struct scsi_vpd_id_descriptor *devid; + uint8_t *phy_addr; + + devid = (struct scsi_vpd_id_descriptor *)devid_buf; + phy_addr = addl->proto_data.sasdev_phys[i].phy_addr; + devid->proto_codeset = (SCSI_PROTO_SAS << SVPD_ID_PROTO_SHIFT) + | SVPD_ID_CODESET_BINARY; + devid->id_type = SVPD_ID_PIV + | SVPD_ID_ASSOC_PORT + | SVPD_ID_TYPE_NAA; + devid->reserved = 0; + devid->length = sizeof(struct scsi_vpd_id_naa_ieee_reg); + memcpy(devid->identifier, phy_addr, devid->length); + + callback(enc, elm, devid, callback_arg); + } +} + +/** + * Function signature for consumers of the ses_paths_iter() interface. + */ +typedef void ses_path_callback_t(enc_softc_t *, enc_element_t *, + struct cam_path *, void *); + +/** + * Argument package passed through ses_devids_iter() by + * ses_paths_iter() to ses_path_iter_devid_callback(). + */ +typedef struct ses_path_iter_args { + ses_path_callback_t *callback; + void *callback_arg; +} ses_path_iter_args_t; + +/** + * ses_devids_iter() callback function used by ses_paths_iter() + * to map device ids to peripheral driver instances. + * + * \param enc SES instance containing elm + * \param elm Element on which device ID matching is active. + * \param periph A device ID corresponding to elm. + * \param arg Argument passed through to callback on each invocation. + */ +static void +ses_path_iter_devid_callback(enc_softc_t *enc, enc_element_t *elem, + struct scsi_vpd_id_descriptor *devid, + void *arg) +{ + struct ccb_dev_match cdm; + struct dev_match_pattern match_pattern; + struct dev_match_result match_result; + struct device_match_result *device_match; + struct device_match_pattern *device_pattern; + ses_path_iter_args_t *args; + + args = (ses_path_iter_args_t *)arg; + match_pattern.type = DEV_MATCH_DEVICE; + device_pattern = &match_pattern.pattern.device_pattern; + device_pattern->flags = DEV_MATCH_DEVID; + device_pattern->data.devid_pat.id_len = + offsetof(struct scsi_vpd_id_descriptor, identifier) + + devid->length; + memcpy(device_pattern->data.devid_pat.id, devid, + device_pattern->data.devid_pat.id_len); + + memset(&cdm, 0, sizeof(cdm)); + if (xpt_create_path(&cdm.ccb_h.path, /*periph*/NULL, CAM_XPT_PATH_ID, + CAM_TARGET_WILDCARD, + CAM_LUN_WILDCARD) != CAM_REQ_CMP) + return; + + cdm.ccb_h.func_code = XPT_DEV_MATCH; + cdm.num_patterns = 1; + cdm.patterns = &match_pattern; + cdm.pattern_buf_len = sizeof(match_pattern); + cdm.match_buf_len = sizeof(match_result); + cdm.matches = &match_result; + + xpt_action((union ccb *)&cdm); + xpt_free_path(cdm.ccb_h.path); + + if ((cdm.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP + || (cdm.status != CAM_DEV_MATCH_LAST + && cdm.status != CAM_DEV_MATCH_MORE) + || cdm.num_matches == 0) + return; + + device_match = &match_result.result.device_result; + if (xpt_create_path(&cdm.ccb_h.path, /*periph*/NULL, + device_match->path_id, + device_match->target_id, + device_match->target_lun) != CAM_REQ_CMP) + return; + + args->callback(enc, elem, cdm.ccb_h.path, args->callback_arg); + xpt_free_path(cdm.ccb_h.path); +} + +/** + * \brief Iterate over and find the matching periph objects for the + * specified element. + * + * \param enc SES instance containing elm + * \param elm Element for which to perform periph object matching. + * \param callback The callback function to invoke with each matching + * periph object. + * \param callback_arg Argument passed through to callback on each invocation. + */ +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; + + args.callback = callback; + args.callback_arg = callback_arg; + ses_devids_iter(enc, elm, ses_path_iter_devid_callback, &args); +} + +/** + * ses_paths_iter() callback function used by ses_get_elmdevname() + * to record periph driver instance strings corresponding to a SES + * element. + * + * \param enc SES instance containing elm + * \param elm Element on which periph matching is active. + * \param periph A periph instance that matches elm. + * \param arg Argument passed through to callback on each invocation. + */ +static void +ses_elmdevname_callback(enc_softc_t *enc, enc_element_t *elem, + struct cam_path *path, void *arg) +{ + struct sbuf *sb; + + sb = (struct sbuf *)arg; + cam_periph_list(path, sb); +} + +/** + * Argument package passed through ses_paths_iter() to + * ses_getcampath_callback. + */ +typedef struct ses_setphyspath_callback_args { + struct sbuf *physpath; + int num_set; +} ses_setphyspath_callback_args_t; + +/** + * \brief ses_paths_iter() callback to set the physical path on the + * CAM EDT entries corresponding to a given SES element. + * + * \param enc SES instance containing elm + * \param elm Element on which periph matching is active. + * \param periph A periph instance that matches elm. + * \param arg Argument passed through to callback on each invocation. + */ +static void +ses_setphyspath_callback(enc_softc_t *enc, enc_element_t *elm, + struct cam_path *path, void *arg) +{ + struct ccb_dev_advinfo cdai; + ses_setphyspath_callback_args_t *args; + char *old_physpath; + + args = (ses_setphyspath_callback_args_t *)arg; + old_physpath = malloc(MAXPATHLEN, M_SCSIENC, M_WAITOK|M_ZERO); + + xpt_setup_ccb(&cdai.ccb_h, path, CAM_PRIORITY_NORMAL); + cdai.ccb_h.func_code = XPT_DEV_ADVINFO; + cdai.buftype = CDAI_TYPE_PHYS_PATH; + cdai.flags = 0; + cdai.bufsiz = MAXPATHLEN; + cdai.buf = old_physpath; + xpt_action((union ccb *)&cdai); + if ((cdai.ccb_h.status & CAM_DEV_QFRZN) != 0) + cam_release_devq(cdai.ccb_h.path, 0, 0, 0, FALSE); + + if (strcmp(old_physpath, sbuf_data(args->physpath)) != 0) { + + xpt_setup_ccb(&cdai.ccb_h, path, CAM_PRIORITY_NORMAL); + cdai.ccb_h.func_code = XPT_DEV_ADVINFO; + cdai.buftype = CDAI_TYPE_PHYS_PATH; + cdai.flags |= CDAI_FLAG_STORE; + cdai.bufsiz = sbuf_len(args->physpath); + cdai.buf = sbuf_data(args->physpath); + xpt_action((union ccb *)&cdai); + if ((cdai.ccb_h.status & CAM_DEV_QFRZN) != 0) + cam_release_devq(cdai.ccb_h.path, 0, 0, 0, FALSE); + if (cdai.ccb_h.status == CAM_REQ_CMP) + args->num_set++; + } + free(old_physpath, M_SCSIENC); +} + +/** + * \brief Set a device's physical path string in CAM XPT. + * + * \param enc SES instance containing elm + * \param elm Element to publish physical path string for + * \param iter Iterator whose state corresponds to elm + * + * \return 0 on success, errno otherwise. + */ +static int +ses_set_physpath(enc_softc_t *enc, enc_element_t *elm, + struct ses_iterator *iter) +{ + struct ccb_dev_advinfo cdai; + ses_setphyspath_callback_args_t args; + int ret; + struct sbuf sb; + uint8_t *devid, *elmaddr; + ses_element_t *elmpriv; + + ret = EIO; + devid = NULL; + + /* + * Assemble the components of the physical path starting with + * the device ID of the enclosure itself. + */ + xpt_setup_ccb(&cdai.ccb_h, enc->periph->path, CAM_PRIORITY_NORMAL); + cdai.ccb_h.func_code = XPT_DEV_ADVINFO; + cdai.buftype = CDAI_TYPE_SCSI_DEVID; + cdai.bufsiz = CAM_SCSI_DEVID_MAXLEN; + cdai.buf = devid = ENC_MALLOCZ(cdai.bufsiz); + if (devid == NULL) { + ret = ENOMEM; + goto out; + } + xpt_action((union ccb *)&cdai); + if ((cdai.ccb_h.status & CAM_DEV_QFRZN) != 0) + cam_release_devq(cdai.ccb_h.path, 0, 0, 0, FALSE); + if (cdai.ccb_h.status != CAM_REQ_CMP) + goto out; + + elmaddr = scsi_get_devid((struct scsi_vpd_device_id *)cdai.buf, + cdai.provsiz, scsi_devid_is_naa_ieee_reg); + if (elmaddr == NULL) + goto out; + + if (sbuf_new(&sb, NULL, 128, SBUF_AUTOEXTEND) == NULL) { + ret = ENOMEM; + goto out; + } + /* Next, generate the physical path string */ + sbuf_printf(&sb, "id1,enc@n%jx/type@%x/slot@%x", + scsi_8btou64(elmaddr), 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@"); + sbuf_bcat(&sb, elmpriv->descr, elmpriv->descr_len); + } + sbuf_finish(&sb); + + /* + * Set this physical path on any CAM devices with a device ID + * descriptor that matches one created from the SES additional + * status data for this element. + */ + args.physpath= &sb; + args.num_set = 0; + ses_paths_iter(enc, elm, ses_setphyspath_callback, &args); + sbuf_delete(&sb); + + ret = args.num_set == 0 ? ENOENT : 0; + +out: + if (devid != NULL) + ENC_FREE(devid); + return (ret); +} + +/** + * \brief Helper to set the CDB fields appropriately. + * + * \param cdb Buffer containing the cdb. + * \param pagenum SES diagnostic page to query for. + * \param dir Direction of query. + */ +static void +ses_page_cdb(char *cdb, int bufsiz, SesDiagPageCodes pagenum, int dir) +{ + + /* Ref: SPC-4 r25 Section 6.20 Table 223 */ + if (dir == CAM_DIR_IN) { + cdb[0] = RECEIVE_DIAGNOSTIC; + cdb[1] = 1; /* Set page code valid bit */ + cdb[2] = pagenum; + } else { + cdb[0] = SEND_DIAGNOSTIC; + cdb[1] = 0x10; + cdb[2] = pagenum; + } + cdb[3] = bufsiz >> 8; /* high bits */ + cdb[4] = bufsiz & 0xff; /* low bits */ + cdb[5] = 0; +} + +/** + * \brief Discover whether this instance supports timed completion of a + * RECEIVE DIAGNOSTIC RESULTS command requesting the Enclosure Status + * page, and store the result in the softc, updating if necessary. + * + * \param enc SES instance to query and update. + * \param tc_en Value of timed completion to set (see \return). + * + * \return 1 if timed completion enabled, 0 otherwise. + */ +static int +ses_set_timed_completion(enc_softc_t *enc, uint8_t tc_en) +{ + int err; + union ccb *ccb; + struct cam_periph *periph; + struct ses_mgmt_mode_page *mgmt; + uint8_t *mode_buf; + size_t mode_buf_len; + ses_softc_t *ses; + + periph = enc->periph; + ses = enc->enc_private; + ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL); + + mode_buf_len = sizeof(struct ses_mgmt_mode_page); + mode_buf = ENC_MALLOCZ(mode_buf_len); + if (mode_buf == NULL) + goto out; + + scsi_mode_sense(&ccb->csio, /*retries*/4, enc_done, MSG_SIMPLE_Q_TAG, + /*dbd*/FALSE, SMS_PAGE_CTRL_CURRENT, SES_MGMT_MODE_PAGE_CODE, + mode_buf, mode_buf_len, SSD_FULL_SIZE, /*timeout*/60 * 1000); + + /* + * Ignore illegal request errors, as they are quite common and we + * will print something out in that case anyway. + */ + err = cam_periph_runccb(ccb, enc_error, ENC_CFLAGS, + ENC_FLAGS|SF_QUIET_IR, NULL); + if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) + cam_release_devq(ccb->ccb_h.path, 0, 0, 0, FALSE); + else if (ccb->ccb_h.status != CAM_REQ_CMP) { + ENC_LOG(enc, "Timed Completion Unsupported\n"); + goto release; + } + + /* Skip the mode select if the desired value is already set */ + mgmt = (struct ses_mgmt_mode_page *)mode_buf; + if ((mgmt->byte5 & SES_MGMT_TIMED_COMP_EN) == tc_en) + goto done; + + /* Value is not what we wanted, set it */ + if (tc_en) + mgmt->byte5 |= SES_MGMT_TIMED_COMP_EN; + else + mgmt->byte5 &= ~SES_MGMT_TIMED_COMP_EN; + /* SES2r20: a completion time of zero means as long as possible */ + bzero(&mgmt->max_comp_time, sizeof(mgmt->max_comp_time)); + + scsi_mode_select(&ccb->csio, 5, enc_done, MSG_SIMPLE_Q_TAG, + /*page_fmt*/FALSE, /*save_pages*/TRUE, mode_buf, mode_buf_len, + SSD_FULL_SIZE, /*timeout*/60 * 1000); + + err = cam_periph_runccb(ccb, enc_error, ENC_CFLAGS, ENC_FLAGS, NULL); + if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) + cam_release_devq(ccb->ccb_h.path, 0, 0, 0, FALSE); + else if (ccb->ccb_h.status != CAM_REQ_CMP) { + ENC_LOG(enc, "Timed Completion Set Failed\n"); + goto release; + } + +done: + if ((mgmt->byte5 & SES_MGMT_TIMED_COMP_EN) != 0) { + ENC_LOG(enc, "Timed Completion Enabled\n"); + ses->ses_flags |= SES_FLAG_TIMEDCOMP; + } else { + ENC_LOG(enc, "Timed Completion Disabled\n"); + ses->ses_flags &= ~SES_FLAG_TIMEDCOMP; + } +release: + ENC_FREE(mode_buf); + xpt_release_ccb(ccb); +out: + return (ses->ses_flags & SES_FLAG_TIMEDCOMP); +} + +/** + * \brief Process the config page and update associated structures. + * + * \param enc SES device to query. + * \param buf Buffer containing the config page. + * \param xfer_len Length of the config page in the buffer. + * + * \return 0 on success, errno otherwise. + */ +static int +ses_process_config(enc_softc_t *enc, struct enc_fsm_state *state, + union ccb *ccb, uint8_t **bufp, int xfer_len) +{ + struct ses_iterator iter; + ses_softc_t *ses; + enc_cache_t *enc_cache; + ses_cache_t *ses_cache; + uint8_t *buf; + int length; + int err; + int nelm; + int ntype; + struct ses_cfg_page *cfg_page; + struct ses_enc_desc *buf_subenc; + const struct ses_enc_desc **subencs; + const struct ses_enc_desc **cur_subenc; + const struct ses_enc_desc **last_subenc; + ses_type_t *ses_types; + ses_type_t *sestype; + const struct ses_elm_type_desc *cur_buf_type; + const struct ses_elm_type_desc *last_buf_type; + uint8_t *last_valid_byte; + enc_element_t *element; + const char *type_text; + + CAM_DEBUG(enc->periph->path, CAM_DEBUG_SUBTRACE, + ("entering %s(%p, %d)\n", __func__, bufp, xfer_len)); + ses = enc->enc_private; + enc_cache = &enc->enc_daemon_cache; + ses_cache = enc_cache->private; + buf = *bufp; + err = -1;; + + if (xfer_len < sizeof(cfg_page->hdr)) { + ENC_LOG(enc, "Unable to parse SES Config Header\n"); + err = EIO; + goto out; + } + + cfg_page = (struct ses_cfg_page *)buf; + length = ses_page_length(&cfg_page->hdr); + if (length > xfer_len) { + ENC_LOG(enc, "Enclosure Config Page Too Long\n"); + goto out; + } + last_valid_byte = &buf[length - 1]; + + ENC_DLOG(enc, "%s: total page length %d, xfer_len %d\n", + __func__, length, xfer_len); + + err = 0; + if (ses_config_cache_valid(ses_cache, cfg_page->hdr.gen_code)) { + + /* Our cache is still valid. Proceed to fetching status. */ + goto out; + } + + /* Cache is no longer valid. Free old data to make way for new. */ + ses_cache_free(enc, enc_cache); + ENC_VLOG(enc, "Generation Code 0x%x has %d SubEnclosures\n", + scsi_4btoul(cfg_page->hdr.gen_code), + ses_cfg_page_get_num_subenc(cfg_page)); + + /* Take ownership of the buffer. */ + ses_cache->cfg_page = cfg_page; + *bufp = NULL; + + /* + * Now waltz through all the subenclosures summing the number of + * types available in each. + */ + subencs = ENC_MALLOCZ(ses_cfg_page_get_num_subenc(cfg_page) + * sizeof(*subencs)); + if (subencs == NULL) { + err = ENOMEM; + goto out; + } + /* + * Sub-enclosure data is const after construction (i.e. when + * accessed via our cache object. + * + * The cast here is not required in C++ but C99 is not so + * sophisticated (see C99 6.5.16.1(1)). + */ + ses_cache->subencs = subencs; + + buf_subenc = cfg_page->subencs; + cur_subenc = subencs; + last_subenc = &subencs[ses_cfg_page_get_num_subenc(cfg_page) - 1]; + ntype = 0; + while (cur_subenc <= last_subenc) { + + if (!ses_enc_desc_is_complete(buf_subenc, last_valid_byte)) { + ENC_LOG(enc, "Enclosure %d Beyond End of " + "Descriptors\n", cur_subenc - subencs); + err = EIO; + goto out; + } + + ENC_VLOG(enc, " SubEnclosure ID %d, %d Types With this ID, " + "Descriptor Length %d, offset %d\n", buf_subenc->subenc_id, + buf_subenc->num_types, buf_subenc->length, + &buf_subenc->byte0 - buf); + ENC_VLOG(enc, "WWN: %jx\n", + (uintmax_t)scsi_8btou64(buf_subenc->logical_id)); + + ntype += buf_subenc->num_types; + *cur_subenc = buf_subenc; + cur_subenc++; + buf_subenc = ses_enc_desc_next(buf_subenc); + } + + /* Process the type headers. */ + ses_types = ENC_MALLOCZ(ntype * sizeof(*ses_types)); + if (ses_types == NULL) { + err = ENOMEM; + goto out; + } + /* + * Type data is const after construction (i.e. when accessed via + * our cache object. + */ + ses_cache->ses_types = ses_types; + + cur_buf_type = (const struct ses_elm_type_desc *) + (&(*last_subenc)->length + (*last_subenc)->length + 1); + last_buf_type = cur_buf_type + ntype - 1; + type_text = (const uint8_t *)(last_buf_type + 1); + nelm = 0; + sestype = ses_types; + while (cur_buf_type <= last_buf_type) { + if (&cur_buf_type->etype_txt_len > last_valid_byte) { + ENC_LOG(enc, "Runt Enclosure Type Header %d\n", + sestype - ses_types); + err = EIO; + goto out; + } + sestype->hdr = cur_buf_type; + sestype->text = type_text; + type_text += cur_buf_type->etype_txt_len; + ENC_LOG(enc, " Type Desc[%d]: Type 0x%x, MaxElt %d, In Subenc " + "%d, Text Length %d: %.*s\n", sestype - ses_types, + sestype->hdr->etype_elm_type, sestype->hdr->etype_maxelt, + sestype->hdr->etype_subenc, sestype->hdr->etype_txt_len, + sestype->hdr->etype_txt_len, sestype->text); + + nelm += sestype->hdr->etype_maxelt + + /*overall status element*/1; + sestype++; + cur_buf_type++; + } + + /* Create the object map. */ + enc_cache->elm_map = ENC_MALLOCZ(nelm * sizeof(enc_element_t)); + if (enc_cache->elm_map == NULL) { + err = ENOMEM; + goto out; + } + ses_cache->ses_ntypes = (uint8_t)ntype; + enc_cache->nelms = nelm; + + ses_iter_init(enc, enc_cache, &iter); + while ((element = ses_iter_next(&iter)) != NULL) { + const struct ses_elm_type_desc *thdr; + + ENC_DLOG(enc, "%s: checking obj %d(%d,%d)\n", __func__, + iter.global_element_index, iter.type_index, nelm, + iter.type_element_index); + thdr = ses_cache->ses_types[iter.type_index].hdr; + element->subenclosure = thdr->etype_subenc; + element->enctype = thdr->etype_elm_type; + element->overall_status_elem = iter.type_element_index == 0; + element->elm_private = ENC_MALLOCZ(sizeof(ses_element_t)); + if (element->elm_private == NULL) { + err = ENOMEM; + goto out; + } + ENC_DLOG(enc, "%s: creating elmpriv %d(%d,%d) subenc %d " + "type 0x%x\n", __func__, iter.global_element_index, + iter.type_index, iter.type_element_index, + thdr->etype_subenc, thdr->etype_elm_type); + } + + err = 0; + +out: + if (err) + ses_softc_cleanup(enc->periph); + else { + enc_update_request(enc, SES_UPDATE_GETSTATUS); + enc_update_request(enc, SES_UPDATE_GETELMDESCS); + enc_update_request(enc, SES_UPDATE_GETELMADDLSTATUS); + enc_update_request(enc, SES_PUBLISH_CACHE); + } + ENC_DLOG(enc, "%s: exiting with err %d\n", __func__, err); + return (err); +} + +/** + * \brief Update the status page and associated structures. + * + * \param enc SES softc to update for. + * \param buf Buffer containing the status page. + * \param bufsz Amount of data in the buffer. + * + * \return 0 on success, errno otherwise. + */ +static int +ses_process_status(enc_softc_t *enc, struct enc_fsm_state *state, + union ccb *ccb, uint8_t **bufp, int xfer_len) +{ + struct ses_iterator iter; + enc_element_t *element; + ses_softc_t *ses; + enc_cache_t *enc_cache; + ses_cache_t *ses_cache; + uint8_t *buf; + int err = -1; + int length; + struct ses_status_page *page; + union ses_status_element *cur_stat; + union ses_status_element *last_stat; + + ses = enc->enc_private; + enc_cache = &enc->enc_daemon_cache; + ses_cache = enc_cache->private; + buf = *bufp; + + ENC_DLOG(enc, "%s: enter (%p, %p, %d)\n", __func__, enc, buf, xfer_len); + page = (struct ses_status_page *)buf; + length = ses_page_length(&page->hdr); + + /* + * Make sure the length fits in the buffer. + * + * XXX all this means is that the page is larger than the space + * we allocated. Since we use a statically sized buffer, this + * could happen... Need to use dynamic discovery of the size. + */ + if (length > xfer_len) { + ENC_LOG(enc, "Enclosure Status Page Too Long\n"); + goto out; + } + /* Make sure the length contains at least one header and status */ + if (length < (sizeof(*page) + sizeof(*page->elements))) { + ENC_LOG(enc, "Enclosure Status Page Too Short\n"); + goto out; + } + + if (!ses_config_cache_valid(ses_cache, page->hdr.gen_code)) { + ENC_DLOG(enc, "%s: Generation count change detected\n", + __func__); + enc_update_request(enc, SES_UPDATE_GETCONFIG); + goto out; + } + + ses_cache_free_status(enc, enc_cache); + ses_cache->status_page = page; + *bufp = NULL; + + enc_cache->enc_status = page->hdr.page_specific_flags; + + /* + * Read in individual element status. The element order + * matches the order reported in the config page (i.e. the + * order of an unfiltered iteration of the config objects).. + */ + ses_iter_init(enc, enc_cache, &iter); + cur_stat = page->elements; + last_stat = (union ses_status_element *) + &buf[length - sizeof(*last_stat)]; + ENC_DLOG(enc, "%s: total page length %d, xfer_len %d\n", + __func__, length, xfer_len); + while (cur_stat <= last_stat + && (element = ses_iter_next(&iter)) != NULL) { + + ENC_DLOG(enc, "%s: obj %d(%d,%d) off=0x%tx status=%jx\n", + __func__, iter.global_element_index, iter.type_index, + iter.type_element_index, (uint8_t *)cur_stat - buf, + scsi_4btoul(cur_stat->bytes)); + + memcpy(&element->encstat, cur_stat, sizeof(element->encstat)); + element->svalid = 1; + cur_stat++; + } + + if (ses_iter_next(&iter) != NULL) { + ENC_LOG(enc, "Status page, length insufficient for " + "expected number of objects\n"); + } else { + if (cur_stat <= last_stat) + ENC_LOG(enc, "Status page, exhausted objects before " + "exhausing page\n"); + enc_update_request(enc, SES_PUBLISH_CACHE); + err = 0; + } +out: + ENC_DLOG(enc, "%s: exiting with error %d\n", __func__, err); + return (err); +} + +typedef enum { + /** + * The enclosure should not provide additional element + * status for this element type in page 0x0A. + * + * \note This status is returned for any types not + * listed SES3r02. Further types added in a + * future specification will be incorrectly + * classified. + */ + TYPE_ADDLSTATUS_NONE, + + /** + * The element type provides additional element status + * in page 0x0A. + */ + TYPE_ADDLSTATUS_MANDATORY, + + /** + * The element type may provide additional element status + * in page 0x0A, but i + */ + TYPE_ADDLSTATUS_OPTIONAL +} ses_addlstatus_avail_t; + +/** + * \brief Check to see whether a given type (as obtained via type headers) is + * supported by the additional status command. + * + * \param enc SES softc to check. + * \param typidx Type index to check for. + * + * \return An enumeration indicating if additional status is mandatory, + * optional, or not required for this type. + */ +static ses_addlstatus_avail_t +ses_typehasaddlstatus(enc_softc_t *enc, uint8_t typidx) +{ + enc_cache_t *enc_cache; + ses_cache_t *ses_cache; + + enc_cache = &enc->enc_daemon_cache; + ses_cache = enc_cache->private; + switch(ses_cache->ses_types[typidx].hdr->etype_elm_type) { + case ELMTYP_DEVICE: + case ELMTYP_ARRAY_DEV: + case ELMTYP_SAS_EXP: + return (TYPE_ADDLSTATUS_MANDATORY); + case ELMTYP_SCSI_INI: + case ELMTYP_SCSI_TGT: + case ELMTYP_ESCC: + return (TYPE_ADDLSTATUS_OPTIONAL); + default: + /* No additional status information available. */ + break; + } + return (TYPE_ADDLSTATUS_NONE); +} + +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); + +/** + * \brief Parse the additional status element data for each object. + * + * \param enc The SES softc to update. + * \param buf The buffer containing the additional status + * element response. + * \param xfer_len Size of the buffer. + * + * \return 0 on success, errno otherwise. + */ +static int +ses_process_elm_addlstatus(enc_softc_t *enc, struct enc_fsm_state *state, + union ccb *ccb, uint8_t **bufp, int xfer_len) +{ + struct ses_iterator iter; + int eip; + int err; + int length; + int offset; + enc_cache_t *enc_cache; + ses_cache_t *ses_cache; + uint8_t *buf; + ses_element_t *elmpriv; + const struct ses_page_hdr *hdr; + enc_element_t *element; + + enc_cache = &enc->enc_daemon_cache; + ses_cache = enc_cache->private; + buf = *bufp; + err = -1; + + ses_cache_free_elm_addlstatus(enc, enc_cache); + ses_cache->elm_addlstatus_page = + (struct ses_addl_elem_status_page *)buf; + *bufp = NULL; + + /* + * The objects appear in the same order here as in Enclosure Status, + * which itself is ordered by the Type Descriptors from the Config + * page. However, it is necessary to skip elements that are not + * supported by this page when counting them. + */ + hdr = &ses_cache->elm_addlstatus_page->hdr; + length = ses_page_length(hdr); + ENC_DLOG(enc, "Additional Element Status Page Length 0x%x\n", length); + /* Make sure the length includes at least one header. */ + if (length < sizeof(*hdr)+sizeof(struct ses_elm_addlstatus_base_hdr)) { + ENC_LOG(enc, "Runt Additional Element Status Page\n"); + goto out; + } + if (length > xfer_len) { + ENC_LOG(enc, "Additional Element Status Page Too Long\n"); + goto out; + } + + if (!ses_config_cache_valid(ses_cache, hdr->gen_code)) { + ENC_DLOG(enc, "%s: Generation count change detected\n", + __func__); + enc_update_request(enc, SES_UPDATE_GETCONFIG); + goto out; + } + + offset = sizeof(struct ses_page_hdr); + ses_iter_init(enc, enc_cache, &iter); + while (offset < length + && (element = ses_iter_next(&iter)) != NULL) { + struct ses_elm_addlstatus_base_hdr *elm_hdr; + int proto_info_len; + ses_addlstatus_avail_t status_type; + + /* + * Additional element status is only provided for + * individual elements (i.e. overal status elements + * are excluded) and those of the types specified + * in the SES spec. + */ + status_type = ses_typehasaddlstatus(enc, iter.type_index); + if (iter.individual_element_index == ITERATOR_INDEX_INVALID + || status_type == TYPE_ADDLSTATUS_NONE) + continue; + + elm_hdr = (struct ses_elm_addlstatus_base_hdr *)&buf[offset]; + eip = ses_elm_addlstatus_eip(elm_hdr); + if (eip) { + struct ses_elm_addlstatus_eip_hdr *eip_hdr; + int expected_index; + + eip_hdr = (struct ses_elm_addlstatus_eip_hdr *)elm_hdr; + expected_index = iter.individual_element_index; + element = ses_iter_seek_to(&iter, + eip_hdr->element_index, + SES_ELEM_INDEX_INDIVIDUAL); + + if (iter.individual_element_index != expected_index + && status_type == TYPE_ADDLSTATUS_MANDATORY) { + ENC_LOG(enc, "%s: provided element " + "index %d skips mandatory status " + " element at index %d\n", + __func__, eip_hdr->element_index, + expected_index); + } + } + 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__, + iter.global_element_index, iter.type_index, + iter.type_element_index, offset, elmpriv->addl.hdr->byte0, + elmpriv->addl.hdr->length); + + /* Skip to after the length field */ + offset += sizeof(struct ses_elm_addlstatus_base_hdr); + + /* Make sure the descriptor is within bounds */ + if ((offset + elmpriv->addl.hdr->length) > length) { + ENC_LOG(enc, "Element %d Beyond End " + "of Additional Element Status Descriptors\n", + iter.global_element_index); + err = EIO; + goto out; + } + + /* Advance to the protocol data, skipping eip bytes if needed */ + offset += (eip * SES_EIP_HDR_EXTRA_LEN); + proto_info_len = elmpriv->addl.hdr->length + - (eip * SES_EIP_HDR_EXTRA_LEN); + + /* Errors in this block are ignored as they are non-fatal */ + switch(ses_elm_addlstatus_proto(elmpriv->addl.hdr)) { + case SPSP_PROTO_FC: + if (elmpriv->addl.hdr->length == 0) + break; + ses_get_elm_addlstatus_fc(enc, enc_cache, + &buf[offset], proto_info_len); + break; + case SPSP_PROTO_SAS: + if (elmpriv->addl.hdr->length <= 2) + break; + ses_get_elm_addlstatus_sas(enc, enc_cache, + &buf[offset], + proto_info_len, + eip, iter.type_index, + iter.global_element_index); + break; + default: + ENC_LOG(enc, "Element %d: Unknown Additional Element " + "Protocol 0x%x\n", iter.global_element_index, + ses_elm_addlstatus_proto(elmpriv->addl.hdr)); + goto out; + } + + offset += proto_info_len; + } + err = 0; +out: + if (err) + ses_cache_free_elm_addlstatus(enc, enc_cache); + enc_update_request(enc, SES_PUBLISH_PHYSPATHS); + enc_update_request(enc, SES_PUBLISH_CACHE); + return (err); +} + +static int +ses_process_control_request(enc_softc_t *enc, struct enc_fsm_state *state, + union ccb *ccb, uint8_t **bufp, int xfer_len) +{ + ses_softc_t *ses; + + ses = enc->enc_private; + /* + * Possible errors: + * o Generation count wrong. + * o Some SCSI status error. + */ + ses_terminate_control_requests(&ses->ses_pending_requests, 0); + return (0); +} + +static int +ses_publish_physpaths(enc_softc_t *enc, struct enc_fsm_state *state, + union ccb *ccb, uint8_t **bufp, int xfer_len) +{ + struct ses_iterator iter; + enc_cache_t *enc_cache; + ses_cache_t *ses_cache; + enc_element_t *element; + + enc_cache = &enc->enc_daemon_cache; + ses_cache = enc_cache->private; + + ses_iter_init(enc, enc_cache, &iter); + while ((element = ses_iter_next(&iter)) != NULL) { + /* + * ses_set_physpath() returns success if we changed + * the physpath of any element. This allows us to + * only announce devices once regardless of how + * many times we process additional element status. + */ + if (ses_set_physpath(enc, element, &iter) == 0) + ses_print_addl_data(enc, element); + } + + return (0); +} + +static int +ses_publish_cache(enc_softc_t *enc, struct enc_fsm_state *state, + union ccb *ccb, uint8_t **bufp, int xfer_len) +{ + + sx_xlock(&enc->enc_cache_lock); + ses_cache_clone(enc, /*src*/&enc->enc_daemon_cache, + /*dst*/&enc->enc_cache); + sx_xunlock(&enc->enc_cache_lock); + + return (0); +} + +/** + * \brief Parse the descriptors for each object. + * + * \param enc The SES softc to update. + * \param buf The buffer containing the descriptor list response. + * \param xfer_len Size of the buffer. + * + * \return 0 on success, errno otherwise. + */ +static int +ses_process_elm_descs(enc_softc_t *enc, struct enc_fsm_state *state, + union ccb *ccb, uint8_t **bufp, int xfer_len) +{ + struct ses_iterator iter; + enc_element_t *element; + int err; + int offset; + u_long length, plength; + enc_cache_t *enc_cache; + ses_cache_t *ses_cache; + uint8_t *buf; + ses_element_t *elmpriv; + const struct ses_page_hdr *phdr; + const struct ses_elm_desc_hdr *hdr; + + enc_cache = &enc->enc_daemon_cache; + ses_cache = enc_cache->private; + buf = *bufp; + err = -1; + + ses_cache_free_elm_descs(enc, enc_cache); + ses_cache->elm_descs_page = (struct ses_elem_descr_page *)buf; + *bufp = NULL; + + phdr = &ses_cache->elm_descs_page->hdr; + plength = ses_page_length(phdr); + if (xfer_len < sizeof(struct ses_page_hdr)) { + ENC_LOG(enc, "Runt Element Descriptor Page\n"); + goto out; + } + if (plength > xfer_len) { + ENC_LOG(enc, "Element Descriptor Page Too Long\n"); + goto out; + } + + if (!ses_config_cache_valid(ses_cache, phdr->gen_code)) { + ENC_VLOG(enc, "%s: Generation count change detected\n", + __func__); + enc_update_request(enc, SES_UPDATE_GETCONFIG); + goto out; + } + + offset = sizeof(struct ses_page_hdr); + + ses_iter_init(enc, enc_cache, &iter); + while (offset < plength + && (element = ses_iter_next(&iter)) != NULL) { + + if ((offset + sizeof(struct ses_elm_desc_hdr)) > plength) { + ENC_LOG(enc, "Element %d Descriptor Header Past " + "End of Buffer\n", iter.global_element_index); + goto out; + } + hdr = (struct ses_elm_desc_hdr *)&buf[offset]; + length = scsi_2btoul(hdr->length); + ENC_DLOG(enc, "%s: obj %d(%d,%d) length=%d off=%d\n", __func__, + iter.global_element_index, iter.type_index, + iter.type_element_index, length, offset); + if ((offset + sizeof(*hdr) + length) > plength) { + ENC_LOG(enc, "Element%d Descriptor Past " + "End of Buffer\n", iter.global_element_index); + goto out; + } + offset += sizeof(*hdr); + + if (length > 0) { + elmpriv = element->elm_private; + elmpriv->descr_len = length; + elmpriv->descr = &buf[offset]; + } + + /* skip over the descriptor itself */ + offset += length; + } + + err = 0; +out: + if (err == 0) + enc_update_request(enc, SES_UPDATE_GETELMADDLSTATUS); + enc_update_request(enc, SES_PUBLISH_CACHE); + return (err); +} + +static int +ses_fill_rcv_diag_csio(enc_softc_t *enc, struct enc_fsm_state *state, + union ccb *ccb, uint8_t *buf) +{ + scsi_receive_diagnostic_results(&ccb->csio, /*retries*/5, + enc_done, MSG_SIMPLE_Q_TAG, /*pcv*/1, + state->page_code, buf, state->buf_size, + SSD_FULL_SIZE, state->timeout); + return (0); +} + +/** + * \brief Encode the object status into the response buffer, which is + * expected to contain the current enclosure status. This function + * turns off all the 'select' bits for the objects except for the + * object specified, then sends it back to the enclosure. + * + * \param enc SES enclosure the change is being applied to. + * \param buf Buffer containing the current enclosure status response. + * \param amt Length of the response in the buffer. + * \param req The control request to be applied to buf. + * + * \return 0 on success, errno otherwise. + */ +static int +ses_encode(enc_softc_t *enc, uint8_t *buf, int amt, ses_control_request_t *req) +{ + struct ses_iterator iter; + enc_element_t *element; + int offset; + struct ses_control_page_hdr *hdr; + + ses_iter_init(enc, &enc->enc_cache, &iter); + hdr = (struct ses_control_page_hdr *)buf; + if (req->elm_idx == -1) { + /* for enclosure status, at least 2 bytes are needed */ + if (amt < 2) + return EIO; + hdr->control_flags = + req->elm_stat.comstatus & SES_SET_STATUS_MASK; + ENC_DLOG(enc, "Set EncStat %x\n", hdr->control_flags); + return (0); + } + + element = ses_iter_seek_to(&iter, req->elm_idx, SES_ELEM_INDEX_GLOBAL); + if (element == NULL) + return (ENXIO); + + /* + * Seek to the type set that corresponds to the requested object. + * The +1 is for the overall status element for the type. + */ + offset = sizeof(struct ses_control_page_hdr) + + (iter.global_element_index * sizeof(struct ses_comstat)); + + /* Check for buffer overflow. */ + if (offset + sizeof(struct ses_comstat) > amt) + return (EIO); + + /* Set the status. */ + memcpy(&buf[offset], &req->elm_stat, sizeof(struct ses_comstat)); + + ENC_DLOG(enc, "Set Type 0x%x Obj 0x%x (offset %d) with %x %x %x %x\n", + iter.type_index, iter.global_element_index, offset, + req->elm_stat.comstatus, req->elm_stat.comstat[0], + req->elm_stat.comstat[1], req->elm_stat.comstat[2]); + + return (0); +} + +static int +ses_fill_control_request(enc_softc_t *enc, struct enc_fsm_state *state, + union ccb *ccb, uint8_t *buf) +{ + ses_softc_t *ses; + enc_cache_t *enc_cache; + ses_cache_t *ses_cache; + struct ses_control_page_hdr *hdr; + ses_control_request_t *req; + size_t plength; + size_t offset; + + ses = enc->enc_private; + enc_cache = &enc->enc_daemon_cache; + ses_cache = enc_cache->private; + hdr = (struct ses_control_page_hdr *)buf; + + if (ses_cache->status_page == NULL) { + ses_terminate_control_requests(&ses->ses_requests, EIO); + return (EIO); + } + + plength = ses_page_length(&ses_cache->status_page->hdr); + memcpy(buf, ses_cache->status_page, plength); + + /* Disable the select bits in all status entries. */ + offset = sizeof(struct ses_control_page_hdr); + for (offset = sizeof(struct ses_control_page_hdr); + offset < plength; offset += sizeof(struct ses_comstat)) { + buf[offset] &= ~SESCTL_CSEL; + } + + /* And make sure the INVOP bit is clear. */ + hdr->control_flags &= ~SES_ENCSTAT_INVOP; + + /* Apply incoming requests. */ + while ((req = TAILQ_FIRST(&ses->ses_requests)) != NULL) { + + TAILQ_REMOVE(&ses->ses_requests, req, links); + req->result = ses_encode(enc, buf, plength, req); + if (req->result != 0) { + wakeup(req); + continue; + } + TAILQ_INSERT_TAIL(&ses->ses_pending_requests, req, links); + } + + if (TAILQ_EMPTY(&ses->ses_pending_requests) != 0) + return (ENOENT); + + /* Fill out the ccb */ + scsi_send_diagnostic(&ccb->csio, /*retries*/5, enc_done, + MSG_SIMPLE_Q_TAG, /*unit_offline*/0, + /*device_offline*/0, /*self_test*/0, + /*page_format*/1, /*self_test_code*/0, + buf, ses_page_length(&ses_cache->status_page->hdr), + SSD_FULL_SIZE, state->timeout); + return (0); +} + +static int +ses_get_elm_addlstatus_fc(enc_softc_t *enc, enc_cache_t *enc_cache, + uint8_t *buf, int bufsiz) +{ + ENC_LOG(enc, "FC Device Support Stubbed in Additional Status Page\n"); + return (ENODEV); +} + +#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_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, " )"); \ +} while(0) + +/** + * \brief Print the additional element status data for this object, for SAS + * type 0 objects. See SES2 r20 Section 6.1.13.3.2. + * + * \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) +{ + int i; + ses_element_t *elmpriv; + struct ses_addl_status *addl; + struct ses_elm_sas_device_phy *phy; + + 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); + if (ses_elm_addlstatus_eip(addl->hdr)) + 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; + for (i = 0;i < addl->proto_hdr.sas->base_hdr.num_phys;i++) { + phy = &addl->proto_data.sasdev_phys[i]; + sbuf_printf(sbp, "%s: phy %d:", sesname, i); + if (ses_elm_sas_dev_phy_sata_dev(phy)) + /* 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", + 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"); + } + sbuf_printf(sbp, "%s: phy %d: parent %jx addr %jx\n", + sesname, i, + (uintmax_t)scsi_8btou64(phy->parent_addr), + (uintmax_t)scsi_8btou64(phy->phy_addr)); + } +} +#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) +{ + int i, num_phys; + ses_element_t *elmpriv; + struct ses_addl_status *addl; + struct ses_elm_sas_expander_phy *exp_phy; + struct ses_elm_sas_port_phy *port_phy; + + 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)) { + num_phys = addl->proto_hdr.sas->base_hdr.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++) { + exp_phy = &addl->proto_data.sasexp_phys[i]; + sbuf_printf(sbp, "%s: phy %d: connector %d other %d\n", + sesname, i, exp_phy->connector_index, + exp_phy->other_index); + } + } else { + num_phys = addl->proto_hdr.sas->base_hdr.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++) { + port_phy = &addl->proto_data.sasport_phys[i]; + sbuf_printf(sbp, + "%s: phy %d: id %d connector %d other %d\n", + sesname, i, port_phy->phy_id, + port_phy->connector_index, port_phy->other_index); + sbuf_printf(sbp, "%s: phy %d: addr %jx\n", sesname, i, + (uintmax_t)scsi_8btou64(port_phy->phy_addr)); + } + } +} + +/** + * \brief Print the additional element status data for this object. + * + * \param enc SES softc associated with the object. + * \param obj The object to print the data for. + */ +static void +ses_print_addl_data(enc_softc_t *enc, enc_element_t *obj) +{ + ses_element_t *elmpriv; + struct ses_addl_status *addl; + struct sbuf sesname, name, out; + + elmpriv = obj->elm_private; + if (elmpriv == NULL) + return; + + addl = &(elmpriv->addl); + if (addl->hdr == NULL) + return; + + sbuf_new(&sesname, NULL, 16, SBUF_AUTOEXTEND); + sbuf_new(&name, NULL, 16, SBUF_AUTOEXTEND); + sbuf_new(&out, NULL, 512, SBUF_AUTOEXTEND); + ses_paths_iter(enc, obj, ses_elmdevname_callback, &name); + if (sbuf_len(&name) == 0) + sbuf_printf(&name, "(none)"); + sbuf_finish(&name); + sbuf_printf(&sesname, "%s%d", enc->periph->periph_name, + enc->periph->unit_number); + sbuf_finish(&sesname); + if (elmpriv->descr != NULL) + sbuf_printf(&out, "%s: %s: Element descriptor: '%s'\n", + sbuf_data(&sesname), sbuf_data(&name), elmpriv->descr); + switch(ses_elm_addlstatus_proto(addl->hdr)) { + case SPSP_PROTO_SAS: + 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)); + break; + case SES_SASOBJ_TYPE_OTHER: + ses_print_addl_data_sas_type1(enc, sbuf_data(&sesname), + &out, obj, sbuf_data(&name)); + break; + default: + break; + } + break; + case SPSP_PROTO_FC: /* stubbed for now */ + break; + default: + break; + } + sbuf_finish(&out); + printf("%s", sbuf_data(&out)); + sbuf_delete(&out); + sbuf_delete(&name); + sbuf_delete(&sesname); +} + +/** + * \brief Update the softc with the additional element status data for this + * object, for SAS type 0 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 nobj Number of objects attached to the SES softc. + * + * \return 0 on success, errno otherwise. + */ +static int +ses_get_elm_addlstatus_sas_type0(enc_softc_t *enc, enc_cache_t *enc_cache, + uint8_t *buf, int bufsiz, int eip, int nobj) +{ + int err, offset, physz; + enc_element_t *obj; + ses_element_t *elmpriv; + struct ses_addl_status *addl; + + err = offset = 0; + + /* basic object setup */ + obj = &(enc_cache->elm_map[nobj]); + elmpriv = obj->elm_private; + addl = &(elmpriv->addl); + + addl->proto_hdr.sas = (union ses_elm_sas_hdr *)&buf[offset]; + + /* Don't assume this object has any phys */ + bzero(&addl->proto_data, sizeof(addl->proto_data)); + if (addl->proto_hdr.sas->base_hdr.num_phys == 0) + goto out; + + /* Skip forward to the phy list */ + if (eip) + offset += sizeof(struct ses_elm_sas_type0_eip_hdr); + else + offset += sizeof(struct ses_elm_sas_type0_base_hdr); + + /* Make sure the phy list fits in the buffer */ + physz = addl->proto_hdr.sas->base_hdr.num_phys; + physz *= sizeof(struct ses_elm_sas_device_phy); + if (physz > (bufsiz - offset + 4)) { + ENC_LOG(enc, "Element %d Device Phy List Beyond End Of Buffer\n", + nobj); + err = EIO; + goto out; + } + + /* Point to the phy list */ + addl->proto_data.sasdev_phys = + (struct ses_elm_sas_device_phy *)&buf[offset]; + +out: + return (err); +} + +/** + * \brief Update the softc with the additional element status data for this + * object, for SAS type 1 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 nobj Number of objects attached to the SES softc. + * + * \return 0 on success, errno otherwise. + */ +static int +ses_get_elm_addlstatus_sas_type1(enc_softc_t *enc, enc_cache_t *enc_cache, + uint8_t *buf, int bufsiz, int eip, int nobj) +{ + int err, offset, physz; + enc_element_t *obj; + ses_element_t *elmpriv; + struct ses_addl_status *addl; + + err = offset = 0; + + /* basic object setup */ + obj = &(enc_cache->elm_map[nobj]); + elmpriv = obj->elm_private; + addl = &(elmpriv->addl); + + addl->proto_hdr.sas = (union ses_elm_sas_hdr *)&buf[offset]; + + /* Don't assume this object has any phys */ + bzero(&addl->proto_data, sizeof(addl->proto_data)); + if (addl->proto_hdr.sas->base_hdr.num_phys == 0) + goto out; + + /* Process expanders differently from other type1 cases */ + if (ses_obj_is_expander(enc, obj)) { + 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); + if (physz > (bufsiz - offset)) { + ENC_LOG(enc, "Element %d: Expander Phy List Beyond " + "End Of Buffer\n", nobj); + err = EIO; + goto out; + } + addl->proto_data.sasexp_phys = + (struct ses_elm_sas_expander_phy *)&buf[offset]; + } else { + offset += sizeof(struct ses_elm_sas_type1_nonexpander_hdr); + physz = addl->proto_hdr.sas->base_hdr.num_phys * + sizeof(struct ses_elm_sas_port_phy); + if (physz > (bufsiz - offset + 4)) { + ENC_LOG(enc, "Element %d: Port Phy List Beyond End " + "Of Buffer\n", nobj); + err = EIO; + goto out; + } + addl->proto_data.sasport_phys = + (struct ses_elm_sas_port_phy *)&buf[offset]; + } + +out: + return (err); +} + +/** + * \brief Update the softc with the additional element status data for this + * object, for SAS 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_sas(enc_softc_t *enc, enc_cache_t *enc_cache, + uint8_t *buf, int bufsiz, int eip, int tidx, + int nobj) +{ + int dtype, err; + ses_cache_t *ses_cache; + union ses_elm_sas_hdr *hdr; + + /* Need to be able to read the descriptor type! */ + if (bufsiz < sizeof(union ses_elm_sas_hdr)) { + err = EIO; + goto out; + } + + ses_cache = enc_cache->private; + + hdr = (union ses_elm_sas_hdr *)buf; + dtype = ses_elm_sas_descr_type(hdr); + switch(dtype) { + case SES_SASOBJ_TYPE_SLOT: + switch(ses_cache->ses_types[tidx].hdr->etype_elm_type) { + case ELMTYP_DEVICE: + case ELMTYP_ARRAY_DEV: + break; + default: + ENC_LOG(enc, "Element %d Additional Status Invalid " + "for SAS device type 0: SES Typ 0x%x\n", nobj, + ses_cache->ses_types[tidx].hdr->etype_elm_type); + err = ENODEV; + goto out; + } + err = ses_get_elm_addlstatus_sas_type0(enc, enc_cache, + buf, bufsiz, eip, + nobj); + break; + case SES_SASOBJ_TYPE_OTHER: + switch(ses_cache->ses_types[tidx].hdr->etype_elm_type) { + case ELMTYP_SAS_EXP: + case ELMTYP_SCSI_INI: + case ELMTYP_SCSI_TGT: + case ELMTYP_ESCC: + break; + default: + ENC_LOG(enc, "Element %d Additional Status Invalid " + "for SAS device type 1\n", nobj); + err = ENODEV; + goto out; + } + err = ses_get_elm_addlstatus_sas_type1(enc, enc_cache, buf, + bufsiz, eip, nobj); + break; + default: + ENC_LOG(enc, "Element %d Additional Status Invalid Type %d for" + " SAS object\n", dtype, nobj); + err = ENODEV; + break; + } + +out: + return (err); +} + +static void +ses_softc_invalidate(struct cam_periph *periph) +{ +} + +static void +ses_softc_cleanup(struct cam_periph *periph) +{ + enc_softc_t *enc; + + CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, + ("entering ses_softc_cleanup(%p)\n", periph)); + enc = periph->softc; + ses_cache_free(enc, &enc->enc_cache); + ses_cache_free(enc, &enc->enc_daemon_cache); + ENC_FREE_AND_NULL(enc->enc_private); + ENC_FREE_AND_NULL(enc->enc_cache.private); + ENC_FREE_AND_NULL(enc->enc_daemon_cache.private); +} + +static int +ses_init_enc(enc_softc_t *enc) +{ + return (0); +} + +static int +ses_get_enc_status(enc_softc_t *enc, int slpflag) +{ + /* Automatically updated, caller checks enc_cache->encstat itself */ + return (0); +} + +static int +ses_set_enc_status(enc_softc_t *enc, uint8_t encstat, int slpflag) +{ + ses_control_request_t req; + ses_softc_t *ses; + + ses = enc->enc_private; + req.elm_idx = SES_SETSTATUS_ENC_IDX; + req.elm_stat.comstatus = encstat & 0xf; + + TAILQ_INSERT_TAIL(&ses->ses_requests, &req, links); + enc_update_request(enc, SES_PROCESS_CONTROL_REQS); + cam_periph_sleep(enc->periph, &req, PUSER, "encstat", 0); + + sx_slock(&enc->enc_cache_lock); + + return (req.result); +} + +static int +ses_get_elm_status(enc_softc_t *enc, encioc_elm_status_t *elms, int slpflag) +{ + unsigned int i = elms->elm_idx; + + memcpy(elms->cstat, &enc->enc_cache.elm_map[i].encstat, 4); + return (0); +} + +static int +ses_set_elm_status(enc_softc_t *enc, encioc_elm_status_t *elms, int slpflag) +{ + ses_control_request_t req; + ses_softc_t *ses; + + /* If this is clear, we don't do diddly. */ + if ((elms->cstat[0] & SESCTL_CSEL) == 0) + return (0); + + ses = enc->enc_private; + req.elm_idx = elms->elm_idx; + memcpy(&req.elm_stat, elms->cstat, sizeof(req.elm_stat)); + + TAILQ_INSERT_TAIL(&ses->ses_requests, &req, links); + enc_update_request(enc, SES_PROCESS_CONTROL_REQS); + cam_periph_sleep(enc->periph, &req, PUSER, "encstat", 0); + + return (req.result); +} + +static int +ses_get_elm_desc(enc_softc_t *enc, encioc_elm_desc_t *elmd) +{ + int i = (int)elmd->elm_idx; + ses_element_t *elmpriv; + + /* Assume caller has already checked obj_id validity */ + elmpriv = enc->enc_cache.elm_map[i].elm_private; + /* object might not have a descriptor */ + if (elmpriv == NULL || elmpriv->descr == NULL) + return (0); + if (elmd->elm_desc_len > elmpriv->descr_len) + elmd->elm_desc_len = elmpriv->descr_len; + copyout(elmpriv->descr, elmd->elm_desc_str, elmd->elm_desc_len); + return (0); +} + +/** + * \brief Respond to ENCIOC_GETELMDEVNAME, providing a device name for the + * given object id if one is available. + * + * \param enc SES softc to examine. + * \param objdn ioctl structure to read/write device name info. + * + * \return 0 on success, errno otherwise. + */ +static int +ses_get_elm_devnames(enc_softc_t *enc, encioc_elm_devnames_t *elmdn) +{ + struct sbuf sb; + int len; + + len = elmdn->elm_names_size; + if (len < 0) + return (EINVAL); + + sbuf_new(&sb, elmdn->elm_devnames, len, 0); + + cam_periph_unlock(enc->periph); + ses_paths_iter(enc, &enc->enc_cache.elm_map[elmdn->elm_idx], + ses_elmdevname_callback, &sb); + sbuf_finish(&sb); + elmdn->elm_names_len = sbuf_len(&sb); + cam_periph_lock(enc->periph); + return (elmdn->elm_names_len > 0 ? 0 : ENODEV); +} + +/** + * \brief Send a string to the primary subenclosure using the String Out + * SES diagnostic page. + * + * \param enc SES enclosure to run the command on. + * \param sstr SES string structure to operate on + * \param ioc Ioctl being performed + * + * \return 0 on success, errno otherwise. + */ +static int +ses_handle_string(enc_softc_t *enc, encioc_string_t *sstr, int ioc) +{ + int amt, payload, ret; + char cdb[6]; + uint8_t *buf; + + /* Implement SES2r20 6.1.6 */ + if (sstr->bufsiz > 0xffff) + return (EINVAL); /* buffer size too large */ + + if (ioc == ENCIOC_SETSTRING) { + payload = sstr->bufsiz + 4; /* header for SEND DIAGNOSTIC */ + amt = 0 - payload; + buf = ENC_MALLOC(payload); + if (buf == NULL) + return ENOMEM; + + ses_page_cdb(cdb, payload, 0, CAM_DIR_OUT); + /* Construct the page request */ + buf[0] = SesStringOut; + buf[1] = 0; + buf[2] = sstr->bufsiz >> 8; + buf[3] = sstr->bufsiz & 0xff; + memcpy(&buf[4], sstr->buf, sstr->bufsiz); + } else if (ioc == ENCIOC_GETSTRING) { + payload = sstr->bufsiz; + amt = payload; + ses_page_cdb(cdb, payload, SesStringIn, CAM_DIR_IN); + buf = sstr->buf; + } else + return EINVAL; + + ret = enc_runcmd(enc, cdb, 6, buf, &amt); + if (ioc == ENCIOC_SETSTRING) + ENC_FREE(buf); + return ret; +} + +/** + * \invariant Called with cam_periph mutex held. + */ +static void +ses_poll_status(enc_softc_t *enc) +{ + enc_update_request(enc, SES_UPDATE_GETSTATUS); + enc_update_request(enc, SES_UPDATE_GETELMADDLSTATUS); +} + +/** + * \brief Notification received when CAM detects a new device in the + * SCSI domain in which this SEP resides. + * + * \param enc SES enclosure instance. + */ +static void +ses_device_found(enc_softc_t *enc) +{ + ses_poll_status(enc); + enc_update_request(enc, SES_PUBLISH_PHYSPATHS); +} + +static struct enc_vec ses_enc_vec = +{ + .softc_invalidate = ses_softc_invalidate, + .softc_cleanup = ses_softc_cleanup, + .init_enc = ses_init_enc, + .get_enc_status = ses_get_enc_status, + .set_enc_status = ses_set_enc_status, + .get_elm_status = ses_get_elm_status, + .set_elm_status = ses_set_elm_status, + .get_elm_desc = ses_get_elm_desc, + .get_elm_devnames = ses_get_elm_devnames, + .handle_string = ses_handle_string, + .device_found = ses_device_found, + .poll_status = ses_poll_status +}; + +/** + * \brief Initialize a new SES instance. + * + * \param enc SES softc structure to set up the instance in. + * \param doinit Do the initialization (see main driver). + * + * \return 0 on success, errno otherwise. + */ +int +ses_softc_init(enc_softc_t *enc, int doinit) +{ + ses_softc_t *ses_softc; + + CAM_DEBUG(enc->periph->path, CAM_DEBUG_SUBTRACE, + ("entering enc_softc_init(%p)\n", enc)); + if (doinit == 0) { + ses_softc_cleanup(enc->periph); + return (0); + } + + enc->enc_vec = ses_enc_vec; + enc->enc_fsm_states = enc_fsm_states; + + if (enc->enc_private == NULL) + enc->enc_private = ENC_MALLOCZ(sizeof(ses_softc_t)); + if (enc->enc_cache.private == NULL) + enc->enc_cache.private = ENC_MALLOCZ(sizeof(ses_cache_t)); + if (enc->enc_daemon_cache.private == NULL) + enc->enc_daemon_cache.private = + ENC_MALLOCZ(sizeof(ses_cache_t)); + + if (enc->enc_private == NULL + || enc->enc_cache.private == NULL + || enc->enc_daemon_cache.private == NULL) { + ENC_FREE_AND_NULL(enc->enc_private); + ENC_FREE_AND_NULL(enc->enc_cache.private); + ENC_FREE_AND_NULL(enc->enc_daemon_cache.private); + return (ENOMEM); + } + + ses_softc = enc->enc_private; + TAILQ_INIT(&ses_softc->ses_requests); + TAILQ_INIT(&ses_softc->ses_pending_requests); + + // XXX: Move this to the FSM so it doesn't hang init + if (0) (void) ses_set_timed_completion(enc, 1); + + return (0); +} + diff --git a/sys/cam/scsi/scsi_ses.c b/sys/cam/scsi/scsi_ses.c deleted file mode 100644 index 2275654c141..00000000000 --- a/sys/cam/scsi/scsi_ses.c +++ /dev/null @@ -1,2535 +0,0 @@ -/*- - * Copyright (c) 2000 Matthew Jacob - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification, immediately at the beginning of the file. - * 2. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -MALLOC_DEFINE(M_SCSISES, "SCSI SES", "SCSI SES buffers"); - -/* - * Platform Independent Driver Internal Definitions for SES devices. - */ -typedef enum { - SES_NONE, - SES_SES_SCSI2, - SES_SES, - SES_SES_PASSTHROUGH, - SES_SEN, - SES_SAFT -} enctyp; - -struct ses_softc; -typedef struct ses_softc ses_softc_t; -typedef struct { - int (*softc_init)(ses_softc_t *, int); - int (*init_enc)(ses_softc_t *); - int (*get_encstat)(ses_softc_t *, int); - int (*set_encstat)(ses_softc_t *, ses_encstat, int); - int (*get_objstat)(ses_softc_t *, ses_objstat *, int); - int (*set_objstat)(ses_softc_t *, ses_objstat *, int); -} encvec; - -#define ENCI_SVALID 0x80 - -typedef struct { - uint32_t - enctype : 8, /* enclosure type */ - subenclosure : 8, /* subenclosure id */ - svalid : 1, /* enclosure information valid */ - priv : 15; /* private data, per object */ - uint8_t encstat[4]; /* state && stats */ -} encobj; - -#define SEN_ID "UNISYS SUN_SEN" -#define SEN_ID_LEN 24 - - -static enctyp ses_type(void *, int); - - -/* Forward reference to Enclosure Functions */ -static int ses_softc_init(ses_softc_t *, int); -static int ses_init_enc(ses_softc_t *); -static int ses_get_encstat(ses_softc_t *, int); -static int ses_set_encstat(ses_softc_t *, uint8_t, int); -static int ses_get_objstat(ses_softc_t *, ses_objstat *, int); -static int ses_set_objstat(ses_softc_t *, ses_objstat *, int); - -static int safte_softc_init(ses_softc_t *, int); -static int safte_init_enc(ses_softc_t *); -static int safte_get_encstat(ses_softc_t *, int); -static int safte_set_encstat(ses_softc_t *, uint8_t, int); -static int safte_get_objstat(ses_softc_t *, ses_objstat *, int); -static int safte_set_objstat(ses_softc_t *, ses_objstat *, int); - -/* - * Platform implementation defines/functions for SES internal kernel stuff - */ - -#define STRNCMP strncmp -#define PRINTF printf -#define SES_LOG ses_log -#ifdef DEBUG -#define SES_DLOG ses_log -#else -#define SES_DLOG if (0) ses_log -#endif -#define SES_VLOG if (bootverbose) ses_log -#define SES_MALLOC(amt) malloc(amt, M_SCSISES, M_NOWAIT) -#define SES_FREE(ptr, amt) free(ptr, M_SCSISES) -#define MEMZERO bzero -#define MEMCPY(dest, src, amt) bcopy(src, dest, amt) - -static int ses_runcmd(struct ses_softc *, char *, int, char *, int *); -static void ses_log(struct ses_softc *, const char *, ...); - -/* - * Gerenal FreeBSD kernel stuff. - */ - - -#define ccb_state ppriv_field0 -#define ccb_bp ppriv_ptr1 - -struct ses_softc { - enctyp ses_type; /* type of enclosure */ - encvec ses_vec; /* vector to handlers */ - void * ses_private; /* per-type private data */ - encobj * ses_objmap; /* objects */ - uint32_t ses_nobjects; /* number of objects */ - ses_encstat ses_encstat; /* overall status */ - uint8_t ses_flags; - union ccb ses_saved_ccb; - struct cdev *ses_dev; - struct cam_periph *periph; -}; -#define SES_FLAG_INVALID 0x01 -#define SES_FLAG_OPEN 0x02 -#define SES_FLAG_INITIALIZED 0x04 - -static d_open_t sesopen; -static d_close_t sesclose; -static d_ioctl_t sesioctl; -static periph_init_t sesinit; -static periph_ctor_t sesregister; -static periph_oninv_t sesoninvalidate; -static periph_dtor_t sescleanup; -static periph_start_t sesstart; - -static void sesasync(void *, uint32_t, struct cam_path *, void *); -static void sesdone(struct cam_periph *, union ccb *); -static int seserror(union ccb *, uint32_t, uint32_t); - -static struct periph_driver sesdriver = { - sesinit, "ses", - TAILQ_HEAD_INITIALIZER(sesdriver.units), /* generation */ 0 -}; - -PERIPHDRIVER_DECLARE(ses, sesdriver); - -static struct cdevsw ses_cdevsw = { - .d_version = D_VERSION, - .d_open = sesopen, - .d_close = sesclose, - .d_ioctl = sesioctl, - .d_name = "ses", - .d_flags = 0, -}; - -static void -sesinit(void) -{ - cam_status status; - - /* - * Install a global async callback. This callback will - * receive async callbacks like "new device found". - */ - status = xpt_register_async(AC_FOUND_DEVICE, sesasync, NULL, NULL); - - if (status != CAM_REQ_CMP) { - printf("ses: Failed to attach master async callback " - "due to status 0x%x!\n", status); - } -} - -static void -sesoninvalidate(struct cam_periph *periph) -{ - struct ses_softc *softc; - - softc = (struct ses_softc *)periph->softc; - - /* - * Unregister any async callbacks. - */ - xpt_register_async(0, sesasync, periph, periph->path); - - softc->ses_flags |= SES_FLAG_INVALID; - - xpt_print(periph->path, "lost device\n"); -} - -static void -sescleanup(struct cam_periph *periph) -{ - struct ses_softc *softc; - - softc = (struct ses_softc *)periph->softc; - - xpt_print(periph->path, "removing device entry\n"); - cam_periph_unlock(periph); - destroy_dev(softc->ses_dev); - cam_periph_lock(periph); - free(softc, M_SCSISES); -} - -static void -sesasync(void *callback_arg, uint32_t code, struct cam_path *path, void *arg) -{ - struct cam_periph *periph; - - periph = (struct cam_periph *)callback_arg; - - switch(code) { - case AC_FOUND_DEVICE: - { - cam_status status; - struct ccb_getdev *cgd; - int inq_len; - - cgd = (struct ccb_getdev *)arg; - if (arg == NULL) { - break; - } - - if (cgd->protocol != PROTO_SCSI) - break; - - inq_len = cgd->inq_data.additional_length + 4; - - /* - * PROBLEM: WE NEED TO LOOK AT BYTES 48-53 TO SEE IF THIS IS - * PROBLEM: IS A SAF-TE DEVICE. - */ - switch (ses_type(&cgd->inq_data, inq_len)) { - case SES_SES: - case SES_SES_SCSI2: - case SES_SES_PASSTHROUGH: - case SES_SEN: - case SES_SAFT: - break; - default: - return; - } - - status = cam_periph_alloc(sesregister, sesoninvalidate, - sescleanup, sesstart, "ses", CAM_PERIPH_BIO, - cgd->ccb_h.path, sesasync, AC_FOUND_DEVICE, cgd); - - if (status != CAM_REQ_CMP && status != CAM_REQ_INPROG) { - printf("sesasync: Unable to probe new device due to " - "status 0x%x\n", status); - } - break; - } - default: - cam_periph_async(periph, code, path, arg); - break; - } -} - -static cam_status -sesregister(struct cam_periph *periph, void *arg) -{ - struct ses_softc *softc; - struct ccb_getdev *cgd; - char *tname; - - cgd = (struct ccb_getdev *)arg; - if (periph == NULL) { - printf("sesregister: periph was NULL!!\n"); - return (CAM_REQ_CMP_ERR); - } - - if (cgd == NULL) { - printf("sesregister: no getdev CCB, can't register device\n"); - return (CAM_REQ_CMP_ERR); - } - - softc = SES_MALLOC(sizeof (struct ses_softc)); - if (softc == NULL) { - printf("sesregister: Unable to probe new device. " - "Unable to allocate softc\n"); - return (CAM_REQ_CMP_ERR); - } - bzero(softc, sizeof (struct ses_softc)); - periph->softc = softc; - softc->periph = periph; - - softc->ses_type = ses_type(&cgd->inq_data, sizeof (cgd->inq_data)); - - switch (softc->ses_type) { - case SES_SES: - case SES_SES_SCSI2: - case SES_SES_PASSTHROUGH: - softc->ses_vec.softc_init = ses_softc_init; - softc->ses_vec.init_enc = ses_init_enc; - softc->ses_vec.get_encstat = ses_get_encstat; - softc->ses_vec.set_encstat = ses_set_encstat; - softc->ses_vec.get_objstat = ses_get_objstat; - softc->ses_vec.set_objstat = ses_set_objstat; - break; - case SES_SAFT: - softc->ses_vec.softc_init = safte_softc_init; - softc->ses_vec.init_enc = safte_init_enc; - softc->ses_vec.get_encstat = safte_get_encstat; - softc->ses_vec.set_encstat = safte_set_encstat; - softc->ses_vec.get_objstat = safte_get_objstat; - softc->ses_vec.set_objstat = safte_set_objstat; - break; - case SES_SEN: - break; - case SES_NONE: - default: - free(softc, M_SCSISES); - return (CAM_REQ_CMP_ERR); - } - - cam_periph_unlock(periph); - softc->ses_dev = make_dev(&ses_cdevsw, periph->unit_number, - UID_ROOT, GID_OPERATOR, 0600, "%s%d", - periph->periph_name, periph->unit_number); - cam_periph_lock(periph); - softc->ses_dev->si_drv1 = periph; - - /* - * Add an async callback so that we get - * notified if this device goes away. - */ - xpt_register_async(AC_LOST_DEVICE, sesasync, periph, periph->path); - - switch (softc->ses_type) { - default: - case SES_NONE: - tname = "No SES device"; - break; - case SES_SES_SCSI2: - tname = "SCSI-2 SES Device"; - break; - case SES_SES: - tname = "SCSI-3 SES Device"; - break; - case SES_SES_PASSTHROUGH: - tname = "SES Passthrough Device"; - break; - case SES_SEN: - tname = "UNISYS SEN Device (NOT HANDLED YET)"; - break; - case SES_SAFT: - tname = "SAF-TE Compliant Device"; - break; - } - xpt_announce_periph(periph, tname); - return (CAM_REQ_CMP); -} - -static int -sesopen(struct cdev *dev, int flags, int fmt, struct thread *td) -{ - struct cam_periph *periph; - struct ses_softc *softc; - int error = 0; - - periph = (struct cam_periph *)dev->si_drv1; - if (periph == NULL) { - return (ENXIO); - } - - if (cam_periph_acquire(periph) != CAM_REQ_CMP) { - cam_periph_unlock(periph); - return (ENXIO); - } - - cam_periph_lock(periph); - - softc = (struct ses_softc *)periph->softc; - - if (softc->ses_flags & SES_FLAG_INVALID) { - error = ENXIO; - goto out; - } - if (softc->ses_flags & SES_FLAG_OPEN) { - error = EBUSY; - goto out; - } - if (softc->ses_vec.softc_init == NULL) { - error = ENXIO; - goto out; - } - - softc->ses_flags |= SES_FLAG_OPEN; - if ((softc->ses_flags & SES_FLAG_INITIALIZED) == 0) { - error = (*softc->ses_vec.softc_init)(softc, 1); - if (error) - softc->ses_flags &= ~SES_FLAG_OPEN; - else - softc->ses_flags |= SES_FLAG_INITIALIZED; - } - -out: - cam_periph_unlock(periph); - if (error) { - cam_periph_release(periph); - } - return (error); -} - -static int -sesclose(struct cdev *dev, int flag, int fmt, struct thread *td) -{ - struct cam_periph *periph; - struct ses_softc *softc; - int error; - - error = 0; - - periph = (struct cam_periph *)dev->si_drv1; - if (periph == NULL) - return (ENXIO); - - cam_periph_lock(periph); - - softc = (struct ses_softc *)periph->softc; - softc->ses_flags &= ~SES_FLAG_OPEN; - - cam_periph_unlock(periph); - cam_periph_release(periph); - - return (0); -} - -static void -sesstart(struct cam_periph *p, union ccb *sccb) -{ - if (p->immediate_priority <= p->pinfo.priority) { - SLIST_INSERT_HEAD(&p->ccb_list, &sccb->ccb_h, periph_links.sle); - p->immediate_priority = CAM_PRIORITY_NONE; - wakeup(&p->ccb_list); - } -} - -static void -sesdone(struct cam_periph *periph, union ccb *dccb) -{ - wakeup(&dccb->ccb_h.cbfcnp); -} - -static int -seserror(union ccb *ccb, uint32_t cflags, uint32_t sflags) -{ - struct ses_softc *softc; - struct cam_periph *periph; - - periph = xpt_path_periph(ccb->ccb_h.path); - softc = (struct ses_softc *)periph->softc; - - return (cam_periph_error(ccb, cflags, sflags, &softc->ses_saved_ccb)); -} - -static int -sesioctl(struct cdev *dev, u_long cmd, caddr_t arg_addr, int flag, struct thread *td) -{ - struct cam_periph *periph; - ses_encstat tmp; - ses_objstat objs; - ses_object *uobj; - struct ses_softc *ssc; - void *addr; - int error, i; - - - if (arg_addr) - addr = *((caddr_t *) arg_addr); - else - addr = NULL; - - periph = (struct cam_periph *)dev->si_drv1; - if (periph == NULL) - return (ENXIO); - - CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering sesioctl\n")); - - cam_periph_lock(periph); - ssc = (struct ses_softc *)periph->softc; - - /* - * Now check to see whether we're initialized or not. - * This actually should never fail as we're not supposed - * to get past ses_open w/o successfully initializing - * things. - */ - if ((ssc->ses_flags & SES_FLAG_INITIALIZED) == 0) { - cam_periph_unlock(periph); - return (ENXIO); - } - cam_periph_unlock(periph); - - error = 0; - - CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, - ("trying to do ioctl %#lx\n", cmd)); - - /* - * If this command can change the device's state, - * we must have the device open for writing. - * - * For commands that get information about the - * device- we don't need to lock the peripheral - * if we aren't running a command. The number - * of objects and the contents will stay stable - * after the first open that does initialization. - * The periph also can't go away while a user - * process has it open. - */ - switch (cmd) { - case SESIOC_GETNOBJ: - case SESIOC_GETOBJMAP: - case SESIOC_GETENCSTAT: - case SESIOC_GETOBJSTAT: - break; - default: - if ((flag & FWRITE) == 0) { - return (EBADF); - } - } - - switch (cmd) { - case SESIOC_GETNOBJ: - error = copyout(&ssc->ses_nobjects, addr, - sizeof (ssc->ses_nobjects)); - break; - - case SESIOC_GETOBJMAP: - for (uobj = addr, i = 0; i != ssc->ses_nobjects; i++) { - ses_object kobj; - kobj.obj_id = i; - kobj.subencid = ssc->ses_objmap[i].subenclosure; - kobj.object_type = ssc->ses_objmap[i].enctype; - error = copyout(&kobj, &uobj[i], sizeof (ses_object)); - if (error) { - break; - } - } - break; - - case SESIOC_GETENCSTAT: - cam_periph_lock(periph); - error = (*ssc->ses_vec.get_encstat)(ssc, 1); - if (error) { - cam_periph_unlock(periph); - break; - } - tmp = ssc->ses_encstat & ~ENCI_SVALID; - cam_periph_unlock(periph); - error = copyout(&tmp, addr, sizeof (ses_encstat)); - ssc->ses_encstat = tmp; - break; - - case SESIOC_SETENCSTAT: - error = copyin(addr, &tmp, sizeof (ses_encstat)); - if (error) - break; - cam_periph_lock(periph); - error = (*ssc->ses_vec.set_encstat)(ssc, tmp, 1); - cam_periph_unlock(periph); - break; - - case SESIOC_GETOBJSTAT: - error = copyin(addr, &objs, sizeof (ses_objstat)); - if (error) - break; - if (objs.obj_id >= ssc->ses_nobjects) { - error = EINVAL; - break; - } - cam_periph_lock(periph); - error = (*ssc->ses_vec.get_objstat)(ssc, &objs, 1); - cam_periph_unlock(periph); - if (error) - break; - error = copyout(&objs, addr, sizeof (ses_objstat)); - /* - * Always (for now) invalidate entry. - */ - ssc->ses_objmap[objs.obj_id].svalid = 0; - break; - - case SESIOC_SETOBJSTAT: - error = copyin(addr, &objs, sizeof (ses_objstat)); - if (error) - break; - - if (objs.obj_id >= ssc->ses_nobjects) { - error = EINVAL; - break; - } - cam_periph_lock(periph); - error = (*ssc->ses_vec.set_objstat)(ssc, &objs, 1); - cam_periph_unlock(periph); - - /* - * Always (for now) invalidate entry. - */ - ssc->ses_objmap[objs.obj_id].svalid = 0; - break; - - case SESIOC_INIT: - - cam_periph_lock(periph); - error = (*ssc->ses_vec.init_enc)(ssc); - cam_periph_unlock(periph); - break; - - default: - cam_periph_lock(periph); - error = cam_periph_ioctl(periph, cmd, arg_addr, seserror); - cam_periph_unlock(periph); - break; - } - return (error); -} - -#define SES_CFLAGS CAM_RETRY_SELTO -#define SES_FLAGS SF_NO_PRINT | SF_RETRY_UA -static int -ses_runcmd(struct ses_softc *ssc, char *cdb, int cdbl, char *dptr, int *dlenp) -{ - int error, dlen; - ccb_flags ddf; - union ccb *ccb; - - if (dptr) { - if ((dlen = *dlenp) < 0) { - dlen = -dlen; - ddf = CAM_DIR_OUT; - } else { - ddf = CAM_DIR_IN; - } - } else { - dlen = 0; - ddf = CAM_DIR_NONE; - } - - if (cdbl > IOCDBLEN) { - cdbl = IOCDBLEN; - } - - ccb = cam_periph_getccb(ssc->periph, 1); - cam_fill_csio(&ccb->csio, 0, sesdone, ddf, MSG_SIMPLE_Q_TAG, dptr, - dlen, sizeof (struct scsi_sense_data), cdbl, 60 * 1000); - bcopy(cdb, ccb->csio.cdb_io.cdb_bytes, cdbl); - - error = cam_periph_runccb(ccb, seserror, SES_CFLAGS, SES_FLAGS, NULL); - if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) - cam_release_devq(ccb->ccb_h.path, 0, 0, 0, FALSE); - if (error) { - if (dptr) { - *dlenp = dlen; - } - } else { - if (dptr) { - *dlenp = ccb->csio.resid; - } - } - xpt_release_ccb(ccb); - return (error); -} - -static void -ses_log(struct ses_softc *ssc, const char *fmt, ...) -{ - va_list ap; - - printf("%s%d: ", ssc->periph->periph_name, ssc->periph->unit_number); - va_start(ap, fmt); - vprintf(fmt, ap); - va_end(ap); -} - -/* - * The code after this point runs on many platforms, - * so forgive the slightly awkward and nonconforming - * appearance. - */ - -/* - * Is this a device that supports enclosure services? - * - * It's a a pretty simple ruleset- if it is device type 0x0D (13), it's - * an SES device. If it happens to be an old UNISYS SEN device, we can - * handle that too. - */ - -#define SAFTE_START 44 -#define SAFTE_END 50 -#define SAFTE_LEN SAFTE_END-SAFTE_START - -static enctyp -ses_type(void *buf, int buflen) -{ - unsigned char *iqd = buf; - - if (buflen < 8+SEN_ID_LEN) - return (SES_NONE); - - if ((iqd[0] & 0x1f) == T_ENCLOSURE) { - if (STRNCMP(&iqd[8], SEN_ID, SEN_ID_LEN) == 0) { - return (SES_SEN); - } else if ((iqd[2] & 0x7) > 2) { - return (SES_SES); - } else { - return (SES_SES_SCSI2); - } - return (SES_NONE); - } - -#ifdef SES_ENABLE_PASSTHROUGH - if ((iqd[6] & 0x40) && (iqd[2] & 0x7) >= 2) { - /* - * PassThrough Device. - */ - return (SES_SES_PASSTHROUGH); - } -#endif - - /* - * The comparison is short for a reason- - * some vendors were chopping it short. - */ - - if (buflen < SAFTE_END - 2) { - return (SES_NONE); - } - - if (STRNCMP((char *)&iqd[SAFTE_START], "SAF-TE", SAFTE_LEN - 2) == 0) { - return (SES_SAFT); - } - return (SES_NONE); -} - -/* - * SES Native Type Device Support - */ - -/* - * SES Diagnostic Page Codes - */ - -typedef enum { - SesConfigPage = 0x1, - SesControlPage, -#define SesStatusPage SesControlPage - SesHelpTxt, - SesStringOut, -#define SesStringIn SesStringOut - SesThresholdOut, -#define SesThresholdIn SesThresholdOut - SesArrayControl, -#define SesArrayStatus SesArrayControl - SesElementDescriptor, - SesShortStatus -} SesDiagPageCodes; - -/* - * minimal amounts - */ - -/* - * Minimum amount of data, starting from byte 0, to have - * the config header. - */ -#define SES_CFGHDR_MINLEN 12 - -/* - * Minimum amount of data, starting from byte 0, to have - * the config header and one enclosure header. - */ -#define SES_ENCHDR_MINLEN 48 - -/* - * Take this value, subtract it from VEnclen and you know - * the length of the vendor unique bytes. - */ -#define SES_ENCHDR_VMIN 36 - -/* - * SES Data Structures - */ - -typedef struct { - uint32_t GenCode; /* Generation Code */ - uint8_t Nsubenc; /* Number of Subenclosures */ -} SesCfgHdr; - -typedef struct { - uint8_t Subencid; /* SubEnclosure Identifier */ - uint8_t Ntypes; /* # of supported types */ - uint8_t VEnclen; /* Enclosure Descriptor Length */ -} SesEncHdr; - -typedef struct { - uint8_t encWWN[8]; /* XXX- Not Right Yet */ - uint8_t encVid[8]; - uint8_t encPid[16]; - uint8_t encRev[4]; - uint8_t encVen[1]; -} SesEncDesc; - -typedef struct { - uint8_t enc_type; /* type of element */ - uint8_t enc_maxelt; /* maximum supported */ - uint8_t enc_subenc; /* in SubEnc # N */ - uint8_t enc_tlen; /* Type Descriptor Text Length */ -} SesThdr; - -typedef struct { - uint8_t comstatus; - uint8_t comstat[3]; -} SesComStat; - -struct typidx { - int ses_tidx; - int ses_oidx; -}; - -struct sscfg { - uint8_t ses_ntypes; /* total number of types supported */ - - /* - * We need to keep a type index as well as an - * object index for each object in an enclosure. - */ - struct typidx *ses_typidx; - - /* - * We also need to keep track of the number of elements - * per type of element. This is needed later so that we - * can find precisely in the returned status data the - * status for the Nth element of the Kth type. - */ - uint8_t * ses_eltmap; -}; - - -/* - * (de)canonicalization defines - */ -#define sbyte(x, byte) ((((uint32_t)(x)) >> (byte * 8)) & 0xff) -#define sbit(x, bit) (((uint32_t)(x)) << bit) -#define sset8(outp, idx, sval) (((uint8_t *)(outp))[idx++]) = sbyte(sval, 0) - -#define sset16(outp, idx, sval) \ - (((uint8_t *)(outp))[idx++]) = sbyte(sval, 1), \ - (((uint8_t *)(outp))[idx++]) = sbyte(sval, 0) - - -#define sset24(outp, idx, sval) \ - (((uint8_t *)(outp))[idx++]) = sbyte(sval, 2), \ - (((uint8_t *)(outp))[idx++]) = sbyte(sval, 1), \ - (((uint8_t *)(outp))[idx++]) = sbyte(sval, 0) - - -#define sset32(outp, idx, sval) \ - (((uint8_t *)(outp))[idx++]) = sbyte(sval, 3), \ - (((uint8_t *)(outp))[idx++]) = sbyte(sval, 2), \ - (((uint8_t *)(outp))[idx++]) = sbyte(sval, 1), \ - (((uint8_t *)(outp))[idx++]) = sbyte(sval, 0) - -#define gbyte(x, byte) ((((uint32_t)(x)) & 0xff) << (byte * 8)) -#define gbit(lv, in, idx, shft, mask) lv = ((in[idx] >> shft) & mask) -#define sget8(inp, idx, lval) lval = (((uint8_t *)(inp))[idx++]) -#define gget8(inp, idx, lval) lval = (((uint8_t *)(inp))[idx]) - -#define sget16(inp, idx, lval) \ - lval = gbyte((((uint8_t *)(inp))[idx]), 1) | \ - (((uint8_t *)(inp))[idx+1]), idx += 2 - -#define gget16(inp, idx, lval) \ - lval = gbyte((((uint8_t *)(inp))[idx]), 1) | \ - (((uint8_t *)(inp))[idx+1]) - -#define sget24(inp, idx, lval) \ - lval = gbyte((((uint8_t *)(inp))[idx]), 2) | \ - gbyte((((uint8_t *)(inp))[idx+1]), 1) | \ - (((uint8_t *)(inp))[idx+2]), idx += 3 - -#define gget24(inp, idx, lval) \ - lval = gbyte((((uint8_t *)(inp))[idx]), 2) | \ - gbyte((((uint8_t *)(inp))[idx+1]), 1) | \ - (((uint8_t *)(inp))[idx+2]) - -#define sget32(inp, idx, lval) \ - lval = gbyte((((uint8_t *)(inp))[idx]), 3) | \ - gbyte((((uint8_t *)(inp))[idx+1]), 2) | \ - gbyte((((uint8_t *)(inp))[idx+2]), 1) | \ - (((uint8_t *)(inp))[idx+3]), idx += 4 - -#define gget32(inp, idx, lval) \ - lval = gbyte((((uint8_t *)(inp))[idx]), 3) | \ - gbyte((((uint8_t *)(inp))[idx+1]), 2) | \ - gbyte((((uint8_t *)(inp))[idx+2]), 1) | \ - (((uint8_t *)(inp))[idx+3]) - -#define SCSZ 0x2000 -#define CFLEN (256 + SES_ENCHDR_MINLEN) - -/* - * Routines specific && private to SES only - */ - -static int ses_getconfig(ses_softc_t *); -static int ses_getputstat(ses_softc_t *, int, SesComStat *, int, int); -static int ses_cfghdr(uint8_t *, int, SesCfgHdr *); -static int ses_enchdr(uint8_t *, int, uint8_t, SesEncHdr *); -static int ses_encdesc(uint8_t *, int, uint8_t, SesEncDesc *); -static int ses_getthdr(uint8_t *, int, int, SesThdr *); -static int ses_decode(char *, int, uint8_t *, int, int, SesComStat *); -static int ses_encode(char *, int, uint8_t *, int, int, SesComStat *); - -static int -ses_softc_init(ses_softc_t *ssc, int doinit) -{ - if (doinit == 0) { - struct sscfg *cc; - if (ssc->ses_nobjects) { - SES_FREE(ssc->ses_objmap, - ssc->ses_nobjects * sizeof (encobj)); - ssc->ses_objmap = NULL; - } - if ((cc = ssc->ses_private) != NULL) { - if (cc->ses_eltmap && cc->ses_ntypes) { - SES_FREE(cc->ses_eltmap, cc->ses_ntypes); - cc->ses_eltmap = NULL; - cc->ses_ntypes = 0; - } - if (cc->ses_typidx && ssc->ses_nobjects) { - SES_FREE(cc->ses_typidx, - ssc->ses_nobjects * sizeof (struct typidx)); - cc->ses_typidx = NULL; - } - SES_FREE(cc, sizeof (struct sscfg)); - ssc->ses_private = NULL; - } - ssc->ses_nobjects = 0; - return (0); - } - if (ssc->ses_private == NULL) { - ssc->ses_private = SES_MALLOC(sizeof (struct sscfg)); - } - if (ssc->ses_private == NULL) { - return (ENOMEM); - } - ssc->ses_nobjects = 0; - ssc->ses_encstat = 0; - return (ses_getconfig(ssc)); -} - -static int -ses_init_enc(ses_softc_t *ssc) -{ - return (0); -} - -static int -ses_get_encstat(ses_softc_t *ssc, int slpflag) -{ - SesComStat ComStat; - int status; - - if ((status = ses_getputstat(ssc, -1, &ComStat, slpflag, 1)) != 0) { - return (status); - } - ssc->ses_encstat = ComStat.comstatus | ENCI_SVALID; - return (0); -} - -static int -ses_set_encstat(ses_softc_t *ssc, uint8_t encstat, int slpflag) -{ - SesComStat ComStat; - int status; - - ComStat.comstatus = encstat & 0xf; - if ((status = ses_getputstat(ssc, -1, &ComStat, slpflag, 0)) != 0) { - return (status); - } - ssc->ses_encstat = encstat & 0xf; /* note no SVALID set */ - return (0); -} - -static int -ses_get_objstat(ses_softc_t *ssc, ses_objstat *obp, int slpflag) -{ - int i = (int)obp->obj_id; - - if (ssc->ses_objmap[i].svalid == 0) { - SesComStat ComStat; - int err = ses_getputstat(ssc, i, &ComStat, slpflag, 1); - if (err) - return (err); - ssc->ses_objmap[i].encstat[0] = ComStat.comstatus; - ssc->ses_objmap[i].encstat[1] = ComStat.comstat[0]; - ssc->ses_objmap[i].encstat[2] = ComStat.comstat[1]; - ssc->ses_objmap[i].encstat[3] = ComStat.comstat[2]; - ssc->ses_objmap[i].svalid = 1; - } - obp->cstat[0] = ssc->ses_objmap[i].encstat[0]; - obp->cstat[1] = ssc->ses_objmap[i].encstat[1]; - obp->cstat[2] = ssc->ses_objmap[i].encstat[2]; - obp->cstat[3] = ssc->ses_objmap[i].encstat[3]; - return (0); -} - -static int -ses_set_objstat(ses_softc_t *ssc, ses_objstat *obp, int slpflag) -{ - SesComStat ComStat; - int err; - /* - * If this is clear, we don't do diddly. - */ - if ((obp->cstat[0] & SESCTL_CSEL) == 0) { - return (0); - } - ComStat.comstatus = obp->cstat[0]; - ComStat.comstat[0] = obp->cstat[1]; - ComStat.comstat[1] = obp->cstat[2]; - ComStat.comstat[2] = obp->cstat[3]; - err = ses_getputstat(ssc, (int)obp->obj_id, &ComStat, slpflag, 0); - ssc->ses_objmap[(int)obp->obj_id].svalid = 0; - return (err); -} - -static int -ses_getconfig(ses_softc_t *ssc) -{ - struct sscfg *cc; - SesCfgHdr cf; - SesEncHdr hd; - SesEncDesc *cdp; - SesThdr thdr; - int err, amt, i, nobj, ntype, maxima; - char storage[CFLEN], *sdata; - static char cdb[6] = { - RECEIVE_DIAGNOSTIC, 0x1, SesConfigPage, SCSZ >> 8, SCSZ & 0xff, 0 - }; - - cc = ssc->ses_private; - if (cc == NULL) { - return (ENXIO); - } - - sdata = SES_MALLOC(SCSZ); - if (sdata == NULL) - return (ENOMEM); - - amt = SCSZ; - err = ses_runcmd(ssc, cdb, 6, sdata, &amt); - if (err) { - SES_FREE(sdata, SCSZ); - return (err); - } - amt = SCSZ - amt; - - if (ses_cfghdr((uint8_t *) sdata, amt, &cf)) { - SES_LOG(ssc, "Unable to parse SES Config Header\n"); - SES_FREE(sdata, SCSZ); - return (EIO); - } - if (amt < SES_ENCHDR_MINLEN) { - SES_LOG(ssc, "runt enclosure length (%d)\n", amt); - SES_FREE(sdata, SCSZ); - return (EIO); - } - - SES_VLOG(ssc, "GenCode %x %d Subenclosures\n", cf.GenCode, cf.Nsubenc); - - /* - * Now waltz through all the subenclosures toting up the - * number of types available in each. For this, we only - * really need the enclosure header. However, we get the - * enclosure descriptor for debug purposes, as well - * as self-consistency checking purposes. - */ - - maxima = cf.Nsubenc + 1; - cdp = (SesEncDesc *) storage; - for (ntype = i = 0; i < maxima; i++) { - MEMZERO((caddr_t)cdp, sizeof (*cdp)); - if (ses_enchdr((uint8_t *) sdata, amt, i, &hd)) { - SES_LOG(ssc, "Cannot Extract Enclosure Header %d\n", i); - SES_FREE(sdata, SCSZ); - return (EIO); - } - SES_VLOG(ssc, " SubEnclosure ID %d, %d Types With this ID, En" - "closure Length %d\n", hd.Subencid, hd.Ntypes, hd.VEnclen); - - if (ses_encdesc((uint8_t *)sdata, amt, i, cdp)) { - SES_LOG(ssc, "Can't get Enclosure Descriptor %d\n", i); - SES_FREE(sdata, SCSZ); - return (EIO); - } - SES_VLOG(ssc, " WWN: %02x%02x%02x%02x%02x%02x%02x%02x\n", - cdp->encWWN[0], cdp->encWWN[1], cdp->encWWN[2], - cdp->encWWN[3], cdp->encWWN[4], cdp->encWWN[5], - cdp->encWWN[6], cdp->encWWN[7]); - ntype += hd.Ntypes; - } - - /* - * Now waltz through all the types that are available, getting - * the type header so we can start adding up the number of - * objects available. - */ - for (nobj = i = 0; i < ntype; i++) { - if (ses_getthdr((uint8_t *)sdata, amt, i, &thdr)) { - SES_LOG(ssc, "Can't get Enclosure Type Header %d\n", i); - SES_FREE(sdata, SCSZ); - return (EIO); - } - SES_LOG(ssc, " Type Desc[%d]: Type 0x%x, MaxElt %d, In Subenc " - "%d, Text Length %d\n", i, thdr.enc_type, thdr.enc_maxelt, - thdr.enc_subenc, thdr.enc_tlen); - nobj += thdr.enc_maxelt; - } - - - /* - * Now allocate the object array and type map. - */ - - ssc->ses_objmap = SES_MALLOC(nobj * sizeof (encobj)); - cc->ses_typidx = SES_MALLOC(nobj * sizeof (struct typidx)); - cc->ses_eltmap = SES_MALLOC(ntype); - - if (ssc->ses_objmap == NULL || cc->ses_typidx == NULL || - cc->ses_eltmap == NULL) { - if (ssc->ses_objmap) { - SES_FREE(ssc->ses_objmap, (nobj * sizeof (encobj))); - ssc->ses_objmap = NULL; - } - if (cc->ses_typidx) { - SES_FREE(cc->ses_typidx, - (nobj * sizeof (struct typidx))); - cc->ses_typidx = NULL; - } - if (cc->ses_eltmap) { - SES_FREE(cc->ses_eltmap, ntype); - cc->ses_eltmap = NULL; - } - SES_FREE(sdata, SCSZ); - return (ENOMEM); - } - MEMZERO(ssc->ses_objmap, nobj * sizeof (encobj)); - MEMZERO(cc->ses_typidx, nobj * sizeof (struct typidx)); - MEMZERO(cc->ses_eltmap, ntype); - cc->ses_ntypes = (uint8_t) ntype; - ssc->ses_nobjects = nobj; - - /* - * Now waltz through the # of types again to fill in the types - * (and subenclosure ids) of the allocated objects. - */ - nobj = 0; - for (i = 0; i < ntype; i++) { - int j; - if (ses_getthdr((uint8_t *)sdata, amt, i, &thdr)) { - continue; - } - cc->ses_eltmap[i] = thdr.enc_maxelt; - for (j = 0; j < thdr.enc_maxelt; j++) { - cc->ses_typidx[nobj].ses_tidx = i; - cc->ses_typidx[nobj].ses_oidx = j; - ssc->ses_objmap[nobj].subenclosure = thdr.enc_subenc; - ssc->ses_objmap[nobj++].enctype = thdr.enc_type; - } - } - SES_FREE(sdata, SCSZ); - return (0); -} - -static int -ses_getputstat(ses_softc_t *ssc, int objid, SesComStat *sp, int slp, int in) -{ - struct sscfg *cc; - int err, amt, bufsiz, tidx, oidx; - char cdb[6], *sdata; - - cc = ssc->ses_private; - if (cc == NULL) { - return (ENXIO); - } - - /* - * If we're just getting overall enclosure status, - * we only need 2 bytes of data storage. - * - * If we're getting anything else, we know how much - * storage we need by noting that starting at offset - * 8 in returned data, all object status bytes are 4 - * bytes long, and are stored in chunks of types(M) - * and nth+1 instances of type M. - */ - if (objid == -1) { - bufsiz = 2; - } else { - bufsiz = (ssc->ses_nobjects * 4) + (cc->ses_ntypes * 4) + 8; - } - sdata = SES_MALLOC(bufsiz); - if (sdata == NULL) - return (ENOMEM); - - cdb[0] = RECEIVE_DIAGNOSTIC; - cdb[1] = 1; - cdb[2] = SesStatusPage; - cdb[3] = bufsiz >> 8; - cdb[4] = bufsiz & 0xff; - cdb[5] = 0; - amt = bufsiz; - err = ses_runcmd(ssc, cdb, 6, sdata, &amt); - if (err) { - SES_FREE(sdata, bufsiz); - return (err); - } - amt = bufsiz - amt; - - if (objid == -1) { - tidx = -1; - oidx = -1; - } else { - tidx = cc->ses_typidx[objid].ses_tidx; - oidx = cc->ses_typidx[objid].ses_oidx; - } - if (in) { - if (ses_decode(sdata, amt, cc->ses_eltmap, tidx, oidx, sp)) { - err = ENODEV; - } - } else { - if (ses_encode(sdata, amt, cc->ses_eltmap, tidx, oidx, sp)) { - err = ENODEV; - } else { - cdb[0] = SEND_DIAGNOSTIC; - cdb[1] = 0x10; - cdb[2] = 0; - cdb[3] = bufsiz >> 8; - cdb[4] = bufsiz & 0xff; - cdb[5] = 0; - amt = -bufsiz; - err = ses_runcmd(ssc, cdb, 6, sdata, &amt); - } - } - SES_FREE(sdata, bufsiz); - return (0); -} - - -/* - * Routines to parse returned SES data structures. - * Architecture and compiler independent. - */ - -static int -ses_cfghdr(uint8_t *buffer, int buflen, SesCfgHdr *cfp) -{ - if (buflen < SES_CFGHDR_MINLEN) { - return (-1); - } - gget8(buffer, 1, cfp->Nsubenc); - gget32(buffer, 4, cfp->GenCode); - return (0); -} - -static int -ses_enchdr(uint8_t *buffer, int amt, uint8_t SubEncId, SesEncHdr *chp) -{ - int s, off = 8; - for (s = 0; s < SubEncId; s++) { - if (off + 3 > amt) - return (-1); - off += buffer[off+3] + 4; - } - if (off + 3 > amt) { - return (-1); - } - gget8(buffer, off+1, chp->Subencid); - gget8(buffer, off+2, chp->Ntypes); - gget8(buffer, off+3, chp->VEnclen); - return (0); -} - -static int -ses_encdesc(uint8_t *buffer, int amt, uint8_t SubEncId, SesEncDesc *cdp) -{ - int s, e, enclen, off = 8; - for (s = 0; s < SubEncId; s++) { - if (off + 3 > amt) - return (-1); - off += buffer[off+3] + 4; - } - if (off + 3 > amt) { - return (-1); - } - gget8(buffer, off+3, enclen); - off += 4; - if (off >= amt) - return (-1); - - e = off + enclen; - if (e > amt) { - e = amt; - } - MEMCPY(cdp, &buffer[off], e - off); - return (0); -} - -static int -ses_getthdr(uint8_t *buffer, int amt, int nth, SesThdr *thp) -{ - int s, off = 8; - - if (amt < SES_CFGHDR_MINLEN) { - return (-1); - } - for (s = 0; s < buffer[1]; s++) { - if (off + 3 > amt) - return (-1); - off += buffer[off+3] + 4; - } - if (off + 3 > amt) { - return (-1); - } - off += buffer[off+3] + 4 + (nth * 4); - if (amt < (off + 4)) - return (-1); - - gget8(buffer, off++, thp->enc_type); - gget8(buffer, off++, thp->enc_maxelt); - gget8(buffer, off++, thp->enc_subenc); - gget8(buffer, off, thp->enc_tlen); - return (0); -} - -/* - * This function needs a little explanation. - * - * The arguments are: - * - * - * char *b, int amt - * - * These describes the raw input SES status data and length. - * - * uint8_t *ep - * - * This is a map of the number of types for each element type - * in the enclosure. - * - * int elt - * - * This is the element type being sought. If elt is -1, - * then overall enclosure status is being sought. - * - * int elm - * - * This is the ordinal Mth element of type elt being sought. - * - * SesComStat *sp - * - * This is the output area to store the status for - * the Mth element of type Elt. - */ - -static int -ses_decode(char *b, int amt, uint8_t *ep, int elt, int elm, SesComStat *sp) -{ - int idx, i; - - /* - * If it's overall enclosure status being sought, get that. - * We need at least 2 bytes of status data to get that. - */ - if (elt == -1) { - if (amt < 2) - return (-1); - gget8(b, 1, sp->comstatus); - sp->comstat[0] = 0; - sp->comstat[1] = 0; - sp->comstat[2] = 0; - return (0); - } - - /* - * Check to make sure that the Mth element is legal for type Elt. - */ - - if (elm >= ep[elt]) - return (-1); - - /* - * Starting at offset 8, start skipping over the storage - * for the element types we're not interested in. - */ - for (idx = 8, i = 0; i < elt; i++) { - idx += ((ep[i] + 1) * 4); - } - - /* - * Skip over Overall status for this element type. - */ - idx += 4; - - /* - * And skip to the index for the Mth element that we're going for. - */ - idx += (4 * elm); - - /* - * Make sure we haven't overflowed the buffer. - */ - if (idx+4 > amt) - return (-1); - - /* - * Retrieve the status. - */ - gget8(b, idx++, sp->comstatus); - gget8(b, idx++, sp->comstat[0]); - gget8(b, idx++, sp->comstat[1]); - gget8(b, idx++, sp->comstat[2]); -#if 0 - PRINTF("Get Elt 0x%x Elm 0x%x (idx %d)\n", elt, elm, idx-4); -#endif - return (0); -} - -/* - * This is the mirror function to ses_decode, but we set the 'select' - * bit for the object which we're interested in. All other objects, - * after a status fetch, should have that bit off. Hmm. It'd be easy - * enough to ensure this, so we will. - */ - -static int -ses_encode(char *b, int amt, uint8_t *ep, int elt, int elm, SesComStat *sp) -{ - int idx, i; - - /* - * If it's overall enclosure status being sought, get that. - * We need at least 2 bytes of status data to get that. - */ - if (elt == -1) { - if (amt < 2) - return (-1); - i = 0; - sset8(b, i, 0); - sset8(b, i, sp->comstatus & 0xf); -#if 0 - PRINTF("set EncStat %x\n", sp->comstatus); -#endif - return (0); - } - - /* - * Check to make sure that the Mth element is legal for type Elt. - */ - - if (elm >= ep[elt]) - return (-1); - - /* - * Starting at offset 8, start skipping over the storage - * for the element types we're not interested in. - */ - for (idx = 8, i = 0; i < elt; i++) { - idx += ((ep[i] + 1) * 4); - } - - /* - * Skip over Overall status for this element type. - */ - idx += 4; - - /* - * And skip to the index for the Mth element that we're going for. - */ - idx += (4 * elm); - - /* - * Make sure we haven't overflowed the buffer. - */ - if (idx+4 > amt) - return (-1); - - /* - * Set the status. - */ - sset8(b, idx, sp->comstatus); - sset8(b, idx, sp->comstat[0]); - sset8(b, idx, sp->comstat[1]); - sset8(b, idx, sp->comstat[2]); - idx -= 4; - -#if 0 - PRINTF("Set Elt 0x%x Elm 0x%x (idx %d) with %x %x %x %x\n", - elt, elm, idx, sp->comstatus, sp->comstat[0], - sp->comstat[1], sp->comstat[2]); -#endif - - /* - * Now make sure all other 'Select' bits are off. - */ - for (i = 8; i < amt; i += 4) { - if (i != idx) - b[i] &= ~0x80; - } - /* - * And make sure the INVOP bit is clear. - */ - b[2] &= ~0x10; - - return (0); -} - -/* - * SAF-TE Type Device Emulation - */ - -static int safte_getconfig(ses_softc_t *); -static int safte_rdstat(ses_softc_t *, int); -static int set_objstat_sel(ses_softc_t *, ses_objstat *, int); -static int wrbuf16(ses_softc_t *, uint8_t, uint8_t, uint8_t, uint8_t, int); -static void wrslot_stat(ses_softc_t *, int); -static int perf_slotop(ses_softc_t *, uint8_t, uint8_t, int); - -#define ALL_ENC_STAT (SES_ENCSTAT_CRITICAL | SES_ENCSTAT_UNRECOV | \ - SES_ENCSTAT_NONCRITICAL | SES_ENCSTAT_INFO) -/* - * SAF-TE specific defines- Mandatory ones only... - */ - -/* - * READ BUFFER ('get' commands) IDs- placed in offset 2 of cdb - */ -#define SAFTE_RD_RDCFG 0x00 /* read enclosure configuration */ -#define SAFTE_RD_RDESTS 0x01 /* read enclosure status */ -#define SAFTE_RD_RDDSTS 0x04 /* read drive slot status */ - -/* - * WRITE BUFFER ('set' commands) IDs- placed in offset 0 of databuf - */ -#define SAFTE_WT_DSTAT 0x10 /* write device slot status */ -#define SAFTE_WT_SLTOP 0x12 /* perform slot operation */ -#define SAFTE_WT_FANSPD 0x13 /* set fan speed */ -#define SAFTE_WT_ACTPWS 0x14 /* turn on/off power supply */ -#define SAFTE_WT_GLOBAL 0x15 /* send global command */ - - -#define SAFT_SCRATCH 64 -#define NPSEUDO_THERM 16 -#define NPSEUDO_ALARM 1 -struct scfg { - /* - * Cached Configuration - */ - uint8_t Nfans; /* Number of Fans */ - uint8_t Npwr; /* Number of Power Supplies */ - uint8_t Nslots; /* Number of Device Slots */ - uint8_t DoorLock; /* Door Lock Installed */ - uint8_t Ntherm; /* Number of Temperature Sensors */ - uint8_t Nspkrs; /* Number of Speakers */ - uint8_t Nalarm; /* Number of Alarms (at least one) */ - /* - * Cached Flag Bytes for Global Status - */ - uint8_t flag1; - uint8_t flag2; - /* - * What object index ID is where various slots start. - */ - uint8_t pwroff; - uint8_t slotoff; -#define SAFT_ALARM_OFFSET(cc) (cc)->slotoff - 1 -}; - -#define SAFT_FLG1_ALARM 0x1 -#define SAFT_FLG1_GLOBFAIL 0x2 -#define SAFT_FLG1_GLOBWARN 0x4 -#define SAFT_FLG1_ENCPWROFF 0x8 -#define SAFT_FLG1_ENCFANFAIL 0x10 -#define SAFT_FLG1_ENCPWRFAIL 0x20 -#define SAFT_FLG1_ENCDRVFAIL 0x40 -#define SAFT_FLG1_ENCDRVWARN 0x80 - -#define SAFT_FLG2_LOCKDOOR 0x4 -#define SAFT_PRIVATE sizeof (struct scfg) - -static char *safte_2little = "Too Little Data Returned (%d) at line %d\n"; -#define SAFT_BAIL(r, x, k, l) \ - if ((r) >= (x)) { \ - SES_LOG(ssc, safte_2little, x, __LINE__);\ - SES_FREE((k), (l)); \ - return (EIO); \ - } - - -static int -safte_softc_init(ses_softc_t *ssc, int doinit) -{ - int err, i, r; - struct scfg *cc; - - if (doinit == 0) { - if (ssc->ses_nobjects) { - if (ssc->ses_objmap) { - SES_FREE(ssc->ses_objmap, - ssc->ses_nobjects * sizeof (encobj)); - ssc->ses_objmap = NULL; - } - ssc->ses_nobjects = 0; - } - if (ssc->ses_private) { - SES_FREE(ssc->ses_private, SAFT_PRIVATE); - ssc->ses_private = NULL; - } - return (0); - } - - if (ssc->ses_private == NULL) { - ssc->ses_private = SES_MALLOC(SAFT_PRIVATE); - if (ssc->ses_private == NULL) { - return (ENOMEM); - } - MEMZERO(ssc->ses_private, SAFT_PRIVATE); - } - - ssc->ses_nobjects = 0; - ssc->ses_encstat = 0; - - if ((err = safte_getconfig(ssc)) != 0) { - return (err); - } - - /* - * The number of objects here, as well as that reported by the - * READ_BUFFER/GET_CONFIG call, are the over-temperature flags (15) - * that get reported during READ_BUFFER/READ_ENC_STATUS. - */ - cc = ssc->ses_private; - ssc->ses_nobjects = cc->Nfans + cc->Npwr + cc->Nslots + cc->DoorLock + - cc->Ntherm + cc->Nspkrs + NPSEUDO_THERM + NPSEUDO_ALARM; - ssc->ses_objmap = (encobj *) - SES_MALLOC(ssc->ses_nobjects * sizeof (encobj)); - if (ssc->ses_objmap == NULL) { - return (ENOMEM); - } - MEMZERO(ssc->ses_objmap, ssc->ses_nobjects * sizeof (encobj)); - - r = 0; - /* - * Note that this is all arranged for the convenience - * in later fetches of status. - */ - for (i = 0; i < cc->Nfans; i++) - ssc->ses_objmap[r++].enctype = SESTYP_FAN; - cc->pwroff = (uint8_t) r; - for (i = 0; i < cc->Npwr; i++) - ssc->ses_objmap[r++].enctype = SESTYP_POWER; - for (i = 0; i < cc->DoorLock; i++) - ssc->ses_objmap[r++].enctype = SESTYP_DOORLOCK; - for (i = 0; i < cc->Nspkrs; i++) - ssc->ses_objmap[r++].enctype = SESTYP_ALARM; - for (i = 0; i < cc->Ntherm; i++) - ssc->ses_objmap[r++].enctype = SESTYP_THERM; - for (i = 0; i < NPSEUDO_THERM; i++) - ssc->ses_objmap[r++].enctype = SESTYP_THERM; - ssc->ses_objmap[r++].enctype = SESTYP_ALARM; - cc->slotoff = (uint8_t) r; - for (i = 0; i < cc->Nslots; i++) - ssc->ses_objmap[r++].enctype = SESTYP_DEVICE; - return (0); -} - -static int -safte_init_enc(ses_softc_t *ssc) -{ - int err; - static char cdb0[6] = { SEND_DIAGNOSTIC }; - - err = ses_runcmd(ssc, cdb0, 6, NULL, 0); - if (err) { - return (err); - } - DELAY(5000); - err = wrbuf16(ssc, SAFTE_WT_GLOBAL, 0, 0, 0, 1); - return (err); -} - -static int -safte_get_encstat(ses_softc_t *ssc, int slpflg) -{ - return (safte_rdstat(ssc, slpflg)); -} - -static int -safte_set_encstat(ses_softc_t *ssc, uint8_t encstat, int slpflg) -{ - struct scfg *cc = ssc->ses_private; - if (cc == NULL) - return (0); - /* - * Since SAF-TE devices aren't necessarily sticky in terms - * of state, make our soft copy of enclosure status 'sticky'- - * that is, things set in enclosure status stay set (as implied - * by conditions set in reading object status) until cleared. - */ - ssc->ses_encstat &= ~ALL_ENC_STAT; - ssc->ses_encstat |= (encstat & ALL_ENC_STAT); - ssc->ses_encstat |= ENCI_SVALID; - cc->flag1 &= ~(SAFT_FLG1_ALARM|SAFT_FLG1_GLOBFAIL|SAFT_FLG1_GLOBWARN); - if ((encstat & (SES_ENCSTAT_CRITICAL|SES_ENCSTAT_UNRECOV)) != 0) { - cc->flag1 |= SAFT_FLG1_ALARM|SAFT_FLG1_GLOBFAIL; - } else if ((encstat & SES_ENCSTAT_NONCRITICAL) != 0) { - cc->flag1 |= SAFT_FLG1_GLOBWARN; - } - return (wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, cc->flag2, 0, slpflg)); -} - -static int -safte_get_objstat(ses_softc_t *ssc, ses_objstat *obp, int slpflg) -{ - int i = (int)obp->obj_id; - - if ((ssc->ses_encstat & ENCI_SVALID) == 0 || - (ssc->ses_objmap[i].svalid) == 0) { - int err = safte_rdstat(ssc, slpflg); - if (err) - return (err); - } - obp->cstat[0] = ssc->ses_objmap[i].encstat[0]; - obp->cstat[1] = ssc->ses_objmap[i].encstat[1]; - obp->cstat[2] = ssc->ses_objmap[i].encstat[2]; - obp->cstat[3] = ssc->ses_objmap[i].encstat[3]; - return (0); -} - - -static int -safte_set_objstat(ses_softc_t *ssc, ses_objstat *obp, int slp) -{ - int idx, err; - encobj *ep; - struct scfg *cc; - - - SES_DLOG(ssc, "safte_set_objstat(%d): %x %x %x %x\n", - (int)obp->obj_id, obp->cstat[0], obp->cstat[1], obp->cstat[2], - obp->cstat[3]); - - /* - * If this is clear, we don't do diddly. - */ - if ((obp->cstat[0] & SESCTL_CSEL) == 0) { - return (0); - } - - err = 0; - /* - * Check to see if the common bits are set and do them first. - */ - if (obp->cstat[0] & ~SESCTL_CSEL) { - err = set_objstat_sel(ssc, obp, slp); - if (err) - return (err); - } - - cc = ssc->ses_private; - if (cc == NULL) - return (0); - - idx = (int)obp->obj_id; - ep = &ssc->ses_objmap[idx]; - - switch (ep->enctype) { - case SESTYP_DEVICE: - { - uint8_t slotop = 0; - /* - * XXX: I should probably cache the previous state - * XXX: of SESCTL_DEVOFF so that when it goes from - * XXX: true to false I can then set PREPARE FOR OPERATION - * XXX: flag in PERFORM SLOT OPERATION write buffer command. - */ - if (obp->cstat[2] & (SESCTL_RQSINS|SESCTL_RQSRMV)) { - slotop |= 0x2; - } - if (obp->cstat[2] & SESCTL_RQSID) { - slotop |= 0x4; - } - err = perf_slotop(ssc, (uint8_t) idx - (uint8_t) cc->slotoff, - slotop, slp); - if (err) - return (err); - if (obp->cstat[3] & SESCTL_RQSFLT) { - ep->priv |= 0x2; - } else { - ep->priv &= ~0x2; - } - if (ep->priv & 0xc6) { - ep->priv &= ~0x1; - } else { - ep->priv |= 0x1; /* no errors */ - } - wrslot_stat(ssc, slp); - break; - } - case SESTYP_POWER: - if (obp->cstat[3] & SESCTL_RQSTFAIL) { - cc->flag1 |= SAFT_FLG1_ENCPWRFAIL; - } else { - cc->flag1 &= ~SAFT_FLG1_ENCPWRFAIL; - } - err = wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, - cc->flag2, 0, slp); - if (err) - return (err); - if (obp->cstat[3] & SESCTL_RQSTON) { - (void) wrbuf16(ssc, SAFTE_WT_ACTPWS, - idx - cc->pwroff, 0, 0, slp); - } else { - (void) wrbuf16(ssc, SAFTE_WT_ACTPWS, - idx - cc->pwroff, 0, 1, slp); - } - break; - case SESTYP_FAN: - if (obp->cstat[3] & SESCTL_RQSTFAIL) { - cc->flag1 |= SAFT_FLG1_ENCFANFAIL; - } else { - cc->flag1 &= ~SAFT_FLG1_ENCFANFAIL; - } - err = wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, - cc->flag2, 0, slp); - if (err) - return (err); - if (obp->cstat[3] & SESCTL_RQSTON) { - uint8_t fsp; - if ((obp->cstat[3] & 0x7) == 7) { - fsp = 4; - } else if ((obp->cstat[3] & 0x7) == 6) { - fsp = 3; - } else if ((obp->cstat[3] & 0x7) == 4) { - fsp = 2; - } else { - fsp = 1; - } - (void) wrbuf16(ssc, SAFTE_WT_FANSPD, idx, fsp, 0, slp); - } else { - (void) wrbuf16(ssc, SAFTE_WT_FANSPD, idx, 0, 0, slp); - } - break; - case SESTYP_DOORLOCK: - if (obp->cstat[3] & 0x1) { - cc->flag2 &= ~SAFT_FLG2_LOCKDOOR; - } else { - cc->flag2 |= SAFT_FLG2_LOCKDOOR; - } - (void) wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, - cc->flag2, 0, slp); - break; - case SESTYP_ALARM: - /* - * On all nonzero but the 'muted' bit, we turn on the alarm, - */ - obp->cstat[3] &= ~0xa; - if (obp->cstat[3] & 0x40) { - cc->flag2 &= ~SAFT_FLG1_ALARM; - } else if (obp->cstat[3] != 0) { - cc->flag2 |= SAFT_FLG1_ALARM; - } else { - cc->flag2 &= ~SAFT_FLG1_ALARM; - } - ep->priv = obp->cstat[3]; - (void) wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, - cc->flag2, 0, slp); - break; - default: - break; - } - ep->svalid = 0; - return (0); -} - -static int -safte_getconfig(ses_softc_t *ssc) -{ - struct scfg *cfg; - int err, amt; - char *sdata; - static char cdb[10] = - { READ_BUFFER, 1, SAFTE_RD_RDCFG, 0, 0, 0, 0, 0, SAFT_SCRATCH, 0 }; - - cfg = ssc->ses_private; - if (cfg == NULL) - return (ENXIO); - - sdata = SES_MALLOC(SAFT_SCRATCH); - if (sdata == NULL) - return (ENOMEM); - - amt = SAFT_SCRATCH; - err = ses_runcmd(ssc, cdb, 10, sdata, &amt); - if (err) { - SES_FREE(sdata, SAFT_SCRATCH); - return (err); - } - amt = SAFT_SCRATCH - amt; - if (amt < 6) { - SES_LOG(ssc, "too little data (%d) for configuration\n", amt); - SES_FREE(sdata, SAFT_SCRATCH); - return (EIO); - } - SES_VLOG(ssc, "Nfans %d Npwr %d Nslots %d Lck %d Ntherm %d Nspkrs %d\n", - sdata[0], sdata[1], sdata[2], sdata[3], sdata[4], sdata[5]); - cfg->Nfans = sdata[0]; - cfg->Npwr = sdata[1]; - cfg->Nslots = sdata[2]; - cfg->DoorLock = sdata[3]; - cfg->Ntherm = sdata[4]; - cfg->Nspkrs = sdata[5]; - cfg->Nalarm = NPSEUDO_ALARM; - SES_FREE(sdata, SAFT_SCRATCH); - return (0); -} - -static int -safte_rdstat(ses_softc_t *ssc, int slpflg) -{ - int err, oid, r, i, hiwater, nitems, amt; - uint16_t tempflags; - size_t buflen; - uint8_t status, oencstat; - char *sdata, cdb[10]; - struct scfg *cc = ssc->ses_private; - - - /* - * The number of objects overstates things a bit, - * both for the bogus 'thermometer' entries and - * the drive status (which isn't read at the same - * time as the enclosure status), but that's okay. - */ - buflen = 4 * cc->Nslots; - if (ssc->ses_nobjects > buflen) - buflen = ssc->ses_nobjects; - sdata = SES_MALLOC(buflen); - if (sdata == NULL) - return (ENOMEM); - - cdb[0] = READ_BUFFER; - cdb[1] = 1; - cdb[2] = SAFTE_RD_RDESTS; - cdb[3] = 0; - cdb[4] = 0; - cdb[5] = 0; - cdb[6] = 0; - cdb[7] = (buflen >> 8) & 0xff; - cdb[8] = buflen & 0xff; - cdb[9] = 0; - amt = buflen; - err = ses_runcmd(ssc, cdb, 10, sdata, &amt); - if (err) { - SES_FREE(sdata, buflen); - return (err); - } - hiwater = buflen - amt; - - - /* - * invalidate all status bits. - */ - for (i = 0; i < ssc->ses_nobjects; i++) - ssc->ses_objmap[i].svalid = 0; - oencstat = ssc->ses_encstat & ALL_ENC_STAT; - ssc->ses_encstat = 0; - - - /* - * Now parse returned buffer. - * If we didn't get enough data back, - * that's considered a fatal error. - */ - oid = r = 0; - - for (nitems = i = 0; i < cc->Nfans; i++) { - SAFT_BAIL(r, hiwater, sdata, buflen); - /* - * 0 = Fan Operational - * 1 = Fan is malfunctioning - * 2 = Fan is not present - * 0x80 = Unknown or Not Reportable Status - */ - ssc->ses_objmap[oid].encstat[1] = 0; /* resvd */ - ssc->ses_objmap[oid].encstat[2] = 0; /* resvd */ - switch ((int)(uint8_t)sdata[r]) { - case 0: - nitems++; - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_OK; - /* - * We could get fancier and cache - * fan speeds that we have set, but - * that isn't done now. - */ - ssc->ses_objmap[oid].encstat[3] = 7; - break; - - case 1: - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_CRIT; - /* - * FAIL and FAN STOPPED synthesized - */ - ssc->ses_objmap[oid].encstat[3] = 0x40; - /* - * Enclosure marked with CRITICAL error - * if only one fan or no thermometers, - * else the NONCRITICAL error is set. - */ - if (cc->Nfans == 1 || cc->Ntherm == 0) - ssc->ses_encstat |= SES_ENCSTAT_CRITICAL; - else - ssc->ses_encstat |= SES_ENCSTAT_NONCRITICAL; - break; - case 2: - ssc->ses_objmap[oid].encstat[0] = - SES_OBJSTAT_NOTINSTALLED; - ssc->ses_objmap[oid].encstat[3] = 0; - /* - * Enclosure marked with CRITICAL error - * if only one fan or no thermometers, - * else the NONCRITICAL error is set. - */ - if (cc->Nfans == 1) - ssc->ses_encstat |= SES_ENCSTAT_CRITICAL; - else - ssc->ses_encstat |= SES_ENCSTAT_NONCRITICAL; - break; - case 0x80: - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_UNKNOWN; - ssc->ses_objmap[oid].encstat[3] = 0; - ssc->ses_encstat |= SES_ENCSTAT_INFO; - break; - default: - ssc->ses_objmap[oid].encstat[0] = - SES_OBJSTAT_UNSUPPORTED; - SES_LOG(ssc, "Unknown fan%d status 0x%x\n", i, - sdata[r] & 0xff); - break; - } - ssc->ses_objmap[oid++].svalid = 1; - r++; - } - - /* - * No matter how you cut it, no cooling elements when there - * should be some there is critical. - */ - if (cc->Nfans && nitems == 0) { - ssc->ses_encstat |= SES_ENCSTAT_CRITICAL; - } - - - for (i = 0; i < cc->Npwr; i++) { - SAFT_BAIL(r, hiwater, sdata, buflen); - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_UNKNOWN; - ssc->ses_objmap[oid].encstat[1] = 0; /* resvd */ - ssc->ses_objmap[oid].encstat[2] = 0; /* resvd */ - ssc->ses_objmap[oid].encstat[3] = 0x20; /* requested on */ - switch ((uint8_t)sdata[r]) { - case 0x00: /* pws operational and on */ - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_OK; - break; - case 0x01: /* pws operational and off */ - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_OK; - ssc->ses_objmap[oid].encstat[3] = 0x10; - ssc->ses_encstat |= SES_ENCSTAT_INFO; - break; - case 0x10: /* pws is malfunctioning and commanded on */ - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_CRIT; - ssc->ses_objmap[oid].encstat[3] = 0x61; - ssc->ses_encstat |= SES_ENCSTAT_NONCRITICAL; - break; - - case 0x11: /* pws is malfunctioning and commanded off */ - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_NONCRIT; - ssc->ses_objmap[oid].encstat[3] = 0x51; - ssc->ses_encstat |= SES_ENCSTAT_NONCRITICAL; - break; - case 0x20: /* pws is not present */ - ssc->ses_objmap[oid].encstat[0] = - SES_OBJSTAT_NOTINSTALLED; - ssc->ses_objmap[oid].encstat[3] = 0; - ssc->ses_encstat |= SES_ENCSTAT_INFO; - break; - case 0x21: /* pws is present */ - /* - * This is for enclosures that cannot tell whether the - * device is on or malfunctioning, but know that it is - * present. Just fall through. - */ - /* FALLTHROUGH */ - case 0x80: /* Unknown or Not Reportable Status */ - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_UNKNOWN; - ssc->ses_objmap[oid].encstat[3] = 0; - ssc->ses_encstat |= SES_ENCSTAT_INFO; - break; - default: - SES_LOG(ssc, "unknown power supply %d status (0x%x)\n", - i, sdata[r] & 0xff); - break; - } - ssc->ses_objmap[oid++].svalid = 1; - r++; - } - - /* - * Skip over Slot SCSI IDs - */ - r += cc->Nslots; - - /* - * We always have doorlock status, no matter what, - * but we only save the status if we have one. - */ - SAFT_BAIL(r, hiwater, sdata, buflen); - if (cc->DoorLock) { - /* - * 0 = Door Locked - * 1 = Door Unlocked, or no Lock Installed - * 0x80 = Unknown or Not Reportable Status - */ - ssc->ses_objmap[oid].encstat[1] = 0; - ssc->ses_objmap[oid].encstat[2] = 0; - switch ((uint8_t)sdata[r]) { - case 0: - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_OK; - ssc->ses_objmap[oid].encstat[3] = 0; - break; - case 1: - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_OK; - ssc->ses_objmap[oid].encstat[3] = 1; - break; - case 0x80: - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_UNKNOWN; - ssc->ses_objmap[oid].encstat[3] = 0; - ssc->ses_encstat |= SES_ENCSTAT_INFO; - break; - default: - ssc->ses_objmap[oid].encstat[0] = - SES_OBJSTAT_UNSUPPORTED; - SES_LOG(ssc, "unknown lock status 0x%x\n", - sdata[r] & 0xff); - break; - } - ssc->ses_objmap[oid++].svalid = 1; - } - r++; - - /* - * We always have speaker status, no matter what, - * but we only save the status if we have one. - */ - SAFT_BAIL(r, hiwater, sdata, buflen); - if (cc->Nspkrs) { - ssc->ses_objmap[oid].encstat[1] = 0; - ssc->ses_objmap[oid].encstat[2] = 0; - if (sdata[r] == 1) { - /* - * We need to cache tone urgency indicators. - * Someday. - */ - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_NONCRIT; - ssc->ses_objmap[oid].encstat[3] = 0x8; - ssc->ses_encstat |= SES_ENCSTAT_NONCRITICAL; - } else if (sdata[r] == 0) { - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_OK; - ssc->ses_objmap[oid].encstat[3] = 0; - } else { - ssc->ses_objmap[oid].encstat[0] = - SES_OBJSTAT_UNSUPPORTED; - ssc->ses_objmap[oid].encstat[3] = 0; - SES_LOG(ssc, "unknown spkr status 0x%x\n", - sdata[r] & 0xff); - } - ssc->ses_objmap[oid++].svalid = 1; - } - r++; - - for (i = 0; i < cc->Ntherm; i++) { - SAFT_BAIL(r, hiwater, sdata, buflen); - /* - * Status is a range from -10 to 245 deg Celsius, - * which we need to normalize to -20 to -245 according - * to the latest SCSI spec, which makes little - * sense since this would overflow an 8bit value. - * Well, still, the base normalization is -20, - * not -10, so we have to adjust. - * - * So what's over and under temperature? - * Hmm- we'll state that 'normal' operating - * is 10 to 40 deg Celsius. - */ - - /* - * Actually.... All of the units that people out in the world - * seem to have do not come even close to setting a value that - * complies with this spec. - * - * The closest explanation I could find was in an - * LSI-Logic manual, which seemed to indicate that - * this value would be set by whatever the I2C code - * would interpolate from the output of an LM75 - * temperature sensor. - * - * This means that it is impossible to use the actual - * numeric value to predict anything. But we don't want - * to lose the value. So, we'll propagate the *uncorrected* - * value and set SES_OBJSTAT_NOTAVAIL. We'll depend on the - * temperature flags for warnings. - */ - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_NOTAVAIL; - ssc->ses_objmap[oid].encstat[1] = 0; - ssc->ses_objmap[oid].encstat[2] = sdata[r]; - ssc->ses_objmap[oid].encstat[3] = 0; - ssc->ses_objmap[oid++].svalid = 1; - r++; - } - - /* - * Now, for "pseudo" thermometers, we have two bytes - * of information in enclosure status- 16 bits. Actually, - * the MSB is a single TEMP ALERT flag indicating whether - * any other bits are set, but, thanks to fuzzy thinking, - * in the SAF-TE spec, this can also be set even if no - * other bits are set, thus making this really another - * binary temperature sensor. - */ - - SAFT_BAIL(r, hiwater, sdata, buflen); - tempflags = sdata[r++]; - SAFT_BAIL(r, hiwater, sdata, buflen); - tempflags |= (tempflags << 8) | sdata[r++]; - - for (i = 0; i < NPSEUDO_THERM; i++) { - ssc->ses_objmap[oid].encstat[1] = 0; - if (tempflags & (1 << (NPSEUDO_THERM - i - 1))) { - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_CRIT; - ssc->ses_objmap[4].encstat[2] = 0xff; - /* - * Set 'over temperature' failure. - */ - ssc->ses_objmap[oid].encstat[3] = 8; - ssc->ses_encstat |= SES_ENCSTAT_CRITICAL; - } else { - /* - * We used to say 'not available' and synthesize a - * nominal 30 deg (C)- that was wrong. Actually, - * Just say 'OK', and use the reserved value of - * zero. - */ - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_OK; - ssc->ses_objmap[oid].encstat[2] = 0; - ssc->ses_objmap[oid].encstat[3] = 0; - } - ssc->ses_objmap[oid++].svalid = 1; - } - - /* - * Get alarm status. - */ - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_OK; - ssc->ses_objmap[oid].encstat[3] = ssc->ses_objmap[oid].priv; - ssc->ses_objmap[oid++].svalid = 1; - - /* - * Now get drive slot status - */ - cdb[2] = SAFTE_RD_RDDSTS; - amt = buflen; - err = ses_runcmd(ssc, cdb, 10, sdata, &amt); - if (err) { - SES_FREE(sdata, buflen); - return (err); - } - hiwater = buflen - amt; - for (r = i = 0; i < cc->Nslots; i++, r += 4) { - SAFT_BAIL(r+3, hiwater, sdata, buflen); - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_UNSUPPORTED; - ssc->ses_objmap[oid].encstat[1] = (uint8_t) i; - ssc->ses_objmap[oid].encstat[2] = 0; - ssc->ses_objmap[oid].encstat[3] = 0; - status = sdata[r+3]; - if ((status & 0x1) == 0) { /* no device */ - ssc->ses_objmap[oid].encstat[0] = - SES_OBJSTAT_NOTINSTALLED; - } else { - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_OK; - } - if (status & 0x2) { - ssc->ses_objmap[oid].encstat[2] = 0x8; - } - if ((status & 0x4) == 0) { - ssc->ses_objmap[oid].encstat[3] = 0x10; - } - ssc->ses_objmap[oid++].svalid = 1; - } - /* see comment below about sticky enclosure status */ - ssc->ses_encstat |= ENCI_SVALID | oencstat; - SES_FREE(sdata, buflen); - return (0); -} - -static int -set_objstat_sel(ses_softc_t *ssc, ses_objstat *obp, int slp) -{ - int idx; - encobj *ep; - struct scfg *cc = ssc->ses_private; - - if (cc == NULL) - return (0); - - idx = (int)obp->obj_id; - ep = &ssc->ses_objmap[idx]; - - switch (ep->enctype) { - case SESTYP_DEVICE: - if (obp->cstat[0] & SESCTL_PRDFAIL) { - ep->priv |= 0x40; - } - /* SESCTL_RSTSWAP has no correspondence in SAF-TE */ - if (obp->cstat[0] & SESCTL_DISABLE) { - ep->priv |= 0x80; - /* - * Hmm. Try to set the 'No Drive' flag. - * Maybe that will count as a 'disable'. - */ - } - if (ep->priv & 0xc6) { - ep->priv &= ~0x1; - } else { - ep->priv |= 0x1; /* no errors */ - } - wrslot_stat(ssc, slp); - break; - case SESTYP_POWER: - /* - * Okay- the only one that makes sense here is to - * do the 'disable' for a power supply. - */ - if (obp->cstat[0] & SESCTL_DISABLE) { - (void) wrbuf16(ssc, SAFTE_WT_ACTPWS, - idx - cc->pwroff, 0, 0, slp); - } - break; - case SESTYP_FAN: - /* - * Okay- the only one that makes sense here is to - * set fan speed to zero on disable. - */ - if (obp->cstat[0] & SESCTL_DISABLE) { - /* remember- fans are the first items, so idx works */ - (void) wrbuf16(ssc, SAFTE_WT_FANSPD, idx, 0, 0, slp); - } - break; - case SESTYP_DOORLOCK: - /* - * Well, we can 'disable' the lock. - */ - if (obp->cstat[0] & SESCTL_DISABLE) { - cc->flag2 &= ~SAFT_FLG2_LOCKDOOR; - (void) wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, - cc->flag2, 0, slp); - } - break; - case SESTYP_ALARM: - /* - * Well, we can 'disable' the alarm. - */ - if (obp->cstat[0] & SESCTL_DISABLE) { - cc->flag2 &= ~SAFT_FLG1_ALARM; - ep->priv |= 0x40; /* Muted */ - (void) wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, - cc->flag2, 0, slp); - } - break; - default: - break; - } - ep->svalid = 0; - return (0); -} - -/* - * This function handles all of the 16 byte WRITE BUFFER commands. - */ -static int -wrbuf16(ses_softc_t *ssc, uint8_t op, uint8_t b1, uint8_t b2, - uint8_t b3, int slp) -{ - int err, amt; - char *sdata; - struct scfg *cc = ssc->ses_private; - static char cdb[10] = { WRITE_BUFFER, 1, 0, 0, 0, 0, 0, 0, 16, 0 }; - - if (cc == NULL) - return (0); - - sdata = SES_MALLOC(16); - if (sdata == NULL) - return (ENOMEM); - - SES_DLOG(ssc, "saf_wrbuf16 %x %x %x %x\n", op, b1, b2, b3); - - sdata[0] = op; - sdata[1] = b1; - sdata[2] = b2; - sdata[3] = b3; - MEMZERO(&sdata[4], 12); - amt = -16; - err = ses_runcmd(ssc, cdb, 10, sdata, &amt); - SES_FREE(sdata, 16); - return (err); -} - -/* - * This function updates the status byte for the device slot described. - * - * Since this is an optional SAF-TE command, there's no point in - * returning an error. - */ -static void -wrslot_stat(ses_softc_t *ssc, int slp) -{ - int i, amt; - encobj *ep; - char cdb[10], *sdata; - struct scfg *cc = ssc->ses_private; - - if (cc == NULL) - return; - - SES_DLOG(ssc, "saf_wrslot\n"); - cdb[0] = WRITE_BUFFER; - cdb[1] = 1; - cdb[2] = 0; - cdb[3] = 0; - cdb[4] = 0; - cdb[5] = 0; - cdb[6] = 0; - cdb[7] = 0; - cdb[8] = cc->Nslots * 3 + 1; - cdb[9] = 0; - - sdata = SES_MALLOC(cc->Nslots * 3 + 1); - if (sdata == NULL) - return; - MEMZERO(sdata, cc->Nslots * 3 + 1); - - sdata[0] = SAFTE_WT_DSTAT; - for (i = 0; i < cc->Nslots; i++) { - ep = &ssc->ses_objmap[cc->slotoff + i]; - SES_DLOG(ssc, "saf_wrslot %d <- %x\n", i, ep->priv & 0xff); - sdata[1 + (3 * i)] = ep->priv & 0xff; - } - amt = -(cc->Nslots * 3 + 1); - (void) ses_runcmd(ssc, cdb, 10, sdata, &amt); - SES_FREE(sdata, cc->Nslots * 3 + 1); -} - -/* - * This function issues the "PERFORM SLOT OPERATION" command. - */ -static int -perf_slotop(ses_softc_t *ssc, uint8_t slot, uint8_t opflag, int slp) -{ - int err, amt; - char *sdata; - struct scfg *cc = ssc->ses_private; - static char cdb[10] = - { WRITE_BUFFER, 1, 0, 0, 0, 0, 0, 0, SAFT_SCRATCH, 0 }; - - if (cc == NULL) - return (0); - - sdata = SES_MALLOC(SAFT_SCRATCH); - if (sdata == NULL) - return (ENOMEM); - MEMZERO(sdata, SAFT_SCRATCH); - - sdata[0] = SAFTE_WT_SLTOP; - sdata[1] = slot; - sdata[2] = opflag; - SES_DLOG(ssc, "saf_slotop slot %d op %x\n", slot, opflag); - amt = -SAFT_SCRATCH; - err = ses_runcmd(ssc, cdb, 10, sdata, &amt); - SES_FREE(sdata, SAFT_SCRATCH); - return (err); -} diff --git a/sys/cam/scsi/scsi_ses.h b/sys/cam/scsi/scsi_ses.h index a52d5171cca..2c9ceb402b3 100644 --- a/sys/cam/scsi/scsi_ses.h +++ b/sys/cam/scsi/scsi_ses.h @@ -29,107 +29,2131 @@ * */ -#define SESIOC ('s' - 040) -#define SESIOC_GETNOBJ _IO(SESIOC, 1) -#define SESIOC_GETOBJMAP _IO(SESIOC, 2) -#define SESIOC_GETENCSTAT _IO(SESIOC, 3) -#define SESIOC_SETENCSTAT _IO(SESIOC, 4) -#define SESIOC_GETOBJSTAT _IO(SESIOC, 5) -#define SESIOC_SETOBJSTAT _IO(SESIOC, 6) -#define SESIOC_GETTEXT _IO(SESIOC, 7) -#define SESIOC_INIT _IO(SESIOC, 8) +#ifndef _SCSI_SES_H_ +#define _SCSI_SES_H_ -/* - * Platform Independent Definitions for SES devices. - */ -/* - * SCSI Based Environmental Services Application Defines - * - * Based almost entirely on SCSI-3 SES Revision 8A specification, - * but slightly abstracted as the underlying device may in fact - * be a SAF-TE or vendor unique device. - */ -/* - * SES Driver Operations: - * (The defines themselves are platform and access method specific) - * - * SESIOC_GETNOBJ - * SESIOC_GETOBJMAP - * SESIOC_GETENCSTAT - * SESIOC_SETENCSTAT - * SESIOC_GETOBJSTAT - * SESIOC_SETOBJSTAT - * SESIOC_INIT - * - * - * An application finds out how many objects an SES instance - * is managing by performing a SESIOC_GETNOBJ operation. It then - * performs a SESIOC_GETOBJMAP to get the map that contains the - * object identifiers for all objects (see ses_object below). - * This information is static. - * - * The application may perform SESIOC_GETOBJSTAT operations to retrieve - * status on an object (see the ses_objstat structure below), SESIOC_SETOBJSTAT - * operations to set status for an object. - * - * Similarly overall enclosure status me be fetched or set via - * SESIOC_GETENCSTAT or SESIOC_SETENCSTAT operations (see ses_encstat below). - * - * Readers should note that there is nothing that requires either a set - * or a clear operation to actually latch and do anything in the target. - * - * A SESIOC_INIT operation causes the enclosure to be initialized. - */ +#include -typedef struct { - unsigned int obj_id; /* Object Identifier */ - unsigned char subencid; /* SubEnclosure ID */ - unsigned char object_type; /* Object Type */ -} ses_object; +/*========================== Field Extraction Macros =========================*/ +#define MK_ENUM(S, F, SUFFIX) S ## _ ## F ## SUFFIX -/* Object Types */ -#define SESTYP_UNSPECIFIED 0x00 -#define SESTYP_DEVICE 0x01 -#define SESTYP_POWER 0x02 -#define SESTYP_FAN 0x03 -#define SESTYP_THERM 0x04 -#define SESTYP_DOORLOCK 0x05 -#define SESTYP_ALARM 0x06 -#define SESTYP_ESCC 0x07 /* Enclosure SCC */ -#define SESTYP_SCC 0x08 /* SCC */ -#define SESTYP_NVRAM 0x09 -#define SESTYP_UPS 0x0b -#define SESTYP_DISPLAY 0x0c -#define SESTYP_KEYPAD 0x0d -#define SESTYP_ENCLOSURE 0x0e -#define SESTYP_SCSIXVR 0x0f -#define SESTYP_LANGUAGE 0x10 -#define SESTYP_COMPORT 0x11 -#define SESTYP_VOM 0x12 -#define SESTYP_AMMETER 0x13 -#define SESTYP_SCSI_TGT 0x14 -#define SESTYP_SCSI_INI 0x15 -#define SESTYP_SUBENC 0x16 -#define SESTYP_ARRAY 0x17 -#define SESTYP_SASEXPANDER 0x18 -#define SESTYP_SASCONNECTOR 0x19 +#define GEN_GETTER(LS, US, LF, UF) \ +static inline int \ +LS ## _get_ ## LF(struct LS *elem) { \ + return ((elem->bytes[MK_ENUM(US,UF,_BYTE)] & MK_ENUM(US,UF,_MASK)) \ + >> MK_ENUM(US,UF,_SHIFT)); \ +} +#define GEN_SETTER(LS, US, LF, UF) \ +static inline void \ +LS ## _set_ ## LF(struct LS *elem, int val) { \ + elem->bytes[MK_ENUM(US,UF,_BYTE)] &= ~MK_ENUM(US,UF,_MASK); \ + elem->bytes[MK_ENUM(US,UF,_BYTE)] |= \ + (val << MK_ENUM(US,UF,_SHIFT)) & MK_ENUM(US,UF,_MASK); \ +} + +#define GEN_HDR_GETTER(LS, US, LF, UF) \ +static inline int \ +LS ## _get_ ## LF(struct LS *page) { \ + return ((page->hdr.page_specific_flags & MK_ENUM(US,UF,_MASK)) \ + >> MK_ENUM(US,UF,_SHIFT)); \ +} + +#define GEN_HDR_SETTER(LS, US, LF, UF) \ +static inline void \ +LS ## _set_ ## LF(struct LS *page, int val) { \ + page->hdr.page_specific_flags &= ~MK_ENUM(US,UF,_MASK); \ + page->hdr.page_specific_flags |= \ + (val << MK_ENUM(US,UF,_SHIFT)) & MK_ENUM(US,UF,_MASK); \ +} + +#define GEN_ACCESSORS(LS, US, LF, UF) \ +GEN_GETTER(LS, US, LF, UF) \ +GEN_SETTER(LS, US, LF, UF) + +#define GEN_HDR_ACCESSORS(LS, US, LF, UF) \ +GEN_HDR_GETTER(LS, US, LF, UF) \ +GEN_HDR_SETTER(LS, US, LF, UF) + +/*=============== Common SCSI ENC Diagnostic Page Structures ===============*/ +struct ses_page_hdr { + uint8_t page_code; + uint8_t page_specific_flags; + uint8_t length[2]; + uint8_t gen_code[4]; +}; + +static inline size_t +ses_page_length(const struct ses_page_hdr *hdr) +{ + /* + * The page length as received only accounts for bytes that + * follow the length field, namely starting with the generation + * code field. + */ + return (scsi_2btoul(hdr->length) + + offsetof(struct ses_page_hdr, gen_code)); +} + +/*============= SCSI ENC Configuration Diagnostic Page Structures ============*/ +struct ses_enc_desc { + uint8_t byte0; + /* + * reserved0 : 1, + * rel_id : 3, relative enclosure process id + * reserved1 : 1, + * num_procs : 3; number of enclosure procesenc + */ + uint8_t subenc_id; /* Sub-enclosure Identifier */ + uint8_t num_types; /* # of supported types */ + uint8_t length; /* Enclosure Descriptor Length */ + uint8_t logical_id[8]; /* formerly wwn */ + uint8_t vendor_id[8]; + uint8_t product_id[16]; + uint8_t product_rev[4]; + uint8_t vendor_bytes[]; +}; + +static inline uint8_t * +ses_enc_desc_last_byte(struct ses_enc_desc *encdesc) +{ + return (&encdesc->length + encdesc->length + 1); +} + +static inline struct ses_enc_desc * +ses_enc_desc_next(struct ses_enc_desc *encdesc) +{ + return ((struct ses_enc_desc *)(ses_enc_desc_last_byte(encdesc) + 1)); +} + +static inline int +ses_enc_desc_is_complete(struct ses_enc_desc *encdesc, uint8_t *last_buf_byte) +{ + return (&encdesc->length <= last_buf_byte + && ses_enc_desc_last_byte(encdesc) <= last_buf_byte); +} + +struct ses_elm_type_desc { + uint8_t etype_elm_type; /* type of element */ + uint8_t etype_maxelt; /* maximum supported */ + uint8_t etype_subenc; /* in sub-enclosure #n */ + uint8_t etype_txt_len; /* Type Descriptor Text Length */ +}; + +struct ses_cfg_page { + struct ses_page_hdr hdr; + struct ses_enc_desc subencs[]; + /* type descriptors */ + /* type text */ +}; + +static inline int +ses_cfg_page_get_num_subenc(struct ses_cfg_page *page) +{ + return (page->hdr.page_specific_flags + 1); +} + + +/*================ SCSI SES Control Diagnostic Page Structures ==============*/ +struct ses_ctrl_common { + uint8_t bytes[1]; +}; + +enum ses_ctrl_common_field_data { + SES_CTRL_COMMON_SELECT_BYTE = 0, + SES_CTRL_COMMON_SELECT_MASK = 0x80, + SES_CTRL_COMMON_SELECT_SHIFT = 7, + + SES_CTRL_COMMON_PRDFAIL_BYTE = 0, + SES_CTRL_COMMON_PRDFAIL_MASK = 0x40, + SES_CTRL_COMMON_PRDFAIL_SHIFT = 6, + + SES_CTRL_COMMON_DISABLE_BYTE = 0, + SES_CTRL_COMMON_DISABLE_MASK = 0x20, + SES_CTRL_COMMON_DISABLE_SHIFT = 5, + + SES_CTRL_COMMON_RST_SWAP_BYTE = 0, + SES_CTRL_COMMON_RST_SWAP_MASK = 0x10, + SES_CTRL_COMMON_RST_SWAP_SHIFT = 4 +}; + +#define GEN_SES_CTRL_COMMON_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_common, SES_CTRL_COMMON, LCASE, UCASE) +GEN_SES_CTRL_COMMON_ACCESSORS(select, SELECT) +GEN_SES_CTRL_COMMON_ACCESSORS(prdfail, PRDFAIL) +GEN_SES_CTRL_COMMON_ACCESSORS(disable, DISABLE) +GEN_SES_CTRL_COMMON_ACCESSORS(rst_swap, RST_SWAP) +#undef GEN_SES_CTRL_COMMON_ACCESSORS + +/*------------------------ Device Slot Control Element ----------------------*/ +struct ses_ctrl_dev_slot { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_dev_slot_field_data { + SES_CTRL_DEV_SLOT_RQST_ACTIVE_BYTE = 1, + SES_CTRL_DEV_SLOT_RQST_ACTIVE_MASK = 0x80, + SES_CTRL_DEV_SLOT_RQST_ACTIVE_SHIFT = 7, + + SES_CTRL_DEV_SLOT_DO_NOT_REMOVE_BYTE = 1, + SES_CTRL_DEV_SLOT_DO_NOT_REMOVE_MASK = 0x40, + SES_CTRL_DEV_SLOT_DO_NOT_REMOVE_SHIFT = 6, + + SES_CTRL_DEV_SLOT_RQST_MISSING_BYTE = 1, + SES_CTRL_DEV_SLOT_RQST_MISSING_MASK = 0x10, + SES_CTRL_DEV_SLOT_RQST_MISSING_SHIFT = 4, + + SES_CTRL_DEV_SLOT_RQST_INSERT_BYTE = 1, + SES_CTRL_DEV_SLOT_RQST_INSERT_MASK = 0x08, + SES_CTRL_DEV_SLOT_RQST_INSERT_SHIFT = 3, + + SES_CTRL_DEV_SLOT_RQST_REMOVE_BYTE = 1, + SES_CTRL_DEV_SLOT_RQST_REMOVE_MASK = 0x04, + SES_CTRL_DEV_SLOT_RQST_REMOVE_SHIFT = 2, + + SES_CTRL_DEV_SLOT_RQST_IDENT_BYTE = 1, + SES_CTRL_DEV_SLOT_RQST_IDENT_MASK = 0x02, + SES_CTRL_DEV_SLOT_RQST_IDENT_SHIFT = 1, + + SES_CTRL_DEV_SLOT_RQST_FAULT_BYTE = 2, + SES_CTRL_DEV_SLOT_RQST_FAULT_MASK = 0x20, + SES_CTRL_DEV_SLOT_RQST_FAULT_SHIFT = 5, + + SES_CTRL_DEV_SLOT_DEVICE_OFF_BYTE = 2, + SES_CTRL_DEV_SLOT_DEVICE_OFF_MASK = 0x10, + SES_CTRL_DEV_SLOT_DEVICE_OFF_SHIFT = 4, + + SES_CTRL_DEV_SLOT_ENABLE_BYP_A_BYTE = 2, + SES_CTRL_DEV_SLOT_ENABLE_BYP_A_MASK = 0x08, + SES_CTRL_DEV_SLOT_ENABLE_BYP_A_SHIFT = 3, + + SES_CTRL_DEV_SLOT_ENABLE_BYP_B_BYTE = 2, + SES_CTRL_DEV_SLOT_ENABLE_BYP_B_MASK = 0x04, + SES_CTRL_DEV_SLOT_ENABLE_BYP_B_SHIFT = 2 +}; +#define GEN_SES_CTRL_DEV_SLOT_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_dev_slot, SES_CTRL_DEV_SLOT, LCASE, UCASE) + +GEN_SES_CTRL_DEV_SLOT_ACCESSORS(rqst_active, RQST_ACTIVE) +GEN_SES_CTRL_DEV_SLOT_ACCESSORS(do_not_remove, DO_NOT_REMOVE) +GEN_SES_CTRL_DEV_SLOT_ACCESSORS(rqst_missing, RQST_MISSING) +GEN_SES_CTRL_DEV_SLOT_ACCESSORS(rqst_insert, RQST_INSERT) +GEN_SES_CTRL_DEV_SLOT_ACCESSORS(rqst_remove, RQST_REMOVE) +GEN_SES_CTRL_DEV_SLOT_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_DEV_SLOT_ACCESSORS(rqst_fault, RQST_FAULT) +GEN_SES_CTRL_DEV_SLOT_ACCESSORS(device_off, DEVICE_OFF) +GEN_SES_CTRL_DEV_SLOT_ACCESSORS(enable_byp_a, ENABLE_BYP_A) +GEN_SES_CTRL_DEV_SLOT_ACCESSORS(enable_byp_b, ENABLE_BYP_B) +#undef GEN_SES_CTRL_DEV_SLOT_ACCESSORS + +/*--------------------- Array Device Slot Control Element --------------------*/ +struct ses_ctrl_array_dev_slot { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_array_dev_slot_field_data { + SES_CTRL_ARRAY_DEV_SLOT_RQST_OK_BYTE = 0, + SES_CTRL_ARRAY_DEV_SLOT_RQST_OK_MASK = 0x80, + SES_CTRL_ARRAY_DEV_SLOT_RQST_OK_SHIFT = 7, + + SES_CTRL_ARRAY_DEV_SLOT_RQST_RSVD_DEVICE_BYTE = 0, + SES_CTRL_ARRAY_DEV_SLOT_RQST_RSVD_DEVICE_MASK = 0x40, + SES_CTRL_ARRAY_DEV_SLOT_RQST_RSVD_DEVICE_SHIFT = 6, + + SES_CTRL_ARRAY_DEV_SLOT_RQST_HOT_SPARE_BYTE = 0, + SES_CTRL_ARRAY_DEV_SLOT_RQST_HOT_SPARE_MASK = 0x20, + SES_CTRL_ARRAY_DEV_SLOT_RQST_HOT_SPARE_SHIFT = 5, + + SES_CTRL_ARRAY_DEV_SLOT_RQST_CONS_CHECK_BYTE = 0, + SES_CTRL_ARRAY_DEV_SLOT_RQST_CONS_CHECK_MASK = 0x10, + SES_CTRL_ARRAY_DEV_SLOT_RQST_CONS_CHECK_SHIFT = 4, + + SES_CTRL_ARRAY_DEV_SLOT_RQST_IN_CRIT_ARRAY_BYTE = 0, + SES_CTRL_ARRAY_DEV_SLOT_RQST_IN_CRIT_ARRAY_MASK = 0x08, + SES_CTRL_ARRAY_DEV_SLOT_RQST_IN_CRIT_ARRAY_SHIFT = 3, + + SES_CTRL_ARRAY_DEV_SLOT_RQST_IN_FAILED_ARRAY_BYTE = 0, + SES_CTRL_ARRAY_DEV_SLOT_RQST_IN_FAILED_ARRAY_MASK = 0x04, + SES_CTRL_ARRAY_DEV_SLOT_RQST_IN_FAILED_ARRAY_SHIFT = 2, + + SES_CTRL_ARRAY_DEV_SLOT_RQST_REBUILD_REMAP_BYTE = 0, + SES_CTRL_ARRAY_DEV_SLOT_RQST_REBUILD_REMAP_MASK = 0x02, + SES_CTRL_ARRAY_DEV_SLOT_RQST_REBUILD_REMAP_SHIFT = 1, + + SES_CTRL_ARRAY_DEV_SLOT_RQST_REBUILD_REMAP_ABORT_BYTE = 0, + SES_CTRL_ARRAY_DEV_SLOT_RQST_REBUILD_REMAP_ABORT_MASK = 0x01, + SES_CTRL_ARRAY_DEV_SLOT_RQST_REBUILD_REMAP_ABORT_SHIFT = 0 + + /* + * The remaining fields are identical to the device + * slot element type. Access them through the device slot + * element type and its accessors. + */ +}; +#define GEN_SES_CTRL_ARRAY_DEV_SLOT_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_array_dev_slot, SES_CTRL_ARRAY_DEV_SLOT, \ + LCASE, UCASE) +GEN_SES_CTRL_ARRAY_DEV_SLOT_ACCESSORS(rqst_ok, RQST_OK) +GEN_SES_CTRL_ARRAY_DEV_SLOT_ACCESSORS(rqst_rsvd_device, RQST_RSVD_DEVICE) +GEN_SES_CTRL_ARRAY_DEV_SLOT_ACCESSORS(rqst_hot_spare, RQST_HOT_SPARE) +GEN_SES_CTRL_ARRAY_DEV_SLOT_ACCESSORS(rqst_cons_check, RQST_CONS_CHECK) +GEN_SES_CTRL_ARRAY_DEV_SLOT_ACCESSORS(rqst_in_crit_array, RQST_IN_CRIT_ARRAY) +GEN_SES_CTRL_ARRAY_DEV_SLOT_ACCESSORS(rqst_in_failed_array, + RQST_IN_FAILED_ARRAY) +GEN_SES_CTRL_ARRAY_DEV_SLOT_ACCESSORS(rqst_rebuild_remap, RQST_REBUILD_REMAP) +GEN_SES_CTRL_ARRAY_DEV_SLOT_ACCESSORS(rqst_rebuild_remap_abort, + RQST_REBUILD_REMAP_ABORT) +#undef GEN_SES_CTRL_ARRAY_DEV_SLOT_ACCESSORS + +/*----------------------- Power Supply Control Element -----------------------*/ +struct ses_ctrl_power_supply { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_power_supply_field_data { + SES_CTRL_POWER_SUPPLY_RQST_IDENT_BYTE = 0, + SES_CTRL_POWER_SUPPLY_RQST_IDENT_MASK = 0x80, + SES_CTRL_POWER_SUPPLY_RQST_IDENT_SHIFT = 7, + + SES_CTRL_POWER_SUPPLY_RQST_FAIL_BYTE = 2, + SES_CTRL_POWER_SUPPLY_RQST_FAIL_MASK = 0x40, + SES_CTRL_POWER_SUPPLY_RQST_FAIL_SHIFT = 6, + + SES_CTRL_POWER_SUPPLY_RQST_ON_BYTE = 2, + SES_CTRL_POWER_SUPPLY_RQST_ON_MASK = 0x20, + SES_CTRL_POWER_SUPPLY_RQST_ON_SHIFT = 5 +}; + +#define GEN_SES_CTRL_POWER_SUPPLY_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_power_supply, SES_CTRL_POWER_SUPPLY, LCASE, UCASE) +GEN_SES_CTRL_POWER_SUPPLY_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_POWER_SUPPLY_ACCESSORS(rqst_fail, RQST_FAIL) +GEN_SES_CTRL_POWER_SUPPLY_ACCESSORS(rqst_on, RQST_ON) +#undef GEN_SES_CTRL_POWER_SUPPLY_ACCESSORS + +/*-------------------------- Cooling Control Element -------------------------*/ +struct ses_ctrl_cooling { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_cooling_field_data { + SES_CTRL_COOLING_RQST_IDENT_BYTE = 0, + SES_CTRL_COOLING_RQST_IDENT_MASK = 0x80, + SES_CTRL_COOLING_RQST_IDENT_SHIFT = 7, + + SES_CTRL_COOLING_RQST_FAIL_BYTE = 2, + SES_CTRL_COOLING_RQST_FAIL_MASK = 0x40, + SES_CTRL_COOLING_RQST_FAIL_SHIFT = 6, + + SES_CTRL_COOLING_RQST_ON_BYTE = 2, + SES_CTRL_COOLING_RQST_ON_MASK = 0x20, + SES_CTRL_COOLING_RQST_ON_SHIFT = 5, + + SES_CTRL_COOLING_RQSTED_SPEED_CODE_BYTE = 2, + SES_CTRL_COOLING_RQSTED_SPEED_CODE_MASK = 0x07, + SES_CTRL_COOLING_RQSTED_SPEED_CODE_SHIFT = 2, + SES_CTRL_COOLING_RQSTED_SPEED_CODE_UNCHANGED = 0x00, + SES_CTRL_COOLING_RQSTED_SPEED_CODE_LOWEST = 0x01, + SES_CTRL_COOLING_RQSTED_SPEED_CODE_HIGHEST = 0x07 +}; + +#define GEN_SES_CTRL_COOLING_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_cooling, SES_CTRL_COOLING, LCASE, UCASE) +GEN_SES_CTRL_COOLING_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_COOLING_ACCESSORS(rqst_fail, RQST_FAIL) +GEN_SES_CTRL_COOLING_ACCESSORS(rqst_on, RQST_ON) +GEN_SES_CTRL_COOLING_ACCESSORS(rqsted_speed_code, RQSTED_SPEED_CODE) +#undef GEN_SES_CTRL_COOLING_ACCESSORS + +/*-------------------- Temperature Sensor Control Element --------------------*/ +struct ses_ctrl_temp_sensor { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_temp_sensor_field_data { + SES_CTRL_TEMP_SENSOR_RQST_IDENT_BYTE = 0, + SES_CTRL_TEMP_SENSOR_RQST_IDENT_MASK = 0x80, + SES_CTRL_TEMP_SENSOR_RQST_IDENT_SHIFT = 7, + + SES_CTRL_TEMP_SENSOR_RQST_FAIL_BYTE = 0, + SES_CTRL_TEMP_SENSOR_RQST_FAIL_MASK = 0x40, + SES_CTRL_TEMP_SENSOR_RQST_FAIL_SHIFT = 6 +}; + +#define GEN_SES_CTRL_TEMP_SENSOR_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_temp_sensor, SES_CTRL_TEMP_SENSOR, LCASE, UCASE) +GEN_SES_CTRL_TEMP_SENSOR_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_TEMP_SENSOR_ACCESSORS(rqst_fail, RQST_FAIL) +#undef GEN_SES_CTRL_TEMP_SENSOR_ACCESSORS + +/*------------------------- Door Lock Control Element ------------------------*/ +struct ses_ctrl_door_lock { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_door_lock_field_data { + SES_CTRL_DOOR_LOCK_RQST_IDENT_BYTE = 0, + SES_CTRL_DOOR_LOCK_RQST_IDENT_MASK = 0x80, + SES_CTRL_DOOR_LOCK_RQST_IDENT_SHIFT = 7, + + SES_CTRL_DOOR_LOCK_RQST_FAIL_BYTE = 0, + SES_CTRL_DOOR_LOCK_RQST_FAIL_MASK = 0x40, + SES_CTRL_DOOR_LOCK_RQST_FAIL_SHIFT = 6, + + SES_CTRL_DOOR_LOCK_UNLOCK_BYTE = 2, + SES_CTRL_DOOR_LOCK_UNLOCK_MASK = 0x01, + SES_CTRL_DOOR_LOCK_UNLOCK_SHIFT = 0 +}; + +#define GEN_SES_CTRL_DOOR_LOCK_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_door_lock, SES_CTRL_DOOR_LOCK, LCASE, UCASE) +GEN_SES_CTRL_DOOR_LOCK_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_DOOR_LOCK_ACCESSORS(rqst_fail, RQST_FAIL) +GEN_SES_CTRL_DOOR_LOCK_ACCESSORS(unlock, UNLOCK) +#undef GEN_SES_CTRL_DOOR_LOCK_ACCESSORS + +/*----------------------- Audible Alarm Control Element ----------------------*/ +struct ses_ctrl_audible_alarm { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_audible_alarm_field_data { + SES_CTRL_AUDIBLE_ALARM_RQST_IDENT_BYTE = 0, + SES_CTRL_AUDIBLE_ALARM_RQST_IDENT_MASK = 0x80, + SES_CTRL_AUDIBLE_ALARM_RQST_IDENT_SHIFT = 7, + + SES_CTRL_AUDIBLE_ALARM_RQST_FAIL_BYTE = 0, + SES_CTRL_AUDIBLE_ALARM_RQST_FAIL_MASK = 0x40, + SES_CTRL_AUDIBLE_ALARM_RQST_FAIL_SHIFT = 6, + + SES_CTRL_AUDIBLE_ALARM_SET_MUTE_BYTE = 2, + SES_CTRL_AUDIBLE_ALARM_SET_MUTE_MASK = 0x40, + SES_CTRL_AUDIBLE_ALARM_SET_MUTE_SHIFT = 6, + + SES_CTRL_AUDIBLE_ALARM_SET_REMIND_BYTE = 2, + SES_CTRL_AUDIBLE_ALARM_SET_REMIND_MASK = 0x10, + SES_CTRL_AUDIBLE_ALARM_SET_REMIND_SHIFT = 4, + + SES_CTRL_AUDIBLE_ALARM_TONE_CONTROL_BYTE = 2, + SES_CTRL_AUDIBLE_ALARM_TONE_CONTROL_MASK = 0x0F, + SES_CTRL_AUDIBLE_ALARM_TONE_CONTROL_SHIFT = 0, + SES_CTRL_AUDIBLE_ALARM_TONE_CONTROL_INFO = 0x08, + SES_CTRL_AUDIBLE_ALARM_TONE_CONTROL_NON_CRIT = 0x04, + SES_CTRL_AUDIBLE_ALARM_TONE_CONTROL_CRIT = 0x02, + SES_CTRL_AUDIBLE_ALARM_TONE_CONTROL_UNRECOV = 0x01 +}; + +#define GEN_SES_CTRL_AUDIBLE_ALARM_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_audible_alarm, SES_CTRL_AUDIBLE_ALARM, LCASE, UCASE) +GEN_SES_CTRL_AUDIBLE_ALARM_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_AUDIBLE_ALARM_ACCESSORS(rqst_fail, RQST_FAIL) +GEN_SES_CTRL_AUDIBLE_ALARM_ACCESSORS(set_mute, SET_MUTE) +GEN_SES_CTRL_AUDIBLE_ALARM_ACCESSORS(set_remind, SET_REMIND) +GEN_SES_CTRL_AUDIBLE_ALARM_ACCESSORS(tone_control, TONE_CONTROL) +#undef GEN_SES_CTRL_AUDIBLE_ALARM_ACCESSORS + +/*--------- Enclosure Services Controller Electronics Control Element --------*/ +struct ses_ctrl_ecc_electronics { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_ecc_electronics_field_data { + SES_CTRL_ECC_ELECTRONICS_RQST_IDENT_BYTE = 0, + SES_CTRL_ECC_ELECTRONICS_RQST_IDENT_MASK = 0x80, + SES_CTRL_ECC_ELECTRONICS_RQST_IDENT_SHIFT = 7, + + SES_CTRL_ECC_ELECTRONICS_RQST_FAIL_BYTE = 0, + SES_CTRL_ECC_ELECTRONICS_RQST_FAIL_MASK = 0x40, + SES_CTRL_ECC_ELECTRONICS_RQST_FAIL_SHIFT = 6, + + SES_CTRL_ECC_ELECTRONICS_SELECT_ELEMENT_BYTE = 1, + SES_CTRL_ECC_ELECTRONICS_SELECT_ELEMENT_MASK = 0x01, + SES_CTRL_ECC_ELECTRONICS_SELECT_ELEMENT_SHIFT = 0 +}; + +#define GEN_SES_CTRL_ECC_ELECTRONICS_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_ecc_electronics, SES_CTRL_ECC_ELECTRONICS, \ + LCASE, UCASE) +GEN_SES_CTRL_ECC_ELECTRONICS_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_ECC_ELECTRONICS_ACCESSORS(rqst_fail, RQST_FAIL) +GEN_SES_CTRL_ECC_ELECTRONICS_ACCESSORS(select_element, SELECT_ELEMENT) +#undef GEN_SES_CTRL_ECC_ELECTRONICS_ACCESSORS + +/*----------- SCSI Services Controller Electronics Control Element -----------*/ +struct ses_ctrl_scc_electronics { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_scc_electronics_field_data { + SES_CTRL_SCC_ELECTRONICS_RQST_IDENT_BYTE = 0, + SES_CTRL_SCC_ELECTRONICS_RQST_IDENT_MASK = 0x80, + SES_CTRL_SCC_ELECTRONICS_RQST_IDENT_SHIFT = 7, + + SES_CTRL_SCC_ELECTRONICS_RQST_FAIL_BYTE = 0, + SES_CTRL_SCC_ELECTRONICS_RQST_FAIL_MASK = 0x40, + SES_CTRL_SCC_ELECTRONICS_RQST_FAIL_SHIFT = 6 +}; + +#define GEN_SES_CTRL_SCC_ELECTRONICS_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_scc_electronics, SES_CTRL_SCC_ELECTRONICS, \ + LCASE, UCASE) +GEN_SES_CTRL_SCC_ELECTRONICS_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_SCC_ELECTRONICS_ACCESSORS(rqst_fail, RQST_FAIL) +#undef GEN_SES_CTRL_SCC_ELECTRONICS_ACCESSORS + +/*--------------------- Nonvolatile Cache Control Element --------------------*/ +struct ses_ctrl_nv_cache { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_nv_cache_field_data { + SES_CTRL_NV_CACHE_RQST_IDENT_BYTE = 0, + SES_CTRL_NV_CACHE_RQST_IDENT_MASK = 0x80, + SES_CTRL_NV_CACHE_RQST_IDENT_SHIFT = 7, + + SES_CTRL_NV_CACHE_RQST_FAIL_BYTE = 0, + SES_CTRL_NV_CACHE_RQST_FAIL_MASK = 0x40, + SES_CTRL_NV_CACHE_RQST_FAIL_SHIFT = 6 +}; + +#define GEN_SES_CTRL_NV_CACHE_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_nv_cache, SES_CTRL_NV_CACHE, LCASE, UCASE) +GEN_SES_CTRL_NV_CACHE_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_NV_CACHE_ACCESSORS(rqst_fail, RQST_FAIL) +#undef GEN_SES_CTRL_NV_CACHE_ACCESSORS + +/*----------------- Invalid Operation Reason Control Element -----------------*/ +struct ses_ctrl_invalid_op_reason { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +/* There are no element specific fields currently defined in the spec. */ + +/*--------------- Uninterruptible Power Supply Control Element ---------------*/ +struct ses_ctrl_ups { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_ups_field_data { + SES_CTRL_UPS_RQST_IDENT_BYTE = 2, + SES_CTRL_UPS_RQST_IDENT_MASK = 0x80, + SES_CTRL_UPS_RQST_IDENT_SHIFT = 7, + + SES_CTRL_UPS_RQST_FAIL_BYTE = 2, + SES_CTRL_UPS_RQST_FAIL_MASK = 0x40, + SES_CTRL_UPS_RQST_FAIL_SHIFT = 6 +}; + +#define GEN_SES_CTRL_UPS_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_ups, SES_CTRL_UPS, LCASE, UCASE) +GEN_SES_CTRL_UPS_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_UPS_ACCESSORS(rqst_fail, RQST_FAIL) +#undef GEN_SES_CTRL_UPS_ACCESSORS + +/*-------------------------- Display Control Element -------------------------*/ +struct ses_ctrl_display { + struct ses_ctrl_common common; + uint8_t bytes[1]; + uint8_t display_character[2]; +}; + +enum ses_ctrl_display_field_data { + SES_CTRL_DISPLAY_RQST_IDENT_BYTE = 0, + SES_CTRL_DISPLAY_RQST_IDENT_MASK = 0x80, + SES_CTRL_DISPLAY_RQST_IDENT_SHIFT = 7, + + SES_CTRL_DISPLAY_RQST_FAIL_BYTE = 0, + SES_CTRL_DISPLAY_RQST_FAIL_MASK = 0x40, + SES_CTRL_DISPLAY_RQST_FAIL_SHIFT = 6, + + SES_CTRL_DISPLAY_DISPLAY_MODE_BYTE = 0, + SES_CTRL_DISPLAY_DISPLAY_MODE_MASK = 0x03, + SES_CTRL_DISPLAY_DISPLAY_MODE_SHIFT = 6, + SES_CTRL_DISPLAY_DISPLAY_MODE_UNCHANGED = 0x0, + SES_CTRL_DISPLAY_DISPLAY_MODE_ESP = 0x1, + SES_CTRL_DISPLAY_DISPLAY_MODE_DC_FIELD = 0x2 +}; + +#define GEN_SES_CTRL_DISPLAY_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_display, SES_CTRL_DISPLAY, LCASE, UCASE) +GEN_SES_CTRL_DISPLAY_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_DISPLAY_ACCESSORS(rqst_fail, RQST_FAIL) +GEN_SES_CTRL_DISPLAY_ACCESSORS(display_mode, DISPLAY_MODE) +#undef GEN_SES_CTRL_DISPLAY_ACCESSORS + +/*----------------------- Key Pad Entry Control Element ----------------------*/ +struct ses_ctrl_key_pad_entry { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_key_pad_entry_field_data { + SES_CTRL_KEY_PAD_ENTRY_RQST_IDENT_BYTE = 0, + SES_CTRL_KEY_PAD_ENTRY_RQST_IDENT_MASK = 0x80, + SES_CTRL_KEY_PAD_ENTRY_RQST_IDENT_SHIFT = 7, + + SES_CTRL_KEY_PAD_ENTRY_RQST_FAIL_BYTE = 0, + SES_CTRL_KEY_PAD_ENTRY_RQST_FAIL_MASK = 0x40, + SES_CTRL_KEY_PAD_ENTRY_RQST_FAIL_SHIFT = 6 +}; + +#define GEN_SES_CTRL_KEY_PAD_ENTRY_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_key_pad_entry, SES_CTRL_KEY_PAD_ENTRY, LCASE, UCASE) +GEN_SES_CTRL_KEY_PAD_ENTRY_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_KEY_PAD_ENTRY_ACCESSORS(rqst_fail, RQST_FAIL) +#undef GEN_SES_CTRL_KEY_PAD_ENTRY_ACCESSORS + +/*------------------------- Enclosure Control Element ------------------------*/ +struct ses_ctrl_enclosure { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_enclosure_field_data { + SES_CTRL_SESLOSURE_RQST_IDENT_BYTE = 0, + SES_CTRL_SESLOSURE_RQST_IDENT_MASK = 0x80, + SES_CTRL_SESLOSURE_RQST_IDENT_SHIFT = 7, + + SES_CTRL_SESLOSURE_POWER_CYCLE_RQST_BYTE = 1, + SES_CTRL_SESLOSURE_POWER_CYCLE_RQST_MASK = 0xC0, + SES_CTRL_SESLOSURE_POWER_CYCLE_RQST_SHIFT = 6, + SES_CTRL_SESLOSURE_POWER_CYCLE_RQST_NONE = 0x0, + SES_CTRL_SESLOSURE_POWER_CYCLE_RQST_AFTER_DELAY = 0x1, + SES_CTRL_SESLOSURE_POWER_CYCLE_RQST_CANCEL = 0x2, + + SES_CTRL_SESLOSURE_POWER_CYCLE_DELAY_BYTE = 1, + SES_CTRL_SESLOSURE_POWER_CYCLE_DELAY_MASK = 0x3F, + SES_CTRL_SESLOSURE_POWER_CYCLE_DELAY_SHIFT = 0, + SES_CTRL_SESLOSURE_POWER_CYCLE_DELAY_MAX = 60,/*minutes*/ + + SES_CTRL_SESLOSURE_POWER_OFF_DURATION_BYTE = 2, + SES_CTRL_SESLOSURE_POWER_OFF_DURATION_MASK = 0xFC, + SES_CTRL_SESLOSURE_POWER_OFF_DURATION_SHIFT = 2, + SES_CTRL_SESLOSURE_POWER_OFF_DURATION_MAX_AUTO = 60, + SES_CTRL_SESLOSURE_POWER_OFF_DURATION_MANUAL = 63, + + SES_CTRL_SESLOSURE_RQST_FAIL_BYTE = 2, + SES_CTRL_SESLOSURE_RQST_FAIL_MASK = 0x02, + SES_CTRL_SESLOSURE_RQST_FAIL_SHIFT = 1, + + SES_CTRL_SESLOSURE_RQST_WARN_BYTE = 2, + SES_CTRL_SESLOSURE_RQST_WARN_MASK = 0x01, + SES_CTRL_SESLOSURE_RQST_WARN_SHIFT = 0 +}; + +#define GEN_SES_CTRL_SESLOSURE_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_enclosure, SES_CTRL_SESLOSURE, LCASE, UCASE) +GEN_SES_CTRL_SESLOSURE_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_SESLOSURE_ACCESSORS(power_cycle_rqst, POWER_CYCLE_RQST) +GEN_SES_CTRL_SESLOSURE_ACCESSORS(power_cycle_delay, POWER_CYCLE_DELAY) +GEN_SES_CTRL_SESLOSURE_ACCESSORS(power_off_duration, POWER_OFF_DURATION) +GEN_SES_CTRL_SESLOSURE_ACCESSORS(rqst_fail, RQST_FAIL) +GEN_SES_CTRL_SESLOSURE_ACCESSORS(rqst_warn, RQST_WARN) +#undef GEN_SES_CTRL_SESLOSURE_ACCESSORS + +/*------------------- SCSI Port/Transceiver Control Element ------------------*/ +struct ses_ctrl_scsi_port_or_xcvr { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_scsi_port_or_xcvr_field_data { + SES_CTRL_SCSI_PORT_OR_XCVR_RQST_IDENT_BYTE = 0, + SES_CTRL_SCSI_PORT_OR_XCVR_RQST_IDENT_MASK = 0x80, + SES_CTRL_SCSI_PORT_OR_XCVR_RQST_IDENT_SHIFT = 7, + + SES_CTRL_SCSI_PORT_OR_XCVR_RQST_FAIL_BYTE = 0, + SES_CTRL_SCSI_PORT_OR_XCVR_RQST_FAIL_MASK = 0x40, + SES_CTRL_SCSI_PORT_OR_XCVR_RQST_FAIL_SHIFT = 6, + + SES_CTRL_SCSI_PORT_OR_XCVR_DISABLE_BYTE = 2, + SES_CTRL_SCSI_PORT_OR_XCVR_DISABLE_MASK = 0x10, + SES_CTRL_SCSI_PORT_OR_XCVR_DISABLE_SHIFT = 4 +}; + +#define GEN_SES_CTRL_SCSI_PORT_OR_XCVR_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_scsi_port_or_xcvr, SES_CTRL_SCSI_PORT_OR_XCVR,\ + LCASE, UCASE) +GEN_SES_CTRL_SCSI_PORT_OR_XCVR_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_SCSI_PORT_OR_XCVR_ACCESSORS(disable, DISABLE) +GEN_SES_CTRL_SCSI_PORT_OR_XCVR_ACCESSORS(rqst_fail, RQST_FAIL) +#undef GEN_SES_CTRL_SCSI_PORT_OR_XCVR_ACCESSORS + +/*------------------------- Language Control Element -------------------------*/ +struct ses_ctrl_language { + struct ses_ctrl_common common; + uint8_t bytes[1]; + uint8_t language_code[2]; +}; + +enum ses_ctrl_language_field_data { + SES_CTRL_LANGUAGE_RQST_IDENT_BYTE = 0, + SES_CTRL_LANGUAGE_RQST_IDENT_MASK = 0x80, + SES_CTRL_LANGUAGE_RQST_IDENT_SHIFT = 7 +}; + +#define GEN_SES_CTRL_LANGUAGE_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_language, SES_CTRL_LANGUAGE, LCASE, UCASE) +GEN_SES_CTRL_LANGUAGE_ACCESSORS(rqst_ident, RQST_IDENT) +#undef GEN_SES_CTRL_LANGUAGE_ACCESSORS + +/*-------------------- Communication Port Control Element --------------------*/ +struct ses_ctrl_comm_port { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_comm_port_field_data { + SES_CTRL_COMM_PORT_RQST_IDENT_BYTE = 0, + SES_CTRL_COMM_PORT_RQST_IDENT_MASK = 0x80, + SES_CTRL_COMM_PORT_RQST_IDENT_SHIFT = 7, + + SES_CTRL_COMM_PORT_RQST_FAIL_BYTE = 0, + SES_CTRL_COMM_PORT_RQST_FAIL_MASK = 0x40, + SES_CTRL_COMM_PORT_RQST_FAIL_SHIFT = 6, + + SES_CTRL_COMM_PORT_DISABLE_BYTE = 2, + SES_CTRL_COMM_PORT_DISABLE_MASK = 0x01, + SES_CTRL_COMM_PORT_DISABLE_SHIFT = 0 +}; + +#define GEN_SES_CTRL_COMM_PORT_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_comm_port, SES_CTRL_COMM_PORT, LCASE, UCASE) +GEN_SES_CTRL_COMM_PORT_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_COMM_PORT_ACCESSORS(rqst_fail, RQST_FAIL) +GEN_SES_CTRL_COMM_PORT_ACCESSORS(disable, DISABLE) +#undef GEN_SES_CTRL_COMM_PORT_ACCESSORS + +/*---------------------- Voltage Sensor Control Element ----------------------*/ +struct ses_ctrl_voltage_sensor { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_voltage_sensor_field_data { + SES_CTRL_VOLTAGE_SENSOR_RQST_IDENT_BYTE = 0, + SES_CTRL_VOLTAGE_SENSOR_RQST_IDENT_MASK = 0x80, + SES_CTRL_VOLTAGE_SENSOR_RQST_IDENT_SHIFT = 7, + + SES_CTRL_VOLTAGE_SENSOR_RQST_FAIL_BYTE = 0, + SES_CTRL_VOLTAGE_SENSOR_RQST_FAIL_MASK = 0x40, + SES_CTRL_VOLTAGE_SENSOR_RQST_FAIL_SHIFT = 6 +}; + +#define GEN_SES_CTRL_VOLTAGE_SENSOR_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_voltage_sensor, SES_CTRL_VOLTAGE_SENSOR, \ + LCASE, UCASE) +GEN_SES_CTRL_VOLTAGE_SENSOR_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_VOLTAGE_SENSOR_ACCESSORS(rqst_fail, RQST_FAIL) +#undef GEN_SES_CTRL_VOLTAGE_SENSOR_ACCESSORS + +/*---------------------- Current Sensor Control Element ----------------------*/ +struct ses_ctrl_current_sensor { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_current_sensor_field_data { + SES_CTRL_CURRENT_SENSOR_RQST_IDENT_BYTE = 0, + SES_CTRL_CURRENT_SENSOR_RQST_IDENT_MASK = 0x80, + SES_CTRL_CURRENT_SENSOR_RQST_IDENT_SHIFT = 7, + + SES_CTRL_CURRENT_SENSOR_RQST_FAIL_BYTE = 0, + SES_CTRL_CURRENT_SENSOR_RQST_FAIL_MASK = 0x40, + SES_CTRL_CURRENT_SENSOR_RQST_FAIL_SHIFT = 6 +}; + +#define GEN_SES_CTRL_CURRENT_SENSOR_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_current_sensor, SES_CTRL_CURRENT_SENSOR, \ + LCASE, UCASE) +GEN_SES_CTRL_CURRENT_SENSOR_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_CURRENT_SENSOR_ACCESSORS(rqst_fail, RQST_FAIL) +#undef GEN_SES_CTRL_CURRENT_SENSOR_ACCESSORS + +/*--------------------- SCSI Target Port Control Element ---------------------*/ +struct ses_ctrl_target_port { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_scsi_target_port_field_data { + SES_CTRL_TARGET_PORT_RQST_IDENT_BYTE = 0, + SES_CTRL_TARGET_PORT_RQST_IDENT_MASK = 0x80, + SES_CTRL_TARGET_PORT_RQST_IDENT_SHIFT = 7, + + SES_CTRL_TARGET_PORT_RQST_FAIL_BYTE = 0, + SES_CTRL_TARGET_PORT_RQST_FAIL_MASK = 0x40, + SES_CTRL_TARGET_PORT_RQST_FAIL_SHIFT = 6, + + SES_CTRL_TARGET_PORT_ENABLE_BYTE = 2, + SES_CTRL_TARGET_PORT_ENABLE_MASK = 0x01, + SES_CTRL_TARGET_PORT_ENABLE_SHIFT = 0 +}; + +#define GEN_SES_CTRL_TARGET_PORT_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_target_port, SES_CTRL_TARGET_PORT, LCASE, UCASE) +GEN_SES_CTRL_TARGET_PORT_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_TARGET_PORT_ACCESSORS(rqst_fail, RQST_FAIL) +GEN_SES_CTRL_TARGET_PORT_ACCESSORS(enable, ENABLE) +#undef GEN_SES_CTRL_TARGET_PORT_ACCESSORS + +/*-------------------- SCSI Initiator Port Control Element -------------------*/ +struct ses_ctrl_initiator_port { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_initiator_port_field_data { + SES_CTRL_INITIATOR_PORT_RQST_IDENT_BYTE = 0, + SES_CTRL_INITIATOR_PORT_RQST_IDENT_MASK = 0x80, + SES_CTRL_INITIATOR_PORT_RQST_IDENT_SHIFT = 7, + + SES_CTRL_INITIATOR_PORT_RQST_FAIL_BYTE = 0, + SES_CTRL_INITIATOR_PORT_RQST_FAIL_MASK = 0x40, + SES_CTRL_INITIATOR_PORT_RQST_FAIL_SHIFT = 6, + + SES_CTRL_INITIATOR_PORT_ENABLE_BYTE = 2, + SES_CTRL_INITIATOR_PORT_ENABLE_MASK = 0x01, + SES_CTRL_INITIATOR_PORT_ENABLE_SHIFT = 0 +}; + +#define GEN_SES_CTRL_INITIATOR_PORT_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_initiator_port, SES_CTRL_INITIATOR_PORT, \ + LCASE, UCASE) +GEN_SES_CTRL_INITIATOR_PORT_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_INITIATOR_PORT_ACCESSORS(rqst_fail, RQST_FAIL) +GEN_SES_CTRL_INITIATOR_PORT_ACCESSORS(enable, ENABLE) +#undef GEN_SES_CTRL_INITIATOR_PORT_ACCESSORS + +/*-------------------- Simple Subenclosure Control Element -------------------*/ +struct ses_ctrl_simple_subenc { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_simple_subenc_field_data { + SES_CTRL_SIMPlE_SUBSES_RQST_IDENT_BYTE = 0, + SES_CTRL_SIMPlE_SUBSES_RQST_IDENT_MASK = 0x80, + SES_CTRL_SIMPlE_SUBSES_RQST_IDENT_SHIFT = 7, + + SES_CTRL_SIMPlE_SUBSES_RQST_FAIL_BYTE = 0, + SES_CTRL_SIMPlE_SUBSES_RQST_FAIL_MASK = 0x40, + SES_CTRL_SIMPlE_SUBSES_RQST_FAIL_SHIFT = 6 +}; + +#define GEN_SES_CTRL_SIMPlE_SUBSES_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_simple_subenc, SES_CTRL_SIMPlE_SUBSES, \ + LCASE, UCASE) +GEN_SES_CTRL_SIMPlE_SUBSES_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_SIMPlE_SUBSES_ACCESSORS(rqst_fail, RQST_FAIL) +#undef GEN_SES_CTRL_SIMPlE_SUBSES_ACCESSORS + +/*----------------------- SAS Expander Control Element -----------------------*/ +struct ses_ctrl_sas_expander { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_sas_expander_field_data { + SES_CTRL_SAS_EXPANDER_RQST_IDENT_BYTE = 0, + SES_CTRL_SAS_EXPANDER_RQST_IDENT_MASK = 0x80, + SES_CTRL_SAS_EXPANDER_RQST_IDENT_SHIFT = 7, + + SES_CTRL_SAS_EXPANDER_RQST_FAIL_BYTE = 0, + SES_CTRL_SAS_EXPANDER_RQST_FAIL_MASK = 0x40, + SES_CTRL_SAS_EXPANDER_RQST_FAIL_SHIFT = 6 +}; + +#define GEN_SES_CTRL_SAS_EXPANDER_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_sas_expander, SES_CTRL_SAS_EXPANDER, LCASE, UCASE) +GEN_SES_CTRL_SAS_EXPANDER_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_SAS_EXPANDER_ACCESSORS(rqst_fail, RQST_FAIL) +#undef GEN_SES_CTRL_SAS_EXPANDER_ACCESSORS + +/*----------------------- SAS Connector Control Element ----------------------*/ +struct ses_ctrl_sas_connector { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_sas_connector_field_data { + SES_CTRL_SAS_CONNECTOR_RQST_IDENT_BYTE = 0, + SES_CTRL_SAS_CONNECTOR_RQST_IDENT_MASK = 0x80, + SES_CTRL_SAS_CONNECTOR_RQST_IDENT_SHIFT = 7, + + SES_CTRL_SAS_CONNECTOR_RQST_FAIL_BYTE = 2, + SES_CTRL_SAS_CONNECTOR_RQST_FAIL_MASK = 0x40, + SES_CTRL_SAS_CONNECTOR_RQST_FAIL_SHIFT = 6 +}; + +#define GEN_SES_CTRL_SAS_CONNECTOR_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_sas_connector, SES_CTRL_SAS_CONNECTOR, \ + LCASE, UCASE) +GEN_SES_CTRL_SAS_CONNECTOR_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_SAS_CONNECTOR_ACCESSORS(rqst_fail, RQST_FAIL) +#undef GEN_SES_CTRL_SAS_CONNECTOR_ACCESSORS + +/*------------------------- Universal Control Element ------------------------*/ +union ses_ctrl_element { + struct ses_ctrl_common common; + struct ses_ctrl_dev_slot dev_slot; + struct ses_ctrl_array_dev_slot array_dev_slot; + struct ses_ctrl_power_supply power_supply; + struct ses_ctrl_cooling cooling; + struct ses_ctrl_temp_sensor temp_sensor; + struct ses_ctrl_door_lock door_lock; + struct ses_ctrl_audible_alarm audible_alarm; + struct ses_ctrl_ecc_electronics ecc_electronics; + struct ses_ctrl_scc_electronics scc_electronics; + struct ses_ctrl_nv_cache nv_cache; + struct ses_ctrl_invalid_op_reason invalid_op_reason; + struct ses_ctrl_ups ups; + struct ses_ctrl_display display; + struct ses_ctrl_key_pad_entry key_pad_entry; + struct ses_ctrl_scsi_port_or_xcvr scsi_port_or_xcvr; + struct ses_ctrl_language language; + struct ses_ctrl_comm_port comm_port; + struct ses_ctrl_voltage_sensor voltage_sensor; + struct ses_ctrl_current_sensor current_sensor; + struct ses_ctrl_target_port target_port; + struct ses_ctrl_initiator_port initiator_port; + struct ses_ctrl_simple_subenc simple_subenc; + struct ses_ctrl_sas_expander sas_expander; + struct ses_ctrl_sas_connector sas_connector; +}; + +/*--------------------- SCSI SES Control Diagnostic Page ---------------------*/ +struct ses_ctrl_page { + struct ses_page_hdr hdr; + union ses_ctrl_element elements[]; +}; + +enum ses_ctrl_page_field_data { + SES_CTRL_PAGE_INFO_MASK = 0x08, + SES_CTRL_PAGE_INFO_SHIFT = 3, + + SES_CTRL_PAGE_NON_CRIT_MASK = 0x04, + SES_CTRL_PAGE_NON_CRIT_SHIFT = 2, + + SES_CTRL_PAGE_CRIT_MASK = 0x02, + SES_CTRL_PAGE_CRIT_SHIFT = 1, + + SES_CTRL_PAGE_UNRECOV_MASK = 0x01, + SES_CTRL_PAGE_UNRECOV_SHIFT = 0 +}; + +#define GEN_SES_CTRL_PAGE_ACCESSORS(LCASE, UCASE) \ + GEN_HDR_ACCESSORS(ses_ctrl_page, SES_CTRL_PAGE, LCASE, UCASE) + +GEN_SES_CTRL_PAGE_ACCESSORS(info, INFO) +GEN_SES_CTRL_PAGE_ACCESSORS(non_crit, NON_CRIT) +GEN_SES_CTRL_PAGE_ACCESSORS(crit, CRIT) +GEN_SES_CTRL_PAGE_ACCESSORS(unrecov, UNRECOV) +#undef GEN_SES_CTRL_PAGE_ACCESSORS + +/*================= SCSI SES Status Diagnostic Page Structures ===============*/ +struct ses_status_common { + uint8_t bytes[1]; +}; + +enum ses_status_common_field_data { + SES_STATUS_COMMON_PRDFAIL_BYTE = 0, + SES_STATUS_COMMON_PRDFAIL_MASK = 0x40, + SES_STATUS_COMMON_PRDFAIL_SHIFT = 6, + + SES_STATUS_COMMON_DISABLED_BYTE = 0, + SES_STATUS_COMMON_DISABLED_MASK = 0x20, + SES_STATUS_COMMON_DISABLED_SHIFT = 5, + + SES_STATUS_COMMON_SWAP_BYTE = 0, + SES_STATUS_COMMON_SWAP_MASK = 0x10, + SES_STATUS_COMMON_SWAP_SHIFT = 4, + + SES_STATUS_COMMON_ELEMENT_STATUS_CODE_BYTE = 0, + SES_STATUS_COMMON_ELEMENT_STATUS_CODE_MASK = 0x0F, + SES_STATUS_COMMON_ELEMENT_STATUS_CODE_SHIFT = 0 +}; + +#define GEN_SES_STATUS_COMMON_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_common, SES_STATUS_COMMON, LCASE, UCASE) + +GEN_SES_STATUS_COMMON_ACCESSORS(prdfail, PRDFAIL) +GEN_SES_STATUS_COMMON_ACCESSORS(disabled, DISABLED) +GEN_SES_STATUS_COMMON_ACCESSORS(swap, SWAP) +GEN_SES_STATUS_COMMON_ACCESSORS(element_status_code, ELEMENT_STATUS_CODE) +#undef GEN_SES_STATUS_COMMON_ACCESSORS + +/*------------------------- Device Slot Status Element -----------------------*/ +struct ses_status_dev_slot { + struct ses_status_common common; + uint8_t slot_address; + uint8_t bytes[2]; +}; + +enum ses_status_dev_slot_field_data { + SES_STATUS_DEV_SLOT_APP_CLIENT_BYPED_A_BYTE = 0, + SES_STATUS_DEV_SLOT_APP_CLIENT_BYPED_A_MASK = 0x80, + SES_STATUS_DEV_SLOT_APP_CLIENT_BYPED_A_SHIFT = 7, + + SES_STATUS_DEV_SLOT_DO_NOT_REMOVE_BYTE = 0, + SES_STATUS_DEV_SLOT_DO_NOT_REMOVE_MASK = 0x40, + SES_STATUS_DEV_SLOT_DO_NOT_REMOVE_SHIFT = 6, + + SES_STATUS_DEV_SLOT_SESLOSURE_BYPED_A_BYTE = 0, + SES_STATUS_DEV_SLOT_SESLOSURE_BYPED_A_MASK = 0x20, + SES_STATUS_DEV_SLOT_SESLOSURE_BYPED_A_SHIFT = 5, + + SES_STATUS_DEV_SLOT_SESLOSURE_BYPED_B_BYTE = 0, + SES_STATUS_DEV_SLOT_SESLOSURE_BYPED_B_MASK = 0x10, + SES_STATUS_DEV_SLOT_SESLOSURE_BYPED_B_SHIFT = 4, + + SES_STATUS_DEV_SLOT_INSERT_READY_BYTE = 0, + SES_STATUS_DEV_SLOT_INSERT_READY_MASK = 0x08, + SES_STATUS_DEV_SLOT_INSERT_READY_SHIFT = 3, + + SES_STATUS_DEV_SLOT_REMOVE_BYTE = 0, + SES_STATUS_DEV_SLOT_REMOVE_MASK = 0x04, + SES_STATUS_DEV_SLOT_REMOVE_SHIFT = 2, + + SES_STATUS_DEV_SLOT_IDENT_BYTE = 0, + SES_STATUS_DEV_SLOT_IDENT_MASK = 0x02, + SES_STATUS_DEV_SLOT_IDENT_SHIFT = 1, + + SES_STATUS_DEV_SLOT_REPORT_BYTE = 0, + SES_STATUS_DEV_SLOT_REPORT_MASK = 0x01, + SES_STATUS_DEV_SLOT_REPORT_SHIFT = 0, + + SES_STATUS_DEV_SLOT_APP_CLIENT_BYPED_B_BYTE = 1, + SES_STATUS_DEV_SLOT_APP_CLIENT_BYPED_B_MASK = 0x80, + SES_STATUS_DEV_SLOT_APP_CLIENT_BYPED_B_SHIFT = 7, + + SES_STATUS_DEV_SLOT_FAULT_SENSED_BYTE = 1, + SES_STATUS_DEV_SLOT_FAULT_SENSED_MASK = 0x40, + SES_STATUS_DEV_SLOT_FAULT_SENSED_SHIFT = 6, + + SES_STATUS_DEV_SLOT_FAULT_REQUESTED_BYTE = 1, + SES_STATUS_DEV_SLOT_FAULT_REQUESTED_MASK = 0x20, + SES_STATUS_DEV_SLOT_FAULT_REQUESTED_SHIFT = 5, + + SES_STATUS_DEV_SLOT_DEVICE_OFF_BYTE = 1, + SES_STATUS_DEV_SLOT_DEVICE_OFF_MASK = 0x10, + SES_STATUS_DEV_SLOT_DEVICE_OFF_SHIFT = 4, + + SES_STATUS_DEV_SLOT_BYPED_A_BYTE = 1, + SES_STATUS_DEV_SLOT_BYPED_A_MASK = 0x08, + SES_STATUS_DEV_SLOT_BYPED_A_SHIFT = 3, + + SES_STATUS_DEV_SLOT_BYPED_B_BYTE = 1, + SES_STATUS_DEV_SLOT_BYPED_B_MASK = 0x04, + SES_STATUS_DEV_SLOT_BYPED_B_SHIFT = 2, + + SES_STATUS_DEV_SLOT_DEVICE_BYPED_A_BYTE = 1, + SES_STATUS_DEV_SLOT_DEVICE_BYPED_A_MASK = 0x02, + SES_STATUS_DEV_SLOT_DEVICE_BYPED_A_SHIFT = 1, + + SES_STATUS_DEV_SLOT_DEVICE_BYPED_B_BYTE = 1, + SES_STATUS_DEV_SLOT_DEVICE_BYPED_B_MASK = 0x01, + SES_STATUS_DEV_SLOT_DEVICE_BYPED_B_SHIFT = 0 +}; +#define GEN_SES_STATUS_DEV_SLOT_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_dev_slot, SES_STATUS_DEV_SLOT, LCASE, UCASE) + +GEN_SES_STATUS_DEV_SLOT_ACCESSORS(app_client_byped_a, APP_CLIENT_BYPED_A) +GEN_SES_STATUS_DEV_SLOT_ACCESSORS(do_not_remove, DO_NOT_REMOVE) +GEN_SES_STATUS_DEV_SLOT_ACCESSORS(seslosure_byped_a, SESLOSURE_BYPED_A) +GEN_SES_STATUS_DEV_SLOT_ACCESSORS(seslosure_byped_b, SESLOSURE_BYPED_B) +GEN_SES_STATUS_DEV_SLOT_ACCESSORS(insert_ready, INSERT_READY) +GEN_SES_STATUS_DEV_SLOT_ACCESSORS(remove, REMOVE) +GEN_SES_STATUS_DEV_SLOT_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_DEV_SLOT_ACCESSORS(report, REPORT) +GEN_SES_STATUS_DEV_SLOT_ACCESSORS(app_client_byped_b, APP_CLIENT_BYPED_B) +GEN_SES_STATUS_DEV_SLOT_ACCESSORS(fault_sensed, FAULT_SENSED) +GEN_SES_STATUS_DEV_SLOT_ACCESSORS(fault_requested, FAULT_REQUESTED) +GEN_SES_STATUS_DEV_SLOT_ACCESSORS(device_off, DEVICE_OFF) +GEN_SES_STATUS_DEV_SLOT_ACCESSORS(byped_a, BYPED_A) +GEN_SES_STATUS_DEV_SLOT_ACCESSORS(byped_b, BYPED_B) +GEN_SES_STATUS_DEV_SLOT_ACCESSORS(device_byped_a, DEVICE_BYPED_A) +GEN_SES_STATUS_DEV_SLOT_ACCESSORS(device_byped_b, DEVICE_BYPED_B) +#undef GEN_SES_STATUS_DEV_SLOT_ACCESSORS + +/*---------------------- Array Device Slot Status Element --------------------*/ +struct ses_status_array_dev_slot { + struct ses_status_common common; + uint8_t bytes[3]; +}; + +enum ses_status_array_dev_slot_field_data { + SES_STATUS_ARRAY_DEV_SLOT_OK_BYTE = 0, + SES_STATUS_ARRAY_DEV_SLOT_OK_MASK = 0x80, + SES_STATUS_ARRAY_DEV_SLOT_OK_SHIFT = 7, + + SES_STATUS_ARRAY_DEV_SLOT_RSVD_DEVICE_BYTE = 0, + SES_STATUS_ARRAY_DEV_SLOT_RSVD_DEVICE_MASK = 0x40, + SES_STATUS_ARRAY_DEV_SLOT_RSVD_DEVICE_SHIFT = 6, + + SES_STATUS_ARRAY_DEV_SLOT_HOT_SPARE_BYTE = 0, + SES_STATUS_ARRAY_DEV_SLOT_HOT_SPARE_MASK = 0x20, + SES_STATUS_ARRAY_DEV_SLOT_HOT_SPARE_SHIFT = 5, + + SES_STATUS_ARRAY_DEV_SLOT_CONS_CHECK_BYTE = 0, + SES_STATUS_ARRAY_DEV_SLOT_CONS_CHECK_MASK = 0x10, + SES_STATUS_ARRAY_DEV_SLOT_CONS_CHECK_SHIFT = 4, + + SES_STATUS_ARRAY_DEV_SLOT_IN_CRIT_ARRAY_BYTE = 0, + SES_STATUS_ARRAY_DEV_SLOT_IN_CRIT_ARRAY_MASK = 0x08, + SES_STATUS_ARRAY_DEV_SLOT_IN_CRIT_ARRAY_SHIFT = 3, + + SES_STATUS_ARRAY_DEV_SLOT_IN_FAILED_ARRAY_BYTE = 0, + SES_STATUS_ARRAY_DEV_SLOT_IN_FAILED_ARRAY_MASK = 0x04, + SES_STATUS_ARRAY_DEV_SLOT_IN_FAILED_ARRAY_SHIFT = 2, + + SES_STATUS_ARRAY_DEV_SLOT_REBUILD_REMAP_BYTE = 0, + SES_STATUS_ARRAY_DEV_SLOT_REBUILD_REMAP_MASK = 0x02, + SES_STATUS_ARRAY_DEV_SLOT_REBUILD_REMAP_SHIFT = 1, + + SES_STATUS_ARRAY_DEV_SLOT_REBUILD_REMAP_ABORT_BYTE = 0, + SES_STATUS_ARRAY_DEV_SLOT_REBUILD_REMAP_ABORT_MASK = 0x01, + SES_STATUS_ARRAY_DEV_SLOT_REBUILD_REMAP_ABORT_SHIFT = 0 + + /* + * The remaining fields are identical to the device + * slot element type. Access them through the device slot + * element type and its accessors. + */ +}; +#define GEN_SES_STATUS_ARRAY_DEV_SLOT_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_array_dev_slot, SES_STATUS_ARRAY_DEV_SLOT, \ + LCASE, UCASE) +GEN_SES_STATUS_ARRAY_DEV_SLOT_ACCESSORS(ok, OK) +GEN_SES_STATUS_ARRAY_DEV_SLOT_ACCESSORS(rsvd_device, RSVD_DEVICE) +GEN_SES_STATUS_ARRAY_DEV_SLOT_ACCESSORS(hot_spare, HOT_SPARE) +GEN_SES_STATUS_ARRAY_DEV_SLOT_ACCESSORS(cons_check, CONS_CHECK) +GEN_SES_STATUS_ARRAY_DEV_SLOT_ACCESSORS(in_crit_array, IN_CRIT_ARRAY) +GEN_SES_STATUS_ARRAY_DEV_SLOT_ACCESSORS(in_failed_array, IN_FAILED_ARRAY) +GEN_SES_STATUS_ARRAY_DEV_SLOT_ACCESSORS(rebuild_remap, REBUILD_REMAP) +GEN_SES_STATUS_ARRAY_DEV_SLOT_ACCESSORS(rebuild_remap_abort, + REBUILD_REMAP_ABORT) +#undef GEN_SES_STATUS_ARRAY_DEV_SLOT_ACCESSORS + +/*----------------------- Power Supply Status Element ------------------------*/ +struct ses_status_power_supply { + struct ses_status_common common; + uint8_t bytes[3]; +}; + +enum ses_status_power_supply_field_data { + SES_STATUS_POWER_SUPPLY_IDENT_BYTE = 0, + SES_STATUS_POWER_SUPPLY_IDENT_MASK = 0x80, + SES_STATUS_POWER_SUPPLY_IDENT_SHIFT = 7, + + SES_STATUS_POWER_SUPPLY_DC_OVER_VOLTAGE_BYTE = 1, + SES_STATUS_POWER_SUPPLY_DC_OVER_VOLTAGE_MASK = 0x08, + SES_STATUS_POWER_SUPPLY_DC_OVER_VOLTAGE_SHIFT = 3, + + SES_STATUS_POWER_SUPPLY_DC_UNDER_VOLTAGE_BYTE = 1, + SES_STATUS_POWER_SUPPLY_DC_UNDER_VOLTAGE_MASK = 0x04, + SES_STATUS_POWER_SUPPLY_DC_UNDER_VOLTAGE_SHIFT = 2, + + SES_STATUS_POWER_SUPPLY_DC_OVER_CURRENT_BYTE = 1, + SES_STATUS_POWER_SUPPLY_DC_OVER_CURRENT_MASK = 0x02, + SES_STATUS_POWER_SUPPLY_DC_OVER_CURRENT_SHIFT = 1, + + SES_STATUS_POWER_SUPPLY_HOT_SWAP_BYTE = 2, + SES_STATUS_POWER_SUPPLY_HOT_SWAP_MASK = 0x80, + SES_STATUS_POWER_SUPPLY_HOT_SWAP_SHIFT = 7, + + SES_STATUS_POWER_SUPPLY_FAIL_BYTE = 2, + SES_STATUS_POWER_SUPPLY_FAIL_MASK = 0x40, + SES_STATUS_POWER_SUPPLY_FAIL_SHIFT = 6, + + SES_STATUS_POWER_SUPPLY_REQUESTED_ON_BYTE = 2, + SES_STATUS_POWER_SUPPLY_REQUESTED_ON_MASK = 0x20, + SES_STATUS_POWER_SUPPLY_REQUESTED_ON_SHIFT = 5, + + SES_STATUS_POWER_SUPPLY_OFF_BYTE = 2, + SES_STATUS_POWER_SUPPLY_OFF_MASK = 0x10, + SES_STATUS_POWER_SUPPLY_OFF_SHIFT = 4, + + SES_STATUS_POWER_SUPPLY_OVERTMP_FAIL_BYTE = 2, + SES_STATUS_POWER_SUPPLY_OVERTMP_FAIL_MASK = 0x08, + SES_STATUS_POWER_SUPPLY_OVERTMP_FAIL_SHIFT = 3, + + SES_STATUS_POWER_SUPPLY_TEMP_WARN_BYTE = 2, + SES_STATUS_POWER_SUPPLY_TEMP_WARN_MASK = 0x04, + SES_STATUS_POWER_SUPPLY_TEMP_WARN_SHIFT = 2, + + SES_STATUS_POWER_SUPPLY_AC_FAIL_BYTE = 2, + SES_STATUS_POWER_SUPPLY_AC_FAIL_MASK = 0x02, + SES_STATUS_POWER_SUPPLY_AC_FAIL_SHIFT = 1, + + SES_STATUS_POWER_SUPPLY_DC_FAIL_BYTE = 2, + SES_STATUS_POWER_SUPPLY_DC_FAIL_MASK = 0x01, + SES_STATUS_POWER_SUPPLY_DC_FAIL_SHIFT = 0 +}; + +#define GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_power_supply, SES_STATUS_POWER_SUPPLY, LCASE, UCASE) +GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(dc_over_voltage, DC_OVER_VOLTAGE) +GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(dc_under_voltage, DC_UNDER_VOLTAGE) +GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(dc_over_current, DC_OVER_CURRENT) +GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(hot_swap, HOT_SWAP) +GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(fail, FAIL) +GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(requested_on, REQUESTED_ON) +GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(off, OFF) +GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(overtmp_fail, OVERTMP_FAIL) +GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(temp_warn, TEMP_WARN) +GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(ac_fail, AC_FAIL) +GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(dc_fail, DC_FAIL) +#undef GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS + +/*-------------------------- Cooling Status Element --------------------------*/ +struct ses_status_cooling { + struct ses_status_common common; + uint8_t bytes[3]; +}; + +enum ses_status_cooling_field_data { + SES_STATUS_COOLING_IDENT_BYTE = 0, + SES_STATUS_COOLING_IDENT_MASK = 0x80, + SES_STATUS_COOLING_IDENT_SHIFT = 7, + + SES_STATUS_COOLING_ACTUAL_FAN_SPEED_MSB_BYTE = 0, + SES_STATUS_COOLING_ACTUAL_FAN_SPEED_MSB_MASK = 0x07, + SES_STATUS_COOLING_ACTUAL_FAN_SPEED_MSB_SHIFT = 0, + + SES_STATUS_COOLING_ACTUAL_FAN_SPEED_LSB_BYTE = 1, + SES_STATUS_COOLING_ACTUAL_FAN_SPEED_LSB_MASK = 0xFF, + SES_STATUS_COOLING_ACTUAL_FAN_SPEED_LSB_SHIFT = 0, + + SES_STATUS_COOLING_HOT_SWAP_BYTE = 2, + SES_STATUS_COOLING_HOT_SWAP_MASK = 0x40, + SES_STATUS_COOLING_HOT_SWAP_SHIFT = 6, + + SES_STATUS_COOLING_FAIL_BYTE = 2, + SES_STATUS_COOLING_FAIL_MASK = 0x40, + SES_STATUS_COOLING_FAIL_SHIFT = 6, + + SES_STATUS_COOLING_REQUESTED_ON_BYTE = 2, + SES_STATUS_COOLING_REQUESTED_ON_MASK = 0x20, + SES_STATUS_COOLING_REQUESTED_ON_SHIFT = 5, + + SES_STATUS_COOLING_OFF_BYTE = 2, + SES_STATUS_COOLING_OFF_MASK = 0x20, + SES_STATUS_COOLING_OFF_SHIFT = 5, + + SES_STATUS_COOLING_ACTUAL_SPEED_CODE_BYTE = 2, + SES_STATUS_COOLING_ACTUAL_SPEED_CODE_MASK = 0x07, + SES_STATUS_COOLING_ACTUAL_SPEED_CODE_SHIFT = 2, + SES_STATUS_COOLING_ACTUAL_SPEED_CODE_STOPPED = 0x00, + SES_STATUS_COOLING_ACTUAL_SPEED_CODE_LOWEST = 0x01, + SES_STATUS_COOLING_ACTUAL_SPEED_CODE_HIGHEST = 0x07 +}; + +#define GEN_SES_STATUS_COOLING_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_cooling, SES_STATUS_COOLING, LCASE, UCASE) +GEN_SES_STATUS_COOLING_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_COOLING_ACCESSORS(actual_fan_speed_msb, ACTUAL_FAN_SPEED_MSB) +GEN_SES_STATUS_COOLING_ACCESSORS(actual_fan_speed_lsb, ACTUAL_FAN_SPEED_LSB) +GEN_SES_STATUS_COOLING_ACCESSORS(hot_swap, HOT_SWAP) +GEN_SES_STATUS_COOLING_ACCESSORS(fail, FAIL) +GEN_SES_STATUS_COOLING_ACCESSORS(requested_on, REQUESTED_ON) +GEN_SES_STATUS_COOLING_ACCESSORS(off, OFF) +GEN_SES_STATUS_COOLING_ACCESSORS(actual_speed_code, ACTUAL_SPEED_CODE) +#undef GEN_SES_STATUS_COOLING_ACCESSORS + +static inline int +ses_status_cooling_get_actual_fan_speed(struct ses_status_cooling *elem) +{ + return (ses_status_cooling_get_actual_fan_speed_msb(elem) << 8 + | ses_status_cooling_get_actual_fan_speed_lsb(elem)); +} + +/*-------------------- Temperature Sensor Status Element ---------------------*/ +struct ses_status_temp_sensor { + struct ses_status_common common; + uint8_t bytes[3]; +}; + +enum ses_status_temp_sensor_field_data { + SES_STATUS_TEMP_SENSOR_IDENT_BYTE = 0, + SES_STATUS_TEMP_SENSOR_IDENT_MASK = 0x80, + SES_STATUS_TEMP_SENSOR_IDENT_SHIFT = 7, + + SES_STATUS_TEMP_SENSOR_FAIL_BYTE = 0, + SES_STATUS_TEMP_SENSOR_FAIL_MASK = 0x40, + SES_STATUS_TEMP_SENSOR_FAIL_SHIFT = 6, + + SES_STATUS_TEMP_SENSOR_TEMPERATURE_BYTE = 1, + SES_STATUS_TEMP_SENSOR_TEMPERATURE_MASK = 0xFF, + SES_STATUS_TEMP_SENSOR_TEMPERATURE_SHIFT = 0, + + SES_STATUS_TEMP_SENSOR_OT_FAILURE_BYTE = 2, + SES_STATUS_TEMP_SENSOR_OT_FAILURE_MASK = 0x08, + SES_STATUS_TEMP_SENSOR_OT_FAILURE_SHIFT = 3, + + SES_STATUS_TEMP_SENSOR_OT_WARNING_BYTE = 2, + SES_STATUS_TEMP_SENSOR_OT_WARNING_MASK = 0x04, + SES_STATUS_TEMP_SENSOR_OT_WARNING_SHIFT = 2, + + SES_STATUS_TEMP_SENSOR_UT_FAILURE_BYTE = 2, + SES_STATUS_TEMP_SENSOR_UT_FAILURE_MASK = 0x02, + SES_STATUS_TEMP_SENSOR_UT_FAILURE_SHIFT = 1, + + SES_STATUS_TEMP_SENSOR_UT_WARNING_BYTE = 2, + SES_STATUS_TEMP_SENSOR_UT_WARNING_MASK = 0x01, + SES_STATUS_TEMP_SENSOR_UT_WARNING_SHIFT = 0 +}; + +#define GEN_SES_STATUS_TEMP_SENSOR_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_temp_sensor, SES_STATUS_TEMP_SENSOR, LCASE, UCASE) +GEN_SES_STATUS_TEMP_SENSOR_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_TEMP_SENSOR_ACCESSORS(fail, FAIL) +GEN_SES_STATUS_TEMP_SENSOR_ACCESSORS(temperature, TEMPERATURE) +GEN_SES_STATUS_TEMP_SENSOR_ACCESSORS(ot_failure, OT_FAILURE) +GEN_SES_STATUS_TEMP_SENSOR_ACCESSORS(ot_warning, OT_WARNING) +GEN_SES_STATUS_TEMP_SENSOR_ACCESSORS(ut_failure, UT_FAILURE) +GEN_SES_STATUS_TEMP_SENSOR_ACCESSORS(ut_warning, UT_WARNING) +#undef GEN_SES_STATUS_TEMP_SENSOR_ACCESSORS + +/*------------------------- Door Lock Status Element -------------------------*/ +struct ses_status_door_lock { + struct ses_status_common common; + uint8_t bytes[3]; +}; + +enum ses_status_door_lock_field_data { + SES_STATUS_DOOR_LOCK_IDENT_BYTE = 0, + SES_STATUS_DOOR_LOCK_IDENT_MASK = 0x80, + SES_STATUS_DOOR_LOCK_IDENT_SHIFT = 7, + + SES_STATUS_DOOR_LOCK_FAIL_BYTE = 0, + SES_STATUS_DOOR_LOCK_FAIL_MASK = 0x40, + SES_STATUS_DOOR_LOCK_FAIL_SHIFT = 6, + + SES_STATUS_DOOR_LOCK_UNLOCKED_BYTE = 2, + SES_STATUS_DOOR_LOCK_UNLOCKED_MASK = 0x01, + SES_STATUS_DOOR_LOCK_UNLOCKED_SHIFT = 0 +}; + +#define GEN_SES_STATUS_DOOR_LOCK_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_door_lock, SES_STATUS_DOOR_LOCK, LCASE, UCASE) +GEN_SES_STATUS_DOOR_LOCK_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_DOOR_LOCK_ACCESSORS(fail, FAIL) +GEN_SES_STATUS_DOOR_LOCK_ACCESSORS(unlocked, UNLOCKED) +#undef GEN_SES_STATUS_DOOR_LOCK_ACCESSORS + +/*----------------------- Audible Alarm Status Element -----------------------*/ +struct ses_status_audible_alarm { + struct ses_status_common common; + uint8_t bytes[3]; +}; + +enum ses_status_audible_alarm_field_data { + SES_STATUS_AUDIBLE_ALARM_IDENT_BYTE = 0, + SES_STATUS_AUDIBLE_ALARM_IDENT_MASK = 0x80, + SES_STATUS_AUDIBLE_ALARM_IDENT_SHIFT = 7, + + SES_STATUS_AUDIBLE_ALARM_FAIL_BYTE = 0, + SES_STATUS_AUDIBLE_ALARM_FAIL_MASK = 0x40, + SES_STATUS_AUDIBLE_ALARM_FAIL_SHIFT = 6, + + SES_STATUS_AUDIBLE_ALARM_RQST_MUTE_BYTE = 2, + SES_STATUS_AUDIBLE_ALARM_RQST_MUTE_MASK = 0x80, + SES_STATUS_AUDIBLE_ALARM_RQST_MUTE_SHIFT = 7, + + SES_STATUS_AUDIBLE_ALARM_MUTED_BYTE = 2, + SES_STATUS_AUDIBLE_ALARM_MUTED_MASK = 0x40, + SES_STATUS_AUDIBLE_ALARM_MUTED_SHIFT = 6, + + SES_STATUS_AUDIBLE_ALARM_REMIND_BYTE = 2, + SES_STATUS_AUDIBLE_ALARM_REMIND_MASK = 0x10, + SES_STATUS_AUDIBLE_ALARM_REMIND_SHIFT = 4, + + SES_STATUS_AUDIBLE_ALARM_TONE_INDICATOR_BYTE = 2, + SES_STATUS_AUDIBLE_ALARM_TONE_INDICATOR_MASK = 0x0F, + SES_STATUS_AUDIBLE_ALARM_TONE_INDICATOR_SHIFT = 0, + SES_STATUS_AUDIBLE_ALARM_TONE_INDICATOR_INFO = 0x08, + SES_STATUS_AUDIBLE_ALARM_TONE_INDICATOR_NON_CRIT = 0x04, + SES_STATUS_AUDIBLE_ALARM_TONE_INDICATOR_CRIT = 0x02, + SES_STATUS_AUDIBLE_ALARM_TONE_INDICATOR_UNRECOV = 0x01 +}; + +#define GEN_SES_STATUS_AUDIBLE_ALARM_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_audible_alarm, SES_STATUS_AUDIBLE_ALARM, LCASE, UCASE) +GEN_SES_STATUS_AUDIBLE_ALARM_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_AUDIBLE_ALARM_ACCESSORS(fail, FAIL) +GEN_SES_STATUS_AUDIBLE_ALARM_ACCESSORS(rqst_mute, RQST_MUTE) +GEN_SES_STATUS_AUDIBLE_ALARM_ACCESSORS(muted, MUTED) +GEN_SES_STATUS_AUDIBLE_ALARM_ACCESSORS(remind, REMIND) +GEN_SES_STATUS_AUDIBLE_ALARM_ACCESSORS(tone_indicator, TONE_INDICATOR) +#undef GEN_SES_STATUS_AUDIBLE_ALARM_ACCESSORS + +/*---------- Enclosure Services Statusler Electronics Status Element ---------*/ +struct ses_status_ecc_electronics { + struct ses_status_common common; + uint8_t bytes[3]; +}; + +enum ses_status_ecc_electronics_field_data { + SES_STATUS_ECC_ELECTRONICS_IDENT_BYTE = 0, + SES_STATUS_ECC_ELECTRONICS_IDENT_MASK = 0x80, + SES_STATUS_ECC_ELECTRONICS_IDENT_SHIFT = 7, + + SES_STATUS_ECC_ELECTRONICS_FAIL_BYTE = 0, + SES_STATUS_ECC_ELECTRONICS_FAIL_MASK = 0x40, + SES_STATUS_ECC_ELECTRONICS_FAIL_SHIFT = 6, + + SES_STATUS_ECC_ELECTRONICS_REPORT_BYTE = 1, + SES_STATUS_ECC_ELECTRONICS_REPORT_MASK = 0x01, + SES_STATUS_ECC_ELECTRONICS_REPORT_SHIFT = 0, + + SES_STATUS_ECC_ELECTRONICS_HOT_SWAP_BYTE = 2, + SES_STATUS_ECC_ELECTRONICS_HOT_SWAP_MASK = 0x80, + SES_STATUS_ECC_ELECTRONICS_HOT_SWAP_SHIFT = 7 +}; + +#define GEN_SES_STATUS_ECC_ELECTRONICS_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_ecc_electronics, SES_STATUS_ECC_ELECTRONICS, \ + LCASE, UCASE) +GEN_SES_STATUS_ECC_ELECTRONICS_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_ECC_ELECTRONICS_ACCESSORS(fail, FAIL) +GEN_SES_STATUS_ECC_ELECTRONICS_ACCESSORS(report, REPORT) +GEN_SES_STATUS_ECC_ELECTRONICS_ACCESSORS(hot_swap, HOT_SWAP) +#undef GEN_SES_STATUS_ECC_ELECTRONICS_ACCESSORS + +/*------------ SCSI Services Statusler Electronics Status Element ------------*/ +struct ses_status_scc_electronics { + struct ses_status_common common; + uint8_t bytes[3]; +}; + +enum ses_status_scc_electronics_field_data { + SES_STATUS_SCC_ELECTRONICS_IDENT_BYTE = 0, + SES_STATUS_SCC_ELECTRONICS_IDENT_MASK = 0x80, + SES_STATUS_SCC_ELECTRONICS_IDENT_SHIFT = 7, + + SES_STATUS_SCC_ELECTRONICS_FAIL_BYTE = 0, + SES_STATUS_SCC_ELECTRONICS_FAIL_MASK = 0x40, + SES_STATUS_SCC_ELECTRONICS_FAIL_SHIFT = 6, + + SES_STATUS_SCC_ELECTRONICS_REPORT_BYTE = 1, + SES_STATUS_SCC_ELECTRONICS_REPORT_MASK = 0x01, + SES_STATUS_SCC_ELECTRONICS_REPORT_SHIFT = 0 +}; + +#define GEN_SES_STATUS_SCC_ELECTRONICS_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_scc_electronics, SES_STATUS_SCC_ELECTRONICS, \ + LCASE, UCASE) +GEN_SES_STATUS_SCC_ELECTRONICS_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_SCC_ELECTRONICS_ACCESSORS(fail, FAIL) +GEN_SES_STATUS_SCC_ELECTRONICS_ACCESSORS(report, REPORT) +#undef GEN_SES_STATUS_SCC_ELECTRONICS_ACCESSORS + +/*--------------------- Nonvolatile Cache Status Element ---------------------*/ +struct ses_status_nv_cache { + struct ses_status_common common; + uint8_t bytes[1]; + uint8_t cache_size[2]; +}; + +enum ses_status_nv_cache_field_data { + SES_STATUS_NV_CACHE_IDENT_BYTE = 0, + SES_STATUS_NV_CACHE_IDENT_MASK = 0x80, + SES_STATUS_NV_CACHE_IDENT_SHIFT = 7, + + SES_STATUS_NV_CACHE_FAIL_BYTE = 0, + SES_STATUS_NV_CACHE_FAIL_MASK = 0x40, + SES_STATUS_NV_CACHE_FAIL_SHIFT = 6, + + SES_STATUS_NV_CACHE_SIZE_MULTIPLIER_BYTE = 0, + SES_STATUS_NV_CACHE_SIZE_MULTIPLIER_MASK = 0x03, + SES_STATUS_NV_CACHE_SIZE_MULTIPLIER_SHIFT = 0, + SES_STATUS_NV_CACHE_SIZE_MULTIPLIER_BYTES = 0x0, + SES_STATUS_NV_CACHE_SIZE_MULTIPLIER_KBYTES = 0x1, + SES_STATUS_NV_CACHE_SIZE_MULTIPLIER_MBYTES = 0x2, + SES_STATUS_NV_CACHE_SIZE_MULTIPLIER_GBYTES = 0x3 +}; + +#define GEN_SES_STATUS_NV_CACHE_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_nv_cache, SES_STATUS_NV_CACHE, LCASE, UCASE) +GEN_SES_STATUS_NV_CACHE_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_NV_CACHE_ACCESSORS(fail, FAIL) +GEN_SES_STATUS_NV_CACHE_ACCESSORS(size_multiplier, SIZE_MULTIPLIER) +#undef GEN_SES_STATUS_NV_CACHE_ACCESSORS + +static inline uintmax_t +ses_status_nv_cache_get_cache_size(struct ses_status_nv_cache *elem) +{ + uintmax_t cache_size; + int multiplier; + + /* Multiplier is in units of 2^10 */ + cache_size = scsi_2btoul(elem->cache_size); + multiplier = 10 * ses_status_nv_cache_get_size_multiplier(elem); + return (cache_size << multiplier); +} + +/*----------------- Invalid Operation Reason Status Element ------------------*/ +struct ses_status_invalid_op_reason { + struct ses_status_common common; + uint8_t bytes[3]; +}; + +enum ses_status_invalid_op_field_data { + SES_STATUS_INVALID_OP_REASON_TYPE_BYTE = 0, + SES_STATUS_INVALID_OP_REASON_TYPE_MASK = 0xC0, + SES_STATUS_INVALID_OP_REASON_TYPE_SHIFT = 6, + SES_STATUS_INVALID_OP_REASON_TYPE_PC_ERROR = 0x00, + SES_STATUS_INVALID_OP_REASON_TYPE_PF_ERROR = 0x01, + SES_STATUS_INVALID_OP_REASON_TYPE_VS_ERROR = 0x03, + + SES_STATUS_INVALID_OP_REASON_PC_ERROR_PC_NOT_SUPPORTED_BYTE = 0, + SES_STATUS_INVALID_OP_REASON_PC_ERROR_PC_NOT_SUPPORTED_MASK = 0x01, + SES_STATUS_INVALID_OP_REASON_PC_ERROR_PC_NOT_SUPPORTED_SHIFT = 0, + + SES_STATUS_INVALID_OP_REASON_PF_ERROR_BIT_NUMBER_BYTE = 0, + SES_STATUS_INVALID_OP_REASON_PF_ERROR_BIT_NUMBER_MASK = 0x03, + SES_STATUS_INVALID_OP_REASON_PF_ERROR_BIT_NUMBER_SHIFT = 0 +}; + +#define GEN_SES_STATUS_INVALID_OP_REASON_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_invalid_op_reason, SES_STATUS_INVALID_OP_REASON, \ + LCASE, UCASE) +GEN_SES_STATUS_INVALID_OP_REASON_ACCESSORS(type, TYPE) +GEN_SES_STATUS_INVALID_OP_REASON_ACCESSORS(pc_error_pc_not_supported, + PC_ERROR_PC_NOT_SUPPORTED) +GEN_SES_STATUS_INVALID_OP_REASON_ACCESSORS(pf_error_bit_number, + PF_ERROR_BIT_NUMBER) +#undef GEN_SES_STATUS_INVALID_OP_ACCESSORS + +/*--------------- Uninterruptible Power Supply Status Element ----------------*/ +struct ses_status_ups { + struct ses_status_common common; + /* Minutes of remaining capacity. */ + uint8_t battery_status; + uint8_t bytes[2]; +}; + +enum ses_status_ups_field_data { + SES_STATUS_UPS_AC_LO_BYTE = 0, + SES_STATUS_UPS_AC_LO_MASK = 0x80, + SES_STATUS_UPS_AC_LO_SHIFT = 7, + + SES_STATUS_UPS_AC_HI_BYTE = 0, + SES_STATUS_UPS_AC_HI_MASK = 0x40, + SES_STATUS_UPS_AC_HI_SHIFT = 6, + + SES_STATUS_UPS_AC_QUAL_BYTE = 0, + SES_STATUS_UPS_AC_QUAL_MASK = 0x20, + SES_STATUS_UPS_AC_QUAL_SHIFT = 5, + + SES_STATUS_UPS_AC_FAIL_BYTE = 0, + SES_STATUS_UPS_AC_FAIL_MASK = 0x10, + SES_STATUS_UPS_AC_FAIL_SHIFT = 4, + + SES_STATUS_UPS_DC_FAIL_BYTE = 0, + SES_STATUS_UPS_DC_FAIL_MASK = 0x08, + SES_STATUS_UPS_DC_FAIL_SHIFT = 3, + + SES_STATUS_UPS_UPS_FAIL_BYTE = 0, + SES_STATUS_UPS_UPS_FAIL_MASK = 0x04, + SES_STATUS_UPS_UPS_FAIL_SHIFT = 2, + + SES_STATUS_UPS_WARN_BYTE = 0, + SES_STATUS_UPS_WARN_MASK = 0x02, + SES_STATUS_UPS_WARN_SHIFT = 1, + + SES_STATUS_UPS_INTF_FAIL_BYTE = 0, + SES_STATUS_UPS_INTF_FAIL_MASK = 0x01, + SES_STATUS_UPS_INTF_FAIL_SHIFT = 0, + + SES_STATUS_UPS_IDENT_BYTE = 0, + SES_STATUS_UPS_IDENT_MASK = 0x80, + SES_STATUS_UPS_IDENT_SHIFT = 7, + + SES_STATUS_UPS_FAIL_BYTE = 1, + SES_STATUS_UPS_FAIL_MASK = 0x40, + SES_STATUS_UPS_FAIL_SHIFT = 6, + + SES_STATUS_UPS_BATT_FAIL_BYTE = 1, + SES_STATUS_UPS_BATT_FAIL_MASK = 0x02, + SES_STATUS_UPS_BATT_FAIL_SHIFT = 1, + + SES_STATUS_UPS_BPF_BYTE = 1, + SES_STATUS_UPS_BPF_MASK = 0x01, + SES_STATUS_UPS_BPF_SHIFT = 0 +}; + +#define GEN_SES_STATUS_UPS_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_ups, SES_STATUS_UPS, LCASE, UCASE) +GEN_SES_STATUS_UPS_ACCESSORS(ac_lo, AC_LO) +GEN_SES_STATUS_UPS_ACCESSORS(ac_hi, AC_HI) +GEN_SES_STATUS_UPS_ACCESSORS(ac_qual, AC_QUAL) +GEN_SES_STATUS_UPS_ACCESSORS(ac_fail, AC_FAIL) +GEN_SES_STATUS_UPS_ACCESSORS(dc_fail, DC_FAIL) +GEN_SES_STATUS_UPS_ACCESSORS(ups_fail, UPS_FAIL) +GEN_SES_STATUS_UPS_ACCESSORS(warn, WARN) +GEN_SES_STATUS_UPS_ACCESSORS(intf_fail, INTF_FAIL) +GEN_SES_STATUS_UPS_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_UPS_ACCESSORS(fail, FAIL) +GEN_SES_STATUS_UPS_ACCESSORS(batt_fail, BATT_FAIL) +GEN_SES_STATUS_UPS_ACCESSORS(bpf, BPF) +#undef GEN_SES_STATUS_UPS_ACCESSORS + +/*-------------------------- Display Status Element --------------------------*/ +struct ses_status_display { + struct ses_status_common common; + uint8_t bytes[1]; + uint8_t display_character[2]; +}; + +enum ses_status_display_field_data { + SES_STATUS_DISPLAY_IDENT_BYTE = 0, + SES_STATUS_DISPLAY_IDENT_MASK = 0x80, + SES_STATUS_DISPLAY_IDENT_SHIFT = 7, + + SES_STATUS_DISPLAY_FAIL_BYTE = 0, + SES_STATUS_DISPLAY_FAIL_MASK = 0x40, + SES_STATUS_DISPLAY_FAIL_SHIFT = 6, + + SES_STATUS_DISPLAY_DISPLAY_MODE_BYTE = 0, + SES_STATUS_DISPLAY_DISPLAY_MODE_MASK = 0x03, + SES_STATUS_DISPLAY_DISPLAY_MODE_SHIFT = 6, + SES_STATUS_DISPLAY_DISPLAY_MODE_DC_FIELD_UNSUPP = 0x0, + SES_STATUS_DISPLAY_DISPLAY_MODE_DC_FIELD_SUPP = 0x1, + SES_STATUS_DISPLAY_DISPLAY_MODE_DC_FIELD = 0x2 +}; + +#define GEN_SES_STATUS_DISPLAY_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_display, SES_STATUS_DISPLAY, LCASE, UCASE) +GEN_SES_STATUS_DISPLAY_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_DISPLAY_ACCESSORS(fail, FAIL) +GEN_SES_STATUS_DISPLAY_ACCESSORS(display_mode, DISPLAY_MODE) +#undef GEN_SES_STATUS_DISPLAY_ACCESSORS + +/*----------------------- Key Pad Entry Status Element -----------------------*/ +struct ses_status_key_pad_entry { + struct ses_status_common common; + uint8_t bytes[3]; +}; + +enum ses_status_key_pad_entry_field_data { + SES_STATUS_KEY_PAD_ENTRY_IDENT_BYTE = 0, + SES_STATUS_KEY_PAD_ENTRY_IDENT_MASK = 0x80, + SES_STATUS_KEY_PAD_ENTRY_IDENT_SHIFT = 7, + + SES_STATUS_KEY_PAD_ENTRY_FAIL_BYTE = 0, + SES_STATUS_KEY_PAD_ENTRY_FAIL_MASK = 0x40, + SES_STATUS_KEY_PAD_ENTRY_FAIL_SHIFT = 6 +}; + +#define GEN_SES_STATUS_KEY_PAD_ENTRY_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_key_pad_entry, SES_STATUS_KEY_PAD_ENTRY, LCASE, UCASE) +GEN_SES_STATUS_KEY_PAD_ENTRY_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_KEY_PAD_ENTRY_ACCESSORS(fail, FAIL) +#undef GEN_SES_STATUS_KEY_PAD_ENTRY_ACCESSORS + +/*------------------------- Enclosure Status Element -------------------------*/ +struct ses_status_seslosure { + struct ses_status_common common; + uint8_t bytes[3]; +}; + +enum ses_status_seslosure_field_data { + SES_STATUS_SESLOSURE_IDENT_BYTE = 0, + SES_STATUS_SESLOSURE_IDENT_MASK = 0x80, + SES_STATUS_SESLOSURE_IDENT_SHIFT = 7, + + SES_STATUS_SESLOSURE_TIME_UNTIL_POWER_CYCLE_BYTE = 1, + SES_STATUS_SESLOSURE_TIME_UNTIL_POWER_CYCLE_MASK = 0xFC, + SES_STATUS_SESLOSURE_TIME_UNTIL_POWER_CYCLE_SHIFT = 2, + + SES_STATUS_SESLOSURE_FAIL_BYTE = 1, + SES_STATUS_SESLOSURE_FAIL_MASK = 0x02, + SES_STATUS_SESLOSURE_FAIL_SHIFT = 1, + + SES_STATUS_SESLOSURE_WARN_BYTE = 1, + SES_STATUS_SESLOSURE_WARN_MASK = 0x01, + SES_STATUS_SESLOSURE_WARN_SHIFT = 0, + + SES_STATUS_SESLOSURE_REQUESTED_POWER_OFF_DURATION_BYTE = 2, + SES_STATUS_SESLOSURE_REQUESTED_POWER_OFF_DURATION_MASK = 0xFC, + SES_STATUS_SESLOSURE_REQUESTED_POWER_OFF_DURATION_SHIFT = 2, + SES_STATUS_SESLOSURE_REQUESTED_POWER_OFF_DURATION_MAX_AUTO = 60, + SES_STATUS_SESLOSURE_REQUESTED_POWER_OFF_DURATION_MANUAL = 63, + + SES_STATUS_SESLOSURE_REQUESTED_FAIL_BYTE = 2, + SES_STATUS_SESLOSURE_REQUESTED_FAIL_MASK = 0x02, + SES_STATUS_SESLOSURE_REQUESTED_FAIL_SHIFT = 1, + + SES_STATUS_SESLOSURE_REQUESTED_WARN_BYTE = 2, + SES_STATUS_SESLOSURE_REQUESTED_WARN_MASK = 0x01, + SES_STATUS_SESLOSURE_REQUESTED_WARN_SHIFT = 0 +}; + +#define GEN_SES_STATUS_SESLOSURE_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_seslosure, SES_STATUS_SESLOSURE, LCASE, UCASE) +GEN_SES_STATUS_SESLOSURE_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_SESLOSURE_ACCESSORS(time_until_power_cycle, + TIME_UNTIL_POWER_CYCLE) +GEN_SES_STATUS_SESLOSURE_ACCESSORS(fail, FAIL) +GEN_SES_STATUS_SESLOSURE_ACCESSORS(warn, WARN) +GEN_SES_STATUS_SESLOSURE_ACCESSORS(requested_power_off_duration, + REQUESTED_POWER_OFF_DURATION) +GEN_SES_STATUS_SESLOSURE_ACCESSORS(requested_fail, REQUESTED_FAIL) +GEN_SES_STATUS_SESLOSURE_ACCESSORS(requested_warn, REQUESTED_WARN) +#undef GEN_SES_STATUS_SESLOSURE_ACCESSORS + +/*------------------- SCSI Port/Transceiver Status Element -------------------*/ +struct ses_status_scsi_port_or_xcvr { + struct ses_status_common common; + uint8_t bytes[3]; +}; + +enum ses_status_scsi_port_or_xcvr_field_data { + SES_STATUS_SCSI_PORT_OR_XCVR_IDENT_BYTE = 0, + SES_STATUS_SCSI_PORT_OR_XCVR_IDENT_MASK = 0x80, + SES_STATUS_SCSI_PORT_OR_XCVR_IDENT_SHIFT = 7, + + SES_STATUS_SCSI_PORT_OR_XCVR_FAIL_BYTE = 0, + SES_STATUS_SCSI_PORT_OR_XCVR_FAIL_MASK = 0x40, + SES_STATUS_SCSI_PORT_OR_XCVR_FAIL_SHIFT = 6, + + SES_STATUS_SCSI_PORT_OR_XCVR_REPORT_BYTE = 1, + SES_STATUS_SCSI_PORT_OR_XCVR_REPORT_MASK = 0x01, + SES_STATUS_SCSI_PORT_OR_XCVR_REPORT_SHIFT = 0, + + SES_STATUS_SCSI_PORT_OR_XCVR_DISABLED_BYTE = 2, + SES_STATUS_SCSI_PORT_OR_XCVR_DISABLED_MASK = 0x10, + SES_STATUS_SCSI_PORT_OR_XCVR_DISABLED_SHIFT = 4, + + SES_STATUS_SCSI_PORT_OR_XCVR_LOL_BYTE = 2, + SES_STATUS_SCSI_PORT_OR_XCVR_LOL_MASK = 0x02, + SES_STATUS_SCSI_PORT_OR_XCVR_LOL_SHIFT = 1, + + SES_STATUS_SCSI_PORT_OR_XCVR_XMIT_FAIL_BYTE = 2, + SES_STATUS_SCSI_PORT_OR_XCVR_XMIT_FAIL_MASK = 0x01, + SES_STATUS_SCSI_PORT_OR_XCVR_XMIT_FAIL_SHIFT = 0 +}; + +#define GEN_SES_STATUS_SCSI_PORT_OR_XCVR_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_scsi_port_or_xcvr, SES_STATUS_SCSI_PORT_OR_XCVR,\ + LCASE, UCASE) +GEN_SES_STATUS_SCSI_PORT_OR_XCVR_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_SCSI_PORT_OR_XCVR_ACCESSORS(fail, FAIL) +GEN_SES_STATUS_SCSI_PORT_OR_XCVR_ACCESSORS(report, REPORT) +GEN_SES_STATUS_SCSI_PORT_OR_XCVR_ACCESSORS(disable, DISABLED) +GEN_SES_STATUS_SCSI_PORT_OR_XCVR_ACCESSORS(lol, LOL) +GEN_SES_STATUS_SCSI_PORT_OR_XCVR_ACCESSORS(xmit_fail, XMIT_FAIL) +#undef GEN_SES_STATUS_SCSI_PORT_OR_XCVR_ACCESSORS + +/*------------------------- Language Status Element --------------------------*/ +struct ses_status_language { + struct ses_status_common common; + uint8_t bytes[1]; + uint8_t language_code[2]; +}; + +enum ses_status_language_field_data { + SES_STATUS_LANGUAGE_IDENT_BYTE = 0, + SES_STATUS_LANGUAGE_IDENT_MASK = 0x80, + SES_STATUS_LANGUAGE_IDENT_SHIFT = 7 +}; + +#define GEN_SES_STATUS_LANGUAGE_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_language, SES_STATUS_LANGUAGE, LCASE, UCASE) +GEN_SES_STATUS_LANGUAGE_ACCESSORS(ident, IDENT) +#undef GEN_SES_STATUS_LANGUAGE_ACCESSORS + +/*-------------------- Communication Port Status Element ---------------------*/ +struct ses_status_comm_port { + struct ses_status_common common; + uint8_t bytes[3]; +}; + +enum ses_status_comm_port_field_data { + SES_STATUS_COMM_PORT_IDENT_BYTE = 0, + SES_STATUS_COMM_PORT_IDENT_MASK = 0x80, + SES_STATUS_COMM_PORT_IDENT_SHIFT = 7, + + SES_STATUS_COMM_PORT_FAIL_BYTE = 0, + SES_STATUS_COMM_PORT_FAIL_MASK = 0x40, + SES_STATUS_COMM_PORT_FAIL_SHIFT = 6, + + SES_STATUS_COMM_PORT_DISABLED_BYTE = 2, + SES_STATUS_COMM_PORT_DISABLED_MASK = 0x01, + SES_STATUS_COMM_PORT_DISABLED_SHIFT = 0 +}; + +#define GEN_SES_STATUS_COMM_PORT_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_comm_port, SES_STATUS_COMM_PORT, LCASE, UCASE) +GEN_SES_STATUS_COMM_PORT_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_COMM_PORT_ACCESSORS(fail, FAIL) +GEN_SES_STATUS_COMM_PORT_ACCESSORS(disabled, DISABLED) +#undef GEN_SES_STATUS_COMM_PORT_ACCESSORS + +/*---------------------- Voltage Sensor Status Element -----------------------*/ +struct ses_status_voltage_sensor { + struct ses_status_common common; + uint8_t bytes[1]; + uint8_t voltage[2]; +}; + +enum ses_status_voltage_sensor_field_data { + SES_STATUS_VOLTAGE_SENSOR_IDENT_BYTE = 0, + SES_STATUS_VOLTAGE_SENSOR_IDENT_MASK = 0x80, + SES_STATUS_VOLTAGE_SENSOR_IDENT_SHIFT = 7, + + SES_STATUS_VOLTAGE_SENSOR_FAIL_BYTE = 0, + SES_STATUS_VOLTAGE_SENSOR_FAIL_MASK = 0x40, + SES_STATUS_VOLTAGE_SENSOR_FAIL_SHIFT = 6, + + SES_STATUS_VOLTAGE_SENSOR_WARN_OVER_BYTE = 0, + SES_STATUS_VOLTAGE_SENSOR_WARN_OVER_MASK = 0x08, + SES_STATUS_VOLTAGE_SENSOR_WARN_OVER_SHIFT = 3, + + SES_STATUS_VOLTAGE_SENSOR_WARN_UNDER_BYTE = 0, + SES_STATUS_VOLTAGE_SENSOR_WARN_UNDER_MASK = 0x04, + SES_STATUS_VOLTAGE_SENSOR_WARN_UNDER_SHIFT = 2, + + SES_STATUS_VOLTAGE_SENSOR_CRIT_OVER_BYTE = 0, + SES_STATUS_VOLTAGE_SENSOR_CRIT_OVER_MASK = 0x02, + SES_STATUS_VOLTAGE_SENSOR_CRIT_OVER_SHIFT = 1, + + SES_STATUS_VOLTAGE_SENSOR_CRIT_UNDER_BYTE = 0, + SES_STATUS_VOLTAGE_SENSOR_CRIT_UNDER_MASK = 0x01, + SES_STATUS_VOLTAGE_SENSOR_CRIT_UNDER_SHIFT = 0 +}; + +#define GEN_SES_STATUS_VOLTAGE_SENSOR_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_voltage_sensor, SES_STATUS_VOLTAGE_SENSOR, \ + LCASE, UCASE) +GEN_SES_STATUS_VOLTAGE_SENSOR_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_VOLTAGE_SENSOR_ACCESSORS(fail, FAIL) +GEN_SES_STATUS_VOLTAGE_SENSOR_ACCESSORS(warn_over, WARN_OVER) +GEN_SES_STATUS_VOLTAGE_SENSOR_ACCESSORS(warn_under, WARN_UNDER) +GEN_SES_STATUS_VOLTAGE_SENSOR_ACCESSORS(crit_over, CRIT_OVER) +GEN_SES_STATUS_VOLTAGE_SENSOR_ACCESSORS(crit_under, CRIT_UNDER) +#undef GEN_SES_STATUS_VOLTAGE_SENSOR_ACCESSORS + +/*---------------------- Current Sensor Status Element -----------------------*/ +struct ses_status_current_sensor { + struct ses_status_common common; + uint8_t bytes[3]; +}; + +enum ses_status_current_sensor_field_data { + SES_STATUS_CURRENT_SENSOR_IDENT_BYTE = 0, + SES_STATUS_CURRENT_SENSOR_IDENT_MASK = 0x80, + SES_STATUS_CURRENT_SENSOR_IDENT_SHIFT = 7, + + SES_STATUS_CURRENT_SENSOR_FAIL_BYTE = 0, + SES_STATUS_CURRENT_SENSOR_FAIL_MASK = 0x40, + SES_STATUS_CURRENT_SENSOR_FAIL_SHIFT = 6, + + SES_STATUS_CURRENT_SENSOR_WARN_OVER_BYTE = 0, + SES_STATUS_CURRENT_SENSOR_WARN_OVER_MASK = 0x08, + SES_STATUS_CURRENT_SENSOR_WARN_OVER_SHIFT = 3, + + SES_STATUS_CURRENT_SENSOR_CRIT_OVER_BYTE = 0, + SES_STATUS_CURRENT_SENSOR_CRIT_OVER_MASK = 0x02, + SES_STATUS_CURRENT_SENSOR_CRIT_OVER_SHIFT = 1 +}; + +#define GEN_SES_STATUS_CURRENT_SENSOR_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_current_sensor, SES_STATUS_CURRENT_SENSOR, \ + LCASE, UCASE) +GEN_SES_STATUS_CURRENT_SENSOR_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_CURRENT_SENSOR_ACCESSORS(fail, FAIL) +GEN_SES_STATUS_CURRENT_SENSOR_ACCESSORS(warn_over, WARN_OVER) +GEN_SES_STATUS_CURRENT_SENSOR_ACCESSORS(crit_over, CRIT_OVER) +#undef GEN_SES_STATUS_CURRENT_SENSOR_ACCESSORS + +/*--------------------- SCSI Target Port Status Element ----------------------*/ +struct ses_status_target_port { + struct ses_status_common common; + uint8_t bytes[3]; +}; + +enum ses_status_scsi_target_port_field_data { + SES_STATUS_TARGET_PORT_IDENT_BYTE = 0, + SES_STATUS_TARGET_PORT_IDENT_MASK = 0x80, + SES_STATUS_TARGET_PORT_IDENT_SHIFT = 7, + + SES_STATUS_TARGET_PORT_FAIL_BYTE = 0, + SES_STATUS_TARGET_PORT_FAIL_MASK = 0x40, + SES_STATUS_TARGET_PORT_FAIL_SHIFT = 6, + + SES_STATUS_TARGET_PORT_REPORT_BYTE = 1, + SES_STATUS_TARGET_PORT_REPORT_MASK = 0x01, + SES_STATUS_TARGET_PORT_REPORT_SHIFT = 0, + + SES_STATUS_TARGET_PORT_ENABLED_BYTE = 2, + SES_STATUS_TARGET_PORT_ENABLED_MASK = 0x01, + SES_STATUS_TARGET_PORT_ENABLED_SHIFT = 0 +}; + +#define GEN_SES_STATUS_TARGET_PORT_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_target_port, SES_STATUS_TARGET_PORT, LCASE, UCASE) +GEN_SES_STATUS_TARGET_PORT_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_TARGET_PORT_ACCESSORS(fail, FAIL) +GEN_SES_STATUS_TARGET_PORT_ACCESSORS(report, REPORT) +GEN_SES_STATUS_TARGET_PORT_ACCESSORS(enabled, ENABLED) +#undef GEN_SES_STATUS_TARGET_PORT_ACCESSORS + +/*-------------------- SCSI Initiator Port Status Element --------------------*/ +struct ses_status_initiator_port { + struct ses_status_common common; + uint8_t bytes[3]; +}; + +enum ses_status_scsi_initiator_port_field_data { + SES_STATUS_INITIATOR_PORT_IDENT_BYTE = 0, + SES_STATUS_INITIATOR_PORT_IDENT_MASK = 0x80, + SES_STATUS_INITIATOR_PORT_IDENT_SHIFT = 7, + + SES_STATUS_INITIATOR_PORT_FAIL_BYTE = 0, + SES_STATUS_INITIATOR_PORT_FAIL_MASK = 0x40, + SES_STATUS_INITIATOR_PORT_FAIL_SHIFT = 6, + + SES_STATUS_INITIATOR_PORT_REPORT_BYTE = 1, + SES_STATUS_INITIATOR_PORT_REPORT_MASK = 0x01, + SES_STATUS_INITIATOR_PORT_REPORT_SHIFT = 0, + + SES_STATUS_INITIATOR_PORT_ENABLED_BYTE = 2, + SES_STATUS_INITIATOR_PORT_ENABLED_MASK = 0x01, + SES_STATUS_INITIATOR_PORT_ENABLED_SHIFT = 0 +}; + +#define GEN_SES_STATUS_INITIATOR_PORT_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_initiator_port, SES_STATUS_INITIATOR_PORT, \ + LCASE, UCASE) +GEN_SES_STATUS_INITIATOR_PORT_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_INITIATOR_PORT_ACCESSORS(fail, FAIL) +GEN_SES_STATUS_INITIATOR_PORT_ACCESSORS(report, REPORT) +GEN_SES_STATUS_INITIATOR_PORT_ACCESSORS(enabled, ENABLED) +#undef GEN_SES_STATUS_INITIATOR_PORT_ACCESSORS + +/*-------------------- Simple Subseslosure Status Element --------------------*/ +struct ses_status_simple_subses { + struct ses_status_common common; + uint8_t bytes[2]; + uint8_t short_seslosure_status; +}; + +enum ses_status_simple_subses_field_data { + SES_STATUS_SIMPlE_SUBSES_IDENT_BYTE = 0, + SES_STATUS_SIMPlE_SUBSES_IDENT_MASK = 0x80, + SES_STATUS_SIMPlE_SUBSES_IDENT_SHIFT = 7, + + SES_STATUS_SIMPlE_SUBSES_FAIL_BYTE = 0, + SES_STATUS_SIMPlE_SUBSES_FAIL_MASK = 0x40, + SES_STATUS_SIMPlE_SUBSES_FAIL_SHIFT = 6 +}; + +#define GEN_SES_STATUS_SIMPlE_SUBSES_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_simple_subses, SES_STATUS_SIMPlE_SUBSES, \ + LCASE, UCASE) +GEN_SES_STATUS_SIMPlE_SUBSES_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_SIMPlE_SUBSES_ACCESSORS(fail, FAIL) +#undef GEN_SES_STATUS_SIMPlE_SUBSES_ACCESSORS + +/*----------------------- SAS Expander Status Element ------------------------*/ +struct ses_status_sas_expander { + struct ses_status_common common; + uint8_t bytes[3]; +}; + +enum ses_status_sas_expander_field_data { + SES_STATUS_SAS_EXPANDER_IDENT_BYTE = 0, + SES_STATUS_SAS_EXPANDER_IDENT_MASK = 0x80, + SES_STATUS_SAS_EXPANDER_IDENT_SHIFT = 7, + + SES_STATUS_SAS_EXPANDER_FAIL_BYTE = 0, + SES_STATUS_SAS_EXPANDER_FAIL_MASK = 0x40, + SES_STATUS_SAS_EXPANDER_FAIL_SHIFT = 6 +}; + +#define GEN_SES_STATUS_SAS_EXPANDER_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_sas_expander, SES_STATUS_SAS_EXPANDER, LCASE, UCASE) +GEN_SES_STATUS_SAS_EXPANDER_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_SAS_EXPANDER_ACCESSORS(fail, FAIL) +#undef GEN_SES_STATUS_SAS_EXPANDER_ACCESSORS + +/*----------------------- SAS Connector Status Element -----------------------*/ +struct ses_status_sas_connector { + struct ses_status_common common; + uint8_t bytes[3]; +}; + +enum ses_status_sas_connector_field_data { + SES_STATUS_SAS_CONNECTOR_IDENT_BYTE = 0, + SES_STATUS_SAS_CONNECTOR_IDENT_MASK = 0x80, + SES_STATUS_SAS_CONNECTOR_IDENT_SHIFT = 7, + + SES_STATUS_SAS_CONNECTOR_TYPE_BYTE = 0, + SES_STATUS_SAS_CONNECTOR_TYPE_MASK = 0x7F, + SES_STATUS_SAS_CONNECTOR_TYPE_SHIFT = 0, + + SES_STATUS_SAS_CONNECTOR_PHYS_LINK_BYTE = 1, + SES_STATUS_SAS_CONNECTOR_PHYS_LINK_MASK = 0xFF, + SES_STATUS_SAS_CONNECTOR_PHYS_LINK_SHIFT = 0, + SES_STATUS_SAS_CONNECTOR_PHYS_LINK_ALL = 0xFF, + + SES_STATUS_SAS_CONNECTOR_FAIL_BYTE = 2, + SES_STATUS_SAS_CONNECTOR_FAIL_MASK = 0x40, + SES_STATUS_SAS_CONNECTOR_FAIL_SHIFT = 6, +}; + +#define GEN_SES_STATUS_SAS_CONNECTOR_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_sas_connector, SES_STATUS_SAS_CONNECTOR, \ + LCASE, UCASE) +GEN_SES_STATUS_SAS_CONNECTOR_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_SAS_CONNECTOR_ACCESSORS(type, TYPE) +GEN_SES_STATUS_SAS_CONNECTOR_ACCESSORS(phys_link, PHYS_LINK) +GEN_SES_STATUS_SAS_CONNECTOR_ACCESSORS(fail, FAIL) +#undef GEN_SES_STATUS_SAS_CONNECTOR_ACCESSORS + +/*------------------------- Universal Status Element -------------------------*/ +union ses_status_element { + struct ses_status_common common; + struct ses_status_dev_slot dev_slot; + struct ses_status_array_dev_slot array_dev_slot; + struct ses_status_power_supply power_supply; + struct ses_status_cooling cooling; + struct ses_status_temp_sensor temp_sensor; + struct ses_status_door_lock door_lock; + struct ses_status_audible_alarm audible_alarm; + struct ses_status_ecc_electronics ecc_electronics; + struct ses_status_scc_electronics scc_electronics; + struct ses_status_nv_cache nv_cache; + struct ses_status_invalid_op_reason invalid_op_reason; + struct ses_status_ups ups; + struct ses_status_display display; + struct ses_status_key_pad_entry key_pad_entry; + struct ses_status_scsi_port_or_xcvr scsi_port_or_xcvr; + struct ses_status_language language; + struct ses_status_comm_port comm_port; + struct ses_status_voltage_sensor voltage_sensor; + struct ses_status_current_sensor current_sensor; + struct ses_status_target_port target_port; + struct ses_status_initiator_port initiator_port; + struct ses_status_simple_subses simple_subses; + struct ses_status_sas_expander sas_expander; + struct ses_status_sas_connector sas_connector; + uint8_t bytes[4]; +}; + +/*===================== SCSI SES Status Diagnostic Page =====================*/ +struct ses_status_page { + struct ses_page_hdr hdr; + union ses_status_element elements[]; +}; + +enum ses_status_page_field_data { + SES_STATUS_PAGE_INVOP_MASK = 0x10, + SES_STATUS_PAGE_INVOP_SHIFT = 4, + + SES_STATUS_PAGE_INFO_MASK = 0x08, + SES_STATUS_PAGE_INFO_SHIFT = 3, + + SES_STATUS_PAGE_NON_CRIT_MASK = 0x04, + SES_STATUS_PAGE_NON_CRIT_SHIFT = 2, + + SES_STATUS_PAGE_CRIT_MASK = 0x02, + SES_STATUS_PAGE_CRIT_SHIFT = 1, + + SES_STATUS_PAGE_UNRECOV_MASK = 0x01, + SES_STATUS_PAGE_UNRECOV_SHIFT = 0, + + SES_STATUS_PAGE_CHANGED_MASK = SES_STATUS_PAGE_INVOP_MASK + | SES_STATUS_PAGE_INFO_MASK + | SES_STATUS_PAGE_NON_CRIT_MASK + | SES_STATUS_PAGE_CRIT_MASK + | SES_STATUS_PAGE_UNRECOV_MASK, + SES_STATUS_PAGE_CHANGED_SHIFT = 0, +}; + +#define GEN_SES_STATUS_PAGE_ACCESSORS(LCASE, UCASE) \ + GEN_HDR_ACCESSORS(ses_status_page, SES_STATUS_PAGE, LCASE, UCASE) + +GEN_SES_STATUS_PAGE_ACCESSORS(invop, INVOP) +GEN_SES_STATUS_PAGE_ACCESSORS(info, INFO) +GEN_SES_STATUS_PAGE_ACCESSORS(non_crit, NON_CRIT) +GEN_SES_STATUS_PAGE_ACCESSORS(crit, CRIT) +GEN_SES_STATUS_PAGE_ACCESSORS(unrecov, UNRECOV) +GEN_SES_STATUS_PAGE_ACCESSORS(changed, CHANGED) +#undef GEN_SES_STATUS_PAGE_ACCESSORS + +/*================ SCSI SES Element Descriptor Diagnostic Page ===============*/ +struct ses_elem_descr { + uint8_t reserved[2]; + uint8_t length[2]; + char description[]; +}; + +struct ses_elem_descr_page { + struct ses_page_hdr hdr; + struct ses_elem_descr descrs[]; +}; + +/*============ SCSI SES Additional Element Status Diagnostic Page ============*/ +struct ses_addl_elem_status_page { + struct ses_page_hdr hdr; +}; + +/*====================== Legacy (Deprecated) Structures ======================*/ +struct ses_control_page_hdr { + uint8_t page_code; + uint8_t control_flags; + uint8_t length[2]; + uint8_t gen_code[4]; +/* Followed by variable length array of descriptors. */ +}; + +struct ses_status_page_hdr { + uint8_t page_code; + uint8_t status_flags; + uint8_t length[2]; + uint8_t gen_code[4]; +/* Followed by variable length array of descriptors. */ +}; + +/* ses_page_hdr.reserved values */ /* - * Overall Enclosure Status + * Enclosure Status Diagnostic Page: + * uint8_t reserved : 3, + * invop : 1, + * info : 1, + * noncritical : 1, + * critical : 1, + * unrecov : 1; */ -typedef unsigned char ses_encstat; -#define SES_ENCSTAT_UNRECOV 0x1 -#define SES_ENCSTAT_CRITICAL 0x2 -#define SES_ENCSTAT_NONCRITICAL 0x4 -#define SES_ENCSTAT_INFO 0x8 +#define SES_ENCSTAT_UNRECOV 0x01 +#define SES_ENCSTAT_CRITICAL 0x02 +#define SES_ENCSTAT_NONCRITICAL 0x04 +#define SES_ENCSTAT_INFO 0x08 +#define SES_ENCSTAT_INVOP 0x10 +/* Status mask: All of the above OR'd together */ +#define SES_STATUS_MASK 0x1f +#define SES_SET_STATUS_MASK 0xf +/* Element Descriptor Diagnostic Page: unused */ +/* Additional Element Status Diagnostic Page: unused */ + -/* - * Object Status - */ -typedef struct { - unsigned int obj_id; - unsigned char cstat[4]; -} ses_objstat; /* Summary SES Status Defines, Common Status Codes */ #define SES_OBJSTAT_UNSUPPORTED 0 @@ -140,10 +2164,11 @@ typedef struct { #define SES_OBJSTAT_NOTINSTALLED 5 #define SES_OBJSTAT_UNKNOWN 6 #define SES_OBJSTAT_NOTAVAIL 7 +#define SES_OBJSTAT_NOACCESS 8 /* * For control pages, cstat[0] is the same for the - * enclosure and is common across all device types. + * seslosure and is common across all device types. * * If SESCTL_CSEL is set, then PRDFAIL, DISABLE and RSTSWAP * are checked, otherwise bits that are specific to the device @@ -181,3 +2206,236 @@ typedef union { unsigned int obj_id; char obj_text[1]; } ses_hlptxt; + +/*============================================================================*/ +struct ses_elm_desc_hdr { + uint8_t reserved[2]; + uint8_t length[2]; +}; + +/* + * SES v2 r20 6.1.13 - Element Additional Status diagnostic page + * Tables 26-28 (general), 29-32 (FC), 33-41 (SAS) + * + * Protocol identifier uses definitions in scsi_all.h; + * SPSP_PROTO_FC, SPSP_PROTO_SAS are the only ones used here. + */ + +struct ses_elm_fc_eip_hdr { + uint8_t num_phys; + uint8_t reserved[2]; + uint8_t dev_slot_num; + uint8_t node_name[8]; +}; + +struct ses_elm_fc_noneip_hdr { + uint8_t num_phys; + uint8_t reserved; + uint8_t node_name[8]; +}; + +struct ses_elm_fc_base_hdr { + uint8_t num_phys; +}; + +union ses_elm_fc_hdr { + struct ses_elm_fc_base_hdr base_hdr; + struct ses_elm_fc_eip_hdr eip_hdr; + struct ses_elm_fc_noneip_hdr noneip_hdr; +}; + +struct ses_elm_fc_port { + uint8_t port_loop_position; + uint8_t bypass_reason; +#define SES_FC_PORT_BYPASS_UNBYPASSED 0x00 + +#define SES_FC_PORT_BYPASS_LINKFAIL_RATE_TOO_HIGH 0x10 +#define SES_FC_PORT_BYPASS_SYNC_LOSS_RATE_TOO_HIGH 0x11 +#define SES_FC_PORT_BYPASS_SIGNAL_LOSS_RATE_TOO_HIGH 0x12 +#define SES_FC_PORT_BYPASS_SEQPROTO_ERR_RATE_TOO_HIGH 0x13 +#define SES_FC_PORT_BYPASS_INVAL_XMIT_RATE_TOO_HIGH 0x14 +#define SES_FC_PORT_BYPASS_CRC_ERR_RATE_TOO_HIGH 0x15 + +#define SES_FC_PORT_BYPASS_ERR_RATE_RESERVED_BEGIN 0x16 +#define SES_FC_PORT_BYPASS_ERR_RATE_RESERVED_END 0x1F + +#define SES_FC_PORT_BYPASS_LINKFAIL_COUNT_TOO_HIGH 0x20 +#define SES_FC_PORT_BYPASS_SYNC_LOSS_COUNT_TOO_HIGH 0x21 +#define SES_FC_PORT_BYPASS_SIGNAL_LOSS_COUNT_TOO_HIGH 0x22 +#define SES_FC_PORT_BYPASS_SEQPROTO_ERR_COUNT_TOO_HIGH 0x23 +#define SES_FC_PORT_BYPASS_INVAL_XMIT_COUNT_TOO_HIGH 0x24 +#define SES_FC_PORT_BYPASS_CRC_ERR_COUNT_TOO_HIGH 0x25 + +#define SES_FC_PORT_BYPASS_ERR_COUNT_RESERVED_BEGIN 0x26 +#define SES_FC_PORT_BYPASS_ERR_COUNT_RESERVED_END 0x2F + +#define SES_FC_PORT_BYPASS_RESERVED_BEGIN 0x30 +#define SES_FC_PORT_BYPASS_RESERVED_END 0xBF + +#define SES_FC_PORT_BYPASS_VENDOR_SPECIFIC_BEGIN 0xC0 +#define SES_FC_PORT_BYPASS_VENDOR_SPECIFIC_END 0xFF + uint8_t port_req_hard_addr; + uint8_t n_port_id[3]; + uint8_t n_port_name[8]; +}; + +struct ses_elm_sas_device_phy { + uint8_t byte0; + /* + * uint8_t reserved0 : 1, + * uint8_t device_type : 3, + * uint8_t reserved1 : 4; + */ + + uint8_t reserved0; + + /* Bit positions for initiator and target port protocols */ +#define SES_SASOBJ_DEV_PHY_SMP 0x2 +#define SES_SASOBJ_DEV_PHY_STP 0x4 +#define SES_SASOBJ_DEV_PHY_SSP 0x8 + /* Select all of the above protocols */ +#define SES_SASOBJ_DEV_PHY_PROTOMASK 0xe + uint8_t initiator_ports; + /* + * uint8_t reserved0 : 4, + * uint8_t ssp : 1, + * uint8_t stp : 1, + * uint8_t smp : 1, + * uint8_t reserved1 : 3; + */ + uint8_t target_ports; + /* + * uint8_t sata_port_selector : 1, + * uint8_t reserved : 3, + * uint8_t ssp : 1, + * uint8_t stp : 1, + * uint8_t smp : 1, + * uint8_t sata_device : 1; + */ + uint8_t parent_addr[8]; /* SAS address of parent */ + uint8_t phy_addr[8]; /* SAS address of this phy */ + uint8_t phy_id; + uint8_t reserved1[7]; +}; +#ifdef _KERNEL +int ses_elm_sas_dev_phy_sata_dev(struct ses_elm_sas_device_phy *); +int ses_elm_sas_dev_phy_sata_port(struct ses_elm_sas_device_phy *); +int ses_elm_sas_dev_phy_dev_type(struct ses_elm_sas_device_phy *); +#endif /* _KERNEL */ + +struct ses_elm_sas_expander_phy { + uint8_t connector_index; + uint8_t other_index; +}; + +struct ses_elm_sas_port_phy { + uint8_t phy_id; + uint8_t reserved; + uint8_t connector_index; + uint8_t other_index; + uint8_t phy_addr[8]; +}; + +struct ses_elm_sas_type0_base_hdr { + uint8_t num_phys; + uint8_t byte1; + /* + * uint8_t descriptor_type : 2, + * uint8_t reserved : 5, + * uint8_t not_all_phys : 1; + */ +#define SES_SASOBJ_TYPE0_NOT_ALL_PHYS(obj) \ + ((obj)->byte1 & 0x1) +}; + +struct ses_elm_sas_type0_eip_hdr { + struct ses_elm_sas_type0_base_hdr base; + uint8_t reserved; + uint8_t dev_slot_num; +}; + +struct ses_elm_sas_type1_expander_hdr { + uint8_t num_phys; + uint8_t byte1; + /* + * uint8_t descriptor_type : 2, + * uint8_t reserved : 6; + */ + uint8_t reserved[2]; + uint8_t sas_addr[8]; +}; + +struct ses_elm_sas_type1_nonexpander_hdr { + uint8_t num_phys; + uint8_t byte1; + /* + * uint8_t descriptor_type : 2, + * uint8_t reserved : 6; + */ + uint8_t reserved[2]; +}; + +/* NB: This is only usable for as long as the headers happen to match */ +struct ses_elm_sas_base_hdr { + uint8_t num_phys; + uint8_t byte1; + /* + * uint8_t descriptor_type : 2, + * uint8_t descr_specific : 6; + */ +#define SES_SASOBJ_TYPE_SLOT 0 +#define SES_SASOBJ_TYPE_OTHER 1 +}; + +union ses_elm_sas_hdr { + struct ses_elm_sas_base_hdr base_hdr; + struct ses_elm_sas_type0_base_hdr type0_noneip; + struct ses_elm_sas_type0_eip_hdr type0_eip; + struct ses_elm_sas_type1_expander_hdr type1_exp; + struct ses_elm_sas_type1_nonexpander_hdr type1_nonexp; +}; +int ses_elm_sas_type0_not_all_phys(union ses_elm_sas_hdr *); +int ses_elm_sas_descr_type(union ses_elm_sas_hdr *); + +struct ses_elm_addlstatus_base_hdr { + uint8_t byte0; + /* + * uint8_t invalid : 1, + * uint8_t reserved : 2, + * uint8_t eip : 1, + * uint8_t proto_id : 4; + */ + uint8_t length; +}; +int ses_elm_addlstatus_proto(struct ses_elm_addlstatus_base_hdr *); +int ses_elm_addlstatus_eip(struct ses_elm_addlstatus_base_hdr *); +int ses_elm_addlstatus_invalid(struct ses_elm_addlstatus_base_hdr *); + +struct ses_elm_addlstatus_eip_hdr { + struct ses_elm_addlstatus_base_hdr base; + uint8_t reserved; + uint8_t element_index; + /* NB: This define (currently) applies to all eip=1 headers */ +#define SES_EIP_HDR_EXTRA_LEN 2 +}; + +union ses_elm_addlstatus_descr_hdr { + struct ses_elm_addlstatus_base_hdr base; + struct ses_elm_addlstatus_eip_hdr eip; +}; + +union ses_elm_addlstatus_proto_hdr { + union ses_elm_fc_hdr fc; + union ses_elm_sas_hdr sas; +}; + +/*============================= Namespace Cleanup ============================*/ +#undef GEN_HDR_ACCESSORS +#undef GEN_ACCESSORS +#undef GEN_HDR_SETTER +#undef GEN_HDR_GETTER +#undef GEN_SETTER +#undef GEN_GETTER +#undef MK_ENUM + +#endif /* _SCSI_SES_H_ */ diff --git a/sys/conf/files b/sys/conf/files index 0dc814e0934..b0538d1402e 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -136,7 +136,9 @@ cam/scsi/scsi_low_pisa.c optional ct | ncv | nsp | stg cam/scsi/scsi_pass.c optional pass cam/scsi/scsi_pt.c optional pt cam/scsi/scsi_sa.c optional sa -cam/scsi/scsi_ses.c optional ses +cam/scsi/scsi_enc.c optional enc +cam/scsi/scsi_enc_ses.c optional enc +cam/scsi/scsi_enc_safte.c optional enc cam/scsi/scsi_sg.c optional sg cam/scsi/scsi_targ_bh.c optional targbh cam/scsi/scsi_target.c optional targ diff --git a/sys/i386/conf/GENERIC b/sys/i386/conf/GENERIC index c4548c6bce1..20072dece42 100644 --- a/sys/i386/conf/GENERIC +++ b/sys/i386/conf/GENERIC @@ -133,7 +133,7 @@ device da # Direct Access (disks) device sa # Sequential Access (tape etc) device cd # CD device pass # Passthrough device (direct ATA/SCSI access) -device ses # SCSI Environmental Services (and SAF-TE) +device enc # Enclosure Services (SES and SAF-TE) # RAID controllers interfaced to the SCSI subsystem device amr # AMI MegaRAID diff --git a/sys/ia64/conf/GENERIC b/sys/ia64/conf/GENERIC index eaf3ffd4452..475e356f794 100644 --- a/sys/ia64/conf/GENERIC +++ b/sys/ia64/conf/GENERIC @@ -106,7 +106,7 @@ device ch # Media changer device da # Direct Access (ie disk) device pass # Passthrough (direct ATA/SCSI access) device sa # Sequential Access (ie tape) -device ses # Environmental Services (and SAF-TE) +device enc # Enclosure Services (SES and SAF-TE) # RAID controllers device aac # Adaptec FSA RAID diff --git a/sys/mips/conf/OCTEON1 b/sys/mips/conf/OCTEON1 index 2a0abcf4224..64d34cbc44d 100644 --- a/sys/mips/conf/OCTEON1 +++ b/sys/mips/conf/OCTEON1 @@ -135,7 +135,7 @@ device da # Direct Access (disks) device sa # Sequential Access (tape etc) device cd # CD device pass # Passthrough device (direct ATA/SCSI access) -device ses # SCSI Environmental Services (and SAF-TE) +device enc # Enclosure Services (SES and SAF-TE) # RAID controllers interfaced to the SCSI subsystem device amr # AMI MegaRAID diff --git a/sys/modules/cam/Makefile b/sys/modules/cam/Makefile index f1bfdb94d31..5b07b539ef1 100644 --- a/sys/modules/cam/Makefile +++ b/sys/modules/cam/Makefile @@ -14,7 +14,7 @@ SRCS+= opt_scsi.h SRCS+= opt_cd.h SRCS+= opt_pt.h SRCS+= opt_sa.h -SRCS+= opt_ses.h +SRCS+= opt_enc.h SRCS+= device_if.h bus_if.h vnode_if.h SRCS+= cam.c cam_periph.c cam_queue.c SRCS+= cam_sim.c cam_xpt.c @@ -23,7 +23,9 @@ SRCS+= scsi_da.c SRCS+= scsi_pass.c SRCS+= scsi_pt.c SRCS+= scsi_sa.c -SRCS+= scsi_ses.c +SRCS+= scsi_enc.c +SRCS+= scsi_enc_ses.c +SRCS+= scsi_enc_safte.c SRCS+= scsi_sg.c SRCS+= scsi_targ_bh.c scsi_target.c SRCS+= scsi_xpt.c diff --git a/sys/pc98/conf/GENERIC b/sys/pc98/conf/GENERIC index e2bed44764b..57392dcab7d 100644 --- a/sys/pc98/conf/GENERIC +++ b/sys/pc98/conf/GENERIC @@ -117,7 +117,7 @@ device da # Direct Access (disks) device sa # Sequential Access (tape etc) device cd # CD device pass # Passthrough device (direct ATA/SCSI access) -device ses # SCSI Environmental Services (and SAF-TE) +device enc # Enclosure Services (SES and SAF-TE) # keyboard driver device pckbd # PC98 keyboard diff --git a/sys/sparc64/conf/GENERIC b/sys/sparc64/conf/GENERIC index 11c5d35f71a..c2636792c01 100644 --- a/sys/sparc64/conf/GENERIC +++ b/sys/sparc64/conf/GENERIC @@ -113,7 +113,7 @@ device da # Direct Access (disks) device sa # Sequential Access (tape etc) device cd # CD device pass # Passthrough device (direct ATA/SCSI access) -device ses # SCSI Environmental Services (and SAF-TE) +device enc # Enclosure Services (SES and SAF-TE) # RAID controllers #device amr # AMI MegaRAID From 425e7053afb83f386d7e9104a8dfc405a4b18c4b Mon Sep 17 00:00:00 2001 From: "Justin T. Gibbs" Date: Mon, 18 Jul 2011 20:06:49 +0000 Subject: [PATCH 03/89] sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c: sys/cddl/contrib/opensolaris/uts/common/sys/sysevent/dev.h: Emit "dev_path" instead of "phys_path" in autoexpand events. The "dev_path" already is a devfs path to the device and this avoids having to convert the physical path information into a devfs path in yet another location. Sponsored by: Spectra Logic Corporation --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c | 9 ++------- .../contrib/opensolaris/uts/common/sys/sysevent/dev.h | 1 + 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c index 9336a6b516c..e119def21b6 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c @@ -5051,7 +5051,6 @@ spa_async_autoexpand(spa_t *spa, vdev_t *vd) { sysevent_id_t eid; nvlist_t *attr; - char *physpath; if (!spa->spa_autoexpand) return; @@ -5061,20 +5060,16 @@ spa_async_autoexpand(spa_t *spa, vdev_t *vd) spa_async_autoexpand(spa, cvd); } - if (!vd->vdev_ops->vdev_op_leaf || vd->vdev_physpath == NULL) + if (!vd->vdev_ops->vdev_op_leaf || vd->vdev_path == NULL) return; - physpath = kmem_zalloc(MAXPATHLEN, KM_SLEEP); - (void) snprintf(physpath, MAXPATHLEN, "/devices%s", vd->vdev_physpath); - VERIFY(nvlist_alloc(&attr, NV_UNIQUE_NAME, KM_SLEEP) == 0); - VERIFY(nvlist_add_string(attr, DEV_PHYS_PATH, physpath) == 0); + VERIFY(nvlist_add_string(attr, DEV_PATH, vd->vdev_path) == 0); (void) ddi_log_sysevent(zfs_dip, SUNW_VENDOR, EC_DEV_STATUS, ESC_ZFS_VDEV_AUTOEXPAND, attr, &eid, DDI_SLEEP); nvlist_free(attr); - kmem_free(physpath, MAXPATHLEN); } static void diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/sysevent/dev.h b/sys/cddl/contrib/opensolaris/uts/common/sys/sysevent/dev.h index 9d3107d0901..b64c0fcb3c6 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/sys/sysevent/dev.h +++ b/sys/cddl/contrib/opensolaris/uts/common/sys/sysevent/dev.h @@ -236,6 +236,7 @@ extern "C" { #define EV_VERSION "version" #define DEV_PHYS_PATH "phys_path" +#define DEV_PATH "dev_path" #define DEV_NAME "dev_name" #define DEV_DRIVER_NAME "driver_name" #define DEV_INSTANCE "instance" From 742d564a7ae08db18505f79148e6ed33a004d06e Mon Sep 17 00:00:00 2001 From: "Justin T. Gibbs" Date: Wed, 20 Jul 2011 22:48:48 +0000 Subject: [PATCH 04/89] Allow ZFS asynchronous event handling to proceed even if the root file system is mounted read-only. This restriction appears to have been put in place to avoid errors with updating the configuration cache file. However: o The majority of asynchronous event handling does not involve configuration cache file updates. o The configuration cache file need not be on the root file system, so the check was not complete. o Other classes of errors (e.g. file system full) can also prevent a successful update yet do not prevent asynchronous event processing. o Configurations such as NanoBSD never have a read-write root, so ZFS event processing is permanently disabled in these systems. o Failure to handle asynchronous events promptly can extend the window of time that a pool is in a critical state. At worst, a missed configuration cache update will force the operator to perform a manual "zfs import" (note -f is not required) to inform the system about a newly created pool. To minimize the likelihood of this rare occurrence, configuration cache write failures now emit FMA events so the operator can take corrective action, and the write is retried every 5 minutes. The retry interval, in seconds, is tunable via the sysctl "vfs.zfs.ccw_retry_interval". sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c: o Add the sysctl "vfs.zfs.ccw_retry_interval". The value defaults to 5 minutes and is used to rate limit, on a per-pool basis, configuration cache file write attempts. o Modify spa_async_dispatch to honor configuration cache write limiting. If other events are pending, a configuration cache write will be attempted at the same time, so the rate limiting only applies when the asynchronous dispatch system is otherwise idle. Async events should be rare (e.g. device arrival/departure) and configuration cache writes rarer, so a more complicated system to strictly honor the retry limit seems unwarranted. o Remove check in spa_async_dispatch() for the root file system being read-write. sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_config.c: Instead of silently ignoring configuration cache write failures, report them via a new FMA event as well as to the console. The current zfs_ereport_post() doesn't allow arbitrary name=value pairs to be appended to the report, so the configuration cache file name is only available on the console output. This limitation should be addressed in a future update. sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa_impl.h: Add a uint64_t to the spa data structure to track the time (via LBOLT) of the last configuration cache file write failure. This is referenced in spa_async_dispatch() to effect the rate limiting. sys/cddl/contrib/opensolaris/uts/common/sys/fm/fs/zfs.h: Add FM_EREPORT_ZFS_CONFIG_CACHE_WRITE as an ereport class. Sponsored by: Spectra Logic Corporation --- .../opensolaris/uts/common/fs/zfs/spa.c | 35 ++++++++++++- .../uts/common/fs/zfs/spa_config.c | 50 +++++++++++++++---- .../uts/common/fs/zfs/sys/spa_impl.h | 1 + .../opensolaris/uts/common/sys/fm/fs/zfs.h | 1 + 4 files changed, 76 insertions(+), 11 deletions(-) diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c index e119def21b6..ae7e1d50f33 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c @@ -73,10 +73,20 @@ /* Check hostid on import? */ static int check_hostid = 1; +/* + * The interval at which failed configuration cache file writes + * should be retried. + */ +static int zfs_ccw_retry_interval = 300; + SYSCTL_DECL(_vfs_zfs); TUNABLE_INT("vfs.zfs.check_hostid", &check_hostid); SYSCTL_INT(_vfs_zfs, OID_AUTO, check_hostid, CTLFLAG_RW, &check_hostid, 0, "Check hostid on import?"); +TUNABLE_INT("vfs.zfs.ccw_retry_interval", &zfs_ccw_retry_interval); +SYSCTL_INT(_vfs_zfs, OID_AUTO, ccw_retry_interval, CTLFLAG_RW, + &zfs_ccw_retry_interval, 0, + "Configuration cache file write, retry after failure, interval (seconds)"); typedef enum zti_modes { zti_mode_fixed, /* value is # of threads (min 1) */ @@ -5178,13 +5188,34 @@ spa_async_resume(spa_t *spa) mutex_exit(&spa->spa_async_lock); } +static int +spa_async_tasks_pending(spa_t *spa) +{ + u_int non_config_tasks; + u_int config_task; + boolean_t config_task_suspended; + + non_config_tasks = spa->spa_async_tasks & ~SPA_ASYNC_CONFIG_UPDATE; + config_task = spa->spa_async_tasks & SPA_ASYNC_CONFIG_UPDATE; + if (spa->spa_ccw_fail_time == 0) { + config_task_suspended = B_FALSE; + } else { + config_task_suspended = + (ddi_get_lbolt64() - spa->spa_ccw_fail_time) + < (zfs_ccw_retry_interval * hz); + } + + return (non_config_tasks || (config_task && !config_task_suspended)); +} + static void spa_async_dispatch(spa_t *spa) { mutex_enter(&spa->spa_async_lock); - if (spa->spa_async_tasks && !spa->spa_async_suspended && + if (spa_async_tasks_pending(spa) && + !spa->spa_async_suspended && spa->spa_async_thread == NULL && - rootdir != NULL && !vn_is_readonly(rootdir)) + rootdir != NULL) spa->spa_async_thread = thread_create(NULL, 0, spa_async_thread, spa, 0, &p0, TS_RUN, maxclsyspri); mutex_exit(&spa->spa_async_lock); diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_config.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_config.c index 0b8255ef355..b4e3bfcc780 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_config.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_config.c @@ -24,6 +24,7 @@ */ #include +#include #include #include #include @@ -136,7 +137,7 @@ out: kobj_close_file(file); } -static void +static int spa_config_write(spa_config_dirent_t *dp, nvlist_t *nvl) { size_t buflen; @@ -144,13 +145,14 @@ spa_config_write(spa_config_dirent_t *dp, nvlist_t *nvl) vnode_t *vp; int oflags = FWRITE | FTRUNC | FCREAT | FOFFMAX; char *temp; + int err; /* * If the nvlist is empty (NULL), then remove the old cachefile. */ if (nvl == NULL) { - (void) vn_remove(dp->scd_path, UIO_SYSSPACE, RMFILE); - return; + err = vn_remove(dp->scd_path, UIO_SYSSPACE, RMFILE); + return (err); } /* @@ -171,11 +173,12 @@ spa_config_write(spa_config_dirent_t *dp, nvlist_t *nvl) */ (void) snprintf(temp, MAXPATHLEN, "%s.tmp", dp->scd_path); - if (vn_open(temp, UIO_SYSSPACE, oflags, 0644, &vp, CRCREAT, 0) == 0) { - if (vn_rdwr(UIO_WRITE, vp, buf, buflen, 0, UIO_SYSSPACE, - 0, RLIM64_INFINITY, kcred, NULL) == 0 && - VOP_FSYNC(vp, FSYNC, kcred, NULL) == 0) { - (void) vn_rename(temp, dp->scd_path, UIO_SYSSPACE); + err = vn_open(temp, UIO_SYSSPACE, oflags, 0644, &vp, CRCREAT, 0); + if (err == 0) { + if ((err = vn_rdwr(UIO_WRITE, vp, buf, buflen, 0, UIO_SYSSPACE, + 0, RLIM64_INFINITY, kcred, NULL)) == 0 && + (err = VOP_FSYNC(vp, FSYNC, kcred, NULL)) == 0) { + err = vn_rename(temp, dp->scd_path, UIO_SYSSPACE); } (void) VOP_CLOSE(vp, oflags, 1, 0, kcred, NULL); } @@ -184,6 +187,7 @@ spa_config_write(spa_config_dirent_t *dp, nvlist_t *nvl) kmem_free(buf, buflen); kmem_free(temp, MAXPATHLEN); + return (err); } /* @@ -195,6 +199,8 @@ spa_config_sync(spa_t *target, boolean_t removing, boolean_t postsysevent) { spa_config_dirent_t *dp, *tdp; nvlist_t *nvl; + boolean_t ccw_failure; + int error; ASSERT(MUTEX_HELD(&spa_namespace_lock)); @@ -206,6 +212,7 @@ spa_config_sync(spa_t *target, boolean_t removing, boolean_t postsysevent) * cachefile is changed, the new one is pushed onto this list, allowing * us to update previous cachefiles that no longer contain this pool. */ + ccw_failure = B_FALSE; for (dp = list_head(&target->spa_config_list); dp != NULL; dp = list_next(&target->spa_config_list, dp)) { spa_t *spa = NULL; @@ -238,10 +245,35 @@ spa_config_sync(spa_t *target, boolean_t removing, boolean_t postsysevent) mutex_exit(&spa->spa_props_lock); } - spa_config_write(dp, nvl); + error = spa_config_write(dp, nvl); + if (error != 0) { + + printf("ZFS ERROR: Update of cache file %s failed: " + "Errno %d\n", dp->scd_path, error); + ccw_failure = B_TRUE; + } + nvlist_free(nvl); } + if (ccw_failure) { + /* + * Keep trying so that configuration data is + * written if/when any temporary filesystem + * resource issues are resolved. + */ + target->spa_ccw_fail_time = ddi_get_lbolt64(); + spa_async_request(target, SPA_ASYNC_CONFIG_UPDATE); + zfs_ereport_post(FM_EREPORT_ZFS_CONFIG_CACHE_WRITE, + target, NULL, NULL, 0, 0); + } else { + /* + * Do not rate limit future attempts to update + * the config cache. + */ + target->spa_ccw_fail_time = 0; + } + /* * Remove any config entries older than the current one. */ diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa_impl.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa_impl.h index a2f15d2863f..ff3e1523dcf 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa_impl.h +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa_impl.h @@ -216,6 +216,7 @@ struct spa { int spa_vdev_locks; /* locks grabbed */ uint64_t spa_creation_version; /* version at pool creation */ uint64_t spa_prev_software_version; + int64_t spa_ccw_fail_time; /* Conf cache write fail time */ /* * spa_refcnt & spa_config_lock must be the last elements * because refcount_t changes size based on compilation options. diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/fm/fs/zfs.h b/sys/cddl/contrib/opensolaris/uts/common/sys/fm/fs/zfs.h index c752edc99bb..029af540b3c 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/sys/fm/fs/zfs.h +++ b/sys/cddl/contrib/opensolaris/uts/common/sys/fm/fs/zfs.h @@ -46,6 +46,7 @@ extern "C" { #define FM_EREPORT_ZFS_IO_FAILURE "io_failure" #define FM_EREPORT_ZFS_PROBE_FAILURE "probe_failure" #define FM_EREPORT_ZFS_LOG_REPLAY "log_replay" +#define FM_EREPORT_ZFS_CONFIG_CACHE_WRITE "config_cache_write" #define FM_EREPORT_PAYLOAD_ZFS_POOL "pool" #define FM_EREPORT_PAYLOAD_ZFS_POOL_FAILMODE "pool_failmode" From c67bfc1d2585f2be95d1ef5119f8363f2a80d2ee Mon Sep 17 00:00:00 2001 From: "Justin T. Gibbs" Date: Sun, 14 Aug 2011 23:26:32 +0000 Subject: [PATCH 05/89] Fix building kdump. The new scsi_ses.h requires and . Sponsored by: Spectra Logic Corporation --- usr.bin/kdump/mkioctls | 2 ++ 1 file changed, 2 insertions(+) diff --git a/usr.bin/kdump/mkioctls b/usr.bin/kdump/mkioctls index 7ca773dca9d..1fe9a7afb52 100644 --- a/usr.bin/kdump/mkioctls +++ b/usr.bin/kdump/mkioctls @@ -54,6 +54,8 @@ BEGIN { print "#include " print "#include " print "#include " + print "#include " + print "#include " print "" print "const char *ioctlname(u_long val);" print "" From 7cc13fcf9f2426bc7cbe99b263e8d545ec1b3a55 Mon Sep 17 00:00:00 2001 From: "Justin T. Gibbs" Date: Tue, 16 Aug 2011 04:35:42 +0000 Subject: [PATCH 06/89] Correct the rendering of device aliases that reside in subdirectories of a devfs. devfs/devfs_vnops.c: In devfs_readlink(), convert the devfs root relative path of an alias's parent, that is recorded in the alias, into a fully qualified path that includes the root of the containing devfs. This avoids the ugliness of generating a relative path by prepending "../"'s. For a non-jailed process, the "symlink root" is the devfs's mount point. For a jailed process, we must remove any jail prefix in the mount point so that our response matches the user process's world view. Sponsored by: Spectra Logic Corporation --- sys/fs/devfs/devfs_vnops.c | 45 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/sys/fs/devfs/devfs_vnops.c b/sys/fs/devfs/devfs_vnops.c index 955bd8b0f22..bf24aa8a033 100644 --- a/sys/fs/devfs/devfs_vnops.c +++ b/sys/fs/devfs/devfs_vnops.c @@ -1248,8 +1248,53 @@ static int devfs_readlink(struct vop_readlink_args *ap) { struct devfs_dirent *de; + struct cdev_priv *cdp; de = ap->a_vp->v_data; + cdp = de->de_cdp; + + if (cdp != NULL && (cdp->cdp_c.si_flags & SI_ALIAS) != 0) { + struct devfs_mount *dmp; + struct prison *pr; + char *mp; + int mp_len; + int pr_path_len; + int err; + + /* + * For device aliases, construct an absolute symlink (to + * shorten its length and avoid the ugliness of a relative + * link) by prepending the fully qualified path to the root + * of this devfs. For a non-jailed process, the devfs root + * is our mount point. For a jailed process, we must remove + * any jail prefix in our mount point so that our response + * matches the user process's world view. + */ + dmp = VFSTODEVFS(ap->a_vp->v_mount); + mp = dmp->dm_mount->mnt_stat.f_mntonname; + mp_len = strlen(mp); + + pr = ap->a_cred->cr_prison; + pr_path_len = strlen(pr->pr_path); + + if (strncmp(pr->pr_path, mp, pr_path_len) == 0 + && mp[pr_path_len] == '/') { + mp += pr_path_len; + mp_len -= pr_path_len; + } + + err = uiomove(mp, mp_len, ap->a_uio); + if (err != 0) + return (err); + + /* + * Devfs cannot be the root file system, so its + * mount point must always be terminated by a '/'. + */ + err = uiomove("/", 1, ap->a_uio); + if (err != 0) + return (err); + } return (uiomove(de->de_symlink, strlen(de->de_symlink), ap->a_uio)); } From dc20e414988cb91b199a54c0f2e61821a7aea049 Mon Sep 17 00:00:00 2001 From: "Justin T. Gibbs" Date: Tue, 16 Aug 2011 19:46:13 +0000 Subject: [PATCH 07/89] sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c: When attaching to a vdev by GUIDs, verify both the pool and vdev GUIDS instead of just the vdev GUID. Sponsored by: Spectra Logic Corporation --- .../opensolaris/uts/common/fs/zfs/vdev_geom.c | 81 +++++++++++-------- 1 file changed, 48 insertions(+), 33 deletions(-) diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c index 47c7b4af009..720e47b64b7 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c @@ -170,20 +170,26 @@ vdev_geom_detach(void *arg, int flag __unused) } } -static uint64_t -nvlist_get_guid(nvlist_t *list) +static void +nvlist_get_guids(nvlist_t *list, uint64_t *pguid, uint64_t *vguid) { nvpair_t *elem = NULL; - uint64_t value; + *vguid = 0; + *pguid = 0; while ((elem = nvlist_next_nvpair(list, elem)) != NULL) { - if (nvpair_type(elem) == DATA_TYPE_UINT64 && - strcmp(nvpair_name(elem), "guid") == 0) { - VERIFY(nvpair_value_uint64(elem, &value) == 0); - return (value); + if (nvpair_type(elem) != DATA_TYPE_UINT64) + continue; + + if (strcmp(nvpair_name(elem), ZPOOL_CONFIG_POOL_GUID) == 0) { + VERIFY(nvpair_value_uint64(elem, pguid) == 0); + } else if (strcmp(nvpair_name(elem), ZPOOL_CONFIG_GUID) == 0) { + VERIFY(nvpair_value_uint64(elem, vguid) == 0); } + + if (*pguid != 0 && *vguid != 0) + break; } - return (0); } static int @@ -221,8 +227,8 @@ vdev_geom_io(struct g_consumer *cp, int cmd, void *data, off_t offset, off_t siz return (error); } -static uint64_t -vdev_geom_read_guid(struct g_consumer *cp) +static void +vdev_geom_read_guids(struct g_consumer *cp, uint64_t *pguid, uint64_t *vguid) { struct g_provider *pp; vdev_label_t *label; @@ -230,13 +236,14 @@ vdev_geom_read_guid(struct g_consumer *cp) size_t buflen; uint64_t psize; off_t offset, size; - uint64_t guid; int error, l, len; g_topology_assert_not(); + *pguid = 0; + *vguid = 0; pp = cp->provider; - ZFS_LOG(1, "Reading guid from %s...", pp->name); + ZFS_LOG(1, "Reading guids from %s...", pp->name); psize = pp->mediasize; psize = P2ALIGN(psize, (uint64_t)sizeof(vdev_label_t)); @@ -244,7 +251,6 @@ vdev_geom_read_guid(struct g_consumer *cp) size = sizeof(*label) + pp->sectorsize - ((sizeof(*label) - 1) % pp->sectorsize) - 1; - guid = 0; label = kmem_alloc(size, KM_SLEEP); buflen = sizeof(label->vl_vdev_phys.vp_nvlist); @@ -262,16 +268,16 @@ vdev_geom_read_guid(struct g_consumer *cp) if (nvlist_unpack(buf, buflen, &config, 0) != 0) continue; - guid = nvlist_get_guid(config); + nvlist_get_guids(config, pguid, vguid); nvlist_free(config); - if (guid != 0) + if (*pguid != 0 && *vguid != 0) break; } kmem_free(label, size); - if (guid != 0) - ZFS_LOG(1, "guid for %s is %ju", pp->name, (uintmax_t)guid); - return (guid); + if (*pguid != 0 && *vguid != 0) + ZFS_LOG(1, "guids for %s are %ju:%ju", pp->name, + (uintmax_t)*pguid, (uintmax_t)*vguid); } static void @@ -283,13 +289,14 @@ vdev_geom_taste_orphan(struct g_consumer *cp) } static struct g_consumer * -vdev_geom_attach_by_guid(uint64_t guid) +vdev_geom_attach_by_guids(vdev_t *vd) { struct g_class *mp; struct g_geom *gp, *zgp; struct g_provider *pp; struct g_consumer *cp, *zcp; uint64_t pguid; + uint64_t vguid; g_topology_assert(); @@ -314,11 +321,12 @@ vdev_geom_attach_by_guid(uint64_t guid) continue; } g_topology_unlock(); - pguid = vdev_geom_read_guid(zcp); + vdev_geom_read_guids(zcp, &pguid, &vguid); g_topology_lock(); g_access(zcp, -1, 0, 0); g_detach(zcp); - if (pguid != guid) + if (pguid != spa_guid(vd->vdev_spa) || + vguid != vd->vdev_guid) continue; cp = vdev_geom_attach(pp); if (cp == NULL) { @@ -341,7 +349,7 @@ end: } static struct g_consumer * -vdev_geom_open_by_guid(vdev_t *vd) +vdev_geom_open_by_guids(vdev_t *vd) { struct g_consumer *cp; char *buf; @@ -349,8 +357,9 @@ vdev_geom_open_by_guid(vdev_t *vd) g_topology_assert(); - ZFS_LOG(1, "Searching by guid [%ju].", (uintmax_t)vd->vdev_guid); - cp = vdev_geom_attach_by_guid(vd->vdev_guid); + ZFS_LOG(1, "Searching by guids [%ju:%ju].", + (uintmax_t)spa_guid(vd->vdev_spa), (uintmax_t)vd->vdev_guid); + cp = vdev_geom_attach_by_guids(vd); if (cp != NULL) { len = strlen(cp->provider->name) + strlen("/dev/") + 1; buf = kmem_alloc(len, KM_SLEEP); @@ -359,10 +368,12 @@ vdev_geom_open_by_guid(vdev_t *vd) spa_strfree(vd->vdev_path); vd->vdev_path = buf; - ZFS_LOG(1, "Attach by guid [%ju] succeeded, provider %s.", + ZFS_LOG(1, "Attach by guids [%ju:%ju] succeeded, provider %s.", + (uintmax_t)spa_guid(vd->vdev_spa), (uintmax_t)vd->vdev_guid, vd->vdev_path); } else { - ZFS_LOG(1, "Search by guid [%ju] failed.", + ZFS_LOG(1, "Search by guids [%ju:%ju] failed.", + (uintmax_t)spa_guid(vd->vdev_spa), (uintmax_t)vd->vdev_guid); } @@ -374,7 +385,8 @@ vdev_geom_open_by_path(vdev_t *vd, int check_guid) { struct g_provider *pp; struct g_consumer *cp; - uint64_t guid; + uint64_t pguid; + uint64_t vguid; g_topology_assert(); @@ -386,16 +398,19 @@ vdev_geom_open_by_path(vdev_t *vd, int check_guid) if (cp != NULL && check_guid && ISP2(pp->sectorsize) && pp->sectorsize <= VDEV_PAD_SIZE) { g_topology_unlock(); - guid = vdev_geom_read_guid(cp); + vdev_geom_read_guids(cp, &pguid, &vguid); g_topology_lock(); - if (guid != vd->vdev_guid) { + if (pguid != spa_guid(vd->vdev_spa) || + vguid != vd->vdev_guid) { vdev_geom_detach(cp, 0); cp = NULL; ZFS_LOG(1, "guid mismatch for provider %s: " - "%ju != %ju.", vd->vdev_path, - (uintmax_t)vd->vdev_guid, (uintmax_t)guid); + "%ju:%ju != %ju:%ju.", vd->vdev_path, + (uintmax_t)spa_guid(vd->vdev_spa), + (uintmax_t)vd->vdev_guid, + (uintmax_t)pguid, (uintmax_t)vguid); } else { - ZFS_LOG(1, "guid match for provider %s.", + ZFS_LOG(1, "guids match for provider %s.", vd->vdev_path); } } @@ -442,7 +457,7 @@ vdev_geom_open(vdev_t *vd, uint64_t *psize, uint64_t *ashift) * moved around so try all other GEOM providers * to find one with the right guid. */ - cp = vdev_geom_open_by_guid(vd); + cp = vdev_geom_open_by_guids(vd); } } From 6f575aa4c2a81d43f088a6cffc301a898e62d17d Mon Sep 17 00:00:00 2001 From: "Justin T. Gibbs" Date: Tue, 16 Aug 2011 20:29:03 +0000 Subject: [PATCH 08/89] Modify the geom vdev provider's open behavior so that it will only unconditionally open a device by path if the open is part of a pool create, pool split, or device add operation, and a search of all known geom provider's label data doesn't yield a device with matching pool and vdev GUIDs. This fixes a bug where the wrong disk could be associated with a vdev's configuration data when device devfs paths change due to insert and remove events. While, ZFS detects this kind of coding mixup and immediately flags the device as faulted before the confusion can cause permanent data loss, a reboot was necessary in order to resurrect the configuration. sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c: Modify the open behavior to: - Open by recorded device path with GUID matching - If that fails, search all geom providers for a device with matching GUIDs. - If that fails and we are opening a "new to a pool configuration" vdev, open by path. - Otherwise fail the open. Sponsored by: Spectra Logic Corporation --- .../opensolaris/uts/common/fs/zfs/vdev_geom.c | 44 ++++++++++++------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c index 720e47b64b7..b42ffa1b737 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c @@ -442,23 +442,37 @@ vdev_geom_open(vdev_t *vd, uint64_t *psize, uint64_t *ashift) error = 0; /* - * If we're creating or splitting a pool, just find the GEOM provider - * by its name and ignore GUID mismatches. + * Try using the recorded path for this device, but only + * accept it if its label data contains the expected GUIDs. */ - if (vd->vdev_spa->spa_load_state == SPA_LOAD_NONE || - vd->vdev_spa->spa_splitting_newspa == B_TRUE) + cp = vdev_geom_open_by_path(vd, 1); + if (cp == NULL) { + /* + * The device at vd->vdev_path doesn't have the + * expected GUIDs. The disks might have merely + * moved around so try all other GEOM providers + * to find one with the right GUIDs. + */ + cp = vdev_geom_open_by_guids(vd); + } + + if (cp == NULL && + ((vd->vdev_prevstate == VDEV_STATE_UNKNOWN && + vd->vdev_spa->spa_load_state == SPA_LOAD_NONE) || + vd->vdev_spa->spa_splitting_newspa == B_TRUE)) { + /* + * We are dealing with a vdev that hasn't been previosly + * opened (since boot), and we are not loading an + * existing pool configuration (e.g. this operations is + * an add of a vdev to new or * existing pool) or we are + * in the process of splitting a pool. Find the GEOM + * provider by its name, ignoring GUID mismatches. + * + * XXPOLICY: It would be safer to only allow a device + * that is unlabeled or labeled but missing + * GUID information to be opened in this fashion. + */ cp = vdev_geom_open_by_path(vd, 0); - else { - cp = vdev_geom_open_by_path(vd, 1); - if (cp == NULL) { - /* - * The device at vd->vdev_path doesn't have the - * expected guid. The disks might have merely - * moved around so try all other GEOM providers - * to find one with the right guid. - */ - cp = vdev_geom_open_by_guids(vd); - } } if (cp == NULL) { From de8574f84e972112e75e6b07e8444aaa528e9ae1 Mon Sep 17 00:00:00 2001 From: "Justin T. Gibbs" Date: Tue, 16 Aug 2011 22:20:45 +0000 Subject: [PATCH 09/89] Close several race conditions in the ZFS vdev geom module, most triggered by concurrent ZFS reprobe and GEOM orphan processing. sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c: o Make vdev_geom_close() synchronous instead of deferring its work to a GEOM event handler. This prevents a future open call from referencing a consumer that is scheduled for destruction. o Move initialization of the consumer private pointer (which references the ZFS vdev object using this consumer) into vdev_geom_attach(). This guarantees that an orphan event received during the small windows where the topology lock is dropped in open processing, is effective in marking a vdev as needing to be removed. o Move clearing of the consumer private pointer from vdev_geom_close() into vdev_geom_detach(). When vdev_geom_open() fails, vdev_geom_detach is called directly, which used to bypass this necessary step. o Modify vdev_geom_orphan handler to ignore a consumer that that is no longer associated with a vdev. Sponsored by: Spectra Logic Corporation --- .../opensolaris/uts/common/fs/zfs/vdev_geom.c | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c index b42ffa1b737..d59c5b69fd4 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c @@ -64,6 +64,10 @@ vdev_geom_orphan(struct g_consumer *cp) g_topology_assert(); vd = cp->private; + if (vd == NULL) { + /* Vdev close in progress. Ignore the event. */ + return; + } /* * Orphan callbacks occur from the GEOM event thread. @@ -85,7 +89,7 @@ vdev_geom_orphan(struct g_consumer *cp) } static struct g_consumer * -vdev_geom_attach(struct g_provider *pp) +vdev_geom_attach(struct g_provider *pp, vdev_t *vd) { struct g_geom *gp; struct g_consumer *cp; @@ -140,20 +144,27 @@ vdev_geom_attach(struct g_provider *pp) ZFS_LOG(1, "Used existing consumer for %s.", pp->name); } } + cp->private = vd; return (cp); } static void -vdev_geom_detach(void *arg, int flag __unused) +vdev_geom_detach(void *arg) { struct g_geom *gp; struct g_consumer *cp; + vdev_t *vd; g_topology_assert(); cp = arg; gp = cp->geom; ZFS_LOG(1, "Closing access to %s.", cp->provider->name); + vd = cp->private; + if (vd != NULL) { + vd->vdev_tsd = NULL; + cp->private = NULL; + } g_access(cp, -1, 0, -1); /* Destroy consumer on last close. */ if (cp->acr == 0 && cp->ace == 0) { @@ -328,7 +339,7 @@ vdev_geom_attach_by_guids(vdev_t *vd) if (pguid != spa_guid(vd->vdev_spa) || vguid != vd->vdev_guid) continue; - cp = vdev_geom_attach(pp); + cp = vdev_geom_attach(pp, vd); if (cp == NULL) { printf("ZFS WARNING: Unable to attach to %s.\n", pp->name); @@ -394,7 +405,7 @@ vdev_geom_open_by_path(vdev_t *vd, int check_guid) pp = g_provider_by_name(vd->vdev_path + sizeof("/dev/") - 1); if (pp != NULL) { ZFS_LOG(1, "Found provider by name %s.", vd->vdev_path); - cp = vdev_geom_attach(pp); + cp = vdev_geom_attach(pp, vd); if (cp != NULL && check_guid && ISP2(pp->sectorsize) && pp->sectorsize <= VDEV_PAD_SIZE) { g_topology_unlock(); @@ -402,7 +413,7 @@ vdev_geom_open_by_path(vdev_t *vd, int check_guid) g_topology_lock(); if (pguid != spa_guid(vd->vdev_spa) || vguid != vd->vdev_guid) { - vdev_geom_detach(cp, 0); + vdev_geom_detach(cp); cp = NULL; ZFS_LOG(1, "guid mismatch for provider %s: " "%ju:%ju != %ju:%ju.", vd->vdev_path, @@ -482,7 +493,7 @@ vdev_geom_open(vdev_t *vd, uint64_t *psize, uint64_t *ashift) !ISP2(cp->provider->sectorsize)) { ZFS_LOG(1, "Provider %s has unsupported sectorsize.", vd->vdev_path); - vdev_geom_detach(cp, 0); + vdev_geom_detach(cp); error = EINVAL; cp = NULL; } else if (cp->acw == 0 && (spa_mode(vd->vdev_spa) & FWRITE) != 0) { @@ -499,10 +510,11 @@ vdev_geom_open(vdev_t *vd, uint64_t *psize, uint64_t *ashift) if (error != 0) { printf("ZFS WARNING: Unable to open %s for writing (error=%d).\n", vd->vdev_path, error); - vdev_geom_detach(cp, 0); + vdev_geom_detach(cp); cp = NULL; } } + g_topology_unlock(); PICKUP_GIANT(); if (cp == NULL) { @@ -510,7 +522,6 @@ vdev_geom_open(vdev_t *vd, uint64_t *psize, uint64_t *ashift) return (error); } - cp->private = vd; vd->vdev_tsd = cp; pp = cp->provider; @@ -547,9 +558,9 @@ vdev_geom_close(vdev_t *vd) cp = vd->vdev_tsd; if (cp == NULL) return; - vd->vdev_tsd = NULL; - vd->vdev_delayed_close = B_FALSE; - g_post_event(vdev_geom_detach, cp, M_WAITOK, NULL); + g_topology_lock(); + vdev_geom_detach(cp); + g_topology_unlock(); } static void From 8ec058257bb4c557fa942619fd8738a55d7e912f Mon Sep 17 00:00:00 2001 From: "Justin T. Gibbs" Date: Tue, 16 Aug 2011 22:33:05 +0000 Subject: [PATCH 10/89] sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c: Subscribe to attribute change notifications and update vdev physical path information (in core and on disk) when a GEOM::physpath event indicates they have changed. Sponsored by: Spectra Logic Corporation --- .../opensolaris/uts/common/fs/zfs/vdev_geom.c | 68 ++++++++++++++++--- 1 file changed, 60 insertions(+), 8 deletions(-) diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c index d59c5b69fd4..eb16d3e6e7e 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c @@ -88,6 +88,59 @@ vdev_geom_orphan(struct g_consumer *cp) spa_async_request(vd->vdev_spa, SPA_ASYNC_REMOVE); } +static void +vdev_geom_attrchanged(struct g_consumer *cp, const char *attr) +{ + vdev_t *vd; + spa_t *spa; + char *physpath; + int error, physpath_len; + + g_topology_assert(); + + if (strcmp(attr, "GEOM::physpath") != 0) + return; + + if (g_access(cp, 1, 0, 0) != 0) + return; + + /* + * Record/Update physical path information for this device. + */ + vd = cp->private; + spa = vd->vdev_spa; + physpath_len = MAXPATHLEN; + physpath = g_malloc(physpath_len, M_WAITOK|M_ZERO); + error = g_io_getattr("GEOM::physpath", cp, &physpath_len, physpath); + g_access(cp, -1, 0, 0); + if (error == 0) { + char *old_physpath; + + old_physpath = vd->vdev_physpath; + vd->vdev_physpath = spa_strdup(physpath); + spa_async_request(spa, SPA_ASYNC_CONFIG_UPDATE); + + if (old_physpath != NULL) { + int held_lock; + + held_lock = spa_config_held(spa, SCL_STATE, RW_WRITER); + if (held_lock == 0) { + g_topology_unlock(); + spa_config_enter(spa, SCL_STATE, FTAG, + RW_WRITER); + } + + spa_strfree(old_physpath); + + if (held_lock == 0) { + spa_config_exit(spa, SCL_STATE, FTAG); + g_topology_lock(); + } + } + } + g_free(physpath); +} + static struct g_consumer * vdev_geom_attach(struct g_provider *pp, vdev_t *vd) { @@ -108,6 +161,7 @@ vdev_geom_attach(struct g_provider *pp, vdev_t *vd) if (gp == NULL) { gp = g_new_geomf(&zfs_vdev_class, "zfs::vdev"); gp->orphan = vdev_geom_orphan; + gp->attrchanged = vdev_geom_attrchanged; cp = g_new_consumer(gp); if (g_attach(cp, pp) != 0) { g_wither_geom(gp, ENXIO); @@ -144,7 +198,12 @@ vdev_geom_attach(struct g_provider *pp, vdev_t *vd) ZFS_LOG(1, "Used existing consumer for %s.", pp->name); } } + cp->private = vd; + + /* Fetch initial physical path information for this device. */ + vdev_geom_attrchanged(cp, "GEOM::physpath"); + return (cp); } @@ -521,9 +580,8 @@ vdev_geom_open(vdev_t *vd, uint64_t *psize, uint64_t *ashift) vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED; return (error); } - - vd->vdev_tsd = cp; pp = cp->provider; + vd->vdev_tsd = cp; /* * Determine the actual size of the device. @@ -541,12 +599,6 @@ vdev_geom_open(vdev_t *vd, uint64_t *psize, uint64_t *ashift) */ vd->vdev_nowritecache = B_FALSE; - if (vd->vdev_physpath != NULL) - spa_strfree(vd->vdev_physpath); - bufsize = sizeof("/dev/") + strlen(pp->name); - vd->vdev_physpath = kmem_alloc(bufsize, KM_SLEEP); - snprintf(vd->vdev_physpath, bufsize, "/dev/%s", pp->name); - return (0); } From 1adecf7a539cc262f3f61af37f1469f558daabd3 Mon Sep 17 00:00:00 2001 From: "Justin T. Gibbs" Date: Tue, 16 Aug 2011 23:47:53 +0000 Subject: [PATCH 11/89] Add ZFSD, a ZFS fault management daemon. This daemon has the following features: o When a vdev for an active pool is inserted into the system, it will re-integrate it with the pool. o When an unlabeled or inactive disk is inserted into the same physical location as a missing member of a pool with the "autoreplace" attribute set, the inserted disk will be used to replace the missing disk. o When the cumulative soft-error count for a vdev exceeds 50 errors, the vdev will be marked degraded, alerting users to a potential problem. The error counts are persisted across reboots. The daemon is written to be easily extended for more advanced fault management policies and to handle new features such as spare pool management. cddl/sbin/zfsd/zpool_list.cc: cddl/sbin/zfsd/zpool_list.h: ZpoolList is a standard container allowing filtering and iteration of imported ZFS pool information. cddl/sbin/zfsd/callout.cc: cddl/sbin/zfsd/callout.h: Timer services built on top of the POSIX interval timer API. cddl/sbin/zfsd/vdev.cc: cddl/sbin/zfsd/vdev.h: Wrapper class used to provide easy access to Vdev nvlist data. cddl/sbin/zfsd/zfsd.cc: cddl/sbin/zfsd/zfsd.h: Daemon main, devctl socket handling, and global application state exported through the ZfsDaemon singleton. cddl/sbin/zfsd/case_file.cc: cddl/sbin/zfsd/case_file.h: CaseFile objects aggregate vdev faults that may require ZFSD action in order to maintain the health of a ZFS pool. They also handle serialization/deserialization of fault data to persistent storage. cddl/sbin/zfsd/vdev_iterator.cc: cddl/sbin/zfsd/vdev_iterator.h: Helper class for traversing and finding vdev objects within a pool configuration. cddl/sbin/zfsd/dev_ctl_event.cc: cddl/sbin/zfsd/dev_ctl_event.h: Class hierarchy used to express events received via the devctl API. cddl/sbin/zfsd/zfsd_exception.cc: cddl/sbin/zfsd/zfsd_exception.h: Definition of exceptions explicitly thrown by ZFSD. cddl/sbin/zfsd/Makefile: cddl/sbin/Makefile: Add zfsd to the build. etc/rc.d/zfsd: Rc script for ZFSD. etc/defaults/rc.conf: ZFSD defaults, just like ZFS, to being disabled. etc/mtree/BSD.root.dist: Create the etc/zfs/cases directory used to store persistent fault data. Sponsored by: Spectra Logic Corporation --- cddl/sbin/Makefile | 5 +- cddl/sbin/zfsd/Makefile | 49 +++ cddl/sbin/zfsd/callout.cc | 162 +++++++ cddl/sbin/zfsd/callout.h | 170 ++++++++ cddl/sbin/zfsd/case_file.cc | 684 ++++++++++++++++++++++++++++++ cddl/sbin/zfsd/case_file.h | 344 +++++++++++++++ cddl/sbin/zfsd/dev_ctl_event.cc | 698 +++++++++++++++++++++++++++++++ cddl/sbin/zfsd/dev_ctl_event.h | 506 ++++++++++++++++++++++ cddl/sbin/zfsd/vdev.cc | 154 +++++++ cddl/sbin/zfsd/vdev.h | 136 ++++++ cddl/sbin/zfsd/vdev_iterator.cc | 141 +++++++ cddl/sbin/zfsd/vdev_iterator.h | 116 +++++ cddl/sbin/zfsd/zfsd.cc | 637 ++++++++++++++++++++++++++++ cddl/sbin/zfsd/zfsd.h | 376 +++++++++++++++++ cddl/sbin/zfsd/zfsd_exception.cc | 157 +++++++ cddl/sbin/zfsd/zfsd_exception.h | 158 +++++++ cddl/sbin/zfsd/zpool_list.cc | 98 +++++ cddl/sbin/zfsd/zpool_list.h | 126 ++++++ etc/defaults/rc.conf | 4 + etc/mtree/BSD.root.dist | 2 + etc/rc.d/zfsd | 17 + 21 files changed, 4739 insertions(+), 1 deletion(-) create mode 100644 cddl/sbin/zfsd/Makefile create mode 100644 cddl/sbin/zfsd/callout.cc create mode 100644 cddl/sbin/zfsd/callout.h create mode 100644 cddl/sbin/zfsd/case_file.cc create mode 100644 cddl/sbin/zfsd/case_file.h create mode 100644 cddl/sbin/zfsd/dev_ctl_event.cc create mode 100644 cddl/sbin/zfsd/dev_ctl_event.h create mode 100644 cddl/sbin/zfsd/vdev.cc create mode 100644 cddl/sbin/zfsd/vdev.h create mode 100644 cddl/sbin/zfsd/vdev_iterator.cc create mode 100644 cddl/sbin/zfsd/vdev_iterator.h create mode 100644 cddl/sbin/zfsd/zfsd.cc create mode 100644 cddl/sbin/zfsd/zfsd.h create mode 100644 cddl/sbin/zfsd/zfsd_exception.cc create mode 100644 cddl/sbin/zfsd/zfsd_exception.h create mode 100644 cddl/sbin/zfsd/zpool_list.cc create mode 100644 cddl/sbin/zfsd/zpool_list.h create mode 100644 etc/rc.d/zfsd diff --git a/cddl/sbin/Makefile b/cddl/sbin/Makefile index f74307c5fd6..8f7d8acf71a 100644 --- a/cddl/sbin/Makefile +++ b/cddl/sbin/Makefile @@ -2,11 +2,14 @@ .include -SUBDIR= ${_zfs} ${_zpool} +SUBDIR= ${_zfs} ${_zpool} ${_zfsd} .if ${MK_ZFS} != "no" _zfs= zfs _zpool= zpool +. if ${MK_CXX} != "no" +_zfsd= zfsd +. endif .endif .include diff --git a/cddl/sbin/zfsd/Makefile b/cddl/sbin/zfsd/Makefile new file mode 100644 index 00000000000..7d9519c4928 --- /dev/null +++ b/cddl/sbin/zfsd/Makefile @@ -0,0 +1,49 @@ +# $FreeBSD$ + +PROG_CXX= zfsd +SRCS= callout.cc \ + case_file.cc \ + dev_ctl_event.cc \ + vdev.cc \ + vdev_iterator.cc \ + zfsd.cc \ + zfsd_exception.cc \ + zpool_list.cc + +NO_MAN= YES + +WARNS?= 0 + +INCFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/lib/libzpool/common +INCFLAGS+= -I${.CURDIR}/../../../cddl/compat/opensolaris/include +INCFLAGS+= -I${.CURDIR}/../../../cddl/compat/opensolaris/lib/libumem +INCFLAGS+= -I${.CURDIR}/../../../sys/cddl/compat/opensolaris +INCFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/head +INCFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/lib/libuutil/common +INCFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/lib/libumem/common +INCFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/lib/libzfs/common +INCFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/lib/libnvpair +INCFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/common/zfs +INCFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common +INCFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common/fs/zfs +INCFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common/sys + +CFLAGS= -g -DNEED_SOLARIS_BOOLEAN ${INCFLAGS} + +#NO_SHARED?= YES + +DPADD= ${LIBZFS} ${LIBUTIL} ${LIBGEOM} ${LIBBSDXML} ${LIBSBUF} \ + ${LIBNVPAIR} ${LIBUUTIL} +LDADD= -lzfs -lutil -lgeom -lbsdxml -lsbuf -lnvpair -luutil + +#DPADD= ${LIBAVL} ${LIBZFS} ${LIBGEOM} ${LIBBSDXML} ${LIBSBUF} \ +# ${LIBM} ${LIBNVPAIR} ${LIBUUTIL} ${LIBUTIL} +#LDADD= -lavl -lzfs -lgeom -lbsdxml -lsbuf \ +# -lm -lnvpair -luutil -lutil + +cscope: + find ${.CURDIR} -type f -a \( -name "*.[ch]" -o -name "*.cc" \) \ + > ${.CURDIR}/cscope.files + cd ${.CURDIR} && cscope -buq ${INCFLAGS} + +.include diff --git a/cddl/sbin/zfsd/callout.cc b/cddl/sbin/zfsd/callout.cc new file mode 100644 index 00000000000..1ef16317d3a --- /dev/null +++ b/cddl/sbin/zfsd/callout.cc @@ -0,0 +1,162 @@ +/*- + * Copyright (c) 2011 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + */ + +#include +#include + +#include "callout.h" +#include "zfsd.h" +#include "zfsd_exception.h" + +std::list Callout::s_activeCallouts; +bool Callout::s_alarmFired(false); + +void +Callout::Init() +{ + signal(SIGALRM, Callout::AlarmSignalHandler); +} + +inline bool +Callout::Stop() +{ + if (!IsPending()) + return (false); + + for (std::list::iterator it(s_activeCallouts.begin()); + it != s_activeCallouts.end(); it++) { + if (*it != this) + continue; + + it = s_activeCallouts.erase(it); + if (it != s_activeCallouts.end()) { + + /* + * Maintain correct interval for the + * callouts that follow the just removed + * entry. + */ + timeradd(&(*it)->m_interval, &m_interval, + &(*it)->m_interval); + } + break; + } + m_pending = false; + return (true); +} + +bool +Callout::Reset(const timeval &interval, CalloutFunc_t *func, void *arg) +{ + bool cancelled(false); + + if (!timerisset(&interval)) + throw ZfsdException("Callout::Reset: interval of 0"); + + cancelled = Stop(); + + m_interval = interval; + m_func = func; + m_arg = arg; + m_pending = true; + + std::list::iterator it(s_activeCallouts.begin()); + for (; it != s_activeCallouts.end(); it++) { + + if (timercmp(&(*it)->m_interval, &m_interval, <=)) { + /* + * Decrease our interval by those that come + * before us. + */ + timersub(&m_interval, &(*it)->m_interval, &m_interval); + } else { + /* + * Account for the time between the newly + * inserted event and those that follow. + */ + timersub(&(*it)->m_interval, &m_interval, + &(*it)->m_interval); + break; + } + } + s_activeCallouts.insert(it, this); + + + if (s_activeCallouts.front() == this) { + itimerval timerval = { {0, 0}, m_interval }; + + setitimer(ITIMER_REAL, &timerval, NULL); + } + + return (cancelled); +} + +void +Callout::AlarmSignalHandler(int) +{ + s_alarmFired = true; + ZfsDaemon::WakeEventLoop(); +} + +void +Callout::ExpireCallouts() +{ + if (!s_alarmFired) + return; + + s_alarmFired = false; + if (s_activeCallouts.empty()) { + /* Callout removal/SIGALRM race was lost. */ + return; + } + + /* + * Expire the first callout (the one we used to set the + * interval timer) as well as any callouts following that + * expire at the same time (have a zero interval from + * the callout before it). + */ + do { + Callout *cur(s_activeCallouts.front()); + s_activeCallouts.pop_front(); + cur->m_pending = false; + cur->m_func(cur->m_arg); + } while (!s_activeCallouts.empty() + && timerisset(&s_activeCallouts.front()->m_interval) == 0); + + if (!s_activeCallouts.empty()) { + Callout *next(s_activeCallouts.front()); + itimerval timerval = { { 0, 0 }, next->m_interval }; + + setitimer(ITIMER_REAL, &timerval, NULL); + } +} diff --git a/cddl/sbin/zfsd/callout.h b/cddl/sbin/zfsd/callout.h new file mode 100644 index 00000000000..1c98f43d885 --- /dev/null +++ b/cddl/sbin/zfsd/callout.h @@ -0,0 +1,170 @@ +/*- + * Copyright (c) 2011 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + */ + +/** + * \file callout.h + * + * \brief Interface for timer based callback services. + */ + +#ifndef _CALLOUT_H_ +#define _CALLOUT_H_ + +#include + +#include + +/** + * \brief Type of the function callback from a Callout. + */ +typedef void CalloutFunc_t(void *); + +/** + * \brief Interface to a schedulable one-shot timer with the granlarity + * of the system clock (see setitimer(2)). + * + * Determination of callback expiration is triggered by the SIGALRM + * signal. Callout callbacks are always delivered from Zfsd's event + * processing loop. + * + * Periodic actions can be triggered via the Callout mechanisms by + * resetting the Callout from within its callback. + */ +class Callout +{ +public: + + /** + * Initialize the Callout subsystem. + */ + static void Init(); + + /** + * Function called (via SIGALRM) when our interval + * timer expires. + */ + static void AlarmSignalHandler(int); + + /** + * Execute callbacks for all callouts that have the same + * expiration time as the first callout in the list. + */ + static void ExpireCallouts(); + + /** Constructor. */ + Callout(); + + /** + * returns true if callout has not been stopped, + * or deactivated since the last time the callout was + * reset. + */ + bool IsActive() const; + + /** + * Returns true if callout is still waiting to expire. + */ + bool IsPending() const; + + /** + * Disestablish a callout. + */ + bool Stop(); + + /** + * \brief Establish or change a timeout. + * + * \param interval Timeval indicating the time which must elapse + * before this callout fires. + * \param func Pointer to the callback funtion + * \param arg Argument pointer to pass to callback function + * + * \return Cancelation status. + * true: The previous callback was pending and therfore + * was cancelled. + * false: The callout was not pending at the time of this + * reset request. + * In all cases, a new callout is established. + */ + bool Reset(const timeval &interval, CalloutFunc_t *func, void *arg); + +private: + /** + * All active callouts sorted by expiration time. The callout + * with the nearest expiration time is at the head of the list. + */ + static std::list s_activeCallouts; + + /** + * The interval timer has expired. This variable is set from + * signal handler context and tested from Zfsd::EventLoop() + * context via ExpireCallouts(). + */ + static bool s_alarmFired; + + /** + * Time, realtive to others in the active list, until + * this callout is fired. + */ + timeval m_interval; + + /** Callback function argument. */ + void *m_arg; + + /** + * The callback function associated with this timer + * entry. + */ + CalloutFunc_t *m_func; + + /** State of this callout. */ + bool m_pending; +}; + +//- Callout public const methods ---------------------------------------------- +inline bool +Callout::IsPending() const +{ + return (m_pending); +} + +//- Callout public methods ---------------------------------------------------- +inline +Callout::Callout() + : m_arg(0), + m_func(NULL), + m_pending(false) +{ + timerclear(&m_interval); +} + +#endif /* CALLOUT_H_ */ diff --git a/cddl/sbin/zfsd/case_file.cc b/cddl/sbin/zfsd/case_file.cc new file mode 100644 index 00000000000..8d151b78ab0 --- /dev/null +++ b/cddl/sbin/zfsd/case_file.cc @@ -0,0 +1,684 @@ +/*- + * Copyright (c) 2011 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + */ + +/** + * \file case_file.cc + * + * We keep case files for any leaf vdev that is not in the optimal state. + * However, we only serialize to disk those events that need to be preserved + * across reboots. For now, this is just a log of soft errors which we + * accumulate in order to mark a device as degraded. + */ +#include +#include +#include +#include +#include + +#include "case_file.h" +#include "vdev.h" +#include "zfsd.h" +#include "zfsd_exception.h" +#include "zpool_list.h" + +/*============================ Namespace Control =============================*/ +using std::auto_ptr; +using std::hex; +using std::stringstream; +using std::setfill; +using std::setw; + +/*--------------------------------- CaseFile ---------------------------------*/ +//- CaseFile Static Data ------------------------------------------------------- +CaseFileList CaseFile::s_activeCases; +const string CaseFile::s_caseFilePath = "/etc/zfs/cases"; +const timeval CaseFile::s_removeGracePeriod = { 60 /*sec*/, 0 /*usec*/}; + +//- CaseFile Static Public Methods --------------------------------------------- +CaseFile * +CaseFile::Find(uint64_t poolGUID, uint64_t vdevGUID) +{ + for (CaseFileList::iterator curCase = s_activeCases.begin(); + curCase != s_activeCases.end(); curCase++) { + + if ((*curCase)->PoolGUID() != poolGUID + || (*curCase)->VdevGUID() != vdevGUID) + continue; + + /* + * We only carry one active case per-vdev. + */ + return (*curCase); + } + return (NULL); +} + +CaseFile * +CaseFile::Find(const string &physPath) +{ + for (CaseFileList::iterator curCase = s_activeCases.begin(); + curCase != s_activeCases.end(); curCase++) { + + if ((*curCase)->PhysicalPath() != physPath) + continue; + + return (*curCase); + } + return (NULL); +} + +CaseFile & +CaseFile::Create(Vdev &vdev) +{ + CaseFile *activeCase; + + activeCase = Find(vdev.PoolGUID(), vdev.GUID()); + if (activeCase == NULL) + activeCase = new CaseFile(vdev); + + return (*activeCase); +} + +void +CaseFile::DeSerialize() +{ + struct dirent **caseFiles; + + int numCaseFiles(scandir(s_caseFilePath.c_str(), &caseFiles, + DeSerializeSelector, /*compar*/NULL)); + + if (numCaseFiles == 0 || numCaseFiles == -1) + return; + + for (int i = 0; i < numCaseFiles; i++) { + + DeSerializeFile(caseFiles[i]->d_name); + free(caseFiles[i]); + } + free(caseFiles); +} + +void +CaseFile::LogAll() +{ + for (CaseFileList::iterator curCase = s_activeCases.begin(); + curCase != s_activeCases.end(); curCase++) + (*curCase)->Log(); +} + +void +CaseFile::PurgeAll() +{ + /* CaseFiles remove themselves from this list on destruction. */ + while (s_activeCases.size() != 0) + delete s_activeCases.front(); +} + +//- CaseFile Public Methods ---------------------------------------------------- +bool +CaseFile::RefreshVdevState() +{ + ZpoolList zpl(ZpoolList::ZpoolByGUID, &m_poolGUID); + if (zpl.empty()) { + syslog(LOG_INFO, + "CaseFile::RefreshVdevState: Unknown pool for " + "Vdev(%ju,%ju).\n", + m_poolGUID, m_vdevGUID); + return (false); + } + + zpool_handle_t *casePool(zpl.front()); + nvlist_t *vdevConfig = VdevIterator(casePool).Find(VdevGUID()); + if (vdevConfig == NULL) { + syslog(LOG_INFO, + "CaseFile::RefreshVdevState: Unknown Vdev(%s,%s).\n", + PoolGUIDString().c_str(), PoolGUIDString().c_str()); + return (false); + } + Vdev caseVdev(casePool, vdevConfig); + + m_vdevState = caseVdev.State(); + m_vdevPhysPath = caseVdev.PhysicalPath(); + return (true); +} + +bool +CaseFile::ReEvaluate(const string &devPath, const string &physPath, Vdev *vdev) +{ + ZpoolList zpl(ZpoolList::ZpoolByGUID, &m_poolGUID); + + if (zpl.empty() || !RefreshVdevState()) { + /* + * The pool or vdev for this case file is no longer + * part of the configuration. This can happen + * if we process a device arrival notification + * before seeing the ZFS configuration change + * event. + */ + syslog(LOG_INFO, + "CaseFile::ReEvaluate(%s,%s) Pool/Vdev unconfigured. " + "Closing\n", + PoolGUIDString().c_str(), + VdevGUIDString().c_str()); + Close(); + + /* + * Since this event was not used to close this + * case, do not report it as consumed. + */ + return (/*consumed*/false); + } + zpool_handle_t *pool(zpl.front()); + + if (VdevState() > VDEV_STATE_CANT_OPEN) { + /* + * For now, newly discovered devices only help for + * devices that are missing. In the future, we might + * use a newly inserted spare to replace a degraded + * or faulted device. + */ + return (false); + } + + if (vdev != NULL + && vdev->PoolGUID() == m_poolGUID + && vdev->GUID() == m_vdevGUID) { + + zpool_vdev_online(pool, vdev->GUIDString().c_str(), + ZFS_ONLINE_CHECKREMOVE | ZFS_ONLINE_UNSPARE, + &m_vdevState); + syslog(LOG_INFO, "Onlined vdev(%s/%s:%s). State now %s.\n", + zpool_get_name(pool), vdev->GUIDString().c_str(), + devPath.c_str(), + zpool_state_to_name(VdevState(), VDEV_AUX_NONE)); + + /* + * Check the vdev state post the online action to see + * if we can retire this case. + */ + CloseIfSolved(); + + return (/*consumed*/true); + } + + /* + * If the auto-replace policy is enabled, and we have physical + * path information, try a physical path replacement. + */ + if (zpool_get_prop_int(pool, ZPOOL_PROP_AUTOREPLACE, NULL) == 0) { + syslog(LOG_INFO, + "CaseFile(%s:%s:%s): AutoReplace not set. " + "Ignoring device insertion.\n", + PoolGUIDString().c_str(), + VdevGUIDString().c_str(), + zpool_state_to_name(VdevState(), VDEV_AUX_NONE)); + return (false); + } + + if (PhysicalPath().empty()) { + syslog(LOG_INFO, + "CaseFile(%s:%s:%s): No vdev physical path information. " + "Ignoring device insertion.\n", + PoolGUIDString().c_str(), + VdevGUIDString().c_str(), + zpool_state_to_name(VdevState(), VDEV_AUX_NONE)); + return (false); + } + + if (physPath != PhysicalPath()) { + syslog(LOG_INFO, + "CaseFile(%s:%s:%s): Physical path mismatch. " + "Ignoring device insertion.\n", + PoolGUIDString().c_str(), + VdevGUIDString().c_str(), + zpool_state_to_name(VdevState(), VDEV_AUX_NONE)); + return (false); + } + + /* Write a label on the newly inserted disk. */ + if (zpool_label_disk(g_zfsHandle, pool, devPath.c_str()) != 0) { + syslog(LOG_ERR, + "Replace vdev(%s/%s) by physical path (label): %s: %s\n", + zpool_get_name(pool), VdevGUIDString().c_str(), + libzfs_error_action(g_zfsHandle), + libzfs_error_description(g_zfsHandle)); + return (/*consumed*/false); + } + + /* + * Build a root vdev/leaf vdev configuration suitable for + * zpool_vdev_attach. Only enough data for the kernel to find + * the device (i.e. type and disk device node path) are needed. + */ + nvlist_t *nvroot(NULL); + nvlist_t *newvd(NULL); + if (nvlist_alloc(&nvroot, NV_UNIQUE_NAME, 0) != 0 + || nvlist_alloc(&newvd, NV_UNIQUE_NAME, 0) != 0) { + syslog(LOG_ERR, "Replace vdev(%s/%s) by physical path: " + "Unable to allocate configuration data.\n", + zpool_get_name(pool), VdevGUIDString().c_str()); + if (nvroot != NULL) + nvlist_free(nvroot); + return (/*consumed*/false); + } + + if (nvlist_add_string(newvd, ZPOOL_CONFIG_TYPE, VDEV_TYPE_DISK) != 0 + || nvlist_add_string(newvd, ZPOOL_CONFIG_PATH, devPath.c_str()) != 0 + || nvlist_add_string(nvroot, ZPOOL_CONFIG_TYPE, VDEV_TYPE_ROOT) != 0 + || nvlist_add_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN, + &newvd, 1) != 0) { + syslog(LOG_ERR, "Replace vdev(%s/%s) by physical path: " + "Unable to initialize configuration data.\n", + zpool_get_name(pool), VdevGUIDString().c_str()); + nvlist_free(newvd); + nvlist_free(nvroot); + return (1); + } + + /* Data was copied when added to the root vdev. */ + nvlist_free(newvd); + + if (zpool_vdev_attach(pool, VdevGUIDString().c_str(), + devPath.c_str(), nvroot, + /*replace*/B_TRUE) != 0) { + syslog(LOG_ERR, + "Replace vdev(%s/%s) by physical path(attach): %s: %s\n", + zpool_get_name(pool), VdevGUIDString().c_str(), + libzfs_error_action(g_zfsHandle), + libzfs_error_description(g_zfsHandle)); + } else { + syslog(LOG_INFO, "Replacing vdev(%s/%s) with %s\n", + zpool_get_name(pool), VdevGUIDString().c_str(), + devPath.c_str()); + } + nvlist_free(nvroot); + + return (true); +} + +bool +CaseFile::ReEvaluate(const ZfsEvent &event) +{ + bool consumed(false); + + if (!RefreshVdevState()) { + /* + * The pool or vdev for this case file is no longer + * part of the configuration. This can happen + * if we process a device arrival notification + * before seeing the ZFS configuration change + * event. + */ + syslog(LOG_INFO, + "CaseFile::ReEvaluate(%s,%s) Pool/Vdev unconfigured. " + "Closing\n", + PoolGUIDString().c_str(), + VdevGUIDString().c_str()); + Close(); + + /* + * Since this event was not used to close this + * case, do not report it as consumed. + */ + return (/*consumed*/false); + } + + if (event.Value("type") == "misc.fs.zfs.vdev_remove") { + /* + * The Vdev we represent has been removed from the + * configuration. This case is no longer of value. + */ + Close(); + + return (/*consumed*/true); + } + + if (event.Value("class") == "resource.fs.zfs.removed") { + + /* + * Discard any tentative I/O error events for + * this case. They were most likely caused by the + * hot-unplug of this device. + */ + PurgeTentativeEvents(); + + /* + * Rescan the drives in the system to see if a recent + * drive arrival can be used to solve this case. + */ + ZfsDaemon::RequestSystemRescan(); + + consumed = true; + } else if (event.Value("class") == "ereport.fs.zfs.io" + || event.Value("class") == "ereport.fs.zfs.checksum") { + + m_tentativeEvents.push_front(event.DeepCopy()); + if (!m_tentativeTimer.IsPending()) + m_tentativeTimer.Reset(s_removeGracePeriod, + OnGracePeriodEnded, this); + consumed = true; + } + + bool closed(CloseIfSolved()); + + return (consumed || closed); +} + +bool +CaseFile::CloseIfSolved() +{ + if (m_events.empty() + && m_tentativeEvents.empty()) { + + /* + * We currently do not track or take actions on + * devices in the degraded or faulted state. + * Once we have support for spare pools, we'll + * retain these cases so that any spares added in + * the future can be applied to them. + */ + if (VdevState() > VDEV_STATE_CANT_OPEN + && VdevState() <= VDEV_STATE_HEALTHY) { + Close(); + return (true); + } + + /* + * Re-serialize the case in order to remove any + * previous event data. + */ + Serialize(); + } + + return (false); +} + +void +CaseFile::Log() +{ + syslog(LOG_INFO, "CaseFile(%s,%s,%s)\n", PoolGUIDString().c_str(), + VdevGUIDString().c_str(), PhysicalPath().c_str()); + syslog(LOG_INFO, "\tVdev State = %s\n", + zpool_state_to_name(VdevState(), VDEV_AUX_NONE)); + if (m_tentativeEvents.size() != 0) { + syslog(LOG_INFO, "\t=== Tentative Events ===\n"); + for (DevCtlEventList::iterator event(m_tentativeEvents.begin()); + event != m_tentativeEvents.end(); event++) + (*event)->Log(LOG_INFO); + } + if (m_events.size() != 0) { + syslog(LOG_INFO, "\t=== Events ===\n"); + for (DevCtlEventList::iterator event(m_events.begin()); + event != m_events.end(); event++) + (*event)->Log(LOG_INFO); + } +} + +//- CaseFile Static Protected Methods ------------------------------------------ +void +CaseFile::OnGracePeriodEnded(void *arg) +{ + CaseFile &casefile(*static_cast(arg)); + + casefile.OnGracePeriodEnded(); +} + +int +CaseFile::DeSerializeSelector(const struct dirent *dirEntry) +{ + uintmax_t poolGUID; + uintmax_t vdevGUID; + + if (dirEntry->d_type == DT_REG + && sscanf(dirEntry->d_name, "pool_%ju_vdev_%ju.case", + &poolGUID, &vdevGUID) == 2) + return (1); + return (0); +} + +void +CaseFile::DeSerializeFile(const char *fileName) +{ + string fullName(s_caseFilePath + '/' + fileName); + string evString; + CaseFile *existingCaseFile(NULL); + CaseFile *caseFile(NULL); + int fd(-1); + + try { + uintmax_t poolGUID; + uintmax_t vdevGUID; + nvlist_t *vdevConf; + + sscanf(fileName, "pool_%ju_vdev_%ju.case", + &poolGUID, &vdevGUID); + existingCaseFile = Find(poolGUID, vdevGUID); + if (existingCaseFile != NULL) { + /* + * If the vdev is already degraded or faulted, + * there's no point in keeping the state around + * that we use to put a drive into the degraded + * state. However, if the vdev is simply missing, + * preseve the case data in the hopes that it will + * return. + */ + caseFile = existingCaseFile; + vdev_state curState(caseFile->VdevState()); + if (curState > VDEV_STATE_CANT_OPEN + && curState < VDEV_STATE_HEALTHY) { + unlink(fileName); + return; + } + } else { + ZpoolList zpl(ZpoolList::ZpoolByGUID, &poolGUID); + if (zpl.empty() + || (vdevConf = VdevIterator(zpl.front()) + .Find(vdevGUID)) == NULL) { + /* + * Either the pool no longer exists + * of this vdev is no longer a member of + * the pool. + */ + unlink(fullName.c_str()); + return; + } + + /* + * Any vdev we find that does not have a case file + * must be in the healthy state and thus worthy of + * continued SERD data tracking. + */ + caseFile = new CaseFile(Vdev(zpl.front(), vdevConf)); + } + + fd = open(fullName.c_str(), O_RDONLY); + if (fd == -1) { + throw ZfsdException("CaseFile::DeSerialize: Unable to " + "read %s.\n", fileName); + return; + } + + /* Re-load EventData */ + EventBuffer eventBuffer(fd); + while (eventBuffer.ExtractEvent(evString)) { + DevCtlEvent *event(DevCtlEvent::CreateEvent(evString)); + caseFile->m_events.push_back(event); + } + close(fd); + } catch (const ParseException &exp) { + + exp.Log(evString); + if (caseFile != existingCaseFile) + delete caseFile; + close(fd); + + /* + * Since we can't parse the file, unlink it so we don't + * trip over it again. + */ + unlink(fileName); + } catch (const ZfsdException &zfsException) { + + zfsException.Log(); + if (caseFile != existingCaseFile) + delete caseFile; + } +} + +//- CaseFile Protected Methods ------------------------------------------------- +CaseFile::CaseFile(const Vdev &vdev) + : m_poolGUID(vdev.PoolGUID()), + m_vdevGUID(vdev.GUID()), + m_vdevState(vdev.State()), + m_vdevPhysPath(vdev.PhysicalPath()) +{ + stringstream guidString; + + guidString << m_vdevGUID; + m_vdevGUIDString = guidString.str(); + guidString.str(""); + guidString << m_poolGUID; + m_poolGUIDString = guidString.str(); + + s_activeCases.push_back(this); + + syslog(LOG_INFO, "Creating new CaseFile:\n"); + Log(); +} + +CaseFile::~CaseFile() +{ + PurgeEvents(); + PurgeTentativeEvents(); + m_tentativeTimer.Stop(); + s_activeCases.remove(this); +} + +void +CaseFile::PurgeEvents() +{ + for (DevCtlEventList::iterator event(m_events.begin()); + event != m_events.end(); event++) + delete *event; + + m_events.clear(); +} + +void +CaseFile::PurgeTentativeEvents() +{ + for (DevCtlEventList::iterator event(m_tentativeEvents.begin()); + event != m_tentativeEvents.end(); event++) + delete *event; + + m_tentativeEvents.clear(); +} + +void +CaseFile::Serialize() +{ + stringstream saveFile; + + saveFile << setfill('0') + << s_caseFilePath << "/" + << "pool_" << PoolGUIDString() + << "_vdev_" << VdevGUIDString() + << ".case"; + + if (m_events.empty()) { + unlink(saveFile.str().c_str()); + return; + } + + int fd(open(saveFile.str().c_str(), O_CREAT|O_TRUNC|O_WRONLY, 0644)); + if (fd == -1) { + syslog(LOG_ERR, "CaseFile::Serialize: Unable to open %s.\n", + saveFile.str().c_str()); + return; + } + for (DevCtlEventList::const_iterator curEvent = m_events.begin(); + curEvent != m_events.end(); curEvent++) { + const string &eventString((*curEvent)->GetEventString()); + + write(fd, eventString.c_str(), eventString.length()); + } +} + +void +CaseFile::Close() +{ + /* + * This case is no longer relevant. Clean up our + * serialization file, and delete the case. + */ + syslog(LOG_INFO, "CaseFile(%s,%s) closed - State %s\n", + PoolGUIDString().c_str(), VdevGUIDString().c_str(), + zpool_state_to_name(VdevState(), VDEV_AUX_NONE)); + + /* + * Serialization of a Case with no event data, clears the + * Serialization data for that event. + */ + PurgeEvents(); + Serialize(); + + delete this; +} + +void +CaseFile::OnGracePeriodEnded() +{ + m_events.splice(m_events.begin(), m_tentativeEvents); + + if (m_events.size() > ZFS_DEGRADE_IO_COUNT) { + + ZpoolList zpl(ZpoolList::ZpoolByGUID, &m_poolGUID); + if (zpl.empty() + || (VdevIterator(zpl.front()).Find(m_vdevGUID)) == NULL) { + /* + * Either the pool no longer exists + * or this vdev is no longer a member of + * the pool. + */ + Close(); + return; + } + + /* Degrade the vdev and close the case. */ + if (zpool_vdev_degrade(zpl.front(), m_vdevGUID, + VDEV_AUX_ERR_EXCEEDED) == 0) { + Close(); + return; + } + } + Serialize(); +} diff --git a/cddl/sbin/zfsd/case_file.h b/cddl/sbin/zfsd/case_file.h new file mode 100644 index 00000000000..ad0bb9c0887 --- /dev/null +++ b/cddl/sbin/zfsd/case_file.h @@ -0,0 +1,344 @@ +/*- + * Copyright (c) 2011 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + * + * $FreeBSD$ + */ + +/** + * \file case_file.h + * + * CaseFile objects aggregate vdev faults that may require ZFSD action + * in order to maintain the health of a ZFS pool. + */ +#ifndef _CASE_FILE_H_ +#define _CASE_FILE_H_ + +#include +#include + +#include + +#include "callout.h" +#include "dev_ctl_event.h" + +/*=========================== Forward Declarations ===========================*/ +class CaseFile; +class Vdev; + +/*============================= Class Definitions ============================*/ +/*------------------------------- CaseFileList -------------------------------*/ +/** + * CaseFileList is a specialization of the standard list STL container. + */ +typedef std::list< CaseFile *> CaseFileList; + +/*--------------------------------- CaseFile ---------------------------------*/ +/** + * A CaseFile object is instantiated anytime a vdev for an active pool + * experiences an I/O error, is faulted by ZFS, or is determined to be + * missing/removed. + * + * A vdev may have at most one CaseFile. + * + * CaseFiles are retired when a vdev leaves an active pool configuration + * or an action is taken to resolve the issues recorded in the CaseFile. + * + * Logging a case against a vdev does not imply that an immediate action + * to resolve a fault is required or even desired. For example, a CaseFile + * must accumulate a number of I/O errors in order to flag a device as + * degraded. + * + * Vdev I/O errors are not recorded in ZFS label inforamation. For this + * reasons, CaseFile%%s with accumulated I/O error events are serialized + * to the file system so that they survive across boots. Currently all + * other fault types can be reconstructed from ZFS label information, so + * CaseFile%%s for missing, faulted, or degradded members are just recreated + * at ZFSD startup instead of being deserialized from the file system. + */ +class CaseFile +{ +public: + /** + * \brief Find a CaseFile object by a vdev's pool/vdev GUID tuple. + * + * \param poolGUID Pool GUID for the vdev of the CaseFile to find. + * \param vdevGUID Vdev GUID for the vdev of the CaseFile to find. + * + * \return If found, a pointer to a valid CaseFile object. + * Otherwise NULL. + */ + static CaseFile *Find(uint64_t poolGUID, uint64_t vdevGUID); + + /** + * \brief Find a CaseFile object by a vdev's current/last known + * physical path. + * + * \param physPath Physical path of the vdev of the CaseFile to find. + * + * \return If found, a pointer to a valid CaseFile object. + * Otherwise NULL. + */ + static CaseFile *Find(const string &physPath); + + /** + * \brief Create or return an existing active CaseFile for the + * specified vdev. + * + * \param vdev The vdev object for which to find/create a CaseFile. + * + * \return A referenc eto a valid CaseFile object. + */ + static CaseFile &Create(Vdev &vdev); + + /** + * \brief Deserialize all serialized CaseFile objects found in + * the file system. + */ + static void DeSerialize(); + + /** + * \brief Emit syslog data on all active CaseFile%%s in the system. + */ + static void LogAll(); + + /** + * \brief Destroy the in-core cache of CaseFile data. + * + * This routine does not disturb the on disk, serialized, CaseFile + * data. + */ + static void PurgeAll(); + + uint64_t PoolGUID() const; + uint64_t VdevGUID() const; + vdev_state VdevState() const; + const string &PoolGUIDString() const; + const string &VdevGUIDString() const; + const string &PhysicalPath() const; + + /** + * \brief Attempt to resolve this CaseFile using the disk + * resource at the given device/physical path/vdev object + * tuple. + * + * \param devPath The devfs path for the disk resource. + * \param physPath The physical path information reported by + * the disk resource. + * \param vdev If the disk contains ZFS label information, + * a pointer to the disk label's vdev object + * data. Otherwise NULL. + * + * \return True if this event was consumed by this CaseFile. + */ + bool ReEvaluate(const string &devPath, const string &physPath, + Vdev *vdev); + + /** + * \brief Update this CaseFile in light of the provided ZfsEvent. + * + * \param event The ZfsEvent to evaluate. + * + * \return True if this event was consumed by this CaseFile. + */ + bool ReEvaluate(const ZfsEvent &event); + + /** + * \breif Close a case if it is no longer relevant. + * + * This method deals with cases tracking soft errors. Soft errors + * will be discarded should a remove event occur within a short period + * of the soft errors being reported. We also discard the events + * if the vdev is marked degraded or failed. + * + * \return True if the case is closed. False otherwise. + */ + bool CloseIfSolved(); + + /** + * \brief Emit data about this CaseFile via syslog(3). + */ + void Log(); + +protected: + enum { + /** + * The number of soft errors on a vdev required + * to transition a vdev from healthy to degrated + * status. + */ + ZFS_DEGRADE_IO_COUNT = 50 + }; + + static CalloutFunc_t OnGracePeriodEnded; + + /** + * \brief scandir(3) filter function used to find files containing + * serialized CaseFile data. + * + * \param dirEntry Directory entry for the file to filter. + * + * \return Non-zero for a file to include in the selection, otherwise 0. + */ + static int DeSerializeSelector(const struct dirent *dirEntry); + + /** + * \brief Given the name of a file containing a serialized CaseFile + * object, create/update an in-core CaseFile object + * representing the serialized data. + * + * \param fileName The name of a file containing a serialized + * CaseFile object. + */ + static void DeSerializeFile(const char *fileName); + + /** Constructor. */ + CaseFile(const Vdev &vdev); + + /** Destructor. */ + ~CaseFile(); + + /** + * \brief Reload state for the vdev associated with this CaseFile. + * + * \return True if the refresh was successful. False if the system + * has no record of the pool or vdev for this CaseFile. + */ + bool RefreshVdevState(); + + /** + * \brief Free all events in the m_events list. + */ + void PurgeEvents(); + + /** + * \brief Free all events in the m_tentativeEvents list. + */ + void PurgeTentativeEvents(); + + /** + * \brief Commit to file system storage. + */ + void Serialize(); + + /** + * \brief Unconditionally close a CaseFile. + */ + void Close(); + + /** + * \brief Callout callback invoked when the remove timer grace + * period expires. + * + * If no remove events are received prior to the grace period + * firing, then any tentative events are promoted and counted + * against the health of the vdev. + */ + void OnGracePeriodEnded(); + + /** + * \brief All CaseFiles being tracked by ZFSD. + */ + static CaseFileList s_activeCases; + + /** + * \brief The file system path to serialized CaseFile data. + */ + static const string s_caseFilePath; + + /** + * \brief The time ZFSD waits before promoting a tentative event + * into a permanent event. + */ + static const timeval s_removeGracePeriod; + + /** + * \brief A list of soft error events counted against the health of + * a vdev. + */ + DevCtlEventList m_events; + + /** + * \brief A list of soft error events waiting for a grace period + * expiration before being counted against the health of + * a vdev. + */ + DevCtlEventList m_tentativeEvents; + + uint64_t m_poolGUID; + uint64_t m_vdevGUID; + vdev_state m_vdevState; + string m_poolGUIDString; + string m_vdevGUIDString; + string m_vdevPhysPath; + + /** + * \brief Callout activated when a grace period + */ + Callout m_tentativeTimer; +}; + +inline uint64_t +CaseFile::PoolGUID() const +{ + return (m_poolGUID); +} + +inline uint64_t +CaseFile::VdevGUID() const +{ + return (m_vdevGUID); +} + +inline vdev_state +CaseFile::VdevState() const +{ + return (m_vdevState); +} + +inline const string & +CaseFile::PoolGUIDString() const +{ + return (m_poolGUIDString); +} + +inline const string & +CaseFile::VdevGUIDString() const +{ + return (m_vdevGUIDString); +} + +inline const string & +CaseFile::PhysicalPath() const +{ + return (m_vdevPhysPath); +} + +#endif /* _CASE_FILE_H_ */ diff --git a/cddl/sbin/zfsd/dev_ctl_event.cc b/cddl/sbin/zfsd/dev_ctl_event.cc new file mode 100644 index 00000000000..c0762e1e9ba --- /dev/null +++ b/cddl/sbin/zfsd/dev_ctl_event.cc @@ -0,0 +1,698 @@ +/*- + * Copyright (c) 2011 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + */ + +/** + * \file dev_ctl_event.cc + * + * Implementation of the class hierarchy used to express events + * received via the devctl API. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "case_file.h" +#include "dev_ctl_event.h" +#include "vdev.h" +#include "zfsd.h" +#include "zfsd_exception.h" +#include "zpool_list.h" + +/*============================ Namespace Control =============================*/ +using std::cerr; +using std::cout; +using std::endl; +using std::stringstream; + +/*=========================== Class Implementations ==========================*/ +/*------------------------------ ParseException ------------------------------*/ +//- ParseException Public Methods ---------------------------------------------- +string +ParseException::ToString(const string &parsedBuffer) const +{ + stringstream result; + + result << "Parsing "; + + switch(Type()) { + case INVALID_FORMAT: + result << "invalid format "; + break; + case DISCARDED_EVENT_TYPE: + result << "discarded event "; + break; + case UNKNOWN_EVENT_TYPE: + result << "unknown event "; + break; + default: + break; + } + result << "exception on buffer: \'"; + if (GetOffset() == 0) { + result << parsedBuffer << '\'' << endl; + } else { + string markedBuffer(parsedBuffer); + + markedBuffer.insert(GetOffset(), ""); + result << markedBuffer << '\'' << endl; + } + + return (result.str()); +} + +void +ParseException::Log(const string &parsedBuffer) const +{ + int priority(LOG_ERR); + + if (Type() == DISCARDED_EVENT_TYPE) + priority = LOG_INFO; + + syslog(priority, ToString(parsedBuffer).c_str()); +} + +/*-------------------------------- DevCtlEvent -------------------------------*/ +//- DevCtlEvent Static Protected Data ------------------------------------------ +const string DevCtlEvent::s_theEmptyString; + +DevCtlEvent::EventTypeRecord DevCtlEvent::s_typeTable[] = +{ + { DevCtlEvent::NOTIFY, "Notify" }, + { DevCtlEvent::NOMATCH, "No Driver Match" }, + { DevCtlEvent::ATTACH, "Attach" }, + { DevCtlEvent::DETACH, "Detach" } +}; + +DevCtlEvent::EventFactoryRecord DevCtlEvent::s_factoryTable[] = +{ + { DevCtlEvent::NOTIFY, "DEVFS", &DevfsEvent::DevfsEventFactory }, + { DevCtlEvent::NOTIFY, "ZFS", &ZfsEvent::ZfsEventFactory} +}; + +DevCtlEvent::EventFactoryRegistry DevCtlEvent::s_factoryRegistry; + +//- DevCtlEvent Static Public Methods ------------------------------------------ +void +DevCtlEvent::Init() +{ + EventFactoryRecord *rec(s_factoryTable); + EventFactoryRecord *lastRec(s_factoryTable + + NUM_ELEMENTS(s_factoryTable) - 1); + + for (; rec <= lastRec; rec++) { + EventFactoryKey key(rec->m_type, rec->m_subsystem); + + s_factoryRegistry[key] = rec->m_method; + } +} + +DevCtlEvent * +DevCtlEvent::CreateEvent(const string &eventString) +{ + NVPairMap &nvpairs(*new NVPairMap); + Type type(static_cast(eventString[0])); + + try { + ParseEventString(type, eventString, nvpairs); + } catch (const ParseException &exp) { + if (exp.GetType() == ParseException::INVALID_FORMAT) + exp.Log(eventString); + return (NULL); + } + + /* + * Allow entries in our table for events with no system specified. + * These entries should specify the string "none". + */ + NVPairMap::iterator system_item(nvpairs.find("system")); + if (system_item == nvpairs.end()) + nvpairs["system"] = "none"; + + EventFactoryKey key(type, nvpairs["system"]); + EventFactoryRegistry::iterator foundMethod(s_factoryRegistry.find(key)); + if (foundMethod == s_factoryRegistry.end()) + return (NULL); + return ((foundMethod->second)(type, nvpairs, eventString)); +} + +const char * +DevCtlEvent::TypeToString(DevCtlEvent::Type type) +{ + EventTypeRecord *rec(s_typeTable); + EventTypeRecord *lastRec(s_typeTable + NUM_ELEMENTS(s_typeTable) - 1); + + for (; rec <= lastRec; rec++) { + if (rec->m_type == type) + return (rec->m_typeName); + } + return ("Unknown"); +} + +//- DevCtlEvent Public Methods ------------------------------------------------- +const string & +DevCtlEvent::Value(const string &varName) const +{ + NVPairMap::const_iterator item(m_nvPairs.find(varName)); + if (item == m_nvPairs.end()) + return (s_theEmptyString); + + return (item->second); +} + +bool +DevCtlEvent::Contains(const string &varName) const +{ + return (m_nvPairs.find(varName) != m_nvPairs.end()); +} + +string +DevCtlEvent::ToString() const +{ + stringstream result; + + NVPairMap::const_iterator devName(m_nvPairs.find("device-name")); + if (devName != m_nvPairs.end()) + result << devName->second << ": "; + + NVPairMap::const_iterator systemName(m_nvPairs.find("system")); + if (systemName != m_nvPairs.end() + && systemName->second != "none") + result << systemName->second << ": "; + + result << TypeToString(GetType()) << ' '; + + for (NVPairMap::const_iterator curVar = m_nvPairs.begin(); + curVar != m_nvPairs.end(); curVar++) { + if (curVar == devName || curVar == systemName) + continue; + + result << ' ' + << curVar->first << "=" << curVar->second; + } + result << endl; + + return (result.str()); +} + +void +DevCtlEvent::Print() const +{ + cout << ToString() << std::flush; +} + +void +DevCtlEvent::Log(int priority) const +{ + syslog(priority, ToString().c_str()); +} + +//- DevCtlEvent Virtual Public Methods ----------------------------------------- +DevCtlEvent::~DevCtlEvent() +{ + delete &m_nvPairs; +} + +void +DevCtlEvent::Process() const +{ +} + +//- DevCtlEvent Protected Methods ---------------------------------------------- +DevCtlEvent::DevCtlEvent(Type type, NVPairMap &map, const string &eventString) + : m_type(type), + m_nvPairs(map), + m_eventString(eventString) +{ +} + +DevCtlEvent::DevCtlEvent(const DevCtlEvent &src) + : m_type(src.m_type), + m_nvPairs(*new NVPairMap(src.m_nvPairs)), + m_eventString(src.m_eventString) +{ +} + +void +DevCtlEvent::ParseEventString(DevCtlEvent::Type type, + const string &eventString, + NVPairMap& nvpairs) +{ + size_t start; + size_t end; + + switch (type) { + case ATTACH: + case DETACH: + + /* + * \ + * at \ + * on + * + * Handle all data that doesn't conform to the + * "name=value" format, and let the generic parser + * below handle the rest. + * + * Type is a single char. Skip it. + */ + start = 1; + end = eventString.find_first_of(" \t\n", start); + if (end == string::npos) + throw ParseException(ParseException::INVALID_FORMAT, + start); + + nvpairs["device-name"] = eventString.substr(start, end - start); + + start = eventString.find(" on ", end); + if (end == string::npos) + throw ParseException(ParseException::INVALID_FORMAT, + start); + start += 4; + end = eventString.find_first_of(" \t\n", start); + nvpairs["parent"] = eventString.substr(start, end); + if (end == string::npos) + break; + + /* + * The parent field should terminate the event with the + * exception of trailing whitespace. + */ + end = eventString.find_first_not_of(" \t\n", end); + if (end != string::npos) + throw ParseException(ParseException::INVALID_FORMAT, + end); + break; + case NOTIFY: + break; + case NOMATCH: + throw ParseException(ParseException::DISCARDED_EVENT_TYPE); + default: + throw ParseException(ParseException::UNKNOWN_EVENT_TYPE); + } + + /* Process common "key=value" format. */ + for (start = 1; start < eventString.length(); start = end + 1) { + + /* Find the '=' in the middle of the key/value pair. */ + end = eventString.find('=', start); + if (end == string::npos) + break; + + /* + * Find the start of the key by backing up until + * we hit whitespace or '!' (event type "notice"). + * Due to the devctl format, all key/value pair must + * start with one of these two characters. + */ + start = eventString.find_last_of("! \t\n", end); + if (start == string::npos) + throw ParseException(ParseException::INVALID_FORMAT, + end); + start++; + string key(eventString.substr(start, end - start)); + + /* + * Walk forward from the '=' until either we exhaust + * the buffer or we hit whitespace. + */ + start = end + 1; + if (start >= eventString.length()) + throw ParseException(ParseException::INVALID_FORMAT, + end); + end = eventString.find_first_of(" \t\n", start); + if (end == string::npos) + end = eventString.length() - 1; + string value(eventString.substr(start, end - start)); + + nvpairs[key] = value; + } +} + +/*-------------------------------- DevfsEvent --------------------------------*/ +//- DevfsEvent Static Public Methods ------------------------------------------- +DevCtlEvent * +DevfsEvent::DevfsEventFactory(DevCtlEvent::Type type, NVPairMap &nvPairs, + const string &eventString) +{ + return (new DevfsEvent(type, nvPairs, eventString)); +} + +//- DevfsEvent Static Protected Methods ---------------------------------------- +bool +DevfsEvent::IsDiskDev(const string &devName) +{ + static const char *diskDevNames[] = + { + "da", + "ada" + }; + + const char **diskName(diskDevNames); + const char **lastDiskName(diskDevNames + + NUM_ELEMENTS(diskDevNames) - 1); + size_t find_start = devName.rfind('/'); + if (find_start == string::npos) { + find_start = 0; + } else { + /* Just after the last '/'. */ + find_start++; + } + + for (; diskName <= lastDiskName; diskName++) { + + size_t loc(devName.find(*diskName, find_start)); + if (loc == find_start) { + size_t prefixLen(strlen(*diskName)); + + if (devName.length() - find_start >= prefixLen + && isdigit(devName[find_start + prefixLen])) + return (true); + } + } + + return (false); +} + +bool +DevfsEvent::IsWholeDev(const string &devName) +{ + string::const_iterator i(devName.begin()); + + size_t start = devName.rfind('/'); + if (start == string::npos) { + start = 0; + } else { + /* Just after the last '/'. */ + start++; + } + i += start; + + /* alpha prefix followed only by digits. */ + for (; i < devName.end() && !isdigit(*i); i++) + ; + + if (i == devName.end()) + return (false); + + for (; i < devName.end() && isdigit(*i); i++) + ; + + return (i == devName.end()); +} + +nvlist_t * +DevfsEvent::ReadLabel(int devFd, bool &inUse, bool °raded) +{ + pool_state_t poolState; + char *poolName; + boolean_t b_inuse; + + inUse = false; + degraded = false; + poolName = NULL; + if (zpool_in_use(g_zfsHandle, devFd, &poolState, + &poolName, &b_inuse) == 0) { + nvlist_t *devLabel; + + inUse = b_inuse == B_TRUE; + if (poolName != NULL) + free(poolName); + + if (zpool_read_label(devFd, &devLabel) != 0 + || devLabel == NULL) + return (NULL); + + Vdev vdev(devLabel); + degraded = vdev.State() != VDEV_STATE_HEALTHY; + return (devLabel); + } + return (NULL); +} + +bool +DevfsEvent::OnlineByLabel(const string &devPath, const string& physPath, + nvlist_t *devConfig) +{ + try { + /* + * A device with ZFS label information has been + * inserted. If it matches a device for which we + * have a case, see if we can solve that case. + */ + syslog(LOG_INFO, "Interrogating VDEV label for %s\n", + devPath.c_str()); + Vdev vdev(devConfig); + CaseFile *caseFile(CaseFile::Find(vdev.PoolGUID(), + vdev.GUID())); + if (caseFile != NULL) + return (caseFile->ReEvaluate(devPath, physPath, &vdev)); + + } catch (ZfsdException &exp) { + string context("DevfsEvent::OnlineByLabel: " + devPath + ": "); + + exp.GetString().insert(0, context); + exp.Log(); + } + return (false); +} + +//- DevfsEvent Virtual Public Methods ------------------------------------------ +DevCtlEvent * +DevfsEvent::DeepCopy() const +{ + return (new DevfsEvent(*this)); +} + +void +DevfsEvent::Process() const +{ + /* + * We are only concerned with newly discovered + * devices that can be ZFS vdevs. + */ + string devName(Value("cdev")); + if (Value("type") != "CREATE" + || Value("subsystem") != "CDEV" + || !IsDiskDev(devName)) + return; + + /* Log the event since it is of interest. */ + Log(LOG_INFO); + + string devPath(_PATH_DEV + devName); + int devFd(open(devPath.c_str(), O_RDONLY)); + if (devFd == -1) + return; + + /* Normalize the device name in case the DEVFS event is for a link. */ + devName = fdevname(devFd); + devPath = _PATH_DEV + devName; + + bool inUse; + bool degraded; + nvlist_t *devLabel(ReadLabel(devFd, inUse, degraded)); + + char physPath[MAXPATHLEN]; + physPath[0] = '\0'; + bool havePhysPath(ioctl(devFd, DIOCGPHYSPATH, physPath) == 0); + + close(devFd); + + if (inUse && devLabel != NULL) { + OnlineByLabel(devPath, physPath, devLabel); + } else if (degraded) { + syslog(LOG_INFO, "%s is marked degraded. Ignoring " + "as a replace by physical path candidate.\n", + devName.c_str()); + } else if (havePhysPath && IsWholeDev(devName)) { + syslog(LOG_INFO, "Searching for CaseFile by Physical Path\n"); + CaseFile *caseFile(CaseFile::Find(physPath)); + if (caseFile != NULL) { + syslog(LOG_INFO, + "Found CaseFile(%s:%s:%s) - ReEvaluating\n", + caseFile->PoolGUIDString().c_str(), + caseFile->VdevGUIDString().c_str(), + zpool_state_to_name(caseFile->VdevState(), + VDEV_AUX_NONE)); + caseFile->ReEvaluate(devPath, physPath, /*vdev*/NULL); + } + } + if (devLabel != NULL) + nvlist_free(devLabel); +} + +//- DevfsEvent Protected Methods ----------------------------------------------- +DevfsEvent::DevfsEvent(DevCtlEvent::Type type, NVPairMap &nvpairs, + const string &eventString) + : DevCtlEvent(type, nvpairs, eventString) +{ +} + +DevfsEvent::DevfsEvent(const DevfsEvent &src) + : DevCtlEvent(src) +{ +} + +/*--------------------------------- ZfsEvent ---------------------------------*/ +//- ZfsEvent Static Public Methods --------------------------------------------- +DevCtlEvent * +ZfsEvent::ZfsEventFactory(DevCtlEvent::Type type, NVPairMap &nvpairs, + const string &eventString) +{ + return (new ZfsEvent(type, nvpairs, eventString)); +} + +//- ZfsEvent Virtual Public Methods -------------------------------------------- +DevCtlEvent * +ZfsEvent::DeepCopy() const +{ + return (new ZfsEvent(*this)); +} + +void +ZfsEvent::Process() const +{ + string logstr(""); + + if (!Contains("class") && !Contains("type")) { + syslog(LOG_ERR, + "ZfsEvent::Process: Missing class or type data."); + return; + } + + /* On config syncs, replay any queued events first. */ + if (Value("type").find("ESC_ZFS_config_sync") == 0) + ZfsDaemon::ReplayUnconsumedEvents(); + + Log(LOG_INFO); + + if (Value("subsystem").find("misc.fs.zfs.") == 0) { + /* Configuration changes, resilver events, etc. */ + ProcessPoolEvent(); + return; + } + + if (!Contains("pool_guid") || !Contains("vdev_guid")) { + /* Only currently interested in Vdev related events. */ + return; + } + + CaseFile *caseFile(CaseFile::Find(PoolGUID(), VdevGUID())); + if (caseFile != NULL) { + syslog(LOG_INFO, "Evaluating existing case file\n"); + caseFile->ReEvaluate(*this); + return; + } + + /* Skip events that can't be handled. */ + uint64_t poolGUID(PoolGUID()); + /* If there are no replicas for a pool, then it's not manageable. */ + if (Value("class").find("fs.zfs.vdev.no_replicas") == 0) { + syslog(LOG_INFO, "No replicas available for pool %ju" + ", ignoring\n", (uintmax_t)poolGUID); + return; + } + + /* + * Create a case file for this vdev, and have it + * evaluate the event. + */ + ZpoolList zpl(ZpoolList::ZpoolByGUID, &poolGUID); + if (zpl.empty()) { + bool queued = ZfsDaemon::SaveEvent(*this); + int priority = queued ? LOG_INFO : LOG_ERR; + syslog(priority, + "ZfsEvent::Process: Event for unknown pool %ju %s", + (uintmax_t)poolGUID, queued ? "queued" : "dropped"); + return; + } + + nvlist_t *vdevConfig = VdevIterator(zpl.front()).Find(VdevGUID()); + if (vdevConfig == NULL) { + bool queued = ZfsDaemon::SaveEvent(*this); + int priority = queued ? LOG_INFO : LOG_ERR; + syslog(priority, + "ZfsEvent::Process: Event for unknown vdev %ju %s", + (uintmax_t)poolGUID, queued ? "queued" : "dropped"); + return; + } + + Vdev vdev(zpl.front(), vdevConfig); + caseFile = &CaseFile::Create(vdev); + caseFile->ReEvaluate(*this); +} + +//- ZfsEvent Protected Methods ------------------------------------------------- +ZfsEvent::ZfsEvent(DevCtlEvent::Type type, NVPairMap &nvpairs, + const string &eventString) + : DevCtlEvent(type, nvpairs, eventString) +{ + /* + * These are zero on conversion failure as will happen if + * Value returns the empty string. + */ + m_poolGUID = (uint64_t)strtoumax(Value("pool_guid").c_str(), NULL, 0); + m_vdevGUID = (uint64_t)strtoumax(Value("vdev_guid").c_str(), NULL, 0); +} + +ZfsEvent::ZfsEvent(const ZfsEvent &src) + : DevCtlEvent(src), + m_poolGUID(src.m_poolGUID), + m_vdevGUID(src.m_vdevGUID) +{ +} + +void +ZfsEvent::ProcessPoolEvent() const +{ + bool degradedDevice(false); + + CaseFile *caseFile(CaseFile::Find(PoolGUID(), VdevGUID())); + if (caseFile != NULL) { + if (caseFile->VdevState() != VDEV_STATE_UNKNOWN + && caseFile->VdevState() < VDEV_STATE_HEALTHY) + degradedDevice = true; + + caseFile->ReEvaluate(*this); + } + + /* XXX Needs to be changed. */ + if (Value("type") == "ESC_ZFS_vdev_remove" + && degradedDevice == false) { + /* See if any other cases can make use of this device. */ + ZfsDaemon::RequestSystemRescan(); + } +} diff --git a/cddl/sbin/zfsd/dev_ctl_event.h b/cddl/sbin/zfsd/dev_ctl_event.h new file mode 100644 index 00000000000..5bc076587b8 --- /dev/null +++ b/cddl/sbin/zfsd/dev_ctl_event.h @@ -0,0 +1,506 @@ +/*- + * Copyright (c) 2011 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + * + * $FreeBSD$ + */ + +/** + * \file dev_ctl_event.h + * + * \brief Class hierarchy used to express events received via + * the devctl API. + */ + +#ifndef _DEV_CTL_EVENT_H_ +#define _DEV_CTL_EVENT_H_ + +#include +#include +#include + +#include +#include + +/*============================ Namespace Control =============================*/ +using std::map; +using std::pair; +using std::string; + +/*============================= Class Definitions ============================*/ +/*------------------------------ ParseException ------------------------------*/ +/** + * Exception thrown when an event string is not converted to an actionable + * DevCtlEvent object. + */ +class ParseException +{ +public: + enum Type + { + /** Improperly formatted event string encounterd. */ + INVALID_FORMAT, + + /** No handlers for this event type. */ + DISCARDED_EVENT_TYPE, + + /** Unhandled event type. */ + UNKNOWN_EVENT_TYPE + }; + + /** + * Constructor + * + * \param type The type of this exception. + * \param offset The location in the parse buffer where this + * exception occurred. + */ + ParseException(Type type, size_t offset = 0); + + /** + * Accessor + * + * \return The classification for this exception. + */ + Type GetType() const; + + /** + * Accessor + * + * \return The offset into the event string where this exception + * occurred. + */ + size_t GetOffset() const; + + /** + * Convert an exception into a human readable string. + * + * \param parsedBuffer The event buffer that caused the failure. + */ + string ToString(const string &parsedBuffer) const; + + /** + * Log exception data to syslog. + * + * \param parsedBuffer The event buffer that caused the failure. + */ + void Log(const string &parsedBuffer) const; + +private: + /** The type of this exception. */ + Type m_type; + + /** + * The offset into the event string buffer from where this + * exception was triggered. + */ + size_t m_offset; +}; + +//- ParseException Inline Public Methods --------------------------------------- +inline +ParseException::ParseException(Type type, size_t offset) + : m_type(type), + m_offset(offset) +{ +} + +//- ParseException Inline Const Public Methods --------------------------------- +inline ParseException::Type +ParseException::GetType() const +{ + return (m_type); +} + +inline size_t +ParseException::GetOffset() const +{ + return (m_offset); +} + +/*-------------------------------- NVPairMap ---------------------------------*/ +/** + * NVPairMap is a specialization of the standard map STL container. + */ +typedef map NVPairMap; + +/*-------------------------------- DevCtlEvent -------------------------------*/ +/** + * \brief Container for the name => value pairs that comprise the content of + * a device control event. + * + * All name => value data for events can be accessed via the Contains() + * and Value() methods. name => value pairs for data not explicitly + * recieved as a a name => value pair are synthesized during parsing. For + * example, ATTACH and DETACH events have "device-name" and "parent" + * name => value pairs added. + */ +class DevCtlEvent +{ +public: + /** Event type */ + enum Type { + /** Generic event notification. */ + NOTIFY = '!', + + /** A driver was not found for this device. */ + NOMATCH = '?', + + /** A bus device instance has been added. */ + ATTACH = '+', + + /** A bus device instance has been removed. */ + DETACH = '-' + }; + + /** Prepare the DevCtlEvent class for processing of events. */ + static void Init(); + + /** + * Factory method for creating events. + * + * \param buffer String representing a single event received + * from devd. + * + * \note Init() must be invoked prior to the first call to + * CreateEvent(). + */ + static DevCtlEvent *CreateEvent(const string &buffer); + + /** + * Provide a user friendly string representation of an + * event type. + * + * \param type The type of event to map to a string. + * + * \return A user friendly string representing the input type. + */ + static const char *TypeToString(Type type); + + /** + * Determine the availability of a name => value pair by name. + * + * \param name The key name to search for in this event instance. + * + * \return true if the specified key is available in this + * event, otherwise false. + */ + bool Contains(const string &name) const; + + /** + * \param key The name of the key for which to retrieve its + * associated value. + * + * \return A const reference to the string representing the + * value associated with key. + * + * \note For key's with no registered value, the empty string + * is returned. + */ + const string &Value(const string &key) const; + + /** + * Get the type of this event instance. + * + * \return The type of this event instance. + */ + Type GetType() const; + + /** + * Get the orginal DevCtl event string for this event. + * + * \return The DevCtl event string. + */ + const string &GetEventString() const; + + /** + * Convert the event instance into a string suitable for + * printing to the console or emitting to syslog. + * + * \return A string of formatted event data. + */ + string ToString() const; + + /** + * Pretty-print this event instance to cout. + */ + void Print() const; + + /** + * Pretty-print this event instance to syslog. + * + * \param priority The logging priority/facility. + * See syslog(3). + */ + void Log(int priority) const; + + /** + * Create and return a fully independent clone + * of this event. + */ + virtual DevCtlEvent *DeepCopy() const = 0; + + /** Destructor */ + virtual ~DevCtlEvent(); + + /** + * Interpret and perform any actions necessary to + * consume the event. + */ + virtual void Process() const; + +protected: + /** Table entries used to map a type to a user friendly string. */ + struct EventTypeRecord + { + Type m_type; + const char *m_typeName; + }; + + /** + * Event creation handlers are matched by event type and a + * string representing the system emitting the event. + */ + typedef pair EventFactoryKey; + + /** + * Event factory methods construct a DevCtlEvent given + * the type of event and an NVPairMap populated from + * the event string received from devd. + */ + typedef DevCtlEvent* (EventFactory)(Type, NVPairMap &, const string &); + + /** Map type for Factory method lookups. */ + typedef map EventFactoryRegistry; + + /** Table record of factory methods to add to our registry. */ + struct EventFactoryRecord + { + Type m_type; + const char *m_subsystem; + EventFactory *m_method; + }; + + /** + * Constructor + * + * \param type The type of event to create. + */ + DevCtlEvent(Type type, NVPairMap &map, const string &eventString); + + /** Deep copy constructor. */ + DevCtlEvent(const DevCtlEvent &src); + + /** Always empty string returned when NVPairMap lookups fail. */ + static const string s_theEmptyString; + + /** Unsorted table of event types. */ + static EventTypeRecord s_typeTable[]; + + /** Unsorted table of factory methods to add to our registry. */ + static EventFactoryRecord s_factoryTable[]; + + /** Registry of event factory methods providing O(log(n)) lookup. */ + static EventFactoryRegistry s_factoryRegistry; + + /** The type of this event. */ + const Type m_type; + + /** + * Event attribute storage. + * + * \note Although stored by reference (since m_nvPairs can + * never be NULL), the NVPairMap referenced by this field + * is dynamically allocated and owned by this event object. + * m_nvPairs must be deleted at event desctruction. + */ + NVPairMap &m_nvPairs; + + /** + * The unaltered event string, as received from devd, used to + * create this event object. + */ + string m_eventString; + +private: + /** + * Ingest event data from the supplied string. + * + * \param eventString The string of devd event data to parse. + */ + static void ParseEventString(Type type, const string &eventString, + NVPairMap &nvpairs); +}; + +inline DevCtlEvent::Type +DevCtlEvent::GetType() const +{ + return (m_type); +} + +inline const string & +DevCtlEvent::GetEventString() const +{ + return (m_eventString); +} + +/*------------------------------ DevCtlEventList -----------------------------*/ +/** + * DevCtlEventList is a specialization of the standard list STL container. + */ +typedef std::list DevCtlEventList; + +/*-------------------------------- DevfsEvent --------------------------------*/ +class DevfsEvent : public DevCtlEvent +{ +public: + /** Specialized DevCtlEvent object factory for Devfs events. */ + static EventFactory DevfsEventFactory; + + virtual DevCtlEvent *DeepCopy() const; + + /** + * Interpret and perform any actions necessary to + * consume the event. + */ + virtual void Process() const; + +protected: + /** + * Determine if the given device name references a potential + * ZFS leaf vdev. + * + * \param devName The device name to test. + */ + static bool IsDiskDev(const string &devName); + + /** + * Given the device name of a disk, determine if the device + * represents the whole device, not just a partition. + * + * \param devName Device name of disk device to test. + * + * \return True if the device name represents the whole device. + * Otherwise false. + */ + static bool IsWholeDev(const string &devName); + + /** + * \brief Read and return label information for a device. + * + * \param devFd The device from which to read ZFS label information. + * \param inUse The device is part of an active or potentially + * active configuration. + * \param degraded The device label indicates the vdev is not healthy. + * + * \return If label information is available, an nvlist describing + * the vdev configuraiton found on the device specified by + * devFd. Otherwise NULL. + */ + static nvlist_t *ReadLabel(int devFd, bool &inUse, bool °raded); + + /** + * Attempt to match the ZFS labeled device at devPath with an active + * CaseFile for a missing vdev. If a CaseFile is found, attempt + * to re-integrate the device with its pool. + * + * \param devPath The devfs path to the potential leaf vdev. + * \param physPath The physical path string reported by the device + * at devPath. + * \param devConfig The ZFS label information found on the device + * at devPath. + * + * \return true if the event that caused the online action can + * be considered consumed. + */ + static bool OnlineByLabel(const string &devPath, + const string& physPath, + nvlist_t *devConfig); + + /** DeepCopy Constructor. */ + DevfsEvent(const DevfsEvent &src); + + /** Constructor */ + DevfsEvent(Type, NVPairMap &, const string &); +}; + +/*--------------------------------- ZfsEvent ---------------------------------*/ +class ZfsEvent : public DevCtlEvent +{ +public: + /** Specialized DevCtlEvent object factory for ZFS events. */ + static EventFactory ZfsEventFactory; + + virtual DevCtlEvent *DeepCopy() const; + + /** + * Interpret and perform any actions necessary to + * consume the event. + */ + virtual void Process() const; + + const string &PoolName() const; + uint64_t PoolGUID() const; + uint64_t VdevGUID() const; + +protected: + /** Constructor */ + ZfsEvent(Type, NVPairMap &, const string &); + + /** Deep copy constructor. */ + ZfsEvent(const ZfsEvent &src); + + void ProcessPoolEvent() const; + + uint64_t m_poolGUID; + uint64_t m_vdevGUID; +}; + +//- ZfsEvent Inline Public Methods -------------------------------------------- +inline const string& +ZfsEvent::PoolName() const +{ + /* The pool name is reported as the subsystem of ZFS events. */ + return (Value("subsystem")); +} + +inline uint64_t +ZfsEvent::PoolGUID() const +{ + return (m_poolGUID); +} + +inline uint64_t +ZfsEvent::VdevGUID() const +{ + return (m_vdevGUID); +} + +#endif /*_DEV_CTL_EVENT_H_ */ diff --git a/cddl/sbin/zfsd/vdev.cc b/cddl/sbin/zfsd/vdev.cc new file mode 100644 index 00000000000..dbdfd523915 --- /dev/null +++ b/cddl/sbin/zfsd/vdev.cc @@ -0,0 +1,154 @@ +/*- + * Copyright (c) 2011 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + * + * $FreeBSD$ + */ + +/** + * \file vdev.cc + * + * Implementation of the Vdev class. + */ +#include + +#include + +#include "vdev.h" +#include "zfsd.h" +#include "zfsd_exception.h" + +__FBSDID("$FreeBSD$"); +/*============================ Namespace Control =============================*/ +using std::stringstream; + +/*=========================== Class Implementations ==========================*/ +/*----------------------------------- Vdev -----------------------------------*/ +Vdev::Vdev(zpool_handle_t *pool, nvlist_t *config) + : m_poolConfig(zpool_get_config(pool, NULL)), + m_config(config) +{ + if (nvlist_lookup_uint64(m_poolConfig, ZPOOL_CONFIG_POOL_GUID, + &m_poolGUID) != 0) + throw ZfsdException("Unable to extract pool GUID " + "from pool handle."); + + if (nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_GUID, &m_vdevGUID) != 0) + throw ZfsdException("Unable to extract vdev GUID " + "from vdev config data."); +} + +Vdev::Vdev(nvlist_t *poolConfig, nvlist_t *config) + : m_poolConfig(poolConfig), + m_config(config) +{ + if (nvlist_lookup_uint64(m_poolConfig, ZPOOL_CONFIG_POOL_GUID, + &m_poolGUID) != 0) + throw ZfsdException("Unable to extract pool GUID " + "from pool handle."); + + if (nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_GUID, &m_vdevGUID) != 0) + throw ZfsdException("Unable to extract vdev GUID " + "from vdev config data."); +} + +Vdev::Vdev(nvlist_t *labelConfig) + : m_poolConfig(labelConfig) +{ + if (nvlist_lookup_uint64(labelConfig, ZPOOL_CONFIG_POOL_GUID, + &m_poolGUID) != 0) + throw ZfsdException("Unable to extract pool GUID " + "from vdev label data."); + + if (nvlist_lookup_uint64(labelConfig, ZPOOL_CONFIG_GUID, + &m_vdevGUID) != 0) + throw ZfsdException("Unable to extract vdev GUID " + "from vdev label data."); + m_config = VdevIterator(labelConfig).Find(m_vdevGUID); + if (m_config == NULL) + throw ZfsdException("Unable to find vdev config " + "within vdev label data."); +} + +vdev_state +Vdev::State() const +{ + vdev_stat_t *vs; + uint_t vsc; + + if (nvlist_lookup_uint64_array(m_config, ZPOOL_CONFIG_VDEV_STATS, + (uint64_t **)&vs, &vsc) == 0) + return (static_cast(vs->vs_state)); + + /* + * Stats are not available. This vdev was created from a label. + * Synthesize a state based on available data. + */ + uint64_t faulted(0); + uint64_t degraded(0); + (void)nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_FAULTED, &faulted); + (void)nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_DEGRADED, °raded); + if (faulted) + return (VDEV_STATE_FAULTED); + if (degraded) + return (VDEV_STATE_DEGRADED); + return (VDEV_STATE_HEALTHY); +} + +string +Vdev::GUIDString() const +{ + stringstream vdevGUIDString; + + vdevGUIDString << GUID(); + return (vdevGUIDString.str()); +} + +string +Vdev::Path() const +{ + char *path(NULL); + + if (nvlist_lookup_string(m_config, ZPOOL_CONFIG_PATH, &path) == 0) + return (path); + + return (""); +} + +string +Vdev::PhysicalPath() const +{ + char *path(NULL); + + if (nvlist_lookup_string(m_config, ZPOOL_CONFIG_PHYS_PATH, &path) == 0) + return (path); + + return (""); +} diff --git a/cddl/sbin/zfsd/vdev.h b/cddl/sbin/zfsd/vdev.h new file mode 100644 index 00000000000..abb252dc999 --- /dev/null +++ b/cddl/sbin/zfsd/vdev.h @@ -0,0 +1,136 @@ +/*- + * Copyright (c) 2011 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + * + * $FreeBSD$ + */ + +/** + * \file vdev.h + * + * Definition of the Vdev class. + */ +#ifndef _VDEV_H_ +#define _VDEV_H_ + +#include + +#include +#include + +/** + * \brief Wrapper class for a vdev's name/value configuration list + * simplifying access to commonly used vdev attributes. + */ +class Vdev +{ +public: + /** + * \brief Instantiate a vdev object for a vdev that is a member + * of an imported pool. + * + * \param pool The pool object containing the vdev with + * configuration data provided in vdevConfig. + * \param vdevConfig Vdev configuration data. + * + * This method should be used whenever dealing with vdev's + * enumerated via the ZpoolList class. The in-core configuration + * data for a vdev does not contain all of the items found in + * the on-disk label. This requires the vdev class to augment + * the data in vdevConfig with data found in the pool object. + */ + Vdev(zpool_handle_t *pool, nvlist_t *vdevConfig); + + /** + * \brief Instantiate a vdev object for a vdev that is a member + * of a pool configuration. + * + * \param poolConfig The pool configuration containing the vdev + * configuration data provided in vdevConfig. + * \param vdevConfig Vdev configuration data. + * + * This method should be used whenever dealing with vdev's + * enumerated via the ZpoolList class. The in-core configuration + * data for a vdev does not contain all of the items found in + * the on-disk label. This requires the vdev class to augment + * the data in vdevConfig with data found in the pool object. + */ + Vdev(nvlist_t *poolConfig, nvlist_t *vdevConfig); + + /** + * \brief Instantiate a vdev object from a ZFS label stored on + * the device. + * + * \param vdevConfig The name/value list retrieved by reading + * the label information on a leaf vdev. + */ + Vdev(nvlist_t *vdevConfig); + + uint64_t GUID() const; + uint64_t PoolGUID() const; + vdev_state State() const; + std::string Path() const; + std::string PhysicalPath() const; + std::string GUIDString() const; + nvlist_t *PoolConfig() const; + nvlist_t *Config() const; + +private: + uint64_t m_poolGUID; + uint64_t m_vdevGUID; + nvlist_t *m_poolConfig; + nvlist_t *m_config; +}; + +inline uint64_t +Vdev::PoolGUID() const +{ + return (m_poolGUID); +} + +inline uint64_t +Vdev::GUID() const +{ + return (m_vdevGUID); +} + +inline nvlist_t * +Vdev::PoolConfig() const +{ + return (m_poolConfig); +} + +inline nvlist_t * +Vdev::Config() const +{ + return (m_config); +} + +#endif /* _VDEV_H_ */ diff --git a/cddl/sbin/zfsd/vdev_iterator.cc b/cddl/sbin/zfsd/vdev_iterator.cc new file mode 100644 index 00000000000..edc6238794c --- /dev/null +++ b/cddl/sbin/zfsd/vdev_iterator.cc @@ -0,0 +1,141 @@ +/*- + * Copyright (c) 2011 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + */ + +/** + * \file vdev_iterator.cc + * + * Implementation of the VdevIterator class. + */ +#include +#include + +#include +#include + +#include "vdev.h" +#include "vdev_iterator.h" +#include "zfsd.h" +#include "zfsd_exception.h" + +/*=========================== Class Implementations ==========================*/ +/*------------------------------- VdevIterator -------------------------------*/ +VdevIterator::VdevIterator(zpool_handle_t *pool) + : m_poolConfig(zpool_get_config(pool, NULL)) +{ + Reset(); +} + +VdevIterator::VdevIterator(nvlist_t *poolConfig) + : m_poolConfig(poolConfig) +{ + Reset(); +} + +void +VdevIterator::Reset() +{ + nvlist_t *rootVdev; + int result; + + result = nvlist_lookup_nvlist(m_poolConfig, + ZPOOL_CONFIG_VDEV_TREE, + &rootVdev); + if (result != 0) + throw ZfsdException(m_poolConfig, "Unable to extract " + "ZPOOL_CONFIG_VDEV_TREE from pool."); + m_vdevQueue.assign(1, rootVdev); +} + +nvlist_t * +VdevIterator::Next() +{ + nvlist_t *vdevConfig; + + if (m_vdevQueue.empty()) + return (NULL); + + while (1) { + nvlist_t **vdevChildren; + int result; + u_int numChildren; + + vdevConfig = m_vdevQueue.front(); + m_vdevQueue.pop_front(); + + /* Expand non-leaf vdevs. */ + result = nvlist_lookup_nvlist_array(vdevConfig, + ZPOOL_CONFIG_CHILDREN, + &vdevChildren, &numChildren); + if (result != 0) { + /* leaf vdev */ + break; + } + + /* + * Insert children at the head of the queue to effect a + * depth first traversal of the tree. + */ + m_vdevQueue.insert(m_vdevQueue.begin(), vdevChildren, + vdevChildren + numChildren); + }; + + return (vdevConfig); +} + +void +VdevIterator::Each(VdevCallback_t *callBack, void *callBackArg) +{ + nvlist_t *vdevConfig; + + Reset(); + while ((vdevConfig = Next()) != NULL) { + Vdev vdev(m_poolConfig, vdevConfig); + + if (callBack(vdev, callBackArg)) + break; + } +} + +nvlist_t * +VdevIterator::Find(uint64_t vdevGUID) +{ + nvlist_t *vdevConfig; + + Reset(); + while ((vdevConfig = Next()) != NULL) { + Vdev vdev(m_poolConfig, vdevConfig); + + if (vdev.GUID() == vdevGUID) + return vdevConfig; + } + return (NULL); +} diff --git a/cddl/sbin/zfsd/vdev_iterator.h b/cddl/sbin/zfsd/vdev_iterator.h new file mode 100644 index 00000000000..0e28362c673 --- /dev/null +++ b/cddl/sbin/zfsd/vdev_iterator.h @@ -0,0 +1,116 @@ +/*- + * Copyright (c) 2011 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + */ + +/** + * \file vdev_iterator.h + * + * VdevIterator class definition. + */ +#ifndef _VDEV_ITERATOR_H_ +#define _VDEV_ITERATOR_H_ + +#include + +#include +#include + +/*=========================== Forward Declarations ===========================*/ +class Vdev; + +/*============================= Class Definitions ============================*/ +/*------------------------------- VdevIterator -------------------------------*/ +typedef bool VdevCallback_t(Vdev &vdev, void *cbArg); + +/** + * \brief VdevIterator provides mechanisms for traversing and searching + * the leaf vdevs contained in a ZFS pool configuration. + */ +class VdevIterator +{ +public: + /** + * \brief Instantiate a VdevIterator for the given ZFS pool. + * + * \param pool The ZFS pool to traverse/search. + */ + VdevIterator(zpool_handle_t *pool); + + /** + * \brief Instantiate a VdevIterator for the given ZFS pool. + * + * \param poolConfig The configuration data for the ZFS pool + * to traverse/search. + */ + VdevIterator(nvlist_t *poolConfig); + + /** + * \brief Reset this iterator's cursor so that Next() will + * report the first member of the pool. + */ + void Reset(); + + /** + * \brief Report the vdev at this iterator's cursor and increment + * the cursor to the next pool member. + */ + nvlist_t *Next(); + + /** + * \brief Traverse the entire pool configuration starting its + * first member, returning a vdev object with the given + * vdev GUID if found. + * + * \param vdevGUID The vdev GUID of the vdev object to find. + * + * \return A Vdev object for the matching vdev if found. Otherwise + * NULL. + * + * Upon return, the VdevIterator's cursor points to the vdev just + * past the returned vdev or end() if no matching vdev is found. + */ + nvlist_t *Find(uint64_t vdevGUID); + + /** + * \brief Perform the specified operation on each leaf member of + * a pool's vdev membership. + * + * \param cb Callback function to execute for each member. + * \param cbArg Argument to pass to cb. + */ + void Each(VdevCallback_t *cb, void *cbArg); + +private: + nvlist_t *m_poolConfig; + std::list m_vdevQueue; +}; + +#endif /* _VDEV_ITERATOR_H_ */ diff --git a/cddl/sbin/zfsd/zfsd.cc b/cddl/sbin/zfsd/zfsd.cc new file mode 100644 index 00000000000..40156d9d968 --- /dev/null +++ b/cddl/sbin/zfsd/zfsd.cc @@ -0,0 +1,637 @@ +/*- + * Copyright (c) 2011 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + */ + +/** + * \file zfsd.cc + * + * The ZFS daemon consumes kernel devctl(4) event data via devd(8)'s + * unix domain socket in order to react to system changes that impact + * the function of ZFS storage pools. The goal of this daemon is to + * provide similar functionality to the Solaris ZFS Diagnostic Engine + * (zfs-diagnosis), the Solaris ZFS fault handler (zfs-retire), and + * the Solaris ZFS vdev insertion agent (zfs-mod sysevent handler). + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "callout.h" +#include "vdev.h" +#include "zfsd.h" +#include "zfsd_exception.h" +#include "zpool_list.h" + +__FBSDID("$FreeBSD$"); + +/*============================ Namespace Control =============================*/ +using std::string; +using std::stringstream; +using std::cerr; +using std::cout; +using std::endl; + +/*================================ Global Data ===============================*/ +const char g_devdSock[] = "/var/run/devd.pipe"; +int g_debug = 0; +libzfs_handle_t *g_zfsHandle; + +/*-------------------------------- EventBuffer -------------------------------*/ +//- EventBuffer Static Data ---------------------------------------------------- +/** + * NOTIFY, NOMATCH, ATTACH, DETACH. See DevCtlEvent::Type. + */ +const char EventBuffer::s_eventStartTokens[] = "!?+-"; + +/** + * Events are terminated by a newline. + */ +const char EventBuffer::s_eventEndTokens[] = "\n"; + +//- EventBuffer Public Methods ------------------------------------------------- +EventBuffer::EventBuffer(int fd) + : m_fd(fd), + m_validLen(0), + m_parsedLen(0), + m_nextEventOffset(0) +{ +} + +bool +EventBuffer::ExtractEvent(string &eventString) +{ + + while (UnParsed() > 0 || Fill()) { + + /* + * If the valid data in the buffer isn't enough to hold + * a full event, try reading more. + */ + if (NextEventMaxLen() < MIN_EVENT_SIZE) { + m_parsedLen += UnParsed(); + continue; + } + + char *nextEvent(m_buf + m_nextEventOffset); + size_t startLen(strcspn(nextEvent, s_eventStartTokens)); + bool aligned(startLen == 0); + if (aligned == false) { + warnx("Re-synchronizing with devd event stream"); + m_nextEventOffset += startLen; + m_parsedLen = m_nextEventOffset; + continue; + } + + /* + * Start tokens may be end tokens too, so skip the start + * token when trying to find the end of the event. + */ + size_t eventLen(strcspn(nextEvent + 1, s_eventEndTokens) + 1); + if (nextEvent[eventLen] == '\0') { + /* Ran out of buffer before hitting a full event. */ + m_parsedLen += eventLen; + continue; + } + + if (nextEvent[eventLen] != '\n') { + warnx("Improperly terminated event encountered"); + } else { + /* + * Include the normal terminator in the extracted + * event data. + */ + eventLen += 1; + } + + m_nextEventOffset += eventLen; + m_parsedLen = m_nextEventOffset; + eventString.assign(nextEvent, eventLen); + return (true); + } + return (false); +} + +//- EventBuffer Private Methods ------------------------------------------------ +bool +EventBuffer::Fill() +{ + ssize_t result; + + /* Compact the buffer. */ + if (m_nextEventOffset != 0) { + memmove(m_buf, m_buf + m_nextEventOffset, + m_validLen - m_nextEventOffset); + m_validLen -= m_nextEventOffset; + m_parsedLen -= m_nextEventOffset; + m_nextEventOffset = 0; + } + + /* Fill any empty space. */ + result = read(m_fd, m_buf + m_validLen, MAX_READ_SIZE - m_validLen); + if (result == -1) { + if (errno == EINTR || errno == EAGAIN) { + return (false); + } else { + err(1, "Read from devd socket failed"); + } + } + m_validLen += result; + /* Guarantee our buffer is always NUL terminated. */ + m_buf[m_validLen] = '\0'; + + return (result > 0); +} + +/*--------------------------------- ZfsDaemon --------------------------------*/ +//- ZfsDaemon Static Private Data ---------------------------------------------- +bool ZfsDaemon::s_logCaseFiles; +bool ZfsDaemon::s_terminateEventLoop; +char ZfsDaemon::s_pidFilePath[] = "/var/run/zfsd.pid"; +pidfh *ZfsDaemon::s_pidFH; +int ZfsDaemon::s_devdSockFD = -1; +int ZfsDaemon::s_signalPipeFD[2]; +bool ZfsDaemon::s_systemRescanRequested(false); +bool ZfsDaemon::s_consumingEvents(false); +DevCtlEventList ZfsDaemon::s_unconsumedEvents; + +//- ZfsDaemon Static Public Methods -------------------------------------------- +void +ZfsDaemon::WakeEventLoop() +{ + write(s_signalPipeFD[1], "+", 1); +} + +void +ZfsDaemon::RequestSystemRescan() +{ + s_systemRescanRequested = true; + ZfsDaemon::WakeEventLoop(); +} + +void +ZfsDaemon::Run() +{ + Init(); + + while (s_terminateEventLoop == false) { + + try { + DisconnectFromDevd(); + + if (ConnectToDevd() == false) { + sleep(30); + continue; + } + + DetectMissedEvents(); + + EventLoop(); + + } catch (const ZfsdException &exp) { + exp.Log(); + } + } + + DisconnectFromDevd(); + + Fini(); +} + +//- ZfsDaemon Static Private Methods ------------------------------------------- +void +ZfsDaemon::Init() +{ + if (pipe(s_signalPipeFD) != 0) + errx(1, "Unable to allocate signal pipe. Exiting"); + + if (fcntl(s_signalPipeFD[0], F_SETFL, O_NONBLOCK) == -1) + errx(1, "Unable to set pipe as non-blocking. Exiting"); + + if (fcntl(s_signalPipeFD[1], F_SETFL, O_NONBLOCK) == -1) + errx(1, "Unable to set pipe as non-blocking. Exiting"); + + signal(SIGHUP, ZfsDaemon::RescanSignalHandler); + signal(SIGINFO, ZfsDaemon::InfoSignalHandler); + signal(SIGINT, ZfsDaemon::QuitSignalHandler); + signal(SIGTERM, ZfsDaemon::QuitSignalHandler); + signal(SIGUSR1, ZfsDaemon::RescanSignalHandler); + + g_zfsHandle = libzfs_init(); + if (g_zfsHandle == NULL) + errx(1, "Unable to initialize ZFS library. Exiting"); + + Callout::Init(); + DevCtlEvent::Init(); + InitializeSyslog(); + OpenPIDFile(); + + if (g_debug == 0) + daemon(0, 0); + + UpdatePIDFile(); +} + +void +ZfsDaemon::Fini() +{ + ClosePIDFile(); +} + +void +ZfsDaemon::InfoSignalHandler(int) +{ + s_logCaseFiles = true; + ZfsDaemon::WakeEventLoop(); +} + +void +ZfsDaemon::RescanSignalHandler(int) +{ + RequestSystemRescan(); +} + +void +ZfsDaemon::QuitSignalHandler(int) +{ + s_terminateEventLoop = true; + ZfsDaemon::WakeEventLoop(); +} + +void +ZfsDaemon::OpenPIDFile() +{ + pid_t otherPID; + + s_pidFH = pidfile_open(s_pidFilePath, 0600, &otherPID); + if (s_pidFH == NULL) { + if (errno == EEXIST) + errx(1, "already running as PID %d. Exiting", otherPID); + warn("cannot open PID file"); + } +} + +void +ZfsDaemon::UpdatePIDFile() +{ + if (s_pidFH != NULL) + pidfile_write(s_pidFH); +} + +void +ZfsDaemon::ClosePIDFile() +{ + if (s_pidFH != NULL) + pidfile_close(s_pidFH); +} + +void +ZfsDaemon::InitializeSyslog() +{ + openlog("zfsd", LOG_NDELAY, LOG_DAEMON); +} + +bool +ZfsDaemon::ConnectToDevd() +{ + struct sockaddr_un devdAddr; + int sLen; + int result; + + syslog(LOG_INFO, "Connecting to devd"); + + memset(&devdAddr, 0, sizeof(devdAddr)); + devdAddr.sun_family= AF_UNIX; + strlcpy(devdAddr.sun_path, g_devdSock, sizeof(devdAddr.sun_path)); + sLen = SUN_LEN(&devdAddr); + + s_devdSockFD = socket(AF_UNIX, SOCK_STREAM, 0); + if (s_devdSockFD == -1) + err(1, "Unable to create socket"); + result = connect(s_devdSockFD, + reinterpret_cast(&devdAddr), + sLen); + if (result == -1) { + syslog(LOG_INFO, "Unable to connect to devd"); + return (false); + } + + /* Don't block on reads. */ + if (fcntl(s_devdSockFD, F_SETFL, O_NONBLOCK) == -1) + err(1, "Unable to enable nonblocking behavior on devd socket"); + + syslog(LOG_INFO, "Connection to devd successful"); + return (true); +} + +void +ZfsDaemon::DisconnectFromDevd() +{ + close(s_devdSockFD); +} + +void +ZfsDaemon::ReplayUnconsumedEvents() +{ + DevCtlEventList::iterator event(s_unconsumedEvents.begin()); + bool replayed_any = (event != s_unconsumedEvents.end()); + + s_consumingEvents = true; + if (replayed_any) + syslog(LOG_INFO, "Started replaying unconsumed events"); + while (event != s_unconsumedEvents.end()) { + (*event)->Process(); + delete *event; + s_unconsumedEvents.erase(event++); + } + if (replayed_any) + syslog(LOG_INFO, "Finished replaying unconsumed events"); + s_consumingEvents = false; +} + +bool +ZfsDaemon::SaveEvent(const DevCtlEvent &event) +{ + if (s_consumingEvents) + return false; + s_unconsumedEvents.push_back(event.DeepCopy()); + return true; +} + +/* Capture and process buffered events. */ +void +ZfsDaemon::ProcessEvents(EventBuffer &eventBuffer) +{ + string evString; + while (eventBuffer.ExtractEvent(evString)) { + DevCtlEvent *event(DevCtlEvent::CreateEvent(evString)); + if (event != NULL) { + event->Process(); + delete event; + } + } +} + +void +ZfsDaemon::FlushEvents() +{ + char discardBuf[256]; + + while (read(s_devdSockFD, discardBuf, sizeof(discardBuf)) > 0) + ; +} + +bool +ZfsDaemon::EventsPending() +{ + struct pollfd fds[1]; + int result; + + fds->fd = s_devdSockFD; + fds->events = POLLIN; + fds->revents = 0; + result = poll(fds, NUM_ELEMENTS(fds), /*timeout*/0); + + return ((fds->revents & POLLIN) != 0); +} + +void +ZfsDaemon::PurgeCaseFiles() +{ + CaseFile::PurgeAll(); +} + +bool +ZfsDaemon::VdevAddCaseFile(Vdev &vdev, void *cbArg) +{ + + if (vdev.State() != VDEV_STATE_HEALTHY) + CaseFile::Create(vdev); + + return (/*break early*/false); +} + +void +ZfsDaemon::BuildCaseFiles() +{ + /* Add CaseFiles for vdevs with issues. */ + ZpoolList zpl; + + for (ZpoolList::iterator pool = zpl.begin(); pool != zpl.end(); pool++) + VdevIterator(*pool).Each(VdevAddCaseFile, NULL); + + /* De-serialize any saved cases. */ + CaseFile::DeSerialize(); +} + +void +ZfsDaemon::RescanSystem() +{ + struct gmesh mesh; + struct gclass *mp; + struct ggeom *gp; + struct gprovider *pp; + int result; + + /* + * The devctl system doesn't replay events for new consumers + * of the interface. Emit manufactured DEVFS arrival events + * for any devices that already before we started or during + * periods where we've lost our connection to devd. + */ + result = geom_gettree(&mesh); + if (result != 0) { + syslog(LOG_ERR, "ZfsDaemon::RescanSystem: " + "geom_gettree faild with error %d\n", result); + return; + } + + const string evStart("!system=DEVFS subsystem=CDEV type=CREATE " + "sub_type=synthesized cdev="); + LIST_FOREACH(mp, &mesh.lg_class, lg_class) { + LIST_FOREACH(gp, &mp->lg_geom, lg_geom) { + LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { + DevCtlEvent *event; + + string evString(evStart + pp->lg_name + "\n"); + event = DevCtlEvent::CreateEvent(evString); + if (event != NULL) + event->Process(); + } + } + } + geom_deletetree(&mesh); +} + +void +ZfsDaemon::DetectMissedEvents() +{ + do { + PurgeCaseFiles(); + + /* + * Discard any events waiting for us. We don't know + * if they still apply to the current state of the + * system. + */ + FlushEvents(); + + BuildCaseFiles(); + + /* + * If the system state has changed durring our + * interrogation, start over. + */ + } while (EventsPending()); + + RescanSystem(); +} + +void +ZfsDaemon::EventLoop() +{ + EventBuffer eventBuffer(s_devdSockFD); + + while (s_terminateEventLoop == false) { + struct pollfd fds[2]; + int result; + + if (s_logCaseFiles == true) { + s_logCaseFiles = false; + CaseFile::LogAll(); + } + + Callout::ExpireCallouts(); + + /* Wait for data. */ + fds[0].fd = s_devdSockFD; + fds[0].events = POLLIN; + fds[0].revents = 0; + fds[1].fd = s_signalPipeFD[0]; + fds[1].events = POLLIN; + fds[1].revents = 0; + result = poll(fds, NUM_ELEMENTS(fds), /*timeout*/INFTIM); + if (result == -1) { + if (errno == EINTR) + continue; + else + err(1, "Polling for devd events failed"); + } else if (result == 0) { + errx(1, "Unexpected result of 0 from poll. Exiting"); + } + + if ((fds[0].revents & POLLIN) != 0) + ProcessEvents(eventBuffer); + + if ((fds[1].revents & POLLIN) != 0) { + static char discardBuf[128]; + + /* + * This pipe exists just to close the signal + * race. Its contents are of no interest to + * us, but we must ensure that future signals + * have space in the pipe to write. + */ + while (read(s_signalPipeFD[0], discardBuf, + sizeof(discardBuf)) > 0) + ; + } + + if (s_systemRescanRequested == true) { + s_systemRescanRequested = false; + RescanSystem(); + } + + if ((fds->revents & POLLERR) != 0) { + /* Try reconnecting. */ + syslog(LOG_INFO, "Error on socket. Disconnecting."); + break; + } + + if ((fds->revents & POLLHUP) != 0) { + /* Try reconnecting. */ + syslog(LOG_INFO, "Hup on socket. Disconnecting."); + break; + } + } +} + +/*=============================== Program Main ===============================*/ +static void +usage() +{ + fprintf(stderr, "usage: %s [-d]\n", getprogname()); + exit(1); +} + +/** + * Program entry point. + */ +int +main(int argc, char **argv) +{ + int ch; + + while ((ch = getopt(argc, argv, "d")) != -1) { + switch (ch) { + case 'd': + g_debug++; + break; + default: + usage(); + } + } + + ZfsDaemon::Run(); + + return (0); +} diff --git a/cddl/sbin/zfsd/zfsd.h b/cddl/sbin/zfsd/zfsd.h new file mode 100644 index 00000000000..7834283d1ad --- /dev/null +++ b/cddl/sbin/zfsd/zfsd.h @@ -0,0 +1,376 @@ +/*- + * Copyright (c) 2011 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + * + * $FreeBSD$ + */ + +/** + * \file zfsd.h + * + * Class definitions and supporting data strutures for the ZFS fault + * management daemon. + */ +#ifndef _ZFSD_H_ +#define _ZFSD_H_ + +#include +#include +#include +#include + +#include +#include + +#include "case_file.h" +#include "dev_ctl_event.h" +#include "vdev_iterator.h" + +/*============================ Namespace Control =============================*/ +using std::auto_ptr; +using std::map; +using std::pair; +using std::string; + +/*================================ Global Data ===============================*/ +extern const char g_devdSock[]; +extern int g_debug; +extern libzfs_handle_t *g_zfsHandle; + +/*=========================== Forward Declarations ===========================*/ +struct EventFactoryRecord; +struct pidfh; + +typedef int LeafIterFunc(zpool_handle_t *, nvlist_t *, void *); + +/*================================== Macros ==================================*/ +#define NUM_ELEMENTS(x) (sizeof(x) / sizeof(*x)) + +/*============================= Class Definitions ============================*/ +/*-------------------------------- EventBuffer -------------------------------*/ +/** + * \brief Class buffering event data from Devd and splitting it + * into individual event strings. + * + * Users of this class initialize it with the file descriptor associated + * with the unix domain socket connection with devd. The lifetime of + * an EventBuffer instance should match that of the file descriptor passed + * to it. This is required as data from partially received events is + * retained in the EventBuffer in order to allow reconstruction of these + * events across multiple reads of the Devd file descriptor. + * + * Once the program determines that the Devd file descriptor is ready + * for reading, the EventBuffer::ExtractEvent() should be called in a + * loop until the method returns false. + */ +class EventBuffer +{ +public: + /** + * Constructor + * + * \param fd The file descriptor on which to buffer/parse event data. + */ + EventBuffer(int fd); + + /** + * Pull a single event string out of the event buffer. + * + * \param eventString The extracted event data (if available). + * + * \return true if event data is available and eventString has + * been populated. Otherwise false. + */ + bool ExtractEvent(string &eventString); + +private: + enum { + /** + * Size of an empty event (start and end token with + * no data. The EventBuffer parsing needs at least + * this much data in the buffer for safe event extraction. + */ + MIN_EVENT_SIZE = 2, + + /** + * The maximum amount of buffer data to read at + * a single time from the Devd file descriptor. + * This size matches the largest event size allowed + * in the system. + */ + MAX_READ_SIZE = 1024, + + /** + * The size of EventBuffer's buffer of Devd event data. + * This is one larger than the maximum event size which + * alows us to always include a terminating NUL without + * overwriting any received data. + */ + EVENT_BUFSIZE = MAX_READ_SIZE + /*NUL*/1 + }; + + /** The amount of data in m_buf we have yet to look at. */ + size_t UnParsed(); + + /** The amount of data in m_buf available for the next event. */ + size_t NextEventMaxLen(); + + /** Fill the event buffer with event data from Devd. */ + bool Fill(); + + /** Characters we treat as beginning an event string. */ + static const char s_eventStartTokens[]; + + /** Characters we treat as ending an event string. */ + static const char s_eventEndTokens[]; + + /** Temporary space for event data during our parsing. */ + char m_buf[EVENT_BUFSIZE]; + + /** Copy of the file descriptor linked to devd's domain socket. */ + int m_fd; + + /** Valid bytes in m_buf. */ + size_t m_validLen; + + /** The amount of data in m_buf we have looked at. */ + size_t m_parsedLen; + + /** Offset to the start token of the next event. */ + size_t m_nextEventOffset; +}; + +//- EventBuffer Inline Private Methods ----------------------------------------- +inline size_t +EventBuffer::UnParsed() +{ + return (m_validLen - m_parsedLen); +} + +inline size_t +EventBuffer::NextEventMaxLen() +{ + return (m_validLen - m_nextEventOffset); +} + +/*--------------------------------- ZfsDaemon --------------------------------*/ +/** + * Static singleton orchestrating the operations of the ZFS daemon program. + */ +class ZfsDaemon +{ +public: + /** + * Used by signal handlers to ensure, in a race free way, that + * the event loop will perform at least one more full loop + * before sleeping again. + */ + static void WakeEventLoop(); + + /** + * Schedules a rescan of devices in the system for potential + * candidates to replace a missing vdev. The scan is performed + * during the next run of the event loop. + */ + static void RequestSystemRescan(); + + /** + * Queue an event for replay after the next ZFS configuration + * sync event is received. This facility is used when an event + * is received for a pool or vdev that is not visible in the + * current ZFS configuration, but may "arrive" once the kernel + * commits the configuration change that emitted the event. + */ + static bool SaveEvent(const DevCtlEvent &event); + + /** + * Reprocess any events saved via the SaveEvent() facility. + */ + static void ReplayUnconsumedEvents(); + + /** Daemonize and perform all functions of the ZFS daemon. */ + static void Run(); + +private: + /** Initialize the daemon. */ + static void Init(); + + /** Perform any necessary cleanup at daemon shutdown. */ + static void Fini(); + + /** Process incoming devctl events from devd. */ + static void ProcessEvents(EventBuffer &eventBuffer); + + /** Discard all data pending in s_devdSockFD. */ + static void FlushEvents(); + + static VdevCallback_t VdevAddCaseFile; + + /** + * Test for data pending in s_devdSockFD + * + * \return True if data is pending. Otherwise false. + */ + static bool EventsPending(); + + /** Purge our cache of outstanding ZFS issues in the system. */ + static void PurgeCaseFiles(); + + /** Build a cache of outstanding ZFS issues in the system. */ + static void BuildCaseFiles(); + + /** + * Iterate over all known issues and attempt to solve them + * given resources currently available in the system. + */ + static void RescanSystem(); + + /** + * Interrogate the system looking for previously unknown + * faults that occurred either before ZFSD was started, + * or during a period of lost communication with Devd. + */ + static void DetectMissedEvents(); + + /** + * Wait for and process event source activity. + */ + static void EventLoop(); + + /** + * Signal handler for which our response is to + * log the current state of the daemon. + * + * \param sigNum The signal caught. + */ + static void InfoSignalHandler(int sigNum); + + /** + * Signal handler for which our response is to + * request a case rescan. + * + * \param sigNum The signal caught. + */ + static void RescanSignalHandler(int sigNum); + + /** + * Signal handler for which our response is to + * gracefully terminate. + * + * \param sigNum The signal caught. + */ + static void QuitSignalHandler(int sigNum); + + /** + * Open and lock our PID file. + */ + static void OpenPIDFile(); + + /** + * Update our PID file with our PID. + */ + static void UpdatePIDFile(); + + /** + * Close and release the lock on our PID file. + */ + static void ClosePIDFile(); + + /** + * Perform syslog configuraiton. + */ + static void InitializeSyslog(); + + /** + * Open a connection to devd's unix domain socket. + * + * \return True if the connection attempt is successsful. Otherwise + * false. + */ + static bool ConnectToDevd(); + + /** + * Close a connection (if any) to devd's unix domain socket. + */ + static void DisconnectFromDevd(); + + /** + * Set to true when our program is signaled to + * gracefully exit. + */ + static bool s_logCaseFiles; + + /** + * Set to true when our program is signaled to + * gracefully exit. + */ + static bool s_terminateEventLoop; + + /** + * The canonical path and file name of zfsd's PID file. + */ + static char s_pidFilePath[]; + + /** + * Control structure for PIDFILE(3) API. + */ + static pidfh *s_pidFH; + + /** + * File descriptor representing the unix domain socket + * connection with devd. + */ + static int s_devdSockFD; + + /** + * Pipe file descriptors used to close races with our + * signal handlers. + */ + static int s_signalPipeFD[2]; + + /** Queued events for replay. */ + static DevCtlEventList s_unconsumedEvents; + + /** + * Flag controlling a rescan from ZFSD's event loop of all + * GEOM providers in the system to find candidates for solving + * cases. + */ + static bool s_systemRescanRequested; + + /** + * Flag controlling whether events can be queued. This boolean + * is set during event replay to ensure that events for pools or + * devices no longer in the system are not retained forever. + */ + static bool s_consumingEvents; +}; + +#endif /* _ZFSD_H_ */ diff --git a/cddl/sbin/zfsd/zfsd_exception.cc b/cddl/sbin/zfsd/zfsd_exception.cc new file mode 100644 index 00000000000..f87e6758042 --- /dev/null +++ b/cddl/sbin/zfsd/zfsd_exception.cc @@ -0,0 +1,157 @@ +/*- + * Copyright (c) 2011 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + */ + +/** + * \file zfsd_exception + * + * Implementation of the ZfsdException class. + */ +#include +#include + +#include +#include + +#include "vdev.h" +#include "zfsd_exception.h" + +/*============================ Namespace Control =============================*/ +using std::endl; +using std::string; +using std::stringstream; + +/*=========================== Class Implementations ==========================*/ +/*------------------------------- ZfsdException ------------------------------*/ +void +ZfsdException::FormatLog(const char *fmt, va_list ap) +{ + char buf[256]; + + vsnprintf(buf, sizeof(buf), fmt, ap); + m_log = buf; +} + +ZfsdException::ZfsdException(const char *fmt, ...) + : m_poolConfig(NULL), + m_vdevConfig(NULL) +{ + va_list ap; + + va_start(ap, fmt); + FormatLog(fmt, ap); + va_end(ap); +} + +ZfsdException::ZfsdException(zpool_handle_t *pool, const char *fmt, ...) + : m_poolConfig(zpool_get_config(pool, NULL)), + m_vdevConfig(NULL) +{ + va_list ap; + + va_start(ap, fmt); + FormatLog(fmt, ap); + va_end(ap); +} + +ZfsdException::ZfsdException(nvlist_t *poolConfig, const char *fmt, ...) + : m_poolConfig(poolConfig), + m_vdevConfig(NULL) +{ + va_list ap; + + va_start(ap, fmt); + FormatLog(fmt, ap); + va_end(ap); +} + +inline +ZfsdException::ZfsdException(zpool_handle_t *pool, nvlist_t *vdevConfig, + const char *fmt, ...) + : m_poolConfig(zpool_get_config(pool, NULL)), + m_vdevConfig(vdevConfig) +{ + va_list ap; + + va_start(ap, fmt); + FormatLog(fmt, ap); + va_end(ap); +} + +inline +ZfsdException::ZfsdException(nvlist_t *poolConfig, nvlist_t *vdevConfig, + const char *fmt, ...) + : m_poolConfig(poolConfig), + m_vdevConfig(vdevConfig) +{ + va_list ap; + + va_start(ap, fmt); + FormatLog(fmt, ap); + va_end(ap); +} + +void +ZfsdException::Log() const +{ + stringstream output; + + if (m_poolConfig != NULL) { + + output << "Pool "; + + char *poolName; + if (nvlist_lookup_string(m_poolConfig, ZPOOL_CONFIG_POOL_NAME, + &poolName) == 0) + output << poolName; + else + output << "Unkown"; + output << ": "; + } + + if (m_vdevConfig != NULL) { + + if (m_poolConfig != NULL) { + Vdev vdev(m_poolConfig, m_vdevConfig); + + output << "Vdev " << vdev.GUID() << ": "; + } else { + Vdev vdev(m_vdevConfig); + + output << "Pool " << vdev.PoolGUID() << ": "; + output << "Vdev " << vdev.GUID() << ": "; + } + } + + output << m_log << endl; + syslog(LOG_ERR, output.str().c_str()); +} + diff --git a/cddl/sbin/zfsd/zfsd_exception.h b/cddl/sbin/zfsd/zfsd_exception.h new file mode 100644 index 00000000000..d8a9fed29db --- /dev/null +++ b/cddl/sbin/zfsd/zfsd_exception.h @@ -0,0 +1,158 @@ +/*- + * Copyright (c) 2011 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + */ + +/** + * \file zfsd_exception.h + * + * Definition of the ZfsdException class hierarchy. All exceptions + * explicitly thrown by Zfsd are defined here. + */ +#ifndef _ZFSD_EXCEPTION_H_ +#define _ZFSD_EXCEPTION_H_ + +#include + +/*=========================== Forward Declarations ===========================*/ +struct zpool_handle; +typedef struct zpool_handle zpool_handle_t; + +struct nvlist; +typedef struct nvlist nvlist_t; + +/*============================= Class Definitions ============================*/ +/*------------------------------- ZfsdException ------------------------------*/ +/** + * \brief Class allowing unified reporting/logging of exceptional events. + */ +class ZfsdException +{ +public: + /** + * \brief ZfsdException constructor allowing arbitrary string + * data to be reported. + * + * \param fmt Printf-like string format specifier. + */ + ZfsdException(const char *fmt, ...); + + /** + * \brief ZfsdException constructor allowing arbitrary string + * data to be reported and associated with the configuration + * data for a ZFS pool. + * + * \param pool Pool handle describing the pool to which this + * exception is associated. + * \param fmt Printf-like string format specifier. + * + * Instantiation with this method is used to report global + * pool errors. + */ + ZfsdException(zpool_handle_t *pool, const char *, ...); + + /** + * \brief ZfsdException constructor allowing arbitrary string + * data to be reported and associated with the configuration + * data for a ZFS pool. + * + * \param poolConfig Pool configuration describing the pool to + * which this exception is associated. + * \param fmt Printf-like string format specifier. + * + * Instantiation with this method is used to report global + * pool errors. + */ + ZfsdException(nvlist_t *poolConfig, const char *, ...); + + /** + * \brief ZfsdException constructor allowing arbitrary string + * data to be reported and associated with the configuration + * data for a single vdev and its parent pool. + * + * \param pool Pool handle describing the pool to which this + * exception is associated. + * \param vdevConfig A name/value list describing the vdev + * to which this exception is associated. + * \param fmt Printf-like string format specifier. + * + * Instantiation with this method is used to report errors + * associated with a vdev when both the vdev's config and + * its pool membership are available. + */ + ZfsdException(zpool_handle_t *pool, nvlist_t *vdevConfig, + const char *fmt, ...); + + /** + * \brief ZfsdException constructor allowing arbitrary string + * data to be reported and associated with the configuration + * data for a single vdev and its parent pool. + * + * \param poolConfig Pool configuration describing the pool to + * which this exception is associated. + * \param vdevConfig A name/value list describing the vdev + * to which this exception is associated. + * \param fmt Printf-like string format specifier. + * + * Instantiation with this method is used to report errors + * associated with a vdev when both the vdev's config and + * its pool membership are available. + */ + ZfsdException(nvlist_t *poolConfig, nvlist_t *vdevConfig, + const char *fmt, ...); + + /** + * \brief Augment/Modify a ZfsdException's string data. + */ + std::string& GetString(); + + /** + * \brief Emit exception data to syslog(3). + */ + void Log() const; +private: + /** + * \brief Convert exception string data and arguments provided + * in ZfsdException constructors into a linear string. + */ + void FormatLog(const char *fmt, va_list ap); + + nvlist_t *m_poolConfig; + nvlist_t *m_vdevConfig; + std::string m_log; +}; + +inline std::string & +ZfsdException::GetString() +{ + return (m_log); +} + +#endif /* _ZFSD_EXCEPTION_H_ */ diff --git a/cddl/sbin/zfsd/zpool_list.cc b/cddl/sbin/zfsd/zpool_list.cc new file mode 100644 index 00000000000..32f9ea8e893 --- /dev/null +++ b/cddl/sbin/zfsd/zpool_list.cc @@ -0,0 +1,98 @@ +/*- + * Copyright (c) 2011 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + */ + +/** + * \file zpool_list.cc + * + * Implementation of the ZpoolList class. + */ +#include "zpool_list.h" +#include "zfsd.h" + +/*=========================== Class Implementations ==========================*/ +/*--------------------------------- ZpoolList --------------------------------*/ +bool +ZpoolList::ZpoolAll(zpool_handle_t *pool, nvlist_t *poolConfig, void *cbArg) +{ + return (true); +} + +bool +ZpoolList::ZpoolByGUID(zpool_handle_t *pool, nvlist_t *poolConfig, + void *cbArg) +{ + uint64_t *desiredPoolGUID(static_cast(cbArg)); + uint64_t poolGUID; + + /* We are only intested in the pool that matches our pool GUID. */ + return (nvlist_lookup_uint64(poolConfig, ZPOOL_CONFIG_POOL_GUID, + &poolGUID) == 0 + && poolGUID == *desiredPoolGUID); +} + +bool +ZpoolList::ZpoolByName(zpool_handle_t *pool, nvlist_t *poolConfig, void *cbArg) +{ + const string &desiredPoolName(*static_cast(cbArg)); + + /* We are only intested in the pool that matches our pool GUID. */ + return (desiredPoolName == zpool_get_name(pool)); +} + +int +ZpoolList::LoadIterator(zpool_handle_t *pool, void *data) +{ + ZpoolList *zpl(reinterpret_cast(data)); + nvlist_t *poolConfig(zpool_get_config(pool, NULL)); + + if (zpl->m_filter(pool, poolConfig, zpl->m_filterArg)) { + zpl->push_back(pool); + } else { + zpool_close(pool); + } + return (0); +} + +ZpoolList::ZpoolList(PoolFilter_t *filter, void * filterArg) + : m_filter(filter), + m_filterArg(filterArg) +{ + zpool_iter(g_zfsHandle, LoadIterator, this); +} + +ZpoolList::~ZpoolList() +{ + for (iterator it(begin()); it != end(); it++) + zpool_close(*it); + + clear(); +} diff --git a/cddl/sbin/zfsd/zpool_list.h b/cddl/sbin/zfsd/zpool_list.h new file mode 100644 index 00000000000..8f42e5dd879 --- /dev/null +++ b/cddl/sbin/zfsd/zpool_list.h @@ -0,0 +1,126 @@ +/*- + * Copyright (c) 2011 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + */ + +/** + * \file zpool_list.h + * + * ZpoolList class definition. ZpoolList is a standard container + * allowing filtering and iteration of imported ZFS pool information. + */ +#ifndef _ZPOOL_LIST_H_ +#define _ZPOOL_LIST_H_ + +#include +#include + +#include +#include + +/*============================ Namespace Control =============================*/ +using std::string; + +/*=========================== Forward Declarations ===========================*/ +class Vdev; + +/*============================= Class Definitions ============================*/ +/*--------------------------------- ZpoolList --------------------------------*/ +class ZpoolList; +typedef bool PoolFilter_t(zpool_handle_t *pool, nvlist_t *poolConfig, + void *filterArg); + +/** + * \brief Container of imported ZFS pool data. + * + * ZpoolList is a convenience class that converts libzfs's ZFS + * pool methods into a standard list container. + */ +class ZpoolList : public std::list +{ +public: + /** + * \brief Utility ZpoolList construction filter that causes all + * pools known to the system to be included in the + * intantiated ZpoolList. + */ + static PoolFilter_t ZpoolAll; + + /** + * \brief Utility ZpoolList construction filter that causes only + * a pool known to the system and having the specified GUID + * to be included in the intantiated ZpoolList. + */ + static PoolFilter_t ZpoolByGUID; + + /** + * \brief Utility ZpoolList construction filter that causes only + * pools known to the system and having the specified name + * to be included in the intantiated ZpoolList. + */ + static PoolFilter_t ZpoolByName; + + /** + * \brief ZpoolList contructor + * + * \param filter The filter function to use when constructing + * the ZpoolList. This may be one of the static + * utility filters defined for ZpoolList or a + * user defined function. + * \param filterArg A single argument to pass into the filter function + * when it is invoked on each candidate pool. + */ + ZpoolList(PoolFilter_t *filter = ZpoolAll, void *filterArg = NULL); + ~ZpoolList(); + +private: + /** + * \brief Helper routine used to populate the internal + * data store of ZFS pool objects using libzfs's + * zpool_iter() function. + * + * \param pool The ZFS pool object to filter. + * \param data User argument passed through zpool_iter(). + */ + static int LoadIterator(zpool_handle_t *pool, void *data); + + /** + * \brief The filter with which this ZpoolList was constructed. + */ + PoolFilter_t *m_filter; + + /** + * \brief The filter argument with which this ZpoolList was + * constructed. + */ + void *m_filterArg; +}; + +#endif /* _ZPOOL_ITERATOR_H_ */ diff --git a/etc/defaults/rc.conf b/etc/defaults/rc.conf index d467eaac6c1..fe37697d593 100644 --- a/etc/defaults/rc.conf +++ b/etc/defaults/rc.conf @@ -59,6 +59,10 @@ rc_conf_files="/etc/rc.conf /etc/rc.conf.local" # ZFS support zfs_enable="NO" # Set to YES to automatically mount ZFS file systems +# ZFSD support +zfsd_enable="NO" # Set to YES to automatically start the ZFS fault + # management daemon. + gptboot_enable="YES" # GPT boot success/failure reporting. # Experimental - test before enabling diff --git a/etc/mtree/BSD.root.dist b/etc/mtree/BSD.root.dist index a6cba247449..86372d85471 100644 --- a/etc/mtree/BSD.root.dist +++ b/etc/mtree/BSD.root.dist @@ -65,6 +65,8 @@ ssl .. zfs + cases + .. .. .. lib diff --git a/etc/rc.d/zfsd b/etc/rc.d/zfsd new file mode 100644 index 00000000000..3167a22505b --- /dev/null +++ b/etc/rc.d/zfsd @@ -0,0 +1,17 @@ +#!/bin/sh +# +# $FreeBSD$ +# + +# PROVIDE: zfsd +# REQUIRE: devd zfs +# KEYWORD: nojail shutdown + +. /etc/rc.subr + +name="zfsd" +rcvar=`set_rcvar` +command="/sbin/${name}" + +load_rc_config $name +run_rc_command "$1" From f0bbee5fdb4d1a70958c11dcab1b1538ca288ffc Mon Sep 17 00:00:00 2001 From: Alexander Motin Date: Sat, 20 Aug 2011 09:31:52 +0000 Subject: [PATCH 12/89] Add support for SATA Enclosure Management Bridge (SEMB). --- sbin/camcontrol/camcontrol.c | 23 ++++- sys/cam/ata/ata_all.c | 67 ++++++++++++ sys/cam/ata/ata_all.h | 26 +++++ sys/cam/ata/ata_da.c | 18 +++- sys/cam/ata/ata_xpt.c | 172 +++++++++++++++++++++++++++++-- sys/cam/cam_ccb.h | 1 + sys/cam/cam_xpt.c | 3 + sys/cam/scsi/scsi_all.h | 2 + sys/cam/scsi/scsi_enc.c | 101 ++++++++++++------ sys/cam/scsi/scsi_enc_internal.h | 4 +- sys/cam/scsi/scsi_enc_ses.c | 31 ++++-- sys/sys/ata.h | 1 + 12 files changed, 395 insertions(+), 54 deletions(-) diff --git a/sbin/camcontrol/camcontrol.c b/sbin/camcontrol/camcontrol.c index 7ad79d5dcb7..aff42a5459e 100644 --- a/sbin/camcontrol/camcontrol.c +++ b/sbin/camcontrol/camcontrol.c @@ -456,7 +456,7 @@ getdevtree(void) case DEV_MATCH_DEVICE: { struct device_match_result *dev_result; char vendor[16], product[48], revision[16]; - char tmpstr[256]; + char fw[5], tmpstr[256]; dev_result = &ccb.cdm.matches[i].result.device_result; @@ -495,6 +495,25 @@ getdevtree(void) sizeof(revision)); sprintf(tmpstr, "<%s %s>", product, revision); + } else if (dev_result->protocol == PROTO_SEMB) { + struct sep_identify_data *sid; + + sid = (struct sep_identify_data *) + &dev_result->ident_data; + cam_strvis(vendor, sid->vendor_id, + sizeof(sid->vendor_id), + sizeof(vendor)); + cam_strvis(product, sid->product_id, + sizeof(sid->product_id), + sizeof(product)); + cam_strvis(revision, sid->product_rev, + sizeof(sid->product_rev), + sizeof(revision)); + cam_strvis(fw, sid->firmware_rev, + sizeof(sid->firmware_rev), + sizeof(fw)); + sprintf(tmpstr, "<%s %s %s %s>", + vendor, product, revision, fw); } else { sprintf(tmpstr, "<>"); } @@ -1131,7 +1150,7 @@ atacapprint(struct ata_params *parm) printf("firmware revision %.8s\n", parm->revision); printf("serial number %.20s\n", parm->serial); if (parm->enabled.extension & ATA_SUPPORT_64BITWWN) { - printf("WWN %02x%02x%02x%02x\n", + printf("WWN %04x%04x%04x%04x\n", parm->wwn[0], parm->wwn[1], parm->wwn[2], parm->wwn[3]); } if (parm->enabled.extension & ATA_SUPPORT_MEDIASN) { diff --git a/sys/cam/ata/ata_all.c b/sys/cam/ata/ata_all.c index 560eef4752e..5bf59f9f3d0 100644 --- a/sys/cam/ata/ata_all.c +++ b/sys/cam/ata/ata_all.c @@ -108,6 +108,16 @@ ata_op_string(struct ata_cmd *cmd) case 0x51: return ("CONFIGURE_STREAM"); case 0x60: return ("READ_FPDMA_QUEUED"); case 0x61: return ("WRITE_FPDMA_QUEUED"); + case 0x67: + if (cmd->features == 0xec) + return ("SEP_ATTN IDENTIFY"); + switch (cmd->lba_low) { + case 0x00: return ("SEP_ATTN READ BUFFER"); + case 0x02: return ("SEP_ATTN RECEIVE DIAGNOSTIC RESULTS"); + case 0x80: return ("SEP_ATTN WRITE BUFFER"); + case 0x82: return ("SEP_ATTN SEND DIAGNOSTIC"); + } + return ("SEP_ATTN"); case 0x70: return ("SEEK"); case 0x87: return ("CFA_TRANSLATE_SECTOR"); case 0x90: return ("EXECUTE_DEVICE_DIAGNOSTIC"); @@ -286,6 +296,21 @@ ata_print_ident(struct ata_params *ident_data) printf(" device\n"); } +void +semb_print_ident(struct sep_identify_data *ident_data) +{ + char vendor[9], product[17], revision[5], fw[5], in[7], ins[5]; + + cam_strvis(vendor, ident_data->vendor_id, 8, sizeof(vendor)); + cam_strvis(product, ident_data->product_id, 16, sizeof(product)); + cam_strvis(revision, ident_data->product_rev, 4, sizeof(revision)); + cam_strvis(fw, ident_data->firmware_rev, 4, sizeof(fw)); + cam_strvis(in, ident_data->interface_id, 6, sizeof(in)); + cam_strvis(ins, ident_data->interface_rev, 4, sizeof(ins)); + printf("<%s %s %s %s> SEMB %s %s device\n", + vendor, product, revision, fw, in, ins); +} + uint32_t ata_logical_sector_size(struct ata_params *ident_data) { @@ -695,3 +720,45 @@ ata_static_identify_match(caddr_t identbuffer, caddr_t table_entry) } return (-1); } + +void +semb_receive_diagnostic_results(struct ccb_ataio *ataio, + u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb*), + uint8_t tag_action, int pcv, uint8_t page_code, + uint8_t *data_ptr, uint16_t length, uint32_t timeout) +{ + + length = min(length, 1020); + length = (length + 3) & ~3; + cam_fill_ataio(ataio, + retries, + cbfcnp, + /*flags*/CAM_DIR_IN, + tag_action, + data_ptr, + length, + timeout); + ata_28bit_cmd(ataio, ATA_SEP_ATTN, + pcv ? page_code : 0, 0x02, length / 4); +} + +void +semb_send_diagnostic(struct ccb_ataio *ataio, + u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), + uint8_t tag_action, uint8_t *data_ptr, uint16_t length, uint32_t timeout) +{ + + length = min(length, 1020); + length = (length + 3) & ~3; + cam_fill_ataio(ataio, + retries, + cbfcnp, + /*flags*/length ? CAM_DIR_OUT : CAM_DIR_NONE, + tag_action, + data_ptr, + length, + timeout); + ata_28bit_cmd(ataio, ATA_SEP_ATTN, + length > 0 ? data_ptr[0] : 0, 0x82, length / 4); +} + diff --git a/sys/cam/ata/ata_all.h b/sys/cam/ata/ata_all.h index 526fc194d0c..5adc3ee0cfb 100644 --- a/sys/cam/ata/ata_all.h +++ b/sys/cam/ata/ata_all.h @@ -83,6 +83,20 @@ struct ata_res { u_int8_t sector_count_exp; }; +struct sep_identify_data { + uint8_t length; /* Enclosure descriptor length */ + uint8_t subenc_id; /* Sub-enclosure identifier */ + uint8_t logical_id[8]; /* Enclosure logical identifier (WWN) */ + uint8_t vendor_id[8]; /* Vendor identification string */ + uint8_t product_id[16]; /* Product identification string */ + uint8_t product_rev[4]; /* Product revision string */ + uint8_t channel_id; /* Channel identifier */ + uint8_t firmware_rev[4];/* Firmware revision */ + uint8_t interface_id[6];/* Interface spec ("S-E-S "/"SAF-TE")*/ + uint8_t interface_rev[4];/* Interface spec revision */ + uint8_t vend_spec[11]; /* Vendor specific information */ +}; + int ata_version(int ver); char * ata_op_string(struct ata_cmd *cmd); @@ -126,4 +140,16 @@ int ata_speed2revision(u_int speed); int ata_identify_match(caddr_t identbuffer, caddr_t table_entry); int ata_static_identify_match(caddr_t identbuffer, caddr_t table_entry); +void semb_print_ident(struct sep_identify_data *ident_data); + +void semb_receive_diagnostic_results(struct ccb_ataio *ataio, + u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb*), + uint8_t tag_action, int pcv, uint8_t page_code, + uint8_t *data_ptr, uint16_t allocation_length, uint32_t timeout); + +void semb_send_diagnostic(struct ccb_ataio *ataio, + u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), + uint8_t tag_action, uint8_t *data_ptr, uint16_t param_list_length, + uint32_t timeout); + #endif diff --git a/sys/cam/ata/ata_da.c b/sys/cam/ata/ata_da.c index b1d5ea83eb5..8f4c1d5c46b 100644 --- a/sys/cam/ata/ata_da.c +++ b/sys/cam/ata/ata_da.c @@ -754,6 +754,20 @@ adaasync(void *callback_arg, u_int32_t code, "due to status 0x%x\n", status); break; } + case AC_ADVINFO_CHANGED: + { + uintptr_t buftype; + + buftype = (uintptr_t)arg; + if (buftype == CDAI_TYPE_PHYS_PATH) { + struct ada_softc *softc; + + softc = periph->softc; + disk_attr_changed(softc->disk, "GEOM::physpath", + M_NOWAIT); + } + break; + } case AC_SENT_BDR: case AC_BUS_RESET: { @@ -1066,8 +1080,8 @@ adaregister(struct cam_periph *periph, void *arg) * them and the only alternative would be to * not attach the device on failure. */ - xpt_register_async(AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE, - adaasync, periph, periph->path); + xpt_register_async(AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE | + AC_ADVINFO_CHANGED, adaasync, periph, periph->path); /* * Schedule a periodic event to occasionally send an diff --git a/sys/cam/ata/ata_xpt.c b/sys/cam/ata/ata_xpt.c index 84ab46b52f1..8d4c3766662 100644 --- a/sys/cam/ata/ata_xpt.c +++ b/sys/cam/ata/ata_xpt.c @@ -93,6 +93,8 @@ typedef enum { PROBE_FULL_INQUIRY, PROBE_PM_PID, PROBE_PM_PRV, + PROBE_IDENTIFY_SES, + PROBE_IDENTIFY_SAFTE, PROBE_INVALID } probe_action; @@ -110,6 +112,8 @@ static char *probe_action_text[] = { "PROBE_FULL_INQUIRY", "PROBE_PM_PID", "PROBE_PM_PRV", + "PROBE_IDENTIFY_SES", + "PROBE_IDENTIFY_SAFTE", "PROBE_INVALID" }; @@ -260,7 +264,8 @@ probeschedule(struct cam_periph *periph) ccb = (union ccb *)TAILQ_FIRST(&softc->request_ccbs); if ((periph->path->device->flags & CAM_DEV_UNCONFIGURED) || - periph->path->device->protocol == PROTO_SATAPM) + periph->path->device->protocol == PROTO_SATAPM || + periph->path->device->protocol == PROTO_SEMB) PROBE_SET_ACTION(softc, PROBE_RESET); else PROBE_SET_ACTION(softc, PROBE_IDENTIFY); @@ -294,7 +299,8 @@ probestart(struct cam_periph *periph, union ccb *start_ccb) if (softc->restart) { softc->restart = 0; if ((path->device->flags & CAM_DEV_UNCONFIGURED) || - path->device->protocol == PROTO_SATAPM) + path->device->protocol == PROTO_SATAPM || + path->device->protocol == PROTO_SEMB) softc->action = PROBE_RESET; else softc->action = PROBE_IDENTIFY; @@ -609,6 +615,30 @@ negotiate: 10 * 1000); ata_pm_read_cmd(ataio, 1, 15); break; + case PROBE_IDENTIFY_SES: + cam_fill_ataio(ataio, + 1, + probedone, + /*flags*/CAM_DIR_IN, + 0, + /*data_ptr*/(u_int8_t *)&softc->ident_data, + /*dxfer_len*/sizeof(softc->ident_data), + 30 * 1000); + ata_28bit_cmd(ataio, ATA_SEP_ATTN, 0xEC, 0x02, + sizeof(softc->ident_data) / 4); + break; + case PROBE_IDENTIFY_SAFTE: + cam_fill_ataio(ataio, + 1, + probedone, + /*flags*/CAM_DIR_IN, + 0, + /*data_ptr*/(u_int8_t *)&softc->ident_data, + /*dxfer_len*/sizeof(softc->ident_data), + 30 * 1000); + ata_28bit_cmd(ataio, ATA_SEP_ATTN, 0xEC, 0x00, + sizeof(softc->ident_data) / 4); + break; case PROBE_INVALID: CAM_DEBUG(path, CAM_DEBUG_INFO, ("probestart: invalid action state\n")); @@ -745,12 +775,16 @@ probedone(struct cam_periph *periph, union ccb *done_ccb) { struct ccb_trans_settings cts; struct ata_params *ident_buf; + struct scsi_inquiry_data *inq_buf; probe_softc *softc; struct cam_path *path; cam_status status; u_int32_t priority; u_int caps; - int found = 1; + int changed = 1, found = 1; + static const uint8_t fake_device_id_hdr[8] = + {0, SVPD_DEVICE_ID, 0, 12, + SVPD_ID_CODESET_BINARY, SVPD_ID_TYPE_NAA, 0, 8}; CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("probedone\n")); @@ -758,6 +792,7 @@ probedone(struct cam_periph *periph, union ccb *done_ccb) path = done_ccb->ccb_h.path; priority = done_ccb->ccb_h.pinfo.priority; ident_buf = &path->device->ident_data; + inq_buf = &path->device->inq_data; if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { if (softc->restart) { @@ -806,6 +841,18 @@ probedone(struct cam_periph *periph, union ccb *done_ccb) } else if (softc->action == PROBE_SETDMAAA && status == CAM_ATA_STATUS_ERROR) { goto noerror; + + /* + * SES and SAF-TE SEPs have different IDENTIFY commands, + * but SATA specification doesn't tell how to identify them. + * Until better way found, just try another if first fail. + */ + } else if (softc->action == PROBE_IDENTIFY_SES && + status == CAM_ATA_STATUS_ERROR) { + PROBE_SET_ACTION(softc, PROBE_IDENTIFY_SAFTE); + xpt_release_ccb(done_ccb); + xpt_schedule(periph, priority); + return; } /* @@ -849,6 +896,10 @@ noerror: xpt_action((union ccb *)&cts); path->device->protocol = PROTO_SATAPM; PROBE_SET_ACTION(softc, PROBE_PM_PID); + } else if (sign == 0xc33c && + done_ccb->ccb_h.target_id != 15) { + path->device->protocol = PROTO_SEMB; + PROBE_SET_ACTION(softc, PROBE_IDENTIFY_SES); } else if (sign == 0xeb14 && done_ccb->ccb_h.target_id != 15) { path->device->protocol = PROTO_SCSI; @@ -868,7 +919,6 @@ noerror: { struct ccb_pathinq cpi; int16_t *ptr; - int changed = 1; ident_buf = &softc->ident_data; for (ptr = (int16_t *)ident_buf; @@ -923,6 +973,11 @@ noerror: path->device->serial_num = NULL; path->device->serial_num_len = 0; } + if (path->device->device_id != NULL) { + free(path->device->device_id, M_CAMXPT); + path->device->device_id = NULL; + path->device->device_id_len = 0; + } path->device->serial_num = (u_int8_t *)malloc((sizeof(ident_buf->serial) + 1), M_CAMXPT, M_NOWAIT); @@ -935,6 +990,18 @@ noerror: path->device->serial_num_len = strlen(path->device->serial_num); } + if (ident_buf->enabled.extension & + ATA_SUPPORT_64BITWWN) { + path->device->device_id = + malloc(16, M_CAMXPT, M_NOWAIT); + if (path->device->device_id != NULL) { + path->device->device_id_len = 16; + bcopy(&fake_device_id_hdr, + path->device->device_id, 8); + bcopy(ident_buf->wwn, + path->device->device_id + 8, 8); + } + } path->device->flags |= CAM_DEV_IDENTIFY_DATA_VALID; } @@ -1079,11 +1146,9 @@ notsata: case PROBE_INQUIRY: case PROBE_FULL_INQUIRY: { - struct scsi_inquiry_data *inq_buf; u_int8_t periph_qual, len; path->device->flags |= CAM_DEV_INQUIRY_DATA_VALID; - inq_buf = &path->device->inq_data; periph_qual = SID_QUAL(inq_buf); @@ -1187,6 +1252,48 @@ notsata: xpt_async(AC_SCSI_AEN, done_ccb->ccb_h.path, done_ccb); } break; + case PROBE_IDENTIFY_SES: + case PROBE_IDENTIFY_SAFTE: + if ((periph->path->device->flags & CAM_DEV_UNCONFIGURED) == 0) { + /* Check that it is the same device. */ + if (bcmp(&softc->ident_data, ident_buf, 53)) { + /* Device changed. */ + xpt_async(AC_LOST_DEVICE, path, NULL); + } else { + bcopy(&softc->ident_data, ident_buf, sizeof(struct ata_params)); + changed = 0; + } + } + if (changed) { + bcopy(&softc->ident_data, ident_buf, sizeof(struct ata_params)); + /* Clean up from previous instance of this device */ + if (path->device->device_id != NULL) { + free(path->device->device_id, M_CAMXPT); + path->device->device_id = NULL; + path->device->device_id_len = 0; + } + path->device->device_id = + malloc(16, M_CAMXPT, M_NOWAIT); + if (path->device->device_id != NULL) { + path->device->device_id_len = 16; + bcopy(&fake_device_id_hdr, + path->device->device_id, 8); + bcopy(((uint8_t*)ident_buf) + 2, + path->device->device_id + 8, 8); + } + + path->device->flags |= CAM_DEV_IDENTIFY_DATA_VALID; + } + + if (periph->path->device->flags & CAM_DEV_UNCONFIGURED) { + path->device->flags &= ~CAM_DEV_UNCONFIGURED; + xpt_acquire_device(path->device); + done_ccb->ccb_h.func_code = XPT_GDEV_TYPE; + xpt_action(done_ccb); + xpt_async(AC_FOUND_DEVICE, done_ccb->ccb_h.path, + done_ccb); + } + break; case PROBE_INVALID: CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_INFO, ("probedone: invalid action state\n")); @@ -1611,10 +1718,20 @@ ata_dev_advinfo(union ccb *start_ccb) device = start_ccb->ccb_h.path->device; cdai = &start_ccb->cdai; switch(cdai->buftype) { + case CDAI_TYPE_SCSI_DEVID: + if (cdai->flags & CDAI_FLAG_STORE) + return; + cdai->provsiz = device->device_id_len; + if (device->device_id_len == 0) + break; + amt = device->device_id_len; + if (cdai->provsiz > cdai->bufsiz) + amt = cdai->bufsiz; + memcpy(cdai->buf, device->device_id, amt); + break; case CDAI_TYPE_SERIAL_NUM: if (cdai->flags & CDAI_FLAG_STORE) - break; - start_ccb->ccb_h.status = CAM_REQ_CMP; + return; cdai->provsiz = device->serial_num_len; if (device->serial_num_len == 0) break; @@ -1623,8 +1740,45 @@ ata_dev_advinfo(union ccb *start_ccb) amt = cdai->bufsiz; memcpy(cdai->buf, device->serial_num, amt); break; - default: + case CDAI_TYPE_PHYS_PATH: + if (cdai->flags & CDAI_FLAG_STORE) { + if (device->physpath != NULL) + free(device->physpath, M_CAMXPT); + device->physpath_len = cdai->bufsiz; + /* Clear existing buffer if zero length */ + if (cdai->bufsiz == 0) + break; + device->physpath = malloc(cdai->bufsiz, M_CAMXPT, M_NOWAIT); + if (device->physpath == NULL) { + start_ccb->ccb_h.status = CAM_REQ_ABORTED; + return; + } + memcpy(device->physpath, cdai->buf, cdai->bufsiz); + } else { + cdai->provsiz = device->physpath_len; + if (device->physpath_len == 0) + break; + amt = device->physpath_len; + if (cdai->provsiz > cdai->bufsiz) + amt = cdai->bufsiz; + memcpy(cdai->buf, device->physpath, amt); + } break; + default: + return; + } + start_ccb->ccb_h.status = CAM_REQ_CMP; + + if (cdai->flags & CDAI_FLAG_STORE) { + int owned; + + owned = mtx_owned(start_ccb->ccb_h.path->bus->sim->mtx); + if (owned == 0) + mtx_lock(start_ccb->ccb_h.path->bus->sim->mtx); + xpt_async(AC_ADVINFO_CHANGED, start_ccb->ccb_h.path, + (void *)(uintptr_t)cdai->buftype); + if (owned == 0) + mtx_unlock(start_ccb->ccb_h.path->bus->sim->mtx); } } diff --git a/sys/cam/cam_ccb.h b/sys/cam/cam_ccb.h index ed2a8907689..c737f62456c 100644 --- a/sys/cam/cam_ccb.h +++ b/sys/cam/cam_ccb.h @@ -242,6 +242,7 @@ typedef enum { PROTO_ATA, /* AT Attachment */ PROTO_ATAPI, /* AT Attachment Packetized Interface */ PROTO_SATAPM, /* SATA Port Multiplier */ + PROTO_SEMB, /* SATA Enclosure Management Bridge */ } cam_proto; typedef enum { diff --git a/sys/cam/cam_xpt.c b/sys/cam/cam_xpt.c index 10b89c77b9b..a0c80f90fc7 100644 --- a/sys/cam/cam_xpt.c +++ b/sys/cam/cam_xpt.c @@ -1081,6 +1081,9 @@ xpt_announce_periph(struct cam_periph *periph, char *announce_string) else if (path->device->protocol == PROTO_ATA || path->device->protocol == PROTO_SATAPM) ata_print_ident(&path->device->ident_data); + else if (path->device->protocol == PROTO_SEMB) + semb_print_ident( + (struct sep_identify_data *)&path->device->ident_data); else printf("Unknown protocol device\n"); if (bootverbose && path->device->serial_num_len > 0) { diff --git a/sys/cam/scsi/scsi_all.h b/sys/cam/scsi/scsi_all.h index 25442f1484a..6ce1d37d5b9 100644 --- a/sys/cam/scsi/scsi_all.h +++ b/sys/cam/scsi/scsi_all.h @@ -869,6 +869,8 @@ struct scsi_vpd_id_descriptor #define SCSI_PROTO_RDMA 0x04 #define SCSI_PROTO_iSCSI 0x05 #define SCSI_PROTO_SAS 0x06 +#define SCSI_PROTO_ADT 0x07 +#define SCSI_PROTO_ATA 0x08 #define SVPD_ID_PROTO_SHIFT 4 #define SVPD_ID_CODESET_BINARY 0x01 #define SVPD_ID_CODESET_ASCII 0x02 diff --git a/sys/cam/scsi/scsi_enc.c b/sys/cam/scsi/scsi_enc.c index 993a344c8c5..224e160533d 100644 --- a/sys/cam/scsi/scsi_enc.c +++ b/sys/cam/scsi/scsi_enc.c @@ -74,7 +74,7 @@ static periph_dtor_t enc_dtor; static periph_start_t enc_start; static void enc_async(void *, uint32_t, struct cam_path *, void *); -static enctyp enc_type(void *, int); +static enctyp enc_type(struct ccb_getdev *); static struct periph_driver encdriver = { enc_init, "enc", @@ -183,7 +183,6 @@ enc_async(void *callback_arg, uint32_t code, struct cam_path *path, void *arg) { struct ccb_getdev *cgd; cam_status status; - int inq_len; path_id_t path_id; cgd = (struct ccb_getdev *)arg; @@ -191,23 +190,7 @@ enc_async(void *callback_arg, uint32_t code, struct cam_path *path, void *arg) break; } - if (cgd->protocol != PROTO_SCSI) - break; - - inq_len = cgd->inq_data.additional_length + 4; - - /* - * PROBLEM: WE NEED TO LOOK AT BYTES 48-53 TO SEE IF THIS - * PROBLEM: IS A SAF-TE DEVICE. - */ - switch (enc_type(&cgd->inq_data, inq_len)) { - case ENC_SES: - case ENC_SES_SCSI2: - case ENC_SES_PASSTHROUGH: - case ENC_SEN: - case ENC_SAFT: - break; - default: + if (enc_type(cgd) == ENC_NONE) { /* * Schedule announcement of the ENC bindings for * this device if it is managed by a SEP. @@ -564,7 +547,7 @@ enc_ioctl(struct cdev *dev, u_long cmd, caddr_t arg_addr, int flag, int enc_runcmd(struct enc_softc *enc, char *cdb, int cdbl, char *dptr, int *dlenp) { - int error, dlen; + int error, dlen, tdlen; ccb_flags ddf; union ccb *ccb; @@ -587,9 +570,32 @@ enc_runcmd(struct enc_softc *enc, char *cdb, int cdbl, char *dptr, int *dlenp) } ccb = cam_periph_getccb(enc->periph, 1); - cam_fill_csio(&ccb->csio, 0, enc_done, ddf, MSG_SIMPLE_Q_TAG, dptr, - dlen, sizeof (struct scsi_sense_data), cdbl, 60 * 1000); - bcopy(cdb, ccb->csio.cdb_io.cdb_bytes, cdbl); + if (enc->enc_type == ENC_SEMB_SES || enc->enc_type == ENC_SEMB_SAFT) { + tdlen = min(dlen, 1020); + tdlen = (tdlen + 3) & ~3; + cam_fill_ataio(&ccb->ataio, 0, enc_done, ddf, 0, dptr, tdlen, + 30 * 1000); + if (cdb[0] == RECEIVE_DIAGNOSTIC) + ata_28bit_cmd(&ccb->ataio, + ATA_SEP_ATTN, cdb[2], 0x02, tdlen / 4); + else if (cdb[0] == SEND_DIAGNOSTIC) + ata_28bit_cmd(&ccb->ataio, + ATA_SEP_ATTN, dlen > 0 ? dptr[0] : 0, + 0x82, tdlen / 4); + else if (cdb[0] == READ_BUFFER) + ata_28bit_cmd(&ccb->ataio, + ATA_SEP_ATTN, cdb[2], 0x00, tdlen / 4); + else + ata_28bit_cmd(&ccb->ataio, + ATA_SEP_ATTN, dlen > 0 ? dptr[0] : 0, + 0x80, tdlen / 4); + } else { + tdlen = dlen; + cam_fill_csio(&ccb->csio, 0, enc_done, ddf, MSG_SIMPLE_Q_TAG, + dptr, dlen, sizeof (struct scsi_sense_data), cdbl, + 60 * 1000); + bcopy(cdb, ccb->csio.cdb_io.cdb_bytes, cdbl); + } error = cam_periph_runccb(ccb, enc_error, ENC_CFLAGS, ENC_FLAGS, NULL); if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) @@ -600,7 +606,11 @@ enc_runcmd(struct enc_softc *enc, char *cdb, int cdbl, char *dptr, int *dlenp) } } else { if (dptr) { - *dlenp = ccb->csio.resid; + if (ccb->ccb_h.func_code == XPT_ATA_IO) + *dlenp = ccb->ataio.resid; + else + *dlenp = ccb->csio.resid; + *dlenp += tdlen - dlen; } } xpt_release_ccb(ccb); @@ -639,10 +649,25 @@ enc_log(struct enc_softc *enc, const char *fmt, ...) #define SAFTE_LEN SAFTE_END-SAFTE_START static enctyp -enc_type(void *buf, int buflen) +enc_type(struct ccb_getdev *cgd) { - unsigned char *iqd = buf; + int buflen; + unsigned char *iqd; + if (cgd->protocol == PROTO_SEMB) { + iqd = (unsigned char *)&cgd->ident_data; + if (STRNCMP(iqd + 43, "S-E-S", 5) == 0) + return (ENC_SEMB_SES); + else if (STRNCMP(iqd + 43, "SAF-TE", 6) == 0) + return (ENC_SEMB_SAFT); + return (ENC_NONE); + + } else if (cgd->protocol != PROTO_SCSI) + return (ENC_NONE); + + iqd = (unsigned char *)&cgd->inq_data; + buflen = min(sizeof(cgd->inq_data), + SID_ADDITIONAL_LENGTH(&cgd->inq_data)); if (buflen < 8+SEN_ID_LEN) return (ENC_NONE); @@ -743,14 +768,18 @@ enc_fsm_step(enc_softc_t *enc) if (error == 0) { - uint32_t resid; + uint32_t len; - resid = 0; - if (ccb != NULL) - resid = ccb->csio.dxfer_len - ccb->csio.resid; + len = 0; + if (ccb != NULL) { + if (ccb->ccb_h.func_code == XPT_ATA_IO) + len = ccb->ataio.dxfer_len - ccb->ataio.resid; + else + len = ccb->csio.dxfer_len - ccb->csio.resid; + } cam_periph_unlock(enc->periph); - cur_state->done(enc, cur_state, ccb, &buf, resid); + cur_state->done(enc, cur_state, ccb, &buf, len); cam_periph_lock(enc->periph); } @@ -880,16 +909,18 @@ enc_ctor(struct cam_periph *periph, void *arg) enc->periph = periph; enc->current_action = ENC_UPDATE_NONE; - enc->enc_type = enc_type(&cgd->inq_data, sizeof (cgd->inq_data)); + enc->enc_type = enc_type(cgd); sx_init(&enc->enc_cache_lock, "enccache"); switch (enc->enc_type) { case ENC_SES: case ENC_SES_SCSI2: case ENC_SES_PASSTHROUGH: + case ENC_SEMB_SES: err = ses_softc_init(enc, 1); break; case ENC_SAFT: + case ENC_SEMB_SAFT: err = safte_softc_init(enc, 1); break; case ENC_SEN: @@ -963,6 +994,12 @@ enc_ctor(struct cam_periph *periph, void *arg) case ENC_SAFT: tname = "SAF-TE Compliant Device"; break; + case ENC_SEMB_SES: + tname = "SEMB SES Device"; + break; + case ENC_SEMB_SAFT: + tname = "SEMB SAF-TE Device"; + break; } xpt_announce_periph(periph, tname); status = CAM_REQ_CMP; diff --git a/sys/cam/scsi/scsi_enc_internal.h b/sys/cam/scsi/scsi_enc_internal.h index e2a8b005a72..d2ca5d6f3f0 100644 --- a/sys/cam/scsi/scsi_enc_internal.h +++ b/sys/cam/scsi/scsi_enc_internal.h @@ -55,7 +55,9 @@ typedef enum { ENC_SES, ENC_SES_PASSTHROUGH, ENC_SEN, - ENC_SAFT + ENC_SAFT, + ENC_SEMB_SES, + ENC_SEMB_SAFT } enctyp; /* Platform Independent Driver Internal Definitions for enclosure devices. */ diff --git a/sys/cam/scsi/scsi_enc_ses.c b/sys/cam/scsi/scsi_enc_ses.c index 852a387d1c5..09fa38b794a 100644 --- a/sys/cam/scsi/scsi_enc_ses.c +++ b/sys/cam/scsi/scsi_enc_ses.c @@ -251,7 +251,7 @@ static enc_softc_cleanup_t ses_softc_cleanup; #define SCSZ 0x8000 -static fsm_fill_handler_t ses_fill_rcv_diag_csio; +static fsm_fill_handler_t ses_fill_rcv_diag_io; static fsm_fill_handler_t ses_fill_control_request; static fsm_done_handler_t ses_process_config; static fsm_done_handler_t ses_process_status; @@ -269,7 +269,7 @@ struct enc_fsm_state enc_fsm_states[SES_NUM_UPDATE_STATES] = SesConfigPage, SCSZ, 60 * 1000, - ses_fill_rcv_diag_csio, + ses_fill_rcv_diag_io, ses_process_config, enc_error }, @@ -278,7 +278,7 @@ struct enc_fsm_state enc_fsm_states[SES_NUM_UPDATE_STATES] = SesStatusPage, SCSZ, 60 * 1000, - ses_fill_rcv_diag_csio, + ses_fill_rcv_diag_io, ses_process_status, enc_error }, @@ -287,7 +287,7 @@ struct enc_fsm_state enc_fsm_states[SES_NUM_UPDATE_STATES] = SesElementDescriptor, SCSZ, 60 * 1000, - ses_fill_rcv_diag_csio, + ses_fill_rcv_diag_io, ses_process_elm_descs, enc_error }, @@ -296,7 +296,7 @@ struct enc_fsm_state enc_fsm_states[SES_NUM_UPDATE_STATES] = SesAddlElementStatus, SCSZ, 60 * 1000, - ses_fill_rcv_diag_csio, + ses_fill_rcv_diag_io, ses_process_elm_addlstatus, enc_error }, @@ -1898,13 +1898,21 @@ out: } static int -ses_fill_rcv_diag_csio(enc_softc_t *enc, struct enc_fsm_state *state, +ses_fill_rcv_diag_io(enc_softc_t *enc, struct enc_fsm_state *state, union ccb *ccb, uint8_t *buf) { - scsi_receive_diagnostic_results(&ccb->csio, /*retries*/5, + + if (enc->enc_type == ENC_SEMB_SES) { + semb_receive_diagnostic_results(&ccb->ataio, /*retries*/5, + enc_done, MSG_SIMPLE_Q_TAG, /*pcv*/1, + state->page_code, buf, state->buf_size, + state->timeout); + } else { + scsi_receive_diagnostic_results(&ccb->csio, /*retries*/5, enc_done, MSG_SIMPLE_Q_TAG, /*pcv*/1, state->page_code, buf, state->buf_size, SSD_FULL_SIZE, state->timeout); + } return (0); } @@ -2018,12 +2026,19 @@ ses_fill_control_request(enc_softc_t *enc, struct enc_fsm_state *state, return (ENOENT); /* Fill out the ccb */ - scsi_send_diagnostic(&ccb->csio, /*retries*/5, enc_done, + if (enc->enc_type == ENC_SEMB_SES) { + semb_send_diagnostic(&ccb->ataio, /*retries*/5, enc_done, + MSG_SIMPLE_Q_TAG, + buf, ses_page_length(&ses_cache->status_page->hdr), + state->timeout); + } else { + scsi_send_diagnostic(&ccb->csio, /*retries*/5, enc_done, MSG_SIMPLE_Q_TAG, /*unit_offline*/0, /*device_offline*/0, /*self_test*/0, /*page_format*/1, /*self_test_code*/0, buf, ses_page_length(&ses_cache->status_page->hdr), SSD_FULL_SIZE, state->timeout); + } return (0); } diff --git a/sys/sys/ata.h b/sys/sys/ata.h index 1d46dc12643..7f1da8dc061 100644 --- a/sys/sys/ata.h +++ b/sys/sys/ata.h @@ -318,6 +318,7 @@ struct ata_params { #define ATA_READ_VERIFY48 0x42 #define ATA_READ_FPDMA_QUEUED 0x60 /* read DMA NCQ */ #define ATA_WRITE_FPDMA_QUEUED 0x61 /* write DMA NCQ */ +#define ATA_SEP_ATTN 0x67 /* SEP request */ #define ATA_SEEK 0x70 /* seek */ #define ATA_PACKET_CMD 0xa0 /* packet command */ #define ATA_ATAPI_IDENTIFY 0xa1 /* get ATAPI params*/ From fad0e9f2fef6dec7d0d5364bf9d1fac2618166c5 Mon Sep 17 00:00:00 2001 From: Alexander Motin Date: Sat, 20 Aug 2011 19:09:25 +0000 Subject: [PATCH 13/89] Fetch list of supported diagnostic pages for SES to find whether optional Additional Element Status page is supported. If it's not, do not try to query it. --- sys/cam/scsi/scsi_all.h | 7 +++ sys/cam/scsi/scsi_enc.c | 2 +- sys/cam/scsi/scsi_enc_internal.h | 1 + sys/cam/scsi/scsi_enc_ses.c | 81 ++++++++++++++++++++++++++++++-- 4 files changed, 86 insertions(+), 5 deletions(-) diff --git a/sys/cam/scsi/scsi_all.h b/sys/cam/scsi/scsi_all.h index 6ce1d37d5b9..364ce52412a 100644 --- a/sys/cam/scsi/scsi_all.h +++ b/sys/cam/scsi/scsi_all.h @@ -992,6 +992,13 @@ struct scsi_vpd_id_scsi_name uint8_t name_string[256]; }; +struct scsi_diag_page { + uint8_t page_code; + uint8_t page_specific_flags; + uint8_t length[2]; + uint8_t params[0]; +}; + struct scsi_read_capacity { u_int8_t opcode; diff --git a/sys/cam/scsi/scsi_enc.c b/sys/cam/scsi/scsi_enc.c index 224e160533d..37c25df978d 100644 --- a/sys/cam/scsi/scsi_enc.c +++ b/sys/cam/scsi/scsi_enc.c @@ -907,7 +907,7 @@ enc_ctor(struct cam_periph *periph, void *arg) goto out; } enc->periph = periph; - enc->current_action = ENC_UPDATE_NONE; + enc->current_action = ENC_UPDATE_INVALID; enc->enc_type = enc_type(cgd); sx_init(&enc->enc_cache_lock, "enccache"); diff --git a/sys/cam/scsi/scsi_enc_internal.h b/sys/cam/scsi/scsi_enc_internal.h index d2ca5d6f3f0..93cfee74fd9 100644 --- a/sys/cam/scsi/scsi_enc_internal.h +++ b/sys/cam/scsi/scsi_enc_internal.h @@ -154,6 +154,7 @@ struct enc_softc { /* The action on which the state machine is currently working. */ uint32_t current_action; #define ENC_UPDATE_NONE 0x00 +#define ENC_UPDATE_INVALID 0xff /* Callout for auto-updating enclosure status */ struct callout status_updater; diff --git a/sys/cam/scsi/scsi_enc_ses.c b/sys/cam/scsi/scsi_enc_ses.c index 09fa38b794a..47c6427c075 100644 --- a/sys/cam/scsi/scsi_enc_ses.c +++ b/sys/cam/scsi/scsi_enc_ses.c @@ -62,6 +62,7 @@ __FBSDID("$FreeBSD: head/sys/cam/scsi/scsi_ses.c 201758 2010-01-07 21:01:37Z mbr /* SES Diagnostic Page Codes */ typedef enum { + SesSupportedPages = 0x0, SesConfigPage = 0x1, SesControlPage = 0x2, SesStatusPage = SesControlPage, @@ -237,6 +238,7 @@ struct ses_iterator { typedef enum { SES_UPDATE_NONE, + SES_UPDATE_PAGES, SES_UPDATE_GETCONFIG, SES_UPDATE_GETSTATUS, SES_UPDATE_GETELMDESCS, @@ -253,6 +255,7 @@ static enc_softc_cleanup_t ses_softc_cleanup; static fsm_fill_handler_t ses_fill_rcv_diag_io; static fsm_fill_handler_t ses_fill_control_request; +static fsm_done_handler_t ses_process_pages; static fsm_done_handler_t ses_process_config; static fsm_done_handler_t ses_process_status; static fsm_done_handler_t ses_process_elm_descs; @@ -264,6 +267,15 @@ static fsm_done_handler_t ses_publish_cache; struct enc_fsm_state enc_fsm_states[SES_NUM_UPDATE_STATES] = { { "SES_UPDATE_NONE", 0, 0, 0, NULL, NULL, NULL }, + { + "SES_UPDATE_PAGES", + SesSupportedPages, + SCSZ, + 60 * 1000, + ses_fill_rcv_diag_io, + ses_process_pages, + enc_error + }, { "SES_UPDATE_GETCONFIG", SesConfigPage, @@ -352,6 +364,7 @@ typedef struct ses_cache { typedef struct ses_softc { uint32_t ses_flags; #define SES_FLAG_TIMEDCOMP 0x01 +#define SES_FLAG_ADDLSTATUS 0x02 ses_control_reqlist_t ses_requests; ses_control_reqlist_t ses_pending_requests; @@ -1210,6 +1223,55 @@ out: return (ses->ses_flags & SES_FLAG_TIMEDCOMP); } +/** + * \brief Process the list of supported pages and update flags. + * + * \param enc SES device to query. + * \param buf Buffer containing the config page. + * \param xfer_len Length of the config page in the buffer. + * + * \return 0 on success, errno otherwise. + */ +static int +ses_process_pages(enc_softc_t *enc, struct enc_fsm_state *state, + union ccb *ccb, uint8_t **bufp, int xfer_len) +{ + ses_softc_t *ses; + struct scsi_diag_page *page; + int err, i, length; + + CAM_DEBUG(enc->periph->path, CAM_DEBUG_SUBTRACE, + ("entering %s(%p, %d)\n", __func__, bufp, xfer_len)); + ses = enc->enc_private; + err = -1; + + if (xfer_len < sizeof(*page)) { + ENC_LOG(enc, "Unable to parse Diag Pages List Header\n"); + err = EIO; + goto out; + } + page = (struct scsi_diag_page *)*bufp; + length = scsi_2btoul(page->length); + if (length + offsetof(struct scsi_diag_page, params) > xfer_len) { + ENC_LOG(enc, "Diag Pages List Too Long\n"); + goto out; + } + ENC_DLOG(enc, "%s: page length %d, xfer_len %d\n", + __func__, length, xfer_len); + + err = 0; + for (i = 0; i < length; i++) { + if (page->params[i] == SesAddlElementStatus) { + ses->ses_flags |= SES_FLAG_ADDLSTATUS; + break; + } + } + +out: + ENC_DLOG(enc, "%s: exiting with err %d\n", __func__, err); + return (err); +} + /** * \brief Process the config page and update associated structures. * @@ -1411,7 +1473,8 @@ out: else { enc_update_request(enc, SES_UPDATE_GETSTATUS); enc_update_request(enc, SES_UPDATE_GETELMDESCS); - enc_update_request(enc, SES_UPDATE_GETELMADDLSTATUS); + if (ses->ses_flags & SES_FLAG_ADDLSTATUS) + enc_update_request(enc, SES_UPDATE_GETELMADDLSTATUS); enc_update_request(enc, SES_PUBLISH_CACHE); } ENC_DLOG(enc, "%s: exiting with err %d\n", __func__, err); @@ -1817,6 +1880,7 @@ static int ses_process_elm_descs(enc_softc_t *enc, struct enc_fsm_state *state, union ccb *ccb, uint8_t **bufp, int xfer_len) { + ses_softc_t *ses; struct ses_iterator iter; enc_element_t *element; int err; @@ -1829,6 +1893,7 @@ ses_process_elm_descs(enc_softc_t *enc, struct enc_fsm_state *state, const struct ses_page_hdr *phdr; const struct ses_elm_desc_hdr *hdr; + ses = enc->enc_private; enc_cache = &enc->enc_daemon_cache; ses_cache = enc_cache->private; buf = *bufp; @@ -1891,8 +1956,10 @@ ses_process_elm_descs(enc_softc_t *enc, struct enc_fsm_state *state, err = 0; out: - if (err == 0) - enc_update_request(enc, SES_UPDATE_GETELMADDLSTATUS); + if (err == 0) { + if (ses->ses_flags & SES_FLAG_ADDLSTATUS) + enc_update_request(enc, SES_UPDATE_GETELMADDLSTATUS); + } enc_update_request(enc, SES_PUBLISH_CACHE); return (err); } @@ -2633,8 +2700,12 @@ ses_handle_string(enc_softc_t *enc, encioc_string_t *sstr, int ioc) static void ses_poll_status(enc_softc_t *enc) { + ses_softc_t *ses; + + ses = enc->enc_private; enc_update_request(enc, SES_UPDATE_GETSTATUS); - enc_update_request(enc, SES_UPDATE_GETELMADDLSTATUS); + if (ses->ses_flags & SES_FLAG_ADDLSTATUS) + enc_update_request(enc, SES_UPDATE_GETELMADDLSTATUS); } /** @@ -2710,6 +2781,8 @@ ses_softc_init(enc_softc_t *enc, int doinit) TAILQ_INIT(&ses_softc->ses_requests); TAILQ_INIT(&ses_softc->ses_pending_requests); + enc_update_request(enc, SES_UPDATE_PAGES); + // XXX: Move this to the FSM so it doesn't hang init if (0) (void) ses_set_timed_completion(enc, 1); From 68cb5c3bbe5616c3fc5d00e15b396520efb5bf07 Mon Sep 17 00:00:00 2001 From: Alexander Motin Date: Sun, 21 Aug 2011 18:23:09 +0000 Subject: [PATCH 14/89] Do not delay boot if we are not going to run daemon process. Otherwise boot will wait infinitely. --- sys/cam/scsi/scsi_enc.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/sys/cam/scsi/scsi_enc.c b/sys/cam/scsi/scsi_enc.c index 37c25df978d..627c46bb308 100644 --- a/sys/cam/scsi/scsi_enc.c +++ b/sys/cam/scsi/scsi_enc.c @@ -940,9 +940,11 @@ enc_ctor(struct cam_periph *periph, void *arg) * through our state machine so that physical path data is * present. */ - enc->enc_boot_hold_ch.ich_func = enc_nop_confighook_cb; - enc->enc_boot_hold_ch.ich_arg = enc; - config_intrhook_establish(&enc->enc_boot_hold_ch); + if (enc->enc_vec.poll_status != NULL) { + enc->enc_boot_hold_ch.ich_func = enc_nop_confighook_cb; + enc->enc_boot_hold_ch.ich_arg = enc; + config_intrhook_establish(&enc->enc_boot_hold_ch); + } /* * The softc field is set only once the enc is fully initialized From 30c2c9436ab8fd2decb1f5b8a40eb9fd77b58723 Mon Sep 17 00:00:00 2001 From: Alexander Motin Date: Tue, 23 Aug 2011 13:07:30 +0000 Subject: [PATCH 15/89] Last SAF-TE spec draft defines the field to identify real number of thermostats. Use it to report real state of things. Unluckily, when it is zero, it is impossible to say whether there are no sensors or this field is not implemented. In this case report "status not available" when no error reported to not inflate expectations. Map temperature sensors status to the respective out of range flags. --- sys/cam/scsi/scsi_enc_safte.c | 67 ++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 25 deletions(-) diff --git a/sys/cam/scsi/scsi_enc_safte.c b/sys/cam/scsi/scsi_enc_safte.c index b3caff7c055..188bfdf079a 100644 --- a/sys/cam/scsi/scsi_enc_safte.c +++ b/sys/cam/scsi/scsi_enc_safte.c @@ -83,7 +83,6 @@ static int perf_slotop(enc_softc_t *, uint8_t, uint8_t, int); #define SAFT_SCRATCH 64 -#define NPSEUDO_THERM 16 #define NPSEUDO_ALARM 1 struct scfg { /* @@ -96,6 +95,7 @@ struct scfg { uint8_t Ntherm; /* Number of Temperature Sensors */ uint8_t Nspkrs; /* Number of Speakers */ uint8_t Nalarm; /* Number of Alarms (at least one) */ + uint8_t Ntstats; /* Number of Thermostats */ /* * Cached Flag Bytes for Global Status */ @@ -158,8 +158,6 @@ safte_getconfig(enc_softc_t *ssc) ENC_FREE(sdata); return (EIO); } - ENC_VLOG(ssc, "Nfans %d Npwr %d Nslots %d Lck %d Ntherm %d Nspkrs %d\n", - sdata[0], sdata[1], sdata[2], sdata[3], sdata[4], sdata[5]); cfg->Nfans = sdata[0]; cfg->Npwr = sdata[1]; cfg->Nslots = sdata[2]; @@ -167,6 +165,14 @@ safte_getconfig(enc_softc_t *ssc) cfg->Ntherm = sdata[4]; cfg->Nspkrs = sdata[5]; cfg->Nalarm = NPSEUDO_ALARM; + if (amt >= 7) + cfg->Ntstats = sdata[6] & 0x0f; + else + cfg->Ntstats = 0; + ENC_VLOG(ssc, "Nfans %d Npwr %d Nslots %d Lck %d Ntherm %d Nspkrs %d " + "Ntstats %d\n", + cfg->Nfans, cfg->Npwr, cfg->Nslots, cfg->DoorLock, cfg->Ntherm, + cfg->Nspkrs, cfg->Ntstats); ENC_FREE(sdata); return (0); } @@ -265,7 +271,7 @@ safte_rdstat(enc_softc_t *ssc, int slpflg) * if only one fan or no thermometers, * else the NONCRITICAL error is set. */ - if (cc->Nfans == 1 || cc->Ntherm == 0) + if (cc->Nfans == 1 || (cc->Ntherm + cc->Ntstats) == 0) cache->enc_status |= SES_ENCSTAT_CRITICAL; else cache->enc_status |= SES_ENCSTAT_NONCRITICAL; @@ -433,6 +439,21 @@ safte_rdstat(enc_softc_t *ssc, int slpflg) } r++; + /* + * Now, for "pseudo" thermometers, we have two bytes + * of information in enclosure status- 16 bits. Actually, + * the MSB is a single TEMP ALERT flag indicating whether + * any other bits are set, but, thanks to fuzzy thinking, + * in the SAF-TE spec, this can also be set even if no + * other bits are set, thus making this really another + * binary temperature sensor. + */ + + SAFT_BAIL(r + cc->Ntherm, hiwater, sdata); + tempflags = sdata[r + cc->Ntherm]; + SAFT_BAIL(r + cc->Ntherm + 1, hiwater, sdata); + tempflags |= (tempflags << 8) | sdata[r + cc->Ntherm + 1]; + for (i = 0; i < cc->Ntherm; i++) { SAFT_BAIL(r, hiwater, sdata); /* @@ -465,7 +486,11 @@ safte_rdstat(enc_softc_t *ssc, int slpflg) * value and set SES_OBJSTAT_NOTAVAIL. We'll depend on the * temperature flags for warnings. */ - cache->elm_map[oid].encstat[0] = SES_OBJSTAT_NOTAVAIL; + if (tempflags & (1 << i)) { + cache->elm_map[oid].encstat[0] = SES_OBJSTAT_CRIT; + cache->enc_status |= SES_ENCSTAT_CRITICAL; + } else + cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK; cache->elm_map[oid].encstat[1] = 0; cache->elm_map[oid].encstat[2] = sdata[r]; cache->elm_map[oid].encstat[3] = 0; @@ -473,24 +498,10 @@ safte_rdstat(enc_softc_t *ssc, int slpflg) r++; } - /* - * Now, for "pseudo" thermometers, we have two bytes - * of information in enclosure status- 16 bits. Actually, - * the MSB is a single TEMP ALERT flag indicating whether - * any other bits are set, but, thanks to fuzzy thinking, - * in the SAF-TE spec, this can also be set even if no - * other bits are set, thus making this really another - * binary temperature sensor. - */ - - SAFT_BAIL(r, hiwater, sdata); - tempflags = sdata[r++]; - SAFT_BAIL(r, hiwater, sdata); - tempflags |= (tempflags << 8) | sdata[r++]; - - for (i = 0; i < NPSEUDO_THERM; i++) { + for (i = 0; i <= cc->Ntstats; i++) { cache->elm_map[oid].encstat[1] = 0; - if (tempflags & (1 << (NPSEUDO_THERM - i - 1))) { + if (tempflags & (1 << + ((i == cc->Ntstats) ? 15 : (cc->Ntherm + i)))) { cache->elm_map[oid].encstat[0] = SES_OBJSTAT_CRIT; cache->elm_map[4].encstat[2] = 0xff; /* @@ -505,12 +516,18 @@ safte_rdstat(enc_softc_t *ssc, int slpflg) * Just say 'OK', and use the reserved value of * zero. */ - cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK; + if (cc->Ntstats == 0) + cache->elm_map[oid].encstat[0] = + SES_OBJSTAT_NOTAVAIL; + else + cache->elm_map[oid].encstat[0] = + SES_OBJSTAT_OK; cache->elm_map[oid].encstat[2] = 0; cache->elm_map[oid].encstat[3] = 0; } cache->elm_map[oid++].svalid = 1; } + r += 2; /* * Get alarm status. @@ -1006,7 +1023,7 @@ safte_softc_init(enc_softc_t *ssc, int doinit) */ cc = ssc->enc_private; ssc->enc_cache.nelms = cc->Nfans + cc->Npwr + cc->Nslots + - cc->DoorLock + cc->Ntherm + cc->Nspkrs + NPSEUDO_THERM + + cc->DoorLock + cc->Ntherm + cc->Nspkrs + cc->Ntstats + 1 + NPSEUDO_ALARM; ssc->enc_cache.elm_map = ENC_MALLOCZ(ssc->enc_cache.nelms * sizeof(enc_element_t)); @@ -1030,7 +1047,7 @@ safte_softc_init(enc_softc_t *ssc, int doinit) ssc->enc_cache.elm_map[r++].enctype = ELMTYP_ALARM; for (i = 0; i < cc->Ntherm; i++) ssc->enc_cache.elm_map[r++].enctype = ELMTYP_THERM; - for (i = 0; i < NPSEUDO_THERM; i++) + for (i = 0; i <= cc->Ntstats; i++) ssc->enc_cache.elm_map[r++].enctype = ELMTYP_THERM; ssc->enc_cache.elm_map[r++].enctype = ELMTYP_ALARM; cc->slotoff = (uint8_t) r; From cfeb63a92b9434de88a34a9b0fb07b6512af8222 Mon Sep 17 00:00:00 2001 From: Alexander Motin Date: Tue, 23 Aug 2011 13:25:29 +0000 Subject: [PATCH 16/89] Tuning r225108, trust ETA flag zero status also when there is some thermometer available. --- sys/cam/scsi/scsi_enc_safte.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/cam/scsi/scsi_enc_safte.c b/sys/cam/scsi/scsi_enc_safte.c index 188bfdf079a..241df9b410b 100644 --- a/sys/cam/scsi/scsi_enc_safte.c +++ b/sys/cam/scsi/scsi_enc_safte.c @@ -516,7 +516,7 @@ safte_rdstat(enc_softc_t *ssc, int slpflg) * Just say 'OK', and use the reserved value of * zero. */ - if (cc->Ntstats == 0) + if ((cc->Ntherm + cc->Ntstats) == 0) cache->elm_map[oid].encstat[0] = SES_OBJSTAT_NOTAVAIL; else From 66514eb8248f8af014890f89b0db8cbddde81f91 Mon Sep 17 00:00:00 2001 From: Alexander Motin Date: Tue, 23 Aug 2011 13:36:34 +0000 Subject: [PATCH 17/89] Element descriptor is optional for SES devices. Respecting that, report empty string for SAF-TE devices where there is no descriptor, instead of returning confusing EINVAL error. --- sys/cam/scsi/scsi_enc.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/sys/cam/scsi/scsi_enc.c b/sys/cam/scsi/scsi_enc.c index 627c46bb308..2435b9758f4 100644 --- a/sys/cam/scsi/scsi_enc.c +++ b/sys/cam/scsi/scsi_enc.c @@ -475,10 +475,6 @@ enc_ioctl(struct cdev *dev, u_long cmd, caddr_t arg_addr, int flag, break; case ENCIOC_GETELMDESC: - if (enc->enc_vec.get_elm_desc == NULL) { - error = EINVAL; - break; - } error = copyin(addr, &elmd, sizeof(elmd)); if (error) break; @@ -486,9 +482,12 @@ enc_ioctl(struct cdev *dev, u_long cmd, caddr_t arg_addr, int flag, error = EINVAL; break; } - error = enc->enc_vec.get_elm_desc(enc, &elmd); - if (error) - break; + if (enc->enc_vec.get_elm_desc != NULL) { + error = enc->enc_vec.get_elm_desc(enc, &elmd); + if (error) + break; + } else + elmd.elm_desc_len = 0; error = copyout(&elmd, addr, sizeof(elmd)); break; From f845e1de64bcef3a6f8468a451d08dcd9352758e Mon Sep 17 00:00:00 2001 From: Alexander Motin Date: Wed, 24 Aug 2011 14:09:32 +0000 Subject: [PATCH 18/89] First part of SAF-TE driver refactoring to the new fsm-based model: - make configuration and status reading asynchronous. --- sys/cam/ata/ata_all.c | 41 +++ sys/cam/ata/ata_all.h | 10 + sys/cam/scsi/scsi_all.c | 62 +++- sys/cam/scsi/scsi_all.h | 14 + sys/cam/scsi/scsi_enc.c | 2 +- sys/cam/scsi/scsi_enc_internal.h | 1 - sys/cam/scsi/scsi_enc_safte.c | 541 ++++++++++++++++--------------- sys/cam/scsi/scsi_enc_ses.c | 2 +- 8 files changed, 408 insertions(+), 265 deletions(-) diff --git a/sys/cam/ata/ata_all.c b/sys/cam/ata/ata_all.c index 5bf59f9f3d0..5faf68ef4c7 100644 --- a/sys/cam/ata/ata_all.c +++ b/sys/cam/ata/ata_all.c @@ -762,3 +762,44 @@ semb_send_diagnostic(struct ccb_ataio *ataio, length > 0 ? data_ptr[0] : 0, 0x82, length / 4); } +void +semb_read_buffer(struct ccb_ataio *ataio, + u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb*), + uint8_t tag_action, uint8_t page_code, + uint8_t *data_ptr, uint16_t length, uint32_t timeout) +{ + + length = min(length, 1020); + length = (length + 3) & ~3; + cam_fill_ataio(ataio, + retries, + cbfcnp, + /*flags*/CAM_DIR_IN, + tag_action, + data_ptr, + length, + timeout); + ata_28bit_cmd(ataio, ATA_SEP_ATTN, + page_code, 0x00, length / 4); +} + +void +semb_write_buffer(struct ccb_ataio *ataio, + u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), + uint8_t tag_action, uint8_t *data_ptr, uint16_t length, uint32_t timeout) +{ + + length = min(length, 1020); + length = (length + 3) & ~3; + cam_fill_ataio(ataio, + retries, + cbfcnp, + /*flags*/length ? CAM_DIR_OUT : CAM_DIR_NONE, + tag_action, + data_ptr, + length, + timeout); + ata_28bit_cmd(ataio, ATA_SEP_ATTN, + length > 0 ? data_ptr[0] : 0, 0x80, length / 4); +} + diff --git a/sys/cam/ata/ata_all.h b/sys/cam/ata/ata_all.h index 5adc3ee0cfb..924fdfe5a6d 100644 --- a/sys/cam/ata/ata_all.h +++ b/sys/cam/ata/ata_all.h @@ -152,4 +152,14 @@ void semb_send_diagnostic(struct ccb_ataio *ataio, uint8_t tag_action, uint8_t *data_ptr, uint16_t param_list_length, uint32_t timeout); +void semb_read_buffer(struct ccb_ataio *ataio, + u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb*), + uint8_t tag_action, uint8_t page_code, + uint8_t *data_ptr, uint16_t allocation_length, uint32_t timeout); + +void semb_write_buffer(struct ccb_ataio *ataio, + u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), + uint8_t tag_action, uint8_t *data_ptr, uint16_t param_list_length, + uint32_t timeout); + #endif diff --git a/sys/cam/scsi/scsi_all.c b/sys/cam/scsi/scsi_all.c index 7361c42f0bd..2cff85416bb 100644 --- a/sys/cam/scsi/scsi_all.c +++ b/sys/cam/scsi/scsi_all.c @@ -3564,7 +3564,7 @@ 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) + if (0 && (naa->naa >> SVPD_ID_NAA_NAA_SHIFT) != SVPD_ID_NAA_IEEE_REG) return 0; return 1; } @@ -4276,6 +4276,66 @@ scsi_send_diagnostic(struct ccb_scsiio *csio, u_int32_t retries, timeout); } +void +scsi_read_buffer(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb*), + uint8_t tag_action, int mode, + uint8_t buffer_id, u_int32_t offset, + uint8_t *data_ptr, uint32_t allocation_length, + uint8_t sense_len, uint32_t timeout) +{ + struct scsi_read_buffer *scsi_cmd; + + scsi_cmd = (struct scsi_read_buffer *)&csio->cdb_io.cdb_bytes; + memset(scsi_cmd, 0, sizeof(*scsi_cmd)); + scsi_cmd->opcode = READ_BUFFER; + scsi_cmd->byte2 = mode; + scsi_cmd->buffer_id = buffer_id; + scsi_ulto3b(offset, scsi_cmd->offset); + scsi_ulto3b(allocation_length, scsi_cmd->length); + + cam_fill_csio(csio, + retries, + cbfcnp, + /*flags*/CAM_DIR_IN, + tag_action, + data_ptr, + allocation_length, + sense_len, + sizeof(*scsi_cmd), + timeout); +} + +void +scsi_write_buffer(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + uint8_t tag_action, int mode, + uint8_t buffer_id, u_int32_t offset, + uint8_t *data_ptr, uint32_t param_list_length, + uint8_t sense_len, uint32_t timeout) +{ + struct scsi_write_buffer *scsi_cmd; + + scsi_cmd = (struct scsi_write_buffer *)&csio->cdb_io.cdb_bytes; + memset(scsi_cmd, 0, sizeof(*scsi_cmd)); + scsi_cmd->opcode = WRITE_BUFFER; + scsi_cmd->byte2 = mode; + scsi_cmd->buffer_id = buffer_id; + scsi_ulto3b(offset, scsi_cmd->offset); + scsi_ulto3b(param_list_length, scsi_cmd->length); + + cam_fill_csio(csio, + retries, + cbfcnp, + /*flags*/param_list_length ? CAM_DIR_OUT : CAM_DIR_NONE, + tag_action, + data_ptr, + param_list_length, + sense_len, + sizeof(*scsi_cmd), + timeout); +} + void scsi_start_stop(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), diff --git a/sys/cam/scsi/scsi_all.h b/sys/cam/scsi/scsi_all.h index 364ce52412a..2380e8e8d82 100644 --- a/sys/cam/scsi/scsi_all.h +++ b/sys/cam/scsi/scsi_all.h @@ -1487,6 +1487,20 @@ void scsi_send_diagnostic(struct ccb_scsiio *csio, u_int32_t retries, uint16_t param_list_length, uint8_t sense_len, uint32_t timeout); +void scsi_read_buffer(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb*), + uint8_t tag_action, int mode, + uint8_t buffer_id, u_int32_t offset, + uint8_t *data_ptr, uint32_t allocation_length, + uint8_t sense_len, uint32_t timeout); + +void scsi_write_buffer(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + uint8_t tag_action, int mode, + uint8_t buffer_id, u_int32_t offset, + uint8_t *data_ptr, uint32_t param_list_length, + uint8_t sense_len, uint32_t timeout); + void scsi_read_write(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, int readop, u_int8_t byte2, diff --git a/sys/cam/scsi/scsi_enc.c b/sys/cam/scsi/scsi_enc.c index 2435b9758f4..3e435ec529a 100644 --- a/sys/cam/scsi/scsi_enc.c +++ b/sys/cam/scsi/scsi_enc.c @@ -429,7 +429,7 @@ enc_ioctl(struct cdev *dev, u_long cmd, caddr_t arg_addr, int flag, cam_periph_unlock(periph); break; } - tmp = cache->enc_status & ~ENCI_SVALID; + tmp = cache->enc_status; cam_periph_unlock(periph); error = copyout(&tmp, addr, sizeof(tmp)); cache->enc_status = tmp; diff --git a/sys/cam/scsi/scsi_enc_internal.h b/sys/cam/scsi/scsi_enc_internal.h index 93cfee74fd9..5be5e9bd067 100644 --- a/sys/cam/scsi/scsi_enc_internal.h +++ b/sys/cam/scsi/scsi_enc_internal.h @@ -139,7 +139,6 @@ struct enc_softc { enc_cache_t enc_daemon_cache; struct sx enc_cache_lock; -#define ENCI_SVALID 0x80 uint8_t enc_flags; #define ENC_FLAG_INVALID 0x01 #define ENC_FLAG_INITIALIZED 0x02 diff --git a/sys/cam/scsi/scsi_enc_safte.c b/sys/cam/scsi/scsi_enc_safte.c index 241df9b410b..a0c8f887204 100644 --- a/sys/cam/scsi/scsi_enc_safte.c +++ b/sys/cam/scsi/scsi_enc_safte.c @@ -45,6 +45,7 @@ __FBSDID("$FreeBSD: head/sys/cam/scsi/scsi_ses.c 201758 2010-01-07 21:01:37Z mbr #include #include +#include #include @@ -52,8 +53,6 @@ __FBSDID("$FreeBSD: head/sys/cam/scsi/scsi_ses.c 201758 2010-01-07 21:01:37Z mbr * SAF-TE Type Device Emulation */ -static int safte_getconfig(enc_softc_t *); -static int safte_rdstat(enc_softc_t *, int); static int set_elm_status_sel(enc_softc_t *, encioc_elm_status_t *, int); static int wrbuf16(enc_softc_t *, uint8_t, uint8_t, uint8_t, uint8_t, int); static void wrslot_stat(enc_softc_t *, int); @@ -81,8 +80,54 @@ static int perf_slotop(enc_softc_t *, uint8_t, uint8_t, int); #define SAFTE_WT_ACTPWS 0x14 /* turn on/off power supply */ #define SAFTE_WT_GLOBAL 0x15 /* send global command */ - #define SAFT_SCRATCH 64 +#define SCSZ 0x8000 + +typedef enum { + SAFTE_UPDATE_NONE, + SAFTE_UPDATE_READCONFIG, + SAFTE_UPDATE_READENCSTATUS, + SAFTE_UPDATE_READSLOTSTATUS, + SAFTE_NUM_UPDATE_STATES +} safte_update_action; + +static fsm_fill_handler_t safte_fill_read_buf_io; +static fsm_done_handler_t safte_process_config; +static fsm_done_handler_t safte_process_status; +static fsm_done_handler_t safte_process_slotstatus; + +static struct enc_fsm_state enc_fsm_states[SAFTE_NUM_UPDATE_STATES] = +{ + { "SAFTE_UPDATE_NONE", 0, 0, 0, NULL, NULL, NULL }, + { + "SAFTE_UPDATE_READCONFIG", + SAFTE_RD_RDCFG, + SAFT_SCRATCH, + 60 * 1000, + safte_fill_read_buf_io, + safte_process_config, + enc_error + }, + { + "SAFTE_UPDATE_READENCSTATUS", + SAFTE_RD_RDESTS, + SCSZ, + 60 * 1000, + safte_fill_read_buf_io, + safte_process_status, + enc_error + }, + { + "SAFTE_UPDATE_READSLOTSTATUS", + SAFTE_RD_RDDSTS, + SCSZ, + 60 * 1000, + safte_fill_read_buf_io, + safte_process_slotstatus, + enc_error + } +}; + #define NPSEUDO_ALARM 1 struct scfg { /* @@ -124,121 +169,127 @@ struct scfg { static char *safte_2little = "Too Little Data Returned (%d) at line %d\n"; #define SAFT_BAIL(r, x, k) \ if ((r) >= (x)) { \ - ENC_LOG(ssc, safte_2little, x, __LINE__);\ + ENC_LOG(enc, safte_2little, x, __LINE__);\ ENC_FREE((k)); \ return (EIO); \ } static int -safte_getconfig(enc_softc_t *ssc) +safte_fill_read_buf_io(enc_softc_t *enc, struct enc_fsm_state *state, + union ccb *ccb, uint8_t *buf) { - struct scfg *cfg; - int err, amt; - char *sdata; - static char cdb[10] = - { READ_BUFFER, 1, SAFTE_RD_RDCFG, 0, 0, 0, 0, 0, SAFT_SCRATCH, 0 }; - cfg = ssc->enc_private; - if (cfg == NULL) - return (ENXIO); - - sdata = ENC_MALLOC(SAFT_SCRATCH); - if (sdata == NULL) - return (ENOMEM); - - amt = SAFT_SCRATCH; - err = enc_runcmd(ssc, cdb, 10, sdata, &amt); - if (err) { - ENC_FREE(sdata); - return (err); + if (state->page_code != SAFTE_RD_RDCFG && + enc->enc_cache.nelms == 0) { + enc_update_request(enc, SAFTE_UPDATE_READCONFIG); + return (-1); } - amt = SAFT_SCRATCH - amt; - if (amt < 6) { - ENC_LOG(ssc, "too little data (%d) for configuration\n", amt); - ENC_FREE(sdata); - return (EIO); + + if (enc->enc_type == ENC_SEMB_SAFT) { + semb_read_buffer(&ccb->ataio, /*retries*/5, + enc_done, MSG_SIMPLE_Q_TAG, + state->page_code, buf, state->buf_size, + state->timeout); + } else { + scsi_read_buffer(&ccb->csio, /*retries*/5, + enc_done, MSG_SIMPLE_Q_TAG, 1, + state->page_code, 0, buf, state->buf_size, + SSD_FULL_SIZE, state->timeout); } - cfg->Nfans = sdata[0]; - cfg->Npwr = sdata[1]; - cfg->Nslots = sdata[2]; - cfg->DoorLock = sdata[3]; - cfg->Ntherm = sdata[4]; - cfg->Nspkrs = sdata[5]; - cfg->Nalarm = NPSEUDO_ALARM; - if (amt >= 7) - cfg->Ntstats = sdata[6] & 0x0f; - else - cfg->Ntstats = 0; - ENC_VLOG(ssc, "Nfans %d Npwr %d Nslots %d Lck %d Ntherm %d Nspkrs %d " - "Ntstats %d\n", - cfg->Nfans, cfg->Npwr, cfg->Nslots, cfg->DoorLock, cfg->Ntherm, - cfg->Nspkrs, cfg->Ntstats); - ENC_FREE(sdata); return (0); } static int -safte_rdstat(enc_softc_t *ssc, int slpflg) +safte_process_config(enc_softc_t *enc, struct enc_fsm_state *state, + union ccb *ccb, uint8_t **bufp, int xfer_len) { - int err, oid, r, i, hiwater, nitems, amt; - uint16_t tempflags; - size_t buflen; - uint8_t status, oencstat; - char *sdata, cdb[10]; - struct scfg *cc = ssc->enc_private; - enc_cache_t *cache = &ssc->enc_cache; + struct scfg *cfg; + uint8_t *buf = *bufp; + int i, r; + cfg = enc->enc_private; + if (cfg == NULL) + return (ENXIO); - /* - * The number of objects overstates things a bit, - * both for the bogus 'thermometer' entries and - * the drive status (which isn't read at the same - * time as the enclosure status), but that's okay. - */ - buflen = 4 * cc->Nslots; - if (cache->nelms > buflen) - buflen = cache->nelms; - sdata = ENC_MALLOC(buflen); - if (sdata == NULL) - return (ENOMEM); - - cdb[0] = READ_BUFFER; - cdb[1] = 1; - cdb[2] = SAFTE_RD_RDESTS; - cdb[3] = 0; - cdb[4] = 0; - cdb[5] = 0; - cdb[6] = 0; - cdb[7] = (buflen >> 8) & 0xff; - cdb[8] = buflen & 0xff; - cdb[9] = 0; - amt = buflen; - err = enc_runcmd(ssc, cdb, 10, sdata, &amt); - if (err) { - ENC_FREE(sdata); - return (err); + if (xfer_len < 6) { + ENC_LOG(enc, "too little data (%d) for configuration\n", + xfer_len); + return (EIO); } - hiwater = buflen - amt; + cfg->Nfans = buf[0]; + cfg->Npwr = buf[1]; + cfg->Nslots = buf[2]; + cfg->DoorLock = buf[3]; + cfg->Ntherm = buf[4]; + cfg->Nspkrs = buf[5]; + cfg->Nalarm = NPSEUDO_ALARM; + if (xfer_len >= 7) + cfg->Ntstats = buf[6] & 0x0f; + else + cfg->Ntstats = 0; + ENC_VLOG(enc, "Nfans %d Npwr %d Nslots %d Lck %d Ntherm %d Nspkrs %d " + "Ntstats %d\n", + cfg->Nfans, cfg->Npwr, cfg->Nslots, cfg->DoorLock, cfg->Ntherm, + cfg->Nspkrs, cfg->Ntstats); + enc->enc_cache.nelms = cfg->Nfans + cfg->Npwr + cfg->Nslots + + cfg->DoorLock + cfg->Ntherm + cfg->Nspkrs + cfg->Ntstats + 1 + + NPSEUDO_ALARM; + ENC_FREE_AND_NULL(enc->enc_cache.elm_map); + enc->enc_cache.elm_map = + ENC_MALLOCZ(enc->enc_cache.nelms * sizeof(enc_element_t)); + if (enc->enc_cache.elm_map == NULL) { + enc->enc_cache.nelms = 0; + return (ENOMEM); + } + r = 0; /* - * invalidate all status bits. + * Note that this is all arranged for the convenience + * in later fetches of status. */ - for (i = 0; i < cache->nelms; i++) - cache->elm_map[i].svalid = 0; - oencstat = cache->enc_status & ALL_ENC_STAT; - ssc->enc_cache.enc_status = 0; + for (i = 0; i < cfg->Nfans; i++) + enc->enc_cache.elm_map[r++].enctype = ELMTYP_FAN; + cfg->pwroff = (uint8_t) r; + for (i = 0; i < cfg->Npwr; i++) + enc->enc_cache.elm_map[r++].enctype = ELMTYP_POWER; + for (i = 0; i < cfg->DoorLock; i++) + enc->enc_cache.elm_map[r++].enctype = ELMTYP_DOORLOCK; + for (i = 0; i < cfg->Nspkrs; i++) + enc->enc_cache.elm_map[r++].enctype = ELMTYP_ALARM; + for (i = 0; i < cfg->Ntherm; i++) + enc->enc_cache.elm_map[r++].enctype = ELMTYP_THERM; + for (i = 0; i <= cfg->Ntstats; i++) + enc->enc_cache.elm_map[r++].enctype = ELMTYP_THERM; + enc->enc_cache.elm_map[r++].enctype = ELMTYP_ALARM; + cfg->slotoff = (uint8_t) r; + for (i = 0; i < cfg->Nslots; i++) + enc->enc_cache.elm_map[r++].enctype = ELMTYP_DEVICE; + enc_update_request(enc, SAFTE_UPDATE_READENCSTATUS); + enc_update_request(enc, SAFTE_UPDATE_READSLOTSTATUS); + + return (0); +} + +static int +safte_process_status(enc_softc_t *enc, struct enc_fsm_state *state, + union ccb *ccb, uint8_t **bufp, int xfer_len) +{ + struct scfg *cfg; + uint8_t *buf = *bufp; + int oid, r, i, nitems; + uint16_t tempflags; + enc_cache_t *cache = &enc->enc_cache; + + cfg = enc->enc_private; + if (cfg == NULL) + return (ENXIO); - /* - * Now parse returned buffer. - * If we didn't get enough data back, - * that's considered a fatal error. - */ oid = r = 0; - for (nitems = i = 0; i < cc->Nfans; i++) { - SAFT_BAIL(r, hiwater, sdata); + for (nitems = i = 0; i < cfg->Nfans; i++) { + SAFT_BAIL(r, xfer_len, buf); /* * 0 = Fan Operational * 1 = Fan is malfunctioning @@ -247,7 +298,7 @@ safte_rdstat(enc_softc_t *ssc, int slpflg) */ cache->elm_map[oid].encstat[1] = 0; /* resvd */ cache->elm_map[oid].encstat[2] = 0; /* resvd */ - switch ((int)(uint8_t)sdata[r]) { + switch ((int)buf[r]) { case 0: nitems++; cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK; @@ -271,7 +322,7 @@ safte_rdstat(enc_softc_t *ssc, int slpflg) * if only one fan or no thermometers, * else the NONCRITICAL error is set. */ - if (cc->Nfans == 1 || (cc->Ntherm + cc->Ntstats) == 0) + if (cfg->Nfans == 1 || (cfg->Ntherm + cfg->Ntstats) == 0) cache->enc_status |= SES_ENCSTAT_CRITICAL; else cache->enc_status |= SES_ENCSTAT_NONCRITICAL; @@ -285,7 +336,7 @@ safte_rdstat(enc_softc_t *ssc, int slpflg) * if only one fan or no thermometers, * else the NONCRITICAL error is set. */ - if (cc->Nfans == 1) + if (cfg->Nfans == 1) cache->enc_status |= SES_ENCSTAT_CRITICAL; else cache->enc_status |= SES_ENCSTAT_NONCRITICAL; @@ -297,8 +348,8 @@ safte_rdstat(enc_softc_t *ssc, int slpflg) break; default: cache->elm_map[oid].encstat[0] = SES_OBJSTAT_UNSUPPORTED; - ENC_LOG(ssc, "Unknown fan%d status 0x%x\n", i, - sdata[r] & 0xff); + ENC_LOG(enc, "Unknown fan%d status 0x%x\n", i, + buf[r] & 0xff); break; } cache->elm_map[oid++].svalid = 1; @@ -309,18 +360,18 @@ safte_rdstat(enc_softc_t *ssc, int slpflg) * No matter how you cut it, no cooling elements when there * should be some there is critical. */ - if (cc->Nfans && nitems == 0) { + if (cfg->Nfans && nitems == 0) { cache->enc_status |= SES_ENCSTAT_CRITICAL; } - for (i = 0; i < cc->Npwr; i++) { - SAFT_BAIL(r, hiwater, sdata); + for (i = 0; i < cfg->Npwr; i++) { + SAFT_BAIL(r, xfer_len, buf); cache->elm_map[oid].encstat[0] = SES_OBJSTAT_UNKNOWN; cache->elm_map[oid].encstat[1] = 0; /* resvd */ cache->elm_map[oid].encstat[2] = 0; /* resvd */ cache->elm_map[oid].encstat[3] = 0x20; /* requested on */ - switch ((uint8_t)sdata[r]) { + switch (buf[r]) { case 0x00: /* pws operational and on */ cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK; break; @@ -359,25 +410,25 @@ safte_rdstat(enc_softc_t *ssc, int slpflg) cache->enc_status |= SES_ENCSTAT_INFO; break; default: - ENC_LOG(ssc, "unknown power supply %d status (0x%x)\n", - i, sdata[r] & 0xff); + ENC_LOG(enc, "unknown power supply %d status (0x%x)\n", + i, buf[r] & 0xff); break; } - ssc->enc_cache.elm_map[oid++].svalid = 1; + enc->enc_cache.elm_map[oid++].svalid = 1; r++; } /* * Skip over Slot SCSI IDs */ - r += cc->Nslots; + r += cfg->Nslots; /* * We always have doorlock status, no matter what, * but we only save the status if we have one. */ - SAFT_BAIL(r, hiwater, sdata); - if (cc->DoorLock) { + SAFT_BAIL(r, xfer_len, buf); + if (cfg->DoorLock) { /* * 0 = Door Locked * 1 = Door Unlocked, or no Lock Installed @@ -385,7 +436,7 @@ safte_rdstat(enc_softc_t *ssc, int slpflg) */ cache->elm_map[oid].encstat[1] = 0; cache->elm_map[oid].encstat[2] = 0; - switch ((uint8_t)sdata[r]) { + switch (buf[r]) { case 0: cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK; cache->elm_map[oid].encstat[3] = 0; @@ -402,8 +453,8 @@ safte_rdstat(enc_softc_t *ssc, int slpflg) default: cache->elm_map[oid].encstat[0] = SES_OBJSTAT_UNSUPPORTED; - ENC_LOG(ssc, "unknown lock status 0x%x\n", - sdata[r] & 0xff); + ENC_LOG(enc, "unknown lock status 0x%x\n", + buf[r] & 0xff); break; } cache->elm_map[oid++].svalid = 1; @@ -414,11 +465,11 @@ safte_rdstat(enc_softc_t *ssc, int slpflg) * We always have speaker status, no matter what, * but we only save the status if we have one. */ - SAFT_BAIL(r, hiwater, sdata); - if (cc->Nspkrs) { + SAFT_BAIL(r, xfer_len, buf); + if (cfg->Nspkrs) { cache->elm_map[oid].encstat[1] = 0; cache->elm_map[oid].encstat[2] = 0; - if (sdata[r] == 1) { + if (buf[r] == 1) { /* * We need to cache tone urgency indicators. * Someday. @@ -426,14 +477,14 @@ safte_rdstat(enc_softc_t *ssc, int slpflg) cache->elm_map[oid].encstat[0] = SES_OBJSTAT_NONCRIT; cache->elm_map[oid].encstat[3] = 0x8; cache->enc_status |= SES_ENCSTAT_NONCRITICAL; - } else if (sdata[r] == 0) { + } else if (buf[r] == 0) { cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK; cache->elm_map[oid].encstat[3] = 0; } else { cache->elm_map[oid].encstat[0] = SES_OBJSTAT_UNSUPPORTED; cache->elm_map[oid].encstat[3] = 0; - ENC_LOG(ssc, "unknown spkr status 0x%x\n", - sdata[r] & 0xff); + ENC_LOG(enc, "unknown spkr status 0x%x\n", + buf[r] & 0xff); } cache->elm_map[oid++].svalid = 1; } @@ -449,13 +500,13 @@ safte_rdstat(enc_softc_t *ssc, int slpflg) * binary temperature sensor. */ - SAFT_BAIL(r + cc->Ntherm, hiwater, sdata); - tempflags = sdata[r + cc->Ntherm]; - SAFT_BAIL(r + cc->Ntherm + 1, hiwater, sdata); - tempflags |= (tempflags << 8) | sdata[r + cc->Ntherm + 1]; + SAFT_BAIL(r + cfg->Ntherm, xfer_len, buf); + tempflags = buf[r + cfg->Ntherm]; + SAFT_BAIL(r + cfg->Ntherm + 1, xfer_len, buf); + tempflags |= (tempflags << 8) | buf[r + cfg->Ntherm + 1]; - for (i = 0; i < cc->Ntherm; i++) { - SAFT_BAIL(r, hiwater, sdata); + for (i = 0; i < cfg->Ntherm; i++) { + SAFT_BAIL(r, xfer_len, buf); /* * Status is a range from -10 to 245 deg Celsius, * which we need to normalize to -20 to -245 according @@ -492,16 +543,16 @@ safte_rdstat(enc_softc_t *ssc, int slpflg) } else cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK; cache->elm_map[oid].encstat[1] = 0; - cache->elm_map[oid].encstat[2] = sdata[r]; + cache->elm_map[oid].encstat[2] = buf[r]; cache->elm_map[oid].encstat[3] = 0; cache->elm_map[oid++].svalid = 1; r++; } - for (i = 0; i <= cc->Ntstats; i++) { + for (i = 0; i <= cfg->Ntstats; i++) { cache->elm_map[oid].encstat[1] = 0; if (tempflags & (1 << - ((i == cc->Ntstats) ? 15 : (cc->Ntherm + i)))) { + ((i == cfg->Ntstats) ? 15 : (cfg->Ntherm + i)))) { cache->elm_map[oid].encstat[0] = SES_OBJSTAT_CRIT; cache->elm_map[4].encstat[2] = 0xff; /* @@ -516,7 +567,7 @@ safte_rdstat(enc_softc_t *ssc, int slpflg) * Just say 'OK', and use the reserved value of * zero. */ - if ((cc->Ntherm + cc->Ntstats) == 0) + if ((cfg->Ntherm + cfg->Ntstats) == 0) cache->elm_map[oid].encstat[0] = SES_OBJSTAT_NOTAVAIL; else @@ -536,24 +587,31 @@ safte_rdstat(enc_softc_t *ssc, int slpflg) cache->elm_map[oid].encstat[3] = cache->elm_map[oid].priv; cache->elm_map[oid++].svalid = 1; - /* - * Now get drive slot status - */ - cdb[2] = SAFTE_RD_RDDSTS; - amt = buflen; - err = enc_runcmd(ssc, cdb, 10, sdata, &amt); - if (err) { - ENC_FREE(sdata); - return (err); - } - hiwater = buflen - amt; - for (r = i = 0; i < cc->Nslots; i++, r += 4) { - SAFT_BAIL(r+3, hiwater, sdata); + return (0); +} + +static int +safte_process_slotstatus(enc_softc_t *enc, struct enc_fsm_state *state, + union ccb *ccb, uint8_t **bufp, int xfer_len) +{ + struct scfg *cfg; + uint8_t *buf = *bufp; + enc_cache_t *cache = &enc->enc_cache; + int oid, r, i; + uint8_t status; + + cfg = enc->enc_private; + if (cfg == NULL) + return (ENXIO); + + oid = cfg->slotoff; + for (r = i = 0; i < cfg->Nslots; i++, r += 4) { + SAFT_BAIL(r+3, xfer_len, buf); cache->elm_map[oid].encstat[0] = SES_OBJSTAT_UNSUPPORTED; cache->elm_map[oid].encstat[1] = (uint8_t) i; cache->elm_map[oid].encstat[2] = 0; cache->elm_map[oid].encstat[3] = 0; - status = sdata[r+3]; + status = buf[r+3]; if ((status & 0x1) == 0) { /* no device */ cache->elm_map[oid].encstat[0] = SES_OBJSTAT_NOTINSTALLED; @@ -568,29 +626,28 @@ safte_rdstat(enc_softc_t *ssc, int slpflg) } cache->elm_map[oid++].svalid = 1; } - /* see comment below about sticky enclosure status */ - cache->enc_status |= ENCI_SVALID | oencstat; - ENC_FREE(sdata); return (0); } static int -set_elm_status_sel(enc_softc_t *ssc, encioc_elm_status_t *elms, int slp) +set_elm_status_sel(enc_softc_t *enc, encioc_elm_status_t *elms, int slp) { int idx; enc_element_t *ep; - struct scfg *cc = ssc->enc_private; + struct scfg *cc = enc->enc_private; if (cc == NULL) return (0); idx = (int)elms->elm_idx; - ep = &ssc->enc_cache.elm_map[idx]; + ep = &enc->enc_cache.elm_map[idx]; switch (ep->enctype) { case ELMTYP_DEVICE: if (elms->cstat[0] & SESCTL_PRDFAIL) { ep->priv |= 0x40; + } else { + ep->priv &= ~0x40; } /* SESCTL_RSTSWAP has no correspondence in SAF-TE */ if (elms->cstat[0] & SESCTL_DISABLE) { @@ -599,13 +656,15 @@ set_elm_status_sel(enc_softc_t *ssc, encioc_elm_status_t *elms, int slp) * Hmm. Try to set the 'No Drive' flag. * Maybe that will count as a 'disable'. */ + } else { + ep->priv &= ~0x80; } if (ep->priv & 0xc6) { ep->priv &= ~0x1; } else { ep->priv |= 0x1; /* no errors */ } - wrslot_stat(ssc, slp); + wrslot_stat(enc, slp); break; case ELMTYP_POWER: /* @@ -613,7 +672,7 @@ set_elm_status_sel(enc_softc_t *ssc, encioc_elm_status_t *elms, int slp) * do the 'disable' for a power supply. */ if (elms->cstat[0] & SESCTL_DISABLE) { - (void) wrbuf16(ssc, SAFTE_WT_ACTPWS, + (void) wrbuf16(enc, SAFTE_WT_ACTPWS, idx - cc->pwroff, 0, 0, slp); } break; @@ -624,7 +683,7 @@ set_elm_status_sel(enc_softc_t *ssc, encioc_elm_status_t *elms, int slp) */ if (elms->cstat[0] & SESCTL_DISABLE) { /* remember- fans are the first items, so idx works */ - (void) wrbuf16(ssc, SAFTE_WT_FANSPD, idx, 0, 0, slp); + (void) wrbuf16(enc, SAFTE_WT_FANSPD, idx, 0, 0, slp); } break; case ELMTYP_DOORLOCK: @@ -633,7 +692,7 @@ set_elm_status_sel(enc_softc_t *ssc, encioc_elm_status_t *elms, int slp) */ if (elms->cstat[0] & SESCTL_DISABLE) { cc->flag2 &= ~SAFT_FLG2_LOCKDOOR; - (void) wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, + (void) wrbuf16(enc, SAFTE_WT_GLOBAL, cc->flag1, cc->flag2, 0, slp); } break; @@ -644,7 +703,7 @@ set_elm_status_sel(enc_softc_t *ssc, encioc_elm_status_t *elms, int slp) if (elms->cstat[0] & SESCTL_DISABLE) { cc->flag2 &= ~SAFT_FLG1_ALARM; ep->priv |= 0x40; /* Muted */ - (void) wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, + (void) wrbuf16(enc, SAFTE_WT_GLOBAL, cc->flag1, cc->flag2, 0, slp); } break; @@ -659,12 +718,12 @@ set_elm_status_sel(enc_softc_t *ssc, encioc_elm_status_t *elms, int slp) * This function handles all of the 16 byte WRITE BUFFER commands. */ static int -wrbuf16(enc_softc_t *ssc, uint8_t op, uint8_t b1, uint8_t b2, +wrbuf16(enc_softc_t *enc, uint8_t op, uint8_t b1, uint8_t b2, uint8_t b3, int slp) { int err, amt; char *sdata; - struct scfg *cc = ssc->enc_private; + struct scfg *cc = enc->enc_private; static char cdb[10] = { WRITE_BUFFER, 1, 0, 0, 0, 0, 0, 0, 16, 0 }; if (cc == NULL) @@ -674,14 +733,14 @@ wrbuf16(enc_softc_t *ssc, uint8_t op, uint8_t b1, uint8_t b2, if (sdata == NULL) return (ENOMEM); - ENC_DLOG(ssc, "saf_wrbuf16 %x %x %x %x\n", op, b1, b2, b3); + ENC_DLOG(enc, "saf_wrbuf16 %x %x %x %x\n", op, b1, b2, b3); sdata[0] = op; sdata[1] = b1; sdata[2] = b2; sdata[3] = b3; amt = -16; - err = enc_runcmd(ssc, cdb, 10, sdata, &amt); + err = enc_runcmd(enc, cdb, 10, sdata, &amt); ENC_FREE(sdata); return (err); } @@ -693,17 +752,17 @@ wrbuf16(enc_softc_t *ssc, uint8_t op, uint8_t b1, uint8_t b2, * returning an error. */ static void -wrslot_stat(enc_softc_t *ssc, int slp) +wrslot_stat(enc_softc_t *enc, int slp) { int i, amt; enc_element_t *ep; char cdb[10], *sdata; - struct scfg *cc = ssc->enc_private; + struct scfg *cc = enc->enc_private; if (cc == NULL) return; - ENC_DLOG(ssc, "saf_wrslot\n"); + ENC_DLOG(enc, "saf_wrslot\n"); cdb[0] = WRITE_BUFFER; cdb[1] = 1; cdb[2] = 0; @@ -721,12 +780,12 @@ wrslot_stat(enc_softc_t *ssc, int slp) sdata[0] = SAFTE_WT_DSTAT; for (i = 0; i < cc->Nslots; i++) { - ep = &ssc->enc_cache.elm_map[cc->slotoff + i]; - ENC_DLOG(ssc, "saf_wrslot %d <- %x\n", i, ep->priv & 0xff); + ep = &enc->enc_cache.elm_map[cc->slotoff + i]; + ENC_DLOG(enc, "saf_wrslot %d <- %x\n", i, ep->priv & 0xff); sdata[1 + (3 * i)] = ep->priv & 0xff; } amt = -(cc->Nslots * 3 + 1); - (void) enc_runcmd(ssc, cdb, 10, sdata, &amt); + (void) enc_runcmd(enc, cdb, 10, sdata, &amt); ENC_FREE(sdata); } @@ -734,11 +793,11 @@ wrslot_stat(enc_softc_t *ssc, int slp) * This function issues the "PERFORM SLOT OPERATION" command. */ static int -perf_slotop(enc_softc_t *ssc, uint8_t slot, uint8_t opflag, int slp) +perf_slotop(enc_softc_t *enc, uint8_t slot, uint8_t opflag, int slp) { int err, amt; char *sdata; - struct scfg *cc = ssc->enc_private; + struct scfg *cc = enc->enc_private; static char cdb[10] = { WRITE_BUFFER, 1, 0, 0, 0, 0, 0, 0, SAFT_SCRATCH, 0 }; @@ -752,9 +811,9 @@ perf_slotop(enc_softc_t *ssc, uint8_t slot, uint8_t opflag, int slp) sdata[0] = SAFTE_WT_SLTOP; sdata[1] = slot; sdata[2] = opflag; - ENC_DLOG(ssc, "saf_slotop slot %d op %x\n", slot, opflag); + ENC_DLOG(enc, "saf_slotop slot %d op %x\n", slot, opflag); amt = -SAFT_SCRATCH; - err = enc_runcmd(ssc, cdb, 10, sdata, &amt); + err = enc_runcmd(enc, cdb, 10, sdata, &amt); ENC_FREE(sdata); return (err); } @@ -762,39 +821,40 @@ perf_slotop(enc_softc_t *ssc, uint8_t slot, uint8_t opflag, int slp) static void safte_softc_cleanup(struct cam_periph *periph) { - enc_softc_t *ssc; + enc_softc_t *enc; - ssc = periph->softc; - ENC_FREE_AND_NULL(ssc->enc_cache.elm_map); - ENC_FREE_AND_NULL(ssc->enc_private); - ssc->enc_cache.nelms = 0; + enc = periph->softc; + ENC_FREE_AND_NULL(enc->enc_cache.elm_map); + ENC_FREE_AND_NULL(enc->enc_private); + enc->enc_cache.nelms = 0; } static int -safte_init_enc(enc_softc_t *ssc) +safte_init_enc(enc_softc_t *enc) { int err; static char cdb0[6] = { SEND_DIAGNOSTIC }; - err = enc_runcmd(ssc, cdb0, 6, NULL, 0); + err = enc_runcmd(enc, cdb0, 6, NULL, 0); if (err) { return (err); } DELAY(5000); - err = wrbuf16(ssc, SAFTE_WT_GLOBAL, 0, 0, 0, 1); + err = wrbuf16(enc, SAFTE_WT_GLOBAL, 0, 0, 0, 1); return (err); } static int -safte_get_enc_status(enc_softc_t *ssc, int slpflg) +safte_get_enc_status(enc_softc_t *enc, int slpflg) { - return (safte_rdstat(ssc, slpflg)); + + return (0); } static int -safte_set_enc_status(enc_softc_t *ssc, uint8_t encstat, int slpflg) +safte_set_enc_status(enc_softc_t *enc, uint8_t encstat, int slpflg) { - struct scfg *cc = ssc->enc_private; + struct scfg *cc = enc->enc_private; if (cc == NULL) return (0); /* @@ -803,46 +863,39 @@ safte_set_enc_status(enc_softc_t *ssc, uint8_t encstat, int slpflg) * that is, things set in enclosure status stay set (as implied * by conditions set in reading object status) until cleared. */ - ssc->enc_cache.enc_status &= ~ALL_ENC_STAT; - ssc->enc_cache.enc_status |= (encstat & ALL_ENC_STAT); - ssc->enc_cache.enc_status |= ENCI_SVALID; + enc->enc_cache.enc_status &= ~ALL_ENC_STAT; + enc->enc_cache.enc_status |= (encstat & ALL_ENC_STAT); cc->flag1 &= ~(SAFT_FLG1_ALARM|SAFT_FLG1_GLOBFAIL|SAFT_FLG1_GLOBWARN); if ((encstat & (SES_ENCSTAT_CRITICAL|SES_ENCSTAT_UNRECOV)) != 0) { cc->flag1 |= SAFT_FLG1_ALARM|SAFT_FLG1_GLOBFAIL; } else if ((encstat & SES_ENCSTAT_NONCRITICAL) != 0) { cc->flag1 |= SAFT_FLG1_GLOBWARN; } - return (wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, cc->flag2, 0, slpflg)); + return (wrbuf16(enc, SAFTE_WT_GLOBAL, cc->flag1, cc->flag2, 0, slpflg)); } static int -safte_get_elm_status(enc_softc_t *ssc, encioc_elm_status_t *elms, int slpflg) +safte_get_elm_status(enc_softc_t *enc, encioc_elm_status_t *elms, int slpflg) { int i = (int)elms->elm_idx; - if ((ssc->enc_cache.enc_status & ENCI_SVALID) == 0 || - (ssc->enc_cache.elm_map[i].svalid) == 0) { - int err = safte_rdstat(ssc, slpflg); - if (err) - return (err); - } - elms->cstat[0] = ssc->enc_cache.elm_map[i].encstat[0]; - elms->cstat[1] = ssc->enc_cache.elm_map[i].encstat[1]; - elms->cstat[2] = ssc->enc_cache.elm_map[i].encstat[2]; - elms->cstat[3] = ssc->enc_cache.elm_map[i].encstat[3]; + elms->cstat[0] = enc->enc_cache.elm_map[i].encstat[0]; + elms->cstat[1] = enc->enc_cache.elm_map[i].encstat[1]; + elms->cstat[2] = enc->enc_cache.elm_map[i].encstat[2]; + elms->cstat[3] = enc->enc_cache.elm_map[i].encstat[3]; return (0); } static int -safte_set_elm_status(enc_softc_t *ssc, encioc_elm_status_t *elms, int slp) +safte_set_elm_status(enc_softc_t *enc, encioc_elm_status_t *elms, int slp) { int idx, err; enc_element_t *ep; struct scfg *cc; - ENC_DLOG(ssc, "safte_set_objstat(%d): %x %x %x %x\n", + ENC_DLOG(enc, "safte_set_objstat(%d): %x %x %x %x\n", (int)elms->elm_idx, elms->cstat[0], elms->cstat[1], elms->cstat[2], elms->cstat[3]); @@ -858,17 +911,17 @@ safte_set_elm_status(enc_softc_t *ssc, encioc_elm_status_t *elms, int slp) * Check to see if the common bits are set and do them first. */ if (elms->cstat[0] & ~SESCTL_CSEL) { - err = set_elm_status_sel(ssc, elms, slp); + err = set_elm_status_sel(enc, elms, slp); if (err) return (err); } - cc = ssc->enc_private; + cc = enc->enc_private; if (cc == NULL) return (0); idx = (int)elms->elm_idx; - ep = &ssc->enc_cache.elm_map[idx]; + ep = &enc->enc_cache.elm_map[idx]; switch (ep->enctype) { case ELMTYP_DEVICE: @@ -886,7 +939,7 @@ safte_set_elm_status(enc_softc_t *ssc, encioc_elm_status_t *elms, int slp) if (elms->cstat[2] & SESCTL_RQSID) { slotop |= 0x4; } - err = perf_slotop(ssc, (uint8_t) idx - (uint8_t) cc->slotoff, + err = perf_slotop(enc, (uint8_t) idx - (uint8_t) cc->slotoff, slotop, slp); if (err) return (err); @@ -900,7 +953,7 @@ safte_set_elm_status(enc_softc_t *ssc, encioc_elm_status_t *elms, int slp) } else { ep->priv |= 0x1; /* no errors */ } - wrslot_stat(ssc, slp); + wrslot_stat(enc, slp); break; } case ELMTYP_POWER: @@ -909,15 +962,15 @@ safte_set_elm_status(enc_softc_t *ssc, encioc_elm_status_t *elms, int slp) } else { cc->flag1 &= ~SAFT_FLG1_ENCPWRFAIL; } - err = wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, + err = wrbuf16(enc, SAFTE_WT_GLOBAL, cc->flag1, cc->flag2, 0, slp); if (err) return (err); if (elms->cstat[3] & SESCTL_RQSTON) { - (void) wrbuf16(ssc, SAFTE_WT_ACTPWS, + (void) wrbuf16(enc, SAFTE_WT_ACTPWS, idx - cc->pwroff, 0, 0, slp); } else { - (void) wrbuf16(ssc, SAFTE_WT_ACTPWS, + (void) wrbuf16(enc, SAFTE_WT_ACTPWS, idx - cc->pwroff, 0, 1, slp); } break; @@ -927,7 +980,7 @@ safte_set_elm_status(enc_softc_t *ssc, encioc_elm_status_t *elms, int slp) } else { cc->flag1 &= ~SAFT_FLG1_ENCFANFAIL; } - err = wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, + err = wrbuf16(enc, SAFTE_WT_GLOBAL, cc->flag1, cc->flag2, 0, slp); if (err) return (err); @@ -942,9 +995,9 @@ safte_set_elm_status(enc_softc_t *ssc, encioc_elm_status_t *elms, int slp) } else { fsp = 1; } - (void) wrbuf16(ssc, SAFTE_WT_FANSPD, idx, fsp, 0, slp); + (void) wrbuf16(enc, SAFTE_WT_FANSPD, idx, fsp, 0, slp); } else { - (void) wrbuf16(ssc, SAFTE_WT_FANSPD, idx, 0, 0, slp); + (void) wrbuf16(enc, SAFTE_WT_FANSPD, idx, 0, 0, slp); } break; case ELMTYP_DOORLOCK: @@ -953,7 +1006,7 @@ safte_set_elm_status(enc_softc_t *ssc, encioc_elm_status_t *elms, int slp) } else { cc->flag2 |= SAFT_FLG2_LOCKDOOR; } - (void) wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, + (void) wrbuf16(enc, SAFTE_WT_GLOBAL, cc->flag1, cc->flag2, 0, slp); break; case ELMTYP_ALARM: @@ -969,7 +1022,7 @@ safte_set_elm_status(enc_softc_t *ssc, encioc_elm_status_t *elms, int slp) cc->flag2 &= ~SAFT_FLG1_ALARM; } ep->priv = elms->cstat[3]; - (void) wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, + (void) wrbuf16(enc, SAFTE_WT_GLOBAL, cc->flag1, cc->flag2, 0, slp); break; default: @@ -979,6 +1032,14 @@ safte_set_elm_status(enc_softc_t *ssc, encioc_elm_status_t *elms, int slp) return (0); } +static void +safte_poll_status(enc_softc_t *enc) +{ + + enc_update_request(enc, SAFTE_UPDATE_READENCSTATUS); + enc_update_request(enc, SAFTE_UPDATE_READSLOTSTATUS); +} + static struct enc_vec safte_enc_vec = { .softc_cleanup = safte_softc_cleanup, @@ -986,73 +1047,31 @@ static struct enc_vec safte_enc_vec = .get_enc_status = safte_get_enc_status, .set_enc_status = safte_set_enc_status, .get_elm_status = safte_get_elm_status, - .set_elm_status = safte_set_elm_status + .set_elm_status = safte_set_elm_status, + .poll_status = safte_poll_status }; int -safte_softc_init(enc_softc_t *ssc, int doinit) +safte_softc_init(enc_softc_t *enc, int doinit) { - int err, i, r; - struct scfg *cc; - if (doinit == 0) { - safte_softc_cleanup(ssc->periph); + safte_softc_cleanup(enc->periph); return (0); } - ssc->enc_vec = safte_enc_vec; + enc->enc_vec = safte_enc_vec; + enc->enc_fsm_states = enc_fsm_states; - if (ssc->enc_private == NULL) { - ssc->enc_private = ENC_MALLOCZ(SAFT_PRIVATE); - if (ssc->enc_private == NULL) { + if (enc->enc_private == NULL) { + enc->enc_private = ENC_MALLOCZ(SAFT_PRIVATE); + if (enc->enc_private == NULL) { return (ENOMEM); } } - ssc->enc_cache.nelms = 0; - ssc->enc_cache.enc_status = 0; + enc->enc_cache.nelms = 0; + enc->enc_cache.enc_status = 0; - if ((err = safte_getconfig(ssc)) != 0) { - return (err); - } - - /* - * The number of objects here, as well as that reported by the - * READ_BUFFER/GET_CONFIG call, are the over-temperature flags (15) - * that get reported during READ_BUFFER/READ_ENC_STATUS. - */ - cc = ssc->enc_private; - ssc->enc_cache.nelms = cc->Nfans + cc->Npwr + cc->Nslots + - cc->DoorLock + cc->Ntherm + cc->Nspkrs + cc->Ntstats + 1 + - NPSEUDO_ALARM; - ssc->enc_cache.elm_map = - ENC_MALLOCZ(ssc->enc_cache.nelms * sizeof(enc_element_t)); - if (ssc->enc_cache.elm_map == NULL) { - return (ENOMEM); - } - - r = 0; - /* - * Note that this is all arranged for the convenience - * in later fetches of status. - */ - for (i = 0; i < cc->Nfans; i++) - ssc->enc_cache.elm_map[r++].enctype = ELMTYP_FAN; - cc->pwroff = (uint8_t) r; - for (i = 0; i < cc->Npwr; i++) - ssc->enc_cache.elm_map[r++].enctype = ELMTYP_POWER; - for (i = 0; i < cc->DoorLock; i++) - ssc->enc_cache.elm_map[r++].enctype = ELMTYP_DOORLOCK; - for (i = 0; i < cc->Nspkrs; i++) - ssc->enc_cache.elm_map[r++].enctype = ELMTYP_ALARM; - for (i = 0; i < cc->Ntherm; i++) - ssc->enc_cache.elm_map[r++].enctype = ELMTYP_THERM; - for (i = 0; i <= cc->Ntstats; i++) - ssc->enc_cache.elm_map[r++].enctype = ELMTYP_THERM; - ssc->enc_cache.elm_map[r++].enctype = ELMTYP_ALARM; - cc->slotoff = (uint8_t) r; - for (i = 0; i < cc->Nslots; i++) - ssc->enc_cache.elm_map[r++].enctype = ELMTYP_DEVICE; return (0); } diff --git a/sys/cam/scsi/scsi_enc_ses.c b/sys/cam/scsi/scsi_enc_ses.c index 47c6427c075..a64e395c6b1 100644 --- a/sys/cam/scsi/scsi_enc_ses.c +++ b/sys/cam/scsi/scsi_enc_ses.c @@ -264,7 +264,7 @@ static fsm_done_handler_t ses_process_control_request; static fsm_done_handler_t ses_publish_physpaths; static fsm_done_handler_t ses_publish_cache; -struct enc_fsm_state enc_fsm_states[SES_NUM_UPDATE_STATES] = +static struct enc_fsm_state enc_fsm_states[SES_NUM_UPDATE_STATES] = { { "SES_UPDATE_NONE", 0, 0, 0, NULL, NULL, NULL }, { From e8f2c6e3dc914a9aa81b766eaea0e55a191a1a43 Mon Sep 17 00:00:00 2001 From: Alexander Motin Date: Wed, 24 Aug 2011 14:53:49 +0000 Subject: [PATCH 19/89] Try to read global flags on startup. Fix SAFT_BAIL() macro usage. --- sys/cam/scsi/scsi_enc_safte.c | 50 ++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/sys/cam/scsi/scsi_enc_safte.c b/sys/cam/scsi/scsi_enc_safte.c index a0c8f887204..84d073334e9 100644 --- a/sys/cam/scsi/scsi_enc_safte.c +++ b/sys/cam/scsi/scsi_enc_safte.c @@ -70,6 +70,7 @@ static int perf_slotop(enc_softc_t *, uint8_t, uint8_t, int); #define SAFTE_RD_RDCFG 0x00 /* read enclosure configuration */ #define SAFTE_RD_RDESTS 0x01 /* read enclosure status */ #define SAFTE_RD_RDDSTS 0x04 /* read drive slot status */ +#define SAFTE_RD_RDGFLG 0x05 /* read global flags */ /* * WRITE BUFFER ('set' commands) IDs- placed in offset 0 of databuf @@ -86,6 +87,7 @@ static int perf_slotop(enc_softc_t *, uint8_t, uint8_t, int); typedef enum { SAFTE_UPDATE_NONE, SAFTE_UPDATE_READCONFIG, + SAFTE_UPDATE_READGFLAGS, SAFTE_UPDATE_READENCSTATUS, SAFTE_UPDATE_READSLOTSTATUS, SAFTE_NUM_UPDATE_STATES @@ -93,6 +95,7 @@ typedef enum { static fsm_fill_handler_t safte_fill_read_buf_io; static fsm_done_handler_t safte_process_config; +static fsm_done_handler_t safte_process_gflags; static fsm_done_handler_t safte_process_status; static fsm_done_handler_t safte_process_slotstatus; @@ -108,6 +111,15 @@ static struct enc_fsm_state enc_fsm_states[SAFTE_NUM_UPDATE_STATES] = safte_process_config, enc_error }, + { + "SAFTE_UPDATE_READGFLAGS", + SAFTE_RD_RDGFLG, + 16, + 60 * 1000, + safte_fill_read_buf_io, + safte_process_gflags, + enc_error + }, { "SAFTE_UPDATE_READENCSTATUS", SAFTE_RD_RDESTS, @@ -167,10 +179,9 @@ struct scfg { #define SAFT_PRIVATE sizeof (struct scfg) static char *safte_2little = "Too Little Data Returned (%d) at line %d\n"; -#define SAFT_BAIL(r, x, k) \ +#define SAFT_BAIL(r, x) \ if ((r) >= (x)) { \ ENC_LOG(enc, safte_2little, x, __LINE__);\ - ENC_FREE((k)); \ return (EIO); \ } @@ -266,12 +277,31 @@ safte_process_config(enc_softc_t *enc, struct enc_fsm_state *state, for (i = 0; i < cfg->Nslots; i++) enc->enc_cache.elm_map[r++].enctype = ELMTYP_DEVICE; + enc_update_request(enc, SAFTE_UPDATE_READGFLAGS); enc_update_request(enc, SAFTE_UPDATE_READENCSTATUS); enc_update_request(enc, SAFTE_UPDATE_READSLOTSTATUS); return (0); } +static int +safte_process_gflags(enc_softc_t *enc, struct enc_fsm_state *state, + union ccb *ccb, uint8_t **bufp, int xfer_len) +{ + struct scfg *cfg; + uint8_t *buf = *bufp; + + cfg = enc->enc_private; + if (cfg == NULL) + return (ENXIO); + + SAFT_BAIL(3, xfer_len); + cfg->flag1 = buf[1]; + cfg->flag2 = buf[2]; + + return (0); +} + static int safte_process_status(enc_softc_t *enc, struct enc_fsm_state *state, union ccb *ccb, uint8_t **bufp, int xfer_len) @@ -289,7 +319,7 @@ safte_process_status(enc_softc_t *enc, struct enc_fsm_state *state, oid = r = 0; for (nitems = i = 0; i < cfg->Nfans; i++) { - SAFT_BAIL(r, xfer_len, buf); + SAFT_BAIL(r, xfer_len); /* * 0 = Fan Operational * 1 = Fan is malfunctioning @@ -366,7 +396,7 @@ safte_process_status(enc_softc_t *enc, struct enc_fsm_state *state, for (i = 0; i < cfg->Npwr; i++) { - SAFT_BAIL(r, xfer_len, buf); + SAFT_BAIL(r, xfer_len); cache->elm_map[oid].encstat[0] = SES_OBJSTAT_UNKNOWN; cache->elm_map[oid].encstat[1] = 0; /* resvd */ cache->elm_map[oid].encstat[2] = 0; /* resvd */ @@ -427,7 +457,7 @@ safte_process_status(enc_softc_t *enc, struct enc_fsm_state *state, * We always have doorlock status, no matter what, * but we only save the status if we have one. */ - SAFT_BAIL(r, xfer_len, buf); + SAFT_BAIL(r, xfer_len); if (cfg->DoorLock) { /* * 0 = Door Locked @@ -465,7 +495,7 @@ safte_process_status(enc_softc_t *enc, struct enc_fsm_state *state, * We always have speaker status, no matter what, * but we only save the status if we have one. */ - SAFT_BAIL(r, xfer_len, buf); + SAFT_BAIL(r, xfer_len); if (cfg->Nspkrs) { cache->elm_map[oid].encstat[1] = 0; cache->elm_map[oid].encstat[2] = 0; @@ -500,13 +530,13 @@ safte_process_status(enc_softc_t *enc, struct enc_fsm_state *state, * binary temperature sensor. */ - SAFT_BAIL(r + cfg->Ntherm, xfer_len, buf); + SAFT_BAIL(r + cfg->Ntherm, xfer_len); tempflags = buf[r + cfg->Ntherm]; - SAFT_BAIL(r + cfg->Ntherm + 1, xfer_len, buf); + SAFT_BAIL(r + cfg->Ntherm + 1, xfer_len); tempflags |= (tempflags << 8) | buf[r + cfg->Ntherm + 1]; for (i = 0; i < cfg->Ntherm; i++) { - SAFT_BAIL(r, xfer_len, buf); + SAFT_BAIL(r, xfer_len); /* * Status is a range from -10 to 245 deg Celsius, * which we need to normalize to -20 to -245 according @@ -606,7 +636,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, buf); + SAFT_BAIL(r+3, xfer_len); cache->elm_map[oid].encstat[0] = SES_OBJSTAT_UNSUPPORTED; cache->elm_map[oid].encstat[1] = (uint8_t) i; cache->elm_map[oid].encstat[2] = 0; From 6c99b538a5ba1976b292e08afd462e221c6d7d86 Mon Sep 17 00:00:00 2001 From: Alexander Motin Date: Wed, 24 Aug 2011 22:46:18 +0000 Subject: [PATCH 20/89] Completely rewrite SAF-TE write operations, using new fsm-based model. Change some SES emulation aspects, trying to be closer to the specs. --- sys/cam/scsi/scsi_enc_safte.c | 748 +++++++++++++++++----------------- 1 file changed, 367 insertions(+), 381 deletions(-) diff --git a/sys/cam/scsi/scsi_enc_safte.c b/sys/cam/scsi/scsi_enc_safte.c index 84d073334e9..851f46832a7 100644 --- a/sys/cam/scsi/scsi_enc_safte.c +++ b/sys/cam/scsi/scsi_enc_safte.c @@ -53,10 +53,7 @@ __FBSDID("$FreeBSD: head/sys/cam/scsi/scsi_ses.c 201758 2010-01-07 21:01:37Z mbr * SAF-TE Type Device Emulation */ -static int set_elm_status_sel(enc_softc_t *, encioc_elm_status_t *, int); static int wrbuf16(enc_softc_t *, uint8_t, uint8_t, uint8_t, uint8_t, int); -static void wrslot_stat(enc_softc_t *, int); -static int perf_slotop(enc_softc_t *, uint8_t, uint8_t, int); #define ALL_ENC_STAT (SES_ENCSTAT_CRITICAL | SES_ENCSTAT_UNRECOV | \ SES_ENCSTAT_NONCRITICAL | SES_ENCSTAT_INFO) @@ -90,14 +87,17 @@ typedef enum { SAFTE_UPDATE_READGFLAGS, SAFTE_UPDATE_READENCSTATUS, SAFTE_UPDATE_READSLOTSTATUS, + SAFTE_PROCESS_CONTROL_REQS, SAFTE_NUM_UPDATE_STATES } safte_update_action; static fsm_fill_handler_t safte_fill_read_buf_io; +static fsm_fill_handler_t safte_fill_control_request; static fsm_done_handler_t safte_process_config; static fsm_done_handler_t safte_process_gflags; static fsm_done_handler_t safte_process_status; static fsm_done_handler_t safte_process_slotstatus; +static fsm_done_handler_t safte_process_control_request; static struct enc_fsm_state enc_fsm_states[SAFTE_NUM_UPDATE_STATES] = { @@ -137,10 +137,30 @@ static struct enc_fsm_state enc_fsm_states[SAFTE_NUM_UPDATE_STATES] = safte_fill_read_buf_io, safte_process_slotstatus, enc_error + }, + { + "SAFTE_PROCESS_CONTROL_REQS", + 0, + SCSZ, + 60 * 1000, + safte_fill_control_request, + safte_process_control_request, + enc_error } }; -#define NPSEUDO_ALARM 1 +typedef struct safte_control_request { + int elm_idx; + uint8_t elm_stat[4]; + int result; + TAILQ_ENTRY(safte_control_request) links; +} safte_control_request_t; +TAILQ_HEAD(safte_control_reqlist, safte_control_request); +typedef struct safte_control_reqlist safte_control_reqlist_t; +enum { + SES_SETSTATUS_ENC_IDX = -1 +}; + struct scfg { /* * Cached Configuration @@ -151,7 +171,6 @@ struct scfg { uint8_t DoorLock; /* Door Lock Installed */ uint8_t Ntherm; /* Number of Temperature Sensors */ uint8_t Nspkrs; /* Number of Speakers */ - uint8_t Nalarm; /* Number of Alarms (at least one) */ uint8_t Ntstats; /* Number of Thermostats */ /* * Cached Flag Bytes for Global Status @@ -164,6 +183,15 @@ struct scfg { uint8_t pwroff; uint8_t slotoff; #define SAFT_ALARM_OFFSET(cc) (cc)->slotoff - 1 + + encioc_enc_status_t adm_status; + encioc_enc_status_t enc_status; + encioc_enc_status_t slot_status; + + safte_control_reqlist_t requests; + safte_control_request_t *current_request; + int current_request_stage; + int current_request_stages; }; #define SAFT_FLG1_ALARM 0x1 @@ -233,7 +261,6 @@ safte_process_config(enc_softc_t *enc, struct enc_fsm_state *state, cfg->DoorLock = buf[3]; cfg->Ntherm = buf[4]; cfg->Nspkrs = buf[5]; - cfg->Nalarm = NPSEUDO_ALARM; if (xfer_len >= 7) cfg->Ntstats = buf[6] & 0x0f; else @@ -244,8 +271,7 @@ safte_process_config(enc_softc_t *enc, struct enc_fsm_state *state, cfg->Nspkrs, cfg->Ntstats); enc->enc_cache.nelms = cfg->Nfans + cfg->Npwr + cfg->Nslots + - cfg->DoorLock + cfg->Ntherm + cfg->Nspkrs + cfg->Ntstats + 1 + - NPSEUDO_ALARM; + cfg->DoorLock + cfg->Ntherm + cfg->Nspkrs + cfg->Ntstats + 1; ENC_FREE_AND_NULL(enc->enc_cache.elm_map); enc->enc_cache.elm_map = ENC_MALLOCZ(enc->enc_cache.nelms * sizeof(enc_element_t)); @@ -266,13 +292,12 @@ safte_process_config(enc_softc_t *enc, struct enc_fsm_state *state, enc->enc_cache.elm_map[r++].enctype = ELMTYP_POWER; for (i = 0; i < cfg->DoorLock; i++) enc->enc_cache.elm_map[r++].enctype = ELMTYP_DOORLOCK; - for (i = 0; i < cfg->Nspkrs; i++) + if (cfg->Nspkrs > 0) enc->enc_cache.elm_map[r++].enctype = ELMTYP_ALARM; for (i = 0; i < cfg->Ntherm; i++) enc->enc_cache.elm_map[r++].enctype = ELMTYP_THERM; for (i = 0; i <= cfg->Ntstats; i++) enc->enc_cache.elm_map[r++].enctype = ELMTYP_THERM; - enc->enc_cache.elm_map[r++].enctype = ELMTYP_ALARM; cfg->slotoff = (uint8_t) r; for (i = 0; i < cfg->Nslots; i++) enc->enc_cache.elm_map[r++].enctype = ELMTYP_DEVICE; @@ -299,6 +324,12 @@ safte_process_gflags(enc_softc_t *enc, struct enc_fsm_state *state, cfg->flag1 = buf[1]; cfg->flag2 = buf[2]; + cfg->adm_status = 0; + if (cfg->flag1 & SAFT_FLG1_GLOBFAIL) + cfg->adm_status |= SES_ENCSTAT_CRITICAL; + else if (cfg->flag1 & SAFT_FLG1_GLOBWARN) + cfg->adm_status |= SES_ENCSTAT_NONCRITICAL; + return (0); } @@ -317,6 +348,7 @@ safte_process_status(enc_softc_t *enc, struct enc_fsm_state *state, return (ENXIO); oid = r = 0; + cfg->enc_status = 0; for (nitems = i = 0; i < cfg->Nfans; i++) { SAFT_BAIL(r, xfer_len); @@ -328,16 +360,16 @@ safte_process_status(enc_softc_t *enc, struct enc_fsm_state *state, */ cache->elm_map[oid].encstat[1] = 0; /* resvd */ cache->elm_map[oid].encstat[2] = 0; /* resvd */ + if (cfg->flag1 & SAFT_FLG1_ENCFANFAIL) + cache->elm_map[oid].encstat[3] |= 0x40; + else + cache->elm_map[oid].encstat[3] &= ~0x40; switch ((int)buf[r]) { case 0: nitems++; cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK; - /* - * We could get fancier and cache - * fan speeds that we have set, but - * that isn't done now. - */ - cache->elm_map[oid].encstat[3] = 7; + if ((cache->elm_map[oid].encstat[3] & 0x37) == 0) + cache->elm_map[oid].encstat[3] |= 0x27; break; case 1: @@ -346,35 +378,37 @@ safte_process_status(enc_softc_t *enc, struct enc_fsm_state *state, /* * FAIL and FAN STOPPED synthesized */ - cache->elm_map[oid].encstat[3] = 0x40; + cache->elm_map[oid].encstat[3] |= 0x10; + cache->elm_map[oid].encstat[3] &= ~0x07; /* * Enclosure marked with CRITICAL error * if only one fan or no thermometers, * else the NONCRITICAL error is set. */ if (cfg->Nfans == 1 || (cfg->Ntherm + cfg->Ntstats) == 0) - cache->enc_status |= SES_ENCSTAT_CRITICAL; + cfg->enc_status |= SES_ENCSTAT_CRITICAL; else - cache->enc_status |= SES_ENCSTAT_NONCRITICAL; + cfg->enc_status |= SES_ENCSTAT_NONCRITICAL; break; case 2: cache->elm_map[oid].encstat[0] = SES_OBJSTAT_NOTINSTALLED; - cache->elm_map[oid].encstat[3] = 0; + cache->elm_map[oid].encstat[3] |= 0x10; + cache->elm_map[oid].encstat[3] &= ~0x07; /* * Enclosure marked with CRITICAL error * if only one fan or no thermometers, * else the NONCRITICAL error is set. */ if (cfg->Nfans == 1) - cache->enc_status |= SES_ENCSTAT_CRITICAL; + cfg->enc_status |= SES_ENCSTAT_CRITICAL; else - cache->enc_status |= SES_ENCSTAT_NONCRITICAL; + cfg->enc_status |= SES_ENCSTAT_NONCRITICAL; break; case 0x80: cache->elm_map[oid].encstat[0] = SES_OBJSTAT_UNKNOWN; cache->elm_map[oid].encstat[3] = 0; - cache->enc_status |= SES_ENCSTAT_INFO; + cfg->enc_status |= SES_ENCSTAT_INFO; break; default: cache->elm_map[oid].encstat[0] = SES_OBJSTAT_UNSUPPORTED; @@ -390,10 +424,8 @@ safte_process_status(enc_softc_t *enc, struct enc_fsm_state *state, * No matter how you cut it, no cooling elements when there * should be some there is critical. */ - if (cfg->Nfans && nitems == 0) { - cache->enc_status |= SES_ENCSTAT_CRITICAL; - } - + if (cfg->Nfans && nitems == 0) + cfg->enc_status |= SES_ENCSTAT_CRITICAL; for (i = 0; i < cfg->Npwr; i++) { SAFT_BAIL(r, xfer_len); @@ -408,24 +440,24 @@ safte_process_status(enc_softc_t *enc, struct enc_fsm_state *state, case 0x01: /* pws operational and off */ cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK; cache->elm_map[oid].encstat[3] = 0x10; - cache->enc_status |= SES_ENCSTAT_INFO; + cfg->enc_status |= SES_ENCSTAT_INFO; break; case 0x10: /* pws is malfunctioning and commanded on */ cache->elm_map[oid].encstat[0] = SES_OBJSTAT_CRIT; cache->elm_map[oid].encstat[3] = 0x61; - cache->enc_status |= SES_ENCSTAT_NONCRITICAL; + cfg->enc_status |= SES_ENCSTAT_NONCRITICAL; break; case 0x11: /* pws is malfunctioning and commanded off */ cache->elm_map[oid].encstat[0] = SES_OBJSTAT_NONCRIT; cache->elm_map[oid].encstat[3] = 0x51; - cache->enc_status |= SES_ENCSTAT_NONCRITICAL; + cfg->enc_status |= SES_ENCSTAT_NONCRITICAL; break; case 0x20: /* pws is not present */ cache->elm_map[oid].encstat[0] = SES_OBJSTAT_NOTINSTALLED; cache->elm_map[oid].encstat[3] = 0; - cache->enc_status |= SES_ENCSTAT_INFO; + cfg->enc_status |= SES_ENCSTAT_INFO; break; case 0x21: /* pws is present */ /* @@ -437,7 +469,7 @@ safte_process_status(enc_softc_t *enc, struct enc_fsm_state *state, case 0x80: /* Unknown or Not Reportable Status */ cache->elm_map[oid].encstat[0] = SES_OBJSTAT_UNKNOWN; cache->elm_map[oid].encstat[3] = 0; - cache->enc_status |= SES_ENCSTAT_INFO; + cfg->enc_status |= SES_ENCSTAT_INFO; break; default: ENC_LOG(enc, "unknown power supply %d status (0x%x)\n", @@ -449,9 +481,13 @@ safte_process_status(enc_softc_t *enc, struct enc_fsm_state *state, } /* - * Skip over Slot SCSI IDs + * Copy Slot SCSI IDs */ - r += cfg->Nslots; + for (i = 0; i < cfg->Nslots; i++) { + SAFT_BAIL(r, xfer_len); + cache->elm_map[cfg->slotoff + i].encstat[1] = buf[r]; + r++; + } /* * We always have doorlock status, no matter what, @@ -478,7 +514,7 @@ safte_process_status(enc_softc_t *enc, struct enc_fsm_state *state, case 0x80: cache->elm_map[oid].encstat[0] = SES_OBJSTAT_UNKNOWN; cache->elm_map[oid].encstat[3] = 0; - cache->enc_status |= SES_ENCSTAT_INFO; + cfg->enc_status |= SES_ENCSTAT_INFO; break; default: cache->elm_map[oid].encstat[0] = @@ -497,24 +533,12 @@ safte_process_status(enc_softc_t *enc, struct enc_fsm_state *state, */ SAFT_BAIL(r, xfer_len); if (cfg->Nspkrs) { + cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK; cache->elm_map[oid].encstat[1] = 0; cache->elm_map[oid].encstat[2] = 0; - if (buf[r] == 1) { - /* - * We need to cache tone urgency indicators. - * Someday. - */ - cache->elm_map[oid].encstat[0] = SES_OBJSTAT_NONCRIT; - cache->elm_map[oid].encstat[3] = 0x8; - cache->enc_status |= SES_ENCSTAT_NONCRITICAL; - } else if (buf[r] == 0) { - cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK; - cache->elm_map[oid].encstat[3] = 0; - } else { - cache->elm_map[oid].encstat[0] = SES_OBJSTAT_UNSUPPORTED; - cache->elm_map[oid].encstat[3] = 0; - ENC_LOG(enc, "unknown spkr status 0x%x\n", - buf[r] & 0xff); + if (buf[r] == 0) { + cache->elm_map[oid].encstat[0] |= SESCTL_DISABLE; + cache->elm_map[oid].encstat[3] |= 0x40; } cache->elm_map[oid++].svalid = 1; } @@ -569,7 +593,7 @@ safte_process_status(enc_softc_t *enc, struct enc_fsm_state *state, */ if (tempflags & (1 << i)) { cache->elm_map[oid].encstat[0] = SES_OBJSTAT_CRIT; - cache->enc_status |= SES_ENCSTAT_CRITICAL; + cfg->enc_status |= SES_ENCSTAT_CRITICAL; } else cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK; cache->elm_map[oid].encstat[1] = 0; @@ -589,7 +613,7 @@ safte_process_status(enc_softc_t *enc, struct enc_fsm_state *state, * Set 'over temperature' failure. */ cache->elm_map[oid].encstat[3] = 8; - cache->enc_status |= SES_ENCSTAT_CRITICAL; + cfg->enc_status |= SES_ENCSTAT_CRITICAL; } else { /* * We used to say 'not available' and synthesize a @@ -610,13 +634,8 @@ safte_process_status(enc_softc_t *enc, struct enc_fsm_state *state, } r += 2; - /* - * Get alarm status. - */ - cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK; - cache->elm_map[oid].encstat[3] = cache->elm_map[oid].priv; - cache->elm_map[oid++].svalid = 1; - + cache->enc_status = + cfg->enc_status | cfg->slot_status | cfg->adm_status; return (0); } @@ -628,119 +647,281 @@ safte_process_slotstatus(enc_softc_t *enc, struct enc_fsm_state *state, uint8_t *buf = *bufp; enc_cache_t *cache = &enc->enc_cache; int oid, r, i; - uint8_t status; cfg = enc->enc_private; if (cfg == NULL) return (ENXIO); + cfg->slot_status = 0; oid = cfg->slotoff; for (r = i = 0; i < cfg->Nslots; i++, r += 4) { SAFT_BAIL(r+3, xfer_len); - cache->elm_map[oid].encstat[0] = SES_OBJSTAT_UNSUPPORTED; - cache->elm_map[oid].encstat[1] = (uint8_t) i; - cache->elm_map[oid].encstat[2] = 0; + cache->elm_map[oid].encstat[2] &= SESCTL_RQSID; cache->elm_map[oid].encstat[3] = 0; - status = buf[r+3]; - if ((status & 0x1) == 0) { /* no device */ - cache->elm_map[oid].encstat[0] = - SES_OBJSTAT_NOTINSTALLED; + if ((buf[r+3] & 0x01) == 0) { /* no device */ + cache->elm_map[oid].encstat[0] = SES_OBJSTAT_NOTINSTALLED; + } else if (buf[r+0] & 0x02) { + cache->elm_map[oid].encstat[0] = SES_OBJSTAT_CRIT; + cfg->slot_status |= SES_ENCSTAT_CRITICAL; + } else if (buf[r+0] & 0x40) { + cache->elm_map[oid].encstat[0] = SES_OBJSTAT_NONCRIT; + cfg->slot_status |= SES_ENCSTAT_NONCRITICAL; } else { cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK; } - if (status & 0x2) { - cache->elm_map[oid].encstat[2] = 0x8; - } - if ((status & 0x4) == 0) { - cache->elm_map[oid].encstat[3] = 0x10; + if (buf[r+3] & 0x2) { + if (buf[r+3] & 0x01) + cache->elm_map[oid].encstat[2] |= SESCTL_RQSRMV; + else + cache->elm_map[oid].encstat[2] |= SESCTL_RQSINS; } + if ((buf[r+3] & 0x04) == 0) + cache->elm_map[oid].encstat[3] |= SESCTL_DEVOFF; + if (buf[r+0] & 0x02) + cache->elm_map[oid].encstat[3] |= SESCTL_RQSFLT; + if (buf[r+0] & 0x40) + cache->elm_map[oid].encstat[0] |= SESCTL_PRDFAIL; cache->elm_map[oid++].svalid = 1; } + + cache->enc_status = + cfg->enc_status | cfg->slot_status | cfg->adm_status; + return (0); +} + +static int +safte_fill_control_request(enc_softc_t *enc, struct enc_fsm_state *state, + union ccb *ccb, uint8_t *buf) +{ + struct scfg *cfg; + enc_element_t *ep, *ep1; + safte_control_request_t *req; + int i, idx, xfer_len; + + cfg = enc->enc_private; + if (cfg == NULL) + return (ENXIO); + + if (enc->enc_cache.nelms == 0) { + enc_update_request(enc, SAFTE_UPDATE_READCONFIG); + return (-1); + } + + if (cfg->current_request == NULL) { + cfg->current_request = TAILQ_FIRST(&cfg->requests); + TAILQ_REMOVE(&cfg->requests, cfg->current_request, links); + cfg->current_request_stage = 0; + cfg->current_request_stages = 1; + } + req = cfg->current_request; + + idx = (int)req->elm_idx; + if (req->elm_idx == SES_SETSTATUS_ENC_IDX) { + cfg->adm_status = req->elm_stat[0] & ALL_ENC_STAT; + cfg->flag1 &= ~(SAFT_FLG1_GLOBFAIL|SAFT_FLG1_GLOBWARN); + if (req->elm_stat[0] & (SES_ENCSTAT_CRITICAL|SES_ENCSTAT_UNRECOV)) + cfg->flag1 |= SAFT_FLG1_GLOBFAIL; + else if (req->elm_stat[0] & SES_ENCSTAT_NONCRITICAL) + cfg->flag1 |= SAFT_FLG1_GLOBWARN; + buf[0] = SAFTE_WT_GLOBAL; + buf[1] = cfg->flag1; + buf[2] = cfg->flag2; + buf[3] = 0; + xfer_len = 16; + } else { + ep = &enc->enc_cache.elm_map[idx]; + + switch (ep->enctype) { + case ELMTYP_DEVICE: + switch (cfg->current_request_stage) { + case 0: + ep->priv = 0; + if (req->elm_stat[0] & SESCTL_PRDFAIL) + ep->priv |= 0x40; + if (req->elm_stat[3] & SESCTL_RQSFLT) + ep->priv |= 0x02; + if ((ep->priv & 0x46) == 0) + ep->priv |= 0x01; /* no errors */ + + buf[0] = SAFTE_WT_DSTAT; + for (i = 0; i < cfg->Nslots; i++) { + ep1 = &enc->enc_cache.elm_map[cfg->slotoff + i]; + buf[1 + (3 * i)] = ep1->priv; + buf[2 + (3 * i)] = ep1->priv >> 8; + } + xfer_len = cfg->Nslots * 3 + 1; +#define DEVON(x) (!(((x)[2] & SESCTL_RQSINS) | \ + ((x)[2] & SESCTL_RQSRMV) | \ + ((x)[3] & SESCTL_DEVOFF))) + if (DEVON(req->elm_stat) != DEVON(ep->encstat)) + cfg->current_request_stages++; +#define IDON(x) (!!((x)[2] & SESCTL_RQSID)) + if (IDON(req->elm_stat) != IDON(ep->encstat)) + cfg->current_request_stages++; + break; + case 1: + case 2: + buf[0] = SAFTE_WT_SLTOP; + buf[1] = idx - cfg->slotoff; + if (cfg->current_request_stage == 1 && + DEVON(req->elm_stat) != DEVON(ep->encstat)) { + if (DEVON(req->elm_stat)) + buf[2] = 0x01; + else + buf[2] = 0x02; + } else { + if (IDON(req->elm_stat)) + buf[2] = 0x04; + else + buf[2] = 0x00; + ep->encstat[2] &= ~SESCTL_RQSID; + ep->encstat[2] |= req->elm_stat[2] & + SESCTL_RQSID; + } + xfer_len = 64; + break; + default: + return (EINVAL); + } + break; + case ELMTYP_POWER: + cfg->current_request_stages = 2; + switch (cfg->current_request_stage) { + case 0: + if (req->elm_stat[3] & SESCTL_RQSTFAIL) { + cfg->flag1 |= SAFT_FLG1_ENCPWRFAIL; + } else { + cfg->flag1 &= ~SAFT_FLG1_ENCPWRFAIL; + } + buf[0] = SAFTE_WT_GLOBAL; + buf[1] = cfg->flag1; + buf[2] = cfg->flag2; + buf[3] = 0; + xfer_len = 16; + break; + case 1: + buf[0] = SAFTE_WT_ACTPWS; + buf[1] = idx - cfg->pwroff; + if (req->elm_stat[3] & SESCTL_RQSTON) + buf[2] = 0x01; + else + buf[2] = 0x00; + buf[3] = 0; + xfer_len = 16; + default: + return (EINVAL); + } + break; + case ELMTYP_FAN: + if ((req->elm_stat[3] & 0x7) != 0) + cfg->current_request_stages = 2; + switch (cfg->current_request_stage) { + case 0: + if (req->elm_stat[3] & SESCTL_RQSTFAIL) + cfg->flag1 |= SAFT_FLG1_ENCFANFAIL; + else + cfg->flag1 &= ~SAFT_FLG1_ENCFANFAIL; + buf[0] = SAFTE_WT_GLOBAL; + buf[1] = cfg->flag1; + buf[2] = cfg->flag2; + buf[3] = 0; + xfer_len = 16; + break; + case 1: + buf[0] = SAFTE_WT_FANSPD; + buf[1] = idx; + if (req->elm_stat[3] & SESCTL_RQSTON) { + if ((req->elm_stat[3] & 0x7) == 7) + buf[2] = 4; + else if ((req->elm_stat[3] & 0x7) >= 5) + buf[2] = 3; + else if ((req->elm_stat[3] & 0x7) >= 3) + buf[2] = 2; + else + buf[2] = 1; + } else + buf[2] = 0; + buf[3] = 0; + xfer_len = 16; + ep->encstat[3] = req->elm_stat[3] & 0x67; + default: + return (EINVAL); + } + break; + case ELMTYP_DOORLOCK: + if (req->elm_stat[3] & 0x1) + cfg->flag2 &= ~SAFT_FLG2_LOCKDOOR; + else + cfg->flag2 |= SAFT_FLG2_LOCKDOOR; + buf[0] = SAFTE_WT_GLOBAL; + buf[1] = cfg->flag1; + buf[2] = cfg->flag2; + buf[3] = 0; + xfer_len = 16; + break; + case ELMTYP_ALARM: + if ((req->elm_stat[0] & SESCTL_DISABLE) || + (req->elm_stat[3] & 0x40)) { + cfg->flag2 &= ~SAFT_FLG1_ALARM; + } else if ((req->elm_stat[3] & 0x0f) != 0) { + cfg->flag2 |= SAFT_FLG1_ALARM; + } else { + cfg->flag2 &= ~SAFT_FLG1_ALARM; + } + buf[0] = SAFTE_WT_GLOBAL; + buf[1] = cfg->flag1; + buf[2] = cfg->flag2; + buf[3] = 0; + xfer_len = 16; + ep->encstat[3] = req->elm_stat[3]; + break; + default: + return (EINVAL); + } + } + + if (enc->enc_type == ENC_SEMB_SAFT) { + semb_write_buffer(&ccb->ataio, /*retries*/5, + enc_done, MSG_SIMPLE_Q_TAG, + buf, xfer_len, state->timeout); + } else { + scsi_write_buffer(&ccb->csio, /*retries*/5, + enc_done, MSG_SIMPLE_Q_TAG, 1, + 0, 0, buf, xfer_len, + SSD_FULL_SIZE, state->timeout); + } return (0); } static int -set_elm_status_sel(enc_softc_t *enc, encioc_elm_status_t *elms, int slp) +safte_process_control_request(enc_softc_t *enc, struct enc_fsm_state *state, + union ccb *ccb, uint8_t **bufp, int xfer_len) { - int idx; - enc_element_t *ep; - struct scfg *cc = enc->enc_private; + struct scfg *cfg; + safte_control_request_t *req; + int idx, type; - if (cc == NULL) - return (0); + cfg = enc->enc_private; + if (cfg == NULL) + return (ENXIO); - idx = (int)elms->elm_idx; - ep = &enc->enc_cache.elm_map[idx]; - - switch (ep->enctype) { - case ELMTYP_DEVICE: - if (elms->cstat[0] & SESCTL_PRDFAIL) { - ep->priv |= 0x40; - } else { - ep->priv &= ~0x40; - } - /* SESCTL_RSTSWAP has no correspondence in SAF-TE */ - if (elms->cstat[0] & SESCTL_DISABLE) { - ep->priv |= 0x80; - /* - * Hmm. Try to set the 'No Drive' flag. - * Maybe that will count as a 'disable'. - */ - } else { - ep->priv &= ~0x80; - } - if (ep->priv & 0xc6) { - ep->priv &= ~0x1; - } else { - ep->priv |= 0x1; /* no errors */ - } - wrslot_stat(enc, slp); - break; - case ELMTYP_POWER: - /* - * Okay- the only one that makes sense here is to - * do the 'disable' for a power supply. - */ - if (elms->cstat[0] & SESCTL_DISABLE) { - (void) wrbuf16(enc, SAFTE_WT_ACTPWS, - idx - cc->pwroff, 0, 0, slp); - } - break; - case ELMTYP_FAN: - /* - * Okay- the only one that makes sense here is to - * set fan speed to zero on disable. - */ - if (elms->cstat[0] & SESCTL_DISABLE) { - /* remember- fans are the first items, so idx works */ - (void) wrbuf16(enc, SAFTE_WT_FANSPD, idx, 0, 0, slp); - } - break; - case ELMTYP_DOORLOCK: - /* - * Well, we can 'disable' the lock. - */ - if (elms->cstat[0] & SESCTL_DISABLE) { - cc->flag2 &= ~SAFT_FLG2_LOCKDOOR; - (void) wrbuf16(enc, SAFTE_WT_GLOBAL, cc->flag1, - cc->flag2, 0, slp); - } - break; - case ELMTYP_ALARM: - /* - * Well, we can 'disable' the alarm. - */ - if (elms->cstat[0] & SESCTL_DISABLE) { - cc->flag2 &= ~SAFT_FLG1_ALARM; - ep->priv |= 0x40; /* Muted */ - (void) wrbuf16(enc, SAFTE_WT_GLOBAL, cc->flag1, - cc->flag2, 0, slp); - } - break; - default: - break; + if (++cfg->current_request_stage >= cfg->current_request_stages) { + req = cfg->current_request; + idx = req->elm_idx; + if (idx == SES_SETSTATUS_ENC_IDX) + type = -1; + else + type = enc->enc_cache.elm_map[idx].enctype; + if (type == ELMTYP_DEVICE) + enc_update_request(enc, SAFTE_UPDATE_READSLOTSTATUS); + else + enc_update_request(enc, SAFTE_UPDATE_READENCSTATUS); + cfg->current_request = NULL; + req->result = 0; + wakeup(req); + } else { + enc_update_request(enc, SAFTE_PROCESS_CONTROL_REQS); } - ep->svalid = 0; return (0); } @@ -775,79 +956,6 @@ wrbuf16(enc_softc_t *enc, uint8_t op, uint8_t b1, uint8_t b2, return (err); } -/* - * This function updates the status byte for the device slot described. - * - * Since this is an optional SAF-TE command, there's no point in - * returning an error. - */ -static void -wrslot_stat(enc_softc_t *enc, int slp) -{ - int i, amt; - enc_element_t *ep; - char cdb[10], *sdata; - struct scfg *cc = enc->enc_private; - - if (cc == NULL) - return; - - ENC_DLOG(enc, "saf_wrslot\n"); - cdb[0] = WRITE_BUFFER; - cdb[1] = 1; - cdb[2] = 0; - cdb[3] = 0; - cdb[4] = 0; - cdb[5] = 0; - cdb[6] = 0; - cdb[7] = 0; - cdb[8] = cc->Nslots * 3 + 1; - cdb[9] = 0; - - sdata = ENC_MALLOCZ(cc->Nslots * 3 + 1); - if (sdata == NULL) - return; - - sdata[0] = SAFTE_WT_DSTAT; - for (i = 0; i < cc->Nslots; i++) { - ep = &enc->enc_cache.elm_map[cc->slotoff + i]; - ENC_DLOG(enc, "saf_wrslot %d <- %x\n", i, ep->priv & 0xff); - sdata[1 + (3 * i)] = ep->priv & 0xff; - } - amt = -(cc->Nslots * 3 + 1); - (void) enc_runcmd(enc, cdb, 10, sdata, &amt); - ENC_FREE(sdata); -} - -/* - * This function issues the "PERFORM SLOT OPERATION" command. - */ -static int -perf_slotop(enc_softc_t *enc, uint8_t slot, uint8_t opflag, int slp) -{ - int err, amt; - char *sdata; - struct scfg *cc = enc->enc_private; - static char cdb[10] = - { WRITE_BUFFER, 1, 0, 0, 0, 0, 0, 0, SAFT_SCRATCH, 0 }; - - if (cc == NULL) - return (0); - - sdata = ENC_MALLOCZ(SAFT_SCRATCH); - if (sdata == NULL) - return (ENOMEM); - - sdata[0] = SAFTE_WT_SLTOP; - sdata[1] = slot; - sdata[2] = opflag; - ENC_DLOG(enc, "saf_slotop slot %d op %x\n", slot, opflag); - amt = -SAFT_SCRATCH; - err = enc_runcmd(enc, cdb, 10, sdata, &amt); - ENC_FREE(sdata); - return (err); -} - static void safte_softc_cleanup(struct cam_periph *periph) { @@ -882,26 +990,23 @@ safte_get_enc_status(enc_softc_t *enc, int slpflg) } static int -safte_set_enc_status(enc_softc_t *enc, uint8_t encstat, int slpflg) +safte_set_enc_status(enc_softc_t *enc, uint8_t encstat, int slpflag) { - struct scfg *cc = enc->enc_private; - if (cc == NULL) - return (0); - /* - * Since SAF-TE devices aren't necessarily sticky in terms - * of state, make our soft copy of enclosure status 'sticky'- - * that is, things set in enclosure status stay set (as implied - * by conditions set in reading object status) until cleared. - */ - enc->enc_cache.enc_status &= ~ALL_ENC_STAT; - enc->enc_cache.enc_status |= (encstat & ALL_ENC_STAT); - cc->flag1 &= ~(SAFT_FLG1_ALARM|SAFT_FLG1_GLOBFAIL|SAFT_FLG1_GLOBWARN); - if ((encstat & (SES_ENCSTAT_CRITICAL|SES_ENCSTAT_UNRECOV)) != 0) { - cc->flag1 |= SAFT_FLG1_ALARM|SAFT_FLG1_GLOBFAIL; - } else if ((encstat & SES_ENCSTAT_NONCRITICAL) != 0) { - cc->flag1 |= SAFT_FLG1_GLOBWARN; - } - return (wrbuf16(enc, SAFTE_WT_GLOBAL, cc->flag1, cc->flag2, 0, slpflg)); + struct scfg *cfg; + safte_control_request_t req; + + cfg = enc->enc_private; + if (cfg == NULL) + return (ENXIO); + + req.elm_idx = SES_SETSTATUS_ENC_IDX; + req.elm_stat[0] = encstat & 0xf; + + TAILQ_INSERT_TAIL(&cfg->requests, &req, links); + enc_update_request(enc, SAFTE_PROCESS_CONTROL_REQS); + cam_periph_sleep(enc->periph, &req, PUSER, "encstat", 0); + + return (req.result); } static int @@ -916,150 +1021,28 @@ safte_get_elm_status(enc_softc_t *enc, encioc_elm_status_t *elms, int slpflg) return (0); } - static int -safte_set_elm_status(enc_softc_t *enc, encioc_elm_status_t *elms, int slp) +safte_set_elm_status(enc_softc_t *enc, encioc_elm_status_t *elms, int slpflag) { - int idx, err; - enc_element_t *ep; - struct scfg *cc; + struct scfg *cfg; + safte_control_request_t req; + cfg = enc->enc_private; + if (cfg == NULL) + return (ENXIO); - ENC_DLOG(enc, "safte_set_objstat(%d): %x %x %x %x\n", - (int)elms->elm_idx, elms->cstat[0], elms->cstat[1], elms->cstat[2], - elms->cstat[3]); - - /* - * If this is clear, we don't do diddly. - */ - if ((elms->cstat[0] & SESCTL_CSEL) == 0) { - return (0); - } - - err = 0; - /* - * Check to see if the common bits are set and do them first. - */ - if (elms->cstat[0] & ~SESCTL_CSEL) { - err = set_elm_status_sel(enc, elms, slp); - if (err) - return (err); - } - - cc = enc->enc_private; - if (cc == NULL) + /* If this is clear, we don't do diddly. */ + if ((elms->cstat[0] & SESCTL_CSEL) == 0) return (0); - idx = (int)elms->elm_idx; - ep = &enc->enc_cache.elm_map[idx]; + req.elm_idx = elms->elm_idx; + memcpy(&req.elm_stat, elms->cstat, sizeof(req.elm_stat)); - switch (ep->enctype) { - case ELMTYP_DEVICE: - { - uint8_t slotop = 0; - /* - * XXX: I should probably cache the previous state - * XXX: of SESCTL_DEVOFF so that when it goes from - * XXX: true to false I can then set PREPARE FOR OPERATION - * XXX: flag in PERFORM SLOT OPERATION write buffer command. - */ - if (elms->cstat[2] & (SESCTL_RQSINS|SESCTL_RQSRMV)) { - slotop |= 0x2; - } - if (elms->cstat[2] & SESCTL_RQSID) { - slotop |= 0x4; - } - err = perf_slotop(enc, (uint8_t) idx - (uint8_t) cc->slotoff, - slotop, slp); - if (err) - return (err); - if (elms->cstat[3] & SESCTL_RQSFLT) { - ep->priv |= 0x2; - } else { - ep->priv &= ~0x2; - } - if (ep->priv & 0xc6) { - ep->priv &= ~0x1; - } else { - ep->priv |= 0x1; /* no errors */ - } - wrslot_stat(enc, slp); - break; - } - case ELMTYP_POWER: - if (elms->cstat[3] & SESCTL_RQSTFAIL) { - cc->flag1 |= SAFT_FLG1_ENCPWRFAIL; - } else { - cc->flag1 &= ~SAFT_FLG1_ENCPWRFAIL; - } - err = wrbuf16(enc, SAFTE_WT_GLOBAL, cc->flag1, - cc->flag2, 0, slp); - if (err) - return (err); - if (elms->cstat[3] & SESCTL_RQSTON) { - (void) wrbuf16(enc, SAFTE_WT_ACTPWS, - idx - cc->pwroff, 0, 0, slp); - } else { - (void) wrbuf16(enc, SAFTE_WT_ACTPWS, - idx - cc->pwroff, 0, 1, slp); - } - break; - case ELMTYP_FAN: - if (elms->cstat[3] & SESCTL_RQSTFAIL) { - cc->flag1 |= SAFT_FLG1_ENCFANFAIL; - } else { - cc->flag1 &= ~SAFT_FLG1_ENCFANFAIL; - } - err = wrbuf16(enc, SAFTE_WT_GLOBAL, cc->flag1, - cc->flag2, 0, slp); - if (err) - return (err); - if (elms->cstat[3] & SESCTL_RQSTON) { - uint8_t fsp; - if ((elms->cstat[3] & 0x7) == 7) { - fsp = 4; - } else if ((elms->cstat[3] & 0x7) == 6) { - fsp = 3; - } else if ((elms->cstat[3] & 0x7) == 4) { - fsp = 2; - } else { - fsp = 1; - } - (void) wrbuf16(enc, SAFTE_WT_FANSPD, idx, fsp, 0, slp); - } else { - (void) wrbuf16(enc, SAFTE_WT_FANSPD, idx, 0, 0, slp); - } - break; - case ELMTYP_DOORLOCK: - if (elms->cstat[3] & 0x1) { - cc->flag2 &= ~SAFT_FLG2_LOCKDOOR; - } else { - cc->flag2 |= SAFT_FLG2_LOCKDOOR; - } - (void) wrbuf16(enc, SAFTE_WT_GLOBAL, cc->flag1, - cc->flag2, 0, slp); - break; - case ELMTYP_ALARM: - /* - * On all nonzero but the 'muted' bit, we turn on the alarm, - */ - elms->cstat[3] &= ~0xa; - if (elms->cstat[3] & 0x40) { - cc->flag2 &= ~SAFT_FLG1_ALARM; - } else if (elms->cstat[3] != 0) { - cc->flag2 |= SAFT_FLG1_ALARM; - } else { - cc->flag2 &= ~SAFT_FLG1_ALARM; - } - ep->priv = elms->cstat[3]; - (void) wrbuf16(enc, SAFTE_WT_GLOBAL, cc->flag1, - cc->flag2, 0, slp); - break; - default: - break; - } - ep->svalid = 0; - return (0); + TAILQ_INSERT_TAIL(&cfg->requests, &req, links); + enc_update_request(enc, SAFTE_PROCESS_CONTROL_REQS); + cam_periph_sleep(enc->periph, &req, PUSER, "encstat", 0); + + return (req.result); } static void @@ -1084,6 +1067,8 @@ static struct enc_vec safte_enc_vec = int safte_softc_init(enc_softc_t *enc, int doinit) { + struct scfg *cfg; + if (doinit == 0) { safte_softc_cleanup(enc->periph); return (0); @@ -1094,14 +1079,15 @@ safte_softc_init(enc_softc_t *enc, int doinit) if (enc->enc_private == NULL) { enc->enc_private = ENC_MALLOCZ(SAFT_PRIVATE); - if (enc->enc_private == NULL) { + if (enc->enc_private == NULL) return (ENOMEM); - } } + cfg = enc->enc_private; enc->enc_cache.nelms = 0; enc->enc_cache.enc_status = 0; + TAILQ_INIT(&cfg->requests); return (0); } From 77acd3d05b8b386f6990dc9098d10c8a8f830110 Mon Sep 17 00:00:00 2001 From: Alexander Motin Date: Thu, 25 Aug 2011 00:14:30 +0000 Subject: [PATCH 21/89] Make SAF-TE driver emulate Array Device Slots instead of Device Slots. This allows to pass to enclosure six more data bits, describing device state from RAID PoV. Add kern.cam.enc.%d.emulate_array_devices tunable, allowing to revert to the original behavior. --- sys/cam/scsi/scsi_enc_safte.c | 46 ++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/sys/cam/scsi/scsi_enc_safte.c b/sys/cam/scsi/scsi_enc_safte.c index 851f46832a7..a7e7e8d5845 100644 --- a/sys/cam/scsi/scsi_enc_safte.c +++ b/sys/cam/scsi/scsi_enc_safte.c @@ -162,6 +162,7 @@ enum { }; struct scfg { + int emulate_array_devices; /* * Cached Configuration */ @@ -300,7 +301,9 @@ safte_process_config(enc_softc_t *enc, struct enc_fsm_state *state, enc->enc_cache.elm_map[r++].enctype = ELMTYP_THERM; cfg->slotoff = (uint8_t) r; for (i = 0; i < cfg->Nslots; i++) - enc->enc_cache.elm_map[r++].enctype = ELMTYP_DEVICE; + enc->enc_cache.elm_map[r++].enctype = + cfg->emulate_array_devices ? ELMTYP_ARRAY_DEV : + ELMTYP_DEVICE; enc_update_request(enc, SAFTE_UPDATE_READGFLAGS); enc_update_request(enc, SAFTE_UPDATE_READENCSTATUS); @@ -485,7 +488,8 @@ safte_process_status(enc_softc_t *enc, struct enc_fsm_state *state, */ for (i = 0; i < cfg->Nslots; i++) { SAFT_BAIL(r, xfer_len); - cache->elm_map[cfg->slotoff + i].encstat[1] = buf[r]; + if (!cfg->emulate_array_devices) + cache->elm_map[cfg->slotoff + i].encstat[1] = buf[r]; r++; } @@ -656,6 +660,8 @@ 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 (cfg->emulate_array_devices) + cache->elm_map[oid].encstat[1] = 0; cache->elm_map[oid].encstat[2] &= SESCTL_RQSID; cache->elm_map[oid].encstat[3] = 0; if ((buf[r+3] & 0x01) == 0) { /* no device */ @@ -681,6 +687,20 @@ 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 (cfg->emulate_array_devices) { + if (buf[r+0] & 0x04) + cache->elm_map[oid].encstat[1] |= 0x02; + if (buf[r+0] & 0x08) + cache->elm_map[oid].encstat[1] |= 0x04; + if (buf[r+0] & 0x10) + cache->elm_map[oid].encstat[1] |= 0x08; + if (buf[r+0] & 0x20) + cache->elm_map[oid].encstat[1] |= 0x10; + if (buf[r+1] & 0x01) + cache->elm_map[oid].encstat[1] |= 0x20; + if (buf[r+1] & 0x02) + cache->elm_map[oid].encstat[1] |= 0x01; + } cache->elm_map[oid++].svalid = 1; } @@ -733,6 +753,7 @@ safte_fill_control_request(enc_softc_t *enc, struct enc_fsm_state *state, switch (ep->enctype) { case ELMTYP_DEVICE: + case ELMTYP_ARRAY_DEV: switch (cfg->current_request_stage) { case 0: ep->priv = 0; @@ -742,6 +763,20 @@ safte_fill_control_request(enc_softc_t *enc, struct enc_fsm_state *state, ep->priv |= 0x02; if ((ep->priv & 0x46) == 0) ep->priv |= 0x01; /* no errors */ + if (cfg->emulate_array_devices) { + if (req->elm_stat[1] & 0x01) + ep->priv |= 0x200; + if (req->elm_stat[1] & 0x02) + ep->priv |= 0x04; + if (req->elm_stat[1] & 0x04) + ep->priv |= 0x08; + if (req->elm_stat[1] & 0x08) + ep->priv |= 0x10; + if (req->elm_stat[1] & 0x10) + ep->priv |= 0x20; + if (req->elm_stat[1] & 0x20) + ep->priv |= 0x100; + } buf[0] = SAFTE_WT_DSTAT; for (i = 0; i < cfg->Nslots; i++) { @@ -912,7 +947,7 @@ safte_process_control_request(enc_softc_t *enc, struct enc_fsm_state *state, type = -1; else type = enc->enc_cache.elm_map[idx].enctype; - if (type == ELMTYP_DEVICE) + if (type == ELMTYP_DEVICE || type == ELMTYP_ARRAY_DEV) enc_update_request(enc, SAFTE_UPDATE_READSLOTSTATUS); else enc_update_request(enc, SAFTE_UPDATE_READENCSTATUS); @@ -1068,6 +1103,7 @@ int safte_softc_init(enc_softc_t *enc, int doinit) { struct scfg *cfg; + char buf[32]; if (doinit == 0) { safte_softc_cleanup(enc->periph); @@ -1088,6 +1124,10 @@ safte_softc_init(enc_softc_t *enc, int doinit) enc->enc_cache.enc_status = 0; TAILQ_INIT(&cfg->requests); + cfg->emulate_array_devices = 1; + snprintf(buf, sizeof(buf), "kern.cam.enc.%d.emulate_array_devices", + enc->periph->unit_number); + TUNABLE_INT_FETCH(buf, &cfg->emulate_array_devices); return (0); } From 94802ffacbb7ea4a99c16ac86f4fd6797953394b Mon Sep 17 00:00:00 2001 From: Alexander Motin Date: Thu, 25 Aug 2011 11:02:01 +0000 Subject: [PATCH 22/89] Remove some strange lock operations. --- sys/cam/scsi/scsi_enc.c | 7 +------ sys/cam/scsi/scsi_enc_ses.c | 2 -- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/sys/cam/scsi/scsi_enc.c b/sys/cam/scsi/scsi_enc.c index 3e435ec529a..dd6926dcbaf 100644 --- a/sys/cam/scsi/scsi_enc.c +++ b/sys/cam/scsi/scsi_enc.c @@ -242,10 +242,8 @@ enc_open(struct cdev *dev, int flags, int fmt, struct thread *td) return (ENXIO); } - if (cam_periph_acquire(periph) != CAM_REQ_CMP) { - cam_periph_unlock(periph); + if (cam_periph_acquire(periph) != CAM_REQ_CMP) return (ENXIO); - } cam_periph_lock(periph); @@ -273,9 +271,6 @@ enc_close(struct cdev *dev, int flag, int fmt, struct thread *td) { struct cam_periph *periph; struct enc_softc *softc; - int error; - - error = 0; periph = (struct cam_periph *)dev->si_drv1; if (periph == NULL) diff --git a/sys/cam/scsi/scsi_enc_ses.c b/sys/cam/scsi/scsi_enc_ses.c index a64e395c6b1..b8315e56f03 100644 --- a/sys/cam/scsi/scsi_enc_ses.c +++ b/sys/cam/scsi/scsi_enc_ses.c @@ -2563,8 +2563,6 @@ ses_set_enc_status(enc_softc_t *enc, uint8_t encstat, int slpflag) enc_update_request(enc, SES_PROCESS_CONTROL_REQS); cam_periph_sleep(enc->periph, &req, PUSER, "encstat", 0); - sx_slock(&enc->enc_cache_lock); - return (req.result); } From 8247d3fc8ee266c92e03861421a51cee02579ac6 Mon Sep 17 00:00:00 2001 From: Alexander Motin Date: Thu, 25 Aug 2011 11:39:32 +0000 Subject: [PATCH 23/89] Drop the periph lock during M_WAITOK buffer allocation. --- sys/cam/scsi/scsi_enc.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sys/cam/scsi/scsi_enc.c b/sys/cam/scsi/scsi_enc.c index dd6926dcbaf..e32eb21413f 100644 --- a/sys/cam/scsi/scsi_enc.c +++ b/sys/cam/scsi/scsi_enc.c @@ -744,8 +744,11 @@ enc_fsm_step(enc_softc_t *enc) cur_state = &enc->enc_fsm_states[enc->current_action]; buf = NULL; - if (cur_state->buf_size != 0) + if (cur_state->buf_size != 0) { + cam_periph_unlock(enc->periph); buf = malloc(cur_state->buf_size, M_SCSIENC, M_WAITOK|M_ZERO); + cam_periph_lock(enc->periph); + } error = 0; ccb = NULL; From 6c143125b2653bf85f293652823fae34d2a96e70 Mon Sep 17 00:00:00 2001 From: Alexander Motin Date: Thu, 25 Aug 2011 12:09:40 +0000 Subject: [PATCH 24/89] Make emulate_array_devices global to not bother with locking and kenv. This also makes it writable sysctl. --- sys/cam/scsi/scsi_enc.c | 4 ++++ sys/cam/scsi/scsi_enc_safte.c | 23 ++++++++++++----------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/sys/cam/scsi/scsi_enc.c b/sys/cam/scsi/scsi_enc.c index e32eb21413f..9e70fa0aead 100644 --- a/sys/cam/scsi/scsi_enc.c +++ b/sys/cam/scsi/scsi_enc.c @@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include @@ -76,6 +77,9 @@ static periph_start_t enc_start; static void enc_async(void *, uint32_t, struct cam_path *, void *); static enctyp enc_type(struct ccb_getdev *); +SYSCTL_NODE(_kern_cam, OID_AUTO, enc, CTLFLAG_RD, 0, + "CAM Enclosure Services driver"); + static struct periph_driver encdriver = { enc_init, "enc", TAILQ_HEAD_INITIALIZER(encdriver.units), /* generation */ 0 diff --git a/sys/cam/scsi/scsi_enc_safte.c b/sys/cam/scsi/scsi_enc_safte.c index a7e7e8d5845..1eae7d541ce 100644 --- a/sys/cam/scsi/scsi_enc_safte.c +++ b/sys/cam/scsi/scsi_enc_safte.c @@ -37,6 +37,7 @@ __FBSDID("$FreeBSD: head/sys/cam/scsi/scsi_ses.c 201758 2010-01-07 21:01:37Z mbr #include #include #include +#include #include #include @@ -162,7 +163,6 @@ enum { }; struct scfg { - int emulate_array_devices; /* * Cached Configuration */ @@ -214,6 +214,12 @@ static char *safte_2little = "Too Little Data Returned (%d) at line %d\n"; return (EIO); \ } +int emulate_array_devices = 1; +SYSCTL_DECL(_kern_cam_enc); +SYSCTL_INT(_kern_cam_enc, OID_AUTO, emulate_array_devices, CTLFLAG_RW, + &emulate_array_devices, 0, "Emulate Array Devices for SAF-TE"); +TUNABLE_INT("kern.cam.enc.emulate_array_devices", &emulate_array_devices); + static int safte_fill_read_buf_io(enc_softc_t *enc, struct enc_fsm_state *state, union ccb *ccb, uint8_t *buf) @@ -302,7 +308,7 @@ safte_process_config(enc_softc_t *enc, struct enc_fsm_state *state, cfg->slotoff = (uint8_t) r; for (i = 0; i < cfg->Nslots; i++) enc->enc_cache.elm_map[r++].enctype = - cfg->emulate_array_devices ? ELMTYP_ARRAY_DEV : + emulate_array_devices ? ELMTYP_ARRAY_DEV : ELMTYP_DEVICE; enc_update_request(enc, SAFTE_UPDATE_READGFLAGS); @@ -488,7 +494,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 (!cfg->emulate_array_devices) + if (cache->elm_map[cfg->slotoff + i].enctype == ELMTYP_DEVICE) cache->elm_map[cfg->slotoff + i].encstat[1] = buf[r]; r++; } @@ -660,7 +666,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 (cfg->emulate_array_devices) + if (cache->elm_map[oid].enctype == 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; @@ -687,7 +693,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 (cfg->emulate_array_devices) { + if (cache->elm_map[oid].enctype == ELMTYP_ARRAY_DEV) { if (buf[r+0] & 0x04) cache->elm_map[oid].encstat[1] |= 0x02; if (buf[r+0] & 0x08) @@ -763,7 +769,7 @@ safte_fill_control_request(enc_softc_t *enc, struct enc_fsm_state *state, ep->priv |= 0x02; if ((ep->priv & 0x46) == 0) ep->priv |= 0x01; /* no errors */ - if (cfg->emulate_array_devices) { + if (ep->enctype == ELMTYP_ARRAY_DEV) { if (req->elm_stat[1] & 0x01) ep->priv |= 0x200; if (req->elm_stat[1] & 0x02) @@ -1103,7 +1109,6 @@ int safte_softc_init(enc_softc_t *enc, int doinit) { struct scfg *cfg; - char buf[32]; if (doinit == 0) { safte_softc_cleanup(enc->periph); @@ -1124,10 +1129,6 @@ safte_softc_init(enc_softc_t *enc, int doinit) enc->enc_cache.enc_status = 0; TAILQ_INIT(&cfg->requests); - cfg->emulate_array_devices = 1; - snprintf(buf, sizeof(buf), "kern.cam.enc.%d.emulate_array_devices", - enc->periph->unit_number); - TUNABLE_INT_FETCH(buf, &cfg->emulate_array_devices); return (0); } From c2d1f1ced24d9fff548c1faba2338c76f9fe6743 Mon Sep 17 00:00:00 2001 From: Alexander Motin Date: Thu, 25 Aug 2011 12:28:41 +0000 Subject: [PATCH 25/89] Reread SES enclosure status after writing it. --- sys/cam/scsi/scsi_enc_ses.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sys/cam/scsi/scsi_enc_ses.c b/sys/cam/scsi/scsi_enc_ses.c index b8315e56f03..5dced23ce84 100644 --- a/sys/cam/scsi/scsi_enc_ses.c +++ b/sys/cam/scsi/scsi_enc_ses.c @@ -1824,6 +1824,7 @@ ses_process_control_request(enc_softc_t *enc, struct enc_fsm_state *state, * o Some SCSI status error. */ ses_terminate_control_requests(&ses->ses_pending_requests, 0); + enc_update_request(enc, SES_UPDATE_GETSTATUS); return (0); } From 88bf72d0cd457f95500614c6cead830de51fea75 Mon Sep 17 00:00:00 2001 From: Alexander Motin Date: Thu, 25 Aug 2011 17:35:10 +0000 Subject: [PATCH 26/89] Map "OK" bit to "No Error". --- sys/cam/scsi/scsi_enc_safte.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sys/cam/scsi/scsi_enc_safte.c b/sys/cam/scsi/scsi_enc_safte.c index 1eae7d541ce..6f796346e68 100644 --- a/sys/cam/scsi/scsi_enc_safte.c +++ b/sys/cam/scsi/scsi_enc_safte.c @@ -694,6 +694,8 @@ safte_process_slotstatus(enc_softc_t *enc, struct enc_fsm_state *state, if (buf[r+0] & 0x40) cache->elm_map[oid].encstat[0] |= SESCTL_PRDFAIL; if (cache->elm_map[oid].enctype == ELMTYP_ARRAY_DEV) { + if (buf[r+0] & 0x01) + cache->elm_map[oid].encstat[1] |= 0x80; if (buf[r+0] & 0x04) cache->elm_map[oid].encstat[1] |= 0x02; if (buf[r+0] & 0x08) @@ -767,8 +769,6 @@ 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->priv & 0x46) == 0) - ep->priv |= 0x01; /* no errors */ if (ep->enctype == ELMTYP_ARRAY_DEV) { if (req->elm_stat[1] & 0x01) ep->priv |= 0x200; @@ -782,7 +782,11 @@ safte_fill_control_request(enc_softc_t *enc, struct enc_fsm_state *state, ep->priv |= 0x20; if (req->elm_stat[1] & 0x20) ep->priv |= 0x100; + if (req->elm_stat[1] & 0x80) + ep->priv |= 0x01; } + if (ep->priv == 0) + ep->priv |= 0x01; /* no errors */ buf[0] = SAFTE_WT_DSTAT; for (i = 0; i < cfg->Nslots; i++) { From 0ccb11907cd19e1a174cca630ac0d1a38f6b8627 Mon Sep 17 00:00:00 2001 From: Alexander Motin Date: Fri, 26 Aug 2011 23:05:34 +0000 Subject: [PATCH 27/89] Report proper zero length when no descriptor available for element. --- sys/cam/scsi/scsi_enc_ses.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sys/cam/scsi/scsi_enc_ses.c b/sys/cam/scsi/scsi_enc_ses.c index 5dced23ce84..762e01ecf4b 100644 --- a/sys/cam/scsi/scsi_enc_ses.c +++ b/sys/cam/scsi/scsi_enc_ses.c @@ -2606,8 +2606,10 @@ ses_get_elm_desc(enc_softc_t *enc, encioc_elm_desc_t *elmd) /* Assume caller has already checked obj_id validity */ elmpriv = enc->enc_cache.elm_map[i].elm_private; /* object might not have a descriptor */ - if (elmpriv == NULL || elmpriv->descr == NULL) + if (elmpriv == NULL || elmpriv->descr == NULL) { + elmd->elm_desc_len = 0; return (0); + } if (elmd->elm_desc_len > elmpriv->descr_len) elmd->elm_desc_len = elmpriv->descr_len; copyout(elmpriv->descr, elmd->elm_desc_str, elmd->elm_desc_len); From ca37a38daae87f14b9095521d5513a109478b1eb Mon Sep 17 00:00:00 2001 From: Alexander Motin Date: Fri, 26 Aug 2011 23:45:27 +0000 Subject: [PATCH 28/89] Unify getencstat output. Tune type and status names to match SES specification. --- share/examples/ses/srcs/eltsub.c | 55 +++++++++++++++------------- share/examples/ses/srcs/getencstat.c | 13 +++---- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/share/examples/ses/srcs/eltsub.c b/share/examples/ses/srcs/eltsub.c index 67f49a74c95..9500423c120 100644 --- a/share/examples/ses/srcs/eltsub.c +++ b/share/examples/ses/srcs/eltsub.c @@ -53,16 +53,16 @@ geteltnm(int type) sprintf(rbuf, "Unspecified"); break; case ELMTYP_DEVICE: - sprintf(rbuf, "Device"); + sprintf(rbuf, "Device Slot"); break; case ELMTYP_POWER: - sprintf(rbuf, "Power supply"); + sprintf(rbuf, "Power Supply"); break; case ELMTYP_FAN: - sprintf(rbuf, "Cooling element"); + sprintf(rbuf, "Cooling"); break; case ELMTYP_THERM: - sprintf(rbuf, "Temperature sensors"); + sprintf(rbuf, "Temperature Sensors"); break; case ELMTYP_DOORLOCK: sprintf(rbuf, "Door Lock"); @@ -71,31 +71,31 @@ geteltnm(int type) sprintf(rbuf, "Audible alarm"); break; case ELMTYP_ESCC: - sprintf(rbuf, "Enclosure services controller electronics"); + sprintf(rbuf, "Enclosure Eervices Controller Electronics"); break; case ELMTYP_SCC: - sprintf(rbuf, "SCC controller electronics"); + sprintf(rbuf, "SCC Controller Electronics"); break; case ELMTYP_NVRAM: - sprintf(rbuf, "Nonvolatile cache"); + sprintf(rbuf, "Nonvolatile Cache"); break; case ELMTYP_INV_OP_REASON: sprintf(rbuf, "Invalid Operation Reason"); break; case ELMTYP_UPS: - sprintf(rbuf, "Uninterruptible power supply"); + sprintf(rbuf, "Uninterruptible Power Supply"); break; case ELMTYP_DISPLAY: sprintf(rbuf, "Display"); break; case ELMTYP_KEYPAD: - sprintf(rbuf, "Key pad entry device"); + sprintf(rbuf, "Key Pad Entry"); break; case ELMTYP_ENCLOSURE: sprintf(rbuf, "Enclosure"); break; case ELMTYP_SCSIXVR: - sprintf(rbuf, "SCSI port/transceiver"); + sprintf(rbuf, "SCSI Port/Transceiver"); break; case ELMTYP_LANGUAGE: sprintf(rbuf, "Language"); @@ -110,22 +110,22 @@ geteltnm(int type) sprintf(rbuf, "Current Sensor"); break; case ELMTYP_SCSI_TGT: - sprintf(rbuf, "SCSI target port"); + sprintf(rbuf, "SCSI Target Port"); break; case ELMTYP_SCSI_INI: - sprintf(rbuf, "SCSI initiator port"); + sprintf(rbuf, "SCSI Initiator Port"); break; case ELMTYP_SUBENC: - sprintf(rbuf, "Simple sub-enclosure"); + sprintf(rbuf, "Simple Subenclosure"); break; case ELMTYP_ARRAY_DEV: - sprintf(rbuf, "Array device"); + sprintf(rbuf, "Array Device Slot"); break; case ELMTYP_SAS_EXP: - sprintf(rbuf, "SAS expander"); + sprintf(rbuf, "SAS Expander"); break; case ELMTYP_SAS_CONN: - sprintf(rbuf, "SAS connector"); + sprintf(rbuf, "SAS Connector"); break; default: (void) sprintf(rbuf, "", type); @@ -140,31 +140,34 @@ scode2ascii(u_char code) static char rbuf[32]; switch (code & 0xf) { case SES_OBJSTAT_UNSUPPORTED: - sprintf(rbuf, "status not supported"); + sprintf(rbuf, "Unsupported"); break; case SES_OBJSTAT_OK: - sprintf(rbuf, "ok"); + sprintf(rbuf, "OK"); break; case SES_OBJSTAT_CRIT: - sprintf(rbuf, "critical"); + sprintf(rbuf, "Critical"); break; case SES_OBJSTAT_NONCRIT: - sprintf(rbuf, "non-critical"); + sprintf(rbuf, "Noncritical"); break; case SES_OBJSTAT_UNRECOV: - sprintf(rbuf, "unrecoverable"); + sprintf(rbuf, "Unrecoverable"); break; case SES_OBJSTAT_NOTINSTALLED: - sprintf(rbuf, "not installed"); + sprintf(rbuf, "Not Installed"); break; case SES_OBJSTAT_UNKNOWN: - sprintf(rbuf, "unknown status"); + sprintf(rbuf, "Unknown"); break; case SES_OBJSTAT_NOTAVAIL: - sprintf(rbuf, "status not available"); + sprintf(rbuf, "Not Available"); + break; + case SES_OBJSTAT_NOACCESS: + sprintf(rbuf, "No Access Allowed"); break; default: - sprintf(rbuf, "unknown status code %x", code & 0xf); + sprintf(rbuf, "", code & 0xf); break; } return (rbuf); @@ -177,7 +180,7 @@ stat2ascii(int eletype __unused, u_char *cstat) static char ebuf[256], *scode; scode = scode2ascii(cstat[0]); - sprintf(ebuf, "Status=%s (bytes=0x%02x 0x%02x 0x%02x 0x%02x)", + sprintf(ebuf, "status: %s (0x%02x 0x%02x 0x%02x 0x%02x)", scode, cstat[0], cstat[1], cstat[2], cstat[3]); return (ebuf); } diff --git a/share/examples/ses/srcs/getencstat.c b/share/examples/ses/srcs/getencstat.c index 1a70eb16360..9048f73553a 100644 --- a/share/examples/ses/srcs/getencstat.c +++ b/share/examples/ses/srcs/getencstat.c @@ -166,14 +166,11 @@ main(int a, char **v) (void)ioctl(fd, ENCIOC_GETELMDEVNAMES, (caddr_t)&objdn); fprintf(stdout, "Element 0x%x: %s", ob.elm_idx, geteltnm(objp[i].elm_type)); - if ((ob.cstat[0] & 0xf) == SES_OBJSTAT_OK) - fprintf(stdout, ", OK (%s)", - stat2ascii(objp[i].elm_type, ob.cstat)); - else - fprintf(stdout, ", %s", - stat2ascii(objp[i].elm_type, ob.cstat)); - fprintf(stdout, ", descriptor: '%s'", - objd.elm_desc_str); + fprintf(stdout, ", %s", + stat2ascii(objp[i].elm_type, ob.cstat)); + if (objd.elm_desc_len > 0) + fprintf(stdout, ", descriptor: '%s'", + objd.elm_desc_str); if (objdn.elm_names_len > 0) fprintf(stdout, ", dev: '%s'", objdn.elm_devnames); From 7a4a2d36af7631a15796a2a06dcfeaaabf752a06 Mon Sep 17 00:00:00 2001 From: Alexander Motin Date: Sat, 27 Aug 2011 12:10:12 +0000 Subject: [PATCH 29/89] Fix/Implement I/O errors handling: - make done() method called even after I/O errors to handle them, - pass error argument to done() method, - return error code to user-level. --- sys/cam/scsi/scsi_enc.c | 38 +++++++++++++++----------------- sys/cam/scsi/scsi_enc_internal.h | 2 +- sys/cam/scsi/scsi_enc_safte.c | 28 ++++++++++++++--------- sys/cam/scsi/scsi_enc_ses.c | 38 ++++++++++++++++++++++++-------- 4 files changed, 66 insertions(+), 40 deletions(-) diff --git a/sys/cam/scsi/scsi_enc.c b/sys/cam/scsi/scsi_enc.c index 9e70fa0aead..34c353b5ce7 100644 --- a/sys/cam/scsi/scsi_enc.c +++ b/sys/cam/scsi/scsi_enc.c @@ -739,6 +739,7 @@ enc_fsm_step(enc_softc_t *enc) uint8_t *buf; struct enc_fsm_state *cur_state; int error; + uint32_t xfer_len; ENC_DLOG(enc, "%s enter %p\n", __func__, enc); @@ -760,30 +761,27 @@ enc_fsm_step(enc_softc_t *enc) ccb = cam_periph_getccb(enc->periph, CAM_PRIORITY_NORMAL); error = cur_state->fill(enc, cur_state, ccb, buf); - if (error == 0) { - error = cam_periph_runccb(ccb, cur_state->error, - ENC_CFLAGS, - ENC_FLAGS|SF_QUIET_IR, NULL); - } + if (error != 0) + goto done; + + error = cam_periph_runccb(ccb, cur_state->error, + ENC_CFLAGS, + ENC_FLAGS|SF_QUIET_IR, NULL); } - - if (error == 0) { - uint32_t len; + if (ccb != NULL) { + if (ccb->ccb_h.func_code == XPT_ATA_IO) + xfer_len = ccb->ataio.dxfer_len - ccb->ataio.resid; + else + xfer_len = ccb->csio.dxfer_len - ccb->csio.resid; + } else + xfer_len = 0; - len = 0; - if (ccb != NULL) { - if (ccb->ccb_h.func_code == XPT_ATA_IO) - len = ccb->ataio.dxfer_len - ccb->ataio.resid; - else - len = ccb->csio.dxfer_len - ccb->csio.resid; - } - - cam_periph_unlock(enc->periph); - cur_state->done(enc, cur_state, ccb, &buf, len); - cam_periph_lock(enc->periph); - } + cam_periph_unlock(enc->periph); + cur_state->done(enc, cur_state, ccb, &buf, error, xfer_len); + cam_periph_lock(enc->periph); +done: ENC_DLOG(enc, "%s exit - result %d\n", __func__, error); ENC_FREE_AND_NULL(buf); if (ccb != NULL) diff --git a/sys/cam/scsi/scsi_enc_internal.h b/sys/cam/scsi/scsi_enc_internal.h index 5be5e9bd067..764d007552f 100644 --- a/sys/cam/scsi/scsi_enc_internal.h +++ b/sys/cam/scsi/scsi_enc_internal.h @@ -72,7 +72,7 @@ typedef int fsm_error_handler_t(union ccb *ccb, uint32_t cflags, uint32_t sflags); typedef int fsm_done_handler_t(enc_softc_t *ssc, struct enc_fsm_state *state, union ccb *ccb, - uint8_t **bufp, int xfer_len); + uint8_t **bufp, int error, int xfer_len); struct enc_fsm_state { const char *name; diff --git a/sys/cam/scsi/scsi_enc_safte.c b/sys/cam/scsi/scsi_enc_safte.c index 6f796346e68..1ab6fac2a19 100644 --- a/sys/cam/scsi/scsi_enc_safte.c +++ b/sys/cam/scsi/scsi_enc_safte.c @@ -247,7 +247,7 @@ safte_fill_read_buf_io(enc_softc_t *enc, struct enc_fsm_state *state, static int safte_process_config(enc_softc_t *enc, struct enc_fsm_state *state, - union ccb *ccb, uint8_t **bufp, int xfer_len) + union ccb *ccb, uint8_t **bufp, int error, int xfer_len) { struct scfg *cfg; uint8_t *buf = *bufp; @@ -256,7 +256,8 @@ safte_process_config(enc_softc_t *enc, struct enc_fsm_state *state, cfg = enc->enc_private; if (cfg == NULL) return (ENXIO); - + if (error != 0) + return (error); if (xfer_len < 6) { ENC_LOG(enc, "too little data (%d) for configuration\n", xfer_len); @@ -320,7 +321,7 @@ safte_process_config(enc_softc_t *enc, struct enc_fsm_state *state, static int safte_process_gflags(enc_softc_t *enc, struct enc_fsm_state *state, - union ccb *ccb, uint8_t **bufp, int xfer_len) + union ccb *ccb, uint8_t **bufp, int error, int xfer_len) { struct scfg *cfg; uint8_t *buf = *bufp; @@ -328,7 +329,8 @@ safte_process_gflags(enc_softc_t *enc, struct enc_fsm_state *state, cfg = enc->enc_private; if (cfg == NULL) return (ENXIO); - + if (error != 0) + return (error); SAFT_BAIL(3, xfer_len); cfg->flag1 = buf[1]; cfg->flag2 = buf[2]; @@ -344,7 +346,7 @@ safte_process_gflags(enc_softc_t *enc, struct enc_fsm_state *state, static int safte_process_status(enc_softc_t *enc, struct enc_fsm_state *state, - union ccb *ccb, uint8_t **bufp, int xfer_len) + union ccb *ccb, uint8_t **bufp, int error, int xfer_len) { struct scfg *cfg; uint8_t *buf = *bufp; @@ -355,6 +357,8 @@ safte_process_status(enc_softc_t *enc, struct enc_fsm_state *state, cfg = enc->enc_private; if (cfg == NULL) return (ENXIO); + if (error != 0) + return (error); oid = r = 0; cfg->enc_status = 0; @@ -651,7 +655,7 @@ safte_process_status(enc_softc_t *enc, struct enc_fsm_state *state, static int safte_process_slotstatus(enc_softc_t *enc, struct enc_fsm_state *state, - union ccb *ccb, uint8_t **bufp, int xfer_len) + union ccb *ccb, uint8_t **bufp, int error, int xfer_len) { struct scfg *cfg; uint8_t *buf = *bufp; @@ -661,7 +665,8 @@ safte_process_slotstatus(enc_softc_t *enc, struct enc_fsm_state *state, cfg = enc->enc_private; if (cfg == NULL) return (ENXIO); - + if (error != 0) + return (error); cfg->slot_status = 0; oid = cfg->slotoff; for (r = i = 0; i < cfg->Nslots; i++, r += 4) { @@ -940,7 +945,7 @@ safte_fill_control_request(enc_softc_t *enc, struct enc_fsm_state *state, static int safte_process_control_request(enc_softc_t *enc, struct enc_fsm_state *state, - union ccb *ccb, uint8_t **bufp, int xfer_len) + union ccb *ccb, uint8_t **bufp, int error, int xfer_len) { struct scfg *cfg; safte_control_request_t *req; @@ -950,8 +955,10 @@ safte_process_control_request(enc_softc_t *enc, struct enc_fsm_state *state, if (cfg == NULL) return (ENXIO); + req = cfg->current_request; + if (req->result == 0) + req->result = error; if (++cfg->current_request_stage >= cfg->current_request_stages) { - req = cfg->current_request; idx = req->elm_idx; if (idx == SES_SETSTATUS_ENC_IDX) type = -1; @@ -962,7 +969,6 @@ safte_process_control_request(enc_softc_t *enc, struct enc_fsm_state *state, else enc_update_request(enc, SAFTE_UPDATE_READENCSTATUS); cfg->current_request = NULL; - req->result = 0; wakeup(req); } else { enc_update_request(enc, SAFTE_PROCESS_CONTROL_REQS); @@ -1046,6 +1052,7 @@ safte_set_enc_status(enc_softc_t *enc, uint8_t encstat, int slpflag) req.elm_idx = SES_SETSTATUS_ENC_IDX; req.elm_stat[0] = encstat & 0xf; + req.result = 0; TAILQ_INSERT_TAIL(&cfg->requests, &req, links); enc_update_request(enc, SAFTE_PROCESS_CONTROL_REQS); @@ -1082,6 +1089,7 @@ safte_set_elm_status(enc_softc_t *enc, encioc_elm_status_t *elms, int slpflag) req.elm_idx = elms->elm_idx; memcpy(&req.elm_stat, elms->cstat, sizeof(req.elm_stat)); + req.result = 0; TAILQ_INSERT_TAIL(&cfg->requests, &req, links); enc_update_request(enc, SAFTE_PROCESS_CONTROL_REQS); diff --git a/sys/cam/scsi/scsi_enc_ses.c b/sys/cam/scsi/scsi_enc_ses.c index 762e01ecf4b..5f94cb4f1f2 100644 --- a/sys/cam/scsi/scsi_enc_ses.c +++ b/sys/cam/scsi/scsi_enc_ses.c @@ -1234,7 +1234,7 @@ out: */ static int ses_process_pages(enc_softc_t *enc, struct enc_fsm_state *state, - union ccb *ccb, uint8_t **bufp, int xfer_len) + union ccb *ccb, uint8_t **bufp, int error, int xfer_len) { ses_softc_t *ses; struct scsi_diag_page *page; @@ -1245,6 +1245,10 @@ ses_process_pages(enc_softc_t *enc, struct enc_fsm_state *state, ses = enc->enc_private; err = -1; + if (error != 0) { + err = error; + goto out; + } if (xfer_len < sizeof(*page)) { ENC_LOG(enc, "Unable to parse Diag Pages List Header\n"); err = EIO; @@ -1283,7 +1287,7 @@ out: */ static int ses_process_config(enc_softc_t *enc, struct enc_fsm_state *state, - union ccb *ccb, uint8_t **bufp, int xfer_len) + union ccb *ccb, uint8_t **bufp, int error, int xfer_len) { struct ses_iterator iter; ses_softc_t *ses; @@ -1315,6 +1319,10 @@ ses_process_config(enc_softc_t *enc, struct enc_fsm_state *state, buf = *bufp; err = -1;; + if (error != 0) { + err = error; + goto out; + } if (xfer_len < sizeof(cfg_page->hdr)) { ENC_LOG(enc, "Unable to parse SES Config Header\n"); err = EIO; @@ -1492,7 +1500,7 @@ out: */ static int ses_process_status(enc_softc_t *enc, struct enc_fsm_state *state, - union ccb *ccb, uint8_t **bufp, int xfer_len) + union ccb *ccb, uint8_t **bufp, int error, int xfer_len) { struct ses_iterator iter; enc_element_t *element; @@ -1515,6 +1523,10 @@ ses_process_status(enc_softc_t *enc, struct enc_fsm_state *state, page = (struct ses_status_page *)buf; length = ses_page_length(&page->hdr); + if (error != 0) { + err = error; + goto out; + } /* * Make sure the length fits in the buffer. * @@ -1660,7 +1672,7 @@ static int ses_get_elm_addlstatus_sas(enc_softc_t *, enc_cache_t *, uint8_t *, */ static int ses_process_elm_addlstatus(enc_softc_t *enc, struct enc_fsm_state *state, - union ccb *ccb, uint8_t **bufp, int xfer_len) + union ccb *ccb, uint8_t **bufp, int error, int xfer_len) { struct ses_iterator iter; int eip; @@ -1679,6 +1691,10 @@ ses_process_elm_addlstatus(enc_softc_t *enc, struct enc_fsm_state *state, buf = *bufp; err = -1; + if (error != 0) { + err = error; + goto out; + } ses_cache_free_elm_addlstatus(enc, enc_cache); ses_cache->elm_addlstatus_page = (struct ses_addl_elem_status_page *)buf; @@ -1813,7 +1829,7 @@ out: static int ses_process_control_request(enc_softc_t *enc, struct enc_fsm_state *state, - union ccb *ccb, uint8_t **bufp, int xfer_len) + union ccb *ccb, uint8_t **bufp, int error, int xfer_len) { ses_softc_t *ses; @@ -1823,14 +1839,14 @@ ses_process_control_request(enc_softc_t *enc, struct enc_fsm_state *state, * o Generation count wrong. * o Some SCSI status error. */ - ses_terminate_control_requests(&ses->ses_pending_requests, 0); + ses_terminate_control_requests(&ses->ses_pending_requests, error); enc_update_request(enc, SES_UPDATE_GETSTATUS); return (0); } static int ses_publish_physpaths(enc_softc_t *enc, struct enc_fsm_state *state, - union ccb *ccb, uint8_t **bufp, int xfer_len) + union ccb *ccb, uint8_t **bufp, int error, int xfer_len) { struct ses_iterator iter; enc_cache_t *enc_cache; @@ -1857,7 +1873,7 @@ ses_publish_physpaths(enc_softc_t *enc, struct enc_fsm_state *state, static int ses_publish_cache(enc_softc_t *enc, struct enc_fsm_state *state, - union ccb *ccb, uint8_t **bufp, int xfer_len) + union ccb *ccb, uint8_t **bufp, int error, int xfer_len) { sx_xlock(&enc->enc_cache_lock); @@ -1879,7 +1895,7 @@ ses_publish_cache(enc_softc_t *enc, struct enc_fsm_state *state, */ static int ses_process_elm_descs(enc_softc_t *enc, struct enc_fsm_state *state, - union ccb *ccb, uint8_t **bufp, int xfer_len) + union ccb *ccb, uint8_t **bufp, int error, int xfer_len) { ses_softc_t *ses; struct ses_iterator iter; @@ -1900,6 +1916,10 @@ ses_process_elm_descs(enc_softc_t *enc, struct enc_fsm_state *state, buf = *bufp; err = -1; + if (error != 0) { + err = error; + goto out; + } ses_cache_free_elm_descs(enc, enc_cache); ses_cache->elm_descs_page = (struct ses_elem_descr_page *)buf; *bufp = NULL; From d3ee0893518def30def56bdafdd5d7123d21526f Mon Sep 17 00:00:00 2001 From: Alexander Motin Date: Sat, 27 Aug 2011 13:05:38 +0000 Subject: [PATCH 30/89] Drop wrbuf16() function, not used any more. --- sys/cam/scsi/scsi_enc_safte.c | 42 ++++++++--------------------------- 1 file changed, 9 insertions(+), 33 deletions(-) diff --git a/sys/cam/scsi/scsi_enc_safte.c b/sys/cam/scsi/scsi_enc_safte.c index 1ab6fac2a19..dcb3f419d62 100644 --- a/sys/cam/scsi/scsi_enc_safte.c +++ b/sys/cam/scsi/scsi_enc_safte.c @@ -54,7 +54,7 @@ __FBSDID("$FreeBSD: head/sys/cam/scsi/scsi_ses.c 201758 2010-01-07 21:01:37Z mbr * SAF-TE Type Device Emulation */ -static int wrbuf16(enc_softc_t *, uint8_t, uint8_t, uint8_t, uint8_t, int); +static int safte_set_enc_status(enc_softc_t *enc, uint8_t encstat, int slpflag); #define ALL_ENC_STAT (SES_ENCSTAT_CRITICAL | SES_ENCSTAT_UNRECOV | \ SES_ENCSTAT_NONCRITICAL | SES_ENCSTAT_INFO) @@ -976,37 +976,6 @@ safte_process_control_request(enc_softc_t *enc, struct enc_fsm_state *state, return (0); } -/* - * This function handles all of the 16 byte WRITE BUFFER commands. - */ -static int -wrbuf16(enc_softc_t *enc, uint8_t op, uint8_t b1, uint8_t b2, - uint8_t b3, int slp) -{ - int err, amt; - char *sdata; - struct scfg *cc = enc->enc_private; - static char cdb[10] = { WRITE_BUFFER, 1, 0, 0, 0, 0, 0, 0, 16, 0 }; - - if (cc == NULL) - return (0); - - sdata = ENC_MALLOCZ(16); - if (sdata == NULL) - return (ENOMEM); - - ENC_DLOG(enc, "saf_wrbuf16 %x %x %x %x\n", op, b1, b2, b3); - - sdata[0] = op; - sdata[1] = b1; - sdata[2] = b2; - sdata[3] = b3; - amt = -16; - err = enc_runcmd(enc, cdb, 10, sdata, &amt); - ENC_FREE(sdata); - return (err); -} - static void safte_softc_cleanup(struct cam_periph *periph) { @@ -1021,15 +990,22 @@ safte_softc_cleanup(struct cam_periph *periph) static int safte_init_enc(enc_softc_t *enc) { + struct scfg *cfg; int err; static char cdb0[6] = { SEND_DIAGNOSTIC }; + cfg = enc->enc_private; + if (cfg == NULL) + return (ENXIO); + err = enc_runcmd(enc, cdb0, 6, NULL, 0); if (err) { return (err); } DELAY(5000); - err = wrbuf16(enc, SAFTE_WT_GLOBAL, 0, 0, 0, 1); + cfg->flag1 = 0; + cfg->flag2 = 0; + err = safte_set_enc_status(enc, 0, 1); return (err); } From 784b65adb4634c389850914aadda4354496d93ea Mon Sep 17 00:00:00 2001 From: Alexander Motin Date: Sat, 27 Aug 2011 21:23:21 +0000 Subject: [PATCH 31/89] Tune destruction sequence, including aborting queued control requests. Cover for some edge cases is still ongoing. --- sys/cam/scsi/scsi_enc.c | 12 ++++++------ sys/cam/scsi/scsi_enc_internal.h | 6 +++--- sys/cam/scsi/scsi_enc_safte.c | 33 +++++++++++++++++++++++--------- sys/cam/scsi/scsi_enc_ses.c | 20 ++++++++----------- 4 files changed, 41 insertions(+), 30 deletions(-) diff --git a/sys/cam/scsi/scsi_enc.c b/sys/cam/scsi/scsi_enc.c index 34c353b5ce7..40fbfc66f3e 100644 --- a/sys/cam/scsi/scsi_enc.c +++ b/sys/cam/scsi/scsi_enc.c @@ -120,9 +120,11 @@ enc_oninvalidate(struct cam_periph *periph) enc = periph->softc; + enc->enc_flags |= ENC_FLAG_INVALID; + /* If the sub-driver has an invalidate routine, call it */ if (enc->enc_vec.softc_invalidate != NULL) - enc->enc_vec.softc_invalidate(periph); + enc->enc_vec.softc_invalidate(enc); /* * Unregister any async callbacks. @@ -146,8 +148,6 @@ enc_oninvalidate(struct cam_periph *periph) } callout_drain(&enc->status_updater); - enc->enc_flags |= ENC_FLAG_INVALID; - xpt_print(periph->path, "lost device\n"); } @@ -165,7 +165,7 @@ enc_dtor(struct cam_periph *periph) /* If the sub-driver has a cleanup routine, call it */ if (enc->enc_vec.softc_cleanup != NULL) - enc->enc_vec.softc_cleanup(periph); + enc->enc_vec.softc_cleanup(enc); if (enc->enc_boot_hold_ch.ich_func != NULL) { config_intrhook_disestablish(&enc->enc_boot_hold_ch); @@ -916,11 +916,11 @@ enc_ctor(struct cam_periph *periph, void *arg) case ENC_SES_SCSI2: case ENC_SES_PASSTHROUGH: case ENC_SEMB_SES: - err = ses_softc_init(enc, 1); + err = ses_softc_init(enc); break; case ENC_SAFT: case ENC_SEMB_SAFT: - err = safte_softc_init(enc, 1); + err = safte_softc_init(enc); break; case ENC_SEN: case ENC_NONE: diff --git a/sys/cam/scsi/scsi_enc_internal.h b/sys/cam/scsi/scsi_enc_internal.h index 764d007552f..e14ca7fa0b4 100644 --- a/sys/cam/scsi/scsi_enc_internal.h +++ b/sys/cam/scsi/scsi_enc_internal.h @@ -84,9 +84,9 @@ struct enc_fsm_state { fsm_error_handler_t *error; }; -typedef int (enc_softc_init_t)(enc_softc_t *, int); -typedef void (enc_softc_invalidate_t)(struct cam_periph *); -typedef void (enc_softc_cleanup_t)(struct cam_periph *); +typedef int (enc_softc_init_t)(enc_softc_t *); +typedef void (enc_softc_invalidate_t)(enc_softc_t *); +typedef void (enc_softc_cleanup_t)(enc_softc_t *); typedef int (enc_init_enc_t)(enc_softc_t *); typedef int (enc_get_enc_status_t)(enc_softc_t *, int); typedef int (enc_set_enc_status_t)(enc_softc_t *, encioc_enc_status_t, int); diff --git a/sys/cam/scsi/scsi_enc_safte.c b/sys/cam/scsi/scsi_enc_safte.c index dcb3f419d62..e4a953f1f73 100644 --- a/sys/cam/scsi/scsi_enc_safte.c +++ b/sys/cam/scsi/scsi_enc_safte.c @@ -162,6 +162,18 @@ enum { SES_SETSTATUS_ENC_IDX = -1 }; +static void +safte_terminate_control_requests(safte_control_reqlist_t *reqlist, int result) +{ + safte_control_request_t *req; + + while ((req = TAILQ_FIRST(reqlist)) != NULL) { + TAILQ_REMOVE(reqlist, req, links); + req->result = result; + wakeup(req); + } +} + struct scfg { /* * Cached Configuration @@ -977,11 +989,18 @@ safte_process_control_request(enc_softc_t *enc, struct enc_fsm_state *state, } static void -safte_softc_cleanup(struct cam_periph *periph) +safte_softc_invalidate(enc_softc_t *enc) +{ + struct scfg *cfg; + + cfg = enc->enc_private; + safte_terminate_control_requests(&cfg->requests, ENXIO); +} + +static void +safte_softc_cleanup(enc_softc_t *enc) { - enc_softc_t *enc; - enc = periph->softc; ENC_FREE_AND_NULL(enc->enc_cache.elm_map); ENC_FREE_AND_NULL(enc->enc_private); enc->enc_cache.nelms = 0; @@ -1084,6 +1103,7 @@ safte_poll_status(enc_softc_t *enc) static struct enc_vec safte_enc_vec = { + .softc_invalidate = safte_softc_invalidate, .softc_cleanup = safte_softc_cleanup, .init_enc = safte_init_enc, .get_enc_status = safte_get_enc_status, @@ -1094,15 +1114,10 @@ static struct enc_vec safte_enc_vec = }; int -safte_softc_init(enc_softc_t *enc, int doinit) +safte_softc_init(enc_softc_t *enc) { struct scfg *cfg; - if (doinit == 0) { - safte_softc_cleanup(enc->periph); - return (0); - } - enc->enc_vec = safte_enc_vec; enc->enc_fsm_states = enc_fsm_states; diff --git a/sys/cam/scsi/scsi_enc_ses.c b/sys/cam/scsi/scsi_enc_ses.c index 5f94cb4f1f2..3717c1099af 100644 --- a/sys/cam/scsi/scsi_enc_ses.c +++ b/sys/cam/scsi/scsi_enc_ses.c @@ -1477,7 +1477,7 @@ ses_process_config(enc_softc_t *enc, struct enc_fsm_state *state, out: if (err) - ses_softc_cleanup(enc->periph); + ses_softc_cleanup(enc); else { enc_update_request(enc, SES_UPDATE_GETSTATUS); enc_update_request(enc, SES_UPDATE_GETELMDESCS); @@ -2538,18 +2538,18 @@ out: } static void -ses_softc_invalidate(struct cam_periph *periph) +ses_softc_invalidate(enc_softc_t *enc) { + ses_softc_t *ses; + + ses = enc->enc_private; + ses_terminate_control_requests(&ses->ses_requests, ENXIO); } static void -ses_softc_cleanup(struct cam_periph *periph) +ses_softc_cleanup(enc_softc_t *enc) { - enc_softc_t *enc; - CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, - ("entering ses_softc_cleanup(%p)\n", periph)); - enc = periph->softc; ses_cache_free(enc, &enc->enc_cache); ses_cache_free(enc, &enc->enc_daemon_cache); ENC_FREE_AND_NULL(enc->enc_private); @@ -2767,16 +2767,12 @@ static struct enc_vec ses_enc_vec = * \return 0 on success, errno otherwise. */ int -ses_softc_init(enc_softc_t *enc, int doinit) +ses_softc_init(enc_softc_t *enc) { ses_softc_t *ses_softc; CAM_DEBUG(enc->periph->path, CAM_DEBUG_SUBTRACE, ("entering enc_softc_init(%p)\n", enc)); - if (doinit == 0) { - ses_softc_cleanup(enc->periph); - return (0); - } enc->enc_vec = ses_enc_vec; enc->enc_fsm_states = enc_fsm_states; From c3d28ef378b4b6cf5e4cad77d448bc092e4ca42c Mon Sep 17 00:00:00 2001 From: Xin LI Date: Wed, 7 Sep 2011 00:16:36 +0000 Subject: [PATCH 32/89] Use %s as format string when calling syslog(). --- cddl/sbin/zfsd/dev_ctl_event.cc | 4 ++-- cddl/sbin/zfsd/zfsd_exception.cc | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cddl/sbin/zfsd/dev_ctl_event.cc b/cddl/sbin/zfsd/dev_ctl_event.cc index c0762e1e9ba..7821ae2e391 100644 --- a/cddl/sbin/zfsd/dev_ctl_event.cc +++ b/cddl/sbin/zfsd/dev_ctl_event.cc @@ -101,7 +101,7 @@ ParseException::Log(const string &parsedBuffer) const if (Type() == DISCARDED_EVENT_TYPE) priority = LOG_INFO; - syslog(priority, ToString(parsedBuffer).c_str()); + syslog(priority, "%s", ToString(parsedBuffer).c_str()); } /*-------------------------------- DevCtlEvent -------------------------------*/ @@ -236,7 +236,7 @@ DevCtlEvent::Print() const void DevCtlEvent::Log(int priority) const { - syslog(priority, ToString().c_str()); + syslog(priority, "%s", ToString().c_str()); } //- DevCtlEvent Virtual Public Methods ----------------------------------------- diff --git a/cddl/sbin/zfsd/zfsd_exception.cc b/cddl/sbin/zfsd/zfsd_exception.cc index f87e6758042..a2fea23e690 100644 --- a/cddl/sbin/zfsd/zfsd_exception.cc +++ b/cddl/sbin/zfsd/zfsd_exception.cc @@ -152,6 +152,6 @@ ZfsdException::Log() const } output << m_log << endl; - syslog(LOG_ERR, output.str().c_str()); + syslog(LOG_ERR, "%s", output.str().c_str()); } From 9e831e55822793648a56f4f5813828a8e22b86aa Mon Sep 17 00:00:00 2001 From: Alexander Motin Date: Sat, 1 Oct 2011 15:10:33 +0000 Subject: [PATCH 33/89] - Do not report error if index found in next Additional Status descriptor is less then expected. Theoretically it may happen if Additional Status will be used for some new type in later spec. - Rephrase some error messages to make them a bit more informative. --- sys/cam/scsi/scsi_enc_ses.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/sys/cam/scsi/scsi_enc_ses.c b/sys/cam/scsi/scsi_enc_ses.c index 3717c1099af..11875819634 100644 --- a/sys/cam/scsi/scsi_enc_ses.c +++ b/sys/cam/scsi/scsi_enc_ses.c @@ -1757,7 +1757,7 @@ ses_process_elm_addlstatus(enc_softc_t *enc, struct enc_fsm_state *state, eip_hdr->element_index, SES_ELEM_INDEX_INDIVIDUAL); - if (iter.individual_element_index != expected_index + if (iter.individual_element_index > expected_index && status_type == TYPE_ADDLSTATUS_MANDATORY) { ENC_LOG(enc, "%s: provided element " "index %d skips mandatory status " @@ -2500,8 +2500,8 @@ ses_get_elm_addlstatus_sas(enc_softc_t *enc, enc_cache_t *enc_cache, case ELMTYP_ARRAY_DEV: break; default: - ENC_LOG(enc, "Element %d Additional Status Invalid " - "for SAS device type 0: SES Typ 0x%x\n", nobj, + ENC_LOG(enc, "Element %d has Additional Status type 0, " + "invalid for SES element type 0x%x\n", nobj, ses_cache->ses_types[tidx].hdr->etype_elm_type); err = ENODEV; goto out; @@ -2518,8 +2518,9 @@ ses_get_elm_addlstatus_sas(enc_softc_t *enc, enc_cache_t *enc_cache, case ELMTYP_ESCC: break; default: - ENC_LOG(enc, "Element %d Additional Status Invalid " - "for SAS device type 1\n", nobj); + ENC_LOG(enc, "Element %d has Additional Status type 1, " + "invalid for SES element type 0x%x\n", nobj, + ses_cache->ses_types[tidx].hdr->etype_elm_type); err = ENODEV; goto out; } @@ -2527,8 +2528,9 @@ ses_get_elm_addlstatus_sas(enc_softc_t *enc, enc_cache_t *enc_cache, bufsiz, eip, nobj); break; default: - ENC_LOG(enc, "Element %d Additional Status Invalid Type %d for" - " SAS object\n", dtype, nobj); + ENC_LOG(enc, "Element %d of type 0x%x has Additional Status " + "of unknown type 0x%x\n", nobj, + ses_cache->ses_types[tidx].hdr->etype_elm_type, dtype); err = ENODEV; break; } From 320256b84995912ef863a8338b065dfc66924d76 Mon Sep 17 00:00:00 2001 From: Xin LI Date: Thu, 17 Nov 2011 08:57:39 +0000 Subject: [PATCH 34/89] Install zfsd rc.d script. --- etc/rc.d/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/rc.d/Makefile b/etc/rc.d/Makefile index fc09cafc4bb..486bb10d290 100644 --- a/etc/rc.d/Makefile +++ b/etc/rc.d/Makefile @@ -40,7 +40,7 @@ FILES= DAEMON FILESYSTEMS LOGIN NETWORKING SERVERS \ watchdogd wpa_supplicant \ ypbind yppasswdd ypserv \ ypset ypupdated ypxfrd \ - zfs zvol + zfs zfsd zvol .if ${MK_IPX} != "no" FILES+= ipxrouted From 682c489897316b162cfc1bb23f379688fd422563 Mon Sep 17 00:00:00 2001 From: Xin LI Date: Tue, 29 Nov 2011 19:47:11 +0000 Subject: [PATCH 35/89] Handle ZfsdException while parsing the pool and give more meaningful log instead of stopping and start over in the main loop. --- cddl/sbin/zfsd/dev_ctl_event.cc | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/cddl/sbin/zfsd/dev_ctl_event.cc b/cddl/sbin/zfsd/dev_ctl_event.cc index 7821ae2e391..be7dbba15a3 100644 --- a/cddl/sbin/zfsd/dev_ctl_event.cc +++ b/cddl/sbin/zfsd/dev_ctl_event.cc @@ -455,9 +455,18 @@ DevfsEvent::ReadLabel(int devFd, bool &inUse, bool °raded) || devLabel == NULL) return (NULL); - Vdev vdev(devLabel); - degraded = vdev.State() != VDEV_STATE_HEALTHY; - return (devLabel); + try { + Vdev vdev(devLabel); + degraded = vdev.State() != VDEV_STATE_HEALTHY; + return (devLabel); + } catch (ZfsdException &exp) { + string devName = fdevname(devFd); + string devPath = _PATH_DEV + devName; + string context("DevfsEvent::ReadLabel: " + devPath + ": "); + + exp.GetString().insert(0, context); + exp.Log(); + } } return (NULL); } From d8d0f8a6180740b7b530a1fcba089ef5bcaa2555 Mon Sep 17 00:00:00 2001 From: "Justin T. Gibbs" Date: Fri, 2 Mar 2012 21:38:06 +0000 Subject: [PATCH 36/89] Modify ZFSD to tolerate events of arbitrary size instead of assuming that events will be no larger than 1024 bytes. This corrects an infinite loop when events larger than this size are received (e.g. when a block checksum fails). cddl/sbin/zfsd/zfsd.h: Bump the event limit up to 8K and add/rename some constants to clarify exactly what ZFSD's limits control. cddl/sbin/zfsd/zfsd.h: cddl/sbin/zfsd/zfsd.cc: o Add EventBuffer::s_keyPairSepTokens[] to make explicit the characters that can separate devctl event key=value pairs. o In EventBuffer::ExtractEvent(), truncate events to the end of the last fully received key=value pair if they are longer than the event size limit imposed by ZFSD. o Prefer syslog to warn(x) for warnings generated after becoming a daemon. --- cddl/sbin/zfsd/zfsd.cc | 39 ++++++++++++++++++++++++++++++++------- cddl/sbin/zfsd/zfsd.h | 23 ++++++++++++++++------- 2 files changed, 48 insertions(+), 14 deletions(-) diff --git a/cddl/sbin/zfsd/zfsd.cc b/cddl/sbin/zfsd/zfsd.cc index 40156d9d968..326932c1176 100644 --- a/cddl/sbin/zfsd/zfsd.cc +++ b/cddl/sbin/zfsd/zfsd.cc @@ -98,6 +98,11 @@ const char EventBuffer::s_eventStartTokens[] = "!?+-"; */ const char EventBuffer::s_eventEndTokens[] = "\n"; +/** + * Key=Value pairs are terminated by whitespace. + */ +const char EventBuffer::s_keyPairSepTokens[] = " \t\n"; + //- EventBuffer Public Methods ------------------------------------------------- EventBuffer::EventBuffer(int fd) : m_fd(fd), @@ -126,7 +131,8 @@ EventBuffer::ExtractEvent(string &eventString) size_t startLen(strcspn(nextEvent, s_eventStartTokens)); bool aligned(startLen == 0); if (aligned == false) { - warnx("Re-synchronizing with devd event stream"); + syslog(LOG_WARNING, + "Re-synchronizing with devd event stream"); m_nextEventOffset += startLen; m_parsedLen = m_nextEventOffset; continue; @@ -136,26 +142,45 @@ EventBuffer::ExtractEvent(string &eventString) * Start tokens may be end tokens too, so skip the start * token when trying to find the end of the event. */ + bool truncated(true); size_t eventLen(strcspn(nextEvent + 1, s_eventEndTokens) + 1); if (nextEvent[eventLen] == '\0') { - /* Ran out of buffer before hitting a full event. */ - m_parsedLen += eventLen; - continue; - } - if (nextEvent[eventLen] != '\n') { - warnx("Improperly terminated event encountered"); + m_parsedLen += eventLen; + if (m_parsedLen < MAX_EVENT_SIZE) { + /* + * Ran out of buffer before hitting + * a full event. Fill() and try again. + */ + continue; + } + syslog(LOG_WARNING, + "Event exceeds event size limit of %d bytes."); + } else if (nextEvent[eventLen] != '\n') { + syslog(LOG_WARNING, + "Improperly terminated event encountered."); } else { /* * Include the normal terminator in the extracted * event data. */ eventLen += 1; + truncated = false; } m_nextEventOffset += eventLen; m_parsedLen = m_nextEventOffset; eventString.assign(nextEvent, eventLen); + + if (truncated) { + size_t fieldEnd; + + fieldEnd = eventString.find_last_of(s_keyPairSepTokens); + eventString.erase(fieldEnd); + syslog(LOG_WARNING, + "Truncated %d characters from event.", + eventLen - fieldEnd); + } return (true); } return (false); diff --git a/cddl/sbin/zfsd/zfsd.h b/cddl/sbin/zfsd/zfsd.h index 7834283d1ad..391aa146119 100644 --- a/cddl/sbin/zfsd/zfsd.h +++ b/cddl/sbin/zfsd/zfsd.h @@ -119,21 +119,27 @@ private: */ MIN_EVENT_SIZE = 2, + /* + * The maximum event size supported by ZFSD. + * Events larger than this size (minus 1) are + * truncated at the end of the last fully received + * key/value pair. + */ + MAX_EVENT_SIZE = 8192, + /** * The maximum amount of buffer data to read at * a single time from the Devd file descriptor. - * This size matches the largest event size allowed - * in the system. */ - MAX_READ_SIZE = 1024, + MAX_READ_SIZE = MAX_EVENT_SIZE, /** * The size of EventBuffer's buffer of Devd event data. - * This is one larger than the maximum event size which - * alows us to always include a terminating NUL without - * overwriting any received data. + * This is one larger than the maximum supported event + * size, which alows us to always include a terminating + * NUL without overwriting any received data. */ - EVENT_BUFSIZE = MAX_READ_SIZE + /*NUL*/1 + EVENT_BUFSIZE = MAX_EVENT_SIZE + /*NUL*/1 }; /** The amount of data in m_buf we have yet to look at. */ @@ -151,6 +157,9 @@ private: /** Characters we treat as ending an event string. */ static const char s_eventEndTokens[]; + /** Characters found between successive "key=value" strings. */ + static const char s_keyPairSepTokens[]; + /** Temporary space for event data during our parsing. */ char m_buf[EVENT_BUFSIZE]; From 841f48699db6a338499e809d1625170ce2881450 Mon Sep 17 00:00:00 2001 From: Alexander Motin Date: Tue, 15 May 2012 06:38:38 +0000 Subject: [PATCH 37/89] Add workaround for broken Supermicro backplanes, reporting wrong value in ELEMENT INDEX field. If index of element for which additional status is not supported is detected, ignore this and further indexes, trying to assign elements sequentially. --- sys/cam/scsi/scsi_enc_ses.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/sys/cam/scsi/scsi_enc_ses.c b/sys/cam/scsi/scsi_enc_ses.c index 127e1995d2e..5cf03795170 100644 --- a/sys/cam/scsi/scsi_enc_ses.c +++ b/sys/cam/scsi/scsi_enc_ses.c @@ -1670,9 +1670,10 @@ static int ses_process_elm_addlstatus(enc_softc_t *enc, struct enc_fsm_state *state, union ccb *ccb, uint8_t **bufp, int error, int xfer_len) { - struct ses_iterator iter; + struct ses_iterator iter, titer; int eip; int err; + int ignore_index = 0; int length; int offset; enc_cache_t *enc_cache; @@ -1680,7 +1681,7 @@ ses_process_elm_addlstatus(enc_softc_t *enc, struct enc_fsm_state *state, uint8_t *buf; ses_element_t *elmpriv; const struct ses_page_hdr *hdr; - enc_element_t *element; + enc_element_t *element, *telement; enc_cache = &enc->enc_daemon_cache; ses_cache = enc_cache->private; @@ -1743,15 +1744,24 @@ ses_process_elm_addlstatus(enc_softc_t *enc, struct enc_fsm_state *state, elm_hdr = (struct ses_elm_addlstatus_base_hdr *)&buf[offset]; eip = ses_elm_addlstatus_eip(elm_hdr); - if (eip) { + if (eip && !ignore_index) { struct ses_elm_addlstatus_eip_hdr *eip_hdr; int expected_index; eip_hdr = (struct ses_elm_addlstatus_eip_hdr *)elm_hdr; expected_index = iter.individual_element_index; - element = ses_iter_seek_to(&iter, + titer = iter; + telement = ses_iter_seek_to(&titer, eip_hdr->element_index, SES_ELEM_INDEX_INDIVIDUAL); + if (telement != NULL && + (ses_typehasaddlstatus(enc, titer.type_index) != + TYPE_ADDLSTATUS_NONE || + titer.type_index > ELMTYP_SAS_CONN)) { + iter = titer; + element = telement; + } else + ignore_index = 1; if (iter.individual_element_index > expected_index && status_type == TYPE_ADDLSTATUS_MANDATORY) { From 2dadbd3dc7d552e4b5e21cc152a279328f9f0e6c Mon Sep 17 00:00:00 2001 From: Alexander Motin Date: Tue, 15 May 2012 06:42:16 +0000 Subject: [PATCH 38/89] Hide warning message under bootverbose. It is impossible for user to do anything about it. --- sys/kern/kern_conf.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sys/kern/kern_conf.c b/sys/kern/kern_conf.c index 461f008cb3f..31f4683e93c 100644 --- a/sys/kern/kern_conf.c +++ b/sys/kern/kern_conf.c @@ -993,9 +993,10 @@ make_dev_physpath_alias(int flags, struct cdev **cdev, struct cdev *pdev, max_parentpath_len = SPECNAMELEN - physpath_len - /*/*/1; parentpath_len = strlen(pdev->si_name); if (max_parentpath_len < parentpath_len) { - printf("make_dev_physpath_alias: WARNING - Unable to alias %s " - "to %s/%s - path too long\n", - pdev->si_name, physpath, pdev->si_name); + if (bootverbose) + printf("WARNING: Unable to alias %s " + "to %s/%s - path too long\n", + pdev->si_name, physpath, pdev->si_name); ret = ENAMETOOLONG; goto out; } From 29be35d7662873e4a88f4b09965191b251c4e7e1 Mon Sep 17 00:00:00 2001 From: Alexander Motin Date: Tue, 15 May 2012 14:46:22 +0000 Subject: [PATCH 39/89] Fix misreplacement: enclosure/seslosure. --- sys/cam/scsi/scsi_ses.h | 158 ++++++++++++++++++++-------------------- 1 file changed, 79 insertions(+), 79 deletions(-) diff --git a/sys/cam/scsi/scsi_ses.h b/sys/cam/scsi/scsi_ses.h index 2c9ceb402b3..3d6009eb2e7 100644 --- a/sys/cam/scsi/scsi_ses.h +++ b/sys/cam/scsi/scsi_ses.h @@ -622,46 +622,46 @@ struct ses_ctrl_enclosure { }; enum ses_ctrl_enclosure_field_data { - SES_CTRL_SESLOSURE_RQST_IDENT_BYTE = 0, - SES_CTRL_SESLOSURE_RQST_IDENT_MASK = 0x80, - SES_CTRL_SESLOSURE_RQST_IDENT_SHIFT = 7, + SES_CTRL_ENCLOSURE_RQST_IDENT_BYTE = 0, + SES_CTRL_ENCLOSURE_RQST_IDENT_MASK = 0x80, + SES_CTRL_ENCLOSURE_RQST_IDENT_SHIFT = 7, - SES_CTRL_SESLOSURE_POWER_CYCLE_RQST_BYTE = 1, - SES_CTRL_SESLOSURE_POWER_CYCLE_RQST_MASK = 0xC0, - SES_CTRL_SESLOSURE_POWER_CYCLE_RQST_SHIFT = 6, - SES_CTRL_SESLOSURE_POWER_CYCLE_RQST_NONE = 0x0, - SES_CTRL_SESLOSURE_POWER_CYCLE_RQST_AFTER_DELAY = 0x1, - SES_CTRL_SESLOSURE_POWER_CYCLE_RQST_CANCEL = 0x2, + SES_CTRL_ENCLOSURE_POWER_CYCLE_RQST_BYTE = 1, + SES_CTRL_ENCLOSURE_POWER_CYCLE_RQST_MASK = 0xC0, + SES_CTRL_ENCLOSURE_POWER_CYCLE_RQST_SHIFT = 6, + SES_CTRL_ENCLOSURE_POWER_CYCLE_RQST_NONE = 0x0, + SES_CTRL_ENCLOSURE_POWER_CYCLE_RQST_AFTER_DELAY = 0x1, + SES_CTRL_ENCLOSURE_POWER_CYCLE_RQST_CANCEL = 0x2, - SES_CTRL_SESLOSURE_POWER_CYCLE_DELAY_BYTE = 1, - SES_CTRL_SESLOSURE_POWER_CYCLE_DELAY_MASK = 0x3F, - SES_CTRL_SESLOSURE_POWER_CYCLE_DELAY_SHIFT = 0, - SES_CTRL_SESLOSURE_POWER_CYCLE_DELAY_MAX = 60,/*minutes*/ + SES_CTRL_ENCLOSURE_POWER_CYCLE_DELAY_BYTE = 1, + SES_CTRL_ENCLOSURE_POWER_CYCLE_DELAY_MASK = 0x3F, + SES_CTRL_ENCLOSURE_POWER_CYCLE_DELAY_SHIFT = 0, + SES_CTRL_ENCLOSURE_POWER_CYCLE_DELAY_MAX = 60,/*minutes*/ - SES_CTRL_SESLOSURE_POWER_OFF_DURATION_BYTE = 2, - SES_CTRL_SESLOSURE_POWER_OFF_DURATION_MASK = 0xFC, - SES_CTRL_SESLOSURE_POWER_OFF_DURATION_SHIFT = 2, - SES_CTRL_SESLOSURE_POWER_OFF_DURATION_MAX_AUTO = 60, - SES_CTRL_SESLOSURE_POWER_OFF_DURATION_MANUAL = 63, + SES_CTRL_ENCLOSURE_POWER_OFF_DURATION_BYTE = 2, + SES_CTRL_ENCLOSURE_POWER_OFF_DURATION_MASK = 0xFC, + SES_CTRL_ENCLOSURE_POWER_OFF_DURATION_SHIFT = 2, + SES_CTRL_ENCLOSURE_POWER_OFF_DURATION_MAX_AUTO = 60, + SES_CTRL_ENCLOSURE_POWER_OFF_DURATION_MANUAL = 63, - SES_CTRL_SESLOSURE_RQST_FAIL_BYTE = 2, - SES_CTRL_SESLOSURE_RQST_FAIL_MASK = 0x02, - SES_CTRL_SESLOSURE_RQST_FAIL_SHIFT = 1, + SES_CTRL_ENCLOSURE_RQST_FAIL_BYTE = 2, + SES_CTRL_ENCLOSURE_RQST_FAIL_MASK = 0x02, + SES_CTRL_ENCLOSURE_RQST_FAIL_SHIFT = 1, - SES_CTRL_SESLOSURE_RQST_WARN_BYTE = 2, - SES_CTRL_SESLOSURE_RQST_WARN_MASK = 0x01, - SES_CTRL_SESLOSURE_RQST_WARN_SHIFT = 0 + SES_CTRL_ENCLOSURE_RQST_WARN_BYTE = 2, + SES_CTRL_ENCLOSURE_RQST_WARN_MASK = 0x01, + SES_CTRL_ENCLOSURE_RQST_WARN_SHIFT = 0 }; -#define GEN_SES_CTRL_SESLOSURE_ACCESSORS(LCASE, UCASE) \ - GEN_ACCESSORS(ses_ctrl_enclosure, SES_CTRL_SESLOSURE, LCASE, UCASE) -GEN_SES_CTRL_SESLOSURE_ACCESSORS(rqst_ident, RQST_IDENT) -GEN_SES_CTRL_SESLOSURE_ACCESSORS(power_cycle_rqst, POWER_CYCLE_RQST) -GEN_SES_CTRL_SESLOSURE_ACCESSORS(power_cycle_delay, POWER_CYCLE_DELAY) -GEN_SES_CTRL_SESLOSURE_ACCESSORS(power_off_duration, POWER_OFF_DURATION) -GEN_SES_CTRL_SESLOSURE_ACCESSORS(rqst_fail, RQST_FAIL) -GEN_SES_CTRL_SESLOSURE_ACCESSORS(rqst_warn, RQST_WARN) -#undef GEN_SES_CTRL_SESLOSURE_ACCESSORS +#define GEN_SES_CTRL_ENCLOSURE_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_enclosure, SES_CTRL_ENCLOSURE, LCASE, UCASE) +GEN_SES_CTRL_ENCLOSURE_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_ENCLOSURE_ACCESSORS(power_cycle_rqst, POWER_CYCLE_RQST) +GEN_SES_CTRL_ENCLOSURE_ACCESSORS(power_cycle_delay, POWER_CYCLE_DELAY) +GEN_SES_CTRL_ENCLOSURE_ACCESSORS(power_off_duration, POWER_OFF_DURATION) +GEN_SES_CTRL_ENCLOSURE_ACCESSORS(rqst_fail, RQST_FAIL) +GEN_SES_CTRL_ENCLOSURE_ACCESSORS(rqst_warn, RQST_WARN) +#undef GEN_SES_CTRL_ENCLOSURE_ACCESSORS /*------------------- SCSI Port/Transceiver Control Element ------------------*/ struct ses_ctrl_scsi_port_or_xcvr { @@ -1011,13 +1011,13 @@ enum ses_status_dev_slot_field_data { SES_STATUS_DEV_SLOT_DO_NOT_REMOVE_MASK = 0x40, SES_STATUS_DEV_SLOT_DO_NOT_REMOVE_SHIFT = 6, - SES_STATUS_DEV_SLOT_SESLOSURE_BYPED_A_BYTE = 0, - SES_STATUS_DEV_SLOT_SESLOSURE_BYPED_A_MASK = 0x20, - SES_STATUS_DEV_SLOT_SESLOSURE_BYPED_A_SHIFT = 5, + SES_STATUS_DEV_SLOT_ENCLOSURE_BYPED_A_BYTE = 0, + SES_STATUS_DEV_SLOT_ENCLOSURE_BYPED_A_MASK = 0x20, + SES_STATUS_DEV_SLOT_ENCLOSURE_BYPED_A_SHIFT = 5, - SES_STATUS_DEV_SLOT_SESLOSURE_BYPED_B_BYTE = 0, - SES_STATUS_DEV_SLOT_SESLOSURE_BYPED_B_MASK = 0x10, - SES_STATUS_DEV_SLOT_SESLOSURE_BYPED_B_SHIFT = 4, + SES_STATUS_DEV_SLOT_ENCLOSURE_BYPED_B_BYTE = 0, + SES_STATUS_DEV_SLOT_ENCLOSURE_BYPED_B_MASK = 0x10, + SES_STATUS_DEV_SLOT_ENCLOSURE_BYPED_B_SHIFT = 4, SES_STATUS_DEV_SLOT_INSERT_READY_BYTE = 0, SES_STATUS_DEV_SLOT_INSERT_READY_MASK = 0x08, @@ -1072,8 +1072,8 @@ enum ses_status_dev_slot_field_data { GEN_SES_STATUS_DEV_SLOT_ACCESSORS(app_client_byped_a, APP_CLIENT_BYPED_A) GEN_SES_STATUS_DEV_SLOT_ACCESSORS(do_not_remove, DO_NOT_REMOVE) -GEN_SES_STATUS_DEV_SLOT_ACCESSORS(seslosure_byped_a, SESLOSURE_BYPED_A) -GEN_SES_STATUS_DEV_SLOT_ACCESSORS(seslosure_byped_b, SESLOSURE_BYPED_B) +GEN_SES_STATUS_DEV_SLOT_ACCESSORS(enclosure_byped_a, enclosure_BYPED_A) +GEN_SES_STATUS_DEV_SLOT_ACCESSORS(enclosure_byped_b, enclosure_BYPED_B) GEN_SES_STATUS_DEV_SLOT_ACCESSORS(insert_ready, INSERT_READY) GEN_SES_STATUS_DEV_SLOT_ACCESSORS(remove, REMOVE) GEN_SES_STATUS_DEV_SLOT_ACCESSORS(ident, IDENT) @@ -1667,55 +1667,55 @@ GEN_SES_STATUS_KEY_PAD_ENTRY_ACCESSORS(fail, FAIL) #undef GEN_SES_STATUS_KEY_PAD_ENTRY_ACCESSORS /*------------------------- Enclosure Status Element -------------------------*/ -struct ses_status_seslosure { +struct ses_status_enclosure { struct ses_status_common common; uint8_t bytes[3]; }; -enum ses_status_seslosure_field_data { - SES_STATUS_SESLOSURE_IDENT_BYTE = 0, - SES_STATUS_SESLOSURE_IDENT_MASK = 0x80, - SES_STATUS_SESLOSURE_IDENT_SHIFT = 7, +enum ses_status_enclosure_field_data { + SES_STATUS_enclosure_IDENT_BYTE = 0, + SES_STATUS_enclosure_IDENT_MASK = 0x80, + SES_STATUS_enclosure_IDENT_SHIFT = 7, - SES_STATUS_SESLOSURE_TIME_UNTIL_POWER_CYCLE_BYTE = 1, - SES_STATUS_SESLOSURE_TIME_UNTIL_POWER_CYCLE_MASK = 0xFC, - SES_STATUS_SESLOSURE_TIME_UNTIL_POWER_CYCLE_SHIFT = 2, + SES_STATUS_enclosure_TIME_UNTIL_POWER_CYCLE_BYTE = 1, + SES_STATUS_enclosure_TIME_UNTIL_POWER_CYCLE_MASK = 0xFC, + SES_STATUS_enclosure_TIME_UNTIL_POWER_CYCLE_SHIFT = 2, - SES_STATUS_SESLOSURE_FAIL_BYTE = 1, - SES_STATUS_SESLOSURE_FAIL_MASK = 0x02, - SES_STATUS_SESLOSURE_FAIL_SHIFT = 1, + SES_STATUS_enclosure_FAIL_BYTE = 1, + SES_STATUS_enclosure_FAIL_MASK = 0x02, + SES_STATUS_enclosure_FAIL_SHIFT = 1, - SES_STATUS_SESLOSURE_WARN_BYTE = 1, - SES_STATUS_SESLOSURE_WARN_MASK = 0x01, - SES_STATUS_SESLOSURE_WARN_SHIFT = 0, + SES_STATUS_enclosure_WARN_BYTE = 1, + SES_STATUS_enclosure_WARN_MASK = 0x01, + SES_STATUS_enclosure_WARN_SHIFT = 0, - SES_STATUS_SESLOSURE_REQUESTED_POWER_OFF_DURATION_BYTE = 2, - SES_STATUS_SESLOSURE_REQUESTED_POWER_OFF_DURATION_MASK = 0xFC, - SES_STATUS_SESLOSURE_REQUESTED_POWER_OFF_DURATION_SHIFT = 2, - SES_STATUS_SESLOSURE_REQUESTED_POWER_OFF_DURATION_MAX_AUTO = 60, - SES_STATUS_SESLOSURE_REQUESTED_POWER_OFF_DURATION_MANUAL = 63, + SES_STATUS_enclosure_REQUESTED_POWER_OFF_DURATION_BYTE = 2, + SES_STATUS_enclosure_REQUESTED_POWER_OFF_DURATION_MASK = 0xFC, + SES_STATUS_enclosure_REQUESTED_POWER_OFF_DURATION_SHIFT = 2, + SES_STATUS_enclosure_REQUESTED_POWER_OFF_DURATION_MAX_AUTO = 60, + SES_STATUS_enclosure_REQUESTED_POWER_OFF_DURATION_MANUAL = 63, - SES_STATUS_SESLOSURE_REQUESTED_FAIL_BYTE = 2, - SES_STATUS_SESLOSURE_REQUESTED_FAIL_MASK = 0x02, - SES_STATUS_SESLOSURE_REQUESTED_FAIL_SHIFT = 1, + SES_STATUS_enclosure_REQUESTED_FAIL_BYTE = 2, + SES_STATUS_enclosure_REQUESTED_FAIL_MASK = 0x02, + SES_STATUS_enclosure_REQUESTED_FAIL_SHIFT = 1, - SES_STATUS_SESLOSURE_REQUESTED_WARN_BYTE = 2, - SES_STATUS_SESLOSURE_REQUESTED_WARN_MASK = 0x01, - SES_STATUS_SESLOSURE_REQUESTED_WARN_SHIFT = 0 + SES_STATUS_enclosure_REQUESTED_WARN_BYTE = 2, + SES_STATUS_enclosure_REQUESTED_WARN_MASK = 0x01, + SES_STATUS_enclosure_REQUESTED_WARN_SHIFT = 0 }; -#define GEN_SES_STATUS_SESLOSURE_ACCESSORS(LCASE, UCASE) \ - GEN_GETTER(ses_status_seslosure, SES_STATUS_SESLOSURE, LCASE, UCASE) -GEN_SES_STATUS_SESLOSURE_ACCESSORS(ident, IDENT) -GEN_SES_STATUS_SESLOSURE_ACCESSORS(time_until_power_cycle, +#define GEN_SES_STATUS_enclosure_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_enclosure, SES_STATUS_enclosure, LCASE, UCASE) +GEN_SES_STATUS_enclosure_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_enclosure_ACCESSORS(time_until_power_cycle, TIME_UNTIL_POWER_CYCLE) -GEN_SES_STATUS_SESLOSURE_ACCESSORS(fail, FAIL) -GEN_SES_STATUS_SESLOSURE_ACCESSORS(warn, WARN) -GEN_SES_STATUS_SESLOSURE_ACCESSORS(requested_power_off_duration, +GEN_SES_STATUS_enclosure_ACCESSORS(fail, FAIL) +GEN_SES_STATUS_enclosure_ACCESSORS(warn, WARN) +GEN_SES_STATUS_enclosure_ACCESSORS(requested_power_off_duration, REQUESTED_POWER_OFF_DURATION) -GEN_SES_STATUS_SESLOSURE_ACCESSORS(requested_fail, REQUESTED_FAIL) -GEN_SES_STATUS_SESLOSURE_ACCESSORS(requested_warn, REQUESTED_WARN) -#undef GEN_SES_STATUS_SESLOSURE_ACCESSORS +GEN_SES_STATUS_enclosure_ACCESSORS(requested_fail, REQUESTED_FAIL) +GEN_SES_STATUS_enclosure_ACCESSORS(requested_warn, REQUESTED_WARN) +#undef GEN_SES_STATUS_enclosure_ACCESSORS /*------------------- SCSI Port/Transceiver Status Element -------------------*/ struct ses_status_scsi_port_or_xcvr { @@ -1947,11 +1947,11 @@ GEN_SES_STATUS_INITIATOR_PORT_ACCESSORS(report, REPORT) GEN_SES_STATUS_INITIATOR_PORT_ACCESSORS(enabled, ENABLED) #undef GEN_SES_STATUS_INITIATOR_PORT_ACCESSORS -/*-------------------- Simple Subseslosure Status Element --------------------*/ +/*-------------------- Simple Subenclosure Status Element --------------------*/ struct ses_status_simple_subses { struct ses_status_common common; uint8_t bytes[2]; - uint8_t short_seslosure_status; + uint8_t short_enclosure_status; }; enum ses_status_simple_subses_field_data { @@ -2168,7 +2168,7 @@ struct ses_status_page_hdr { /* * For control pages, cstat[0] is the same for the - * seslosure and is common across all device types. + * enclosure and is common across all device types. * * If SESCTL_CSEL is set, then PRDFAIL, DISABLE and RSTSWAP * are checked, otherwise bits that are specific to the device From 0f59fddde563e5adb56ca780501e8b6b515331dd Mon Sep 17 00:00:00 2001 From: Alexander Motin Date: Tue, 15 May 2012 15:00:26 +0000 Subject: [PATCH 40/89] mmm, fix for a fix. :( --- sys/cam/scsi/scsi_ses.h | 70 ++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/sys/cam/scsi/scsi_ses.h b/sys/cam/scsi/scsi_ses.h index 3d6009eb2e7..ffc5493cc9f 100644 --- a/sys/cam/scsi/scsi_ses.h +++ b/sys/cam/scsi/scsi_ses.h @@ -1072,8 +1072,8 @@ enum ses_status_dev_slot_field_data { GEN_SES_STATUS_DEV_SLOT_ACCESSORS(app_client_byped_a, APP_CLIENT_BYPED_A) GEN_SES_STATUS_DEV_SLOT_ACCESSORS(do_not_remove, DO_NOT_REMOVE) -GEN_SES_STATUS_DEV_SLOT_ACCESSORS(enclosure_byped_a, enclosure_BYPED_A) -GEN_SES_STATUS_DEV_SLOT_ACCESSORS(enclosure_byped_b, enclosure_BYPED_B) +GEN_SES_STATUS_DEV_SLOT_ACCESSORS(enclosure_byped_a, ENCLOSURE_BYPED_A) +GEN_SES_STATUS_DEV_SLOT_ACCESSORS(enclosure_byped_b, ENCLOSURE_BYPED_B) GEN_SES_STATUS_DEV_SLOT_ACCESSORS(insert_ready, INSERT_READY) GEN_SES_STATUS_DEV_SLOT_ACCESSORS(remove, REMOVE) GEN_SES_STATUS_DEV_SLOT_ACCESSORS(ident, IDENT) @@ -1673,49 +1673,49 @@ struct ses_status_enclosure { }; enum ses_status_enclosure_field_data { - SES_STATUS_enclosure_IDENT_BYTE = 0, - SES_STATUS_enclosure_IDENT_MASK = 0x80, - SES_STATUS_enclosure_IDENT_SHIFT = 7, + SES_STATUS_ENCLOSURE_IDENT_BYTE = 0, + SES_STATUS_ENCLOSURE_IDENT_MASK = 0x80, + SES_STATUS_ENCLOSURE_IDENT_SHIFT = 7, - SES_STATUS_enclosure_TIME_UNTIL_POWER_CYCLE_BYTE = 1, - SES_STATUS_enclosure_TIME_UNTIL_POWER_CYCLE_MASK = 0xFC, - SES_STATUS_enclosure_TIME_UNTIL_POWER_CYCLE_SHIFT = 2, + SES_STATUS_ENCLOSURE_TIME_UNTIL_POWER_CYCLE_BYTE = 1, + SES_STATUS_ENCLOSURE_TIME_UNTIL_POWER_CYCLE_MASK = 0xFC, + SES_STATUS_ENCLOSURE_TIME_UNTIL_POWER_CYCLE_SHIFT = 2, - SES_STATUS_enclosure_FAIL_BYTE = 1, - SES_STATUS_enclosure_FAIL_MASK = 0x02, - SES_STATUS_enclosure_FAIL_SHIFT = 1, + SES_STATUS_ENCLOSURE_FAIL_BYTE = 1, + SES_STATUS_ENCLOSURE_FAIL_MASK = 0x02, + SES_STATUS_ENCLOSURE_FAIL_SHIFT = 1, - SES_STATUS_enclosure_WARN_BYTE = 1, - SES_STATUS_enclosure_WARN_MASK = 0x01, - SES_STATUS_enclosure_WARN_SHIFT = 0, + SES_STATUS_ENCLOSURE_WARN_BYTE = 1, + SES_STATUS_ENCLOSURE_WARN_MASK = 0x01, + SES_STATUS_ENCLOSURE_WARN_SHIFT = 0, - SES_STATUS_enclosure_REQUESTED_POWER_OFF_DURATION_BYTE = 2, - SES_STATUS_enclosure_REQUESTED_POWER_OFF_DURATION_MASK = 0xFC, - SES_STATUS_enclosure_REQUESTED_POWER_OFF_DURATION_SHIFT = 2, - SES_STATUS_enclosure_REQUESTED_POWER_OFF_DURATION_MAX_AUTO = 60, - SES_STATUS_enclosure_REQUESTED_POWER_OFF_DURATION_MANUAL = 63, + SES_STATUS_ENCLOSURE_REQUESTED_POWER_OFF_DURATION_BYTE = 2, + SES_STATUS_ENCLOSURE_REQUESTED_POWER_OFF_DURATION_MASK = 0xFC, + SES_STATUS_ENCLOSURE_REQUESTED_POWER_OFF_DURATION_SHIFT = 2, + SES_STATUS_ENCLOSURE_REQUESTED_POWER_OFF_DURATION_MAX_AUTO = 60, + SES_STATUS_ENCLOSURE_REQUESTED_POWER_OFF_DURATION_MANUAL = 63, - SES_STATUS_enclosure_REQUESTED_FAIL_BYTE = 2, - SES_STATUS_enclosure_REQUESTED_FAIL_MASK = 0x02, - SES_STATUS_enclosure_REQUESTED_FAIL_SHIFT = 1, + SES_STATUS_ENCLOSURE_REQUESTED_FAIL_BYTE = 2, + SES_STATUS_ENCLOSURE_REQUESTED_FAIL_MASK = 0x02, + SES_STATUS_ENCLOSURE_REQUESTED_FAIL_SHIFT = 1, - SES_STATUS_enclosure_REQUESTED_WARN_BYTE = 2, - SES_STATUS_enclosure_REQUESTED_WARN_MASK = 0x01, - SES_STATUS_enclosure_REQUESTED_WARN_SHIFT = 0 + SES_STATUS_ENCLOSURE_REQUESTED_WARN_BYTE = 2, + SES_STATUS_ENCLOSURE_REQUESTED_WARN_MASK = 0x01, + SES_STATUS_ENCLOSURE_REQUESTED_WARN_SHIFT = 0 }; -#define GEN_SES_STATUS_enclosure_ACCESSORS(LCASE, UCASE) \ - GEN_GETTER(ses_status_enclosure, SES_STATUS_enclosure, LCASE, UCASE) -GEN_SES_STATUS_enclosure_ACCESSORS(ident, IDENT) -GEN_SES_STATUS_enclosure_ACCESSORS(time_until_power_cycle, +#define GEN_SES_STATUS_ENCLOSURE_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_enclosure, SES_STATUS_ENCLOSURE, LCASE, UCASE) +GEN_SES_STATUS_ENCLOSURE_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_ENCLOSURE_ACCESSORS(time_until_power_cycle, TIME_UNTIL_POWER_CYCLE) -GEN_SES_STATUS_enclosure_ACCESSORS(fail, FAIL) -GEN_SES_STATUS_enclosure_ACCESSORS(warn, WARN) -GEN_SES_STATUS_enclosure_ACCESSORS(requested_power_off_duration, +GEN_SES_STATUS_ENCLOSURE_ACCESSORS(fail, FAIL) +GEN_SES_STATUS_ENCLOSURE_ACCESSORS(warn, WARN) +GEN_SES_STATUS_ENCLOSURE_ACCESSORS(requested_power_off_duration, REQUESTED_POWER_OFF_DURATION) -GEN_SES_STATUS_enclosure_ACCESSORS(requested_fail, REQUESTED_FAIL) -GEN_SES_STATUS_enclosure_ACCESSORS(requested_warn, REQUESTED_WARN) -#undef GEN_SES_STATUS_enclosure_ACCESSORS +GEN_SES_STATUS_ENCLOSURE_ACCESSORS(requested_fail, REQUESTED_FAIL) +GEN_SES_STATUS_ENCLOSURE_ACCESSORS(requested_warn, REQUESTED_WARN) +#undef GEN_SES_STATUS_ENCLOSURE_ACCESSORS /*------------------- SCSI Port/Transceiver Status Element -------------------*/ struct ses_status_scsi_port_or_xcvr { From 81a5c20a0eb461f461b8c5084c3730500647e848 Mon Sep 17 00:00:00 2001 From: Alexander Motin Date: Tue, 15 May 2012 16:17:30 +0000 Subject: [PATCH 41/89] Rename enclosure management driver from "enc" back to "ses". "enc" name is already occupied by the enc(4) -- Encapsulating Interface. --- sys/amd64/conf/GENERIC | 2 +- sys/cam/scsi/scsi_enc.c | 6 +++--- sys/conf/files | 6 +++--- sys/i386/conf/GENERIC | 2 +- sys/ia64/conf/GENERIC | 2 +- sys/mips/conf/OCTEON1 | 2 +- sys/pc98/conf/GENERIC | 2 +- sys/sparc64/conf/GENERIC | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/sys/amd64/conf/GENERIC b/sys/amd64/conf/GENERIC index 7fdf6f4bedd..4c57afe9b23 100644 --- a/sys/amd64/conf/GENERIC +++ b/sys/amd64/conf/GENERIC @@ -135,7 +135,7 @@ device da # Direct Access (disks) device sa # Sequential Access (tape etc) device cd # CD device pass # Passthrough device (direct ATA/SCSI access) -device enc # Enclosure Services (SES and SAF-TE) +device ses # Enclosure Services (SES and SAF-TE) device ctl # CAM Target Layer # RAID controllers interfaced to the SCSI subsystem diff --git a/sys/cam/scsi/scsi_enc.c b/sys/cam/scsi/scsi_enc.c index e43689fa037..03e43bce56c 100644 --- a/sys/cam/scsi/scsi_enc.c +++ b/sys/cam/scsi/scsi_enc.c @@ -81,7 +81,7 @@ SYSCTL_NODE(_kern_cam, OID_AUTO, enc, CTLFLAG_RD, 0, "CAM Enclosure Services driver"); static struct periph_driver encdriver = { - enc_init, "enc", + enc_init, "ses", TAILQ_HEAD_INITIALIZER(encdriver.units), /* generation */ 0 }; @@ -92,7 +92,7 @@ static struct cdevsw enc_cdevsw = { .d_open = enc_open, .d_close = enc_close, .d_ioctl = enc_ioctl, - .d_name = "enc", + .d_name = "ses", .d_flags = 0, }; @@ -219,7 +219,7 @@ enc_async(void *callback_arg, uint32_t code, struct cam_path *path, void *arg) } status = cam_periph_alloc(enc_ctor, enc_oninvalidate, - enc_dtor, enc_start, "enc", CAM_PERIPH_BIO, + enc_dtor, enc_start, "ses", CAM_PERIPH_BIO, cgd->ccb_h.path, enc_async, AC_FOUND_DEVICE, cgd); if (status != CAM_REQ_CMP && status != CAM_REQ_INPROG) { diff --git a/sys/conf/files b/sys/conf/files index 5ac2e0768af..a1e7e8e8466 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -134,9 +134,9 @@ cam/scsi/scsi_low_pisa.c optional ct | ncv | nsp | stg cam/scsi/scsi_pass.c optional pass cam/scsi/scsi_pt.c optional pt cam/scsi/scsi_sa.c optional sa -cam/scsi/scsi_enc.c optional enc -cam/scsi/scsi_enc_ses.c optional enc -cam/scsi/scsi_enc_safte.c optional enc +cam/scsi/scsi_enc.c optional ses +cam/scsi/scsi_enc_ses.c optional ses +cam/scsi/scsi_enc_safte.c optional ses cam/scsi/scsi_sg.c optional sg cam/scsi/scsi_targ_bh.c optional targbh cam/scsi/scsi_target.c optional targ diff --git a/sys/i386/conf/GENERIC b/sys/i386/conf/GENERIC index aa93269ca1c..61e54d22d6a 100644 --- a/sys/i386/conf/GENERIC +++ b/sys/i386/conf/GENERIC @@ -142,7 +142,7 @@ device da # Direct Access (disks) device sa # Sequential Access (tape etc) device cd # CD device pass # Passthrough device (direct ATA/SCSI access) -device enc # Enclosure Services (SES and SAF-TE) +device ses # Enclosure Services (SES and SAF-TE) device ctl # CAM Target Layer # RAID controllers interfaced to the SCSI subsystem diff --git a/sys/ia64/conf/GENERIC b/sys/ia64/conf/GENERIC index 28ac6f6fd19..e22450b17c0 100644 --- a/sys/ia64/conf/GENERIC +++ b/sys/ia64/conf/GENERIC @@ -113,7 +113,7 @@ device ch # Media changer device da # Direct Access (ie disk) device pass # Passthrough (direct ATA/SCSI access) device sa # Sequential Access (ie tape) -device enc # Enclosure Services (SES and SAF-TE) +device ses # Enclosure Services (SES and SAF-TE) device ctl # CAM Target Layer # RAID controllers diff --git a/sys/mips/conf/OCTEON1 b/sys/mips/conf/OCTEON1 index c14f8e29807..aa6490bdabe 100644 --- a/sys/mips/conf/OCTEON1 +++ b/sys/mips/conf/OCTEON1 @@ -135,7 +135,7 @@ device da # Direct Access (disks) device sa # Sequential Access (tape etc) device cd # CD device pass # Passthrough device (direct ATA/SCSI access) -device enc # Enclosure Services (SES and SAF-TE) +device ses # Enclosure Services (SES and SAF-TE) # RAID controllers interfaced to the SCSI subsystem device amr # AMI MegaRAID diff --git a/sys/pc98/conf/GENERIC b/sys/pc98/conf/GENERIC index e31d4451f3c..8f3d9b66f3d 100644 --- a/sys/pc98/conf/GENERIC +++ b/sys/pc98/conf/GENERIC @@ -110,7 +110,7 @@ device da # Direct Access (disks) device sa # Sequential Access (tape etc) device cd # CD device pass # Passthrough device (direct ATA/SCSI access) -device enc # Enclosure Services (SES and SAF-TE) +device ses # Enclosure Services (SES and SAF-TE) # keyboard driver device pckbd # PC98 keyboard diff --git a/sys/sparc64/conf/GENERIC b/sys/sparc64/conf/GENERIC index c00a4d30451..766148cc6a2 100644 --- a/sys/sparc64/conf/GENERIC +++ b/sys/sparc64/conf/GENERIC @@ -118,7 +118,7 @@ device da # Direct Access (disks) device sa # Sequential Access (tape etc) device cd # CD device pass # Passthrough device (direct ATA/SCSI access) -device enc # Enclosure Services (SES and SAF-TE) +device ses # Enclosure Services (SES and SAF-TE) device ctl # CAM Target Layer # RAID controllers From 07e020815b2a3e633c453f67489c8f68055ec7ef Mon Sep 17 00:00:00 2001 From: Alexander Motin Date: Tue, 15 May 2012 22:55:23 +0000 Subject: [PATCH 42/89] Restore old API/ABI compatibility to make this code mergeable. --- sys/cam/scsi/scsi_enc.h | 4 +- sys/cam/scsi/scsi_ses.h | 101 +++++++++++++++++++++++++++++++++++++++- usr.bin/kdump/mkioctls | 2 - 3 files changed, 102 insertions(+), 5 deletions(-) diff --git a/sys/cam/scsi/scsi_enc.h b/sys/cam/scsi/scsi_enc.h index e73172ce143..a5af9a57e2b 100644 --- a/sys/cam/scsi/scsi_enc.h +++ b/sys/cam/scsi/scsi_enc.h @@ -125,10 +125,10 @@ typedef struct encioc_element { unsigned int elm_idx; /* ID of SubEnclosure containing Element*/ - unsigned int elm_subenc_id; + unsigned char elm_subenc_id; /* Element Type */ - elm_type_t elm_type; + unsigned char elm_type; } encioc_element_t; /* diff --git a/sys/cam/scsi/scsi_ses.h b/sys/cam/scsi/scsi_ses.h index ffc5493cc9f..5a94cacb267 100644 --- a/sys/cam/scsi/scsi_ses.h +++ b/sys/cam/scsi/scsi_ses.h @@ -32,8 +32,107 @@ #ifndef _SCSI_SES_H_ #define _SCSI_SES_H_ +#include +#include #include +#if 1 /* Legacy API compatibility */ + +#define SESIOC ('s' - 040) +#define SESIOC_GETNOBJ _IO(SESIOC, 1) +#define SESIOC_GETOBJMAP _IO(SESIOC, 2) +#define SESIOC_GETENCSTAT _IO(SESIOC, 3) +#define SESIOC_SETENCSTAT _IO(SESIOC, 4) +#define SESIOC_GETOBJSTAT _IO(SESIOC, 5) +#define SESIOC_SETOBJSTAT _IO(SESIOC, 6) +#define SESIOC_GETTEXT _IO(SESIOC, 7) +#define SESIOC_INIT _IO(SESIOC, 8) + +typedef struct { + unsigned int obj_id; /* Object Identifier */ + unsigned char subencid; /* SubEnclosure ID */ + unsigned char object_type; /* Object Type */ +} ses_object; + +/* Object Types */ +#define SESTYP_UNSPECIFIED 0x00 +#define SESTYP_DEVICE 0x01 +#define SESTYP_POWER 0x02 +#define SESTYP_FAN 0x03 +#define SESTYP_THERM 0x04 +#define SESTYP_DOORLOCK 0x05 +#define SESTYP_ALARM 0x06 +#define SESTYP_ESCC 0x07 /* Enclosure SCC */ +#define SESTYP_SCC 0x08 /* SCC */ +#define SESTYP_NVRAM 0x09 +#define SESTYP_UPS 0x0b +#define SESTYP_DISPLAY 0x0c +#define SESTYP_KEYPAD 0x0d +#define SESTYP_ENCLOSURE 0x0e +#define SESTYP_SCSIXVR 0x0f +#define SESTYP_LANGUAGE 0x10 +#define SESTYP_COMPORT 0x11 +#define SESTYP_VOM 0x12 +#define SESTYP_AMMETER 0x13 +#define SESTYP_SCSI_TGT 0x14 +#define SESTYP_SCSI_INI 0x15 +#define SESTYP_SUBENC 0x16 +#define SESTYP_ARRAY 0x17 +#define SESTYP_SASEXPANDER 0x18 +#define SESTYP_SASCONNECTOR 0x19 + +/* + * Overall Enclosure Status + */ +typedef unsigned char ses_encstat; + +/* + * Object Status + */ +typedef struct { + unsigned int obj_id; + unsigned char cstat[4]; +} ses_objstat; + +/* Summary SES Status Defines, Common Status Codes */ +#define SES_OBJSTAT_UNSUPPORTED 0 +#define SES_OBJSTAT_OK 1 +#define SES_OBJSTAT_CRIT 2 +#define SES_OBJSTAT_NONCRIT 3 +#define SES_OBJSTAT_UNRECOV 4 +#define SES_OBJSTAT_NOTINSTALLED 5 +#define SES_OBJSTAT_UNKNOWN 6 +#define SES_OBJSTAT_NOTAVAIL 7 + +/* + * For control pages, cstat[0] is the same for the + * enclosure and is common across all device types. + * + * If SESCTL_CSEL is set, then PRDFAIL, DISABLE and RSTSWAP + * are checked, otherwise bits that are specific to the device + * type in the other 3 bytes of cstat or checked. + */ +#define SESCTL_CSEL 0x80 +#define SESCTL_PRDFAIL 0x40 +#define SESCTL_DISABLE 0x20 +#define SESCTL_RSTSWAP 0x10 + + +/* Control bits, Device Elements, byte 2 */ +#define SESCTL_DRVLCK 0x40 /* "DO NOT REMOVE" */ +#define SESCTL_RQSINS 0x08 /* RQST INSERT */ +#define SESCTL_RQSRMV 0x04 /* RQST REMOVE */ +#define SESCTL_RQSID 0x02 /* RQST IDENT */ +/* Control bits, Device Elements, byte 3 */ +#define SESCTL_RQSFLT 0x20 /* RQST FAULT */ +#define SESCTL_DEVOFF 0x10 /* DEVICE OFF */ + +/* Control bits, Generic, byte 3 */ +#define SESCTL_RQSTFAIL 0x40 +#define SESCTL_RQSTON 0x20 + +#endif /* Legacy API compatibility */ + /*========================== Field Extraction Macros =========================*/ #define MK_ENUM(S, F, SUFFIX) S ## _ ## F ## SUFFIX @@ -92,7 +191,7 @@ ses_page_length(const struct ses_page_hdr *hdr) * code field. */ return (scsi_2btoul(hdr->length) - + offsetof(struct ses_page_hdr, gen_code)); + + __offsetof(struct ses_page_hdr, gen_code)); } /*============= SCSI ENC Configuration Diagnostic Page Structures ============*/ diff --git a/usr.bin/kdump/mkioctls b/usr.bin/kdump/mkioctls index 0dac68425b0..eefe14cce1a 100644 --- a/usr.bin/kdump/mkioctls +++ b/usr.bin/kdump/mkioctls @@ -63,8 +63,6 @@ BEGIN { print "#include " print "#include " print "#include " - print "#include " - print "#include " print "" print ioctl_includes print "" From aed0e7df6bdb24145e1473951c0a727c6ffb018b Mon Sep 17 00:00:00 2001 From: Alexander Motin Date: Wed, 16 May 2012 00:29:26 +0000 Subject: [PATCH 43/89] Add missing sx_destroy(). --- sys/cam/scsi/scsi_enc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sys/cam/scsi/scsi_enc.c b/sys/cam/scsi/scsi_enc.c index 03e43bce56c..7b53bc427db 100644 --- a/sys/cam/scsi/scsi_enc.c +++ b/sys/cam/scsi/scsi_enc.c @@ -172,6 +172,7 @@ enc_dtor(struct cam_periph *periph) enc->enc_boot_hold_ch.ich_func = NULL; } + sx_destroy(&enc->enc_cache_lock); ENC_FREE(enc); } From 42df813953adb94ca59e65b2bff4119ed205a952 Mon Sep 17 00:00:00 2001 From: "Justin T. Gibbs" Date: Wed, 30 May 2012 00:03:33 +0000 Subject: [PATCH 44/89] cddl/sbin/zfsd/Makefile: Remove commented out make directives left over from the Makefile reference used as a template for zfsd's Makefile. Sponsored by: Spectra Logic Corporation --- cddl/sbin/zfsd/Makefile | 7 ------- 1 file changed, 7 deletions(-) diff --git a/cddl/sbin/zfsd/Makefile b/cddl/sbin/zfsd/Makefile index 7d9519c4928..7070726408b 100644 --- a/cddl/sbin/zfsd/Makefile +++ b/cddl/sbin/zfsd/Makefile @@ -30,17 +30,10 @@ INCFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common/sys CFLAGS= -g -DNEED_SOLARIS_BOOLEAN ${INCFLAGS} -#NO_SHARED?= YES - DPADD= ${LIBZFS} ${LIBUTIL} ${LIBGEOM} ${LIBBSDXML} ${LIBSBUF} \ ${LIBNVPAIR} ${LIBUUTIL} LDADD= -lzfs -lutil -lgeom -lbsdxml -lsbuf -lnvpair -luutil -#DPADD= ${LIBAVL} ${LIBZFS} ${LIBGEOM} ${LIBBSDXML} ${LIBSBUF} \ -# ${LIBM} ${LIBNVPAIR} ${LIBUUTIL} ${LIBUTIL} -#LDADD= -lavl -lzfs -lgeom -lbsdxml -lsbuf \ -# -lm -lnvpair -luutil -lutil - cscope: find ${.CURDIR} -type f -a \( -name "*.[ch]" -o -name "*.cc" \) \ > ${.CURDIR}/cscope.files From ffa28c2eb31e45b2b2c8d0b37faed5e3d9aa1a59 Mon Sep 17 00:00:00 2001 From: "Justin T. Gibbs" Date: Wed, 30 May 2012 00:14:13 +0000 Subject: [PATCH 45/89] Fix a race condition between the notification of a ZFS vdev's removal and the removal being visible in the pool's configuration. sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c: Defer the generation of a vdev removal event until the spa's async remove task has run and updated the in-core state for the vdev being removed. This ensures that a userspace agent responding to this event can refresh its copy of the pool configuration and see that the device has been removed. Submitted by: Alan Somers (Spectra Logic Corporation) Sponsored by: Spectra Logic Corporation --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c | 2 ++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c index af42de24a6b..d421d4b0ca9 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c @@ -5112,6 +5112,8 @@ spa_async_remove(spa_t *spa, vdev_t *vd) vd->vdev_stat.vs_checksum_errors = 0; vdev_state_dirty(vd->vdev_top); + /* Tell userspace that the vdev is gone. */ + zfs_post_remove(spa, vd); } for (int c = 0; c < vd->vdev_children; c++) diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c index eb16d3e6e7e..6793391cbbe 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c @@ -83,7 +83,6 @@ vdev_geom_orphan(struct g_consumer *cp) * async removal support to invoke a close on this * vdev once it is safe to do so. */ - zfs_post_remove(vd->vdev_spa, vd); vd->vdev_remove_wanted = B_TRUE; spa_async_request(vd->vdev_spa, SPA_ASYNC_REMOVE); } From 1ddacabc249692aa0d86600774406331f27b4fe8 Mon Sep 17 00:00:00 2001 From: "Justin T. Gibbs" Date: Wed, 30 May 2012 00:27:12 +0000 Subject: [PATCH 46/89] Properly terminate truncated events with a newline (end of event token), and discard any data that exceeds zfsd's MAX_EVENT_SIZE limit until zfsd is resynchronized with the event stream. This resolves issues (de)serializing CaseFiles with truncated events. cddl/sbin/zfsd/case_file.cc: Do not assume that deserialization of all events from a case file will be successful. Just skip over unparsable events. cddl/sbin/zfsd/zfsd.cc: cddl/sbin/zfsd/zfsd.h: o In EventBuffer::ExtractEvent(), remove code and a comment dealing with "start tokens" and "end tokens" sharing characters. In the normal case of untruncated events, the event terminator is present and distinct from the start token. Devd immediately closes our unix domain socket if it cannot write a complete event, which means a start token for a new event after a truncted event cannot happen. o In EventBuffer::ExtractEvent(), remove dead code that tested for end of event tokens other than '\n'. Due to the way that strcspn() operates, this cannot happen. o Add stateful resynchronization to the EventBuffer class. EventBuffer can get out of synch whenever it truncates an incoming event due to its internal event size limit. The original code dealt with this issue by looking for start tokens. Since start tokens may also be valid within an event body, the only fully safe option is to discard data until the end of event token (which can't occur within an event) is read. o When truncating an event, properly terminate it so that when EventBuffer is used for CaseFile deserialization the input data is properly formed. o EventBuffer::UnParsed() and EventBuffer::NextEventMaxLen() are const. Enforce this via the const keyword. Sponsored by: Spectra Logic Corporation --- cddl/sbin/zfsd/case_file.cc | 3 ++- cddl/sbin/zfsd/zfsd.cc | 39 +++++++++++++++++-------------------- cddl/sbin/zfsd/zfsd.h | 11 +++++++---- 3 files changed, 27 insertions(+), 26 deletions(-) diff --git a/cddl/sbin/zfsd/case_file.cc b/cddl/sbin/zfsd/case_file.cc index 8d151b78ab0..2838dba15d4 100644 --- a/cddl/sbin/zfsd/case_file.cc +++ b/cddl/sbin/zfsd/case_file.cc @@ -531,7 +531,8 @@ CaseFile::DeSerializeFile(const char *fileName) EventBuffer eventBuffer(fd); while (eventBuffer.ExtractEvent(evString)) { DevCtlEvent *event(DevCtlEvent::CreateEvent(evString)); - caseFile->m_events.push_back(event); + if (event != NULL) + caseFile->m_events.push_back(event); } close(fd); } catch (const ParseException &exp) { diff --git a/cddl/sbin/zfsd/zfsd.cc b/cddl/sbin/zfsd/zfsd.cc index 326932c1176..404e760e702 100644 --- a/cddl/sbin/zfsd/zfsd.cc +++ b/cddl/sbin/zfsd/zfsd.cc @@ -108,7 +108,8 @@ EventBuffer::EventBuffer(int fd) : m_fd(fd), m_validLen(0), m_parsedLen(0), - m_nextEventOffset(0) + m_nextEventOffset(0), + m_synchronized(true) { } @@ -127,24 +128,18 @@ EventBuffer::ExtractEvent(string &eventString) continue; } - char *nextEvent(m_buf + m_nextEventOffset); - size_t startLen(strcspn(nextEvent, s_eventStartTokens)); - bool aligned(startLen == 0); - if (aligned == false) { - syslog(LOG_WARNING, - "Re-synchronizing with devd event stream"); - m_nextEventOffset += startLen; + char *nextEvent(m_buf + m_nextEventOffset); + bool truncated(true); + size_t eventLen(strcspn(nextEvent, s_eventEndTokens)); + + if (!m_synchronized) { + /* Discard data until an end token is read. */ + if (nextEvent[eventLen] != '\0') + m_synchronized = true; + m_nextEventOffset += eventLen; m_parsedLen = m_nextEventOffset; continue; - } - - /* - * Start tokens may be end tokens too, so skip the start - * token when trying to find the end of the event. - */ - bool truncated(true); - size_t eventLen(strcspn(nextEvent + 1, s_eventEndTokens) + 1); - if (nextEvent[eventLen] == '\0') { + } else if (nextEvent[eventLen] == '\0') { m_parsedLen += eventLen; if (m_parsedLen < MAX_EVENT_SIZE) { @@ -156,9 +151,6 @@ EventBuffer::ExtractEvent(string &eventString) } syslog(LOG_WARNING, "Event exceeds event size limit of %d bytes."); - } else if (nextEvent[eventLen] != '\n') { - syslog(LOG_WARNING, - "Improperly terminated event encountered."); } else { /* * Include the normal terminator in the extracted @@ -175,8 +167,13 @@ EventBuffer::ExtractEvent(string &eventString) if (truncated) { size_t fieldEnd; + /* Break cleanly at the end of a key<=>value pair. */ fieldEnd = eventString.find_last_of(s_keyPairSepTokens); - eventString.erase(fieldEnd); + if (fieldEnd != string::npos) + eventString.erase(fieldEnd); + eventString += '\n'; + + m_synchronized = false; syslog(LOG_WARNING, "Truncated %d characters from event.", eventLen - fieldEnd); diff --git a/cddl/sbin/zfsd/zfsd.h b/cddl/sbin/zfsd/zfsd.h index 391aa146119..d0766b07e75 100644 --- a/cddl/sbin/zfsd/zfsd.h +++ b/cddl/sbin/zfsd/zfsd.h @@ -143,10 +143,10 @@ private: }; /** The amount of data in m_buf we have yet to look at. */ - size_t UnParsed(); + size_t UnParsed() const; /** The amount of data in m_buf available for the next event. */ - size_t NextEventMaxLen(); + size_t NextEventMaxLen() const; /** Fill the event buffer with event data from Devd. */ bool Fill(); @@ -174,17 +174,20 @@ private: /** Offset to the start token of the next event. */ size_t m_nextEventOffset; + + /** The EventBuffer is aligned and tracking event records. */ + bool m_synchronized; }; //- EventBuffer Inline Private Methods ----------------------------------------- inline size_t -EventBuffer::UnParsed() +EventBuffer::UnParsed() const { return (m_validLen - m_parsedLen); } inline size_t -EventBuffer::NextEventMaxLen() +EventBuffer::NextEventMaxLen() const { return (m_validLen - m_nextEventOffset); } From e74ed7c770cb551cc66199b0ae7d1e70bfc400a9 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Fri, 11 Oct 2013 21:24:33 +0000 Subject: [PATCH 47/89] Miscellaneous bug fixes in zfsd * Created a new abstract Reader class that provides a common interface to read from both file descriptors and std:istreams, implemented as separate derived classes FDReader and IstreamReader. Changed EventBuffer to get its input from a Reader. Changed CaseFile::DeSerializeFile to open case files as ifstreams instead of as file descriptors. * Eliminated ZFSDaemon::ConnectToDevd's call to set the socket to nonblocking mode. Instead, use an API that never attempts to read more data than is available. EventBuffer::Fill() no longer ignore EAGAIN, which shouldn't happen now that the socket blocks. * Fix two file leaks * Fix file leak in CaseFile::Serialize(): open() without close() * Fix potential file leak in DeSerializeFile(): The exception handler for ZfsdException did not close the open case file. This code path was probably not exercised. * Fix four memory leaks * CaseFile::DeSerialize() was freeing its dirent structure when it had entries, but not when the directory was empty * zfsd did not purge case files on exit. This isn't really a problem because the memory won't leak until the program quits, but it clutters valgrind's output. * ZfsDaemon::RescanSystem() wasn't deleting newly created events after processing them. * DeSerializeFile(): an ifstream was closed() instead of deleted. * Miscellaneous style changes. Submitted by: asomers Approved by: ken (mentor) Sponsored by: Spectra Logic Corporation --- cddl/sbin/zfsd/case_file.cc | 27 +++++-- cddl/sbin/zfsd/zfsd.cc | 74 +++++++++++++----- cddl/sbin/zfsd/zfsd.h | 147 ++++++++++++++++++++++++++++++++---- 3 files changed, 208 insertions(+), 40 deletions(-) diff --git a/cddl/sbin/zfsd/case_file.cc b/cddl/sbin/zfsd/case_file.cc index 2838dba15d4..9e9e6577c2a 100644 --- a/cddl/sbin/zfsd/case_file.cc +++ b/cddl/sbin/zfsd/case_file.cc @@ -40,6 +40,7 @@ */ #include #include +#include #include #include #include @@ -53,6 +54,7 @@ /*============================ Namespace Control =============================*/ using std::auto_ptr; using std::hex; +using std::ifstream; using std::stringstream; using std::setfill; using std::setw; @@ -116,8 +118,12 @@ CaseFile::DeSerialize() int numCaseFiles(scandir(s_caseFilePath.c_str(), &caseFiles, DeSerializeSelector, /*compar*/NULL)); - if (numCaseFiles == 0 || numCaseFiles == -1) + if (numCaseFiles == -1) return; + if (numCaseFiles == 0) { + free(caseFiles); + return; + } for (int i = 0; i < numCaseFiles; i++) { @@ -472,7 +478,7 @@ CaseFile::DeSerializeFile(const char *fileName) string evString; CaseFile *existingCaseFile(NULL); CaseFile *caseFile(NULL); - int fd(-1); + ifstream *caseStream(NULL); try { uintmax_t poolGUID; @@ -519,28 +525,30 @@ CaseFile::DeSerializeFile(const char *fileName) */ caseFile = new CaseFile(Vdev(zpl.front(), vdevConf)); } - - fd = open(fullName.c_str(), O_RDONLY); - if (fd == -1) { + + caseStream = new ifstream(fullName.c_str()); + if ( (caseStream == NULL) || (! *caseStream) ) { throw ZfsdException("CaseFile::DeSerialize: Unable to " "read %s.\n", fileName); return; } + IstreamReader caseReader(caseStream); /* Re-load EventData */ - EventBuffer eventBuffer(fd); + EventBuffer eventBuffer(caseReader); while (eventBuffer.ExtractEvent(evString)) { DevCtlEvent *event(DevCtlEvent::CreateEvent(evString)); if (event != NULL) caseFile->m_events.push_back(event); } - close(fd); + delete caseStream; } catch (const ParseException &exp) { exp.Log(evString); if (caseFile != existingCaseFile) delete caseFile; - close(fd); + if (caseStream) + delete caseStream; /* * Since we can't parse the file, unlink it so we don't @@ -552,6 +560,8 @@ CaseFile::DeSerializeFile(const char *fileName) zfsException.Log(); if (caseFile != existingCaseFile) delete caseFile; + if (caseStream) + delete caseStream; } } @@ -632,6 +642,7 @@ CaseFile::Serialize() write(fd, eventString.c_str(), eventString.length()); } + close(fd); } void diff --git a/cddl/sbin/zfsd/zfsd.cc b/cddl/sbin/zfsd/zfsd.cc index 404e760e702..85430c3b9ba 100644 --- a/cddl/sbin/zfsd/zfsd.cc +++ b/cddl/sbin/zfsd/zfsd.cc @@ -43,6 +43,7 @@ #include #include +#include #include #include #include @@ -86,6 +87,32 @@ const char g_devdSock[] = "/var/run/devd.pipe"; int g_debug = 0; libzfs_handle_t *g_zfsHandle; +/*-------------------------------- FDReader -------------------------------*/ +//- FDReader Public Methods ---------------------------------------------------- +size_t +FDReader::in_avail() const +{ + int bytes; + if (ioctl(m_fd, FIONREAD, &bytes)) { + syslog(LOG_ERR, "ioctl FIONREAD: %s", strerror(errno)); + return (0); + } + return (bytes); +} + + +/*-------------------------------- IstreamReader ---------------------------*/ +//- IstreamReader Public Methods ---------------------------------------------- +ssize_t +IstreamReader::read(char* buf, size_t count) +{ + m_stream->read(buf, count); + if (m_stream->fail()) + return (-1); + return (m_stream->gcount()); +} + + /*-------------------------------- EventBuffer -------------------------------*/ //- EventBuffer Static Data ---------------------------------------------------- /** @@ -104,8 +131,8 @@ const char EventBuffer::s_eventEndTokens[] = "\n"; const char EventBuffer::s_keyPairSepTokens[] = " \t\n"; //- EventBuffer Public Methods ------------------------------------------------- -EventBuffer::EventBuffer(int fd) - : m_fd(fd), +EventBuffer::EventBuffer(Reader& reader) + : m_reader(reader), m_validLen(0), m_parsedLen(0), m_nextEventOffset(0), @@ -187,7 +214,8 @@ EventBuffer::ExtractEvent(string &eventString) bool EventBuffer::Fill() { - ssize_t result; + size_t avail; + ssize_t consumed(0); /* Compact the buffer. */ if (m_nextEventOffset != 0) { @@ -199,19 +227,26 @@ EventBuffer::Fill() } /* Fill any empty space. */ - result = read(m_fd, m_buf + m_validLen, MAX_READ_SIZE - m_validLen); - if (result == -1) { - if (errno == EINTR || errno == EAGAIN) { - return (false); - } else { - err(1, "Read from devd socket failed"); + avail = m_reader.in_avail(); + if (avail) { + size_t want; + + want = std::min(avail, MAX_READ_SIZE - m_validLen); + consumed = m_reader.read(m_buf + m_validLen, want); + if (consumed == -1) { + if (errno == EINTR) { + return (false); + } else { + err(1, "EventBuffer::Fill(): Read failed"); + } } } - m_validLen += result; + + m_validLen += consumed; /* Guarantee our buffer is always NUL terminated. */ m_buf[m_validLen] = '\0'; - return (result > 0); + return (consumed > 0); } /*--------------------------------- ZfsDaemon --------------------------------*/ @@ -220,6 +255,7 @@ bool ZfsDaemon::s_logCaseFiles; bool ZfsDaemon::s_terminateEventLoop; char ZfsDaemon::s_pidFilePath[] = "/var/run/zfsd.pid"; pidfh *ZfsDaemon::s_pidFH; +FDReader* ZfsDaemon::s_reader; int ZfsDaemon::s_devdSockFD = -1; int ZfsDaemon::s_signalPipeFD[2]; bool ZfsDaemon::s_systemRescanRequested(false); @@ -306,6 +342,7 @@ ZfsDaemon::Init() void ZfsDaemon::Fini() { + PurgeCaseFiles(); ClosePIDFile(); } @@ -387,10 +424,9 @@ ZfsDaemon::ConnectToDevd() return (false); } - /* Don't block on reads. */ - if (fcntl(s_devdSockFD, F_SETFL, O_NONBLOCK) == -1) - err(1, "Unable to enable nonblocking behavior on devd socket"); + /* Connect the stream to the file descriptor */ + s_reader = new FDReader(s_devdSockFD); syslog(LOG_INFO, "Connection to devd successful"); return (true); } @@ -398,7 +434,10 @@ ZfsDaemon::ConnectToDevd() void ZfsDaemon::DisconnectFromDevd() { + delete s_reader; + s_reader = NULL; close(s_devdSockFD); + s_devdSockFD = -1; } void @@ -448,8 +487,8 @@ ZfsDaemon::FlushEvents() { char discardBuf[256]; - while (read(s_devdSockFD, discardBuf, sizeof(discardBuf)) > 0) - ; + while (s_reader->in_avail()) + s_reader->read(discardBuf, sizeof(discardBuf)); } bool @@ -528,6 +567,7 @@ ZfsDaemon::RescanSystem() event = DevCtlEvent::CreateEvent(evString); if (event != NULL) event->Process(); + delete event; } } } @@ -561,7 +601,7 @@ ZfsDaemon::DetectMissedEvents() void ZfsDaemon::EventLoop() { - EventBuffer eventBuffer(s_devdSockFD); + EventBuffer eventBuffer(*s_reader); while (s_terminateEventLoop == false) { struct pollfd fds[2]; diff --git a/cddl/sbin/zfsd/zfsd.h b/cddl/sbin/zfsd/zfsd.h index d0766b07e75..9d34d8ceeb8 100644 --- a/cddl/sbin/zfsd/zfsd.h +++ b/cddl/sbin/zfsd/zfsd.h @@ -42,6 +42,7 @@ #define _ZFSD_H_ #include +#include #include #include #include @@ -57,6 +58,7 @@ using std::auto_ptr; using std::map; using std::pair; +using std::istream; using std::string; /*================================ Global Data ===============================*/ @@ -74,21 +76,131 @@ typedef int LeafIterFunc(zpool_handle_t *, nvlist_t *, void *); #define NUM_ELEMENTS(x) (sizeof(x) / sizeof(*x)) /*============================= Class Definitions ============================*/ + +/*-------------------------------- Reader -------------------------------*/ +/** + * \brief A class that presents a common interface to both file descriptors and + * istreams . + * + * Standard C++ provides no way to create an iostream from a file descriptor or + * a FILE. The GNU, Apache, HPUX, and Solaris C++ libraries all provide + * non-standard ways to construct such a stream using similar semantics, but + * LLVM does not. Therefore this class is needed to ensure that zfsd can + * compile under LLVM. This class supports only the functionality needed by + * ZFSD; it does not implement the iostream API. + */ +class Reader +{ +public: + /** + * \brief Return the number of bytes immediately available for reading + */ + virtual size_t in_avail() const = 0; + + /** + * \brief Reads up to count bytes + * + * Whether this call blocks depends on the underlying input source. + * On error, -1 is returned, and errno will be set by the underlying + * source. + * + * \param buf Destination for the data + * \param count Maximum amount of data to read + * \returns Amount of data that was actually read + */ + virtual ssize_t read(char* buf, size_t count) = 0; +}; + + +/*-------------------------------- FDReader -------------------------------*/ +/** + * \brief Specialization of Reader that uses a file descriptor + */ +class FDReader : public Reader +{ +public: + /** + * \brief Constructor + * + * \param fd An open file descriptor. It will not be garbage + * collected by the destructor. + */ + FDReader(int fd); + + virtual size_t in_avail() const; + + virtual ssize_t read(char* buf, size_t count); + +protected: + /** Copy of the underlying file descriptor */ + int m_fd; +}; + +//- FDReader Inline Public Methods ----------------------------------------- +inline FDReader::FDReader(int fd) + : m_fd(fd) +{ +} + +inline ssize_t +FDReader::read(char* buf, size_t count) +{ + return (::read(m_fd, buf, count)); +} + + +/*-------------------------------- IstreamReader------------------------------*/ +/** + * \brief Specialization of Reader that uses a std::istream + */ +class IstreamReader : public Reader +{ +public: + /** + * Constructor + * + * \param stream Pointer to an open istream. It will not be + * garbage collected by the destructor. + */ + IstreamReader(istream* stream); + + virtual size_t in_avail() const; + + virtual ssize_t read(char* buf, size_t count); + +protected: + /** Copy of the underlying stream */ + istream* m_stream; +}; + +//- IstreamReader Inline Public Methods ---------------------------------------- +inline IstreamReader::IstreamReader(istream* stream) + : m_stream(stream) +{ +} + +inline size_t +IstreamReader::in_avail() const +{ + return (m_stream->rdbuf()->in_avail()); +} + + /*-------------------------------- EventBuffer -------------------------------*/ /** - * \brief Class buffering event data from Devd and splitting it - * into individual event strings. + * \brief Class buffering event data from Devd or a similar source and + * splitting it into individual event strings. * - * Users of this class initialize it with the file descriptor associated - * with the unix domain socket connection with devd. The lifetime of - * an EventBuffer instance should match that of the file descriptor passed - * to it. This is required as data from partially received events is - * retained in the EventBuffer in order to allow reconstruction of these - * events across multiple reads of the Devd file descriptor. + * Users of this class initialize it with a Reader associated with the unix + * domain socket connection with devd or a compatible source. The lifetime of + * an EventBuffer instance should match that of the Reader passed to it. This + * is required as data from partially received events is retained in the + * EventBuffer in order to allow reconstruction of these events across multiple + * reads of the stream. * - * Once the program determines that the Devd file descriptor is ready - * for reading, the EventBuffer::ExtractEvent() should be called in a - * loop until the method returns false. + * Once the program determines that the Reader is ready for reading, the + * EventBuffer::ExtractEvent() should be called in a loop until the method + * returns false. */ class EventBuffer { @@ -96,9 +208,9 @@ public: /** * Constructor * - * \param fd The file descriptor on which to buffer/parse event data. + * \param reader The data source on which to buffer/parse event data. */ - EventBuffer(int fd); + EventBuffer(Reader& reader); /** * Pull a single event string out of the event buffer. @@ -163,8 +275,8 @@ private: /** Temporary space for event data during our parsing. */ char m_buf[EVENT_BUFSIZE]; - /** Copy of the file descriptor linked to devd's domain socket. */ - int m_fd; + /** Reference to the reader linked to devd's domain socket. */ + Reader& m_reader; /** Valid bytes in m_buf. */ size_t m_validLen; @@ -361,6 +473,11 @@ private: */ static int s_devdSockFD; + /** + * Reader object used by the EventBuffer + */ + static FDReader* s_reader; + /** * Pipe file descriptors used to close races with our * signal handlers. From a25397f0f1a72b784aab54f2dfd1ebecf2fa988c Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Fri, 11 Oct 2013 21:41:07 +0000 Subject: [PATCH 48/89] cddl/sbin/zfsd/callout.cc Restore the doxygen file header that was accidentally left out of rev 224922. No functional changes. Submitted by: gibbs Approved by: ken (mentor) Sponsored by: Spectra Logic Corporation --- cddl/sbin/zfsd/callout.cc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cddl/sbin/zfsd/callout.cc b/cddl/sbin/zfsd/callout.cc index 1ef16317d3a..bac6208b300 100644 --- a/cddl/sbin/zfsd/callout.cc +++ b/cddl/sbin/zfsd/callout.cc @@ -30,6 +30,13 @@ * Authors: Justin T. Gibbs (Spectra Logic Corporation) */ +/** + * \file callout.cc + * + * \brief Implementation of the Callout class - multi-client + * timer services built on top of the POSIX interval timer. + */ + #include #include From c04a982ed55c196c9b51cf86e4b58ae1a1919cef Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Fri, 11 Oct 2013 21:50:04 +0000 Subject: [PATCH 49/89] Fix a bug in zfsd whereby it won't degrade or fault a drive if the devd socket gets HUPed after the drive's last error. The key was to not discard case files when the socket gets HUPed, because doing so drops their callouts. * When calling CaseFile::PurgeAll (such as happens after the DevD socket gets HUPed), serialize case files before purging them. That way, they can be read back in after the connection to devd is restored and proessing may continue. * Added Callout::TimeRemaining to calculate how much time until any given callout expires. * When registering callouts, compute the timeout value based on the event's timestamp, not the present time. * In CaseFile::Serialize, serialize both m_events and m_tentativeEvents. Tentative events will be prefixed with "tentative " in the serialized file. * In DeSerializeFile, push tentative events and regular events into the corresponding m_events or m_tentativeEvents, as appropriate. * ExtractEvents will timestamp each incoming event with a timestamp=seconds_since_epoch nvpair, if not already present. Added a DevCtlEvent::getTimestamp method to read it. * style: in ZfsDaemon::EventLoop changed fds->XXX to fds[0]. to emphase that fds is an array. * A few minor typo fixes in comments. Submitted by: asomers Approved by: ken (mentor) Sponsored by: Spectra Logic Corporation --- cddl/sbin/zfsd/callout.cc | 34 +++++++++ cddl/sbin/zfsd/callout.h | 11 +++ cddl/sbin/zfsd/case_file.cc | 129 +++++++++++++++++++++++++------- cddl/sbin/zfsd/case_file.h | 24 ++++-- cddl/sbin/zfsd/dev_ctl_event.cc | 17 +++++ cddl/sbin/zfsd/dev_ctl_event.h | 10 ++- cddl/sbin/zfsd/zfsd.cc | 22 +++++- 7 files changed, 209 insertions(+), 38 deletions(-) diff --git a/cddl/sbin/zfsd/callout.cc b/cddl/sbin/zfsd/callout.cc index bac6208b300..a16ad78f657 100644 --- a/cddl/sbin/zfsd/callout.cc +++ b/cddl/sbin/zfsd/callout.cc @@ -167,3 +167,37 @@ Callout::ExpireCallouts() setitimer(ITIMER_REAL, &timerval, NULL); } } + +timeval +Callout::TimeRemaining() const +{ + /* + * Outline: Add the m_interval for each callout in s_activeCallouts + * ahead of this, except for the first callout. Add to that the result + * of getitimer (That's because the first callout stores its original + * interval setting while the timer is ticking). + */ + itimerval timervalToAlarm; + timeval timeToExpiry; + std::list::iterator it; + + if (! IsPending() ) { + timeToExpiry.tv_sec = INT_MAX; + timeToExpiry.tv_usec = 999999; /*maximum normalized value*/ + return (timeToExpiry); + } + + timerclear(&timeToExpiry); + getitimer(ITIMER_REAL, &timervalToAlarm); + timeval& timeToAlarm = timervalToAlarm.it_value; + timeradd(&timeToExpiry, &timeToAlarm, &timeToExpiry); + + it =s_activeCallouts.begin(); + it++; /*skip the first callout in the list*/ + for (; it != s_activeCallouts.end(); it++) { + timeradd(&timeToExpiry, &(*it)->m_interval, &timeToExpiry); + if ((*it) == this) + break; + } + return (timeToExpiry); +} diff --git a/cddl/sbin/zfsd/callout.h b/cddl/sbin/zfsd/callout.h index 1c98f43d885..678ba402f71 100644 --- a/cddl/sbin/zfsd/callout.h +++ b/cddl/sbin/zfsd/callout.h @@ -117,6 +117,17 @@ public: */ bool Reset(const timeval &interval, CalloutFunc_t *func, void *arg); + /** + * \brief Calculate the remaining time until this Callout's timer + * expires. + * + * The return value will be slightly greater than the actual time to + * expiry. + * + * If the callout is not pending, returns INT_MAX. + */ + timeval TimeRemaining() const; + private: /** * All active callouts sorted by expiration time. The callout diff --git a/cddl/sbin/zfsd/case_file.cc b/cddl/sbin/zfsd/case_file.cc index 9e9e6577c2a..084dad40ec5 100644 --- a/cddl/sbin/zfsd/case_file.cc +++ b/cddl/sbin/zfsd/case_file.cc @@ -144,9 +144,17 @@ CaseFile::LogAll() void CaseFile::PurgeAll() { - /* CaseFiles remove themselves from this list on destruction. */ - while (s_activeCases.size() != 0) - delete s_activeCases.front(); + /* + * Serialize casefiles before deleting them so that they can be reread + * and revalidated during BuildCaseFiles. + * CaseFiles remove themselves from this list on destruction. + */ + while (s_activeCases.size() != 0) { + CaseFile *casefile = s_activeCases.front(); + casefile->Serialize(); + delete casefile; + } + } //- CaseFile Public Methods ---------------------------------------------------- @@ -388,9 +396,7 @@ CaseFile::ReEvaluate(const ZfsEvent &event) || event.Value("class") == "ereport.fs.zfs.checksum") { m_tentativeEvents.push_front(event.DeepCopy()); - if (!m_tentativeTimer.IsPending()) - m_tentativeTimer.Reset(s_removeGracePeriod, - OnGracePeriodEnded, this); + RegisterCallout(event); consumed = true; } @@ -399,6 +405,33 @@ CaseFile::ReEvaluate(const ZfsEvent &event) return (consumed || closed); } + +void +CaseFile::RegisterCallout(const DevCtlEvent &event) +{ + timeval now, countdown, elapsed, timestamp, zero, remaining; + gettimeofday(&now, 0); + timestamp = event.GetTimestamp(); + timersub(&now, ×tamp, &elapsed); + timersub(&s_removeGracePeriod, &elapsed, &countdown); + /* + * If countdown is <= zero, Reset the timer to the + * smallest positive time value instead + */ + timerclear(&zero); + if (timercmp(&countdown, &zero, <=)) { + timerclear(&countdown); + countdown.tv_usec = 1; + } + + remaining = m_tentativeTimer.TimeRemaining(); + + if (!m_tentativeTimer.IsPending() + || timercmp(&countdown, &remaining, <)) + m_tentativeTimer.Reset(countdown, OnGracePeriodEnded, this); +} + + bool CaseFile::CloseIfSolved() { @@ -478,7 +511,6 @@ CaseFile::DeSerializeFile(const char *fileName) string evString; CaseFile *existingCaseFile(NULL); CaseFile *caseFile(NULL); - ifstream *caseStream(NULL); try { uintmax_t poolGUID; @@ -511,8 +543,8 @@ CaseFile::DeSerializeFile(const char *fileName) .Find(vdevGUID)) == NULL) { /* * Either the pool no longer exists - * of this vdev is no longer a member of - * the pool. + * or this vdev is no longer a member of + * the pool. */ unlink(fullName.c_str()); return; @@ -526,29 +558,58 @@ CaseFile::DeSerializeFile(const char *fileName) caseFile = new CaseFile(Vdev(zpl.front(), vdevConf)); } - caseStream = new ifstream(fullName.c_str()); - if ( (caseStream == NULL) || (! *caseStream) ) { + ifstream caseStream(fullName.c_str()); + if (! caseStream) { throw ZfsdException("CaseFile::DeSerialize: Unable to " "read %s.\n", fileName); return; } - IstreamReader caseReader(caseStream); + stringstream fakeDevdSocket(stringstream::in + | stringstream::out); + IstreamReader caseReader(&fakeDevdSocket); /* Re-load EventData */ EventBuffer eventBuffer(caseReader); - while (eventBuffer.ExtractEvent(evString)) { - DevCtlEvent *event(DevCtlEvent::CreateEvent(evString)); - if (event != NULL) - caseFile->m_events.push_back(event); + caseStream >> std::noskipws >> std::ws; + while (!caseStream.eof()) { + /* + * Outline: + * read the beginning of a line and check it for + * "tentative". If found, discard "tentative". + * Shove into fakeDevdSocket. + * call ExtractEvent + * continue + */ + DevCtlEventList* destEvents; + string tentFlag("tentative "); + string line; + std::stringbuf lineBuf; + caseStream.get(lineBuf); + caseStream.ignore(); /*discard the newline character*/ + line = lineBuf.str(); + if (line.compare(0, tentFlag.size(), tentFlag) == 0) { + line.erase(0, tentFlag.size()); + destEvents = &caseFile->m_tentativeEvents; + } else { + destEvents = &caseFile->m_events; + } + fakeDevdSocket << line; + fakeDevdSocket << '\n'; + while (eventBuffer.ExtractEvent(evString)) { + DevCtlEvent *event(DevCtlEvent::CreateEvent( + evString)); + if (event != NULL) { + destEvents->push_back(event); + caseFile->RegisterCallout(*event); + } + } } - delete caseStream; + } catch (const ParseException &exp) { exp.Log(evString); if (caseFile != existingCaseFile) delete caseFile; - if (caseStream) - delete caseStream; /* * Since we can't parse the file, unlink it so we don't @@ -560,8 +621,6 @@ CaseFile::DeSerializeFile(const char *fileName) zfsException.Log(); if (caseFile != existingCaseFile) delete caseFile; - if (caseStream) - delete caseStream; } } @@ -614,6 +673,24 @@ CaseFile::PurgeTentativeEvents() m_tentativeEvents.clear(); } + +void +CaseFile::SerializeEvList(const DevCtlEventList events, int fd, + const char* prefix) const +{ + if (events.empty()) + return; + for (DevCtlEventList::const_iterator curEvent = events.begin(); + curEvent != events.end(); curEvent++) { + const string &eventString((*curEvent)->GetEventString()); + + if (prefix) + write(fd, prefix, strlen(prefix)); + write(fd, eventString.c_str(), eventString.length()); + } +} + + void CaseFile::Serialize() { @@ -625,7 +702,7 @@ CaseFile::Serialize() << "_vdev_" << VdevGUIDString() << ".case"; - if (m_events.empty()) { + if (m_events.empty() && m_tentativeEvents.empty()) { unlink(saveFile.str().c_str()); return; } @@ -636,12 +713,8 @@ CaseFile::Serialize() saveFile.str().c_str()); return; } - for (DevCtlEventList::const_iterator curEvent = m_events.begin(); - curEvent != m_events.end(); curEvent++) { - const string &eventString((*curEvent)->GetEventString()); - - write(fd, eventString.c_str(), eventString.length()); - } + SerializeEvList(m_events, fd); + SerializeEvList(m_tentativeEvents, fd, "tentative "); close(fd); } diff --git a/cddl/sbin/zfsd/case_file.h b/cddl/sbin/zfsd/case_file.h index ad0bb9c0887..91753a5310f 100644 --- a/cddl/sbin/zfsd/case_file.h +++ b/cddl/sbin/zfsd/case_file.h @@ -171,7 +171,12 @@ public: bool ReEvaluate(const ZfsEvent &event); /** - * \breif Close a case if it is no longer relevant. + * \brief Register an itimer callout for the given event, if necessary + */ + void RegisterCallout(const DevCtlEvent &event); + + /** + * \brief Close a case if it is no longer relevant. * * This method deals with cases tracking soft errors. Soft errors * will be discarded should a remove event occur within a short period @@ -210,12 +215,12 @@ protected: static int DeSerializeSelector(const struct dirent *dirEntry); /** - * \brief Given the name of a file containing a serialized CaseFile - * object, create/update an in-core CaseFile object + * \brief Given the name of a file containing serialized events from a + * CaseFile object, create/update an in-core CaseFile object * representing the serialized data. * - * \param fileName The name of a file containing a serialized - * CaseFile object. + * \param fileName The name of a file containing serialized events + * from a CaseFile object. */ static void DeSerializeFile(const char *fileName); @@ -248,6 +253,15 @@ protected: */ void Serialize(); + /** + * \brief Serializes the supplied event list and writes it to fd + * + * \param prefix If not NULL, this prefix will be prepended to + * every event in the file. + */ + void SerializeEvList(const DevCtlEventList events, int fd, + const char* prefix=NULL) const; + /** * \brief Unconditionally close a CaseFile. */ diff --git a/cddl/sbin/zfsd/dev_ctl_event.cc b/cddl/sbin/zfsd/dev_ctl_event.cc index be7dbba15a3..c869f4e033e 100644 --- a/cddl/sbin/zfsd/dev_ctl_event.cc +++ b/cddl/sbin/zfsd/dev_ctl_event.cc @@ -250,6 +250,23 @@ DevCtlEvent::Process() const { } +timeval +DevCtlEvent::GetTimestamp() const +{ + timeval tv_timestamp; + struct tm tm_timestamp; + + if ( ! Contains("timestamp") ) { + throw ZfsdException("Event contains no timestamp: %s", + m_eventString.c_str()); + } + strptime(Value(string("timestamp")).c_str(), "%s", &tm_timestamp); + tv_timestamp.tv_sec = mktime(&tm_timestamp); + tv_timestamp.tv_usec = 0; + return (tv_timestamp); +} + + //- DevCtlEvent Protected Methods ---------------------------------------------- DevCtlEvent::DevCtlEvent(Type type, NVPairMap &map, const string &eventString) : m_type(type), diff --git a/cddl/sbin/zfsd/dev_ctl_event.h b/cddl/sbin/zfsd/dev_ctl_event.h index 5bc076587b8..d5cfe3531d1 100644 --- a/cddl/sbin/zfsd/dev_ctl_event.h +++ b/cddl/sbin/zfsd/dev_ctl_event.h @@ -158,7 +158,7 @@ typedef map NVPairMap; * * All name => value data for events can be accessed via the Contains() * and Value() methods. name => value pairs for data not explicitly - * recieved as a a name => value pair are synthesized during parsing. For + * recieved as a name => value pair are synthesized during parsing. For * example, ATTACH and DETACH events have "device-name" and "parent" * name => value pairs added. */ @@ -276,6 +276,11 @@ public: */ virtual void Process() const; + /** + * Get the time that the event was created + */ + timeval GetTimestamp() const; + protected: /** Table entries used to map a type to a user friendly string. */ struct EventTypeRecord @@ -353,7 +358,8 @@ private: /** * Ingest event data from the supplied string. * - * \param eventString The string of devd event data to parse. + * \param[in] eventString The string of devd event data to parse. + * \param[out] nvpairs Returns the parsed data */ static void ParseEventString(Type type, const string &eventString, NVPairMap &nvpairs); diff --git a/cddl/sbin/zfsd/zfsd.cc b/cddl/sbin/zfsd/zfsd.cc index 85430c3b9ba..4f57f14d662 100644 --- a/cddl/sbin/zfsd/zfsd.cc +++ b/cddl/sbin/zfsd/zfsd.cc @@ -143,6 +143,11 @@ EventBuffer::EventBuffer(Reader& reader) bool EventBuffer::ExtractEvent(string &eventString) { + stringstream tsField; + timeval now; + + gettimeofday(&now, NULL); + tsField << " timestamp=" << now.tv_sec; while (UnParsed() > 0 || Fill()) { @@ -160,7 +165,7 @@ EventBuffer::ExtractEvent(string &eventString) size_t eventLen(strcspn(nextEvent, s_eventEndTokens)); if (!m_synchronized) { - /* Discard data until an end token is read. */ + /* Discard data until an end token is read. */ if (nextEvent[eventLen] != '\0') m_synchronized = true; m_nextEventOffset += eventLen; @@ -205,6 +210,17 @@ EventBuffer::ExtractEvent(string &eventString) "Truncated %d characters from event.", eventLen - fieldEnd); } + + /* + * Add a timestamp as the final field of the event if it is + * not already present. + */ + if ( eventString.find("timestamp=") == string::npos) { + eventString.insert( + eventString.find_last_not_of('\n') + 1, + tsField.str()); + } + return (true); } return (false); @@ -653,13 +669,13 @@ ZfsDaemon::EventLoop() RescanSystem(); } - if ((fds->revents & POLLERR) != 0) { + if ((fds[0].revents & POLLERR) != 0) { /* Try reconnecting. */ syslog(LOG_INFO, "Error on socket. Disconnecting."); break; } - if ((fds->revents & POLLHUP) != 0) { + if ((fds[0].revents & POLLHUP) != 0) { /* Try reconnecting. */ syslog(LOG_INFO, "Hup on socket. Disconnecting."); break; From 9d8b459c94f6979d52abde66a152eb6c10bc361c Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Fri, 11 Oct 2013 21:55:12 +0000 Subject: [PATCH 50/89] cddl/sbin/zfsd/callout.cc: cddl/sbin/zfsd/callout.h: cddl/sbin/zfsd/case_file.cc: cddl/sbin/zfsd/case_file.h: cddl/sbin/zfsd/dev_ctl_event.h: cddl/sbin/zfsd/zfsd.cc: cddl/sbin/zfsd/zfsd.h: Style and spelling in comments fixes. No functional changes. Submitted by: gibbs Approved by: ken (mentor) Sponsored by: Spectra Logic Corporation --- cddl/sbin/zfsd/callout.cc | 2 +- cddl/sbin/zfsd/callout.h | 6 ++--- cddl/sbin/zfsd/case_file.cc | 27 ++++++++++------------ cddl/sbin/zfsd/case_file.h | 19 +++++++-------- cddl/sbin/zfsd/dev_ctl_event.h | 22 +++++++++--------- cddl/sbin/zfsd/zfsd.cc | 8 +++---- cddl/sbin/zfsd/zfsd.h | 42 +++++++++++++++++----------------- 7 files changed, 62 insertions(+), 64 deletions(-) diff --git a/cddl/sbin/zfsd/callout.cc b/cddl/sbin/zfsd/callout.cc index a16ad78f657..07dc4dbf981 100644 --- a/cddl/sbin/zfsd/callout.cc +++ b/cddl/sbin/zfsd/callout.cc @@ -181,7 +181,7 @@ Callout::TimeRemaining() const timeval timeToExpiry; std::list::iterator it; - if (! IsPending() ) { + if (!IsPending()) { timeToExpiry.tv_sec = INT_MAX; timeToExpiry.tv_usec = 999999; /*maximum normalized value*/ return (timeToExpiry); diff --git a/cddl/sbin/zfsd/callout.h b/cddl/sbin/zfsd/callout.h index 678ba402f71..897bcea62f5 100644 --- a/cddl/sbin/zfsd/callout.h +++ b/cddl/sbin/zfsd/callout.h @@ -84,7 +84,7 @@ public: Callout(); /** - * returns true if callout has not been stopped, + * Returns true if callout has not been stopped, * or deactivated since the last time the callout was * reset. */ @@ -105,7 +105,7 @@ public: * * \param interval Timeval indicating the time which must elapse * before this callout fires. - * \param func Pointer to the callback funtion + * \param func Pointer to the callback funtion * \param arg Argument pointer to pass to callback function * * \return Cancelation status. @@ -119,7 +119,7 @@ public: /** * \brief Calculate the remaining time until this Callout's timer - * expires. + * expires. * * The return value will be slightly greater than the actual time to * expiry. diff --git a/cddl/sbin/zfsd/case_file.cc b/cddl/sbin/zfsd/case_file.cc index 084dad40ec5..5edadbe3494 100644 --- a/cddl/sbin/zfsd/case_file.cc +++ b/cddl/sbin/zfsd/case_file.cc @@ -220,7 +220,7 @@ CaseFile::ReEvaluate(const string &devPath, const string &physPath, Vdev *vdev) * use a newly inserted spare to replace a degraded * or faulted device. */ - return (false); + return (/*consumed*/false); } if (vdev != NULL @@ -255,17 +255,17 @@ CaseFile::ReEvaluate(const string &devPath, const string &physPath, Vdev *vdev) PoolGUIDString().c_str(), VdevGUIDString().c_str(), zpool_state_to_name(VdevState(), VDEV_AUX_NONE)); - return (false); + return (/*consumed*/false); } if (PhysicalPath().empty()) { syslog(LOG_INFO, - "CaseFile(%s:%s:%s): No vdev physical path information. " + "CaseFile(%s:%s:%s): No physical path information. " "Ignoring device insertion.\n", PoolGUIDString().c_str(), VdevGUIDString().c_str(), zpool_state_to_name(VdevState(), VDEV_AUX_NONE)); - return (false); + return (/*consumed*/false); } if (physPath != PhysicalPath()) { @@ -275,7 +275,7 @@ CaseFile::ReEvaluate(const string &devPath, const string &physPath, Vdev *vdev) PoolGUIDString().c_str(), VdevGUIDString().c_str(), zpool_state_to_name(VdevState(), VDEV_AUX_NONE)); - return (false); + return (/*consumed*/false); } /* Write a label on the newly inserted disk. */ @@ -315,7 +315,7 @@ CaseFile::ReEvaluate(const string &devPath, const string &physPath, Vdev *vdev) zpool_get_name(pool), VdevGUIDString().c_str()); nvlist_free(newvd); nvlist_free(nvroot); - return (1); + return (/*consumed*/true); } /* Data was copied when added to the root vdev. */ @@ -405,11 +405,11 @@ CaseFile::ReEvaluate(const ZfsEvent &event) return (consumed || closed); } - void CaseFile::RegisterCallout(const DevCtlEvent &event) -{ +{ timeval now, countdown, elapsed, timestamp, zero, remaining; + gettimeofday(&now, 0); timestamp = event.GetTimestamp(); timersub(&now, ×tamp, &elapsed); @@ -427,7 +427,7 @@ CaseFile::RegisterCallout(const DevCtlEvent &event) remaining = m_tentativeTimer.TimeRemaining(); if (!m_tentativeTimer.IsPending() - || timercmp(&countdown, &remaining, <)) + || timercmp(&countdown, &remaining, <)) m_tentativeTimer.Reset(countdown, OnGracePeriodEnded, this); } @@ -559,13 +559,12 @@ CaseFile::DeSerializeFile(const char *fileName) } ifstream caseStream(fullName.c_str()); - if (! caseStream) { + if (!caseStream) { throw ZfsdException("CaseFile::DeSerialize: Unable to " - "read %s.\n", fileName); + "read %s.\n", fileName); return; } - stringstream fakeDevdSocket(stringstream::in - | stringstream::out); + stringstream fakeDevdSocket(stringstream::in|stringstream::out); IstreamReader caseReader(&fakeDevdSocket); /* Re-load EventData */ @@ -604,7 +603,6 @@ CaseFile::DeSerializeFile(const char *fileName) } } } - } catch (const ParseException &exp) { exp.Log(evString); @@ -673,7 +671,6 @@ CaseFile::PurgeTentativeEvents() m_tentativeEvents.clear(); } - void CaseFile::SerializeEvList(const DevCtlEventList events, int fd, const char* prefix) const diff --git a/cddl/sbin/zfsd/case_file.h b/cddl/sbin/zfsd/case_file.h index 91753a5310f..0ce437c743d 100644 --- a/cddl/sbin/zfsd/case_file.h +++ b/cddl/sbin/zfsd/case_file.h @@ -210,13 +210,14 @@ protected: * * \param dirEntry Directory entry for the file to filter. * - * \return Non-zero for a file to include in the selection, otherwise 0. + * \return Non-zero for a file to include in the selection, + * otherwise 0. */ static int DeSerializeSelector(const struct dirent *dirEntry); /** * \brief Given the name of a file containing serialized events from a - * CaseFile object, create/update an in-core CaseFile object + * CaseFile object, create/update an in-core CaseFile object * representing the serialized data. * * \param fileName The name of a file containing serialized events @@ -253,14 +254,14 @@ protected: */ void Serialize(); - /** + /** * \brief Serializes the supplied event list and writes it to fd * - * \param prefix If not NULL, this prefix will be prepended to - * every event in the file. + * \param prefix If not NULL, this prefix will be prepended to + * every event in the file. */ void SerializeEvList(const DevCtlEventList events, int fd, - const char* prefix=NULL) const; + const char* prefix=NULL) const; /** * \brief Unconditionally close a CaseFile. @@ -309,9 +310,9 @@ protected: uint64_t m_poolGUID; uint64_t m_vdevGUID; vdev_state m_vdevState; - string m_poolGUIDString; - string m_vdevGUIDString; - string m_vdevPhysPath; + string m_poolGUIDString; + string m_vdevGUIDString; + string m_vdevPhysPath; /** * \brief Callout activated when a grace period diff --git a/cddl/sbin/zfsd/dev_ctl_event.h b/cddl/sbin/zfsd/dev_ctl_event.h index d5cfe3531d1..f53dae6e151 100644 --- a/cddl/sbin/zfsd/dev_ctl_event.h +++ b/cddl/sbin/zfsd/dev_ctl_event.h @@ -95,7 +95,7 @@ public: * Accessor * * \return The offset into the event string where this exception - * occurred. + * occurred. */ size_t GetOffset() const; @@ -158,7 +158,7 @@ typedef map NVPairMap; * * All name => value data for events can be accessed via the Contains() * and Value() methods. name => value pairs for data not explicitly - * recieved as a name => value pair are synthesized during parsing. For + * received as a name => value pair are synthesized during parsing. For * example, ATTACH and DETACH events have "device-name" and "parent" * name => value pairs added. */ @@ -219,7 +219,7 @@ public: * associated value. * * \return A const reference to the string representing the - * value associated with key. + * value associated with key. * * \note For key's with no registered value, the empty string * is returned. @@ -265,7 +265,7 @@ public: * Create and return a fully independent clone * of this event. */ - virtual DevCtlEvent *DeepCopy() const = 0; + virtual DevCtlEvent *DeepCopy() const = 0; /** Destructor */ virtual ~DevCtlEvent(); @@ -358,8 +358,8 @@ private: /** * Ingest event data from the supplied string. * - * \param[in] eventString The string of devd event data to parse. - * \param[out] nvpairs Returns the parsed data + * \param[in] eventString The string of devd event data to parse. + * \param[out] nvpairs Returns the parsed data */ static void ParseEventString(Type type, const string &eventString, NVPairMap &nvpairs); @@ -470,11 +470,11 @@ public: * Interpret and perform any actions necessary to * consume the event. */ - virtual void Process() const; + virtual void Process() const; - const string &PoolName() const; - uint64_t PoolGUID() const; - uint64_t VdevGUID() const; + const string &PoolName() const; + uint64_t PoolGUID() const; + uint64_t VdevGUID() const; protected: /** Constructor */ @@ -483,7 +483,7 @@ protected: /** Deep copy constructor. */ ZfsEvent(const ZfsEvent &src); - void ProcessPoolEvent() const; + void ProcessPoolEvent() const; uint64_t m_poolGUID; uint64_t m_vdevGUID; diff --git a/cddl/sbin/zfsd/zfsd.cc b/cddl/sbin/zfsd/zfsd.cc index 4f57f14d662..43f3d1531ac 100644 --- a/cddl/sbin/zfsd/zfsd.cc +++ b/cddl/sbin/zfsd/zfsd.cc @@ -215,10 +215,10 @@ EventBuffer::ExtractEvent(string &eventString) * Add a timestamp as the final field of the event if it is * not already present. */ - if ( eventString.find("timestamp=") == string::npos) { - eventString.insert( - eventString.find_last_not_of('\n') + 1, - tsField.str()); + if (eventString.find("timestamp=") == string::npos) { + size_t eventEnd(eventString.find_last_not_of('\n') + 1); + + eventString.insert(eventEnd, tsField.str()); } return (true); diff --git a/cddl/sbin/zfsd/zfsd.h b/cddl/sbin/zfsd/zfsd.h index 9d34d8ceeb8..635fa8c4c54 100644 --- a/cddl/sbin/zfsd/zfsd.h +++ b/cddl/sbin/zfsd/zfsd.h @@ -79,15 +79,14 @@ typedef int LeafIterFunc(zpool_handle_t *, nvlist_t *, void *); /*-------------------------------- Reader -------------------------------*/ /** - * \brief A class that presents a common interface to both file descriptors and - * istreams . + * \brief A class that presents a common interface to both file descriptors + * and istreams. * * Standard C++ provides no way to create an iostream from a file descriptor or * a FILE. The GNU, Apache, HPUX, and Solaris C++ libraries all provide * non-standard ways to construct such a stream using similar semantics, but - * LLVM does not. Therefore this class is needed to ensure that zfsd can - * compile under LLVM. This class supports only the functionality needed by - * ZFSD; it does not implement the iostream API. + * FreeBSD's C++ library does not. This class supports only the functionality + * needed by ZFSD; it does not implement the iostream API. */ class Reader { @@ -104,9 +103,9 @@ public: * On error, -1 is returned, and errno will be set by the underlying * source. * - * \param buf Destination for the data - * \param count Maximum amount of data to read - * \returns Amount of data that was actually read + * \param buf Destination for the data + * \param count Maximum amount of data to read + * \returns Amount of data that was actually read */ virtual ssize_t read(char* buf, size_t count) = 0; }; @@ -122,12 +121,12 @@ public: /** * \brief Constructor * - * \param fd An open file descriptor. It will not be garbage - * collected by the destructor. + * \param fd An open file descriptor. It will not be garbage + * collected by the destructor. */ FDReader(int fd); - virtual size_t in_avail() const; + virtual size_t in_avail() const; virtual ssize_t read(char* buf, size_t count); @@ -137,8 +136,9 @@ protected: }; //- FDReader Inline Public Methods ----------------------------------------- -inline FDReader::FDReader(int fd) - : m_fd(fd) +inline +FDReader::FDReader(int fd) + : m_fd(fd) { } @@ -148,7 +148,6 @@ FDReader::read(char* buf, size_t count) return (::read(m_fd, buf, count)); } - /*-------------------------------- IstreamReader------------------------------*/ /** * \brief Specialization of Reader that uses a std::istream @@ -159,8 +158,8 @@ public: /** * Constructor * - * \param stream Pointer to an open istream. It will not be - * garbage collected by the destructor. + * \param stream Pointer to an open istream. It will not be + * garbage collected by the destructor. */ IstreamReader(istream* stream); @@ -170,12 +169,13 @@ public: protected: /** Copy of the underlying stream */ - istream* m_stream; + istream *m_stream; }; //- IstreamReader Inline Public Methods ---------------------------------------- -inline IstreamReader::IstreamReader(istream* stream) - : m_stream(stream) +inline +IstreamReader::IstreamReader(istream* stream) + : m_stream(stream) { } @@ -189,7 +189,7 @@ IstreamReader::in_avail() const /*-------------------------------- EventBuffer -------------------------------*/ /** * \brief Class buffering event data from Devd or a similar source and - * splitting it into individual event strings. + * splitting it into individual event strings. * * Users of this class initialize it with a Reader associated with the unix * domain socket connection with devd or a compatible source. The lifetime of @@ -276,7 +276,7 @@ private: char m_buf[EVENT_BUFSIZE]; /** Reference to the reader linked to devd's domain socket. */ - Reader& m_reader; + Reader& m_reader; /** Valid bytes in m_buf. */ size_t m_validLen; From c462338e7afab3fcce8c9b63d913f4f909cb5ce1 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Fri, 11 Oct 2013 22:00:54 +0000 Subject: [PATCH 51/89] Modify the parsing of device control attach and detach events to tolerate the timestamp key<=>value pair added to all events in changeset 256353. cddl/sbin/zfsd/dev_ctl_event.cc: o In DevCtlEvent::ParseEventString(), remove code that verified that attach and detach events contained no trailing key<=>value pairs after the "parent" field. o Fix whitespace/style error in an if statement. Submitted by: gibbs Approved by: ken (mentor) Sponsored By: Spectra Logic Corporation --- cddl/sbin/zfsd/dev_ctl_event.cc | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/cddl/sbin/zfsd/dev_ctl_event.cc b/cddl/sbin/zfsd/dev_ctl_event.cc index c869f4e033e..93c1af14e9b 100644 --- a/cddl/sbin/zfsd/dev_ctl_event.cc +++ b/cddl/sbin/zfsd/dev_ctl_event.cc @@ -256,7 +256,7 @@ DevCtlEvent::GetTimestamp() const timeval tv_timestamp; struct tm tm_timestamp; - if ( ! Contains("timestamp") ) { + if (!Contains("timestamp")) { throw ZfsdException("Event contains no timestamp: %s", m_eventString.c_str()); } @@ -320,17 +320,6 @@ DevCtlEvent::ParseEventString(DevCtlEvent::Type type, start += 4; end = eventString.find_first_of(" \t\n", start); nvpairs["parent"] = eventString.substr(start, end); - if (end == string::npos) - break; - - /* - * The parent field should terminate the event with the - * exception of trailing whitespace. - */ - end = eventString.find_first_not_of(" \t\n", end); - if (end != string::npos) - throw ParseException(ParseException::INVALID_FORMAT, - end); break; case NOTIFY: break; From e2c3880c31231c050afcbb43f97407126ef5d9c8 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Fri, 11 Oct 2013 22:19:45 +0000 Subject: [PATCH 52/89] General cleanup to facilitate unit testing. The unit tests will be committed separately. cddl/sbin/zfsd/zfsd.cc cddl/sbin/zfsd/zfsd_main.cc cddl/sbin/zfsd/Makefile Split main() into a separate file. cddl/sbin/zfsd/callout.cc cddl/sbin/zfsd/zfsd_exception.h cddl/sbin/zfsd/zfsd_exception.cc Removed dead code and de-inlined functions. Submitted by: alans Approved by: ken (mentor) Sponsored by: Spectra Logic Corporation --- cddl/sbin/zfsd/Makefile | 3 +- cddl/sbin/zfsd/callout.cc | 2 +- cddl/sbin/zfsd/zfsd.cc | 30 ------------- cddl/sbin/zfsd/zfsd_exception.cc | 26 ----------- cddl/sbin/zfsd/zfsd_exception.h | 36 --------------- cddl/sbin/zfsd/zfsd_main.cc | 76 ++++++++++++++++++++++++++++++++ 6 files changed, 79 insertions(+), 94 deletions(-) create mode 100644 cddl/sbin/zfsd/zfsd_main.cc diff --git a/cddl/sbin/zfsd/Makefile b/cddl/sbin/zfsd/Makefile index 7070726408b..5e0af13aaf7 100644 --- a/cddl/sbin/zfsd/Makefile +++ b/cddl/sbin/zfsd/Makefile @@ -8,7 +8,8 @@ SRCS= callout.cc \ vdev_iterator.cc \ zfsd.cc \ zfsd_exception.cc \ - zpool_list.cc + zpool_list.cc \ + zfsd_main.cc NO_MAN= YES diff --git a/cddl/sbin/zfsd/callout.cc b/cddl/sbin/zfsd/callout.cc index 07dc4dbf981..b1599596476 100644 --- a/cddl/sbin/zfsd/callout.cc +++ b/cddl/sbin/zfsd/callout.cc @@ -53,7 +53,7 @@ Callout::Init() signal(SIGALRM, Callout::AlarmSignalHandler); } -inline bool +bool Callout::Stop() { if (!IsPending()) diff --git a/cddl/sbin/zfsd/zfsd.cc b/cddl/sbin/zfsd/zfsd.cc index 43f3d1531ac..fd6ced207af 100644 --- a/cddl/sbin/zfsd/zfsd.cc +++ b/cddl/sbin/zfsd/zfsd.cc @@ -61,7 +61,6 @@ #include #include #include -#include #include #include @@ -683,33 +682,4 @@ ZfsDaemon::EventLoop() } } -/*=============================== Program Main ===============================*/ -static void -usage() -{ - fprintf(stderr, "usage: %s [-d]\n", getprogname()); - exit(1); -} -/** - * Program entry point. - */ -int -main(int argc, char **argv) -{ - int ch; - - while ((ch = getopt(argc, argv, "d")) != -1) { - switch (ch) { - case 'd': - g_debug++; - break; - default: - usage(); - } - } - - ZfsDaemon::Run(); - - return (0); -} diff --git a/cddl/sbin/zfsd/zfsd_exception.cc b/cddl/sbin/zfsd/zfsd_exception.cc index a2fea23e690..243cafa30a9 100644 --- a/cddl/sbin/zfsd/zfsd_exception.cc +++ b/cddl/sbin/zfsd/zfsd_exception.cc @@ -93,32 +93,6 @@ ZfsdException::ZfsdException(nvlist_t *poolConfig, const char *fmt, ...) va_end(ap); } -inline -ZfsdException::ZfsdException(zpool_handle_t *pool, nvlist_t *vdevConfig, - const char *fmt, ...) - : m_poolConfig(zpool_get_config(pool, NULL)), - m_vdevConfig(vdevConfig) -{ - va_list ap; - - va_start(ap, fmt); - FormatLog(fmt, ap); - va_end(ap); -} - -inline -ZfsdException::ZfsdException(nvlist_t *poolConfig, nvlist_t *vdevConfig, - const char *fmt, ...) - : m_poolConfig(poolConfig), - m_vdevConfig(vdevConfig) -{ - va_list ap; - - va_start(ap, fmt); - FormatLog(fmt, ap); - va_end(ap); -} - void ZfsdException::Log() const { diff --git a/cddl/sbin/zfsd/zfsd_exception.h b/cddl/sbin/zfsd/zfsd_exception.h index d8a9fed29db..33d03ca71a2 100644 --- a/cddl/sbin/zfsd/zfsd_exception.h +++ b/cddl/sbin/zfsd/zfsd_exception.h @@ -92,42 +92,6 @@ public: */ ZfsdException(nvlist_t *poolConfig, const char *, ...); - /** - * \brief ZfsdException constructor allowing arbitrary string - * data to be reported and associated with the configuration - * data for a single vdev and its parent pool. - * - * \param pool Pool handle describing the pool to which this - * exception is associated. - * \param vdevConfig A name/value list describing the vdev - * to which this exception is associated. - * \param fmt Printf-like string format specifier. - * - * Instantiation with this method is used to report errors - * associated with a vdev when both the vdev's config and - * its pool membership are available. - */ - ZfsdException(zpool_handle_t *pool, nvlist_t *vdevConfig, - const char *fmt, ...); - - /** - * \brief ZfsdException constructor allowing arbitrary string - * data to be reported and associated with the configuration - * data for a single vdev and its parent pool. - * - * \param poolConfig Pool configuration describing the pool to - * which this exception is associated. - * \param vdevConfig A name/value list describing the vdev - * to which this exception is associated. - * \param fmt Printf-like string format specifier. - * - * Instantiation with this method is used to report errors - * associated with a vdev when both the vdev's config and - * its pool membership are available. - */ - ZfsdException(nvlist_t *poolConfig, nvlist_t *vdevConfig, - const char *fmt, ...); - /** * \brief Augment/Modify a ZfsdException's string data. */ diff --git a/cddl/sbin/zfsd/zfsd_main.cc b/cddl/sbin/zfsd/zfsd_main.cc new file mode 100644 index 00000000000..0fdcc6a28d5 --- /dev/null +++ b/cddl/sbin/zfsd/zfsd_main.cc @@ -0,0 +1,76 @@ +/*- + * Copyright (c) 2011 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Alan Somers (Spectra Logic Corporation) + */ + +/** + * \file zfsd_main.cc + * + * main function for the ZFS Daemon. Separated to facilitate testing. + * + */ + +#include +#include +#include + +#include "zfsd.h" + + +/*=============================== Program Main ===============================*/ +static void +usage() +{ + fprintf(stderr, "usage: %s [-d]\n", getprogname()); + exit(1); +} + +/** + * Program entry point. + */ +int +main(int argc, char **argv) +{ + int ch; + + while ((ch = getopt(argc, argv, "d")) != -1) { + switch (ch) { + case 'd': + g_debug++; + break; + default: + usage(); + } + } + + ZfsDaemon::Run(); + + return (0); +} From 8be7ef53a82779e39949fe9bfdcf3e90a506c4dd Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Fri, 11 Oct 2013 22:44:15 +0000 Subject: [PATCH 53/89] zfsd will now try to activate a spare, if one is available, when a resource dissapears. Other functionality, such as detaching the spare when the original device returns, is TBD. cddl/sbin/zfsd/vdev_iterator.h cddl/sbin/zfsd/vdev_iterator.cc cddl/sbin/zfsd/zpool_list.cc cddl/sbin/zfsd/dev_ctl_event.h cddl/sbin/zfsd/case_file.h cddl/sbin/zfsd/case_file.cc cddl/sbin/zfsd/vdev.h cddl/sbin/zfsd/vdev.cc Created a new Guid class that can have a None value. Modified the Vdev class to be able to represent available spares, which do not have pool or vdev guids. cddl/sbin/zfsd/case_file.h cddl/sbin/zfsd/case_file.cc Abstract device replacement into the CaseFile::Replace method, which is used by both ActivateSpare and replace by physical path. Try to active a hotspare whenever a vdev dissappears. Submitted by: asomers Approved by: ken (mentor) Sponsored by: Spectra Logic Corporation --- cddl/sbin/zfsd/case_file.cc | 214 +++++++++++++++++++++++--------- cddl/sbin/zfsd/case_file.h | 38 ++++-- cddl/sbin/zfsd/dev_ctl_event.cc | 43 +++++-- cddl/sbin/zfsd/dev_ctl_event.h | 14 ++- cddl/sbin/zfsd/vdev.cc | 67 +++++++--- cddl/sbin/zfsd/vdev.h | 64 +++++++++- cddl/sbin/zfsd/vdev_iterator.cc | 2 +- cddl/sbin/zfsd/vdev_iterator.h | 2 +- cddl/sbin/zfsd/zpool_list.cc | 5 +- 9 files changed, 343 insertions(+), 106 deletions(-) diff --git a/cddl/sbin/zfsd/case_file.cc b/cddl/sbin/zfsd/case_file.cc index 5edadbe3494..967fcc4892a 100644 --- a/cddl/sbin/zfsd/case_file.cc +++ b/cddl/sbin/zfsd/case_file.cc @@ -66,8 +66,9 @@ const string CaseFile::s_caseFilePath = "/etc/zfs/cases"; const timeval CaseFile::s_removeGracePeriod = { 60 /*sec*/, 0 /*usec*/}; //- CaseFile Static Public Methods --------------------------------------------- + CaseFile * -CaseFile::Find(uint64_t poolGUID, uint64_t vdevGUID) +CaseFile::Find(Guid poolGUID, Guid vdevGUID) { for (CaseFileList::iterator curCase = s_activeCases.begin(); curCase != s_activeCases.end(); curCase++) { @@ -163,16 +164,17 @@ CaseFile::RefreshVdevState() { ZpoolList zpl(ZpoolList::ZpoolByGUID, &m_poolGUID); if (zpl.empty()) { - syslog(LOG_INFO, - "CaseFile::RefreshVdevState: Unknown pool for " - "Vdev(%ju,%ju).\n", - m_poolGUID, m_vdevGUID); - return (false); + stringstream msg; + msg << "CaseFile::RefreshVdevState: Unknown pool for Vdev("; + msg << m_poolGUID << "," << m_vdevGUID << ")."; + syslog(LOG_INFO, msg.str().c_str()); + return (false); } zpool_handle_t *casePool(zpl.front()); nvlist_t *vdevConfig = VdevIterator(casePool).Find(VdevGUID()); if (vdevConfig == NULL) { + stringstream msg; syslog(LOG_INFO, "CaseFile::RefreshVdevState: Unknown Vdev(%s,%s).\n", PoolGUIDString().c_str(), PoolGUIDString().c_str()); @@ -288,55 +290,7 @@ CaseFile::ReEvaluate(const string &devPath, const string &physPath, Vdev *vdev) return (/*consumed*/false); } - /* - * Build a root vdev/leaf vdev configuration suitable for - * zpool_vdev_attach. Only enough data for the kernel to find - * the device (i.e. type and disk device node path) are needed. - */ - nvlist_t *nvroot(NULL); - nvlist_t *newvd(NULL); - if (nvlist_alloc(&nvroot, NV_UNIQUE_NAME, 0) != 0 - || nvlist_alloc(&newvd, NV_UNIQUE_NAME, 0) != 0) { - syslog(LOG_ERR, "Replace vdev(%s/%s) by physical path: " - "Unable to allocate configuration data.\n", - zpool_get_name(pool), VdevGUIDString().c_str()); - if (nvroot != NULL) - nvlist_free(nvroot); - return (/*consumed*/false); - } - - if (nvlist_add_string(newvd, ZPOOL_CONFIG_TYPE, VDEV_TYPE_DISK) != 0 - || nvlist_add_string(newvd, ZPOOL_CONFIG_PATH, devPath.c_str()) != 0 - || nvlist_add_string(nvroot, ZPOOL_CONFIG_TYPE, VDEV_TYPE_ROOT) != 0 - || nvlist_add_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN, - &newvd, 1) != 0) { - syslog(LOG_ERR, "Replace vdev(%s/%s) by physical path: " - "Unable to initialize configuration data.\n", - zpool_get_name(pool), VdevGUIDString().c_str()); - nvlist_free(newvd); - nvlist_free(nvroot); - return (/*consumed*/true); - } - - /* Data was copied when added to the root vdev. */ - nvlist_free(newvd); - - if (zpool_vdev_attach(pool, VdevGUIDString().c_str(), - devPath.c_str(), nvroot, - /*replace*/B_TRUE) != 0) { - syslog(LOG_ERR, - "Replace vdev(%s/%s) by physical path(attach): %s: %s\n", - zpool_get_name(pool), VdevGUIDString().c_str(), - libzfs_error_action(g_zfsHandle), - libzfs_error_description(g_zfsHandle)); - } else { - syslog(LOG_INFO, "Replacing vdev(%s/%s) with %s\n", - zpool_get_name(pool), VdevGUIDString().c_str(), - devPath.c_str()); - } - nvlist_free(nvroot); - - return (true); + return (Replace(VDEV_TYPE_DISK, devPath.c_str())); } bool @@ -385,6 +339,9 @@ CaseFile::ReEvaluate(const ZfsEvent &event) */ PurgeTentativeEvents(); + /* Try to activate spares if they are available */ + ActivateSpare(); + /* * Rescan the drives in the system to see if a recent * drive arrival can be used to solve this case. @@ -405,6 +362,83 @@ CaseFile::ReEvaluate(const ZfsEvent &event) return (consumed || closed); } + +bool +CaseFile::ActivateSpare() { + nvlist_t *config, *nvroot; + nvlist_t **spares; + zpool_handle_t *zhp; + char *devPath, *vdev_type; + const char* poolname; + unsigned nspares, i; + + ZpoolList zpl(ZpoolList::ZpoolByGUID, &m_poolGUID); + if (zpl.empty()) { + syslog(LOG_ERR, "CaseFile::Replace: could not find pool for " + "pool_guid %ju", (uint64_t)m_poolGUID); + return (false); + } + zhp = zpl.front(); + poolname = zpool_get_name(zhp); + config = zpool_get_config(zhp, NULL); + if (config == NULL) { + syslog(LOG_ERR, + "ActivateSpare: Could not find pool config for pool %s", + poolname); + return (false); + } + if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &nvroot) != 0){ + syslog(LOG_ERR, + "ActivateSpare: Could not find vdev tree for pool %s", + poolname); + return (false); + } + nspares = 0; + nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES, &spares, + &nspares); + if (nspares == 0) { + /* The pool has no spares configured */ + return (false); + } + for (i = 0; i < nspares; i++) { + vdev_stat_t *vs; + unsigned nstats; + + if (nvlist_lookup_uint64_array(spares[i], + ZPOOL_CONFIG_VDEV_STATS, (uint64_t**)&vs, &nstats) != 0) { + syslog(LOG_ERR, "ActivateSpare: Could not find vdev " + "stats for pool %s, spare %d", + poolname, i); + return (false); + } + + if ( (vs->vs_aux != VDEV_AUX_SPARED) + && (vs->vs_state == VDEV_STATE_HEALTHY)) { + /* We found a usable spare */ + break; + } + } + + if (i == nspares) { + /* No available spares were found */ + return (false); + } + + if (nvlist_lookup_string(spares[i], ZPOOL_CONFIG_PATH, &devPath) != 0){ + syslog(LOG_ERR, "ActivateSpare: Cannot determine the path of " + "pool %s, spare %d", poolname, i); + return (false); + } + + if (nvlist_lookup_string(spares[i], ZPOOL_CONFIG_TYPE, &vdev_type)!= 0){ + syslog(LOG_ERR, "ActivateSpare: Cannot determine the vdev type " + "of pool %s, spare %d", poolname, i); + return (false); + } + + return (Replace(vdev_type, devPath)); +} + void CaseFile::RegisterCallout(const DevCtlEvent &event) { @@ -519,7 +553,7 @@ CaseFile::DeSerializeFile(const char *fileName) sscanf(fileName, "pool_%ju_vdev_%ju.case", &poolGUID, &vdevGUID); - existingCaseFile = Find(poolGUID, vdevGUID); + existingCaseFile = Find(Guid(poolGUID), Guid(vdevGUID)); if (existingCaseFile != NULL) { /* * If the vdev is already degraded or faulted, @@ -756,7 +790,7 @@ CaseFile::OnGracePeriodEnded() } /* Degrade the vdev and close the case. */ - if (zpool_vdev_degrade(zpl.front(), m_vdevGUID, + if (zpool_vdev_degrade(zpl.front(), (uint64_t)m_vdevGUID, VDEV_AUX_ERR_EXCEEDED) == 0) { Close(); return; @@ -764,3 +798,69 @@ CaseFile::OnGracePeriodEnded() } Serialize(); } + +bool +CaseFile::Replace(const char* vdev_type, const char* path) { + nvlist_t *nvroot, *newvd; + zpool_handle_t *zhp; + const char* poolname; + + /* Figure out what pool we're working on */ + ZpoolList zpl(ZpoolList::ZpoolByGUID, &m_poolGUID); + if (zpl.empty()) { + syslog(LOG_ERR, "CaseFile::Replace: could not find pool for " + "pool_guid %ju", (uint64_t)m_poolGUID); + return (false); + } + zhp = zpl.front(); + poolname = zpool_get_name(zhp); + + /* + * Build a root vdev/leaf vdev configuration suitable for + * zpool_vdev_attach. Only enough data for the kernel to find + * the device (i.e. type and disk device node path) are needed. + */ + nvroot = NULL; + newvd = NULL; + + if (nvlist_alloc(&nvroot, NV_UNIQUE_NAME, 0) != 0 + || nvlist_alloc(&newvd, NV_UNIQUE_NAME, 0) != 0) { + syslog(LOG_ERR, "Replace vdev(%s/%s) by physical path: " + "Unable to allocate configuration data.\n", + poolname, VdevGUIDString().c_str()); + if (nvroot != NULL) + nvlist_free(nvroot); + return (false); + } + if (nvlist_add_string(newvd, ZPOOL_CONFIG_TYPE, vdev_type) != 0 + || nvlist_add_string(newvd, ZPOOL_CONFIG_PATH, path) != 0 + || nvlist_add_string(nvroot, ZPOOL_CONFIG_TYPE, VDEV_TYPE_ROOT) != 0 + || nvlist_add_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN, + &newvd, 1) != 0) { + syslog(LOG_ERR, "Replace vdev(%s/%s) by physical path: " + "Unable to initialize configuration data.\n", + poolname, VdevGUIDString().c_str()); + nvlist_free(newvd); + nvlist_free(nvroot); + return (true); + } + + /* Data was copied when added to the root vdev. */ + nvlist_free(newvd); + + if (zpool_vdev_attach(zhp, VdevGUIDString().c_str(), + path, nvroot, /*replace*/B_TRUE) != 0) { + syslog(LOG_ERR, + "Replace vdev(%s/%s) by physical path(attach): %s: %s\n", + poolname, VdevGUIDString().c_str(), + libzfs_error_action(g_zfsHandle), + libzfs_error_description(g_zfsHandle)); + } else { + syslog(LOG_INFO, "Replacing vdev(%s/%s) with %s\n", + poolname, VdevGUIDString().c_str(), + path); + } + nvlist_free(nvroot); + + return (true); +} diff --git a/cddl/sbin/zfsd/case_file.h b/cddl/sbin/zfsd/case_file.h index 0ce437c743d..84a51c77556 100644 --- a/cddl/sbin/zfsd/case_file.h +++ b/cddl/sbin/zfsd/case_file.h @@ -95,7 +95,7 @@ public: * \return If found, a pointer to a valid CaseFile object. * Otherwise NULL. */ - static CaseFile *Find(uint64_t poolGUID, uint64_t vdevGUID); + static CaseFile *Find(Guid poolGUID, Guid vdevGUID); /** * \brief Find a CaseFile object by a vdev's current/last known @@ -137,8 +137,8 @@ public: */ static void PurgeAll(); - uint64_t PoolGUID() const; - uint64_t VdevGUID() const; + Guid PoolGUID() const; + Guid VdevGUID() const; vdev_state VdevState() const; const string &PoolGUIDString() const; const string &VdevGUIDString() const; @@ -278,6 +278,30 @@ protected: */ void OnGracePeriodEnded(); + /** + * \brief Attempt to activate a spare on this case's pool. + * + * Call this whenever a pool becomes degraded. It will look for any + * spare devices and activate one to replace the casefile's vdev. It + * will _not_ close the casefile; that should only happen when the + * missing drive is replaced or the user promotes the spare. + * + * \return True if a spare was activated + */ + bool ActivateSpare(); + + /** + * \brief replace a pool's vdev with another + * + * \param vdev_type The type of the new vdev. Usually either + * VDEV_TYPE_DISK or VDEV_TYPE_FILE + * \param path The file system path to the new vdev + * + * \return true iff the replacement was successful + * + */ + bool Replace(const char* vdev_type, const char* path); + /** * \brief All CaseFiles being tracked by ZFSD. */ @@ -307,8 +331,8 @@ protected: */ DevCtlEventList m_tentativeEvents; - uint64_t m_poolGUID; - uint64_t m_vdevGUID; + Guid m_poolGUID; + Guid m_vdevGUID; vdev_state m_vdevState; string m_poolGUIDString; string m_vdevGUIDString; @@ -320,13 +344,13 @@ protected: Callout m_tentativeTimer; }; -inline uint64_t +inline Guid CaseFile::PoolGUID() const { return (m_poolGUID); } -inline uint64_t +inline Guid CaseFile::VdevGUID() const { return (m_vdevGUID); diff --git a/cddl/sbin/zfsd/dev_ctl_event.cc b/cddl/sbin/zfsd/dev_ctl_event.cc index 93c1af14e9b..05cbd166f80 100644 --- a/cddl/sbin/zfsd/dev_ctl_event.cc +++ b/cddl/sbin/zfsd/dev_ctl_event.cc @@ -163,8 +163,11 @@ DevCtlEvent::CreateEvent(const string &eventString) EventFactoryKey key(type, nvpairs["system"]); EventFactoryRegistry::iterator foundMethod(s_factoryRegistry.find(key)); - if (foundMethod == s_factoryRegistry.end()) + if (foundMethod == s_factoryRegistry.end()) { + syslog(LOG_INFO, "DevCtlEvent::CreateEvent: unhandled event %s", + eventString.c_str()); return (NULL); + } return ((foundMethod->second)(type, nvpairs, eventString)); } @@ -633,11 +636,13 @@ ZfsEvent::Process() const } /* Skip events that can't be handled. */ - uint64_t poolGUID(PoolGUID()); + Guid poolGUID(PoolGUID()); /* If there are no replicas for a pool, then it's not manageable. */ if (Value("class").find("fs.zfs.vdev.no_replicas") == 0) { - syslog(LOG_INFO, "No replicas available for pool %ju" - ", ignoring\n", (uintmax_t)poolGUID); + stringstream msg; + msg << "No replicas available for pool " << poolGUID; + msg << ", ignoring"; + syslog(LOG_INFO, msg.str().c_str()); return; } @@ -647,21 +652,25 @@ ZfsEvent::Process() const */ ZpoolList zpl(ZpoolList::ZpoolByGUID, &poolGUID); if (zpl.empty()) { + stringstream msg; bool queued = ZfsDaemon::SaveEvent(*this); int priority = queued ? LOG_INFO : LOG_ERR; - syslog(priority, - "ZfsEvent::Process: Event for unknown pool %ju %s", - (uintmax_t)poolGUID, queued ? "queued" : "dropped"); + msg << "ZfsEvent::Process: Event for unknown pool "; + msg << poolGUID << " "; + msg << (queued ? "queued" : "dropped"); + syslog(priority, msg.str().c_str()); return; } nvlist_t *vdevConfig = VdevIterator(zpl.front()).Find(VdevGUID()); if (vdevConfig == NULL) { + stringstream msg; bool queued = ZfsDaemon::SaveEvent(*this); int priority = queued ? LOG_INFO : LOG_ERR; - syslog(priority, - "ZfsEvent::Process: Event for unknown vdev %ju %s", - (uintmax_t)poolGUID, queued ? "queued" : "dropped"); + msg << "ZfsEvent::Process: Event for unknown vdev "; + msg << VdevGUID() << " "; + msg << (queued ? "queued" : "dropped"); + syslog(priority, msg.str().c_str()); return; } @@ -679,8 +688,18 @@ ZfsEvent::ZfsEvent(DevCtlEvent::Type type, NVPairMap &nvpairs, * These are zero on conversion failure as will happen if * Value returns the empty string. */ - m_poolGUID = (uint64_t)strtoumax(Value("pool_guid").c_str(), NULL, 0); - m_vdevGUID = (uint64_t)strtoumax(Value("vdev_guid").c_str(), NULL, 0); + if (Contains("pool_guid")) { + m_poolGUID = (uint64_t)strtoumax(Value("pool_guid").c_str(), + NULL, 0); + } + else + m_poolGUID = Guid(); + if (Contains("vdev_guid")) { + m_vdevGUID = (uint64_t)strtoumax(Value("vdev_guid").c_str(), + NULL, 0); + } + else + m_vdevGUID = Guid(); } ZfsEvent::ZfsEvent(const ZfsEvent &src) diff --git a/cddl/sbin/zfsd/dev_ctl_event.h b/cddl/sbin/zfsd/dev_ctl_event.h index f53dae6e151..7916e761a3f 100644 --- a/cddl/sbin/zfsd/dev_ctl_event.h +++ b/cddl/sbin/zfsd/dev_ctl_event.h @@ -49,6 +49,8 @@ #include #include +#include "vdev.h" + /*============================ Namespace Control =============================*/ using std::map; using std::pair; @@ -473,8 +475,8 @@ public: virtual void Process() const; const string &PoolName() const; - uint64_t PoolGUID() const; - uint64_t VdevGUID() const; + Guid PoolGUID() const; + Guid VdevGUID() const; protected: /** Constructor */ @@ -485,8 +487,8 @@ protected: void ProcessPoolEvent() const; - uint64_t m_poolGUID; - uint64_t m_vdevGUID; + Guid m_poolGUID; + Guid m_vdevGUID; }; //- ZfsEvent Inline Public Methods -------------------------------------------- @@ -497,13 +499,13 @@ ZfsEvent::PoolName() const return (Value("subsystem")); } -inline uint64_t +inline Guid ZfsEvent::PoolGUID() const { return (m_poolGUID); } -inline uint64_t +inline Guid ZfsEvent::VdevGUID() const { return (m_vdevGUID); diff --git a/cddl/sbin/zfsd/vdev.cc b/cddl/sbin/zfsd/vdev.cc index dbdfd523915..1f200a3e13f 100644 --- a/cddl/sbin/zfsd/vdev.cc +++ b/cddl/sbin/zfsd/vdev.cc @@ -50,51 +50,76 @@ __FBSDID("$FreeBSD$"); using std::stringstream; /*=========================== Class Implementations ==========================*/ +/*----------------------------------- Guid -----------------------------------*/ +std::ostream& operator<< (std::ostream& out, Guid g){ + if (g.isValid()) + out << (uint64_t) g; + else + out << "None"; + return (out); +} + + /*----------------------------------- Vdev -----------------------------------*/ Vdev::Vdev(zpool_handle_t *pool, nvlist_t *config) : m_poolConfig(zpool_get_config(pool, NULL)), m_config(config) { + uint64_t raw_guid; if (nvlist_lookup_uint64(m_poolConfig, ZPOOL_CONFIG_POOL_GUID, - &m_poolGUID) != 0) + &raw_guid) != 0) throw ZfsdException("Unable to extract pool GUID " "from pool handle."); + m_poolGUID = raw_guid; - if (nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_GUID, &m_vdevGUID) != 0) + if (nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_GUID, &raw_guid) != 0) throw ZfsdException("Unable to extract vdev GUID " "from vdev config data."); + m_vdevGUID = raw_guid; } Vdev::Vdev(nvlist_t *poolConfig, nvlist_t *config) : m_poolConfig(poolConfig), m_config(config) { + uint64_t raw_guid; if (nvlist_lookup_uint64(m_poolConfig, ZPOOL_CONFIG_POOL_GUID, - &m_poolGUID) != 0) + &raw_guid) != 0) throw ZfsdException("Unable to extract pool GUID " "from pool handle."); + m_poolGUID = raw_guid; - if (nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_GUID, &m_vdevGUID) != 0) + if (nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_GUID, &raw_guid) != 0) throw ZfsdException("Unable to extract vdev GUID " "from vdev config data."); + m_vdevGUID = raw_guid; } Vdev::Vdev(nvlist_t *labelConfig) : m_poolConfig(labelConfig) { + uint64_t raw_guid; if (nvlist_lookup_uint64(labelConfig, ZPOOL_CONFIG_POOL_GUID, - &m_poolGUID) != 0) - throw ZfsdException("Unable to extract pool GUID " - "from vdev label data."); + &raw_guid) != 0) + m_vdevGUID = Guid(); + else + m_poolGUID = raw_guid; if (nvlist_lookup_uint64(labelConfig, ZPOOL_CONFIG_GUID, - &m_vdevGUID) != 0) + &raw_guid) != 0) throw ZfsdException("Unable to extract vdev GUID " "from vdev label data."); - m_config = VdevIterator(labelConfig).Find(m_vdevGUID); - if (m_config == NULL) - throw ZfsdException("Unable to find vdev config " - "within vdev label data."); + m_vdevGUID = raw_guid; + + try { + m_config = VdevIterator(labelConfig).Find(m_vdevGUID); + } catch (const ZfsdException &exp) { + /* + * When reading a spare's label, it is normal not to find + * a list of vdevs + */ + m_config = NULL; + } } vdev_state @@ -103,6 +128,18 @@ Vdev::State() const vdev_stat_t *vs; uint_t vsc; + if (m_config == NULL) { + /* + * If we couldn't find the list of vdevs, that normally means + * that this is an available hotspare. In that case, we will + * presume it to be healthy. Even if this spare had formerly + * been in use, been degraded, and been replaced, the act of + * replacement wipes the degraded bit from the label. So we + * have no choice but to presume that it is healthy. + */ + return (VDEV_STATE_HEALTHY); + } + if (nvlist_lookup_uint64_array(m_config, ZPOOL_CONFIG_VDEV_STATS, (uint64_t **)&vs, &vsc) == 0) return (static_cast(vs->vs_state)); @@ -136,7 +173,8 @@ Vdev::Path() const { char *path(NULL); - if (nvlist_lookup_string(m_config, ZPOOL_CONFIG_PATH, &path) == 0) + if ((m_config != NULL) + && (nvlist_lookup_string(m_config, ZPOOL_CONFIG_PATH, &path) == 0)) return (path); return (""); @@ -147,7 +185,8 @@ Vdev::PhysicalPath() const { char *path(NULL); - if (nvlist_lookup_string(m_config, ZPOOL_CONFIG_PHYS_PATH, &path) == 0) + if ((m_config != NULL) && (nvlist_lookup_string(m_config, + ZPOOL_CONFIG_PHYS_PATH, &path) == 0)) return (path); return (""); diff --git a/cddl/sbin/zfsd/vdev.h b/cddl/sbin/zfsd/vdev.h index abb252dc999..c47e6c3438f 100644 --- a/cddl/sbin/zfsd/vdev.h +++ b/cddl/sbin/zfsd/vdev.h @@ -40,11 +40,63 @@ #ifndef _VDEV_H_ #define _VDEV_H_ +#include #include #include #include + +/** + * \brief Object that represents guids. + * + * It can generally be manipulated as a uint64_t, but with a special value + * "None" that does not equal any valid guid. + * + * As of this writing, spa_generate_guid() in spa_misc.c explicitly refuses to + * return a guid of 0. So this class uses 0 as a flag value for "None". In the + * future, if 0 is allowed to be a valid guid, the implementation of this class + * must change. + */ +class Guid +{ +public: + /* Constructors */ + Guid(uint64_t guid) : m_GUID(guid) {}; + Guid() { m_GUID = NONE_FLAG; }; + + /* Assignment */ + Guid& operator=(const uint64_t& other) { + m_GUID = other; + return (*this); + }; + + /* Test the validity of this guid. */ + bool isValid() const { return ((bool)m_GUID); }; + + /* Comparison to other Guid operators */ + bool operator==(const Guid& other) const { + return (m_GUID == other.m_GUID); + }; + bool operator!=(const Guid& other) const { + return (m_GUID != other.m_GUID); + }; + + /* Integer conversion operators */ + operator uint64_t() const { return (m_GUID); }; + operator bool() const { return (m_GUID != NONE_FLAG); }; + +protected: + const static uint64_t NONE_FLAG = 0; + /* The stored value. 0 is a flag for "None" */ + uint64_t m_GUID; +}; + + +/** Convert the GUID into its string representation */ +std::ostream& operator<< (std::ostream& out, Guid g); + + /** * \brief Wrapper class for a vdev's name/value configuration list * simplifying access to commonly used vdev attributes. @@ -93,8 +145,8 @@ public: */ Vdev(nvlist_t *vdevConfig); - uint64_t GUID() const; - uint64_t PoolGUID() const; + Guid GUID() const; + Guid PoolGUID() const; vdev_state State() const; std::string Path() const; std::string PhysicalPath() const; @@ -103,19 +155,19 @@ public: nvlist_t *Config() const; private: - uint64_t m_poolGUID; - uint64_t m_vdevGUID; + Guid m_poolGUID; + Guid m_vdevGUID; nvlist_t *m_poolConfig; nvlist_t *m_config; }; -inline uint64_t +inline Guid Vdev::PoolGUID() const { return (m_poolGUID); } -inline uint64_t +inline Guid Vdev::GUID() const { return (m_vdevGUID); diff --git a/cddl/sbin/zfsd/vdev_iterator.cc b/cddl/sbin/zfsd/vdev_iterator.cc index edc6238794c..9145901fa06 100644 --- a/cddl/sbin/zfsd/vdev_iterator.cc +++ b/cddl/sbin/zfsd/vdev_iterator.cc @@ -126,7 +126,7 @@ VdevIterator::Each(VdevCallback_t *callBack, void *callBackArg) } nvlist_t * -VdevIterator::Find(uint64_t vdevGUID) +VdevIterator::Find(Guid vdevGUID) { nvlist_t *vdevConfig; diff --git a/cddl/sbin/zfsd/vdev_iterator.h b/cddl/sbin/zfsd/vdev_iterator.h index 0e28362c673..bf670973163 100644 --- a/cddl/sbin/zfsd/vdev_iterator.h +++ b/cddl/sbin/zfsd/vdev_iterator.h @@ -97,7 +97,7 @@ public: * Upon return, the VdevIterator's cursor points to the vdev just * past the returned vdev or end() if no matching vdev is found. */ - nvlist_t *Find(uint64_t vdevGUID); + nvlist_t *Find(Guid vdevGUID); /** * \brief Perform the specified operation on each leaf member of diff --git a/cddl/sbin/zfsd/zpool_list.cc b/cddl/sbin/zfsd/zpool_list.cc index 32f9ea8e893..f96d3d3466f 100644 --- a/cddl/sbin/zfsd/zpool_list.cc +++ b/cddl/sbin/zfsd/zpool_list.cc @@ -35,6 +35,7 @@ * * Implementation of the ZpoolList class. */ +#include "vdev.h" #include "zpool_list.h" #include "zfsd.h" @@ -50,13 +51,13 @@ bool ZpoolList::ZpoolByGUID(zpool_handle_t *pool, nvlist_t *poolConfig, void *cbArg) { - uint64_t *desiredPoolGUID(static_cast(cbArg)); + Guid *desiredPoolGUID(static_cast(cbArg)); uint64_t poolGUID; /* We are only intested in the pool that matches our pool GUID. */ return (nvlist_lookup_uint64(poolConfig, ZPOOL_CONFIG_POOL_GUID, &poolGUID) == 0 - && poolGUID == *desiredPoolGUID); + && poolGUID == (uint64_t)*desiredPoolGUID); } bool From 0841864fb1fdb99bd3b6bb95ed96c06c98484570 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Fri, 11 Oct 2013 22:54:02 +0000 Subject: [PATCH 54/89] Miscellaneous bug fixes in zfsd. cddl/sbin/zfsd/zfsd.cc Properly handle POLLHUP, POLLER, and EINTR on the devd socket. Ignore POLLHUP led to zfsd spinning the cpu. cddl/sbin/zfsd/case_file.cc: cddl/sbin/zfsd/dev_ctl_event.cc: Use a constant format string so that syslog() cannot be confused by random '%' characters in the string we are logging. Fixes a warning with Clang. cddl/sbin/zfsd/zfsd.cc: In EventBuffer::ExtractEvent(), add a missing argument to one syslog() invocation and specify that an integer is a size_t in another. Fixes warnings in Clang. Submitted by: asomers, gibbs Approved by: ken (mentor) Sponsored by: Spectra Logic Corporation --- cddl/sbin/zfsd/case_file.cc | 2 +- cddl/sbin/zfsd/dev_ctl_event.cc | 6 +++--- cddl/sbin/zfsd/zfsd.cc | 34 +++++++++++++++++++++++++++------ 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/cddl/sbin/zfsd/case_file.cc b/cddl/sbin/zfsd/case_file.cc index 967fcc4892a..9eda4ce8d35 100644 --- a/cddl/sbin/zfsd/case_file.cc +++ b/cddl/sbin/zfsd/case_file.cc @@ -167,7 +167,7 @@ CaseFile::RefreshVdevState() stringstream msg; msg << "CaseFile::RefreshVdevState: Unknown pool for Vdev("; msg << m_poolGUID << "," << m_vdevGUID << ")."; - syslog(LOG_INFO, msg.str().c_str()); + syslog(LOG_INFO, "%s", msg.str().c_str()); return (false); } diff --git a/cddl/sbin/zfsd/dev_ctl_event.cc b/cddl/sbin/zfsd/dev_ctl_event.cc index 05cbd166f80..a58769f2e8a 100644 --- a/cddl/sbin/zfsd/dev_ctl_event.cc +++ b/cddl/sbin/zfsd/dev_ctl_event.cc @@ -642,7 +642,7 @@ ZfsEvent::Process() const stringstream msg; msg << "No replicas available for pool " << poolGUID; msg << ", ignoring"; - syslog(LOG_INFO, msg.str().c_str()); + syslog(LOG_INFO, "%s", msg.str().c_str()); return; } @@ -658,7 +658,7 @@ ZfsEvent::Process() const msg << "ZfsEvent::Process: Event for unknown pool "; msg << poolGUID << " "; msg << (queued ? "queued" : "dropped"); - syslog(priority, msg.str().c_str()); + syslog(priority, "%s", msg.str().c_str()); return; } @@ -670,7 +670,7 @@ ZfsEvent::Process() const msg << "ZfsEvent::Process: Event for unknown vdev "; msg << VdevGUID() << " "; msg << (queued ? "queued" : "dropped"); - syslog(priority, msg.str().c_str()); + syslog(priority, "%s", msg.str().c_str()); return; } diff --git a/cddl/sbin/zfsd/zfsd.cc b/cddl/sbin/zfsd/zfsd.cc index fd6ced207af..36023db5c9a 100644 --- a/cddl/sbin/zfsd/zfsd.cc +++ b/cddl/sbin/zfsd/zfsd.cc @@ -181,7 +181,8 @@ EventBuffer::ExtractEvent(string &eventString) continue; } syslog(LOG_WARNING, - "Event exceeds event size limit of %d bytes."); + "Event exceeds event size limit of %d bytes.", + MAX_EVENT_SIZE); } else { /* * Include the normal terminator in the extracted @@ -206,7 +207,7 @@ EventBuffer::ExtractEvent(string &eventString) m_synchronized = false; syslog(LOG_WARNING, - "Truncated %d characters from event.", + "Truncated %zd characters from event.", eventLen - fieldEnd); } @@ -512,10 +513,31 @@ ZfsDaemon::EventsPending() struct pollfd fds[1]; int result; - fds->fd = s_devdSockFD; - fds->events = POLLIN; - fds->revents = 0; - result = poll(fds, NUM_ELEMENTS(fds), /*timeout*/0); + do { + fds->fd = s_devdSockFD; + fds->events = POLLIN; + fds->revents = 0; + result = poll(fds, NUM_ELEMENTS(fds), /*timeout*/0); + } while ( (result == -1) && (errno == EINTR) ) ; + + if (result == -1) { + /* Unexpected error; try reconnecting the socket */ + throw ZfsdException( + "ZfsdDaemon::EventsPending(): Unexpected error from poll()"); + } + + if ((fds->revents & POLLHUP) != 0) { + /* The other end hung up the socket. Throw an exception + * so ZfsDaemon will try to reconnect + */ + throw ZfsdException("ZfsDaemon::EventsPending(): Got POLLHUP"); + } + + if ((fds->revents & POLLERR) != 0) { + /* Try reconnecting. */ + throw ZfsdException( + "ZfsdDaemon:EventsPending(): Got POLLERR. Reconnecting."); + } return ((fds->revents & POLLIN) != 0); } From e55d42e650a79d59dacf691aa556cfab3c48dab2 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Fri, 11 Oct 2013 23:02:46 +0000 Subject: [PATCH 55/89] Zfsd will now activate a spare when it gets added to an already degraded pool. cddl/sbin/zfsd/case_file.cc * Keep a casefile for a removed drive open if it can't be immediately resolved by activating a spare. * Add a log message when degrading vdevs. cddl/sbin/zfsd/dev_ctl_event.cc * Fix replaying log messages in response to a config sync event. The event's name is different under FreeBSD than under Illumos. * Fix two printf formatting bugs in the log messages of EventBuffer::ExtractEvent(). Submitted by: asomers Approved by: ken (mentor) Sponsored by: Spectra Logic Corporation --- cddl/sbin/zfsd/case_file.cc | 19 +++++++++++++++++-- cddl/sbin/zfsd/dev_ctl_event.cc | 14 ++++++++++++-- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/cddl/sbin/zfsd/case_file.cc b/cddl/sbin/zfsd/case_file.cc index 9eda4ce8d35..06f2fd4b49a 100644 --- a/cddl/sbin/zfsd/case_file.cc +++ b/cddl/sbin/zfsd/case_file.cc @@ -331,6 +331,7 @@ CaseFile::ReEvaluate(const ZfsEvent &event) } if (event.Value("class") == "resource.fs.zfs.removed") { + bool spare_activated; /* * Discard any tentative I/O error events for @@ -340,7 +341,7 @@ CaseFile::ReEvaluate(const ZfsEvent &event) PurgeTentativeEvents(); /* Try to activate spares if they are available */ - ActivateSpare(); + spare_activated = ActivateSpare(); /* * Rescan the drives in the system to see if a recent @@ -348,7 +349,13 @@ CaseFile::ReEvaluate(const ZfsEvent &event) */ ZfsDaemon::RequestSystemRescan(); - consumed = true; + /* + * Consume the event if we successfully activated a spare. + * Otherwise, leave it in the unconsumed events list so that the + * future addition of a spare to this pool might be able to + * close the case + */ + consumed = spare_activated; } else if (event.Value("class") == "ereport.fs.zfs.io" || event.Value("class") == "ereport.fs.zfs.checksum") { @@ -792,9 +799,17 @@ CaseFile::OnGracePeriodEnded() /* Degrade the vdev and close the case. */ if (zpool_vdev_degrade(zpl.front(), (uint64_t)m_vdevGUID, VDEV_AUX_ERR_EXCEEDED) == 0) { + syslog(LOG_INFO, "Degrading vdev(%s/%s)", + PoolGUIDString().c_str(), VdevGUIDString().c_str()); Close(); return; } + else { + syslog(LOG_ERR, "Degrade vdev(%s/%s): %s: %s\n", + PoolGUIDString().c_str(), VdevGUIDString().c_str(), + libzfs_error_action(g_zfsHandle), + libzfs_error_description(g_zfsHandle)); + } } Serialize(); } diff --git a/cddl/sbin/zfsd/dev_ctl_event.cc b/cddl/sbin/zfsd/dev_ctl_event.cc index a58769f2e8a..8fdbe58e1e5 100644 --- a/cddl/sbin/zfsd/dev_ctl_event.cc +++ b/cddl/sbin/zfsd/dev_ctl_event.cc @@ -612,7 +612,7 @@ ZfsEvent::Process() const } /* On config syncs, replay any queued events first. */ - if (Value("type").find("ESC_ZFS_config_sync") == 0) + if (Value("type").find("misc.fs.zfs.config_sync") == 0) ZfsDaemon::ReplayUnconsumedEvents(); Log(LOG_INFO); @@ -676,7 +676,17 @@ ZfsEvent::Process() const Vdev vdev(zpl.front(), vdevConfig); caseFile = &CaseFile::Create(vdev); - caseFile->ReEvaluate(*this); + if ( caseFile->ReEvaluate(*this) == false) { + stringstream msg; + bool queued = ZfsDaemon::SaveEvent(*this); + int priority = queued ? LOG_INFO : LOG_ERR; + msg << "ZfsEvent::Process: Unconsumed event for vdev("; + msg << zpool_get_name(zpl.front()) << ","; + msg << vdev.GUID() << ") "; + msg << (queued ? "queued" : "dropped"); + syslog(priority, msg.str().c_str()); + return; + } } //- ZfsEvent Protected Methods ------------------------------------------------- From 1939d6bc81300ac7c9ad05d877f02c257a92b2f7 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Fri, 11 Oct 2013 23:11:33 +0000 Subject: [PATCH 56/89] Style, organization, and syntax improvements to zfsd. cddl/sbin/zfsd/guid.cc: cddl/sbin/zfsd/guid.h: cddl/sbin/zfsd/vdev.h: cddl/sbin/zfsd/vdev.cc: cddl/sbin/zfsd/Makefile: o Since GUIDs pertain to more than just vdevs, move its implementation out into its own file. No functional changes. cddl/sbin/zfsd/case_file.cc: cddl/sbin/zfsd/zfsd.cc: cddl/sbin/zfsd/dev_ctl_event.cc: o Local varables declared in the same block are aligned by their first character. cddl/sbin/zfsd/case_file.cc: cddl/sbin/zfsd/zfsd.cc: cddl/sbin/zfsd/dev_ctl_event.cc: o Function arguments wrap to the first column after the function's opening '('. o syslog strings referencing a method use the fully qualified method name. o There are no spaces after '(' or before ')' in conditionals. o Boolean operators in conditionals are aligned on the right side and indented based on the level of their operation. o Cache return values in local variable to avoid the need to compress whitespace to make lines fit in 80 cols. o There is one newline between function declarations. cddl/sbin/zfsd/vdev.h: cddl/sbin/zfsd/vdev.cc: o Wrap comments at 76 cols (i.e. what fmt does by default). o Method names start with a capital letter. o Implementations are never inlined within a class definition. This just encourages consumers of an interface to depend unnecessarily upon the implementation. cddl/sbin/zfsd/zfsd.cc: o Simple checks in conditionals (like '==' and '!=') do not require extra parenthesis. cddl/sbin/zfsd/dev_ctl_event.cc: o Use a constant format string so that syslog() cannot be confused by random '%' characters in the string we are logging. Submitted by: gibbs Approved by: ken (mentor) Sponsored by: Spectra Logic Corporation --- cddl/sbin/zfsd/Makefile | 3 +- cddl/sbin/zfsd/case_file.cc | 68 ++++++++-------- cddl/sbin/zfsd/dev_ctl_event.cc | 4 +- cddl/sbin/zfsd/guid.cc | 62 +++++++++++++++ cddl/sbin/zfsd/guid.h | 136 ++++++++++++++++++++++++++++++++ cddl/sbin/zfsd/vdev.cc | 10 --- cddl/sbin/zfsd/vdev.h | 52 +----------- cddl/sbin/zfsd/zfsd.cc | 15 ++-- 8 files changed, 247 insertions(+), 103 deletions(-) create mode 100644 cddl/sbin/zfsd/guid.cc create mode 100644 cddl/sbin/zfsd/guid.h diff --git a/cddl/sbin/zfsd/Makefile b/cddl/sbin/zfsd/Makefile index 5e0af13aaf7..571161c035e 100644 --- a/cddl/sbin/zfsd/Makefile +++ b/cddl/sbin/zfsd/Makefile @@ -4,11 +4,12 @@ PROG_CXX= zfsd SRCS= callout.cc \ case_file.cc \ dev_ctl_event.cc \ + guid.cc \ vdev.cc \ vdev_iterator.cc \ zfsd.cc \ zfsd_exception.cc \ - zpool_list.cc \ + zpool_list.cc \ zfsd_main.cc NO_MAN= YES diff --git a/cddl/sbin/zfsd/case_file.cc b/cddl/sbin/zfsd/case_file.cc index 06f2fd4b49a..37b9c6ae9bc 100644 --- a/cddl/sbin/zfsd/case_file.cc +++ b/cddl/sbin/zfsd/case_file.cc @@ -372,37 +372,36 @@ CaseFile::ReEvaluate(const ZfsEvent &event) bool CaseFile::ActivateSpare() { - nvlist_t *config, *nvroot; - nvlist_t **spares; - zpool_handle_t *zhp; - char *devPath, *vdev_type; - const char* poolname; - unsigned nspares, i; + nvlist_t *config, *nvroot; + nvlist_t **spares; + zpool_handle_t *zhp; + char *devPath, *vdev_type; + const char *poolname; + u_int nspares, i; + int error; ZpoolList zpl(ZpoolList::ZpoolByGUID, &m_poolGUID); if (zpl.empty()) { - syslog(LOG_ERR, "CaseFile::Replace: could not find pool for " - "pool_guid %ju", (uint64_t)m_poolGUID); + syslog(LOG_ERR, "CaseFile::ActivateSpare: Could not find pool " + "for pool_guid %ju.", (uint64_t)m_poolGUID); return (false); } zhp = zpl.front(); poolname = zpool_get_name(zhp); config = zpool_get_config(zhp, NULL); if (config == NULL) { - syslog(LOG_ERR, - "ActivateSpare: Could not find pool config for pool %s", - poolname); + syslog(LOG_ERR, "CaseFile::ActivateSpare: Could not find pool " + "config for pool %s", poolname); return (false); } if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &nvroot) != 0){ - syslog(LOG_ERR, - "ActivateSpare: Could not find vdev tree for pool %s", - poolname); + syslog(LOG_ERR, "CaseFile::ActivateSpare: Could not find vdev " + "tree for pool %s", poolname); return (false); } nspares = 0; nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES, &spares, - &nspares); + &nspares); if (nspares == 0) { /* The pool has no spares configured */ return (false); @@ -413,14 +412,14 @@ CaseFile::ActivateSpare() { if (nvlist_lookup_uint64_array(spares[i], ZPOOL_CONFIG_VDEV_STATS, (uint64_t**)&vs, &nstats) != 0) { - syslog(LOG_ERR, "ActivateSpare: Could not find vdev " - "stats for pool %s, spare %d", - poolname, i); + syslog(LOG_ERR, "CaseFile::ActivateSpare: Could not " + "find vdev stats for pool %s, spare %d", + poolname, i); return (false); } - if ( (vs->vs_aux != VDEV_AUX_SPARED) - && (vs->vs_state == VDEV_STATE_HEALTHY)) { + if ((vs->vs_aux != VDEV_AUX_SPARED) + && (vs->vs_state == VDEV_STATE_HEALTHY)) { /* We found a usable spare */ break; } @@ -431,15 +430,19 @@ CaseFile::ActivateSpare() { return (false); } - if (nvlist_lookup_string(spares[i], ZPOOL_CONFIG_PATH, &devPath) != 0){ - syslog(LOG_ERR, "ActivateSpare: Cannot determine the path of " - "pool %s, spare %d", poolname, i); + error = nvlist_lookup_string(spares[i], ZPOOL_CONFIG_PATH, &devPath); + if (error != 0) { + syslog(LOG_ERR, "CaseFile::ActivateSpare: Cannot determine " + "the path of pool %s, spare %d. Error %d", + poolname, i, error); return (false); } - if (nvlist_lookup_string(spares[i], ZPOOL_CONFIG_TYPE, &vdev_type)!= 0){ - syslog(LOG_ERR, "ActivateSpare: Cannot determine the vdev type " - "of pool %s, spare %d", poolname, i); + error = nvlist_lookup_string(spares[i], ZPOOL_CONFIG_TYPE, &vdev_type); + if (error != 0) { + syslog(LOG_ERR, "CaseFile::ActivateSpare: Cannot determine " + "the vdev type of pool %s, spare %d. Error %d", + poolname, i, error); return (false); } @@ -728,7 +731,6 @@ CaseFile::SerializeEvList(const DevCtlEventList events, int fd, } } - void CaseFile::Serialize() { @@ -800,15 +802,17 @@ CaseFile::OnGracePeriodEnded() if (zpool_vdev_degrade(zpl.front(), (uint64_t)m_vdevGUID, VDEV_AUX_ERR_EXCEEDED) == 0) { syslog(LOG_INFO, "Degrading vdev(%s/%s)", - PoolGUIDString().c_str(), VdevGUIDString().c_str()); + PoolGUIDString().c_str(), + VdevGUIDString().c_str()); Close(); return; } else { syslog(LOG_ERR, "Degrade vdev(%s/%s): %s: %s\n", - PoolGUIDString().c_str(), VdevGUIDString().c_str(), - libzfs_error_action(g_zfsHandle), - libzfs_error_description(g_zfsHandle)); + PoolGUIDString().c_str(), + VdevGUIDString().c_str(), + libzfs_error_action(g_zfsHandle), + libzfs_error_description(g_zfsHandle)); } } Serialize(); @@ -824,7 +828,7 @@ CaseFile::Replace(const char* vdev_type, const char* path) { ZpoolList zpl(ZpoolList::ZpoolByGUID, &m_poolGUID); if (zpl.empty()) { syslog(LOG_ERR, "CaseFile::Replace: could not find pool for " - "pool_guid %ju", (uint64_t)m_poolGUID); + "pool_guid %ju.", (uint64_t)m_poolGUID); return (false); } zhp = zpl.front(); diff --git a/cddl/sbin/zfsd/dev_ctl_event.cc b/cddl/sbin/zfsd/dev_ctl_event.cc index 8fdbe58e1e5..b62b04bd397 100644 --- a/cddl/sbin/zfsd/dev_ctl_event.cc +++ b/cddl/sbin/zfsd/dev_ctl_event.cc @@ -165,7 +165,7 @@ DevCtlEvent::CreateEvent(const string &eventString) EventFactoryRegistry::iterator foundMethod(s_factoryRegistry.find(key)); if (foundMethod == s_factoryRegistry.end()) { syslog(LOG_INFO, "DevCtlEvent::CreateEvent: unhandled event %s", - eventString.c_str()); + eventString.c_str()); return (NULL); } return ((foundMethod->second)(type, nvpairs, eventString)); @@ -684,7 +684,7 @@ ZfsEvent::Process() const msg << zpool_get_name(zpl.front()) << ","; msg << vdev.GUID() << ") "; msg << (queued ? "queued" : "dropped"); - syslog(priority, msg.str().c_str()); + syslog(priority, "%s", msg.str().c_str()); return; } } diff --git a/cddl/sbin/zfsd/guid.cc b/cddl/sbin/zfsd/guid.cc new file mode 100644 index 00000000000..70ea7b90898 --- /dev/null +++ b/cddl/sbin/zfsd/guid.cc @@ -0,0 +1,62 @@ +/*- + * Copyright (c) 2012 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Alan Somers (Spectra Logic Corporation) + * + * $FreeBSD$ + */ + +/** + * \file guid.cc + * + * Implementation of the Guid class. + */ +#include + +#include +#include +#include + +#include +#include + +#include "guid.h" + +__FBSDID("$FreeBSD$"); +/*=========================== Class Implementations ==========================*/ +/*----------------------------------- Guid -----------------------------------*/ +std::ostream& +operator<< (std::ostream& out, Guid g) +{ + if (g.IsValid()) + out << (uint64_t)g; + else + out << "None"; + return (out); +} diff --git a/cddl/sbin/zfsd/guid.h b/cddl/sbin/zfsd/guid.h new file mode 100644 index 00000000000..bcdcaa1359e --- /dev/null +++ b/cddl/sbin/zfsd/guid.h @@ -0,0 +1,136 @@ +/*- + * Copyright (c) 2012 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Alan Somers (Spectra Logic Corporation) + * + * $FreeBSD$ + */ + +/** + * \file guid.h + * + * Definition of the Guid class. + */ +#ifndef _GUID_H_ +#define _GUID_H_ + +#include + +/** + * \brief Object that represents guids. + * + * It can generally be manipulated as a uint64_t, but with a special + * value "None" that does not equal any valid guid. + * + * As of this writing, spa_generate_guid() in spa_misc.c explicitly + * refuses to return a guid of 0. So this class uses 0 as a flag + * value for "None". In the future, if 0 is allowed to be a valid + * guid, the implementation of this class must change. + */ +class Guid +{ +public: + /* Constructors */ + Guid(); + Guid(uint64_t guid); + + /* Assignment */ + Guid& operator=(const Guid& rhs); + + /* Test the validity of this guid. */ + bool IsValid() const; + + /* Comparison to other Guid operators */ + bool operator==(const Guid& rhs) const; + bool operator!=(const Guid& rhs) const; + + /* Integer conversion operators */ + operator uint64_t() const; + operator bool() const; + + static const uint64_t NONE_FLAG = 0; +protected: + /* The stored value. 0 is a flag for "None" */ + uint64_t m_GUID; +}; + +//- Guid Inline Public Methods ------------------------------------------------ +inline +Guid::Guid() + : m_GUID(NONE_FLAG) +{ +} + +inline +Guid::Guid(uint64_t guid) + : m_GUID(guid) +{ +} + +inline Guid& +Guid::operator=(const Guid &rhs) +{ + m_GUID = rhs.m_GUID; + return (*this); +} + +inline bool +Guid::IsValid() const +{ + return (m_GUID != NONE_FLAG); +} + +inline bool +Guid::operator==(const Guid& rhs) const +{ + return (m_GUID == rhs.m_GUID); +} + +inline bool +Guid::operator!=(const Guid& rhs) const +{ + return (m_GUID != rhs.m_GUID); +} + +inline +Guid::operator uint64_t() const +{ + return (m_GUID); +} + +inline +Guid::operator bool() const +{ + return (m_GUID != NONE_FLAG); +} + +/** Convert the GUID into its string representation */ +std::ostream& operator<< (std::ostream& out, Guid g); + +#endif /* _GUID_H_ */ diff --git a/cddl/sbin/zfsd/vdev.cc b/cddl/sbin/zfsd/vdev.cc index 1f200a3e13f..2a24f331f0e 100644 --- a/cddl/sbin/zfsd/vdev.cc +++ b/cddl/sbin/zfsd/vdev.cc @@ -50,16 +50,6 @@ __FBSDID("$FreeBSD$"); using std::stringstream; /*=========================== Class Implementations ==========================*/ -/*----------------------------------- Guid -----------------------------------*/ -std::ostream& operator<< (std::ostream& out, Guid g){ - if (g.isValid()) - out << (uint64_t) g; - else - out << "None"; - return (out); -} - - /*----------------------------------- Vdev -----------------------------------*/ Vdev::Vdev(zpool_handle_t *pool, nvlist_t *config) : m_poolConfig(zpool_get_config(pool, NULL)), diff --git a/cddl/sbin/zfsd/vdev.h b/cddl/sbin/zfsd/vdev.h index c47e6c3438f..50b915204c5 100644 --- a/cddl/sbin/zfsd/vdev.h +++ b/cddl/sbin/zfsd/vdev.h @@ -46,56 +46,7 @@ #include #include - -/** - * \brief Object that represents guids. - * - * It can generally be manipulated as a uint64_t, but with a special value - * "None" that does not equal any valid guid. - * - * As of this writing, spa_generate_guid() in spa_misc.c explicitly refuses to - * return a guid of 0. So this class uses 0 as a flag value for "None". In the - * future, if 0 is allowed to be a valid guid, the implementation of this class - * must change. - */ -class Guid -{ -public: - /* Constructors */ - Guid(uint64_t guid) : m_GUID(guid) {}; - Guid() { m_GUID = NONE_FLAG; }; - - /* Assignment */ - Guid& operator=(const uint64_t& other) { - m_GUID = other; - return (*this); - }; - - /* Test the validity of this guid. */ - bool isValid() const { return ((bool)m_GUID); }; - - /* Comparison to other Guid operators */ - bool operator==(const Guid& other) const { - return (m_GUID == other.m_GUID); - }; - bool operator!=(const Guid& other) const { - return (m_GUID != other.m_GUID); - }; - - /* Integer conversion operators */ - operator uint64_t() const { return (m_GUID); }; - operator bool() const { return (m_GUID != NONE_FLAG); }; - -protected: - const static uint64_t NONE_FLAG = 0; - /* The stored value. 0 is a flag for "None" */ - uint64_t m_GUID; -}; - - -/** Convert the GUID into its string representation */ -std::ostream& operator<< (std::ostream& out, Guid g); - +#include "guid.h" /** * \brief Wrapper class for a vdev's name/value configuration list @@ -161,6 +112,7 @@ private: nvlist_t *m_config; }; +//- Vdev Inline Public Methods ------------------------------------------------ inline Guid Vdev::PoolGUID() const { diff --git a/cddl/sbin/zfsd/zfsd.cc b/cddl/sbin/zfsd/zfsd.cc index 36023db5c9a..121f5bc291c 100644 --- a/cddl/sbin/zfsd/zfsd.cc +++ b/cddl/sbin/zfsd/zfsd.cc @@ -518,16 +518,17 @@ ZfsDaemon::EventsPending() fds->events = POLLIN; fds->revents = 0; result = poll(fds, NUM_ELEMENTS(fds), /*timeout*/0); - } while ( (result == -1) && (errno == EINTR) ) ; + } while (result == -1 && errno == EINTR); if (result == -1) { /* Unexpected error; try reconnecting the socket */ - throw ZfsdException( - "ZfsdDaemon::EventsPending(): Unexpected error from poll()"); + throw ZfsdException("ZfsdDaemon::EventsPending(): " + "Unexpected error from poll()"); } if ((fds->revents & POLLHUP) != 0) { - /* The other end hung up the socket. Throw an exception + /* + * The other end hung up the socket. Throw an exception * so ZfsDaemon will try to reconnect */ throw ZfsdException("ZfsDaemon::EventsPending(): Got POLLHUP"); @@ -535,8 +536,8 @@ ZfsDaemon::EventsPending() if ((fds->revents & POLLERR) != 0) { /* Try reconnecting. */ - throw ZfsdException( - "ZfsdDaemon:EventsPending(): Got POLLERR. Reconnecting."); + throw ZfsdException("ZfsdDaemon:EventsPending(): Got POLLERR. " + " Reconnecting."); } return ((fds->revents & POLLIN) != 0); @@ -703,5 +704,3 @@ ZfsDaemon::EventLoop() } } } - - From 74c8ed989a34c0ff30851832c22969ef476e8ebc Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Mon, 14 Oct 2013 20:51:51 +0000 Subject: [PATCH 57/89] Move parsing policy for invalid GUIDs into the Guid class. cddl/sbin/zfsd/guid.cc: cddl/sbin/zfsd/guid.h: o Allow a Guid to be constructed from a string. The empty string is treated as an invalid Guid. o Rename NONE_FLAG to INVALID_GUID to clarify its meaning. The concept of a Guid being "none" only applies in certain contexts like reading a spare label. cddl/sbin/zfsd/dev_ctl_event.cc: Modify ZfsEvent's constructor to just use Guid's string contructor instead of embedding logic to manually create invalid guids when these nvpairs do not exist. stable/cddl/sbin/zfsd/vdev.cc: Rely on Guid's default constructor creating an invalid guid to simplify the handling of pool GUIDs parsed from a label. This also fixes a vdev/pool guid variable mixup bug that, due to Guid's default construction, had no ill effects. Submitted by: gibbs Approved by: ken (mentor) Sponsored By: Spectra Logic Corporation --- cddl/sbin/zfsd/dev_ctl_event.cc | 20 +++----------------- cddl/sbin/zfsd/guid.cc | 16 ++++++++++++++++ cddl/sbin/zfsd/guid.h | 17 +++++++++-------- cddl/sbin/zfsd/vdev.cc | 11 ++++++++--- 4 files changed, 36 insertions(+), 28 deletions(-) diff --git a/cddl/sbin/zfsd/dev_ctl_event.cc b/cddl/sbin/zfsd/dev_ctl_event.cc index b62b04bd397..c08ef9e6bed 100644 --- a/cddl/sbin/zfsd/dev_ctl_event.cc +++ b/cddl/sbin/zfsd/dev_ctl_event.cc @@ -692,24 +692,10 @@ ZfsEvent::Process() const //- ZfsEvent Protected Methods ------------------------------------------------- ZfsEvent::ZfsEvent(DevCtlEvent::Type type, NVPairMap &nvpairs, const string &eventString) - : DevCtlEvent(type, nvpairs, eventString) + : DevCtlEvent(type, nvpairs, eventString), + m_poolGUID(Guid(Value("pool_guid"))), + m_vdevGUID(Guid(Value("vdev_guid"))) { - /* - * These are zero on conversion failure as will happen if - * Value returns the empty string. - */ - if (Contains("pool_guid")) { - m_poolGUID = (uint64_t)strtoumax(Value("pool_guid").c_str(), - NULL, 0); - } - else - m_poolGUID = Guid(); - if (Contains("vdev_guid")) { - m_vdevGUID = (uint64_t)strtoumax(Value("vdev_guid").c_str(), - NULL, 0); - } - else - m_vdevGUID = Guid(); } ZfsEvent::ZfsEvent(const ZfsEvent &src) diff --git a/cddl/sbin/zfsd/guid.cc b/cddl/sbin/zfsd/guid.cc index 70ea7b90898..08c1ce6a107 100644 --- a/cddl/sbin/zfsd/guid.cc +++ b/cddl/sbin/zfsd/guid.cc @@ -49,8 +49,24 @@ #include "guid.h" __FBSDID("$FreeBSD$"); +/*============================ Namespace Control =============================*/ +using std::string; + /*=========================== Class Implementations ==========================*/ /*----------------------------------- Guid -----------------------------------*/ +Guid::Guid(const string &guidString) +{ + if (guidString.empty()) { + m_GUID = INVALID_GUID; + } else { + /* + * strtoumax() returns zero on conversion failure + * which nicely matches our choice for INVALID_GUID. + */ + m_GUID = (uint64_t)strtoumax(guidString.c_str(), NULL, 0); + } +} + std::ostream& operator<< (std::ostream& out, Guid g) { diff --git a/cddl/sbin/zfsd/guid.h b/cddl/sbin/zfsd/guid.h index bcdcaa1359e..7a280afcfc7 100644 --- a/cddl/sbin/zfsd/guid.h +++ b/cddl/sbin/zfsd/guid.h @@ -46,11 +46,11 @@ * \brief Object that represents guids. * * It can generally be manipulated as a uint64_t, but with a special - * value "None" that does not equal any valid guid. + * value INVALID_GUID that does not equal any valid guid. * * As of this writing, spa_generate_guid() in spa_misc.c explicitly - * refuses to return a guid of 0. So this class uses 0 as a flag - * value for "None". In the future, if 0 is allowed to be a valid + * refuses to return a guid of 0. So this class uses 0 as the value + * for INVALID_GUID. In the future, if 0 is allowed to be a valid * guid, the implementation of this class must change. */ class Guid @@ -59,6 +59,7 @@ public: /* Constructors */ Guid(); Guid(uint64_t guid); + Guid(const std::string &guid); /* Assignment */ Guid& operator=(const Guid& rhs); @@ -74,16 +75,16 @@ public: operator uint64_t() const; operator bool() const; - static const uint64_t NONE_FLAG = 0; + static const uint64_t INVALID_GUID = 0; protected: - /* The stored value. 0 is a flag for "None" */ + /* The integer value of the GUID. */ uint64_t m_GUID; }; //- Guid Inline Public Methods ------------------------------------------------ inline Guid::Guid() - : m_GUID(NONE_FLAG) + : m_GUID(INVALID_GUID) { } @@ -103,7 +104,7 @@ Guid::operator=(const Guid &rhs) inline bool Guid::IsValid() const { - return (m_GUID != NONE_FLAG); + return (m_GUID != INVALID_GUID); } inline bool @@ -127,7 +128,7 @@ Guid::operator uint64_t() const inline Guid::operator bool() const { - return (m_GUID != NONE_FLAG); + return (m_GUID != INVALID_GUID); } /** Convert the GUID into its string representation */ diff --git a/cddl/sbin/zfsd/vdev.cc b/cddl/sbin/zfsd/vdev.cc index 2a24f331f0e..1d95b06dd8a 100644 --- a/cddl/sbin/zfsd/vdev.cc +++ b/cddl/sbin/zfsd/vdev.cc @@ -89,10 +89,15 @@ Vdev::Vdev(nvlist_t *labelConfig) : m_poolConfig(labelConfig) { uint64_t raw_guid; + + /* + * Spares do not have a Pool GUID. Tolerate its absence. + * Code accessing this Vdev in a context where the Pool GUID is + * required will find it invalid (as it is upon Vdev construction) + * and act accordingly. + */ if (nvlist_lookup_uint64(labelConfig, ZPOOL_CONFIG_POOL_GUID, - &raw_guid) != 0) - m_vdevGUID = Guid(); - else + &raw_guid) == 0) m_poolGUID = raw_guid; if (nvlist_lookup_uint64(labelConfig, ZPOOL_CONFIG_GUID, From d8c6545ab07a3054facff785833284f096bff80d Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Mon, 14 Oct 2013 20:53:51 +0000 Subject: [PATCH 58/89] Modify ParseException so that it records the parse buffer upon construction. This allows it to be caught at any level in the program and logged with full fidelity. cddl/sbin/zfsd/dev_ctl_event.cc: cddl/sbin/zfsd/dev_ctl_event.h: Update ParseException implementation: take the parse buffer string on construction and don't require the parse buffer string when logging or converting the exception to a string. cddl/sbin/zfsd/dev_ctl_event.cc: cddl/sbin/zfsd/case_file.cc: Adjust to Log() and ToString() taking no arguments. Submitted by: gibbs Approved by: ken (mentor) Sponsored By: Spectra Logic --- cddl/sbin/zfsd/case_file.cc | 2 +- cddl/sbin/zfsd/dev_ctl_event.cc | 26 ++++++++++++++------------ cddl/sbin/zfsd/dev_ctl_event.h | 30 +++++++++++++++++++----------- 3 files changed, 34 insertions(+), 24 deletions(-) diff --git a/cddl/sbin/zfsd/case_file.cc b/cddl/sbin/zfsd/case_file.cc index 37b9c6ae9bc..f51d19bb3f3 100644 --- a/cddl/sbin/zfsd/case_file.cc +++ b/cddl/sbin/zfsd/case_file.cc @@ -649,7 +649,7 @@ CaseFile::DeSerializeFile(const char *fileName) } } catch (const ParseException &exp) { - exp.Log(evString); + exp.Log(); if (caseFile != existingCaseFile) delete caseFile; diff --git a/cddl/sbin/zfsd/dev_ctl_event.cc b/cddl/sbin/zfsd/dev_ctl_event.cc index c08ef9e6bed..19000350a2d 100644 --- a/cddl/sbin/zfsd/dev_ctl_event.cc +++ b/cddl/sbin/zfsd/dev_ctl_event.cc @@ -61,7 +61,7 @@ using std::stringstream; /*------------------------------ ParseException ------------------------------*/ //- ParseException Public Methods ---------------------------------------------- string -ParseException::ToString(const string &parsedBuffer) const +ParseException::ToString() const { stringstream result; @@ -82,9 +82,9 @@ ParseException::ToString(const string &parsedBuffer) const } result << "exception on buffer: \'"; if (GetOffset() == 0) { - result << parsedBuffer << '\'' << endl; + result << m_parsedBuffer << '\'' << endl; } else { - string markedBuffer(parsedBuffer); + string markedBuffer(m_parsedBuffer); markedBuffer.insert(GetOffset(), ""); result << markedBuffer << '\'' << endl; @@ -94,14 +94,14 @@ ParseException::ToString(const string &parsedBuffer) const } void -ParseException::Log(const string &parsedBuffer) const +ParseException::Log() const { int priority(LOG_ERR); if (Type() == DISCARDED_EVENT_TYPE) priority = LOG_INFO; - syslog(priority, "%s", ToString(parsedBuffer).c_str()); + syslog(priority, "%s", ToString().c_str()); } /*-------------------------------- DevCtlEvent -------------------------------*/ @@ -149,7 +149,7 @@ DevCtlEvent::CreateEvent(const string &eventString) ParseEventString(type, eventString, nvpairs); } catch (const ParseException &exp) { if (exp.GetType() == ParseException::INVALID_FORMAT) - exp.Log(eventString); + exp.Log(); return (NULL); } @@ -312,14 +312,14 @@ DevCtlEvent::ParseEventString(DevCtlEvent::Type type, end = eventString.find_first_of(" \t\n", start); if (end == string::npos) throw ParseException(ParseException::INVALID_FORMAT, - start); + eventString, start); nvpairs["device-name"] = eventString.substr(start, end - start); start = eventString.find(" on ", end); if (end == string::npos) throw ParseException(ParseException::INVALID_FORMAT, - start); + eventString, start); start += 4; end = eventString.find_first_of(" \t\n", start); nvpairs["parent"] = eventString.substr(start, end); @@ -327,9 +327,11 @@ DevCtlEvent::ParseEventString(DevCtlEvent::Type type, case NOTIFY: break; case NOMATCH: - throw ParseException(ParseException::DISCARDED_EVENT_TYPE); + throw ParseException(ParseException::DISCARDED_EVENT_TYPE, + eventString); default: - throw ParseException(ParseException::UNKNOWN_EVENT_TYPE); + throw ParseException(ParseException::UNKNOWN_EVENT_TYPE, + eventString); } /* Process common "key=value" format. */ @@ -349,7 +351,7 @@ DevCtlEvent::ParseEventString(DevCtlEvent::Type type, start = eventString.find_last_of("! \t\n", end); if (start == string::npos) throw ParseException(ParseException::INVALID_FORMAT, - end); + eventString, end); start++; string key(eventString.substr(start, end - start)); @@ -360,7 +362,7 @@ DevCtlEvent::ParseEventString(DevCtlEvent::Type type, start = end + 1; if (start >= eventString.length()) throw ParseException(ParseException::INVALID_FORMAT, - end); + eventString, end); end = eventString.find_first_of(" \t\n", start); if (end == string::npos) end = eventString.length() - 1; diff --git a/cddl/sbin/zfsd/dev_ctl_event.h b/cddl/sbin/zfsd/dev_ctl_event.h index 7916e761a3f..d984f404576 100644 --- a/cddl/sbin/zfsd/dev_ctl_event.h +++ b/cddl/sbin/zfsd/dev_ctl_event.h @@ -80,18 +80,21 @@ public: /** * Constructor * - * \param type The type of this exception. - * \param offset The location in the parse buffer where this - * exception occurred. + * \param type The type of this exception. + * \param parsedBuffer The parsing buffer active at the time of + * the exception. + * \param offset The location in the parse buffer where this + * exception occurred. */ - ParseException(Type type, size_t offset = 0); + ParseException(Type type, const string &parsedBuffer, + size_t offset = 0); /** * Accessor * * \return The classification for this exception. */ - Type GetType() const; + Type GetType() const; /** * Accessor @@ -99,37 +102,42 @@ public: * \return The offset into the event string where this exception * occurred. */ - size_t GetOffset() const; + size_t GetOffset() const; /** * Convert an exception into a human readable string. * * \param parsedBuffer The event buffer that caused the failure. */ - string ToString(const string &parsedBuffer) const; + string ToString() const; /** * Log exception data to syslog. * * \param parsedBuffer The event buffer that caused the failure. */ - void Log(const string &parsedBuffer) const; + void Log() const; private: /** The type of this exception. */ - Type m_type; + Type m_type; + + /** The parsing buffer that was active at the time of the exception. */ + const string m_parsedBuffer; /** * The offset into the event string buffer from where this * exception was triggered. */ - size_t m_offset; + size_t m_offset; }; //- ParseException Inline Public Methods --------------------------------------- inline -ParseException::ParseException(Type type, size_t offset) +ParseException::ParseException(Type type, const string &parsedBuffer, + size_t offset) : m_type(type), + m_parsedBuffer(parsedBuffer), m_offset(offset) { } From 7b62581d6019c2f957c0b250b576748a1aa378fa Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Mon, 14 Oct 2013 20:56:51 +0000 Subject: [PATCH 59/89] In the static method CaseFile::DeSerializeFile(), break out the acutal parsing of the serialization stream into the instance method CaseFile::DeSerialize(ifstream &caseStream). cddl/sbin/zfsd/case_file.h: Declaration of CaseFile::DeSerialize(). cddl/sbin/zfsd/case_file.cc: o Factor out CaseFile::DeSerialize() from CaseFile::DeSerializeFile(). o In CaseFile::DeSerialize() remove a superfluous return statement after a throw. Submitted by: gibbs Approved by: ken (mentor) Sponsored by: Spectra Logic Corporation --- cddl/sbin/zfsd/case_file.cc | 88 +++++++++++++++++++------------------ cddl/sbin/zfsd/case_file.h | 7 +++ 2 files changed, 53 insertions(+), 42 deletions(-) diff --git a/cddl/sbin/zfsd/case_file.cc b/cddl/sbin/zfsd/case_file.cc index f51d19bb3f3..8d24507239f 100644 --- a/cddl/sbin/zfsd/case_file.cc +++ b/cddl/sbin/zfsd/case_file.cc @@ -552,7 +552,6 @@ void CaseFile::DeSerializeFile(const char *fileName) { string fullName(s_caseFilePath + '/' + fileName); - string evString; CaseFile *existingCaseFile(NULL); CaseFile *caseFile(NULL); @@ -603,50 +602,11 @@ CaseFile::DeSerializeFile(const char *fileName) } ifstream caseStream(fullName.c_str()); - if (!caseStream) { + if (!caseStream) throw ZfsdException("CaseFile::DeSerialize: Unable to " "read %s.\n", fileName); - return; - } - stringstream fakeDevdSocket(stringstream::in|stringstream::out); - IstreamReader caseReader(&fakeDevdSocket); - /* Re-load EventData */ - EventBuffer eventBuffer(caseReader); - caseStream >> std::noskipws >> std::ws; - while (!caseStream.eof()) { - /* - * Outline: - * read the beginning of a line and check it for - * "tentative". If found, discard "tentative". - * Shove into fakeDevdSocket. - * call ExtractEvent - * continue - */ - DevCtlEventList* destEvents; - string tentFlag("tentative "); - string line; - std::stringbuf lineBuf; - caseStream.get(lineBuf); - caseStream.ignore(); /*discard the newline character*/ - line = lineBuf.str(); - if (line.compare(0, tentFlag.size(), tentFlag) == 0) { - line.erase(0, tentFlag.size()); - destEvents = &caseFile->m_tentativeEvents; - } else { - destEvents = &caseFile->m_events; - } - fakeDevdSocket << line; - fakeDevdSocket << '\n'; - while (eventBuffer.ExtractEvent(evString)) { - DevCtlEvent *event(DevCtlEvent::CreateEvent( - evString)); - if (event != NULL) { - destEvents->push_back(event); - caseFile->RegisterCallout(*event); - } - } - } + caseFile->DeSerialize(caseStream); } catch (const ParseException &exp) { exp.Log(); @@ -758,6 +718,50 @@ CaseFile::Serialize() close(fd); } +void +CaseFile::DeSerialize(ifstream &caseStream) +{ + stringstream fakeDevdSocket(stringstream::in|stringstream::out); + IstreamReader caseReader(&fakeDevdSocket); + EventBuffer eventBuffer(caseReader); + string evString; + + caseStream >> std::noskipws >> std::ws; + while (!caseStream.eof()) { + /* + * Outline: + * read the beginning of a line and check it for + * "tentative". If found, discard "tentative". + * Shove into fakeDevdSocket. + * call ExtractEvent + * continue + */ + DevCtlEventList* destEvents; + string tentFlag("tentative "); + string line; + std::stringbuf lineBuf; + + caseStream.get(lineBuf); + caseStream.ignore(); /*discard the newline character*/ + line = lineBuf.str(); + if (line.compare(0, tentFlag.size(), tentFlag) == 0) { + line.erase(0, tentFlag.size()); + destEvents = &m_tentativeEvents; + } else { + destEvents = &m_events; + } + fakeDevdSocket << line; + fakeDevdSocket << '\n'; + while (eventBuffer.ExtractEvent(evString)) { + DevCtlEvent *event(DevCtlEvent::CreateEvent(evString)); + if (event != NULL) { + destEvents->push_back(event); + RegisterCallout(*event); + } + } + } +} + void CaseFile::Close() { diff --git a/cddl/sbin/zfsd/case_file.h b/cddl/sbin/zfsd/case_file.h index 84a51c77556..cce86346e7f 100644 --- a/cddl/sbin/zfsd/case_file.h +++ b/cddl/sbin/zfsd/case_file.h @@ -254,6 +254,13 @@ protected: */ void Serialize(); + /** + * \brief Retrieve event data from a serialization stream. + * + * \param caseStream The serializtion stream to parse. + */ + void DeSerialize(std::ifstream &caseStream); + /** * \brief Serializes the supplied event list and writes it to fd * From bda5ae3232719b82790cc120e690bbf514337ed0 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Mon, 14 Oct 2013 20:59:17 +0000 Subject: [PATCH 60/89] Remove information about devd reconnection policies that was distributed within comments and syslog() strings in functions well below where these decisions are made. stable/cddl/sbin/zfsd/zfsd.cc: o In ZfsDaemon::DisconnectFromDevd(), log when the connection is torn down. This removes the need to guess that this will happen in comments or log strings in code that all eventually sends us to this function. o In ZfsDaemon::EventsPending(), treat poll returning -1 with an errno other than EINTR, or 0 as a program terminating event just as it is in the other case where we invoke poll. These events indicate poll or our code is seriously broken. o In ZfsDaemon::EventsPending(), prioritize emitting a POLLERR event over POLLHUP in case they are both set. POLLHUP is benign, but POLLERR may indicate something we need to investigate. o In ZfsDaemon::EventLoop(), remove comments and log strings that claim knowledge of policy implemented in EventLoop's caller, ZfsDaemon::Run(). Submitted by: gibbs Approved by: ken (mentor) Obtained from: Spectra Logic Corporation --- cddl/sbin/zfsd/zfsd.cc | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/cddl/sbin/zfsd/zfsd.cc b/cddl/sbin/zfsd/zfsd.cc index 121f5bc291c..c7b2a9cf600 100644 --- a/cddl/sbin/zfsd/zfsd.cc +++ b/cddl/sbin/zfsd/zfsd.cc @@ -450,6 +450,9 @@ ZfsDaemon::ConnectToDevd() void ZfsDaemon::DisconnectFromDevd() { + if (s_devdSockFD != -1) + syslog(LOG_INFO, "Disconnecting from devd."); + delete s_reader; s_reader = NULL; close(s_devdSockFD); @@ -520,25 +523,19 @@ ZfsDaemon::EventsPending() result = poll(fds, NUM_ELEMENTS(fds), /*timeout*/0); } while (result == -1 && errno == EINTR); - if (result == -1) { - /* Unexpected error; try reconnecting the socket */ - throw ZfsdException("ZfsdDaemon::EventsPending(): " - "Unexpected error from poll()"); - } + if (result == -1) + err(1, "Polling for devd events failed"); - if ((fds->revents & POLLHUP) != 0) { - /* - * The other end hung up the socket. Throw an exception - * so ZfsDaemon will try to reconnect - */ - throw ZfsdException("ZfsDaemon::EventsPending(): Got POLLHUP"); - } + if (result == 0) + errx(1, "Unexpected result of 0 from poll. Exiting"); - if ((fds->revents & POLLERR) != 0) { - /* Try reconnecting. */ - throw ZfsdException("ZfsdDaemon:EventsPending(): Got POLLERR. " - " Reconnecting."); - } + if ((fds->revents & POLLERR) != 0) + throw ZfsdException("ZfsdDaemon:EventsPending(): " + "POLLERR detected on devd socket."); + + if ((fds->revents & POLLHUP) != 0) + throw ZfsdException("ZfsDaemon::EventsPending(): " + "POLLHUP detected on devd socket."); return ((fds->revents & POLLIN) != 0); } @@ -692,14 +689,12 @@ ZfsDaemon::EventLoop() } if ((fds[0].revents & POLLERR) != 0) { - /* Try reconnecting. */ - syslog(LOG_INFO, "Error on socket. Disconnecting."); + syslog(LOG_INFO, "POLLERROR detected on devd socket."); break; } if ((fds[0].revents & POLLHUP) != 0) { - /* Try reconnecting. */ - syslog(LOG_INFO, "Hup on socket. Disconnecting."); + syslog(LOG_INFO, "POLLHUP detected on devd socket."); break; } } From 918b3e7429092ac001835479c828142eafd50d1b Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Mon, 14 Oct 2013 21:05:09 +0000 Subject: [PATCH 61/89] cddl/sbin/zfsd/zfsd.cc In ZfsdDaemon::EventsPending(), we do not block waiting for events, so poll will return 0 if no events are pending. Don't treat this as a fatal error. Submitted by: gibbs Approved by: ken (mentor) Sponsored by: Spectra Logic Corporation --- cddl/sbin/zfsd/zfsd.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/cddl/sbin/zfsd/zfsd.cc b/cddl/sbin/zfsd/zfsd.cc index c7b2a9cf600..b3af2f6ce96 100644 --- a/cddl/sbin/zfsd/zfsd.cc +++ b/cddl/sbin/zfsd/zfsd.cc @@ -526,9 +526,6 @@ ZfsDaemon::EventsPending() if (result == -1) err(1, "Polling for devd events failed"); - if (result == 0) - errx(1, "Unexpected result of 0 from poll. Exiting"); - if ((fds->revents & POLLERR) != 0) throw ZfsdException("ZfsdDaemon:EventsPending(): " "POLLERR detected on devd socket."); From 03af32970c280755e79931783497e3af35f49416 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Mon, 14 Oct 2013 21:08:31 +0000 Subject: [PATCH 62/89] Fix compiler warnings in zfsd cddl/sbin/zfsd/case_file.cc: Use proper printf/scanf format specifiers so Zfsd operates on uint64_t variables instead of uintmax_t variables that are then assumed to be 64 bits in size. cddl/sbin/zfsd/case_file.cc: cddl/sbin/zfsd/vdev.cc: When fetching datastructures from nvlists that are an array of uint64_ts, use a uint64_t pointer to call into the nvlist API and a reinterpret_cast<> to convert it to the pointer type of interest. Fixes strict-aliasing warnings. Submitted by: gibbs Approved by: ken (mentor) Sponsored by: Spectra Logic Corporation --- cddl/sbin/zfsd/case_file.cc | 22 ++++++++++++---------- cddl/sbin/zfsd/vdev.cc | 5 ++++- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/cddl/sbin/zfsd/case_file.cc b/cddl/sbin/zfsd/case_file.cc index 8d24507239f..c838e5cd674 100644 --- a/cddl/sbin/zfsd/case_file.cc +++ b/cddl/sbin/zfsd/case_file.cc @@ -383,7 +383,7 @@ CaseFile::ActivateSpare() { ZpoolList zpl(ZpoolList::ZpoolByGUID, &m_poolGUID); if (zpl.empty()) { syslog(LOG_ERR, "CaseFile::ActivateSpare: Could not find pool " - "for pool_guid %ju.", (uint64_t)m_poolGUID); + "for pool_guid %"PRIu64".", (uint64_t)m_poolGUID); return (false); } zhp = zpl.front(); @@ -407,16 +407,18 @@ CaseFile::ActivateSpare() { return (false); } for (i = 0; i < nspares; i++) { + uint64_t *nvlist_array; vdev_stat_t *vs; - unsigned nstats; + uint_t nstats; if (nvlist_lookup_uint64_array(spares[i], - ZPOOL_CONFIG_VDEV_STATS, (uint64_t**)&vs, &nstats) != 0) { + ZPOOL_CONFIG_VDEV_STATS, &nvlist_array, &nstats) != 0) { syslog(LOG_ERR, "CaseFile::ActivateSpare: Could not " "find vdev stats for pool %s, spare %d", poolname, i); return (false); } + vs = reinterpret_cast(nvlist_array); if ((vs->vs_aux != VDEV_AUX_SPARED) && (vs->vs_state == VDEV_STATE_HEALTHY)) { @@ -538,11 +540,11 @@ CaseFile::OnGracePeriodEnded(void *arg) int CaseFile::DeSerializeSelector(const struct dirent *dirEntry) { - uintmax_t poolGUID; - uintmax_t vdevGUID; + uint64_t poolGUID; + uint64_t vdevGUID; if (dirEntry->d_type == DT_REG - && sscanf(dirEntry->d_name, "pool_%ju_vdev_%ju.case", + && sscanf(dirEntry->d_name, "pool_%"PRIu64"_vdev_%"PRIu64".case", &poolGUID, &vdevGUID) == 2) return (1); return (0); @@ -556,11 +558,11 @@ CaseFile::DeSerializeFile(const char *fileName) CaseFile *caseFile(NULL); try { - uintmax_t poolGUID; - uintmax_t vdevGUID; + uint64_t poolGUID; + uint64_t vdevGUID; nvlist_t *vdevConf; - sscanf(fileName, "pool_%ju_vdev_%ju.case", + sscanf(fileName, "pool_%"PRIu64"_vdev_%"PRIu64".case", &poolGUID, &vdevGUID); existingCaseFile = Find(Guid(poolGUID), Guid(vdevGUID)); if (existingCaseFile != NULL) { @@ -832,7 +834,7 @@ CaseFile::Replace(const char* vdev_type, const char* path) { ZpoolList zpl(ZpoolList::ZpoolByGUID, &m_poolGUID); if (zpl.empty()) { syslog(LOG_ERR, "CaseFile::Replace: could not find pool for " - "pool_guid %ju.", (uint64_t)m_poolGUID); + "pool_guid %"PRIu64".", (uint64_t)m_poolGUID); return (false); } zhp = zpl.front(); diff --git a/cddl/sbin/zfsd/vdev.cc b/cddl/sbin/zfsd/vdev.cc index 1d95b06dd8a..cd7d5e7ae00 100644 --- a/cddl/sbin/zfsd/vdev.cc +++ b/cddl/sbin/zfsd/vdev.cc @@ -120,6 +120,7 @@ Vdev::Vdev(nvlist_t *labelConfig) vdev_state Vdev::State() const { + uint64_t *nvlist_array; vdev_stat_t *vs; uint_t vsc; @@ -136,8 +137,10 @@ Vdev::State() const } if (nvlist_lookup_uint64_array(m_config, ZPOOL_CONFIG_VDEV_STATS, - (uint64_t **)&vs, &vsc) == 0) + &nvlist_array, &vsc) == 0) { + vs = reinterpret_cast(nvlist_array); return (static_cast(vs->vs_state)); + } /* * Stats are not available. This vdev was created from a label. From 5ee801b572e496eecc67e3c594a11b5faad4e2bf Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Mon, 14 Oct 2013 21:21:05 +0000 Subject: [PATCH 63/89] Style, comment, and warning message improvements to zfsd. * zfsd.h Clarified member documentation * zfsd.cc Clarified warning message * case_file.cc Omits a space before the { of a control block * vdev_iterator.cc * zfsd.cc Return statements must enclose their values in parenthesis * dev_ctl_event.cc: Omit a space after flow control keywords * case_file.cc: * vdev_iterator.cc * zfsd.cc * case_file.h * dev_ctl_event.h * guid.h * zfsd_exception.h * zpool_list.h * dev_ctl_event.cc Trailing whitespace * zfsd.cc * zpool_list.cc Braces around a single line statement following a control block Submitted by: alans Approved by: ken (mentor) Sponsored by: Spectra Logic Corporation --- cddl/sbin/zfsd/case_file.cc | 17 +++++++++-------- cddl/sbin/zfsd/case_file.h | 4 ++-- cddl/sbin/zfsd/dev_ctl_event.cc | 8 ++++---- cddl/sbin/zfsd/dev_ctl_event.h | 4 ++-- cddl/sbin/zfsd/guid.h | 2 +- cddl/sbin/zfsd/vdev_iterator.cc | 4 ++-- cddl/sbin/zfsd/zfsd.cc | 19 +++++++++---------- cddl/sbin/zfsd/zfsd.h | 28 ++++++++++++++++++++++++---- cddl/sbin/zfsd/zfsd_exception.h | 2 +- cddl/sbin/zfsd/zpool_list.cc | 5 ++--- cddl/sbin/zfsd/zpool_list.h | 8 ++++---- 11 files changed, 60 insertions(+), 41 deletions(-) diff --git a/cddl/sbin/zfsd/case_file.cc b/cddl/sbin/zfsd/case_file.cc index c838e5cd674..186034e0351 100644 --- a/cddl/sbin/zfsd/case_file.cc +++ b/cddl/sbin/zfsd/case_file.cc @@ -202,7 +202,7 @@ CaseFile::ReEvaluate(const string &devPath, const string &physPath, Vdev *vdev) */ syslog(LOG_INFO, "CaseFile::ReEvaluate(%s,%s) Pool/Vdev unconfigured. " - "Closing\n", + "Closing\n", PoolGUIDString().c_str(), VdevGUIDString().c_str()); Close(); @@ -308,7 +308,7 @@ CaseFile::ReEvaluate(const ZfsEvent &event) */ syslog(LOG_INFO, "CaseFile::ReEvaluate(%s,%s) Pool/Vdev unconfigured. " - "Closing\n", + "Closing\n", PoolGUIDString().c_str(), VdevGUIDString().c_str()); Close(); @@ -349,7 +349,7 @@ CaseFile::ReEvaluate(const ZfsEvent &event) */ ZfsDaemon::RequestSystemRescan(); - /* + /* * Consume the event if we successfully activated a spare. * Otherwise, leave it in the unconsumed events list so that the * future addition of a spare to this pool might be able to @@ -394,7 +394,8 @@ CaseFile::ActivateSpare() { "config for pool %s", poolname); return (false); } - if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &nvroot) != 0){ + error = nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &nvroot); + if (error != 0){ syslog(LOG_ERR, "CaseFile::ActivateSpare: Could not find vdev " "tree for pool %s", poolname); return (false); @@ -453,7 +454,7 @@ CaseFile::ActivateSpare() { void CaseFile::RegisterCallout(const DevCtlEvent &event) -{ +{ timeval now, countdown, elapsed, timestamp, zero, remaining; gettimeofday(&now, 0); @@ -484,7 +485,7 @@ CaseFile::CloseIfSolved() if (m_events.empty() && m_tentativeEvents.empty()) { - /* + /* * We currently do not track or take actions on * devices in the degraded or faulted state. * Once we have support for spare pools, we'll @@ -774,7 +775,7 @@ CaseFile::Close() syslog(LOG_INFO, "CaseFile(%s,%s) closed - State %s\n", PoolGUIDString().c_str(), VdevGUIDString().c_str(), zpool_state_to_name(VdevState(), VDEV_AUX_NONE)); - + /* * Serialization of a Case with no event data, clears the * Serialization data for that event. @@ -809,7 +810,7 @@ CaseFile::OnGracePeriodEnded() VDEV_AUX_ERR_EXCEEDED) == 0) { syslog(LOG_INFO, "Degrading vdev(%s/%s)", PoolGUIDString().c_str(), - VdevGUIDString().c_str()); + VdevGUIDString().c_str()); Close(); return; } diff --git a/cddl/sbin/zfsd/case_file.h b/cddl/sbin/zfsd/case_file.h index cce86346e7f..defe921be86 100644 --- a/cddl/sbin/zfsd/case_file.h +++ b/cddl/sbin/zfsd/case_file.h @@ -154,7 +154,7 @@ public: * the disk resource. * \param vdev If the disk contains ZFS label information, * a pointer to the disk label's vdev object - * data. Otherwise NULL. + * data. Otherwise NULL. * * \return True if this event was consumed by this CaseFile. */ @@ -346,7 +346,7 @@ protected: string m_vdevPhysPath; /** - * \brief Callout activated when a grace period + * \brief Callout activated when a grace period */ Callout m_tentativeTimer; }; diff --git a/cddl/sbin/zfsd/dev_ctl_event.cc b/cddl/sbin/zfsd/dev_ctl_event.cc index 19000350a2d..e4d213fe55d 100644 --- a/cddl/sbin/zfsd/dev_ctl_event.cc +++ b/cddl/sbin/zfsd/dev_ctl_event.cc @@ -67,7 +67,7 @@ ParseException::ToString() const result << "Parsing "; - switch(Type()) { + switch (Type()) { case INVALID_FORMAT: result << "invalid format "; break; @@ -131,7 +131,7 @@ DevCtlEvent::Init() EventFactoryRecord *rec(s_factoryTable); EventFactoryRecord *lastRec(s_factoryTable + NUM_ELEMENTS(s_factoryTable) - 1); - + for (; rec <= lastRec; rec++) { EventFactoryKey key(rec->m_type, rec->m_subsystem); @@ -434,10 +434,10 @@ DevfsEvent::IsWholeDev(const string &devName) /* alpha prefix followed only by digits. */ for (; i < devName.end() && !isdigit(*i); i++) ; - + if (i == devName.end()) return (false); - + for (; i < devName.end() && isdigit(*i); i++) ; diff --git a/cddl/sbin/zfsd/dev_ctl_event.h b/cddl/sbin/zfsd/dev_ctl_event.h index d984f404576..f20752e4bbf 100644 --- a/cddl/sbin/zfsd/dev_ctl_event.h +++ b/cddl/sbin/zfsd/dev_ctl_event.h @@ -143,7 +143,7 @@ ParseException::ParseException(Type type, const string &parsedBuffer, } //- ParseException Inline Const Public Methods --------------------------------- -inline ParseException::Type +inline ParseException::Type ParseException::GetType() const { return (m_type); @@ -271,7 +271,7 @@ public: */ void Log(int priority) const; - /** + /** * Create and return a fully independent clone * of this event. */ diff --git a/cddl/sbin/zfsd/guid.h b/cddl/sbin/zfsd/guid.h index 7a280afcfc7..ecfd7e015a4 100644 --- a/cddl/sbin/zfsd/guid.h +++ b/cddl/sbin/zfsd/guid.h @@ -47,7 +47,7 @@ * * It can generally be manipulated as a uint64_t, but with a special * value INVALID_GUID that does not equal any valid guid. - * + * * As of this writing, spa_generate_guid() in spa_misc.c explicitly * refuses to return a guid of 0. So this class uses 0 as the value * for INVALID_GUID. In the future, if 0 is allowed to be a valid diff --git a/cddl/sbin/zfsd/vdev_iterator.cc b/cddl/sbin/zfsd/vdev_iterator.cc index 9145901fa06..ce624fbfcca 100644 --- a/cddl/sbin/zfsd/vdev_iterator.cc +++ b/cddl/sbin/zfsd/vdev_iterator.cc @@ -69,7 +69,7 @@ VdevIterator::Reset() result = nvlist_lookup_nvlist(m_poolConfig, ZPOOL_CONFIG_VDEV_TREE, &rootVdev); - if (result != 0) + if (result != 0) throw ZfsdException(m_poolConfig, "Unable to extract " "ZPOOL_CONFIG_VDEV_TREE from pool."); m_vdevQueue.assign(1, rootVdev); @@ -135,7 +135,7 @@ VdevIterator::Find(Guid vdevGUID) Vdev vdev(m_poolConfig, vdevConfig); if (vdev.GUID() == vdevGUID) - return vdevConfig; + return (vdevConfig); } return (NULL); } diff --git a/cddl/sbin/zfsd/zfsd.cc b/cddl/sbin/zfsd/zfsd.cc index b3af2f6ce96..1983c99121f 100644 --- a/cddl/sbin/zfsd/zfsd.cc +++ b/cddl/sbin/zfsd/zfsd.cc @@ -35,7 +35,7 @@ * * The ZFS daemon consumes kernel devctl(4) event data via devd(8)'s * unix domain socket in order to react to system changes that impact - * the function of ZFS storage pools. The goal of this daemon is to + * the function of ZFS storage pools. The goal of this daemon is to * provide similar functionality to the Solaris ZFS Diagnostic Engine * (zfs-diagnosis), the Solaris ZFS fault handler (zfs-retire), and * the Solaris ZFS vdev insertion agent (zfs-mod sysevent handler). @@ -180,9 +180,9 @@ EventBuffer::ExtractEvent(string &eventString) */ continue; } - syslog(LOG_WARNING, - "Event exceeds event size limit of %d bytes.", - MAX_EVENT_SIZE); + syslog(LOG_WARNING, "Overran event buffer\n\tm_nextEventOffset" + "=%d\n\tm_parsedLen=%d\n\tm_validLen=%d", + m_nextEventOffset, m_parsedLen, m_validLen); } else { /* * Include the normal terminator in the extracted @@ -250,11 +250,10 @@ EventBuffer::Fill() want = std::min(avail, MAX_READ_SIZE - m_validLen); consumed = m_reader.read(m_buf + m_validLen, want); if (consumed == -1) { - if (errno == EINTR) { + if (errno == EINTR) return (false); - } else { + else err(1, "EventBuffer::Fill(): Read failed"); - } } } @@ -430,7 +429,7 @@ ZfsDaemon::ConnectToDevd() sLen = SUN_LEN(&devdAddr); s_devdSockFD = socket(AF_UNIX, SOCK_STREAM, 0); - if (s_devdSockFD == -1) + if (s_devdSockFD == -1) err(1, "Unable to create socket"); result = connect(s_devdSockFD, reinterpret_cast(&devdAddr), @@ -482,9 +481,9 @@ bool ZfsDaemon::SaveEvent(const DevCtlEvent &event) { if (s_consumingEvents) - return false; + return (false); s_unconsumedEvents.push_back(event.DeepCopy()); - return true; + return (true); } /* Capture and process buffered events. */ diff --git a/cddl/sbin/zfsd/zfsd.h b/cddl/sbin/zfsd/zfsd.h index 635fa8c4c54..e5eb4aa39ea 100644 --- a/cddl/sbin/zfsd/zfsd.h +++ b/cddl/sbin/zfsd/zfsd.h @@ -272,19 +272,39 @@ private: /** Characters found between successive "key=value" strings. */ static const char s_keyPairSepTokens[]; - /** Temporary space for event data during our parsing. */ + /** Temporary space for event data during our parsing. Laid out like + * this: + * <---------------------------------------------------------> + * | | | | | + * m_buf---| | | | | + * m_nextEventOffset-- | | | + * m_parsedLen------------- | | + * m_validLen-------------------------- | + * EVENT_BUFSIZE------------------------------------------------------ + * + * Data before m_nextEventOffset has already been processed. + * + * Data between m_nextEvenOffset and m_parsedLen has been parsed, but + * not processed as a single event. + * + * Data between m_parsedLen and m_validLen has been read from the + * source, but not yet parsed. + * + * Between m_validLen and EVENT_BUFSIZE is empty space. + * + * */ char m_buf[EVENT_BUFSIZE]; /** Reference to the reader linked to devd's domain socket. */ Reader& m_reader; - /** Valid bytes in m_buf. */ + /** Offset within m_buf to the beginning of free space. */ size_t m_validLen; - /** The amount of data in m_buf we have looked at. */ + /** Offset within m_buf to the beginning of data not yet parsed */ size_t m_parsedLen; - /** Offset to the start token of the next event. */ + /** Offset within m_buf to the start token of the next event. */ size_t m_nextEventOffset; /** The EventBuffer is aligned and tracking event records. */ diff --git a/cddl/sbin/zfsd/zfsd_exception.h b/cddl/sbin/zfsd/zfsd_exception.h index 33d03ca71a2..e28ea2eb824 100644 --- a/cddl/sbin/zfsd/zfsd_exception.h +++ b/cddl/sbin/zfsd/zfsd_exception.h @@ -96,7 +96,7 @@ public: * \brief Augment/Modify a ZfsdException's string data. */ std::string& GetString(); - + /** * \brief Emit exception data to syslog(3). */ diff --git a/cddl/sbin/zfsd/zpool_list.cc b/cddl/sbin/zfsd/zpool_list.cc index f96d3d3466f..0b9cd9819fc 100644 --- a/cddl/sbin/zfsd/zpool_list.cc +++ b/cddl/sbin/zfsd/zpool_list.cc @@ -75,11 +75,10 @@ ZpoolList::LoadIterator(zpool_handle_t *pool, void *data) ZpoolList *zpl(reinterpret_cast(data)); nvlist_t *poolConfig(zpool_get_config(pool, NULL)); - if (zpl->m_filter(pool, poolConfig, zpl->m_filterArg)) { + if (zpl->m_filter(pool, poolConfig, zpl->m_filterArg)) zpl->push_back(pool); - } else { + else zpool_close(pool); - } return (0); } diff --git a/cddl/sbin/zfsd/zpool_list.h b/cddl/sbin/zfsd/zpool_list.h index 8f42e5dd879..ec2948b5d48 100644 --- a/cddl/sbin/zfsd/zpool_list.h +++ b/cddl/sbin/zfsd/zpool_list.h @@ -54,7 +54,7 @@ class Vdev; /*============================= Class Definitions ============================*/ /*--------------------------------- ZpoolList --------------------------------*/ class ZpoolList; -typedef bool PoolFilter_t(zpool_handle_t *pool, nvlist_t *poolConfig, +typedef bool PoolFilter_t(zpool_handle_t *pool, nvlist_t *poolConfig, void *filterArg); /** @@ -82,7 +82,7 @@ public: /** * \brief Utility ZpoolList construction filter that causes only - * pools known to the system and having the specified name + * pools known to the system and having the specified name * to be included in the intantiated ZpoolList. */ static PoolFilter_t ZpoolByName; @@ -103,9 +103,9 @@ public: private: /** * \brief Helper routine used to populate the internal - * data store of ZFS pool objects using libzfs's + * data store of ZFS pool objects using libzfs's * zpool_iter() function. - * + * * \param pool The ZFS pool object to filter. * \param data User argument passed through zpool_iter(). */ From 7c11a1d9fada408416f66f7bae0fc342c4740d6f Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Mon, 14 Oct 2013 21:23:58 +0000 Subject: [PATCH 64/89] zfsd will attempt to activate a hotspare in response to a drive becoming DEGRADED or FAULTED. case_file.cc Attempt to activate a spare whenver we get a "resource.fs.zfs.statechange" event and the vdev state is either DEGRADED or FAULTED. Submitted by: alans Approved by: ken (mentor) Sponsored by: Spectra Logic Corporation --- cddl/sbin/zfsd/case_file.cc | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/cddl/sbin/zfsd/case_file.cc b/cddl/sbin/zfsd/case_file.cc index 186034e0351..d44351736cb 100644 --- a/cddl/sbin/zfsd/case_file.cc +++ b/cddl/sbin/zfsd/case_file.cc @@ -356,8 +356,19 @@ CaseFile::ReEvaluate(const ZfsEvent &event) * close the case */ consumed = spare_activated; - } else if (event.Value("class") == "ereport.fs.zfs.io" - || event.Value("class") == "ereport.fs.zfs.checksum") { + } else if (event.Value("class") == "resource.fs.zfs.statechange") { + /* + * If this vdev is DEGRADED or FAULTED, try to activate a + * hotspare. Otherwise, ignore the event + */ + if (VdevState() == VDEV_STATE_FAULTED || + VdevState() == VDEV_STATE_DEGRADED) + consumed = ActivateSpare(); + else + consumed = true; + } + else if (event.Value("class") == "ereport.fs.zfs.io" || + event.Value("class") == "ereport.fs.zfs.checksum") { m_tentativeEvents.push_front(event.DeepCopy()); RegisterCallout(event); @@ -370,6 +381,10 @@ CaseFile::ReEvaluate(const ZfsEvent &event) } +/* + * TODO: ensure that we don't activate a spare for a vdev that is already being + * replaced by another spare. + */ bool CaseFile::ActivateSpare() { nvlist_t *config, *nvroot; @@ -405,6 +420,8 @@ CaseFile::ActivateSpare() { &nspares); if (nspares == 0) { /* The pool has no spares configured */ + syslog(LOG_INFO, "CaseFile::ActivateSpare: " + "No spares available for pool %s", poolname); return (false); } for (i = 0; i < nspares; i++) { From 7af495d32e08e0a7fa596215514a3bbe5b943869 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Mon, 14 Oct 2013 21:30:00 +0000 Subject: [PATCH 65/89] Fixed ZfsEvent::ProcessPoolEvent(): it was never getting called, and didn't have the right string for removal events. dev_ctl_event.cc Fixed event strings dev_ctl_event.h Made a function virtual to facilitate unit testing zfsd.cc Fixed a printf format warning in ExtractEvent. Submitted by: alans Approved by: ken (mentor) Sponsored by: Spectra Logic Corporation --- cddl/sbin/zfsd/dev_ctl_event.cc | 5 ++--- cddl/sbin/zfsd/dev_ctl_event.h | 2 +- cddl/sbin/zfsd/zfsd.cc | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/cddl/sbin/zfsd/dev_ctl_event.cc b/cddl/sbin/zfsd/dev_ctl_event.cc index e4d213fe55d..04ec06fadff 100644 --- a/cddl/sbin/zfsd/dev_ctl_event.cc +++ b/cddl/sbin/zfsd/dev_ctl_event.cc @@ -619,7 +619,7 @@ ZfsEvent::Process() const Log(LOG_INFO); - if (Value("subsystem").find("misc.fs.zfs.") == 0) { + if (Value("type").find("misc.fs.zfs.") == 0) { /* Configuration changes, resilver events, etc. */ ProcessPoolEvent(); return; @@ -721,8 +721,7 @@ ZfsEvent::ProcessPoolEvent() const caseFile->ReEvaluate(*this); } - /* XXX Needs to be changed. */ - if (Value("type") == "ESC_ZFS_vdev_remove" + if (Value("type") == "misc.fs.zfs.vdev_remove" && degradedDevice == false) { /* See if any other cases can make use of this device. */ ZfsDaemon::RequestSystemRescan(); diff --git a/cddl/sbin/zfsd/dev_ctl_event.h b/cddl/sbin/zfsd/dev_ctl_event.h index f20752e4bbf..181f1e5d80d 100644 --- a/cddl/sbin/zfsd/dev_ctl_event.h +++ b/cddl/sbin/zfsd/dev_ctl_event.h @@ -493,7 +493,7 @@ protected: /** Deep copy constructor. */ ZfsEvent(const ZfsEvent &src); - void ProcessPoolEvent() const; + virtual void ProcessPoolEvent() const; Guid m_poolGUID; Guid m_vdevGUID; diff --git a/cddl/sbin/zfsd/zfsd.cc b/cddl/sbin/zfsd/zfsd.cc index 1983c99121f..c6d74c6d397 100644 --- a/cddl/sbin/zfsd/zfsd.cc +++ b/cddl/sbin/zfsd/zfsd.cc @@ -181,7 +181,7 @@ EventBuffer::ExtractEvent(string &eventString) continue; } syslog(LOG_WARNING, "Overran event buffer\n\tm_nextEventOffset" - "=%d\n\tm_parsedLen=%d\n\tm_validLen=%d", + "=%zd\n\tm_parsedLen=%zd\n\tm_validLen=%zd", m_nextEventOffset, m_parsedLen, m_validLen); } else { /* From f496e4478ee5fe8b371c53e0ee09220b586d8520 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Mon, 14 Oct 2013 21:34:49 +0000 Subject: [PATCH 66/89] When a vdev generates IO errors, mark it as FAULTED instead of DEGRADED. Checksum errors will still be marked as DEGRADED. This matches the behavior of Illumos. I had to make a number of methods virtual to facilitate unit testing. Submitted by: alans Approved by: ken (mentor) Sponsored by: Spectra Logic Corporation --- cddl/sbin/zfsd/case_file.cc | 52 ++++++++++++++++++++++++++++++++++--- cddl/sbin/zfsd/case_file.h | 23 ++++++++++++---- cddl/sbin/zfsd/vdev.h | 10 +++---- 3 files changed, 72 insertions(+), 13 deletions(-) diff --git a/cddl/sbin/zfsd/case_file.cc b/cddl/sbin/zfsd/case_file.cc index d44351736cb..6e4d83f769a 100644 --- a/cddl/sbin/zfsd/case_file.cc +++ b/cddl/sbin/zfsd/case_file.cc @@ -806,11 +806,13 @@ CaseFile::Close() void CaseFile::OnGracePeriodEnded() { + bool should_fault, should_degrade; + ZpoolList zpl(ZpoolList::ZpoolByGUID, &m_poolGUID); m_events.splice(m_events.begin(), m_tentativeEvents); + should_fault = ShouldFault(); + should_degrade = ShouldDegrade(); - if (m_events.size() > ZFS_DEGRADE_IO_COUNT) { - - ZpoolList zpl(ZpoolList::ZpoolByGUID, &m_poolGUID); + if (should_fault || should_degrade) { if (zpl.empty() || (VdevIterator(zpl.front()).Find(m_vdevGUID)) == NULL) { /* @@ -822,6 +824,28 @@ CaseFile::OnGracePeriodEnded() return; } + } + + /* A fault condition has priority over a degrade condition */ + if (ShouldFault()) { + /* Fault the vdev and close the case. */ + if (zpool_vdev_fault(zpl.front(), (uint64_t)m_vdevGUID, + VDEV_AUX_ERR_EXCEEDED) == 0) { + syslog(LOG_INFO, "Faulting vdev(%s/%s)", + PoolGUIDString().c_str(), + VdevGUIDString().c_str()); + Close(); + return; + } + else { + syslog(LOG_ERR, "Fault vdev(%s/%s): %s: %s\n", + PoolGUIDString().c_str(), + VdevGUIDString().c_str(), + libzfs_error_action(g_zfsHandle), + libzfs_error_description(g_zfsHandle)); + } + } + else if (ShouldDegrade()) { /* Degrade the vdev and close the case. */ if (zpool_vdev_degrade(zpl.front(), (uint64_t)m_vdevGUID, VDEV_AUX_ERR_EXCEEDED) == 0) { @@ -907,3 +931,25 @@ CaseFile::Replace(const char* vdev_type, const char* path) { return (true); } + +/* Does the argument event refer to a checksum error? */ +static bool IsChecksumEvent(const DevCtlEvent* const event){ + return ("ereport.fs.zfs.checksum" == event->Value("type")); +} + +/* Does the argument event refer to an IO error? */ +static bool IsIOEvent(const DevCtlEvent* const event){ + return ("ereport.fs.zfs.io" == event->Value("type")); +} + +bool +CaseFile::ShouldDegrade() const { + return (std::count_if(m_events.begin(), m_events.end(), + IsChecksumEvent) > ZFS_DEGRADE_IO_COUNT); +} + +bool +CaseFile::ShouldFault() const { + return (std::count_if(m_events.begin(), m_events.end(), + IsIOEvent) > ZFS_DEGRADE_IO_COUNT); +} diff --git a/cddl/sbin/zfsd/case_file.h b/cddl/sbin/zfsd/case_file.h index defe921be86..d0a9b5512ce 100644 --- a/cddl/sbin/zfsd/case_file.h +++ b/cddl/sbin/zfsd/case_file.h @@ -114,7 +114,7 @@ public: * * \param vdev The vdev object for which to find/create a CaseFile. * - * \return A referenc eto a valid CaseFile object. + * \return A reference to a valid CaseFile object. */ static CaseFile &Create(Vdev &vdev); @@ -173,7 +173,7 @@ public: /** * \brief Register an itimer callout for the given event, if necessary */ - void RegisterCallout(const DevCtlEvent &event); + virtual void RegisterCallout(const DevCtlEvent &event); /** * \brief Close a case if it is no longer relevant. @@ -192,6 +192,16 @@ public: */ void Log(); + /** + * \brief Whether we should degrade this vdev + */ + bool ShouldDegrade() const; + + /** + * \brief Whether we should fault this vdev + */ + bool ShouldFault() const; + protected: enum { /** @@ -228,8 +238,11 @@ protected: /** Constructor. */ CaseFile(const Vdev &vdev); - /** Destructor. */ - ~CaseFile(); + /** + * Destructor. + * Must be virtual so it can be subclassed in the unit tests + */ + virtual ~CaseFile(); /** * \brief Reload state for the vdev associated with this CaseFile. @@ -237,7 +250,7 @@ protected: * \return True if the refresh was successful. False if the system * has no record of the pool or vdev for this CaseFile. */ - bool RefreshVdevState(); + virtual bool RefreshVdevState(); /** * \brief Free all events in the m_events list. diff --git a/cddl/sbin/zfsd/vdev.h b/cddl/sbin/zfsd/vdev.h index 50b915204c5..ddeb9771f6f 100644 --- a/cddl/sbin/zfsd/vdev.h +++ b/cddl/sbin/zfsd/vdev.h @@ -96,11 +96,11 @@ public: */ Vdev(nvlist_t *vdevConfig); - Guid GUID() const; - Guid PoolGUID() const; - vdev_state State() const; - std::string Path() const; - std::string PhysicalPath() const; + virtual Guid GUID() const; + virtual Guid PoolGUID() const; + virtual vdev_state State() const; + std::string Path() const; + virtual std::string PhysicalPath() const; std::string GUIDString() const; nvlist_t *PoolConfig() const; nvlist_t *Config() const; From 24d87685377be51cb51d97008267861b7cedac40 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Mon, 14 Oct 2013 21:41:36 +0000 Subject: [PATCH 67/89] Make zfsd WARNS=1 safe. cddl/sbin/zfsd/zfsd.h Make Reader's destructor virtual, because it has virtual functions cddl/sbin/zfsd/Makefile Add WARNS=1 and -Wall Submitted by: will Approved by: ken (mentor) Sponsored by: Spectra Logic Corporation --- cddl/sbin/zfsd/Makefile | 4 ++-- cddl/sbin/zfsd/zfsd.h | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/cddl/sbin/zfsd/Makefile b/cddl/sbin/zfsd/Makefile index 571161c035e..82df7d493a4 100644 --- a/cddl/sbin/zfsd/Makefile +++ b/cddl/sbin/zfsd/Makefile @@ -14,7 +14,7 @@ SRCS= callout.cc \ NO_MAN= YES -WARNS?= 0 +WARNS?= 1 INCFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/lib/libzpool/common INCFLAGS+= -I${.CURDIR}/../../../cddl/compat/opensolaris/include @@ -30,7 +30,7 @@ INCFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common INCFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common/fs/zfs INCFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common/sys -CFLAGS= -g -DNEED_SOLARIS_BOOLEAN ${INCFLAGS} +CFLAGS= -Wall -g -DNEED_SOLARIS_BOOLEAN ${INCFLAGS} DPADD= ${LIBZFS} ${LIBUTIL} ${LIBGEOM} ${LIBBSDXML} ${LIBSBUF} \ ${LIBNVPAIR} ${LIBUUTIL} diff --git a/cddl/sbin/zfsd/zfsd.h b/cddl/sbin/zfsd/zfsd.h index e5eb4aa39ea..3e9e16b37ff 100644 --- a/cddl/sbin/zfsd/zfsd.h +++ b/cddl/sbin/zfsd/zfsd.h @@ -108,8 +108,12 @@ public: * \returns Amount of data that was actually read */ virtual ssize_t read(char* buf, size_t count) = 0; + + virtual ~Reader() = 0; }; +inline Reader::~Reader() {} + /*-------------------------------- FDReader -------------------------------*/ /** From 3ad5f0ff965f3bd720cbca26b19e24e7ac7846dc Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Mon, 14 Oct 2013 21:50:56 +0000 Subject: [PATCH 68/89] Fix a bug in zfsd: when a drive is experiencing a rapid storm of IO or checksum errors, zfsd will not degrade/fault it until hundreds or thousands of errors have occured. cddl/sbin/zfsd/case_file.cc RefreshVdevState() iterates through all the system's zpools, which involves the ioctls ZFS_IOC_POOL_CONFIGS and ZFS_IOC_POOL_STATS. Both of those acquire spa_namespace_lock, which may block for a long time under certain circumstances, including when the system has a storm of IO or checksum errors. This change eliminates the call to RefreshVdevState() whenever a ZFSEvent is received. Instead, RefreshVdevState() will only be called when a CaseFile is closed, if necessary. This way, zfsd won't spend too much time blocking on ioctl()s and miss reading events from devd. Submitted by: alans Approved by: ken (mentor) Sponsored by: Spectra Logic Corporation --- cddl/sbin/zfsd/case_file.cc | 44 ++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/cddl/sbin/zfsd/case_file.cc b/cddl/sbin/zfsd/case_file.cc index 6e4d83f769a..8640684c2f7 100644 --- a/cddl/sbin/zfsd/case_file.cc +++ b/cddl/sbin/zfsd/case_file.cc @@ -298,28 +298,6 @@ CaseFile::ReEvaluate(const ZfsEvent &event) { bool consumed(false); - if (!RefreshVdevState()) { - /* - * The pool or vdev for this case file is no longer - * part of the configuration. This can happen - * if we process a device arrival notification - * before seeing the ZFS configuration change - * event. - */ - syslog(LOG_INFO, - "CaseFile::ReEvaluate(%s,%s) Pool/Vdev unconfigured. " - "Closing\n", - PoolGUIDString().c_str(), - VdevGUIDString().c_str()); - Close(); - - /* - * Since this event was not used to close this - * case, do not report it as consumed. - */ - return (/*consumed*/false); - } - if (event.Value("type") == "misc.fs.zfs.vdev_remove") { /* * The Vdev we represent has been removed from the @@ -333,6 +311,28 @@ CaseFile::ReEvaluate(const ZfsEvent &event) if (event.Value("class") == "resource.fs.zfs.removed") { bool spare_activated; + if (!RefreshVdevState()) { + /* + * The pool or vdev for this case file is no longer + * part of the configuration. This can happen + * if we process a device arrival notification + * before seeing the ZFS configuration change + * event. + */ + syslog(LOG_INFO, + "CaseFile::ReEvaluate(%s,%s) Pool/Vdev " + "unconfigured. Closing\n", + PoolGUIDString().c_str(), + VdevGUIDString().c_str()); + Close(); + + /* + * Since this event was not used to close this + * case, do not report it as consumed. + */ + return (/*consumed*/false); + } + /* * Discard any tentative I/O error events for * this case. They were most likely caused by the From b1d326478b0255ddfd494eb92679595f77f61623 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Mon, 14 Oct 2013 21:53:45 +0000 Subject: [PATCH 69/89] cddl/sbin/zfsd/case_file.cc Comment change. No functional change. Submitted by: alans Approved by: ken (mentor) Sponsored by: Spectra Logic Corporation --- cddl/sbin/zfsd/case_file.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cddl/sbin/zfsd/case_file.cc b/cddl/sbin/zfsd/case_file.cc index 8640684c2f7..3ba52333411 100644 --- a/cddl/sbin/zfsd/case_file.cc +++ b/cddl/sbin/zfsd/case_file.cc @@ -324,6 +324,10 @@ CaseFile::ReEvaluate(const ZfsEvent &event) "unconfigured. Closing\n", PoolGUIDString().c_str(), VdevGUIDString().c_str()); + /* + * Close the case now so we won't waste cycles in the + * system rescan + */ Close(); /* From 6dd90c779b78a40039d9a7ed340e8b754d72df61 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Mon, 14 Oct 2013 21:59:31 +0000 Subject: [PATCH 70/89] zfsd should activate a spare that is newly added to an already-degraded pool cddl/sbin/zfsd/case_file.cc cddl/sbin/zfsd/case_file.h cddl/sbin/zfsd/dev_ctl_event.cc When a config_sync event arrives, reevaluate all open casefiles on the same pool. cddl/sbin/zfsd/case_file.cc When evaluating a config_sync event, try to activate spares if our pool is unhealthy. This is necessary because we don't get any more specific event when a spare is added to a pool. This required adding the CaseFileReEvaluator functor and the CaseFile::ReEvaluateByGuid method. cddl/sbin/zfsd/case_file.cc When processing a statechange event, refresh the vdev state. This is necessary because we could be reevaluating an already-existing casefile that was opened before the pool became degraded. Also, always consume the event, even if we couldn't activate the spare. It's no longer necessary to queue the statechange event since we can now reevaluate CaseFiles on a config_sync event. cddl/sbin/zfsd/case_file.cc In CloseIfSolved(), don't close casefiles for DEGRADED or FAULTED vdevs since we now have the ability to fix them. cddl/sbin/zfsd/dev_ctl_event.cc cddl/sbin/zfsd/dev_ctl_event.h cddl/sbin/zfsd/zfsd.cc Redelegated responsibility for queueing events from DevCtlEvent(and its child classes) to ZfsDaemon. cddl/sbin/zfsd/zfsd.cc In the SIGINFO handler, print unconsumed events as well as saved casefiles. Submitted by: alans Approved by: ken (mentor) Sponsored by: Spectra Logic Corporation --- cddl/sbin/zfsd/case_file.cc | 66 ++++++++++++++++++++++++++++++--- cddl/sbin/zfsd/case_file.h | 11 +++++- cddl/sbin/zfsd/dev_ctl_event.cc | 48 ++++++++++++------------ cddl/sbin/zfsd/dev_ctl_event.h | 9 +++-- cddl/sbin/zfsd/zfsd.cc | 16 +++++++- 5 files changed, 116 insertions(+), 34 deletions(-) diff --git a/cddl/sbin/zfsd/case_file.cc b/cddl/sbin/zfsd/case_file.cc index 3ba52333411..e0c059549c3 100644 --- a/cddl/sbin/zfsd/case_file.cc +++ b/cddl/sbin/zfsd/case_file.cc @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -59,6 +60,27 @@ using std::stringstream; using std::setfill; using std::setw; +/*-------------------------- File-scoped classes ----------------------------*/ +/** + * \brief Functor that operators on STL collections of CaseFiles + * Selectively calls ReEvaluate on the casefile, based on its pool GUID. + */ +class CaseFileReEvaluator : public std::unary_function +{ +public: + CaseFileReEvaluator(Guid guid, const ZfsEvent &event) : + m_poolGUID(guid), m_event(event) {}; + void operator() (CaseFile *casefile) { + if (m_poolGUID == casefile->PoolGUID()) + casefile->ReEvaluate(m_event); + } +private: + Guid m_poolGUID; + const ZfsEvent &m_event; +}; + + + /*--------------------------------- CaseFile ---------------------------------*/ //- CaseFile Static Data ------------------------------------------------------- CaseFileList CaseFile::s_activeCases; @@ -99,6 +121,14 @@ CaseFile::Find(const string &physPath) return (NULL); } + +void +CaseFile::ReEvaluateByGuid(Guid poolGUID, const ZfsEvent &event) +{ + CaseFileReEvaluator reevaluator(poolGUID, event); + std::for_each(s_activeCases.begin(), s_activeCases.end(), reevaluator); +} + CaseFile & CaseFile::Create(Vdev &vdev) { @@ -307,6 +337,11 @@ CaseFile::ReEvaluate(const ZfsEvent &event) return (/*consumed*/true); } + else if (event.Value("type") == "misc.fs.zfs.config_sync") { + if (VdevState() < VDEV_STATE_HEALTHY) + consumed = ActivateSpare(); + } + if (event.Value("class") == "resource.fs.zfs.removed") { bool spare_activated; @@ -361,15 +396,15 @@ CaseFile::ReEvaluate(const ZfsEvent &event) */ consumed = spare_activated; } else if (event.Value("class") == "resource.fs.zfs.statechange") { + RefreshVdevState(); /* * If this vdev is DEGRADED or FAULTED, try to activate a * hotspare. Otherwise, ignore the event */ if (VdevState() == VDEV_STATE_FAULTED || VdevState() == VDEV_STATE_DEGRADED) - consumed = ActivateSpare(); - else - consumed = true; + (void) ActivateSpare(); + consumed = true; } else if (event.Value("class") == "ereport.fs.zfs.io" || event.Value("class") == "ereport.fs.zfs.checksum") { @@ -513,10 +548,31 @@ CaseFile::CloseIfSolved() * retain these cases so that any spares added in * the future can be applied to them. */ - if (VdevState() > VDEV_STATE_CANT_OPEN - && VdevState() <= VDEV_STATE_HEALTHY) { + switch (VdevState()) { + case VDEV_STATE_HEALTHY: + /* No need to keep cases for healthy vdevs */ Close(); return (true); + case VDEV_STATE_REMOVED: + case VDEV_STATE_CANT_OPEN: + /* + * Keep open. We may solve it with a newly inserted + * device. + */ + case VDEV_STATE_FAULTED: + case VDEV_STATE_DEGRADED: + /* + * Keep open. We may solve it with the future + * addition of a spare to the pool + */ + case VDEV_STATE_UNKNOWN: + case VDEV_STATE_CLOSED: + case VDEV_STATE_OFFLINE: + /* + * Keep open? This may not be the correct behavior, + * but it's what we've always done + */ + ; } /* diff --git a/cddl/sbin/zfsd/case_file.h b/cddl/sbin/zfsd/case_file.h index d0a9b5512ce..3c757647849 100644 --- a/cddl/sbin/zfsd/case_file.h +++ b/cddl/sbin/zfsd/case_file.h @@ -108,6 +108,14 @@ public: */ static CaseFile *Find(const string &physPath); + /** + * \brief ReEvaluate all open cases whose pool guid matches the argument + * + * \param poolGUID Only reevaluate cases for this pool + * \param event Try to consume this event with the casefile + */ + static void ReEvaluateByGuid(Guid poolGUID, const ZfsEvent &event); + /** * \brief Create or return an existing active CaseFile for the * specified vdev. @@ -163,12 +171,13 @@ public: /** * \brief Update this CaseFile in light of the provided ZfsEvent. + * Must be virtual so it can be overridden in the unit tests * * \param event The ZfsEvent to evaluate. * * \return True if this event was consumed by this CaseFile. */ - bool ReEvaluate(const ZfsEvent &event); + virtual bool ReEvaluate(const ZfsEvent &event); /** * \brief Register an itimer callout for the given event, if necessary diff --git a/cddl/sbin/zfsd/dev_ctl_event.cc b/cddl/sbin/zfsd/dev_ctl_event.cc index 04ec06fadff..57d44721bd9 100644 --- a/cddl/sbin/zfsd/dev_ctl_event.cc +++ b/cddl/sbin/zfsd/dev_ctl_event.cc @@ -248,9 +248,10 @@ DevCtlEvent::~DevCtlEvent() delete &m_nvPairs; } -void +bool DevCtlEvent::Process() const { + return (false); } timeval @@ -516,7 +517,7 @@ DevfsEvent::DeepCopy() const return (new DevfsEvent(*this)); } -void +bool DevfsEvent::Process() const { /* @@ -527,7 +528,7 @@ DevfsEvent::Process() const if (Value("type") != "CREATE" || Value("subsystem") != "CDEV" || !IsDiskDev(devName)) - return; + return (false); /* Log the event since it is of interest. */ Log(LOG_INFO); @@ -535,7 +536,7 @@ DevfsEvent::Process() const string devPath(_PATH_DEV + devName); int devFd(open(devPath.c_str(), O_RDONLY)); if (devFd == -1) - return; + return (false); /* Normalize the device name in case the DEVFS event is for a link. */ devName = fdevname(devFd); @@ -572,6 +573,7 @@ DevfsEvent::Process() const } if (devLabel != NULL) nvlist_free(devLabel); + return (false); } //- DevfsEvent Protected Methods ----------------------------------------------- @@ -602,7 +604,7 @@ ZfsEvent::DeepCopy() const return (new ZfsEvent(*this)); } -void +bool ZfsEvent::Process() const { string logstr(""); @@ -610,31 +612,33 @@ ZfsEvent::Process() const if (!Contains("class") && !Contains("type")) { syslog(LOG_ERR, "ZfsEvent::Process: Missing class or type data."); - return; + return (false); } /* On config syncs, replay any queued events first. */ - if (Value("type").find("misc.fs.zfs.config_sync") == 0) + if (Value("type").find("misc.fs.zfs.config_sync") == 0) { ZfsDaemon::ReplayUnconsumedEvents(); + CaseFile::ReEvaluateByGuid(PoolGUID(), *this); + } Log(LOG_INFO); if (Value("type").find("misc.fs.zfs.") == 0) { /* Configuration changes, resilver events, etc. */ ProcessPoolEvent(); - return; + return (false); } if (!Contains("pool_guid") || !Contains("vdev_guid")) { /* Only currently interested in Vdev related events. */ - return; + return (false); } CaseFile *caseFile(CaseFile::Find(PoolGUID(), VdevGUID())); if (caseFile != NULL) { syslog(LOG_INFO, "Evaluating existing case file\n"); caseFile->ReEvaluate(*this); - return; + return (false); } /* Skip events that can't be handled. */ @@ -645,7 +649,7 @@ ZfsEvent::Process() const msg << "No replicas available for pool " << poolGUID; msg << ", ignoring"; syslog(LOG_INFO, "%s", msg.str().c_str()); - return; + return (false); } /* @@ -655,40 +659,38 @@ ZfsEvent::Process() const ZpoolList zpl(ZpoolList::ZpoolByGUID, &poolGUID); if (zpl.empty()) { stringstream msg; - bool queued = ZfsDaemon::SaveEvent(*this); - int priority = queued ? LOG_INFO : LOG_ERR; + int priority = LOG_INFO; msg << "ZfsEvent::Process: Event for unknown pool "; msg << poolGUID << " "; - msg << (queued ? "queued" : "dropped"); + msg << "queued"; syslog(priority, "%s", msg.str().c_str()); - return; + return (true); } nvlist_t *vdevConfig = VdevIterator(zpl.front()).Find(VdevGUID()); if (vdevConfig == NULL) { stringstream msg; - bool queued = ZfsDaemon::SaveEvent(*this); - int priority = queued ? LOG_INFO : LOG_ERR; + int priority = LOG_INFO; msg << "ZfsEvent::Process: Event for unknown vdev "; msg << VdevGUID() << " "; - msg << (queued ? "queued" : "dropped"); + msg << "queued"; syslog(priority, "%s", msg.str().c_str()); - return; + return (true); } Vdev vdev(zpl.front(), vdevConfig); caseFile = &CaseFile::Create(vdev); if ( caseFile->ReEvaluate(*this) == false) { stringstream msg; - bool queued = ZfsDaemon::SaveEvent(*this); - int priority = queued ? LOG_INFO : LOG_ERR; + int priority = LOG_INFO; msg << "ZfsEvent::Process: Unconsumed event for vdev("; msg << zpool_get_name(zpl.front()) << ","; msg << vdev.GUID() << ") "; - msg << (queued ? "queued" : "dropped"); + msg << "queued"; syslog(priority, "%s", msg.str().c_str()); - return; + return (true); } + return (false); } //- ZfsEvent Protected Methods ------------------------------------------------- diff --git a/cddl/sbin/zfsd/dev_ctl_event.h b/cddl/sbin/zfsd/dev_ctl_event.h index 181f1e5d80d..51f81dddbfe 100644 --- a/cddl/sbin/zfsd/dev_ctl_event.h +++ b/cddl/sbin/zfsd/dev_ctl_event.h @@ -283,8 +283,9 @@ public: /** * Interpret and perform any actions necessary to * consume the event. + * \return True if this event should be queued for later reevaluation */ - virtual void Process() const; + virtual bool Process() const; /** * Get the time that the event was created @@ -405,8 +406,9 @@ public: /** * Interpret and perform any actions necessary to * consume the event. + * \return True if this event should be queued for later reevaluation */ - virtual void Process() const; + virtual bool Process() const; protected: /** @@ -479,8 +481,9 @@ public: /** * Interpret and perform any actions necessary to * consume the event. + * \return True if this event should be queued for later reevaluation */ - virtual void Process() const; + virtual bool Process() const; const string &PoolName() const; Guid PoolGUID() const; diff --git a/cddl/sbin/zfsd/zfsd.cc b/cddl/sbin/zfsd/zfsd.cc index c6d74c6d397..01a5d71b844 100644 --- a/cddl/sbin/zfsd/zfsd.cc +++ b/cddl/sbin/zfsd/zfsd.cc @@ -468,6 +468,12 @@ ZfsDaemon::ReplayUnconsumedEvents() if (replayed_any) syslog(LOG_INFO, "Started replaying unconsumed events"); while (event != s_unconsumedEvents.end()) { + /* + * Even if the event is unconsumed the second time around, drop + * it. By now we should've gotten a config_sync so any events + * that still can't be consumed are probably referring to vdevs + * or pools that no longer exist. + */ (*event)->Process(); delete *event; s_unconsumedEvents.erase(event++); @@ -494,7 +500,8 @@ ZfsDaemon::ProcessEvents(EventBuffer &eventBuffer) while (eventBuffer.ExtractEvent(evString)) { DevCtlEvent *event(DevCtlEvent::CreateEvent(evString)); if (event != NULL) { - event->Process(); + if (event->Process()) + SaveEvent(*event); delete event; } } @@ -597,7 +604,8 @@ ZfsDaemon::RescanSystem() string evString(evStart + pp->lg_name + "\n"); event = DevCtlEvent::CreateEvent(evString); if (event != NULL) - event->Process(); + if (event->Process()) + SaveEvent(*event); delete event; } } @@ -639,8 +647,12 @@ ZfsDaemon::EventLoop() int result; if (s_logCaseFiles == true) { + DevCtlEventList::iterator + event(s_unconsumedEvents.begin()); s_logCaseFiles = false; CaseFile::LogAll(); + while (event != s_unconsumedEvents.end()) + (*event++)->Log(LOG_INFO); } Callout::ExpireCallouts(); From e00f9cda5187769163abed9935a7bfbf8cccd631 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Mon, 14 Oct 2013 22:01:34 +0000 Subject: [PATCH 71/89] cddl/sbin/zfsd/case_file.cc Log message correction: replacements are not necessarily by physical path. Submitted by: alans Approved by: ken (mentor) Sponsored by: Spectra Logic Corporation --- cddl/sbin/zfsd/case_file.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cddl/sbin/zfsd/case_file.cc b/cddl/sbin/zfsd/case_file.cc index e0c059549c3..15deaabc71e 100644 --- a/cddl/sbin/zfsd/case_file.cc +++ b/cddl/sbin/zfsd/case_file.cc @@ -952,7 +952,7 @@ CaseFile::Replace(const char* vdev_type, const char* path) { if (nvlist_alloc(&nvroot, NV_UNIQUE_NAME, 0) != 0 || nvlist_alloc(&newvd, NV_UNIQUE_NAME, 0) != 0) { - syslog(LOG_ERR, "Replace vdev(%s/%s) by physical path: " + syslog(LOG_ERR, "Replace vdev(%s/%s): " "Unable to allocate configuration data.\n", poolname, VdevGUIDString().c_str()); if (nvroot != NULL) @@ -964,7 +964,7 @@ CaseFile::Replace(const char* vdev_type, const char* path) { || nvlist_add_string(nvroot, ZPOOL_CONFIG_TYPE, VDEV_TYPE_ROOT) != 0 || nvlist_add_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN, &newvd, 1) != 0) { - syslog(LOG_ERR, "Replace vdev(%s/%s) by physical path: " + syslog(LOG_ERR, "Replace vdev(%s/%s): " "Unable to initialize configuration data.\n", poolname, VdevGUIDString().c_str()); nvlist_free(newvd); @@ -978,7 +978,7 @@ CaseFile::Replace(const char* vdev_type, const char* path) { if (zpool_vdev_attach(zhp, VdevGUIDString().c_str(), path, nvroot, /*replace*/B_TRUE) != 0) { syslog(LOG_ERR, - "Replace vdev(%s/%s) by physical path(attach): %s: %s\n", + "Replace vdev(%s/%s): %s: %s\n", poolname, VdevGUIDString().c_str(), libzfs_error_action(g_zfsHandle), libzfs_error_description(g_zfsHandle)); From 31b1be9cfaa47f92ec450ce3817b80b345951eec Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Mon, 14 Oct 2013 22:58:48 +0000 Subject: [PATCH 72/89] Extract devctl event processing support out of zfsd and into a new library: libdevctl. etc/mtree/BSD.include.dist: Add the /usr/include/devctl directory to the base system. lib/Makefile: lib/libdevctl/Makefile: Build support for the new library. lib/libdevctl/consumer.cc: lib/libdevctl/consumer.h: cddl/sbin/zfsd/zfsd.cc: cddl/sbin/zfsd/zfsd.h: New class, DevCtl::Consumer, from functionality extracted from ZfsDaemon, which can connect to devd, parse an event stream, and invoke event handlers. lib/libdevctl/event.cc: lib/libdevctl/event.h: cddl/sbin/zfsd/dev_ctl_event.cc: cddl/sbin/zfsd/dev_ctl_event.h: Strip Zfsd specific event handling from Zfsd's event classes to create DevCtl::Event, DevCtl::DevfsEvent, and DevCtl::ZfsEvent. lib/libdevctl/event_buffer.cc: lib/libdevctl/event_buffer.h: cddl/sbin/zfsd/zfsd.cc: cddl/sbin/zfsd/zfsd.h: DevCtl::EventBuffer: buffer for incoming devctl stream used to parse/extract individual event strings. lib/libdevctl/event_factory.cc: lib/libdevctl/event_factory.h: cddl/sbin/zfsd/zfsd.cc: cddl/sbin/zfsd/zfsd.h: DevCtl::EventFactory: Method map for converting event strings to Devctl::Event objects. lib/libdevctl/exception.cc: lib/libdevctl/exception.h: cddl/sbin/zfsd/zfsd_exception.cc: cddl/sbin/zfsd/zfsd_exception.h: DevCtl::Exception and DevCtl::ParseException, the exceptions which are thrown by this library. lib/libdevctl/guid.cc: lib/libdevctl/guid.h: cddl/sbin/zfsd/guid.cc: cddl/sbin/zfsd/guid.h: DevCtl::Guid: Helper routines for dealing with 64bit GUIDs such as found in Zfs pools and vdevs. lib/libdevctl/reader.cc: lib/libdevctl/reader.h: cddl/sbin/zfsd/zfsd.cc: cddl/sbin/zfsd/zfsd.h: DevCtl::Reader class hierarchy. Used to direct a Consumer to an event stream (e.g. from devd or a local/saved file). cddl/sbin/zfsd/zfsd_event.cc: cddl/sbin/zfsd/zfsd_event.h: Zfsd specialization of DevCtl::Event types. cddl/sbin/zfsd/zfsd_exception.cc: cddl/sbin/zfsd/zfsd_exception.h: Zfsd specific exception types, now derived from DevCtl::Excpetion. cddl/sbin/zfsd/Makefile: cddl/sbin/zfsd/callout.cc: cddl/sbin/zfsd/callout.h: cddl/sbin/zfsd/case_file.cc: cddl/sbin/zfsd/case_file.h: cddl/sbin/zfsd/vdev.cc: cddl/sbin/zfsd/vdev.h: cddl/sbin/zfsd/vdev_iterator.cc: cddl/sbin/zfsd/vdev_iterator.h: cddl/sbin/zfsd/zfsd.cc: cddl/sbin/zfsd/zfsd.h: cddl/sbin/zfsd/zfsd_event.cc: cddl/sbin/zfsd/zfsd_event.h: cddl/sbin/zfsd/zfsd_exception.cc: cddl/sbin/zfsd/zfsd_exception.h: cddl/sbin/zfsd/zfsd_main.cc: cddl/sbin/zfsd/zpool_list.cc: cddl/sbin/zfsd/zpool_list.h: Miscelaneuous refactoring to support using libdevctl instead of local implementation. cddl/sbin/zfsd/Makefile: cddl/sbin/zfsd/Makefile.common: tools/regression/zfsd/Makefile: Export common build information for zfsd and its unit tests to a "Makefile.common". Submitted by: gibbs Approved by: ken (mentor) Sponsored by: Spectra Logic Corporation --- cddl/sbin/zfsd/Makefile | 41 +- cddl/sbin/zfsd/Makefile.common | 43 + cddl/sbin/zfsd/callout.cc | 13 + cddl/sbin/zfsd/callout.h | 10 +- cddl/sbin/zfsd/case_file.cc | 91 ++- cddl/sbin/zfsd/case_file.h | 52 +- cddl/sbin/zfsd/vdev.cc | 15 + cddl/sbin/zfsd/vdev.h | 47 +- cddl/sbin/zfsd/vdev_iterator.cc | 14 +- cddl/sbin/zfsd/vdev_iterator.h | 17 +- cddl/sbin/zfsd/zfsd.cc | 742 ++++++------------ cddl/sbin/zfsd/zfsd.h | 402 ++-------- cddl/sbin/zfsd/zfsd_event.cc | 369 +++++++++ cddl/sbin/zfsd/zfsd_event.h | 146 ++++ cddl/sbin/zfsd/zfsd_exception.cc | 32 +- cddl/sbin/zfsd/zfsd_exception.h | 29 +- cddl/sbin/zfsd/zfsd_main.cc | 14 + cddl/sbin/zfsd/zpool_list.cc | 21 + cddl/sbin/zfsd/zpool_list.h | 17 +- etc/mtree/BSD.include.dist | 2 + lib/Makefile | 1 + lib/libdevctl/Makefile | 23 + lib/libdevctl/consumer.cc | 246 ++++++ lib/libdevctl/consumer.h | 171 ++++ .../libdevctl/event.cc | 476 +++-------- .../dev_ctl_event.h => lib/libdevctl/event.h | 327 ++------ lib/libdevctl/event_buffer.cc | 216 +++++ lib/libdevctl/event_buffer.h | 188 +++++ lib/libdevctl/event_factory.cc | 96 +++ lib/libdevctl/event_factory.h | 94 +++ lib/libdevctl/exception.cc | 123 +++ lib/libdevctl/exception.h | 166 ++++ {cddl/sbin/zfsd => lib/libdevctl}/guid.cc | 8 +- {cddl/sbin/zfsd => lib/libdevctl}/guid.h | 20 +- lib/libdevctl/reader.cc | 99 +++ lib/libdevctl/reader.h | 136 ++++ share/mk/bsd.libnames.mk | 1 + 37 files changed, 2878 insertions(+), 1630 deletions(-) create mode 100644 cddl/sbin/zfsd/Makefile.common create mode 100644 cddl/sbin/zfsd/zfsd_event.cc create mode 100644 cddl/sbin/zfsd/zfsd_event.h create mode 100644 lib/libdevctl/Makefile create mode 100644 lib/libdevctl/consumer.cc create mode 100644 lib/libdevctl/consumer.h rename cddl/sbin/zfsd/dev_ctl_event.cc => lib/libdevctl/event.cc (50%) rename cddl/sbin/zfsd/dev_ctl_event.h => lib/libdevctl/event.h (50%) create mode 100644 lib/libdevctl/event_buffer.cc create mode 100644 lib/libdevctl/event_buffer.h create mode 100644 lib/libdevctl/event_factory.cc create mode 100644 lib/libdevctl/event_factory.h create mode 100644 lib/libdevctl/exception.cc create mode 100644 lib/libdevctl/exception.h rename {cddl/sbin/zfsd => lib/libdevctl}/guid.cc (95%) rename {cddl/sbin/zfsd => lib/libdevctl}/guid.h (85%) create mode 100644 lib/libdevctl/reader.cc create mode 100644 lib/libdevctl/reader.h diff --git a/cddl/sbin/zfsd/Makefile b/cddl/sbin/zfsd/Makefile index 82df7d493a4..58caefb77a0 100644 --- a/cddl/sbin/zfsd/Makefile +++ b/cddl/sbin/zfsd/Makefile @@ -1,44 +1,7 @@ # $FreeBSD$ +.include "Makefile.common" + PROG_CXX= zfsd -SRCS= callout.cc \ - case_file.cc \ - dev_ctl_event.cc \ - guid.cc \ - vdev.cc \ - vdev_iterator.cc \ - zfsd.cc \ - zfsd_exception.cc \ - zpool_list.cc \ - zfsd_main.cc - -NO_MAN= YES - -WARNS?= 1 - -INCFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/lib/libzpool/common -INCFLAGS+= -I${.CURDIR}/../../../cddl/compat/opensolaris/include -INCFLAGS+= -I${.CURDIR}/../../../cddl/compat/opensolaris/lib/libumem -INCFLAGS+= -I${.CURDIR}/../../../sys/cddl/compat/opensolaris -INCFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/head -INCFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/lib/libuutil/common -INCFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/lib/libumem/common -INCFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/lib/libzfs/common -INCFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/lib/libnvpair -INCFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/common/zfs -INCFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common -INCFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common/fs/zfs -INCFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common/sys - -CFLAGS= -Wall -g -DNEED_SOLARIS_BOOLEAN ${INCFLAGS} - -DPADD= ${LIBZFS} ${LIBUTIL} ${LIBGEOM} ${LIBBSDXML} ${LIBSBUF} \ - ${LIBNVPAIR} ${LIBUUTIL} -LDADD= -lzfs -lutil -lgeom -lbsdxml -lsbuf -lnvpair -luutil - -cscope: - find ${.CURDIR} -type f -a \( -name "*.[ch]" -o -name "*.cc" \) \ - > ${.CURDIR}/cscope.files - cd ${.CURDIR} && cscope -buq ${INCFLAGS} .include diff --git a/cddl/sbin/zfsd/Makefile.common b/cddl/sbin/zfsd/Makefile.common new file mode 100644 index 00000000000..65fecb5c5f2 --- /dev/null +++ b/cddl/sbin/zfsd/Makefile.common @@ -0,0 +1,43 @@ +# $FreeBSD$ + +SRCS= callout.cc \ + case_file.cc \ + zfsd_event.cc \ + vdev.cc \ + vdev_iterator.cc \ + zfsd.cc \ + zfsd_exception.cc \ + zpool_list.cc \ + zfsd_main.cc + +NO_MAN= YES + +WARNS?= 3 + +# Ignore warnings about Solaris specific pragmas. +IGNORE_PRAGMA= YES + +INCFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/lib/libzpool/common +INCFLAGS+= -I${.CURDIR}/../../../cddl/compat/opensolaris/include +INCFLAGS+= -I${.CURDIR}/../../../cddl/compat/opensolaris/lib/libumem +INCFLAGS+= -I${.CURDIR}/../../../sys/cddl/compat/opensolaris +INCFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/head +INCFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/lib/libuutil/common +INCFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/lib/libumem/common +INCFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/lib/libzfs/common +INCFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/lib/libnvpair +INCFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/common/zfs +INCFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common +INCFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common/fs/zfs +INCFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common/sys + +CFLAGS= -g -DNEED_SOLARIS_BOOLEAN ${INCFLAGS} + +DPADD= ${LIBDEVCTL} ${LIBZFS} ${LIBUTIL} ${LIBGEOM} ${LIBBSDXML} ${LIBSBUF} \ + ${LIBNVPAIR} ${LIBUUTIL} +LDADD= -ldevctl -lzfs -lutil -lgeom -lbsdxml -lsbuf -lnvpair -luutil + +cscope: + find ${.CURDIR} -type f -a \( -name "*.[ch]" -o -name "*.cc" \) \ + > ${.CURDIR}/cscope.files + cd ${.CURDIR} && cscope -buq ${INCFLAGS} diff --git a/cddl/sbin/zfsd/callout.cc b/cddl/sbin/zfsd/callout.cc index b1599596476..1f1735cdcb3 100644 --- a/cddl/sbin/zfsd/callout.cc +++ b/cddl/sbin/zfsd/callout.cc @@ -37,10 +37,23 @@ * timer services built on top of the POSIX interval timer. */ +#include + #include #include +#include +#include +#include + +#include +#include +#include +#include +#include + #include "callout.h" +#include "vdev_iterator.h" #include "zfsd.h" #include "zfsd_exception.h" diff --git a/cddl/sbin/zfsd/callout.h b/cddl/sbin/zfsd/callout.h index 897bcea62f5..83732dd3c7a 100644 --- a/cddl/sbin/zfsd/callout.h +++ b/cddl/sbin/zfsd/callout.h @@ -34,15 +34,17 @@ * \file callout.h * * \brief Interface for timer based callback services. + * + * Header requirements: + * + * #include + * + * #include */ #ifndef _CALLOUT_H_ #define _CALLOUT_H_ -#include - -#include - /** * \brief Type of the function callback from a Callout. */ diff --git a/cddl/sbin/zfsd/case_file.cc b/cddl/sbin/zfsd/case_file.cc index 15deaabc71e..a3442473bc8 100644 --- a/cddl/sbin/zfsd/case_file.cc +++ b/cddl/sbin/zfsd/case_file.cc @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2011 Spectra Logic Corporation + * Copyright (c) 2011, 2012, 2013 Spectra Logic Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,6 +38,11 @@ * across reboots. For now, this is just a log of soft errors which we * accumulate in order to mark a device as degraded. */ +#include +#include + +#include + #include #include #include @@ -46,12 +51,31 @@ #include #include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "callout.h" +#include "zfsd_event.h" #include "case_file.h" #include "vdev.h" +#include "vdev_iterator.h" #include "zfsd.h" #include "zfsd_exception.h" #include "zpool_list.h" +__FBSDID("$FreeBSD$"); + /*============================ Namespace Control =============================*/ using std::auto_ptr; using std::hex; @@ -60,26 +84,43 @@ using std::stringstream; using std::setfill; using std::setw; +using DevCtl::Event; +using DevCtl::EventBuffer; +using DevCtl::EventFactory; +using DevCtl::EventList; +using DevCtl::Guid; +using DevCtl::IstreamReader; +using DevCtl::ParseException; + /*-------------------------- File-scoped classes ----------------------------*/ /** * \brief Functor that operators on STL collections of CaseFiles + * * Selectively calls ReEvaluate on the casefile, based on its pool GUID. */ class CaseFileReEvaluator : public std::unary_function { public: - CaseFileReEvaluator(Guid guid, const ZfsEvent &event) : - m_poolGUID(guid), m_event(event) {}; - void operator() (CaseFile *casefile) { - if (m_poolGUID == casefile->PoolGUID()) - casefile->ReEvaluate(m_event); - } + CaseFileReEvaluator(Guid guid, const ZfsEvent &event); + + void operator() (CaseFile *casefile); + private: Guid m_poolGUID; - const ZfsEvent &m_event; + const ZfsEvent &m_event; }; +CaseFileReEvaluator::CaseFileReEvaluator(Guid guid, const ZfsEvent &event) + : m_poolGUID(guid), m_event(event) +{ +} +void +CaseFileReEvaluator::operator() (CaseFile *casefile) +{ + if (m_poolGUID == casefile->PoolGUID()) + casefile->ReEvaluate(m_event); +} /*--------------------------------- CaseFile ---------------------------------*/ //- CaseFile Static Data ------------------------------------------------------- @@ -88,7 +129,6 @@ const string CaseFile::s_caseFilePath = "/etc/zfs/cases"; const timeval CaseFile::s_removeGracePeriod = { 60 /*sec*/, 0 /*usec*/}; //- CaseFile Static Public Methods --------------------------------------------- - CaseFile * CaseFile::Find(Guid poolGUID, Guid vdevGUID) { @@ -509,7 +549,7 @@ CaseFile::ActivateSpare() { } void -CaseFile::RegisterCallout(const DevCtlEvent &event) +CaseFile::RegisterCallout(const Event &event) { timeval now, countdown, elapsed, timestamp, zero, remaining; @@ -594,13 +634,13 @@ CaseFile::Log() zpool_state_to_name(VdevState(), VDEV_AUX_NONE)); if (m_tentativeEvents.size() != 0) { syslog(LOG_INFO, "\t=== Tentative Events ===\n"); - for (DevCtlEventList::iterator event(m_tentativeEvents.begin()); + for (EventList::iterator event(m_tentativeEvents.begin()); event != m_tentativeEvents.end(); event++) (*event)->Log(LOG_INFO); } if (m_events.size() != 0) { syslog(LOG_INFO, "\t=== Events ===\n"); - for (DevCtlEventList::iterator event(m_events.begin()); + for (EventList::iterator event(m_events.begin()); event != m_events.end(); event++) (*event)->Log(LOG_INFO); } @@ -738,7 +778,7 @@ CaseFile::~CaseFile() void CaseFile::PurgeEvents() { - for (DevCtlEventList::iterator event(m_events.begin()); + for (EventList::iterator event(m_events.begin()); event != m_events.end(); event++) delete *event; @@ -748,7 +788,7 @@ CaseFile::PurgeEvents() void CaseFile::PurgeTentativeEvents() { - for (DevCtlEventList::iterator event(m_tentativeEvents.begin()); + for (EventList::iterator event(m_tentativeEvents.begin()); event != m_tentativeEvents.end(); event++) delete *event; @@ -756,12 +796,12 @@ CaseFile::PurgeTentativeEvents() } void -CaseFile::SerializeEvList(const DevCtlEventList events, int fd, +CaseFile::SerializeEvList(const EventList events, int fd, const char* prefix) const { if (events.empty()) return; - for (DevCtlEventList::const_iterator curEvent = events.begin(); + for (EventList::const_iterator curEvent = events.begin(); curEvent != events.end(); curEvent++) { const string &eventString((*curEvent)->GetEventString()); @@ -816,7 +856,7 @@ CaseFile::DeSerialize(ifstream &caseStream) * call ExtractEvent * continue */ - DevCtlEventList* destEvents; + EventList* destEvents; string tentFlag("tentative "); string line; std::stringbuf lineBuf; @@ -832,8 +872,9 @@ CaseFile::DeSerialize(ifstream &caseStream) } fakeDevdSocket << line; fakeDevdSocket << '\n'; + const EventFactory &factory(ZfsDaemon::Get().GetFactory()); while (eventBuffer.ExtractEvent(evString)) { - DevCtlEvent *event(DevCtlEvent::CreateEvent(evString)); + Event *event(Event::CreateEvent(factory, evString)); if (event != NULL) { destEvents->push_back(event); RegisterCallout(*event); @@ -993,23 +1034,29 @@ CaseFile::Replace(const char* vdev_type, const char* path) { } /* Does the argument event refer to a checksum error? */ -static bool IsChecksumEvent(const DevCtlEvent* const event){ +static bool +IsChecksumEvent(const Event* const event) +{ return ("ereport.fs.zfs.checksum" == event->Value("type")); } /* Does the argument event refer to an IO error? */ -static bool IsIOEvent(const DevCtlEvent* const event){ +static bool +IsIOEvent(const Event* const event) +{ return ("ereport.fs.zfs.io" == event->Value("type")); } bool -CaseFile::ShouldDegrade() const { +CaseFile::ShouldDegrade() const +{ return (std::count_if(m_events.begin(), m_events.end(), IsChecksumEvent) > ZFS_DEGRADE_IO_COUNT); } bool -CaseFile::ShouldFault() const { +CaseFile::ShouldFault() const +{ return (std::count_if(m_events.begin(), m_events.end(), IsIOEvent) > ZFS_DEGRADE_IO_COUNT); } diff --git a/cddl/sbin/zfsd/case_file.h b/cddl/sbin/zfsd/case_file.h index 3c757647849..489645cb85f 100644 --- a/cddl/sbin/zfsd/case_file.h +++ b/cddl/sbin/zfsd/case_file.h @@ -37,18 +37,17 @@ * * CaseFile objects aggregate vdev faults that may require ZFSD action * in order to maintain the health of a ZFS pool. + * + * Header requirements: + * + * #include + * + * #include "callout.h" + * #include "zfsd_event.h" */ #ifndef _CASE_FILE_H_ #define _CASE_FILE_H_ -#include -#include - -#include - -#include "callout.h" -#include "dev_ctl_event.h" - /*=========================== Forward Declarations ===========================*/ class CaseFile; class Vdev; @@ -95,7 +94,7 @@ public: * \return If found, a pointer to a valid CaseFile object. * Otherwise NULL. */ - static CaseFile *Find(Guid poolGUID, Guid vdevGUID); + static CaseFile *Find(DevCtl::Guid poolGUID, DevCtl::Guid vdevGUID); /** * \brief Find a CaseFile object by a vdev's current/last known @@ -114,7 +113,8 @@ public: * \param poolGUID Only reevaluate cases for this pool * \param event Try to consume this event with the casefile */ - static void ReEvaluateByGuid(Guid poolGUID, const ZfsEvent &event); + static void ReEvaluateByGuid(DevCtl::Guid poolGUID, + const ZfsEvent &event); /** * \brief Create or return an existing active CaseFile for the @@ -145,8 +145,8 @@ public: */ static void PurgeAll(); - Guid PoolGUID() const; - Guid VdevGUID() const; + DevCtl::Guid PoolGUID() const; + DevCtl::Guid VdevGUID() const; vdev_state VdevState() const; const string &PoolGUIDString() const; const string &VdevGUIDString() const; @@ -171,6 +171,7 @@ public: /** * \brief Update this CaseFile in light of the provided ZfsEvent. + * * Must be virtual so it can be overridden in the unit tests * * \param event The ZfsEvent to evaluate. @@ -182,7 +183,7 @@ public: /** * \brief Register an itimer callout for the given event, if necessary */ - virtual void RegisterCallout(const DevCtlEvent &event); + virtual void RegisterCallout(const DevCtl::Event &event); /** * \brief Close a case if it is no longer relevant. @@ -289,7 +290,7 @@ protected: * \param prefix If not NULL, this prefix will be prepended to * every event in the file. */ - void SerializeEvList(const DevCtlEventList events, int fd, + void SerializeEvList(const DevCtl::EventList events, int fd, const char* prefix=NULL) const; /** @@ -327,7 +328,6 @@ protected: * \param path The file system path to the new vdev * * \return true iff the replacement was successful - * */ bool Replace(const char* vdev_type, const char* path); @@ -351,35 +351,35 @@ protected: * \brief A list of soft error events counted against the health of * a vdev. */ - DevCtlEventList m_events; + DevCtl::EventList m_events; /** * \brief A list of soft error events waiting for a grace period * expiration before being counted against the health of * a vdev. */ - DevCtlEventList m_tentativeEvents; + DevCtl::EventList m_tentativeEvents; - Guid m_poolGUID; - Guid m_vdevGUID; - vdev_state m_vdevState; - string m_poolGUIDString; - string m_vdevGUIDString; - string m_vdevPhysPath; + DevCtl::Guid m_poolGUID; + DevCtl::Guid m_vdevGUID; + vdev_state m_vdevState; + string m_poolGUIDString; + string m_vdevGUIDString; + string m_vdevPhysPath; /** * \brief Callout activated when a grace period */ - Callout m_tentativeTimer; + Callout m_tentativeTimer; }; -inline Guid +inline DevCtl::Guid CaseFile::PoolGUID() const { return (m_poolGUID); } -inline Guid +inline DevCtl::Guid CaseFile::VdevGUID() const { return (m_vdevGUID); diff --git a/cddl/sbin/zfsd/vdev.cc b/cddl/sbin/zfsd/vdev.cc index cd7d5e7ae00..f05413b122b 100644 --- a/cddl/sbin/zfsd/vdev.cc +++ b/cddl/sbin/zfsd/vdev.cc @@ -38,15 +38,30 @@ * Implementation of the Vdev class. */ #include +#include +#include + +#include +#include +#include #include +#include +#include +#include +#include +#include +#include + #include "vdev.h" +#include "vdev_iterator.h" #include "zfsd.h" #include "zfsd_exception.h" __FBSDID("$FreeBSD$"); /*============================ Namespace Control =============================*/ +using std::string; using std::stringstream; /*=========================== Class Implementations ==========================*/ diff --git a/cddl/sbin/zfsd/vdev.h b/cddl/sbin/zfsd/vdev.h index ddeb9771f6f..9c49b1a769f 100644 --- a/cddl/sbin/zfsd/vdev.h +++ b/cddl/sbin/zfsd/vdev.h @@ -36,18 +36,25 @@ * \file vdev.h * * Definition of the Vdev class. + * + * Header requirements: + * + * #include + * + * #include */ #ifndef _VDEV_H_ #define _VDEV_H_ -#include -#include +/*=========================== Forward Declarations ===========================*/ +struct zpool_handle; +typedef struct zpool_handle zpool_handle_t; -#include -#include - -#include "guid.h" +struct nvlist; +typedef struct nvlist nvlist_t; +/*============================= Class Definitions ============================*/ +/*----------------------------------- Vdev -----------------------------------*/ /** * \brief Wrapper class for a vdev's name/value configuration list * simplifying access to commonly used vdev attributes. @@ -96,30 +103,30 @@ public: */ Vdev(nvlist_t *vdevConfig); - virtual Guid GUID() const; - virtual Guid PoolGUID() const; - virtual vdev_state State() const; - std::string Path() const; - virtual std::string PhysicalPath() const; - std::string GUIDString() const; - nvlist_t *PoolConfig() const; - nvlist_t *Config() const; + virtual DevCtl::Guid GUID() const; + virtual DevCtl::Guid PoolGUID() const; + virtual vdev_state State() const; + std::string Path() const; + virtual std::string PhysicalPath() const; + std::string GUIDString() const; + nvlist_t *PoolConfig() const; + nvlist_t *Config() const; private: - Guid m_poolGUID; - Guid m_vdevGUID; - nvlist_t *m_poolConfig; - nvlist_t *m_config; + DevCtl::Guid m_poolGUID; + DevCtl::Guid m_vdevGUID; + nvlist_t *m_poolConfig; + nvlist_t *m_config; }; //- Vdev Inline Public Methods ------------------------------------------------ -inline Guid +inline DevCtl::Guid Vdev::PoolGUID() const { return (m_poolGUID); } -inline Guid +inline DevCtl::Guid Vdev::GUID() const { return (m_vdevGUID); diff --git a/cddl/sbin/zfsd/vdev_iterator.cc b/cddl/sbin/zfsd/vdev_iterator.cc index ce624fbfcca..6fac3259d91 100644 --- a/cddl/sbin/zfsd/vdev_iterator.cc +++ b/cddl/sbin/zfsd/vdev_iterator.cc @@ -35,17 +35,27 @@ * * Implementation of the VdevIterator class. */ +#include +#include + #include #include -#include #include +#include +#include + +#include +#include + #include "vdev.h" #include "vdev_iterator.h" -#include "zfsd.h" #include "zfsd_exception.h" +/*============================ Namespace Control =============================*/ +using DevCtl::Guid; + /*=========================== Class Implementations ==========================*/ /*------------------------------- VdevIterator -------------------------------*/ VdevIterator::VdevIterator(zpool_handle_t *pool) diff --git a/cddl/sbin/zfsd/vdev_iterator.h b/cddl/sbin/zfsd/vdev_iterator.h index bf670973163..398d0aec8e9 100644 --- a/cddl/sbin/zfsd/vdev_iterator.h +++ b/cddl/sbin/zfsd/vdev_iterator.h @@ -34,16 +34,21 @@ * \file vdev_iterator.h * * VdevIterator class definition. + * + * Header requirements: + * + * #include */ #ifndef _VDEV_ITERATOR_H_ #define _VDEV_ITERATOR_H_ -#include - -#include -#include - /*=========================== Forward Declarations ===========================*/ +struct zpool_handle; +typedef struct zpool_handle zpool_handle_t; + +struct nvlist; +typedef struct nvlist nvlist_t; + class Vdev; /*============================= Class Definitions ============================*/ @@ -97,7 +102,7 @@ public: * Upon return, the VdevIterator's cursor points to the vdev just * past the returned vdev or end() if no matching vdev is found. */ - nvlist_t *Find(Guid vdevGUID); + nvlist_t *Find(DevCtl::Guid vdevGUID); /** * \brief Perform the specified operation on each leaf member of diff --git a/cddl/sbin/zfsd/zfsd.cc b/cddl/sbin/zfsd/zfsd.cc index 01a5d71b844..a6ee947403f 100644 --- a/cddl/sbin/zfsd/zfsd.cc +++ b/cddl/sbin/zfsd/zfsd.cc @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2011 Spectra Logic Corporation + * Copyright (c) 2011, 2012, 2013 Spectra Logic Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -42,242 +42,77 @@ */ #include -#include -#include #include -#include -#include -#include -#include +#include -#include -#include -#include #include -#include -#include -#include +#include #include -#include -#include +#include #include -#include #include -#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include #include "callout.h" +#include "zfsd_event.h" +#include "case_file.h" #include "vdev.h" +#include "vdev_iterator.h" #include "zfsd.h" #include "zfsd_exception.h" #include "zpool_list.h" __FBSDID("$FreeBSD$"); +/*================================== Macros ==================================*/ +#define NUM_ELEMENTS(x) (sizeof(x) / sizeof(*x)) + /*============================ Namespace Control =============================*/ -using std::string; -using std::stringstream; -using std::cerr; -using std::cout; -using std::endl; +using DevCtl::Event; +using DevCtl::EventBuffer; +using DevCtl::EventFactory; +using DevCtl::EventList; +using DevCtl::Guid; +using DevCtl::NVPairMap; /*================================ Global Data ===============================*/ -const char g_devdSock[] = "/var/run/devd.pipe"; int g_debug = 0; libzfs_handle_t *g_zfsHandle; -/*-------------------------------- FDReader -------------------------------*/ -//- FDReader Public Methods ---------------------------------------------------- -size_t -FDReader::in_avail() const -{ - int bytes; - if (ioctl(m_fd, FIONREAD, &bytes)) { - syslog(LOG_ERR, "ioctl FIONREAD: %s", strerror(errno)); - return (0); - } - return (bytes); -} - - -/*-------------------------------- IstreamReader ---------------------------*/ -//- IstreamReader Public Methods ---------------------------------------------- -ssize_t -IstreamReader::read(char* buf, size_t count) -{ - m_stream->read(buf, count); - if (m_stream->fail()) - return (-1); - return (m_stream->gcount()); -} - - -/*-------------------------------- EventBuffer -------------------------------*/ -//- EventBuffer Static Data ---------------------------------------------------- -/** - * NOTIFY, NOMATCH, ATTACH, DETACH. See DevCtlEvent::Type. - */ -const char EventBuffer::s_eventStartTokens[] = "!?+-"; - -/** - * Events are terminated by a newline. - */ -const char EventBuffer::s_eventEndTokens[] = "\n"; - -/** - * Key=Value pairs are terminated by whitespace. - */ -const char EventBuffer::s_keyPairSepTokens[] = " \t\n"; - -//- EventBuffer Public Methods ------------------------------------------------- -EventBuffer::EventBuffer(Reader& reader) - : m_reader(reader), - m_validLen(0), - m_parsedLen(0), - m_nextEventOffset(0), - m_synchronized(true) -{ -} - -bool -EventBuffer::ExtractEvent(string &eventString) -{ - stringstream tsField; - timeval now; - - gettimeofday(&now, NULL); - tsField << " timestamp=" << now.tv_sec; - - while (UnParsed() > 0 || Fill()) { - - /* - * If the valid data in the buffer isn't enough to hold - * a full event, try reading more. - */ - if (NextEventMaxLen() < MIN_EVENT_SIZE) { - m_parsedLen += UnParsed(); - continue; - } - - char *nextEvent(m_buf + m_nextEventOffset); - bool truncated(true); - size_t eventLen(strcspn(nextEvent, s_eventEndTokens)); - - if (!m_synchronized) { - /* Discard data until an end token is read. */ - if (nextEvent[eventLen] != '\0') - m_synchronized = true; - m_nextEventOffset += eventLen; - m_parsedLen = m_nextEventOffset; - continue; - } else if (nextEvent[eventLen] == '\0') { - - m_parsedLen += eventLen; - if (m_parsedLen < MAX_EVENT_SIZE) { - /* - * Ran out of buffer before hitting - * a full event. Fill() and try again. - */ - continue; - } - syslog(LOG_WARNING, "Overran event buffer\n\tm_nextEventOffset" - "=%zd\n\tm_parsedLen=%zd\n\tm_validLen=%zd", - m_nextEventOffset, m_parsedLen, m_validLen); - } else { - /* - * Include the normal terminator in the extracted - * event data. - */ - eventLen += 1; - truncated = false; - } - - m_nextEventOffset += eventLen; - m_parsedLen = m_nextEventOffset; - eventString.assign(nextEvent, eventLen); - - if (truncated) { - size_t fieldEnd; - - /* Break cleanly at the end of a key<=>value pair. */ - fieldEnd = eventString.find_last_of(s_keyPairSepTokens); - if (fieldEnd != string::npos) - eventString.erase(fieldEnd); - eventString += '\n'; - - m_synchronized = false; - syslog(LOG_WARNING, - "Truncated %zd characters from event.", - eventLen - fieldEnd); - } - - /* - * Add a timestamp as the final field of the event if it is - * not already present. - */ - if (eventString.find("timestamp=") == string::npos) { - size_t eventEnd(eventString.find_last_not_of('\n') + 1); - - eventString.insert(eventEnd, tsField.str()); - } - - return (true); - } - return (false); -} - -//- EventBuffer Private Methods ------------------------------------------------ -bool -EventBuffer::Fill() -{ - size_t avail; - ssize_t consumed(0); - - /* Compact the buffer. */ - if (m_nextEventOffset != 0) { - memmove(m_buf, m_buf + m_nextEventOffset, - m_validLen - m_nextEventOffset); - m_validLen -= m_nextEventOffset; - m_parsedLen -= m_nextEventOffset; - m_nextEventOffset = 0; - } - - /* Fill any empty space. */ - avail = m_reader.in_avail(); - if (avail) { - size_t want; - - want = std::min(avail, MAX_READ_SIZE - m_validLen); - consumed = m_reader.read(m_buf + m_validLen, want); - if (consumed == -1) { - if (errno == EINTR) - return (false); - else - err(1, "EventBuffer::Fill(): Read failed"); - } - } - - m_validLen += consumed; - /* Guarantee our buffer is always NUL terminated. */ - m_buf[m_validLen] = '\0'; - - return (consumed > 0); -} - /*--------------------------------- ZfsDaemon --------------------------------*/ //- ZfsDaemon Static Private Data ---------------------------------------------- -bool ZfsDaemon::s_logCaseFiles; -bool ZfsDaemon::s_terminateEventLoop; -char ZfsDaemon::s_pidFilePath[] = "/var/run/zfsd.pid"; -pidfh *ZfsDaemon::s_pidFH; -FDReader* ZfsDaemon::s_reader; -int ZfsDaemon::s_devdSockFD = -1; -int ZfsDaemon::s_signalPipeFD[2]; -bool ZfsDaemon::s_systemRescanRequested(false); -bool ZfsDaemon::s_consumingEvents(false); -DevCtlEventList ZfsDaemon::s_unconsumedEvents; +ZfsDaemon *ZfsDaemon::s_theZfsDaemon; +bool ZfsDaemon::s_logCaseFiles; +bool ZfsDaemon::s_terminateEventLoop; +char ZfsDaemon::s_pidFilePath[] = "/var/run/zfsd.pid"; +pidfh *ZfsDaemon::s_pidFH; +int ZfsDaemon::s_signalPipeFD[2]; +bool ZfsDaemon::s_systemRescanRequested(false); +EventFactory::Record ZfsDaemon::s_registryEntries[] = +{ + { Event::NOTIFY, "DEVFS", &DevfsEvent::DevfsEventBuilder }, + { Event::NOTIFY, "ZFS", &ZfsEvent::ZfsEventBuilder } +}; //- ZfsDaemon Static Public Methods -------------------------------------------- +ZfsDaemon & +ZfsDaemon::Get() +{ + return (*s_theZfsDaemon); +} + void ZfsDaemon::WakeEventLoop() { @@ -294,36 +129,40 @@ ZfsDaemon::RequestSystemRescan() void ZfsDaemon::Run() { - Init(); + ZfsDaemon daemon; while (s_terminateEventLoop == false) { try { - DisconnectFromDevd(); + daemon.DisconnectFromDevd(); - if (ConnectToDevd() == false) { + if (daemon.ConnectToDevd() == false) { sleep(30); continue; } - DetectMissedEvents(); + daemon.DetectMissedEvents(); - EventLoop(); + daemon.EventLoop(); } catch (const ZfsdException &exp) { exp.Log(); } } - DisconnectFromDevd(); - - Fini(); + daemon.DisconnectFromDevd(); } -//- ZfsDaemon Static Private Methods ------------------------------------------- -void -ZfsDaemon::Init() +//- ZfsDaemon Private Methods -------------------------------------------------- +ZfsDaemon::ZfsDaemon() + : Consumer(/*defBuilder*/NULL, s_registryEntries, + NUM_ELEMENTS(s_registryEntries)) { + if (s_theZfsDaemon != NULL) + errx(1, "Multiple ZfsDaemon instances created. Exiting"); + + s_theZfsDaemon = this; + if (pipe(s_signalPipeFD) != 0) errx(1, "Unable to allocate signal pipe. Exiting"); @@ -344,7 +183,6 @@ ZfsDaemon::Init() errx(1, "Unable to initialize ZFS library. Exiting"); Callout::Init(); - DevCtlEvent::Init(); InitializeSyslog(); OpenPIDFile(); @@ -354,13 +192,174 @@ ZfsDaemon::Init() UpdatePIDFile(); } -void -ZfsDaemon::Fini() +ZfsDaemon::~ZfsDaemon() { PurgeCaseFiles(); ClosePIDFile(); } +void +ZfsDaemon::PurgeCaseFiles() +{ + CaseFile::PurgeAll(); +} + +bool +ZfsDaemon::VdevAddCaseFile(Vdev &vdev, void *cbArg) +{ + if (vdev.State() != VDEV_STATE_HEALTHY) + CaseFile::Create(vdev); + + return (/*break early*/false); +} + +void +ZfsDaemon::BuildCaseFiles() +{ + /* Add CaseFiles for vdevs with issues. */ + ZpoolList zpl; + + for (ZpoolList::iterator pool = zpl.begin(); pool != zpl.end(); pool++) + VdevIterator(*pool).Each(VdevAddCaseFile, NULL); + + /* De-serialize any saved cases. */ + CaseFile::DeSerialize(); +} + +void +ZfsDaemon::RescanSystem() +{ + struct gmesh mesh; + struct gclass *mp; + struct ggeom *gp; + struct gprovider *pp; + int result; + + /* + * The devctl system doesn't replay events for new consumers + * of the interface. Emit manufactured DEVFS arrival events + * for any devices that already before we started or during + * periods where we've lost our connection to devd. + */ + result = geom_gettree(&mesh); + if (result != 0) { + syslog(LOG_ERR, "ZfsDaemon::RescanSystem: " + "geom_gettree faild with error %d\n", result); + return; + } + + const string evStart("!system=DEVFS subsystem=CDEV type=CREATE " + "sub_type=synthesized cdev="); + LIST_FOREACH(mp, &mesh.lg_class, lg_class) { + LIST_FOREACH(gp, &mp->lg_geom, lg_geom) { + LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { + Event *event; + + string evString(evStart + pp->lg_name + "\n"); + event = Event::CreateEvent(GetFactory(), + evString); + if (event != NULL) + if (event->Process()) + SaveEvent(*event); + delete event; + } + } + } + geom_deletetree(&mesh); +} + +void +ZfsDaemon::DetectMissedEvents() +{ + do { + PurgeCaseFiles(); + + /* + * Discard any events waiting for us. We don't know + * if they still apply to the current state of the + * system. + */ + FlushEvents(); + + BuildCaseFiles(); + + /* + * If the system state has changed durring our + * interrogation, start over. + */ + } while (EventsPending()); + + RescanSystem(); +} + +void +ZfsDaemon::EventLoop() +{ + while (s_terminateEventLoop == false) { + struct pollfd fds[2]; + int result; + + if (s_logCaseFiles == true) { + EventList::iterator event(m_unconsumedEvents.begin()); + s_logCaseFiles = false; + CaseFile::LogAll(); + while (event != m_unconsumedEvents.end()) + (*event++)->Log(LOG_INFO); + } + + Callout::ExpireCallouts(); + + /* Wait for data. */ + fds[0].fd = m_devdSockFD; + fds[0].events = POLLIN; + fds[0].revents = 0; + fds[1].fd = s_signalPipeFD[0]; + fds[1].events = POLLIN; + fds[1].revents = 0; + result = poll(fds, NUM_ELEMENTS(fds), /*timeout*/INFTIM); + if (result == -1) { + if (errno == EINTR) + continue; + else + err(1, "Polling for devd events failed"); + } else if (result == 0) { + errx(1, "Unexpected result of 0 from poll. Exiting"); + } + + if ((fds[0].revents & POLLIN) != 0) + ProcessEvents(); + + if ((fds[1].revents & POLLIN) != 0) { + static char discardBuf[128]; + + /* + * This pipe exists just to close the signal + * race. Its contents are of no interest to + * us, but we must ensure that future signals + * have space in the pipe to write. + */ + while (read(s_signalPipeFD[0], discardBuf, + sizeof(discardBuf)) > 0) + ; + } + + if (s_systemRescanRequested == true) { + s_systemRescanRequested = false; + RescanSystem(); + } + + if ((fds[0].revents & POLLERR) != 0) { + syslog(LOG_INFO, "POLLERROR detected on devd socket."); + break; + } + + if ((fds[0].revents & POLLHUP) != 0) { + syslog(LOG_INFO, "POLLHUP detected on devd socket."); + break; + } + } +} +//- ZfsDaemon staic Private Methods -------------------------------------------- void ZfsDaemon::InfoSignalHandler(int) { @@ -414,296 +413,3 @@ ZfsDaemon::InitializeSyslog() openlog("zfsd", LOG_NDELAY, LOG_DAEMON); } -bool -ZfsDaemon::ConnectToDevd() -{ - struct sockaddr_un devdAddr; - int sLen; - int result; - - syslog(LOG_INFO, "Connecting to devd"); - - memset(&devdAddr, 0, sizeof(devdAddr)); - devdAddr.sun_family= AF_UNIX; - strlcpy(devdAddr.sun_path, g_devdSock, sizeof(devdAddr.sun_path)); - sLen = SUN_LEN(&devdAddr); - - s_devdSockFD = socket(AF_UNIX, SOCK_STREAM, 0); - if (s_devdSockFD == -1) - err(1, "Unable to create socket"); - result = connect(s_devdSockFD, - reinterpret_cast(&devdAddr), - sLen); - if (result == -1) { - syslog(LOG_INFO, "Unable to connect to devd"); - return (false); - } - - - /* Connect the stream to the file descriptor */ - s_reader = new FDReader(s_devdSockFD); - syslog(LOG_INFO, "Connection to devd successful"); - return (true); -} - -void -ZfsDaemon::DisconnectFromDevd() -{ - if (s_devdSockFD != -1) - syslog(LOG_INFO, "Disconnecting from devd."); - - delete s_reader; - s_reader = NULL; - close(s_devdSockFD); - s_devdSockFD = -1; -} - -void -ZfsDaemon::ReplayUnconsumedEvents() -{ - DevCtlEventList::iterator event(s_unconsumedEvents.begin()); - bool replayed_any = (event != s_unconsumedEvents.end()); - - s_consumingEvents = true; - if (replayed_any) - syslog(LOG_INFO, "Started replaying unconsumed events"); - while (event != s_unconsumedEvents.end()) { - /* - * Even if the event is unconsumed the second time around, drop - * it. By now we should've gotten a config_sync so any events - * that still can't be consumed are probably referring to vdevs - * or pools that no longer exist. - */ - (*event)->Process(); - delete *event; - s_unconsumedEvents.erase(event++); - } - if (replayed_any) - syslog(LOG_INFO, "Finished replaying unconsumed events"); - s_consumingEvents = false; -} - -bool -ZfsDaemon::SaveEvent(const DevCtlEvent &event) -{ - if (s_consumingEvents) - return (false); - s_unconsumedEvents.push_back(event.DeepCopy()); - return (true); -} - -/* Capture and process buffered events. */ -void -ZfsDaemon::ProcessEvents(EventBuffer &eventBuffer) -{ - string evString; - while (eventBuffer.ExtractEvent(evString)) { - DevCtlEvent *event(DevCtlEvent::CreateEvent(evString)); - if (event != NULL) { - if (event->Process()) - SaveEvent(*event); - delete event; - } - } -} - -void -ZfsDaemon::FlushEvents() -{ - char discardBuf[256]; - - while (s_reader->in_avail()) - s_reader->read(discardBuf, sizeof(discardBuf)); -} - -bool -ZfsDaemon::EventsPending() -{ - struct pollfd fds[1]; - int result; - - do { - fds->fd = s_devdSockFD; - fds->events = POLLIN; - fds->revents = 0; - result = poll(fds, NUM_ELEMENTS(fds), /*timeout*/0); - } while (result == -1 && errno == EINTR); - - if (result == -1) - err(1, "Polling for devd events failed"); - - if ((fds->revents & POLLERR) != 0) - throw ZfsdException("ZfsdDaemon:EventsPending(): " - "POLLERR detected on devd socket."); - - if ((fds->revents & POLLHUP) != 0) - throw ZfsdException("ZfsDaemon::EventsPending(): " - "POLLHUP detected on devd socket."); - - return ((fds->revents & POLLIN) != 0); -} - -void -ZfsDaemon::PurgeCaseFiles() -{ - CaseFile::PurgeAll(); -} - -bool -ZfsDaemon::VdevAddCaseFile(Vdev &vdev, void *cbArg) -{ - - if (vdev.State() != VDEV_STATE_HEALTHY) - CaseFile::Create(vdev); - - return (/*break early*/false); -} - -void -ZfsDaemon::BuildCaseFiles() -{ - /* Add CaseFiles for vdevs with issues. */ - ZpoolList zpl; - - for (ZpoolList::iterator pool = zpl.begin(); pool != zpl.end(); pool++) - VdevIterator(*pool).Each(VdevAddCaseFile, NULL); - - /* De-serialize any saved cases. */ - CaseFile::DeSerialize(); -} - -void -ZfsDaemon::RescanSystem() -{ - struct gmesh mesh; - struct gclass *mp; - struct ggeom *gp; - struct gprovider *pp; - int result; - - /* - * The devctl system doesn't replay events for new consumers - * of the interface. Emit manufactured DEVFS arrival events - * for any devices that already before we started or during - * periods where we've lost our connection to devd. - */ - result = geom_gettree(&mesh); - if (result != 0) { - syslog(LOG_ERR, "ZfsDaemon::RescanSystem: " - "geom_gettree faild with error %d\n", result); - return; - } - - const string evStart("!system=DEVFS subsystem=CDEV type=CREATE " - "sub_type=synthesized cdev="); - LIST_FOREACH(mp, &mesh.lg_class, lg_class) { - LIST_FOREACH(gp, &mp->lg_geom, lg_geom) { - LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { - DevCtlEvent *event; - - string evString(evStart + pp->lg_name + "\n"); - event = DevCtlEvent::CreateEvent(evString); - if (event != NULL) - if (event->Process()) - SaveEvent(*event); - delete event; - } - } - } - geom_deletetree(&mesh); -} - -void -ZfsDaemon::DetectMissedEvents() -{ - do { - PurgeCaseFiles(); - - /* - * Discard any events waiting for us. We don't know - * if they still apply to the current state of the - * system. - */ - FlushEvents(); - - BuildCaseFiles(); - - /* - * If the system state has changed durring our - * interrogation, start over. - */ - } while (EventsPending()); - - RescanSystem(); -} - -void -ZfsDaemon::EventLoop() -{ - EventBuffer eventBuffer(*s_reader); - - while (s_terminateEventLoop == false) { - struct pollfd fds[2]; - int result; - - if (s_logCaseFiles == true) { - DevCtlEventList::iterator - event(s_unconsumedEvents.begin()); - s_logCaseFiles = false; - CaseFile::LogAll(); - while (event != s_unconsumedEvents.end()) - (*event++)->Log(LOG_INFO); - } - - Callout::ExpireCallouts(); - - /* Wait for data. */ - fds[0].fd = s_devdSockFD; - fds[0].events = POLLIN; - fds[0].revents = 0; - fds[1].fd = s_signalPipeFD[0]; - fds[1].events = POLLIN; - fds[1].revents = 0; - result = poll(fds, NUM_ELEMENTS(fds), /*timeout*/INFTIM); - if (result == -1) { - if (errno == EINTR) - continue; - else - err(1, "Polling for devd events failed"); - } else if (result == 0) { - errx(1, "Unexpected result of 0 from poll. Exiting"); - } - - if ((fds[0].revents & POLLIN) != 0) - ProcessEvents(eventBuffer); - - if ((fds[1].revents & POLLIN) != 0) { - static char discardBuf[128]; - - /* - * This pipe exists just to close the signal - * race. Its contents are of no interest to - * us, but we must ensure that future signals - * have space in the pipe to write. - */ - while (read(s_signalPipeFD[0], discardBuf, - sizeof(discardBuf)) > 0) - ; - } - - if (s_systemRescanRequested == true) { - s_systemRescanRequested = false; - RescanSystem(); - } - - if ((fds[0].revents & POLLERR) != 0) { - syslog(LOG_INFO, "POLLERROR detected on devd socket."); - break; - } - - if ((fds[0].revents & POLLHUP) != 0) { - syslog(LOG_INFO, "POLLHUP detected on devd socket."); - break; - } - } -} diff --git a/cddl/sbin/zfsd/zfsd.h b/cddl/sbin/zfsd/zfsd.h index 3e9e16b37ff..94bbd83108e 100644 --- a/cddl/sbin/zfsd/zfsd.h +++ b/cddl/sbin/zfsd/zfsd.h @@ -37,304 +37,57 @@ * * Class definitions and supporting data strutures for the ZFS fault * management daemon. + * + * Header requirements: + * + * #include + * + * #include + * + * #include + * #include + * #include + * + * #include + * #include + * #include + * #include + * #include + * + * #include "vdev_iterator.h" */ #ifndef _ZFSD_H_ #define _ZFSD_H_ -#include -#include -#include -#include -#include - -#include -#include - -#include "case_file.h" -#include "dev_ctl_event.h" -#include "vdev_iterator.h" - -/*============================ Namespace Control =============================*/ -using std::auto_ptr; -using std::map; -using std::pair; -using std::istream; -using std::string; - -/*================================ Global Data ===============================*/ -extern const char g_devdSock[]; -extern int g_debug; -extern libzfs_handle_t *g_zfsHandle; - /*=========================== Forward Declarations ===========================*/ -struct EventFactoryRecord; struct pidfh; +struct zpool_handle; +typedef struct zpool_handle zpool_handle_t; + +struct zfs_handle; +typedef struct libzfs_handle libzfs_handle_t; + +struct nvlist; +typedef struct nvlist nvlist_t; + typedef int LeafIterFunc(zpool_handle_t *, nvlist_t *, void *); -/*================================== Macros ==================================*/ -#define NUM_ELEMENTS(x) (sizeof(x) / sizeof(*x)) +/*================================ Global Data ===============================*/ +extern int g_debug; +extern libzfs_handle_t *g_zfsHandle; /*============================= Class Definitions ============================*/ - -/*-------------------------------- Reader -------------------------------*/ -/** - * \brief A class that presents a common interface to both file descriptors - * and istreams. - * - * Standard C++ provides no way to create an iostream from a file descriptor or - * a FILE. The GNU, Apache, HPUX, and Solaris C++ libraries all provide - * non-standard ways to construct such a stream using similar semantics, but - * FreeBSD's C++ library does not. This class supports only the functionality - * needed by ZFSD; it does not implement the iostream API. - */ -class Reader -{ -public: - /** - * \brief Return the number of bytes immediately available for reading - */ - virtual size_t in_avail() const = 0; - - /** - * \brief Reads up to count bytes - * - * Whether this call blocks depends on the underlying input source. - * On error, -1 is returned, and errno will be set by the underlying - * source. - * - * \param buf Destination for the data - * \param count Maximum amount of data to read - * \returns Amount of data that was actually read - */ - virtual ssize_t read(char* buf, size_t count) = 0; - - virtual ~Reader() = 0; -}; - -inline Reader::~Reader() {} - - -/*-------------------------------- FDReader -------------------------------*/ -/** - * \brief Specialization of Reader that uses a file descriptor - */ -class FDReader : public Reader -{ -public: - /** - * \brief Constructor - * - * \param fd An open file descriptor. It will not be garbage - * collected by the destructor. - */ - FDReader(int fd); - - virtual size_t in_avail() const; - - virtual ssize_t read(char* buf, size_t count); - -protected: - /** Copy of the underlying file descriptor */ - int m_fd; -}; - -//- FDReader Inline Public Methods ----------------------------------------- -inline -FDReader::FDReader(int fd) - : m_fd(fd) -{ -} - -inline ssize_t -FDReader::read(char* buf, size_t count) -{ - return (::read(m_fd, buf, count)); -} - -/*-------------------------------- IstreamReader------------------------------*/ -/** - * \brief Specialization of Reader that uses a std::istream - */ -class IstreamReader : public Reader -{ -public: - /** - * Constructor - * - * \param stream Pointer to an open istream. It will not be - * garbage collected by the destructor. - */ - IstreamReader(istream* stream); - - virtual size_t in_avail() const; - - virtual ssize_t read(char* buf, size_t count); - -protected: - /** Copy of the underlying stream */ - istream *m_stream; -}; - -//- IstreamReader Inline Public Methods ---------------------------------------- -inline -IstreamReader::IstreamReader(istream* stream) - : m_stream(stream) -{ -} - -inline size_t -IstreamReader::in_avail() const -{ - return (m_stream->rdbuf()->in_avail()); -} - - -/*-------------------------------- EventBuffer -------------------------------*/ -/** - * \brief Class buffering event data from Devd or a similar source and - * splitting it into individual event strings. - * - * Users of this class initialize it with a Reader associated with the unix - * domain socket connection with devd or a compatible source. The lifetime of - * an EventBuffer instance should match that of the Reader passed to it. This - * is required as data from partially received events is retained in the - * EventBuffer in order to allow reconstruction of these events across multiple - * reads of the stream. - * - * Once the program determines that the Reader is ready for reading, the - * EventBuffer::ExtractEvent() should be called in a loop until the method - * returns false. - */ -class EventBuffer -{ -public: - /** - * Constructor - * - * \param reader The data source on which to buffer/parse event data. - */ - EventBuffer(Reader& reader); - - /** - * Pull a single event string out of the event buffer. - * - * \param eventString The extracted event data (if available). - * - * \return true if event data is available and eventString has - * been populated. Otherwise false. - */ - bool ExtractEvent(string &eventString); - -private: - enum { - /** - * Size of an empty event (start and end token with - * no data. The EventBuffer parsing needs at least - * this much data in the buffer for safe event extraction. - */ - MIN_EVENT_SIZE = 2, - - /* - * The maximum event size supported by ZFSD. - * Events larger than this size (minus 1) are - * truncated at the end of the last fully received - * key/value pair. - */ - MAX_EVENT_SIZE = 8192, - - /** - * The maximum amount of buffer data to read at - * a single time from the Devd file descriptor. - */ - MAX_READ_SIZE = MAX_EVENT_SIZE, - - /** - * The size of EventBuffer's buffer of Devd event data. - * This is one larger than the maximum supported event - * size, which alows us to always include a terminating - * NUL without overwriting any received data. - */ - EVENT_BUFSIZE = MAX_EVENT_SIZE + /*NUL*/1 - }; - - /** The amount of data in m_buf we have yet to look at. */ - size_t UnParsed() const; - - /** The amount of data in m_buf available for the next event. */ - size_t NextEventMaxLen() const; - - /** Fill the event buffer with event data from Devd. */ - bool Fill(); - - /** Characters we treat as beginning an event string. */ - static const char s_eventStartTokens[]; - - /** Characters we treat as ending an event string. */ - static const char s_eventEndTokens[]; - - /** Characters found between successive "key=value" strings. */ - static const char s_keyPairSepTokens[]; - - /** Temporary space for event data during our parsing. Laid out like - * this: - * <---------------------------------------------------------> - * | | | | | - * m_buf---| | | | | - * m_nextEventOffset-- | | | - * m_parsedLen------------- | | - * m_validLen-------------------------- | - * EVENT_BUFSIZE------------------------------------------------------ - * - * Data before m_nextEventOffset has already been processed. - * - * Data between m_nextEvenOffset and m_parsedLen has been parsed, but - * not processed as a single event. - * - * Data between m_parsedLen and m_validLen has been read from the - * source, but not yet parsed. - * - * Between m_validLen and EVENT_BUFSIZE is empty space. - * - * */ - char m_buf[EVENT_BUFSIZE]; - - /** Reference to the reader linked to devd's domain socket. */ - Reader& m_reader; - - /** Offset within m_buf to the beginning of free space. */ - size_t m_validLen; - - /** Offset within m_buf to the beginning of data not yet parsed */ - size_t m_parsedLen; - - /** Offset within m_buf to the start token of the next event. */ - size_t m_nextEventOffset; - - /** The EventBuffer is aligned and tracking event records. */ - bool m_synchronized; -}; - -//- EventBuffer Inline Private Methods ----------------------------------------- -inline size_t -EventBuffer::UnParsed() const -{ - return (m_validLen - m_parsedLen); -} - -inline size_t -EventBuffer::NextEventMaxLen() const -{ - return (m_validLen - m_nextEventOffset); -} - /*--------------------------------- ZfsDaemon --------------------------------*/ /** * Static singleton orchestrating the operations of the ZFS daemon program. */ -class ZfsDaemon +class ZfsDaemon : public DevCtl::Consumer { public: + /** Return the ZfsDaemon singleton. */ + static ZfsDaemon &Get(); + /** * Used by signal handlers to ensure, in a race free way, that * the event loop will perform at least one more full loop @@ -349,68 +102,38 @@ public: */ static void RequestSystemRescan(); - /** - * Queue an event for replay after the next ZFS configuration - * sync event is received. This facility is used when an event - * is received for a pool or vdev that is not visible in the - * current ZFS configuration, but may "arrive" once the kernel - * commits the configuration change that emitted the event. - */ - static bool SaveEvent(const DevCtlEvent &event); - - /** - * Reprocess any events saved via the SaveEvent() facility. - */ - static void ReplayUnconsumedEvents(); - /** Daemonize and perform all functions of the ZFS daemon. */ static void Run(); private: - /** Initialize the daemon. */ - static void Init(); - - /** Perform any necessary cleanup at daemon shutdown. */ - static void Fini(); - - /** Process incoming devctl events from devd. */ - static void ProcessEvents(EventBuffer &eventBuffer); - - /** Discard all data pending in s_devdSockFD. */ - static void FlushEvents(); + ZfsDaemon(); + ~ZfsDaemon(); static VdevCallback_t VdevAddCaseFile; - /** - * Test for data pending in s_devdSockFD - * - * \return True if data is pending. Otherwise false. - */ - static bool EventsPending(); - /** Purge our cache of outstanding ZFS issues in the system. */ - static void PurgeCaseFiles(); + void PurgeCaseFiles(); /** Build a cache of outstanding ZFS issues in the system. */ - static void BuildCaseFiles(); + void BuildCaseFiles(); /** * Iterate over all known issues and attempt to solve them * given resources currently available in the system. */ - static void RescanSystem(); + void RescanSystem(); /** * Interrogate the system looking for previously unknown * faults that occurred either before ZFSD was started, * or during a period of lost communication with Devd. */ - static void DetectMissedEvents(); + void DetectMissedEvents(); /** * Wait for and process event source activity. */ - static void EventLoop(); + void EventLoop(); /** * Signal handler for which our response is to @@ -456,74 +179,51 @@ private: */ static void InitializeSyslog(); - /** - * Open a connection to devd's unix domain socket. - * - * \return True if the connection attempt is successsful. Otherwise - * false. - */ - static bool ConnectToDevd(); - - /** - * Close a connection (if any) to devd's unix domain socket. - */ - static void DisconnectFromDevd(); + static ZfsDaemon *s_theZfsDaemon; /** * Set to true when our program is signaled to * gracefully exit. */ - static bool s_logCaseFiles; + static bool s_logCaseFiles; /** * Set to true when our program is signaled to * gracefully exit. */ - static bool s_terminateEventLoop; + static bool s_terminateEventLoop; /** * The canonical path and file name of zfsd's PID file. */ - static char s_pidFilePath[]; + static char s_pidFilePath[]; /** * Control structure for PIDFILE(3) API. */ - static pidfh *s_pidFH; - - /** - * File descriptor representing the unix domain socket - * connection with devd. - */ - static int s_devdSockFD; - - /** - * Reader object used by the EventBuffer - */ - static FDReader* s_reader; + static pidfh *s_pidFH; /** * Pipe file descriptors used to close races with our * signal handlers. */ - static int s_signalPipeFD[2]; - - /** Queued events for replay. */ - static DevCtlEventList s_unconsumedEvents; + static int s_signalPipeFD[2]; /** * Flag controlling a rescan from ZFSD's event loop of all * GEOM providers in the system to find candidates for solving * cases. */ - static bool s_systemRescanRequested; + static bool s_systemRescanRequested; /** * Flag controlling whether events can be queued. This boolean * is set during event replay to ensure that events for pools or * devices no longer in the system are not retained forever. */ - static bool s_consumingEvents; + static bool s_consumingEvents; + + static DevCtl::EventFactory::Record s_registryEntries[]; }; #endif /* _ZFSD_H_ */ diff --git a/cddl/sbin/zfsd/zfsd_event.cc b/cddl/sbin/zfsd/zfsd_event.cc new file mode 100644 index 00000000000..81cf8eeb031 --- /dev/null +++ b/cddl/sbin/zfsd/zfsd_event.cc @@ -0,0 +1,369 @@ +/*- + * Copyright (c) 2011 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + */ + +/** + * \file zfsd_event.cc + */ +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "callout.h" +#include "zfsd_event.h" +#include "case_file.h" +#include "vdev.h" +#include "vdev_iterator.h" +#include "zfsd.h" +#include "zfsd_exception.h" +#include "zpool_list.h" + +__FBSDID("$FreeBSD$"); +/*============================ Namespace Control =============================*/ +using DevCtl::Event; +using DevCtl::Guid; +using DevCtl::NVPairMap; +using std::stringstream; + +/*=========================== Class Implementations ==========================*/ + +/*-------------------------------- DevfsEvent --------------------------------*/ + +//- DevfsEvent Static Public Methods ------------------------------------------- +Event * +DevfsEvent::DevfsEventBuilder(Event::Type type, + NVPairMap &nvPairs, + const string &eventString) +{ + return (new DevfsEvent(type, nvPairs, eventString)); +} + +//- DevfsEvent Static Protected Methods ---------------------------------------- +nvlist_t * +DevfsEvent::ReadLabel(int devFd, bool &inUse, bool °raded) +{ + pool_state_t poolState; + char *poolName; + boolean_t b_inuse; + + inUse = false; + degraded = false; + poolName = NULL; + if (zpool_in_use(g_zfsHandle, devFd, &poolState, + &poolName, &b_inuse) == 0) { + nvlist_t *devLabel; + + inUse = b_inuse == B_TRUE; + if (poolName != NULL) + free(poolName); + + if (zpool_read_label(devFd, &devLabel) != 0 + || devLabel == NULL) + return (NULL); + + try { + Vdev vdev(devLabel); + degraded = vdev.State() != VDEV_STATE_HEALTHY; + return (devLabel); + } catch (ZfsdException &exp) { + string devName = fdevname(devFd); + string devPath = _PATH_DEV + devName; + string context("DevfsEvent::ReadLabel: " + + devPath + ": "); + + exp.GetString().insert(0, context); + exp.Log(); + } + } + return (NULL); +} + +bool +DevfsEvent::OnlineByLabel(const string &devPath, const string& physPath, + nvlist_t *devConfig) +{ + try { + /* + * A device with ZFS label information has been + * inserted. If it matches a device for which we + * have a case, see if we can solve that case. + */ + syslog(LOG_INFO, "Interrogating VDEV label for %s\n", + devPath.c_str()); + Vdev vdev(devConfig); + CaseFile *caseFile(CaseFile::Find(vdev.PoolGUID(), + vdev.GUID())); + if (caseFile != NULL) + return (caseFile->ReEvaluate(devPath, physPath, &vdev)); + + } catch (ZfsdException &exp) { + string context("DevfsEvent::OnlineByLabel: " + devPath + ": "); + + exp.GetString().insert(0, context); + exp.Log(); + } + return (false); +} + +//- DevfsEvent Virtual Public Methods ------------------------------------------ +Event * +DevfsEvent::DeepCopy() const +{ + return (new DevfsEvent(*this)); +} + +bool +DevfsEvent::Process() const +{ + /* + * We are only concerned with newly discovered + * devices that can be ZFS vdevs. + */ + if (Value("type") != "CREATE" || !IsDiskDev()) + return (false); + + /* Log the event since it is of interest. */ + Log(LOG_INFO); + + string devPath; + if (!DevPath(devPath)) + return (false); + + int devFd(open(devPath.c_str(), O_RDONLY)); + if (devFd == -1) + return (false); + + bool inUse; + bool degraded; + nvlist_t *devLabel(ReadLabel(devFd, inUse, degraded)); + + string physPath; + bool havePhysPath(PhysicalPath(physPath)); + + string devName; + DevName(devName); + close(devFd); + + if (inUse && devLabel != NULL) { + OnlineByLabel(devPath, physPath, devLabel); + } else if (degraded) { + syslog(LOG_INFO, "%s is marked degraded. Ignoring " + "as a replace by physical path candidate.\n", + devName.c_str()); + } else if (havePhysPath && IsWholeDev()) { + syslog(LOG_INFO, "Searching for CaseFile by Physical Path\n"); + CaseFile *caseFile(CaseFile::Find(physPath)); + if (caseFile != NULL) { + syslog(LOG_INFO, + "Found CaseFile(%s:%s:%s) - ReEvaluating\n", + caseFile->PoolGUIDString().c_str(), + caseFile->VdevGUIDString().c_str(), + zpool_state_to_name(caseFile->VdevState(), + VDEV_AUX_NONE)); + caseFile->ReEvaluate(devPath, physPath, /*vdev*/NULL); + } + } + if (devLabel != NULL) + nvlist_free(devLabel); + return (false); +} + +//- DevfsEvent Protected Methods ----------------------------------------------- +DevfsEvent::DevfsEvent(Event::Type type, NVPairMap &nvpairs, + const string &eventString) + : DevCtl::DevfsEvent(type, nvpairs, eventString) +{ +} + +DevfsEvent::DevfsEvent(const DevfsEvent &src) + : DevCtl::DevfsEvent::DevfsEvent(src) +{ +} + +/*--------------------------------- ZfsEvent ---------------------------------*/ +//- ZfsEvent Static Public Methods --------------------------------------------- +DevCtl::Event * +ZfsEvent::ZfsEventBuilder(Event::Type type, NVPairMap &nvpairs, + const string &eventString) +{ + return (new ZfsEvent(type, nvpairs, eventString)); +} + +//- ZfsEvent Virtual Public Methods -------------------------------------------- +Event * +ZfsEvent::DeepCopy() const +{ + return (new ZfsEvent(*this)); +} + +bool +ZfsEvent::Process() const +{ + string logstr(""); + + if (!Contains("class") && !Contains("type")) { + syslog(LOG_ERR, + "ZfsEvent::Process: Missing class or type data."); + return (false); + } + + /* On config syncs, replay any queued events first. */ + if (Value("type").find("misc.fs.zfs.config_sync") == 0) { + /* + * Even if saved events are unconsumed the second time + * around, drop them. Any events that still can't be + * consumed are probably referring to vdevs or pools that + * no longer exist. + */ + ZfsDaemon::Get().ReplayUnconsumedEvents(/*discard*/true); + CaseFile::ReEvaluateByGuid(PoolGUID(), *this); + } + + Log(LOG_INFO); + + if (Value("type").find("misc.fs.zfs.") == 0) { + /* Configuration changes, resilver events, etc. */ + ProcessPoolEvent(); + return (false); + } + + if (!Contains("pool_guid") || !Contains("vdev_guid")) { + /* Only currently interested in Vdev related events. */ + return (false); + } + + CaseFile *caseFile(CaseFile::Find(PoolGUID(), VdevGUID())); + if (caseFile != NULL) { + syslog(LOG_INFO, "Evaluating existing case file\n"); + caseFile->ReEvaluate(*this); + return (false); + } + + /* Skip events that can't be handled. */ + Guid poolGUID(PoolGUID()); + /* If there are no replicas for a pool, then it's not manageable. */ + if (Value("class").find("fs.zfs.vdev.no_replicas") == 0) { + stringstream msg; + msg << "No replicas available for pool " << poolGUID; + msg << ", ignoring"; + syslog(LOG_INFO, "%s", msg.str().c_str()); + return (false); + } + + /* + * Create a case file for this vdev, and have it + * evaluate the event. + */ + ZpoolList zpl(ZpoolList::ZpoolByGUID, &poolGUID); + if (zpl.empty()) { + stringstream msg; + int priority = LOG_INFO; + msg << "ZfsEvent::Process: Event for unknown pool "; + msg << poolGUID << " "; + msg << "queued"; + syslog(priority, "%s", msg.str().c_str()); + return (true); + } + + nvlist_t *vdevConfig = VdevIterator(zpl.front()).Find(VdevGUID()); + if (vdevConfig == NULL) { + stringstream msg; + int priority = LOG_INFO; + msg << "ZfsEvent::Process: Event for unknown vdev "; + msg << VdevGUID() << " "; + msg << "queued"; + syslog(priority, "%s", msg.str().c_str()); + return (true); + } + + Vdev vdev(zpl.front(), vdevConfig); + caseFile = &CaseFile::Create(vdev); + if ( caseFile->ReEvaluate(*this) == false) { + stringstream msg; + int priority = LOG_INFO; + msg << "ZfsEvent::Process: Unconsumed event for vdev("; + msg << zpool_get_name(zpl.front()) << ","; + msg << vdev.GUID() << ") "; + msg << "queued"; + syslog(priority, "%s", msg.str().c_str()); + return (true); + } + return (false); +} + +//- ZfsEvent Protected Methods ------------------------------------------------- +ZfsEvent::ZfsEvent(Event::Type type, NVPairMap &nvpairs, + const string &eventString) + : DevCtl::ZfsEvent(type, nvpairs, eventString) +{ +} + +ZfsEvent::ZfsEvent(const ZfsEvent &src) + : DevCtl::ZfsEvent(src) +{ +} + +void +ZfsEvent::ProcessPoolEvent() const +{ + bool degradedDevice(false); + + CaseFile *caseFile(CaseFile::Find(PoolGUID(), VdevGUID())); + if (caseFile != NULL) { + if (caseFile->VdevState() != VDEV_STATE_UNKNOWN + && caseFile->VdevState() < VDEV_STATE_HEALTHY) + degradedDevice = true; + + caseFile->ReEvaluate(*this); + } + + if (Value("type") == "misc.fs.zfs.vdev_remove" + && degradedDevice == false) { + /* See if any other cases can make use of this device. */ + ZfsDaemon::RequestSystemRescan(); + } +} diff --git a/cddl/sbin/zfsd/zfsd_event.h b/cddl/sbin/zfsd/zfsd_event.h new file mode 100644 index 00000000000..dfd405a583f --- /dev/null +++ b/cddl/sbin/zfsd/zfsd_event.h @@ -0,0 +1,146 @@ +/*- + * Copyright (c) 2011 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + * + * $FreeBSD$ + */ + +/** + * \file dev_ctl_event.h + * + * \brief Class hierarchy used to express events received via + * the devctl API. + * + * Header requirements: + * #include + * #include + * #include + * + * #include + * #include + */ + +#ifndef _ZFSD_EVENT_H_ +#define _ZFSD_EVENT_H_ + +/*============================ Namespace Control =============================*/ +using std::string; + +/*=========================== Forward Declarations ===========================*/ +struct zpool_handle; +typedef struct zpool_handle zpool_handle_t; + +struct nvlist; +typedef struct nvlist nvlist_t; + +/*============================= Class Definitions ============================*/ +/*-------------------------------- DevfsEvent --------------------------------*/ +class DevfsEvent : public DevCtl::DevfsEvent +{ +public: + /** Specialized DevCtlEvent object factory for Devfs events. */ + static BuildMethod DevfsEventBuilder; + + virtual DevCtl::Event *DeepCopy() const; + + /** + * Interpret and perform any actions necessary to + * consume the event. + * \return True if this event should be queued for later reevaluation + */ + virtual bool Process() const; + +protected: + /** + * \brief Read and return label information for a device. + * + * \param devFd The device from which to read ZFS label information. + * \param inUse The device is part of an active or potentially + * active configuration. + * \param degraded The device label indicates the vdev is not healthy. + * + * \return If label information is available, an nvlist describing + * the vdev configuraiton found on the device specified by + * devFd. Otherwise NULL. + */ + static nvlist_t *ReadLabel(int devFd, bool &inUse, bool °raded); + + /** + * Attempt to match the ZFS labeled device at devPath with an active + * CaseFile for a missing vdev. If a CaseFile is found, attempt + * to re-integrate the device with its pool. + * + * \param devPath The devfs path to the potential leaf vdev. + * \param physPath The physical path string reported by the device + * at devPath. + * \param devConfig The ZFS label information found on the device + * at devPath. + * + * \return true if the event that caused the online action can + * be considered consumed. + */ + static bool OnlineByLabel(const string &devPath, + const string& physPath, + nvlist_t *devConfig); + + /** DeepCopy Constructor. */ + DevfsEvent(const DevfsEvent &src); + + /** Constructor */ + DevfsEvent(Type, DevCtl::NVPairMap &, const string &); +}; + +/*--------------------------------- ZfsEvent ---------------------------------*/ +class ZfsEvent : public DevCtl::ZfsEvent +{ +public: + /** Specialized DevCtlEvent object factory for ZFS events. */ + static BuildMethod ZfsEventBuilder; + + virtual DevCtl::Event *DeepCopy() const; + + /** + * Interpret and perform any actions necessary to + * consume the event. + * \return True if this event should be queued for later reevaluation + */ + virtual bool Process() const; + +protected: + /** DeepCopy Constructor. */ + ZfsEvent(const ZfsEvent &src); + + /** Constructor */ + ZfsEvent(Type, DevCtl::NVPairMap &, const string &); + + virtual void ProcessPoolEvent() const; +}; + +#endif /*_ZFSD_EVENT_H_ */ diff --git a/cddl/sbin/zfsd/zfsd_exception.cc b/cddl/sbin/zfsd/zfsd_exception.cc index 243cafa30a9..1aff8a3576b 100644 --- a/cddl/sbin/zfsd/zfsd_exception.cc +++ b/cddl/sbin/zfsd/zfsd_exception.cc @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2011 Spectra Logic Corporation + * Copyright (c) 2011, 2012, 2013 Spectra Logic Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,15 +35,23 @@ * * Implementation of the ZfsdException class. */ -#include +#include +#include + #include -#include +#include +#include + +#include +#include + #include #include "vdev.h" #include "zfsd_exception.h" +__FBSDID("$FreeBSD$"); /*============================ Namespace Control =============================*/ using std::endl; using std::string; @@ -51,17 +59,9 @@ using std::stringstream; /*=========================== Class Implementations ==========================*/ /*------------------------------- ZfsdException ------------------------------*/ -void -ZfsdException::FormatLog(const char *fmt, va_list ap) -{ - char buf[256]; - - vsnprintf(buf, sizeof(buf), fmt, ap); - m_log = buf; -} - ZfsdException::ZfsdException(const char *fmt, ...) - : m_poolConfig(NULL), + : DevCtl::Exception(), + m_poolConfig(NULL), m_vdevConfig(NULL) { va_list ap; @@ -72,7 +72,8 @@ ZfsdException::ZfsdException(const char *fmt, ...) } ZfsdException::ZfsdException(zpool_handle_t *pool, const char *fmt, ...) - : m_poolConfig(zpool_get_config(pool, NULL)), + : DevCtl::Exception(), + m_poolConfig(zpool_get_config(pool, NULL)), m_vdevConfig(NULL) { va_list ap; @@ -83,7 +84,8 @@ ZfsdException::ZfsdException(zpool_handle_t *pool, const char *fmt, ...) } ZfsdException::ZfsdException(nvlist_t *poolConfig, const char *fmt, ...) - : m_poolConfig(poolConfig), + : DevCtl::Exception(), + m_poolConfig(poolConfig), m_vdevConfig(NULL) { va_list ap; diff --git a/cddl/sbin/zfsd/zfsd_exception.h b/cddl/sbin/zfsd/zfsd_exception.h index e28ea2eb824..412836f5e38 100644 --- a/cddl/sbin/zfsd/zfsd_exception.h +++ b/cddl/sbin/zfsd/zfsd_exception.h @@ -35,12 +35,15 @@ * * Definition of the ZfsdException class hierarchy. All exceptions * explicitly thrown by Zfsd are defined here. + * + * Header requirements: + * #include + * + * #include */ #ifndef _ZFSD_EXCEPTION_H_ #define _ZFSD_EXCEPTION_H_ -#include - /*=========================== Forward Declarations ===========================*/ struct zpool_handle; typedef struct zpool_handle zpool_handle_t; @@ -53,7 +56,7 @@ typedef struct nvlist nvlist_t; /** * \brief Class allowing unified reporting/logging of exceptional events. */ -class ZfsdException +class ZfsdException : public DevCtl::Exception { public: /** @@ -92,31 +95,13 @@ public: */ ZfsdException(nvlist_t *poolConfig, const char *, ...); - /** - * \brief Augment/Modify a ZfsdException's string data. - */ - std::string& GetString(); - /** * \brief Emit exception data to syslog(3). */ - void Log() const; + virtual void Log() const; private: - /** - * \brief Convert exception string data and arguments provided - * in ZfsdException constructors into a linear string. - */ - void FormatLog(const char *fmt, va_list ap); - nvlist_t *m_poolConfig; nvlist_t *m_vdevConfig; - std::string m_log; }; -inline std::string & -ZfsdException::GetString() -{ - return (m_log); -} - #endif /* _ZFSD_EXCEPTION_H_ */ diff --git a/cddl/sbin/zfsd/zfsd_main.cc b/cddl/sbin/zfsd/zfsd_main.cc index 0fdcc6a28d5..62d154a29b9 100644 --- a/cddl/sbin/zfsd/zfsd_main.cc +++ b/cddl/sbin/zfsd/zfsd_main.cc @@ -37,12 +37,26 @@ * */ +#include + #include #include #include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "vdev_iterator.h" #include "zfsd.h" +__FBSDID("$FreeBSD$"); /*=============================== Program Main ===============================*/ static void diff --git a/cddl/sbin/zfsd/zpool_list.cc b/cddl/sbin/zfsd/zpool_list.cc index 0b9cd9819fc..7529770a71b 100644 --- a/cddl/sbin/zfsd/zpool_list.cc +++ b/cddl/sbin/zfsd/zpool_list.cc @@ -35,10 +35,31 @@ * * Implementation of the ZpoolList class. */ +#include +#include + +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + #include "vdev.h" +#include "vdev_iterator.h" #include "zpool_list.h" #include "zfsd.h" +/*============================ Namespace Control =============================*/ +using DevCtl::Guid; + /*=========================== Class Implementations ==========================*/ /*--------------------------------- ZpoolList --------------------------------*/ bool diff --git a/cddl/sbin/zfsd/zpool_list.h b/cddl/sbin/zfsd/zpool_list.h index ec2948b5d48..0a97c3b94ee 100644 --- a/cddl/sbin/zfsd/zpool_list.h +++ b/cddl/sbin/zfsd/zpool_list.h @@ -35,20 +35,25 @@ * * ZpoolList class definition. ZpoolList is a standard container * allowing filtering and iteration of imported ZFS pool information. + * + * Header requirements: + * + * #include + * #include */ #ifndef _ZPOOL_LIST_H_ #define _ZPOOL_LIST_H_ -#include -#include - -#include -#include - /*============================ Namespace Control =============================*/ using std::string; /*=========================== Forward Declarations ===========================*/ +struct zpool_handle; +typedef struct zpool_handle zpool_handle_t; + +struct nvlist; +typedef struct nvlist nvlist_t; + class Vdev; /*============================= Class Definitions ============================*/ diff --git a/etc/mtree/BSD.include.dist b/etc/mtree/BSD.include.dist index 76cdf9488d4..911b95fabfc 100644 --- a/etc/mtree/BSD.include.dist +++ b/etc/mtree/BSD.include.dist @@ -159,6 +159,8 @@ wi .. .. + devctl + .. edit readline .. diff --git a/lib/Makefile b/lib/Makefile index 9ac89450599..2d845cb3c91 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -69,6 +69,7 @@ SUBDIR= ${SUBDIR_ORDERED} \ libcalendar \ libcam \ libcompat \ + libdevctl \ libdevinfo \ libdevstat \ libdwarf \ diff --git a/lib/libdevctl/Makefile b/lib/libdevctl/Makefile new file mode 100644 index 00000000000..250538f1ae2 --- /dev/null +++ b/lib/libdevctl/Makefile @@ -0,0 +1,23 @@ +# $FreeBSD: stable/9/lib/libdevinfo/Makefile 201381 2010-01-02 09:58:07Z ed $ + +LIB= devctl +INCS= consumer.h \ + event.h \ + event_buffer.h \ + event_factory.h \ + exception.h \ + guid.h \ + reader.h +SRCS= consumer.cc \ + event.cc \ + event_buffer.cc \ + event_factory.cc \ + exception.cc \ + guid.cc \ + reader.cc + +INCSDIR= ${INCLUDEDIR}/devctl + +WARNS?= 3 + +.include diff --git a/lib/libdevctl/consumer.cc b/lib/libdevctl/consumer.cc new file mode 100644 index 00000000000..d2d5c0c6f71 --- /dev/null +++ b/lib/libdevctl/consumer.cc @@ -0,0 +1,246 @@ +/*- + * Copyright (c) 2011, 2012, 2013 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + */ + +/** + * \file consumer.cc + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "guid.h" +#include "event.h" +#include "event_buffer.h" +#include "event_factory.h" +#include "exception.h" +#include "reader.h" + +#include "consumer.h" + +__FBSDID("$FreeBSD$"); + +/*================================== Macros ==================================*/ +#define NUM_ELEMENTS(x) (sizeof(x) / sizeof(*x)) + +/*============================ Namespace Control =============================*/ +using std::string; +namespace DevCtl +{ + +/*============================= Class Definitions ============================*/ +/*----------------------------- DevCtl::Consumer -----------------------------*/ +//- Consumer Static Private Data ----------------------------------------------- +const char Consumer::s_devdSockPath[] = "/var/run/devd.pipe"; + +//- Consumer Public Methods ---------------------------------------------------- +Consumer::Consumer(Event::BuildMethod *defBuilder, + EventFactory::Record *regEntries, + size_t numEntries) + : m_devdSockFD(-1), + m_reader(NULL), + m_eventBuffer(NULL), + m_eventFactory(defBuilder), + m_replayingEvents(false) +{ + m_eventFactory.UpdateRegistry(regEntries, numEntries); +} + +Consumer::~Consumer() +{ + DisconnectFromDevd(); + delete m_reader; + m_reader = NULL; +} + +bool +Consumer::ConnectToDevd() +{ + struct sockaddr_un devdAddr; + int sLen; + int result; + + syslog(LOG_INFO, "Connecting to devd"); + if (m_devdSockFD != -1) { + /* Already connected. */ + return (true); + } + + memset(&devdAddr, 0, sizeof(devdAddr)); + devdAddr.sun_family= AF_UNIX; + strlcpy(devdAddr.sun_path, s_devdSockPath, sizeof(devdAddr.sun_path)); + sLen = SUN_LEN(&devdAddr); + + m_devdSockFD = socket(AF_UNIX, SOCK_STREAM, 0); + if (m_devdSockFD == -1) + err(1, "Unable to create socket"); + result = connect(m_devdSockFD, + reinterpret_cast(&devdAddr), + sLen); + if (result == -1) { + syslog(LOG_INFO, "Unable to connect to devd"); + return (false); + } + + /* Connect the stream to the file descriptor */ + m_reader = new FDReader(m_devdSockFD); + m_eventBuffer = new EventBuffer(*m_reader); + syslog(LOG_INFO, "Connection to devd successful"); + return (true); +} + +void +Consumer::DisconnectFromDevd() +{ + if (m_devdSockFD != -1) + syslog(LOG_INFO, "Disconnecting from devd."); + + delete m_eventBuffer; + m_eventBuffer = NULL; + delete m_reader; + m_reader = NULL; + close(m_devdSockFD); + m_devdSockFD = -1; +} + +void +Consumer::ReplayUnconsumedEvents(bool discardUnconsumed) +{ + EventList::iterator event(m_unconsumedEvents.begin()); + bool replayed_any = (event != m_unconsumedEvents.end()); + + m_replayingEvents = true; + if (replayed_any) + syslog(LOG_INFO, "Started replaying unconsumed events"); + while (event != m_unconsumedEvents.end()) { + bool consumed((*event)->Process()); + if (consumed || discardUnconsumed) { + delete *event; + event = m_unconsumedEvents.erase(event); + } else { + event++; + } + } + if (replayed_any) + syslog(LOG_INFO, "Finished replaying unconsumed events"); + m_replayingEvents = false; +} + +bool +Consumer::SaveEvent(const Event &event) +{ + if (m_replayingEvents) + return (false); + m_unconsumedEvents.push_back(event.DeepCopy()); + return (true); +} + +Event * +Consumer::NextEvent(EventBuffer *eventBuffer) +{ + if (!Connected()) + return(NULL); + + if (eventBuffer == NULL) + eventBuffer = m_eventBuffer; + + Event *event(NULL); + try { + string evString; + + if (eventBuffer->ExtractEvent(evString)) + event = Event::CreateEvent(m_eventFactory, evString); + } catch (const Exception &exp) { + exp.Log(); + DisconnectFromDevd(); + } + return (event); +} + +/* Capture and process buffered events. */ +void +Consumer::ProcessEvents(EventBuffer *eventBuffer) +{ + Event *event; + while ((event = NextEvent(eventBuffer)) != NULL) { + if (event->Process()) + SaveEvent(*event); + delete event; + } +} + +void +Consumer::FlushEvents() +{ + char discardBuf[256]; + + while (m_reader->in_avail()) + m_reader->read(discardBuf, sizeof(discardBuf)); +} + +bool +Consumer::EventsPending() +{ + struct pollfd fds[1]; + int result; + + do { + fds->fd = m_devdSockFD; + fds->events = POLLIN; + fds->revents = 0; + result = poll(fds, NUM_ELEMENTS(fds), /*timeout*/0); + } while (result == -1 && errno == EINTR); + + if (result == -1) + err(1, "Polling for devd events failed"); + + if ((fds->revents & POLLERR) != 0) + throw Exception("ZfsdDaemon:EventsPending(): " + "POLLERR detected on devd socket."); + + if ((fds->revents & POLLHUP) != 0) + throw Exception("Consumer::EventsPending(): " + "POLLHUP detected on devd socket."); + + return ((fds->revents & POLLIN) != 0); +} + +} // namespace DevCtl diff --git a/lib/libdevctl/consumer.h b/lib/libdevctl/consumer.h new file mode 100644 index 00000000000..f8242425a3a --- /dev/null +++ b/lib/libdevctl/consumer.h @@ -0,0 +1,171 @@ +/*- + * Copyright (c) 2011, 2012, 2013 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + * + * $FreeBSD$ + */ + +/** + * \file devctl_consumer.h + */ +#ifndef _DEVCTL_CONSUMER_H_ +#define _DEVCTL_CONSUMER_H_ + +/*============================ Namespace Control =============================*/ +namespace DevCtl +{ + +/*=========================== Forward Declarations ===========================*/ +class Event; +class EventBuffer; +class FDReader; + +/*============================ Class Declarations ============================*/ +/*----------------------------- DevCtl::Consumer -----------------------------*/ + +/** + */ +class Consumer +{ +public: + Consumer(Event::BuildMethod *defBuilder = NULL, + EventFactory::Record *regEntries = NULL, + size_t numEntries = 0); + virtual ~Consumer(); + + bool Connected() const; + + /** + * Return file descriptor useful for client's wishing to poll(2) + * for new events. + */ + int GetPollFd(); + + /** + * Queue an event for deferred processing or replay. + */ + bool SaveEvent(const Event &event); + + /** + * Reprocess any events saved via the SaveEvent() facility. + * + * \param discardUnconsumed If true, events that are not conumed + * during replay are discarded. + */ + void ReplayUnconsumedEvents(bool discardUnconsumed); + + /** Return an event, if available, from the provided EventBuffer. */ + Event *NextEvent(EventBuffer *eventBuffer = NULL); + + /** + * Extract events from the provided eventBuffer and invoke + * each event's Process method. + */ + void ProcessEvents(EventBuffer *eventBuffer = NULL); + + /** Discard all data pending in m_devdSockFD. */ + void FlushEvents(); + + /** + * Test for data pending in m_devdSockFD + * + * \return True if data is pending. Otherwise false. + */ + bool EventsPending(); + + /** + * Open a connection to devd's unix domain socket. + * + * \return True if the connection attempt is successsful. Otherwise + * false. + */ + bool ConnectToDevd(); + + /** + * Close a connection (if any) to devd's unix domain socket. + */ + void DisconnectFromDevd(); + + EventFactory GetFactory(); + +protected: + static const char s_devdSockPath[]; + + /** + * File descriptor representing the unix domain socket + * connection with devd. + */ + int m_devdSockFD; + + /** + * Reader tied to the devd socket. + */ + FDReader *m_reader; + + /** + * Default EventBuffer connected to m_reader. + */ + EventBuffer *m_eventBuffer; + + EventFactory m_eventFactory; + + /** Queued events for replay. */ + EventList m_unconsumedEvents; + + /** + * Flag controlling whether events can be queued. This boolean + * is set during event replay to ensure that previosuly deferred + * events are not requeued and thus retained forever. + */ + bool m_replayingEvents; +}; + +//- Consumer Const Public Inline Methods --------------------------------------- +inline bool +Consumer::Connected() const +{ + return (m_devdSockFD != -1); +} + +//- Consumer Public Inline Methods --------------------------------------------- +inline int +Consumer::GetPollFd() +{ + return (m_devdSockFD); +} + +inline EventFactory +Consumer::GetFactory() +{ + return (m_eventFactory); +} + +} // namespace DevCtl +#endif /* _DEVCTL_CONSUMER_H_ */ diff --git a/cddl/sbin/zfsd/dev_ctl_event.cc b/lib/libdevctl/event.cc similarity index 50% rename from cddl/sbin/zfsd/dev_ctl_event.cc rename to lib/libdevctl/event.cc index 57d44721bd9..20230944b9a 100644 --- a/cddl/sbin/zfsd/dev_ctl_event.cc +++ b/lib/libdevctl/event.cc @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2011 Spectra Logic Corporation + * Copyright (c) 2011, 2012, 2013 Spectra Logic Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,119 +31,64 @@ */ /** - * \file dev_ctl_event.cc + * \file event.cc * * Implementation of the class hierarchy used to express events * received via the devctl API. */ -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include + +#include +#include #include -#include "case_file.h" -#include "dev_ctl_event.h" -#include "vdev.h" -#include "zfsd.h" -#include "zfsd_exception.h" -#include "zpool_list.h" +#include +#include +#include +#include +#include + +#include "guid.h" +#include "event.h" +#include "event_factory.h" +#include "exception.h" + +__FBSDID("$FreeBSD$"); + +/*================================== Macros ==================================*/ +#define NUM_ELEMENTS(x) (sizeof(x) / sizeof(*x)) /*============================ Namespace Control =============================*/ -using std::cerr; using std::cout; using std::endl; +using std::string; using std::stringstream; +namespace DevCtl +{ + /*=========================== Class Implementations ==========================*/ -/*------------------------------ ParseException ------------------------------*/ -//- ParseException Public Methods ---------------------------------------------- -string -ParseException::ToString() const +/*----------------------------------- Event ----------------------------------*/ +//- Event Static Protected Data ------------------------------------------------ +const string Event::s_theEmptyString; + +Event::EventTypeRecord Event::s_typeTable[] = { - stringstream result; - - result << "Parsing "; - - switch (Type()) { - case INVALID_FORMAT: - result << "invalid format "; - break; - case DISCARDED_EVENT_TYPE: - result << "discarded event "; - break; - case UNKNOWN_EVENT_TYPE: - result << "unknown event "; - break; - default: - break; - } - result << "exception on buffer: \'"; - if (GetOffset() == 0) { - result << m_parsedBuffer << '\'' << endl; - } else { - string markedBuffer(m_parsedBuffer); - - markedBuffer.insert(GetOffset(), ""); - result << markedBuffer << '\'' << endl; - } - - return (result.str()); -} - -void -ParseException::Log() const -{ - int priority(LOG_ERR); - - if (Type() == DISCARDED_EVENT_TYPE) - priority = LOG_INFO; - - syslog(priority, "%s", ToString().c_str()); -} - -/*-------------------------------- DevCtlEvent -------------------------------*/ -//- DevCtlEvent Static Protected Data ------------------------------------------ -const string DevCtlEvent::s_theEmptyString; - -DevCtlEvent::EventTypeRecord DevCtlEvent::s_typeTable[] = -{ - { DevCtlEvent::NOTIFY, "Notify" }, - { DevCtlEvent::NOMATCH, "No Driver Match" }, - { DevCtlEvent::ATTACH, "Attach" }, - { DevCtlEvent::DETACH, "Detach" } + { Event::NOTIFY, "Notify" }, + { Event::NOMATCH, "No Driver Match" }, + { Event::ATTACH, "Attach" }, + { Event::DETACH, "Detach" } }; -DevCtlEvent::EventFactoryRecord DevCtlEvent::s_factoryTable[] = -{ - { DevCtlEvent::NOTIFY, "DEVFS", &DevfsEvent::DevfsEventFactory }, - { DevCtlEvent::NOTIFY, "ZFS", &ZfsEvent::ZfsEventFactory} -}; - -DevCtlEvent::EventFactoryRegistry DevCtlEvent::s_factoryRegistry; - -//- DevCtlEvent Static Public Methods ------------------------------------------ -void -DevCtlEvent::Init() -{ - EventFactoryRecord *rec(s_factoryTable); - EventFactoryRecord *lastRec(s_factoryTable - + NUM_ELEMENTS(s_factoryTable) - 1); - - for (; rec <= lastRec; rec++) { - EventFactoryKey key(rec->m_type, rec->m_subsystem); - - s_factoryRegistry[key] = rec->m_method; - } -} - -DevCtlEvent * -DevCtlEvent::CreateEvent(const string &eventString) +//- Event Static Public Methods ------------------------------------------------ +Event * +Event::CreateEvent(const EventFactory &factory, const string &eventString) { NVPairMap &nvpairs(*new NVPairMap); - Type type(static_cast(eventString[0])); + Type type(static_cast(eventString[0])); try { ParseEventString(type, eventString, nvpairs); @@ -161,18 +106,11 @@ DevCtlEvent::CreateEvent(const string &eventString) if (system_item == nvpairs.end()) nvpairs["system"] = "none"; - EventFactoryKey key(type, nvpairs["system"]); - EventFactoryRegistry::iterator foundMethod(s_factoryRegistry.find(key)); - if (foundMethod == s_factoryRegistry.end()) { - syslog(LOG_INFO, "DevCtlEvent::CreateEvent: unhandled event %s", - eventString.c_str()); - return (NULL); - } - return ((foundMethod->second)(type, nvpairs, eventString)); + return (factory.Build(type, nvpairs, eventString)); } const char * -DevCtlEvent::TypeToString(DevCtlEvent::Type type) +Event::TypeToString(Event::Type type) { EventTypeRecord *rec(s_typeTable); EventTypeRecord *lastRec(s_typeTable + NUM_ELEMENTS(s_typeTable) - 1); @@ -184,9 +122,9 @@ DevCtlEvent::TypeToString(DevCtlEvent::Type type) return ("Unknown"); } -//- DevCtlEvent Public Methods ------------------------------------------------- +//- Event Public Methods ------------------------------------------------------- const string & -DevCtlEvent::Value(const string &varName) const +Event::Value(const string &varName) const { NVPairMap::const_iterator item(m_nvPairs.find(varName)); if (item == m_nvPairs.end()) @@ -196,13 +134,13 @@ DevCtlEvent::Value(const string &varName) const } bool -DevCtlEvent::Contains(const string &varName) const +Event::Contains(const string &varName) const { return (m_nvPairs.find(varName) != m_nvPairs.end()); } string -DevCtlEvent::ToString() const +Event::ToString() const { stringstream result; @@ -231,37 +169,37 @@ DevCtlEvent::ToString() const } void -DevCtlEvent::Print() const +Event::Print() const { cout << ToString() << std::flush; } void -DevCtlEvent::Log(int priority) const +Event::Log(int priority) const { syslog(priority, "%s", ToString().c_str()); } -//- DevCtlEvent Virtual Public Methods ----------------------------------------- -DevCtlEvent::~DevCtlEvent() +//- Event Virtual Public Methods ----------------------------------------------- +Event::~Event() { delete &m_nvPairs; } bool -DevCtlEvent::Process() const +Event::Process() const { return (false); } timeval -DevCtlEvent::GetTimestamp() const +Event::GetTimestamp() const { timeval tv_timestamp; struct tm tm_timestamp; if (!Contains("timestamp")) { - throw ZfsdException("Event contains no timestamp: %s", + throw Exception("Event contains no timestamp: %s", m_eventString.c_str()); } strptime(Value(string("timestamp")).c_str(), "%s", &tm_timestamp); @@ -271,15 +209,15 @@ DevCtlEvent::GetTimestamp() const } -//- DevCtlEvent Protected Methods ---------------------------------------------- -DevCtlEvent::DevCtlEvent(Type type, NVPairMap &map, const string &eventString) +//- Event Protected Methods ---------------------------------------------------- +Event::Event(Type type, NVPairMap &map, const string &eventString) : m_type(type), m_nvPairs(map), m_eventString(eventString) { } -DevCtlEvent::DevCtlEvent(const DevCtlEvent &src) +Event::Event(const Event &src) : m_type(src.m_type), m_nvPairs(*new NVPairMap(src.m_nvPairs)), m_eventString(src.m_eventString) @@ -287,7 +225,7 @@ DevCtlEvent::DevCtlEvent(const DevCtlEvent &src) } void -DevCtlEvent::ParseEventString(DevCtlEvent::Type type, +Event::ParseEventString(Event::Type type, const string &eventString, NVPairMap& nvpairs) { @@ -375,8 +313,8 @@ DevCtlEvent::ParseEventString(DevCtlEvent::Type type, /*-------------------------------- DevfsEvent --------------------------------*/ //- DevfsEvent Static Public Methods ------------------------------------------- -DevCtlEvent * -DevfsEvent::DevfsEventFactory(DevCtlEvent::Type type, NVPairMap &nvPairs, +Event * +DevfsEvent::DevfsEventBuilder(Event::Type type, NVPairMap &nvPairs, const string &eventString) { return (new DevfsEvent(type, nvPairs, eventString)); @@ -445,73 +383,8 @@ DevfsEvent::IsWholeDev(const string &devName) return (i == devName.end()); } -nvlist_t * -DevfsEvent::ReadLabel(int devFd, bool &inUse, bool °raded) -{ - pool_state_t poolState; - char *poolName; - boolean_t b_inuse; - - inUse = false; - degraded = false; - poolName = NULL; - if (zpool_in_use(g_zfsHandle, devFd, &poolState, - &poolName, &b_inuse) == 0) { - nvlist_t *devLabel; - - inUse = b_inuse == B_TRUE; - if (poolName != NULL) - free(poolName); - - if (zpool_read_label(devFd, &devLabel) != 0 - || devLabel == NULL) - return (NULL); - - try { - Vdev vdev(devLabel); - degraded = vdev.State() != VDEV_STATE_HEALTHY; - return (devLabel); - } catch (ZfsdException &exp) { - string devName = fdevname(devFd); - string devPath = _PATH_DEV + devName; - string context("DevfsEvent::ReadLabel: " + devPath + ": "); - - exp.GetString().insert(0, context); - exp.Log(); - } - } - return (NULL); -} - -bool -DevfsEvent::OnlineByLabel(const string &devPath, const string& physPath, - nvlist_t *devConfig) -{ - try { - /* - * A device with ZFS label information has been - * inserted. If it matches a device for which we - * have a case, see if we can solve that case. - */ - syslog(LOG_INFO, "Interrogating VDEV label for %s\n", - devPath.c_str()); - Vdev vdev(devConfig); - CaseFile *caseFile(CaseFile::Find(vdev.PoolGUID(), - vdev.GUID())); - if (caseFile != NULL) - return (caseFile->ReEvaluate(devPath, physPath, &vdev)); - - } catch (ZfsdException &exp) { - string context("DevfsEvent::OnlineByLabel: " + devPath + ": "); - - exp.GetString().insert(0, context); - exp.Log(); - } - return (false); -} - //- DevfsEvent Virtual Public Methods ------------------------------------------ -DevCtlEvent * +Event * DevfsEvent::DeepCopy() const { return (new DevfsEvent(*this)); @@ -520,18 +393,43 @@ DevfsEvent::DeepCopy() const bool DevfsEvent::Process() const { - /* - * We are only concerned with newly discovered - * devices that can be ZFS vdevs. - */ - string devName(Value("cdev")); - if (Value("type") != "CREATE" - || Value("subsystem") != "CDEV" - || !IsDiskDev(devName)) + return (true); +} + +//- DevfsEvent Public Methods -------------------------------------------------- +bool +DevfsEvent::IsDiskDev() const +{ + string devName; + + return (DevName(devName) && IsDiskDev(devName)); +} + +bool +DevfsEvent::IsWholeDev() const +{ + string devName; + + return (DevName(devName) && IsDiskDev() && IsWholeDev(devName)); +} + +bool +DevfsEvent::DevName(std::string &name) const +{ + if (Value("subsystem") != "CDEV") return (false); - /* Log the event since it is of interest. */ - Log(LOG_INFO); + name = Value("cdev"); + return (!name.empty()); +} + +bool +DevfsEvent::DevPath(std::string &path) const +{ + string devName; + + if (!DevName(devName)) + return (false); string devPath(_PATH_DEV + devName); int devFd(open(devPath.c_str(), O_RDONLY)); @@ -540,192 +438,76 @@ DevfsEvent::Process() const /* Normalize the device name in case the DEVFS event is for a link. */ devName = fdevname(devFd); - devPath = _PATH_DEV + devName; - - bool inUse; - bool degraded; - nvlist_t *devLabel(ReadLabel(devFd, inUse, degraded)); - - char physPath[MAXPATHLEN]; - physPath[0] = '\0'; - bool havePhysPath(ioctl(devFd, DIOCGPHYSPATH, physPath) == 0); + path = _PATH_DEV + devName; close(devFd); - if (inUse && devLabel != NULL) { - OnlineByLabel(devPath, physPath, devLabel); - } else if (degraded) { - syslog(LOG_INFO, "%s is marked degraded. Ignoring " - "as a replace by physical path candidate.\n", - devName.c_str()); - } else if (havePhysPath && IsWholeDev(devName)) { - syslog(LOG_INFO, "Searching for CaseFile by Physical Path\n"); - CaseFile *caseFile(CaseFile::Find(physPath)); - if (caseFile != NULL) { - syslog(LOG_INFO, - "Found CaseFile(%s:%s:%s) - ReEvaluating\n", - caseFile->PoolGUIDString().c_str(), - caseFile->VdevGUIDString().c_str(), - zpool_state_to_name(caseFile->VdevState(), - VDEV_AUX_NONE)); - caseFile->ReEvaluate(devPath, physPath, /*vdev*/NULL); - } - } - if (devLabel != NULL) - nvlist_free(devLabel); - return (false); + return (true); +} + +bool +DevfsEvent::PhysicalPath(std::string &path) const +{ + string devPath; + + if (!DevPath(devPath)) + return (false); + + int devFd(open(devPath.c_str(), O_RDONLY)); + if (devFd == -1) + return (false); + + char physPath[MAXPATHLEN]; + physPath[0] = '\0'; + bool result(ioctl(devFd, DIOCGPHYSPATH, physPath) == 0); + close(devFd); + if (result) + path = physPath; + return (result); } //- DevfsEvent Protected Methods ----------------------------------------------- -DevfsEvent::DevfsEvent(DevCtlEvent::Type type, NVPairMap &nvpairs, +DevfsEvent::DevfsEvent(Event::Type type, NVPairMap &nvpairs, const string &eventString) - : DevCtlEvent(type, nvpairs, eventString) + : Event(type, nvpairs, eventString) { } DevfsEvent::DevfsEvent(const DevfsEvent &src) - : DevCtlEvent(src) + : Event(src) { } /*--------------------------------- ZfsEvent ---------------------------------*/ //- ZfsEvent Static Public Methods --------------------------------------------- -DevCtlEvent * -ZfsEvent::ZfsEventFactory(DevCtlEvent::Type type, NVPairMap &nvpairs, +Event * +ZfsEvent::ZfsEventBuilder(Event::Type type, NVPairMap &nvpairs, const string &eventString) { return (new ZfsEvent(type, nvpairs, eventString)); } //- ZfsEvent Virtual Public Methods -------------------------------------------- -DevCtlEvent * +Event * ZfsEvent::DeepCopy() const { return (new ZfsEvent(*this)); } -bool -ZfsEvent::Process() const -{ - string logstr(""); - - if (!Contains("class") && !Contains("type")) { - syslog(LOG_ERR, - "ZfsEvent::Process: Missing class or type data."); - return (false); - } - - /* On config syncs, replay any queued events first. */ - if (Value("type").find("misc.fs.zfs.config_sync") == 0) { - ZfsDaemon::ReplayUnconsumedEvents(); - CaseFile::ReEvaluateByGuid(PoolGUID(), *this); - } - - Log(LOG_INFO); - - if (Value("type").find("misc.fs.zfs.") == 0) { - /* Configuration changes, resilver events, etc. */ - ProcessPoolEvent(); - return (false); - } - - if (!Contains("pool_guid") || !Contains("vdev_guid")) { - /* Only currently interested in Vdev related events. */ - return (false); - } - - CaseFile *caseFile(CaseFile::Find(PoolGUID(), VdevGUID())); - if (caseFile != NULL) { - syslog(LOG_INFO, "Evaluating existing case file\n"); - caseFile->ReEvaluate(*this); - return (false); - } - - /* Skip events that can't be handled. */ - Guid poolGUID(PoolGUID()); - /* If there are no replicas for a pool, then it's not manageable. */ - if (Value("class").find("fs.zfs.vdev.no_replicas") == 0) { - stringstream msg; - msg << "No replicas available for pool " << poolGUID; - msg << ", ignoring"; - syslog(LOG_INFO, "%s", msg.str().c_str()); - return (false); - } - - /* - * Create a case file for this vdev, and have it - * evaluate the event. - */ - ZpoolList zpl(ZpoolList::ZpoolByGUID, &poolGUID); - if (zpl.empty()) { - stringstream msg; - int priority = LOG_INFO; - msg << "ZfsEvent::Process: Event for unknown pool "; - msg << poolGUID << " "; - msg << "queued"; - syslog(priority, "%s", msg.str().c_str()); - return (true); - } - - nvlist_t *vdevConfig = VdevIterator(zpl.front()).Find(VdevGUID()); - if (vdevConfig == NULL) { - stringstream msg; - int priority = LOG_INFO; - msg << "ZfsEvent::Process: Event for unknown vdev "; - msg << VdevGUID() << " "; - msg << "queued"; - syslog(priority, "%s", msg.str().c_str()); - return (true); - } - - Vdev vdev(zpl.front(), vdevConfig); - caseFile = &CaseFile::Create(vdev); - if ( caseFile->ReEvaluate(*this) == false) { - stringstream msg; - int priority = LOG_INFO; - msg << "ZfsEvent::Process: Unconsumed event for vdev("; - msg << zpool_get_name(zpl.front()) << ","; - msg << vdev.GUID() << ") "; - msg << "queued"; - syslog(priority, "%s", msg.str().c_str()); - return (true); - } - return (false); -} - //- ZfsEvent Protected Methods ------------------------------------------------- -ZfsEvent::ZfsEvent(DevCtlEvent::Type type, NVPairMap &nvpairs, +ZfsEvent::ZfsEvent(Event::Type type, NVPairMap &nvpairs, const string &eventString) - : DevCtlEvent(type, nvpairs, eventString), + : Event(type, nvpairs, eventString), m_poolGUID(Guid(Value("pool_guid"))), m_vdevGUID(Guid(Value("vdev_guid"))) { } ZfsEvent::ZfsEvent(const ZfsEvent &src) - : DevCtlEvent(src), + : Event(src), m_poolGUID(src.m_poolGUID), m_vdevGUID(src.m_vdevGUID) { } -void -ZfsEvent::ProcessPoolEvent() const -{ - bool degradedDevice(false); - - CaseFile *caseFile(CaseFile::Find(PoolGUID(), VdevGUID())); - if (caseFile != NULL) { - if (caseFile->VdevState() != VDEV_STATE_UNKNOWN - && caseFile->VdevState() < VDEV_STATE_HEALTHY) - degradedDevice = true; - - caseFile->ReEvaluate(*this); - } - - if (Value("type") == "misc.fs.zfs.vdev_remove" - && degradedDevice == false) { - /* See if any other cases can make use of this device. */ - ZfsDaemon::RequestSystemRescan(); - } -} +} // namespace DevCtl diff --git a/cddl/sbin/zfsd/dev_ctl_event.h b/lib/libdevctl/event.h similarity index 50% rename from cddl/sbin/zfsd/dev_ctl_event.h rename to lib/libdevctl/event.h index 51f81dddbfe..e7b92300fe3 100644 --- a/cddl/sbin/zfsd/dev_ctl_event.h +++ b/lib/libdevctl/event.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2011 Spectra Logic Corporation + * Copyright (c) 2011, 2012, 2013 Spectra Logic Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -33,135 +33,30 @@ */ /** - * \file dev_ctl_event.h + * \file devctl_event.h * * \brief Class hierarchy used to express events received via * the devctl API. */ -#ifndef _DEV_CTL_EVENT_H_ -#define _DEV_CTL_EVENT_H_ - -#include -#include -#include - -#include -#include - -#include "vdev.h" +#ifndef _DEVCTL_EVENT_H_ +#define _DEVCTL_EVENT_H_ /*============================ Namespace Control =============================*/ -using std::map; -using std::pair; -using std::string; +namespace DevCtl +{ + +/*=========================== Forward Declarations ===========================*/ +class EventFactory; /*============================= Class Definitions ============================*/ -/*------------------------------ ParseException ------------------------------*/ -/** - * Exception thrown when an event string is not converted to an actionable - * DevCtlEvent object. - */ -class ParseException -{ -public: - enum Type - { - /** Improperly formatted event string encounterd. */ - INVALID_FORMAT, - - /** No handlers for this event type. */ - DISCARDED_EVENT_TYPE, - - /** Unhandled event type. */ - UNKNOWN_EVENT_TYPE - }; - - /** - * Constructor - * - * \param type The type of this exception. - * \param parsedBuffer The parsing buffer active at the time of - * the exception. - * \param offset The location in the parse buffer where this - * exception occurred. - */ - ParseException(Type type, const string &parsedBuffer, - size_t offset = 0); - - /** - * Accessor - * - * \return The classification for this exception. - */ - Type GetType() const; - - /** - * Accessor - * - * \return The offset into the event string where this exception - * occurred. - */ - size_t GetOffset() const; - - /** - * Convert an exception into a human readable string. - * - * \param parsedBuffer The event buffer that caused the failure. - */ - string ToString() const; - - /** - * Log exception data to syslog. - * - * \param parsedBuffer The event buffer that caused the failure. - */ - void Log() const; - -private: - /** The type of this exception. */ - Type m_type; - - /** The parsing buffer that was active at the time of the exception. */ - const string m_parsedBuffer; - - /** - * The offset into the event string buffer from where this - * exception was triggered. - */ - size_t m_offset; -}; - -//- ParseException Inline Public Methods --------------------------------------- -inline -ParseException::ParseException(Type type, const string &parsedBuffer, - size_t offset) - : m_type(type), - m_parsedBuffer(parsedBuffer), - m_offset(offset) -{ -} - -//- ParseException Inline Const Public Methods --------------------------------- -inline ParseException::Type -ParseException::GetType() const -{ - return (m_type); -} - -inline size_t -ParseException::GetOffset() const -{ - return (m_offset); -} - /*-------------------------------- NVPairMap ---------------------------------*/ /** * NVPairMap is a specialization of the standard map STL container. */ -typedef map NVPairMap; +typedef std::map NVPairMap; -/*-------------------------------- DevCtlEvent -------------------------------*/ +/*----------------------------------- Event ----------------------------------*/ /** * \brief Container for the name => value pairs that comprise the content of * a device control event. @@ -172,8 +67,10 @@ typedef map NVPairMap; * example, ATTACH and DETACH events have "device-name" and "parent" * name => value pairs added. */ -class DevCtlEvent +class Event { + friend class EventFactory; + public: /** Event type */ enum Type { @@ -190,19 +87,15 @@ public: DETACH = '-' }; - /** Prepare the DevCtlEvent class for processing of events. */ - static void Init(); - /** - * Factory method for creating events. - * - * \param buffer String representing a single event received - * from devd. - * - * \note Init() must be invoked prior to the first call to - * CreateEvent(). + * Factory method type to construct an Event given + * the type of event and an NVPairMap populated from + * the event string received from devd. */ - static DevCtlEvent *CreateEvent(const string &buffer); + typedef Event* (BuildMethod)(Type, NVPairMap &, const std::string &); + + static Event *CreateEvent(const EventFactory &factory, + const std::string &eventString); /** * Provide a user friendly string representation of an @@ -222,7 +115,7 @@ public: * \return true if the specified key is available in this * event, otherwise false. */ - bool Contains(const string &name) const; + bool Contains(const std::string &name) const; /** * \param key The name of the key for which to retrieve its @@ -234,21 +127,21 @@ public: * \note For key's with no registered value, the empty string * is returned. */ - const string &Value(const string &key) const; + const std::string &Value(const std::string &key) const; /** * Get the type of this event instance. * * \return The type of this event instance. */ - Type GetType() const; + Type GetType() const; /** * Get the orginal DevCtl event string for this event. * * \return The DevCtl event string. */ - const string &GetEventString() const; + const std::string &GetEventString() const; /** * Convert the event instance into a string suitable for @@ -256,12 +149,12 @@ public: * * \return A string of formatted event data. */ - string ToString() const; + std::string ToString() const; /** * Pretty-print this event instance to cout. */ - void Print() const; + void Print() const; /** * Pretty-print this event instance to syslog. @@ -269,28 +162,34 @@ public: * \param priority The logging priority/facility. * See syslog(3). */ - void Log(int priority) const; + void Log(int priority) const; /** * Create and return a fully independent clone * of this event. */ - virtual DevCtlEvent *DeepCopy() const = 0; + virtual Event *DeepCopy() const = 0; /** Destructor */ - virtual ~DevCtlEvent(); + virtual ~Event(); /** * Interpret and perform any actions necessary to * consume the event. + * * \return True if this event should be queued for later reevaluation */ - virtual bool Process() const; + virtual bool Process() const; /** * Get the time that the event was created */ - timeval GetTimestamp() const; + timeval GetTimestamp() const; + + /** + * Access all parsed key => value pairs. + */ + const NVPairMap &GetMap() const; protected: /** Table entries used to map a type to a user friendly string. */ @@ -300,52 +199,22 @@ protected: const char *m_typeName; }; - /** - * Event creation handlers are matched by event type and a - * string representing the system emitting the event. - */ - typedef pair EventFactoryKey; - - /** - * Event factory methods construct a DevCtlEvent given - * the type of event and an NVPairMap populated from - * the event string received from devd. - */ - typedef DevCtlEvent* (EventFactory)(Type, NVPairMap &, const string &); - - /** Map type for Factory method lookups. */ - typedef map EventFactoryRegistry; - - /** Table record of factory methods to add to our registry. */ - struct EventFactoryRecord - { - Type m_type; - const char *m_subsystem; - EventFactory *m_method; - }; - /** * Constructor * * \param type The type of event to create. */ - DevCtlEvent(Type type, NVPairMap &map, const string &eventString); + Event(Type type, NVPairMap &map, const std::string &eventString); /** Deep copy constructor. */ - DevCtlEvent(const DevCtlEvent &src); + Event(const Event &src); /** Always empty string returned when NVPairMap lookups fail. */ - static const string s_theEmptyString; + static const std::string s_theEmptyString; /** Unsorted table of event types. */ static EventTypeRecord s_typeTable[]; - /** Unsorted table of factory methods to add to our registry. */ - static EventFactoryRecord s_factoryTable[]; - - /** Registry of event factory methods providing O(log(n)) lookup. */ - static EventFactoryRegistry s_factoryRegistry; - /** The type of this event. */ const Type m_type; @@ -363,7 +232,7 @@ protected: * The unaltered event string, as received from devd, used to * create this event object. */ - string m_eventString; + std::string m_eventString; private: /** @@ -372,52 +241,64 @@ private: * \param[in] eventString The string of devd event data to parse. * \param[out] nvpairs Returns the parsed data */ - static void ParseEventString(Type type, const string &eventString, + static void ParseEventString(Type type, const std::string &eventString, NVPairMap &nvpairs); }; -inline DevCtlEvent::Type -DevCtlEvent::GetType() const +inline Event::Type +Event::GetType() const { return (m_type); } -inline const string & -DevCtlEvent::GetEventString() const +inline const std::string & +Event::GetEventString() const { return (m_eventString); } -/*------------------------------ DevCtlEventList -----------------------------*/ +inline const NVPairMap & +Event::GetMap() const +{ + return (m_nvPairs); +} + +/*--------------------------------- EventList --------------------------------*/ /** - * DevCtlEventList is a specialization of the standard list STL container. + * EventList is a specialization of the standard list STL container. */ -typedef std::list DevCtlEventList; +typedef std::list EventList; /*-------------------------------- DevfsEvent --------------------------------*/ -class DevfsEvent : public DevCtlEvent +class DevfsEvent : public Event { public: - /** Specialized DevCtlEvent object factory for Devfs events. */ - static EventFactory DevfsEventFactory; + /** Specialized Event object factory for Devfs events. */ + static BuildMethod DevfsEventBuilder; - virtual DevCtlEvent *DeepCopy() const; + virtual Event *DeepCopy() const; /** * Interpret and perform any actions necessary to * consume the event. * \return True if this event should be queued for later reevaluation */ - virtual bool Process() const; + virtual bool Process() const; + + bool IsDiskDev() const; + bool IsWholeDev() const; + bool DevName(std::string &name) const; + bool DevPath(std::string &path) const; + bool PhysicalPath(std::string &path) const; protected: /** * Determine if the given device name references a potential - * ZFS leaf vdev. + * disk device. * * \param devName The device name to test. */ - static bool IsDiskDev(const string &devName); + static bool IsDiskDev(const std::string &devName); /** * Given the device name of a disk, determine if the device @@ -428,82 +309,41 @@ protected: * \return True if the device name represents the whole device. * Otherwise false. */ - static bool IsWholeDev(const string &devName); - - /** - * \brief Read and return label information for a device. - * - * \param devFd The device from which to read ZFS label information. - * \param inUse The device is part of an active or potentially - * active configuration. - * \param degraded The device label indicates the vdev is not healthy. - * - * \return If label information is available, an nvlist describing - * the vdev configuraiton found on the device specified by - * devFd. Otherwise NULL. - */ - static nvlist_t *ReadLabel(int devFd, bool &inUse, bool °raded); - - /** - * Attempt to match the ZFS labeled device at devPath with an active - * CaseFile for a missing vdev. If a CaseFile is found, attempt - * to re-integrate the device with its pool. - * - * \param devPath The devfs path to the potential leaf vdev. - * \param physPath The physical path string reported by the device - * at devPath. - * \param devConfig The ZFS label information found on the device - * at devPath. - * - * \return true if the event that caused the online action can - * be considered consumed. - */ - static bool OnlineByLabel(const string &devPath, - const string& physPath, - nvlist_t *devConfig); + static bool IsWholeDev(const std::string &devName); /** DeepCopy Constructor. */ DevfsEvent(const DevfsEvent &src); /** Constructor */ - DevfsEvent(Type, NVPairMap &, const string &); + DevfsEvent(Type, NVPairMap &, const std::string &); }; /*--------------------------------- ZfsEvent ---------------------------------*/ -class ZfsEvent : public DevCtlEvent +class ZfsEvent : public Event { public: - /** Specialized DevCtlEvent object factory for ZFS events. */ - static EventFactory ZfsEventFactory; + /** Specialized Event object factory for ZFS events. */ + static BuildMethod ZfsEventBuilder; - virtual DevCtlEvent *DeepCopy() const; + virtual Event *DeepCopy() const; - /** - * Interpret and perform any actions necessary to - * consume the event. - * \return True if this event should be queued for later reevaluation - */ - virtual bool Process() const; - - const string &PoolName() const; - Guid PoolGUID() const; - Guid VdevGUID() const; + const std::string &PoolName() const; + Guid PoolGUID() const; + Guid VdevGUID() const; protected: /** Constructor */ - ZfsEvent(Type, NVPairMap &, const string &); + ZfsEvent(Type, NVPairMap &, const std::string &); /** Deep copy constructor. */ ZfsEvent(const ZfsEvent &src); - virtual void ProcessPoolEvent() const; - - Guid m_poolGUID; - Guid m_vdevGUID; + Guid m_poolGUID; + Guid m_vdevGUID; }; //- ZfsEvent Inline Public Methods -------------------------------------------- -inline const string& +inline const std::string& ZfsEvent::PoolName() const { /* The pool name is reported as the subsystem of ZFS events. */ @@ -522,4 +362,5 @@ ZfsEvent::VdevGUID() const return (m_vdevGUID); } -#endif /*_DEV_CTL_EVENT_H_ */ +} // namespace DevCtl +#endif /*_DEVCTL_EVENT_H_ */ diff --git a/lib/libdevctl/event_buffer.cc b/lib/libdevctl/event_buffer.cc new file mode 100644 index 00000000000..5fe0ac64b92 --- /dev/null +++ b/lib/libdevctl/event_buffer.cc @@ -0,0 +1,216 @@ +/*- + * Copyright (c) 2011, 2012, 2013 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + */ + +/** + * \file event_buffer.cc + */ + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "event_buffer.h" +#include "exception.h" +#include "reader.h" + +__FBSDID("$FreeBSD$"); + +/*============================ Namespace Control =============================*/ +using std::string; +using std::stringstream; +namespace DevCtl +{ + +/*============================= Class Definitions ============================*/ +/*-------------------------------- EventBuffer -------------------------------*/ +//- EventBuffer Static Data ---------------------------------------------------- +/** + * NOTIFY, NOMATCH, ATTACH, DETACH. See DevCtlEvent::Type. + */ +const char EventBuffer::s_eventStartTokens[] = "!?+-"; + +/** + * Events are terminated by a newline. + */ +const char EventBuffer::s_eventEndTokens[] = "\n"; + +/** + * Key=Value pairs are terminated by whitespace. + */ +const char EventBuffer::s_keyPairSepTokens[] = " \t\n"; + +//- EventBuffer Public Methods ------------------------------------------------- +EventBuffer::EventBuffer(Reader& reader) + : m_reader(reader), + m_validLen(0), + m_parsedLen(0), + m_nextEventOffset(0), + m_synchronized(true) +{ +} + +bool +EventBuffer::ExtractEvent(string &eventString) +{ + stringstream tsField; + timeval now; + + gettimeofday(&now, NULL); + tsField << " timestamp=" << now.tv_sec; + + while (UnParsed() > 0 || Fill()) { + + /* + * If the valid data in the buffer isn't enough to hold + * a full event, try reading more. + */ + if (NextEventMaxLen() < MIN_EVENT_SIZE) { + m_parsedLen += UnParsed(); + continue; + } + + char *nextEvent(m_buf + m_nextEventOffset); + bool truncated(true); + size_t eventLen(strcspn(nextEvent, s_eventEndTokens)); + + if (!m_synchronized) { + /* Discard data until an end token is read. */ + if (nextEvent[eventLen] != '\0') + m_synchronized = true; + m_nextEventOffset += eventLen; + m_parsedLen = m_nextEventOffset; + continue; + } else if (nextEvent[eventLen] == '\0') { + + m_parsedLen += eventLen; + if (m_parsedLen < MAX_EVENT_SIZE) { + /* + * Ran out of buffer before hitting + * a full event. Fill() and try again. + */ + continue; + } + syslog(LOG_WARNING, "Overran event buffer\n\tm_nextEventOffset" + "=%zd\n\tm_parsedLen=%zd\n\tm_validLen=%zd", + m_nextEventOffset, m_parsedLen, m_validLen); + } else { + /* + * Include the normal terminator in the extracted + * event data. + */ + eventLen += 1; + truncated = false; + } + + m_nextEventOffset += eventLen; + m_parsedLen = m_nextEventOffset; + eventString.assign(nextEvent, eventLen); + + if (truncated) { + size_t fieldEnd; + + /* Break cleanly at the end of a key<=>value pair. */ + fieldEnd = eventString.find_last_of(s_keyPairSepTokens); + if (fieldEnd != string::npos) + eventString.erase(fieldEnd); + eventString += '\n'; + + m_synchronized = false; + syslog(LOG_WARNING, + "Truncated %zd characters from event.", + eventLen - fieldEnd); + } + + /* + * Add a timestamp as the final field of the event if it is + * not already present. + */ + if (eventString.find("timestamp=") == string::npos) { + size_t eventEnd(eventString.find_last_not_of('\n') + 1); + + eventString.insert(eventEnd, tsField.str()); + } + + return (true); + } + return (false); +} + +//- EventBuffer Private Methods ------------------------------------------------ +bool +EventBuffer::Fill() +{ + ssize_t avail; + ssize_t consumed(0); + + /* Compact the buffer. */ + if (m_nextEventOffset != 0) { + memmove(m_buf, m_buf + m_nextEventOffset, + m_validLen - m_nextEventOffset); + m_validLen -= m_nextEventOffset; + m_parsedLen -= m_nextEventOffset; + m_nextEventOffset = 0; + } + + /* Fill any empty space. */ + avail = m_reader.in_avail(); + if (avail > 0) { + size_t want; + + want = std::min((size_t)avail, MAX_READ_SIZE - m_validLen); + consumed = m_reader.read(m_buf + m_validLen, want); + if (consumed == -1) { + if (errno == EINTR) + return (false); + else + err(1, "EventBuffer::Fill(): Read failed"); + } + } else if (avail == -1) { + throw Exception("EventBuffer::Fill(): Reader disconnected"); + } + + m_validLen += consumed; + /* Guarantee our buffer is always NUL terminated. */ + m_buf[m_validLen] = '\0'; + + return (consumed > 0); +} + +} // namespace DevCtl diff --git a/lib/libdevctl/event_buffer.h b/lib/libdevctl/event_buffer.h new file mode 100644 index 00000000000..4640a2d0128 --- /dev/null +++ b/lib/libdevctl/event_buffer.h @@ -0,0 +1,188 @@ +/*- + * Copyright (c) 2011, 2012, 2013 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + * + * $FreeBSD$ + */ + +/** + * \file devctl_event_buffer.h + */ +#ifndef _DEVCTL_EVENT_BUFFER_H_ +#define _DEVCTL_EVENT_BUFFER_H_ + +/*============================ Namespace Control =============================*/ +namespace DevCtl +{ + +/*=========================== Forward Declarations ===========================*/ +class Reader; + +/*============================= Class Definitions ============================*/ +/*-------------------------------- EventBuffer -------------------------------*/ +/** + * \brief Class buffering event data from Devd or a similar source and + * splitting it into individual event strings. + * + * Users of this class initialize it with a Reader associated with the unix + * domain socket connection with devd or a compatible source. The lifetime of + * an EventBuffer instance should match that of the Reader passed to it. This + * is required as data from partially received events is retained in the + * EventBuffer in order to allow reconstruction of these events across multiple + * reads of the stream. + * + * Once the program determines that the Reader is ready for reading, the + * EventBuffer::ExtractEvent() should be called in a loop until the method + * returns false. + */ +class EventBuffer +{ +public: + /** + * Constructor + * + * \param reader The data source on which to buffer/parse event data. + */ + EventBuffer(Reader& reader); + + /** + * Pull a single event string out of the event buffer. + * + * \param eventString The extracted event data (if available). + * + * \return true if event data is available and eventString has + * been populated. Otherwise false. + */ + bool ExtractEvent(std::string &eventString); + +private: + enum { + /** + * Size of an empty event (start and end token with + * no data. The EventBuffer parsing needs at least + * this much data in the buffer for safe event extraction. + */ + MIN_EVENT_SIZE = 2, + + /* + * The maximum event size supported by ZFSD. + * Events larger than this size (minus 1) are + * truncated at the end of the last fully received + * key/value pair. + */ + MAX_EVENT_SIZE = 8192, + + /** + * The maximum amount of buffer data to read at + * a single time from the Devd file descriptor. + */ + MAX_READ_SIZE = MAX_EVENT_SIZE, + + /** + * The size of EventBuffer's buffer of Devd event data. + * This is one larger than the maximum supported event + * size, which alows us to always include a terminating + * NUL without overwriting any received data. + */ + EVENT_BUFSIZE = MAX_EVENT_SIZE + /*NUL*/1 + }; + + /** The amount of data in m_buf we have yet to look at. */ + size_t UnParsed() const; + + /** The amount of data in m_buf available for the next event. */ + size_t NextEventMaxLen() const; + + /** Fill the event buffer with event data from Devd. */ + bool Fill(); + + /** Characters we treat as beginning an event string. */ + static const char s_eventStartTokens[]; + + /** Characters we treat as ending an event string. */ + static const char s_eventEndTokens[]; + + /** Characters found between successive "key=value" strings. */ + static const char s_keyPairSepTokens[]; + + /** Temporary space for event data during our parsing. Laid out like + * this: + * <---------------------------------------------------------> + * | | | | | + * m_buf---| | | | | + * m_nextEventOffset-- | | | + * m_parsedLen------------- | | + * m_validLen-------------------------- | + * EVENT_BUFSIZE------------------------------------------------------ + * + * Data before m_nextEventOffset has already been processed. + * + * Data between m_nextEvenOffset and m_parsedLen has been parsed, but + * not processed as a single event. + * + * Data between m_parsedLen and m_validLen has been read from the + * source, but not yet parsed. + * + * Between m_validLen and EVENT_BUFSIZE is empty space. + * + * */ + char m_buf[EVENT_BUFSIZE]; + + /** Reference to the reader linked to devd's domain socket. */ + Reader& m_reader; + + /** Offset within m_buf to the beginning of free space. */ + size_t m_validLen; + + /** Offset within m_buf to the beginning of data not yet parsed */ + size_t m_parsedLen; + + /** Offset within m_buf to the start token of the next event. */ + size_t m_nextEventOffset; + + /** The EventBuffer is aligned and tracking event records. */ + bool m_synchronized; +}; + +//- EventBuffer Inline Private Methods ----------------------------------------- +inline size_t +EventBuffer::UnParsed() const +{ + return (m_validLen - m_parsedLen); +} + +inline size_t +EventBuffer::NextEventMaxLen() const +{ + return (m_validLen - m_nextEventOffset); +} + +} // namespace DevCtl +#endif /* _DEVCTL_EVENT_BUFFER_H_ */ diff --git a/lib/libdevctl/event_factory.cc b/lib/libdevctl/event_factory.cc new file mode 100644 index 00000000000..2e9da4eb6a8 --- /dev/null +++ b/lib/libdevctl/event_factory.cc @@ -0,0 +1,96 @@ +/*- + * Copyright (c) 2011, 2012, 2013 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + */ + +/** + * \file event_factory.cc + */ +#include + +#include +#include +#include + +#include "guid.h" +#include "event.h" +#include "event_factory.h" + +__FBSDID("$FreeBSD$"); + +/*================================== Macros ==================================*/ +#define NUM_ELEMENTS(x) (sizeof(x) / sizeof(*x)) + +/*============================ Namespace Control =============================*/ +namespace DevCtl +{ + +/*=========================== Class Implementations ==========================*/ +/*------------------------------- EventFactory -------------------------------*/ +//- Event Public Methods ------------------------------------------------------- +EventFactory::EventFactory(Event::BuildMethod *defaultBuildMethod) + : m_defaultBuildMethod(defaultBuildMethod) +{ +} + +void +EventFactory::UpdateRegistry(Record regEntries[], size_t numEntries) +{ + EventFactory::Record *rec(regEntries); + EventFactory::Record *lastRec(rec + numEntries - 1); + + for (; rec <= lastRec; rec++) { + Key key(rec->m_type, rec->m_subsystem); + + if (rec->m_buildMethod == NULL) + m_registry.erase(key); + else + m_registry[key] = rec->m_buildMethod; + } +} + +Event * +EventFactory::Build(Event::Type type, NVPairMap &nvpairs, + const std::string eventString) const +{ + Key key(type, nvpairs["system"]); + Event::BuildMethod *buildMethod(m_defaultBuildMethod); + + Registry::const_iterator foundMethod(m_registry.find(key)); + if (foundMethod != m_registry.end()) + buildMethod = foundMethod->second; + + if (buildMethod == NULL) + return (NULL); + + return ((foundMethod->second)(type, nvpairs, eventString)); +} + +} // namespace DevCtl diff --git a/lib/libdevctl/event_factory.h b/lib/libdevctl/event_factory.h new file mode 100644 index 00000000000..d7fdecb7786 --- /dev/null +++ b/lib/libdevctl/event_factory.h @@ -0,0 +1,94 @@ +/*- + * Copyright (c) 2011, 2012, 2013 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + * + * $FreeBSD$ + */ + +/** + * \file devctl_event_factory.h + */ + +#ifndef _DEVCTL_EVENT_FACTORY_H_ +#define _DEVCTL_EVENT_FACTORY_H_ + +/*============================ Namespace Control =============================*/ +namespace DevCtl +{ + +/*============================= Class Definitions ============================*/ +/*------------------------------- EventFactory -------------------------------*/ +/** + * \brief Container for "event type" => "event object" creaction methods. + */ +class EventFactory +{ +public: + /** + * Event creation handlers are matched by event type and a + * string representing the system emitting the event. + */ + typedef std::pair Key; + + /** Map type for Factory method lookups. */ + typedef std::map Registry; + + /** Table record of factory methods to add to our registry. */ + struct Record + { + Event::Type m_type; + const char *m_subsystem; + Event::BuildMethod *m_buildMethod; + }; + + const Registry &GetRegistry() const; + Event *Build(Event::Type type, NVPairMap &nvpairs, + const std::string eventString) const; + + EventFactory(Event::BuildMethod *defaultBuildMethod = NULL); + + void UpdateRegistry(Record regEntries[], size_t numEntries); + + +protected: + /** Registry of event factory methods providing O(log(n)) lookup. */ + Registry m_registry; + + Event::BuildMethod *m_defaultBuildMethod; +}; + +inline const EventFactory::Registry & +EventFactory::GetRegistry() const +{ + return (m_registry); +} + +} // namespace DevCtl +#endif /*_DEVCTL_EVENT_FACTORY_H_ */ diff --git a/lib/libdevctl/exception.cc b/lib/libdevctl/exception.cc new file mode 100644 index 00000000000..4d242df29f4 --- /dev/null +++ b/lib/libdevctl/exception.cc @@ -0,0 +1,123 @@ +/*- + * Copyright (c) 2011, 2012, 2013 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + */ + +/** + * \file exception.cc + */ +#include + +#include + +#include +#include + +#include "exception.h" + +__FBSDID("$FreeBSD$"); + +/*============================ Namespace Control =============================*/ +using std::string; +using std::stringstream; +using std::endl; +namespace DevCtl +{ + +/*=========================== Class Implementations ==========================*/ +/*--------------------------------- Exception --------------------------------*/ +void +Exception::FormatLog(const char *fmt, va_list ap) +{ + char buf[256]; + + vsnprintf(buf, sizeof(buf), fmt, ap); + m_log = buf; +} + +Exception::Exception(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + FormatLog(fmt, ap); + va_end(ap); +} + +Exception::Exception() +{ +} + +void +Exception::Log() const +{ + syslog(LOG_ERR, "%s", m_log.c_str()); +} + +/*------------------------------ ParseException ------------------------------*/ +//- ParseException Inline Public Methods --------------------------------------- +ParseException::ParseException(Type type, const std::string &parsedBuffer, + size_t offset) + : Exception(), + m_type(type), + m_parsedBuffer(parsedBuffer), + m_offset(offset) +{ + stringstream logstream; + + logstream << "Parsing "; + + switch (Type()) { + case INVALID_FORMAT: + logstream << "invalid format "; + break; + case DISCARDED_EVENT_TYPE: + logstream << "discarded event "; + break; + case UNKNOWN_EVENT_TYPE: + logstream << "unknown event "; + break; + default: + break; + } + logstream << "exception on buffer: \'"; + if (GetOffset() == 0) { + logstream << m_parsedBuffer << '\'' << endl; + } else { + string markedBuffer(m_parsedBuffer); + + markedBuffer.insert(GetOffset(), ""); + logstream << markedBuffer << '\'' << endl; + } + + GetString() = logstream.str(); +} + +} // namespace DevCtl diff --git a/lib/libdevctl/exception.h b/lib/libdevctl/exception.h new file mode 100644 index 00000000000..8c4a4e7b296 --- /dev/null +++ b/lib/libdevctl/exception.h @@ -0,0 +1,166 @@ +/*- + * Copyright (c) 2011, 2012, 2013 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + */ + +/** + * \file zfsd_exception.h + * + * Definition of the ZfsdException class hierarchy. All exceptions + * explicitly thrown by Zfsd are defined here. + */ +#ifndef _DEVCTL_EXCEPTION_H_ +#define _DEVCTL_EXCEPTION_H_ + +/*============================ Namespace Control =============================*/ +namespace DevCtl +{ + +/*============================= Class Definitions ============================*/ + +/*--------------------------------- Exception --------------------------------*/ +/** + * \brief Class allowing unified reporting/logging of exceptional events. + */ +class Exception +{ +public: + /** + * \brief Exception constructor allowing arbitrary string + * data to be reported. + * + * \param fmt Printf-like string format specifier. + */ + Exception(const char *fmt, ...); + + /** + * \brief Augment/Modify a Exception's string data. + */ + std::string& GetString(); + + /** + * \brief Emit exception data to syslog(3). + */ + virtual void Log() const; + +protected: + Exception(); + + /** + * \brief Convert exception string format and arguments provided + * in event constructors into a linear string. + */ + void FormatLog(const char *fmt, va_list ap); + + std::string m_log; +}; + +inline std::string & +Exception::GetString() +{ + return (m_log); +} + +/*------------------------------ ParseException ------------------------------*/ +/** + * Exception thrown when an event string is not converted to an actionable + * Event object. + */ +class ParseException : public Exception +{ +public: + enum Type + { + /** Improperly formatted event string encounterd. */ + INVALID_FORMAT, + + /** No handlers for this event type. */ + DISCARDED_EVENT_TYPE, + + /** Unhandled event type. */ + UNKNOWN_EVENT_TYPE + }; + + /** + * Constructor + * + * \param type The type of this exception. + * \param parsedBuffer The parsing buffer active at the time of + * the exception. + * \param offset The location in the parse buffer where this + * exception occurred. + */ + ParseException(Type type, const std::string &parsedBuffer, + size_t offset = 0); + + /** + * Accessor + * + * \return The classification for this exception. + */ + Type GetType() const; + + /** + * Accessor + * + * \return The offset into the event string where this exception + * occurred. + */ + size_t GetOffset() const; + +private: + /** The type of this exception. */ + Type m_type; + + /** The parsing buffer that was active at the time of the exception. */ + const std::string m_parsedBuffer; + + /** + * The offset into the event string buffer from where this + * exception was triggered. + */ + size_t m_offset; +}; + +//- ParseException Inline Const Public Methods --------------------------------- +inline ParseException::Type +ParseException::GetType() const +{ + return (m_type); +} + +inline size_t +ParseException::GetOffset() const +{ + return (m_offset); +} + +} // namespace DevCtl +#endif /* _DEVCTL_EXCEPTION_H_ */ diff --git a/cddl/sbin/zfsd/guid.cc b/lib/libdevctl/guid.cc similarity index 95% rename from cddl/sbin/zfsd/guid.cc rename to lib/libdevctl/guid.cc index 08c1ce6a107..031f3f61558 100644 --- a/cddl/sbin/zfsd/guid.cc +++ b/lib/libdevctl/guid.cc @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2012 Spectra Logic Corporation + * Copyright (c) 2012, 2013 Spectra Logic Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -43,7 +43,7 @@ #include #include -#include +#include #include #include "guid.h" @@ -51,6 +51,8 @@ __FBSDID("$FreeBSD$"); /*============================ Namespace Control =============================*/ using std::string; +namespace DevCtl +{ /*=========================== Class Implementations ==========================*/ /*----------------------------------- Guid -----------------------------------*/ @@ -76,3 +78,5 @@ operator<< (std::ostream& out, Guid g) out << "None"; return (out); } + +} // namespace DevCtl diff --git a/cddl/sbin/zfsd/guid.h b/lib/libdevctl/guid.h similarity index 85% rename from cddl/sbin/zfsd/guid.h rename to lib/libdevctl/guid.h index ecfd7e015a4..cecc5e1fff4 100644 --- a/cddl/sbin/zfsd/guid.h +++ b/lib/libdevctl/guid.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2012 Spectra Logic Corporation + * Copyright (c) 2012, 2013 Spectra Logic Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -33,22 +33,27 @@ */ /** - * \file guid.h + * \file devctl_guid.h * * Definition of the Guid class. */ -#ifndef _GUID_H_ -#define _GUID_H_ +#ifndef _DEVCTL_GUID_H_ +#define _DEVCTL_GUID_H_ -#include +/*============================ Namespace Control =============================*/ +namespace DevCtl +{ +/*============================= Class Definitions ============================*/ +/*----------------------------------- Guid -----------------------------------*/ /** * \brief Object that represents guids. * * It can generally be manipulated as a uint64_t, but with a special * value INVALID_GUID that does not equal any valid guid. * - * As of this writing, spa_generate_guid() in spa_misc.c explicitly + * As of this writing, this class is only used to to represent ZFS + * guids in events and spa_generate_guid() in spa_misc.c explicitly * refuses to return a guid of 0. So this class uses 0 as the value * for INVALID_GUID. In the future, if 0 is allowed to be a valid * guid, the implementation of this class must change. @@ -134,4 +139,5 @@ Guid::operator bool() const /** Convert the GUID into its string representation */ std::ostream& operator<< (std::ostream& out, Guid g); -#endif /* _GUID_H_ */ +} // namespace DevCtl +#endif /* _DEVCTL_GUID_H_ */ diff --git a/lib/libdevctl/reader.cc b/lib/libdevctl/reader.cc new file mode 100644 index 00000000000..85286f7bb1a --- /dev/null +++ b/lib/libdevctl/reader.cc @@ -0,0 +1,99 @@ +/*- + * Copyright (c) 2011, 2012, 2013 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Alan Somers (Spectra Logic Corporation) + */ + +/** + * \file reader.cc + */ + +#include +#include + +#include +#include +#include +#include + +#include + +#include "reader.h" + +__FBSDID("$FreeBSD$"); + +/*============================ Namespace Control =============================*/ +namespace DevCtl +{ + +//- FDReader Public Methods --------------------------------------------------- +FDReader::FDReader(int fd) + : m_fd(fd) +{ +} + +ssize_t +FDReader::read(char* buf, size_t count) +{ + return (::read(m_fd, buf, count)); +} + +ssize_t +FDReader::in_avail() const +{ + int bytes; + if (ioctl(m_fd, FIONREAD, &bytes)) { + syslog(LOG_ERR, "ioctl FIONREAD: %s", strerror(errno)); + return (-1); + } + return (bytes); +} + +//- IstreamReader Inline Public Methods ---------------------------------------- +IstreamReader::IstreamReader(std::istream* stream) + : m_stream(stream) +{ +} + +ssize_t +IstreamReader::read(char* buf, size_t count) +{ + m_stream->read(buf, count); + if (m_stream->fail()) + return (-1); + return (m_stream->gcount()); +} + +ssize_t +IstreamReader::in_avail() const +{ + return (m_stream->rdbuf()->in_avail()); +} + +} // namespace DevCtl diff --git a/lib/libdevctl/reader.h b/lib/libdevctl/reader.h new file mode 100644 index 00000000000..9eb8533f7cb --- /dev/null +++ b/lib/libdevctl/reader.h @@ -0,0 +1,136 @@ +/*- + * Copyright (c) 2011, 2012, 2013 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + * + * $FreeBSD$ + */ + +/** + * \file devctl_reader.h + */ +#ifndef _DEVCTL_READER_H_ +#define _DEVCTL_READER_H_ + +/*=========================== Forward Declarations ===========================*/ + +/*============================ Namespace Control =============================*/ +namespace DevCtl +{ + +/*============================= Class Definitions ============================*/ + +/*-------------------------------- Reader -------------------------------*/ +/** + * \brief A class that presents a common interface to both file descriptors + * and istreams. + * + * Standard C++ provides no way to create an iostream from a file descriptor or + * a FILE. The GNU, Apache, HPUX, and Solaris C++ libraries all provide + * non-standard ways to construct such a stream using similar semantics, but + * FreeBSD's C++ library does not. This class supports only the functionality + * needed by ZFSD; it does not implement the iostream API. + */ +class Reader +{ +public: + /** + * \brief Return the number of bytes immediately available for reading + */ + virtual ssize_t in_avail() const = 0; + + /** + * \brief Reads up to count bytes + * + * Whether this call blocks depends on the underlying input source. + * On error, -1 is returned, and errno will be set by the underlying + * source. + * + * \param buf Destination for the data + * \param count Maximum amount of data to read + * \returns Amount of data that was actually read + */ + virtual ssize_t read(char* buf, size_t count) = 0; + + virtual ~Reader() = 0; +}; + +inline Reader::~Reader() {} + + +/*--------------------------------- FDReader ---------------------------------*/ +/** + * \brief Specialization of Reader that uses a file descriptor + */ +class FDReader : public Reader +{ +public: + /** + * \brief Constructor + * + * \param fd An open file descriptor. It will not be garbage + * collected by the destructor. + */ + FDReader(int fd); + + virtual ssize_t in_avail() const; + + virtual ssize_t read(char* buf, size_t count); + +protected: + /** Copy of the underlying file descriptor */ + int m_fd; +}; + +/*-------------------------------- IstreamReader------------------------------*/ +/** + * \brief Specialization of Reader that uses a std::istream + */ +class IstreamReader : public Reader +{ +public: + /** + * Constructor + * + * \param stream Pointer to an open istream. It will not be + * garbage collected by the destructor. + */ + IstreamReader(std::istream* stream); + + virtual ssize_t in_avail() const; + + virtual ssize_t read(char* buf, size_t count); + +protected: + /** Copy of the underlying stream */ + std::istream *m_stream; +}; + +} // namespace DevCtl +#endif /* _DEVCTL_READER_H_ */ diff --git a/share/mk/bsd.libnames.mk b/share/mk/bsd.libnames.mk index 8ef6e7a9f13..b0fb1666442 100644 --- a/share/mk/bsd.libnames.mk +++ b/share/mk/bsd.libnames.mk @@ -39,6 +39,7 @@ LIBCRYPT?= ${DESTDIR}${LIBDIR}/libcrypt.a LIBCRYPTO?= ${DESTDIR}${LIBDIR}/libcrypto.a LIBCTF?= ${DESTDIR}${LIBDIR}/libctf.a LIBCURSES?= ${DESTDIR}${LIBDIR}/libcurses.a +LIBDEVCLT?= ${DESTDIR}${LIBDIR}/libdevctl.a LIBDEVINFO?= ${DESTDIR}${LIBDIR}/libdevinfo.a LIBDEVSTAT?= ${DESTDIR}${LIBDIR}/libdevstat.a LIBDIALOG?= ${DESTDIR}${LIBDIR}/libdialog.a From 2a9910811990abecce5cf764b1e49cedc352fbba Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Mon, 14 Oct 2013 23:06:12 +0000 Subject: [PATCH 73/89] Fix a bug in zfsd: If you pull two drives on a raidz2 pool with at least two available hotspares, only one will be activated. cddl/sbin/zfsd/case_file.cc CaseFile::Replace should return false when zpool_vdev_attach fails. That way, the operation will be reattempted the next time that a config_sync event arrives. Submitted by: alans Approved by: ken (mentor) Sponsored by: Spectra Logic Corporation --- cddl/sbin/zfsd/case_file.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cddl/sbin/zfsd/case_file.cc b/cddl/sbin/zfsd/case_file.cc index a3442473bc8..85d4ce2f7c9 100644 --- a/cddl/sbin/zfsd/case_file.cc +++ b/cddl/sbin/zfsd/case_file.cc @@ -972,6 +972,7 @@ CaseFile::Replace(const char* vdev_type, const char* path) { nvlist_t *nvroot, *newvd; zpool_handle_t *zhp; const char* poolname; + bool retval = true; /* Figure out what pool we're working on */ ZpoolList zpl(ZpoolList::ZpoolByGUID, &m_poolGUID); @@ -1023,6 +1024,7 @@ CaseFile::Replace(const char* vdev_type, const char* path) { poolname, VdevGUIDString().c_str(), libzfs_error_action(g_zfsHandle), libzfs_error_description(g_zfsHandle)); + retval = false; } else { syslog(LOG_INFO, "Replacing vdev(%s/%s) with %s\n", poolname, VdevGUIDString().c_str(), @@ -1030,7 +1032,7 @@ CaseFile::Replace(const char* vdev_type, const char* path) { } nvlist_free(nvroot); - return (true); + return (retval); } /* Does the argument event refer to a checksum error? */ From ea30dc898da44cc642efe0f78c99d1c0fe9c6ce5 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Mon, 14 Oct 2013 23:13:03 +0000 Subject: [PATCH 74/89] Fix a few libdevctl bugs found by inspection lib/libdevctl/consumer.cc: In Consummer::ConnectToDevd(), invoke DisconnectFromDevd() if our connect fails so that the connection to DevD is recorded as closed. This avoids fooling future invocations of ConnectToDevd() into thinking the connection is already established. In Consumer::FlushEvents(), handle in_avail() returning -1, which indicates and error. In Consumer::EventsPending(), update exception string to reflect the conversion of this class from ZfsDaemon to Consumer when it was refactored out of zfsd. Submitted by: gibbs Approved by: ken (mentor) Obtained from: Spectra Logic Corporation --- lib/libdevctl/consumer.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/libdevctl/consumer.cc b/lib/libdevctl/consumer.cc index d2d5c0c6f71..c35980dc11e 100644 --- a/lib/libdevctl/consumer.cc +++ b/lib/libdevctl/consumer.cc @@ -117,6 +117,7 @@ Consumer::ConnectToDevd() sLen); if (result == -1) { syslog(LOG_INFO, "Unable to connect to devd"); + DisconnectFromDevd(); return (false); } @@ -212,7 +213,7 @@ Consumer::FlushEvents() { char discardBuf[256]; - while (m_reader->in_avail()) + while (m_reader->in_avail() > 0) m_reader->read(discardBuf, sizeof(discardBuf)); } @@ -233,7 +234,7 @@ Consumer::EventsPending() err(1, "Polling for devd events failed"); if ((fds->revents & POLLERR) != 0) - throw Exception("ZfsdDaemon:EventsPending(): " + throw Exception("Consumer::EventsPending(): " "POLLERR detected on devd socket."); if ((fds->revents & POLLHUP) != 0) From 0337c61a7ed12773c29197b8e8472857a967bf6f Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Mon, 14 Oct 2013 23:15:58 +0000 Subject: [PATCH 75/89] Modify Zfsd to honor shutdown signals even when started during a devctl event flood. cddl/sbin/zfsd/zfsd.cc: In ZfsDaemon::Run(), catch DevCtl::Exceptions instead of one of its derived classes ZfsdExceptions. libdevctl, which contains some of the methods we call in this context, only emits DevCtl::Exceptions, and we must catch these too. In ZfsDaemon::DetectMissedEvents(), monitor s_terminateEventLoop so we are guaranteed to exit our loop if a shutdown is requested. Submitted by: gibbs Approved by: ken (mentor) Sponsored by: Spectra Logic Corporation --- cddl/sbin/zfsd/zfsd.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cddl/sbin/zfsd/zfsd.cc b/cddl/sbin/zfsd/zfsd.cc index a6ee947403f..0ed727d21a2 100644 --- a/cddl/sbin/zfsd/zfsd.cc +++ b/cddl/sbin/zfsd/zfsd.cc @@ -145,7 +145,7 @@ ZfsDaemon::Run() daemon.EventLoop(); - } catch (const ZfsdException &exp) { + } catch (const DevCtl::Exception &exp) { exp.Log(); } } @@ -287,7 +287,7 @@ ZfsDaemon::DetectMissedEvents() * If the system state has changed durring our * interrogation, start over. */ - } while (EventsPending()); + } while (s_terminateEventLoop == false && EventsPending()); RescanSystem(); } From 39b0f1d489d81cdffb65132ef818e48576273d3e Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Mon, 14 Oct 2013 23:21:02 +0000 Subject: [PATCH 76/89] Fix registration of a default event builder for the Consumer class. lib/libdevctl/consumer.cc: In Consumer::ConnectToDevd(), improve syslog output. lib/libdevctl/event.cc: lib/libdevctl/event.h: Implement pure virtual method in the Event class so that it can be instantiated. Provide a generic EventBuilder method that builds generic Events. This is expected to be used by consumers of this library that just need key=>value data for all events received. lib/libdevctl/event_factory.cc: In EventFactory::Build, fix segmentation violation when a default event builder is registered and no more specific event builder is found in the factory's registry. Submitted by: gibbs Approved by: ken (mentor) Sponsored by: Spectra Logic Corporation --- lib/libdevctl/consumer.cc | 3 ++- lib/libdevctl/event.cc | 13 +++++++++++++ lib/libdevctl/event.h | 5 ++++- lib/libdevctl/event_factory.cc | 2 +- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/lib/libdevctl/consumer.cc b/lib/libdevctl/consumer.cc index c35980dc11e..4dc3a3eea9d 100644 --- a/lib/libdevctl/consumer.cc +++ b/lib/libdevctl/consumer.cc @@ -98,11 +98,12 @@ Consumer::ConnectToDevd() int sLen; int result; - syslog(LOG_INFO, "Connecting to devd"); if (m_devdSockFD != -1) { /* Already connected. */ + syslog(LOG_INFO, "%s: Already connected.", __func__); return (true); } + syslog(LOG_INFO, "%s: Connecting to devd.", __func__); memset(&devdAddr, 0, sizeof(devdAddr)); devdAddr.sun_family= AF_UNIX; diff --git a/lib/libdevctl/event.cc b/lib/libdevctl/event.cc index 20230944b9a..d7e61f1d2f2 100644 --- a/lib/libdevctl/event.cc +++ b/lib/libdevctl/event.cc @@ -84,6 +84,13 @@ Event::EventTypeRecord Event::s_typeTable[] = }; //- Event Static Public Methods ------------------------------------------------ +Event * +Event::EventBuilder(Event::Type type, NVPairMap &nvPairs, + const string &eventString) +{ + return (new Event(type, nvPairs, eventString)); +} + Event * Event::CreateEvent(const EventFactory &factory, const string &eventString) { @@ -186,6 +193,12 @@ Event::~Event() delete &m_nvPairs; } +Event * +Event::DeepCopy() const +{ + return (new Event(*this)); +} + bool Event::Process() const { diff --git a/lib/libdevctl/event.h b/lib/libdevctl/event.h index e7b92300fe3..9f13f91db33 100644 --- a/lib/libdevctl/event.h +++ b/lib/libdevctl/event.h @@ -94,6 +94,9 @@ public: */ typedef Event* (BuildMethod)(Type, NVPairMap &, const std::string &); + /** Generic Event object factory. */ + static BuildMethod EventBuilder; + static Event *CreateEvent(const EventFactory &factory, const std::string &eventString); @@ -168,7 +171,7 @@ public: * Create and return a fully independent clone * of this event. */ - virtual Event *DeepCopy() const = 0; + virtual Event *DeepCopy() const; /** Destructor */ virtual ~Event(); diff --git a/lib/libdevctl/event_factory.cc b/lib/libdevctl/event_factory.cc index 2e9da4eb6a8..229233d3e89 100644 --- a/lib/libdevctl/event_factory.cc +++ b/lib/libdevctl/event_factory.cc @@ -90,7 +90,7 @@ EventFactory::Build(Event::Type type, NVPairMap &nvpairs, if (buildMethod == NULL) return (NULL); - return ((foundMethod->second)(type, nvpairs, eventString)); + return (buildMethod(type, nvpairs, eventString)); } } // namespace DevCtl From 0214b613c4f6e1662dd51a1978d29b28851848ec Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Mon, 14 Oct 2013 23:24:53 +0000 Subject: [PATCH 77/89] Logging changes in zfsd sbin/zfsd/zfsd.cc Log when a system rescan event request is processed. This is primarily so it can be differentiated from other system rescan events. sbin/zfsd/zfsd_event.cc Stop emitting "Searching for CaseFile by Physical Path" log. It contributes nothing to the log and occurs very frequently. Submitted by: will Approved by: ken (mentor) Sponsored by: Spectra Logic Corporation --- cddl/sbin/zfsd/zfsd.cc | 1 + cddl/sbin/zfsd/zfsd_event.cc | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/cddl/sbin/zfsd/zfsd.cc b/cddl/sbin/zfsd/zfsd.cc index 0ed727d21a2..27b0ccd73e6 100644 --- a/cddl/sbin/zfsd/zfsd.cc +++ b/cddl/sbin/zfsd/zfsd.cc @@ -345,6 +345,7 @@ ZfsDaemon::EventLoop() if (s_systemRescanRequested == true) { s_systemRescanRequested = false; + syslog(LOG_INFO, "System Rescan request processed."); RescanSystem(); } diff --git a/cddl/sbin/zfsd/zfsd_event.cc b/cddl/sbin/zfsd/zfsd_event.cc index 81cf8eeb031..762368cf4ac 100644 --- a/cddl/sbin/zfsd/zfsd_event.cc +++ b/cddl/sbin/zfsd/zfsd_event.cc @@ -195,7 +195,6 @@ DevfsEvent::Process() const "as a replace by physical path candidate.\n", devName.c_str()); } else if (havePhysPath && IsWholeDev()) { - syslog(LOG_INFO, "Searching for CaseFile by Physical Path\n"); CaseFile *caseFile(CaseFile::Find(physPath)); if (caseFile != NULL) { syslog(LOG_INFO, From 75ddcee0f6538c8553abd5641b0db0a046d3adc7 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Mon, 14 Oct 2013 23:31:18 +0000 Subject: [PATCH 78/89] Improve zfsd's spare handling. Zfsd can now: - Spare broken spares, whether a case file is created for the original drive or for the previous spare. - Avoid sparing spares that are either resilvering or healthy. This enables failover of multiple devices in a pool. It also means that zfsd doesn't require more spares than are actually needed to make a pool whole again. cddl/sbin/zfsd/vdev.h: cddl/sbin/zfsd/vdev.cc: - Add NonexistentVdev, a singleton instance of Vdev that represents a vdev that doesn't exist. Supporting this are new methods: - Vdev::Vdev(), an empty copy constructor. - Vdev::DoesNotExist(), which returns true if the vdev doesn't exist. - Add vdev tree methods of use for purposes for which VdevIterator is not appropriate: - Vdev::Children(), which returns a list of the vdev's children. - Vdev::RootVdev(), which returns the root vdev of the vdev's pool. - Vdev::Parent(), which returns the vdev's parent. This works by traversing the tree until it finds a vdev whose children include the caller object. - Add several useful vdev instance methods: - Vdev::IsAvailableSpare(), which returns whether the vdev is an unassigned and usable spare. - Vdev::Name(), which returns the vdev's name; it can be made to return a "verbose" name too (e.g. "spare" vs. "spare-1"). - Vdev::IsSpare(), Vdev::IsActiveSpare(), and Vdev::IsResilvering(). - While I'm here, refactor Vdev::Vdev(*) so that common initialization code only exists in one place. cddl/sbin/zfsd/case_file.h: cddl/sbin/zfsd/case_file.cc: - Add CaseFile::BeingReplacedBy(), which returns the device replacing the current device, if one exists. Performing this requires finding the vdev's parent and checking the state of its children. - Change CaseFile::Replace() to allow the caller to specify whether the case's vdev is being replaced by a spare. This enables Replace() to detect the appropriate device to perform the actual replace on. - Add CaseFile::CaseVdev() to obtain a Vdev for the case file, and use it in several places that perform the operation. cddl/sbin/zfsd/case_file.cc: - Improve logging for case file evaluation so that the action chosen is always logged. - While I'm here, make zpool lookups consistent and always generate a zpool_handle_t * local to the function where it's needed. cddl/sbin/zfsd/vdev.h: cddl/sbin/zfsd/zfsd_exception.cc: - Add , now required because of Vdev::Children() in vdev.h. Submitted by: will Approved by: ken (mentor) Sponsored by: Spectra Logic Corporation --- cddl/sbin/zfsd/case_file.cc | 170 +++++++++++++++++--------- cddl/sbin/zfsd/case_file.h | 16 ++- cddl/sbin/zfsd/vdev.cc | 203 +++++++++++++++++++++++++------ cddl/sbin/zfsd/vdev.h | 38 +++++- cddl/sbin/zfsd/zfsd_exception.cc | 1 + 5 files changed, 332 insertions(+), 96 deletions(-) diff --git a/cddl/sbin/zfsd/case_file.cc b/cddl/sbin/zfsd/case_file.cc index 85d4ce2f7c9..35b6e74c2e0 100644 --- a/cddl/sbin/zfsd/case_file.cc +++ b/cddl/sbin/zfsd/case_file.cc @@ -233,27 +233,16 @@ bool CaseFile::RefreshVdevState() { ZpoolList zpl(ZpoolList::ZpoolByGUID, &m_poolGUID); - if (zpl.empty()) { - stringstream msg; - msg << "CaseFile::RefreshVdevState: Unknown pool for Vdev("; - msg << m_poolGUID << "," << m_vdevGUID << ")."; - syslog(LOG_INFO, "%s", msg.str().c_str()); - return (false); - } - - zpool_handle_t *casePool(zpl.front()); - nvlist_t *vdevConfig = VdevIterator(casePool).Find(VdevGUID()); - if (vdevConfig == NULL) { - stringstream msg; - syslog(LOG_INFO, - "CaseFile::RefreshVdevState: Unknown Vdev(%s,%s).\n", - PoolGUIDString().c_str(), PoolGUIDString().c_str()); + zpool_handle_t *casePool(zpl.empty() ? NULL : zpl.front()); + if (casePool == NULL) return (false); - } - Vdev caseVdev(casePool, vdevConfig); - m_vdevState = caseVdev.State(); - m_vdevPhysPath = caseVdev.PhysicalPath(); + Vdev vd(casePool, CaseVdev(casePool)); + if (vd.DoesNotExist()) + return (false); + + m_vdevState = vd.State(); + m_vdevPhysPath = vd.PhysicalPath(); return (true); } @@ -261,8 +250,9 @@ bool CaseFile::ReEvaluate(const string &devPath, const string &physPath, Vdev *vdev) { ZpoolList zpl(ZpoolList::ZpoolByGUID, &m_poolGUID); + zpool_handle_t *pool(zpl.empty() ? NULL : zpl.front()); - if (zpl.empty() || !RefreshVdevState()) { + if (pool == NULL || !RefreshVdevState()) { /* * The pool or vdev for this case file is no longer * part of the configuration. This can happen @@ -283,7 +273,6 @@ CaseFile::ReEvaluate(const string &devPath, const string &physPath, Vdev *vdev) */ return (/*consumed*/false); } - zpool_handle_t *pool(zpl.front()); if (VdevState() > VDEV_STATE_CANT_OPEN) { /* @@ -292,6 +281,8 @@ CaseFile::ReEvaluate(const string &devPath, const string &physPath, Vdev *vdev) * use a newly inserted spare to replace a degraded * or faulted device. */ + syslog(LOG_INFO, "CaseFile::ReEvaluate(%s,%s): Pool/Vdev ignored", + PoolGUIDString().c_str(), VdevGUIDString().c_str()); return (/*consumed*/false); } @@ -360,7 +351,10 @@ CaseFile::ReEvaluate(const string &devPath, const string &physPath, Vdev *vdev) return (/*consumed*/false); } - return (Replace(VDEV_TYPE_DISK, devPath.c_str())); + syslog(LOG_INFO, "CaseFile::ReEvaluate(%s/%s): Replacing with %s", + PoolGUIDString().c_str(), VdevGUIDString().c_str(), + devPath.c_str()); + return (Replace(VDEV_TYPE_DISK, devPath.c_str(), /*isspare*/false)); } bool @@ -378,6 +372,7 @@ CaseFile::ReEvaluate(const ZfsEvent &event) return (/*consumed*/true); } else if (event.Value("type") == "misc.fs.zfs.config_sync") { + RefreshVdevState(); if (VdevState() < VDEV_STATE_HEALTHY) consumed = ActivateSpare(); } @@ -460,27 +455,22 @@ CaseFile::ReEvaluate(const ZfsEvent &event) } -/* - * TODO: ensure that we don't activate a spare for a vdev that is already being - * replaced by another spare. - */ bool CaseFile::ActivateSpare() { nvlist_t *config, *nvroot; nvlist_t **spares; - zpool_handle_t *zhp; char *devPath, *vdev_type; const char *poolname; u_int nspares, i; int error; ZpoolList zpl(ZpoolList::ZpoolByGUID, &m_poolGUID); - if (zpl.empty()) { + zpool_handle_t *zhp(zpl.empty() ? NULL : zpl.front()); + if (zhp == NULL) { syslog(LOG_ERR, "CaseFile::ActivateSpare: Could not find pool " "for pool_guid %"PRIu64".", (uint64_t)m_poolGUID); return (false); } - zhp = zpl.front(); poolname = zpool_get_name(zhp); config = zpool_get_config(zhp, NULL); if (config == NULL) { @@ -545,7 +535,7 @@ CaseFile::ActivateSpare() { return (false); } - return (Replace(vdev_type, devPath)); + return (Replace(vdev_type, devPath, /*isspare*/true)); } void @@ -909,13 +899,15 @@ CaseFile::OnGracePeriodEnded() { bool should_fault, should_degrade; ZpoolList zpl(ZpoolList::ZpoolByGUID, &m_poolGUID); + zpool_handle_t *zhp(zpl.empty() ? NULL : zpl.front()); + m_events.splice(m_events.begin(), m_tentativeEvents); should_fault = ShouldFault(); should_degrade = ShouldDegrade(); if (should_fault || should_degrade) { - if (zpl.empty() - || (VdevIterator(zpl.front()).Find(m_vdevGUID)) == NULL) { + if (zhp == NULL + || (VdevIterator(zhp).Find(m_vdevGUID)) == NULL) { /* * Either the pool no longer exists * or this vdev is no longer a member of @@ -930,7 +922,7 @@ CaseFile::OnGracePeriodEnded() /* A fault condition has priority over a degrade condition */ if (ShouldFault()) { /* Fault the vdev and close the case. */ - if (zpool_vdev_fault(zpl.front(), (uint64_t)m_vdevGUID, + if (zpool_vdev_fault(zhp, (uint64_t)m_vdevGUID, VDEV_AUX_ERR_EXCEEDED) == 0) { syslog(LOG_INFO, "Faulting vdev(%s/%s)", PoolGUIDString().c_str(), @@ -948,7 +940,7 @@ CaseFile::OnGracePeriodEnded() } else if (ShouldDegrade()) { /* Degrade the vdev and close the case. */ - if (zpool_vdev_degrade(zpl.front(), (uint64_t)m_vdevGUID, + if (zpool_vdev_degrade(zhp, (uint64_t)m_vdevGUID, VDEV_AUX_ERR_EXCEEDED) == 0) { syslog(LOG_INFO, "Degrading vdev(%s/%s)", PoolGUIDString().c_str(), @@ -967,22 +959,86 @@ CaseFile::OnGracePeriodEnded() Serialize(); } +Vdev +CaseFile::BeingReplacedBy(zpool_handle_t *zhp) { + Vdev vd(zhp, CaseVdev(zhp)); + std::list children; + std::list::iterator children_it; + + Vdev parent(vd.Parent()); + Vdev replacing(NonexistentVdev); + + /* + * To determine whether we are being replaced by another spare that + * is still working, then make sure that it is currently spared and + * that the spare is either resilvering or healthy. If any of these + * conditions fail, then we are not being replaced by a spare. + * + * If the spare is healthy, then the case file should be closed very + * soon after this check. + */ + if (parent.DoesNotExist() + || parent.Name(zhp, /*verbose*/false) != "spare") + return (NonexistentVdev); + + children = parent.Children(); + children_it = children.begin(); + for (;children_it != children.end(); children_it++) { + Vdev child = *children_it; + + /* Skip our vdev. */ + if (child.GUID() == VdevGUID()) + continue; + /* + * Accept the first child that doesn't match our GUID, or + * any resilvering/healthy device if one exists. + */ + if (replacing.DoesNotExist() || child.IsResilvering() + || child.State() == VDEV_STATE_HEALTHY) + replacing = child; + } + + return (replacing); +} + bool -CaseFile::Replace(const char* vdev_type, const char* path) { +CaseFile::Replace(const char* vdev_type, const char* path, bool isspare) { nvlist_t *nvroot, *newvd; - zpool_handle_t *zhp; - const char* poolname; + const char *poolname; + string oldstr(VdevGUIDString()); bool retval = true; /* Figure out what pool we're working on */ ZpoolList zpl(ZpoolList::ZpoolByGUID, &m_poolGUID); - if (zpl.empty()) { + zpool_handle_t *zhp(zpl.empty() ? NULL : zpl.front()); + if (zhp == NULL) { syslog(LOG_ERR, "CaseFile::Replace: could not find pool for " "pool_guid %"PRIu64".", (uint64_t)m_poolGUID); return (false); } - zhp = zpl.front(); poolname = zpool_get_name(zhp); + Vdev vd(zhp, CaseVdev(zhp)); + Vdev replaced(BeingReplacedBy(zhp)); + + if (!vd.IsSpare() && !replaced.DoesNotExist()) { + /* If we are already being replaced by a working spare, pass. */ + if (replaced.IsResilvering() + || replaced.State() == VDEV_STATE_HEALTHY) { + syslog(LOG_INFO, "CaseFile::Replace(%s->%s): already " + "replaced", VdevGUIDString().c_str(), path); + return (/*consumed*/false); + } + /* + * If we have already been replaced by a spare, but that spare + * is broken, we must spare the spare, not the original device. + */ + if (isspare) { + oldstr = replaced.GUIDString(); + syslog(LOG_INFO, "CaseFile::Replace(%s->%s): sparing " + "broken spare %s instead", VdevGUIDString().c_str(), + path, oldstr.c_str()); + } + } /* * Build a root vdev/leaf vdev configuration suitable for @@ -994,9 +1050,8 @@ CaseFile::Replace(const char* vdev_type, const char* path) { if (nvlist_alloc(&nvroot, NV_UNIQUE_NAME, 0) != 0 || nvlist_alloc(&newvd, NV_UNIQUE_NAME, 0) != 0) { - syslog(LOG_ERR, "Replace vdev(%s/%s): " - "Unable to allocate configuration data.\n", - poolname, VdevGUIDString().c_str()); + syslog(LOG_ERR, "Replace vdev(%s/%s): Unable to allocate " + "configuration data.", poolname, oldstr.c_str()); if (nvroot != NULL) nvlist_free(nvroot); return (false); @@ -1006,9 +1061,8 @@ CaseFile::Replace(const char* vdev_type, const char* path) { || nvlist_add_string(nvroot, ZPOOL_CONFIG_TYPE, VDEV_TYPE_ROOT) != 0 || nvlist_add_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN, &newvd, 1) != 0) { - syslog(LOG_ERR, "Replace vdev(%s/%s): " - "Unable to initialize configuration data.\n", - poolname, VdevGUIDString().c_str()); + syslog(LOG_ERR, "Replace vdev(%s/%s): Unable to initialize " + "configuration data.", poolname, oldstr.c_str()); nvlist_free(newvd); nvlist_free(nvroot); return (true); @@ -1017,19 +1071,15 @@ CaseFile::Replace(const char* vdev_type, const char* path) { /* Data was copied when added to the root vdev. */ nvlist_free(newvd); - if (zpool_vdev_attach(zhp, VdevGUIDString().c_str(), - path, nvroot, /*replace*/B_TRUE) != 0) { - syslog(LOG_ERR, - "Replace vdev(%s/%s): %s: %s\n", - poolname, VdevGUIDString().c_str(), - libzfs_error_action(g_zfsHandle), - libzfs_error_description(g_zfsHandle)); - retval = false; - } else { + retval = (zpool_vdev_attach(zhp, oldstr.c_str(), path, nvroot, + /*replace*/B_TRUE) == 0); + if (retval) syslog(LOG_INFO, "Replacing vdev(%s/%s) with %s\n", - poolname, VdevGUIDString().c_str(), - path); - } + poolname, oldstr.c_str(), path); + else + syslog(LOG_ERR, "Replace vdev(%s/%s): %s: %s\n", + poolname, oldstr.c_str(), libzfs_error_action(g_zfsHandle), + libzfs_error_description(g_zfsHandle)); nvlist_free(nvroot); return (retval); @@ -1062,3 +1112,9 @@ CaseFile::ShouldFault() const return (std::count_if(m_events.begin(), m_events.end(), IsIOEvent) > ZFS_DEGRADE_IO_COUNT); } + +nvlist_t * +CaseFile::CaseVdev(zpool_handle_t *zhp) const +{ + return (VdevIterator(zhp).Find(VdevGUID())); +} diff --git a/cddl/sbin/zfsd/case_file.h b/cddl/sbin/zfsd/case_file.h index 489645cb85f..0a05a0688d3 100644 --- a/cddl/sbin/zfsd/case_file.h +++ b/cddl/sbin/zfsd/case_file.h @@ -326,10 +326,21 @@ protected: * \param vdev_type The type of the new vdev. Usually either * VDEV_TYPE_DISK or VDEV_TYPE_FILE * \param path The file system path to the new vdev + * \param isspare Whether the new vdev is a spare * * \return true iff the replacement was successful */ - bool Replace(const char* vdev_type, const char* path); + bool Replace(const char* vdev_type, const char* path, bool isspare); + + /** + * \brief Which vdev, if any, is replacing ours. + * + * \param zhp Pool handle state from the caller context + * + * \return the vdev that is currently replacing ours, + * or NonexistentVdev if there isn't one. + */ + Vdev BeingReplacedBy(zpool_handle_t *zhp); /** * \brief All CaseFiles being tracked by ZFSD. @@ -371,6 +382,9 @@ protected: * \brief Callout activated when a grace period */ Callout m_tentativeTimer; + +private: + nvlist_t *CaseVdev(zpool_handle_t *zhp) const; }; inline DevCtl::Guid diff --git a/cddl/sbin/zfsd/vdev.cc b/cddl/sbin/zfsd/vdev.cc index f05413b122b..3ac2c276738 100644 --- a/cddl/sbin/zfsd/vdev.cc +++ b/cddl/sbin/zfsd/vdev.cc @@ -37,6 +37,7 @@ * * Implementation of the Vdev class. */ +#include #include #include @@ -58,68 +59,76 @@ #include "vdev_iterator.h" #include "zfsd.h" #include "zfsd_exception.h" +#include "zpool_list.h" __FBSDID("$FreeBSD$"); /*============================ Namespace Control =============================*/ using std::string; using std::stringstream; +//- Special objects ----------------------------------------------------------- +Vdev NonexistentVdev; + +//- Vdev Inline Public Methods ------------------------------------------------ /*=========================== Class Implementations ==========================*/ /*----------------------------------- Vdev -----------------------------------*/ + +/* Special constructor for NonexistentVdev. */ +Vdev::Vdev() + : m_poolConfig(NULL), + m_config(NULL) +{} + +bool +Vdev::VdevLookupPoolGuid() +{ + uint64_t guid; + if (nvlist_lookup_uint64(m_poolConfig, ZPOOL_CONFIG_POOL_GUID, &guid)) + return (false); + m_poolGUID = guid; + return (true); +} + +void +Vdev::VdevLookupGuid() +{ + uint64_t guid; + if (nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_GUID, &guid) != 0) + throw ZfsdException("Unable to extract vdev GUID " + "from vdev config data."); + m_vdevGUID = guid; +} + Vdev::Vdev(zpool_handle_t *pool, nvlist_t *config) : m_poolConfig(zpool_get_config(pool, NULL)), m_config(config) { - uint64_t raw_guid; - if (nvlist_lookup_uint64(m_poolConfig, ZPOOL_CONFIG_POOL_GUID, - &raw_guid) != 0) - throw ZfsdException("Unable to extract pool GUID " - "from pool handle."); - m_poolGUID = raw_guid; - - if (nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_GUID, &raw_guid) != 0) - throw ZfsdException("Unable to extract vdev GUID " - "from vdev config data."); - m_vdevGUID = raw_guid; + if (!VdevLookupPoolGuid()) + throw ZfsdException("Can't extract pool GUID from handle."); + VdevLookupGuid(); } Vdev::Vdev(nvlist_t *poolConfig, nvlist_t *config) : m_poolConfig(poolConfig), m_config(config) { - uint64_t raw_guid; - if (nvlist_lookup_uint64(m_poolConfig, ZPOOL_CONFIG_POOL_GUID, - &raw_guid) != 0) - throw ZfsdException("Unable to extract pool GUID " - "from pool handle."); - m_poolGUID = raw_guid; - - if (nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_GUID, &raw_guid) != 0) - throw ZfsdException("Unable to extract vdev GUID " - "from vdev config data."); - m_vdevGUID = raw_guid; + if (!VdevLookupPoolGuid()) + throw ZfsdException("Can't extract pool GUID from config."); + VdevLookupGuid(); } Vdev::Vdev(nvlist_t *labelConfig) - : m_poolConfig(labelConfig) + : m_poolConfig(labelConfig), + m_config(labelConfig) { - uint64_t raw_guid; - /* * Spares do not have a Pool GUID. Tolerate its absence. * Code accessing this Vdev in a context where the Pool GUID is * required will find it invalid (as it is upon Vdev construction) * and act accordingly. */ - if (nvlist_lookup_uint64(labelConfig, ZPOOL_CONFIG_POOL_GUID, - &raw_guid) == 0) - m_poolGUID = raw_guid; - - if (nvlist_lookup_uint64(labelConfig, ZPOOL_CONFIG_GUID, - &raw_guid) != 0) - throw ZfsdException("Unable to extract vdev GUID " - "from vdev label data."); - m_vdevGUID = raw_guid; + (void) VdevLookupPoolGuid(); + VdevLookupGuid(); try { m_config = VdevIterator(labelConfig).Find(m_vdevGUID); @@ -172,6 +181,125 @@ Vdev::State() const return (VDEV_STATE_HEALTHY); } +std::list +Vdev::Children() +{ + nvlist_t **vdevChildren; + int result; + u_int numChildren; + std::list children; + + if (m_poolConfig == NULL || m_config == NULL) + return (children); + + result = nvlist_lookup_nvlist_array(m_config, + ZPOOL_CONFIG_CHILDREN, &vdevChildren, &numChildren); + if (result != 0) + return (children); + + for (u_int c = 0;c < numChildren; c++) + children.push_back(Vdev(m_poolConfig, vdevChildren[c])); + + return (children); +} + +Vdev +Vdev::RootVdev() +{ + nvlist_t *rootVdev; + + if (m_poolConfig == NULL) + return (NonexistentVdev); + + if (nvlist_lookup_nvlist(m_poolConfig, ZPOOL_CONFIG_VDEV_TREE, + &rootVdev) != 0) + return (NonexistentVdev); + return (Vdev(m_poolConfig, rootVdev)); +} + +/* + * Find our parent. This requires doing a traversal of the config; we can't + * cache it as leaf vdevs may change their pool config location (spare, + * replacing, mirror, etc). + */ +Vdev +Vdev::Parent() +{ + std::list to_examine; + std::list children; + std::list::iterator children_it; + + to_examine.push_back(RootVdev()); + for (;;) { + if (to_examine.empty()) + return (NonexistentVdev); + Vdev vd = to_examine.front(); + if (vd.DoesNotExist()) + return (NonexistentVdev); + to_examine.pop_front(); + children = vd.Children(); + children_it = children.begin(); + for (;children_it != children.end(); children_it++) { + Vdev child = *children_it; + + if (child.GUID() == GUID()) + return (vd); + to_examine.push_front(child); + } + } +} + +bool +Vdev::IsAvailableSpare() const +{ + /* If we have a pool guid, we cannot be an available spare. */ + if (PoolGUID()) + return (false); + + return (true); +} + +bool +Vdev::IsSpare() +{ + uint64_t spare; + if (nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_IS_SPARE, &spare) != 0) + return (false); + return (spare != 0); +} + +bool +Vdev::IsActiveSpare() const +{ + vdev_stat_t *vs; + uint_t c; + + if (m_poolConfig == NULL) + return (false); + + (void) nvlist_lookup_uint64_array(m_config, ZPOOL_CONFIG_VDEV_STATS, + reinterpret_cast(&vs), &c); + if (vs == NULL || vs->vs_aux != VDEV_AUX_SPARED) + return (false); + return (true); +} + +bool +Vdev::IsResilvering() const +{ + pool_scan_stat_t *ps = NULL; + uint_t c; + + if (State() != VDEV_STATE_HEALTHY) + return (false); + + (void) nvlist_lookup_uint64_array(m_config, ZPOOL_CONFIG_SCAN_STATS, + reinterpret_cast(&ps), &c); + if (ps == NULL || ps->pss_func != POOL_SCAN_RESILVER) + return (false); + return (true); +} + string Vdev::GUIDString() const { @@ -181,6 +309,13 @@ Vdev::GUIDString() const return (vdevGUIDString.str()); } +string +Vdev::Name(zpool_handle_t *zhp, bool verbose) const +{ + return (zpool_vdev_name(g_zfsHandle, zhp, m_config, + verbose ? B_TRUE : B_FALSE)); +} + string Vdev::Path() const { diff --git a/cddl/sbin/zfsd/vdev.h b/cddl/sbin/zfsd/vdev.h index 9c49b1a769f..69f7b4e7ab9 100644 --- a/cddl/sbin/zfsd/vdev.h +++ b/cddl/sbin/zfsd/vdev.h @@ -40,6 +40,7 @@ * Header requirements: * * #include + * #include * * #include */ @@ -103,6 +104,17 @@ public: */ Vdev(nvlist_t *vdevConfig); + /** + * \brief No-op copy constructor for nonexistent vdevs. + */ + Vdev(); + bool DoesNotExist() const; + + /** + * \brief Return a list of the vdev's children. + */ + std::list Children(); + virtual DevCtl::Guid GUID() const; virtual DevCtl::Guid PoolGUID() const; virtual vdev_state State() const; @@ -111,14 +123,26 @@ public: std::string GUIDString() const; nvlist_t *PoolConfig() const; nvlist_t *Config() const; + Vdev Parent(); + Vdev RootVdev(); + std::string Name(zpool_handle_t *, bool verbose) const; + bool IsSpare(); + bool IsAvailableSpare() const; + bool IsActiveSpare() const; + bool IsResilvering() const; private: - DevCtl::Guid m_poolGUID; - DevCtl::Guid m_vdevGUID; - nvlist_t *m_poolConfig; - nvlist_t *m_config; + void VdevLookupGuid(); + bool VdevLookupPoolGuid(); + DevCtl::Guid m_poolGUID; + DevCtl::Guid m_vdevGUID; + nvlist_t *m_poolConfig; + nvlist_t *m_config; }; +//- Special objects ----------------------------------------------------------- +extern Vdev NonexistentVdev; + //- Vdev Inline Public Methods ------------------------------------------------ inline DevCtl::Guid Vdev::PoolGUID() const @@ -144,4 +168,10 @@ Vdev::Config() const return (m_config); } +inline bool +Vdev::DoesNotExist() const +{ + return (m_config == NULL); +} + #endif /* _VDEV_H_ */ diff --git a/cddl/sbin/zfsd/zfsd_exception.cc b/cddl/sbin/zfsd/zfsd_exception.cc index 1aff8a3576b..0bc009d093d 100644 --- a/cddl/sbin/zfsd/zfsd_exception.cc +++ b/cddl/sbin/zfsd/zfsd_exception.cc @@ -41,6 +41,7 @@ #include #include +#include #include #include From bd98904640a474cb5dfbefb7079da77e517444b1 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Mon, 14 Oct 2013 23:36:10 +0000 Subject: [PATCH 79/89] Zfsd should deactivate a spare if the original device is replaced while the system is powered off. cddl/sbin/zfsd/case_file.cc cddl/sbin/zfsd/vdev.cc cddl/sbin/zfsd/vdev.h cddl/sbin/zfsd/vdev_iterator.h cddl/sbin/zfsd/zfsd.cc cddl/sbin/zfsd/zfsd_event.cc cddl/sbin/zfsd/zfsd_event.h When zfsd receives a misc.fs.zfs.resilver_finish event, it will iterate through all spare leaf vdevs on the pool. It will detach any that appear to be unnecessary. Submitted by: alans Approved by: ken (mentor) Sponsored by: Spectra Logic Corporation --- cddl/sbin/zfsd/case_file.cc | 2 +- cddl/sbin/zfsd/vdev.cc | 12 ++++++ cddl/sbin/zfsd/vdev.h | 1 + cddl/sbin/zfsd/vdev_iterator.h | 4 +- cddl/sbin/zfsd/zfsd.cc | 1 + cddl/sbin/zfsd/zfsd_event.cc | 74 +++++++++++++++++++++++++++++++++- cddl/sbin/zfsd/zfsd_event.h | 6 +++ 7 files changed, 96 insertions(+), 4 deletions(-) diff --git a/cddl/sbin/zfsd/case_file.cc b/cddl/sbin/zfsd/case_file.cc index 35b6e74c2e0..13bf92d458c 100644 --- a/cddl/sbin/zfsd/case_file.cc +++ b/cddl/sbin/zfsd/case_file.cc @@ -66,10 +66,10 @@ #include #include "callout.h" +#include "vdev_iterator.h" #include "zfsd_event.h" #include "case_file.h" #include "vdev.h" -#include "vdev_iterator.h" #include "zfsd.h" #include "zfsd_exception.h" #include "zpool_list.h" diff --git a/cddl/sbin/zfsd/vdev.cc b/cddl/sbin/zfsd/vdev.cc index 3ac2c276738..cbf668966c4 100644 --- a/cddl/sbin/zfsd/vdev.cc +++ b/cddl/sbin/zfsd/vdev.cc @@ -141,6 +141,18 @@ Vdev::Vdev(nvlist_t *labelConfig) } } +bool +Vdev::IsSpare() const +{ + uint64_t is_spare(0); + + if (m_config == NULL) + return (false); + + (void)nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_IS_SPARE, &is_spare); + return (bool(is_spare)); +} + vdev_state Vdev::State() const { diff --git a/cddl/sbin/zfsd/vdev.h b/cddl/sbin/zfsd/vdev.h index 69f7b4e7ab9..aabdcbf84f7 100644 --- a/cddl/sbin/zfsd/vdev.h +++ b/cddl/sbin/zfsd/vdev.h @@ -116,6 +116,7 @@ public: std::list Children(); virtual DevCtl::Guid GUID() const; + bool IsSpare() const; virtual DevCtl::Guid PoolGUID() const; virtual vdev_state State() const; std::string Path() const; diff --git a/cddl/sbin/zfsd/vdev_iterator.h b/cddl/sbin/zfsd/vdev_iterator.h index 398d0aec8e9..377772aa75e 100644 --- a/cddl/sbin/zfsd/vdev_iterator.h +++ b/cddl/sbin/zfsd/vdev_iterator.h @@ -84,8 +84,8 @@ public: void Reset(); /** - * \brief Report the vdev at this iterator's cursor and increment - * the cursor to the next pool member. + * \brief Report the leaf vdev at this iterator's cursor and increment + * the cursor to the next leaf pool member. */ nvlist_t *Next(); diff --git a/cddl/sbin/zfsd/zfsd.cc b/cddl/sbin/zfsd/zfsd.cc index 27b0ccd73e6..b16ecfb46c8 100644 --- a/cddl/sbin/zfsd/zfsd.cc +++ b/cddl/sbin/zfsd/zfsd.cc @@ -66,6 +66,7 @@ #include #include "callout.h" +#include "vdev_iterator.h" #include "zfsd_event.h" #include "case_file.h" #include "vdev.h" diff --git a/cddl/sbin/zfsd/zfsd_event.cc b/cddl/sbin/zfsd/zfsd_event.cc index 762368cf4ac..69bc9eb1275 100644 --- a/cddl/sbin/zfsd/zfsd_event.cc +++ b/cddl/sbin/zfsd/zfsd_event.cc @@ -54,10 +54,10 @@ #include #include "callout.h" +#include "vdev_iterator.h" #include "zfsd_event.h" #include "case_file.h" #include "vdev.h" -#include "vdev_iterator.h" #include "zfsd.h" #include "zfsd_exception.h" #include "zpool_list.h" @@ -346,6 +346,26 @@ ZfsEvent::ZfsEvent(const ZfsEvent &src) { } +/* + * Sometimes the kernel won't detach a spare when it is no longer needed. This + * can happen for example if a drive is removed, then either the pool is + * exported or the machine is powered off, then the drive is reinserted, then + * the machine is powered on or the pool is imported. ZFSD must detach these + * spares itself. + */ +void +ZfsEvent::CleanupSpares() const +{ + Guid poolGUID(PoolGUID()); + ZpoolList zpl(ZpoolList::ZpoolByGUID, &poolGUID); + if (!zpl.empty()) { + zpool_handle_t* hdl; + + hdl = zpl.front(); + VdevIterator(hdl).Each(TryDetach, (void*)hdl); + } +} + void ZfsEvent::ProcessPoolEvent() const { @@ -359,6 +379,15 @@ ZfsEvent::ProcessPoolEvent() const caseFile->ReEvaluate(*this); } + else if (Value("type") == "misc.fs.zfs.resilver_finish") + { + /* + * It's possible to get a resilver_finish event with no + * corresponding casefile. For example, if a damaged pool were + * exported, repaired, then reimported. + */ + CleanupSpares(); + } if (Value("type") == "misc.fs.zfs.vdev_remove" && degradedDevice == false) { @@ -366,3 +395,46 @@ ZfsEvent::ProcessPoolEvent() const ZfsDaemon::RequestSystemRescan(); } } + +bool +ZfsEvent::TryDetach(Vdev &vdev, void *cbArg) +{ + /* + * Outline: + * if this device is a spare, and its parent includes one healthy, + * non-spare child, then detach this device. + */ + zpool_handle_t *hdl(static_cast(cbArg)); + + if (vdev.IsSpare()) { + std::list siblings; + std::list::iterator siblings_it; + boolean_t cleanup = B_FALSE; + + Vdev parent = vdev.Parent(); + siblings = parent.Children(); + + /* Determine whether the parent should be cleaned up */ + for (siblings_it = siblings.begin(); + siblings_it != siblings.end(); + siblings_it++) { + Vdev sibling = *siblings_it; + + if (!sibling.IsSpare() && + sibling.State() == VDEV_STATE_HEALTHY) { + cleanup = B_TRUE; + break; + } + } + + if (cleanup) { + syslog(LOG_INFO, "Detaching spare vdev %s from pool %s", + vdev.Path().c_str(), zpool_get_name(hdl)); + zpool_vdev_detach(hdl, vdev.Path().c_str()); + } + + } + + /* Always return false, because there may be other spares to detach */ + return (false); +} diff --git a/cddl/sbin/zfsd/zfsd_event.h b/cddl/sbin/zfsd/zfsd_event.h index dfd405a583f..f2f7967ae7a 100644 --- a/cddl/sbin/zfsd/zfsd_event.h +++ b/cddl/sbin/zfsd/zfsd_event.h @@ -140,7 +140,13 @@ protected: /** Constructor */ ZfsEvent(Type, DevCtl::NVPairMap &, const string &); + /** + * Detach any spares that are no longer needed, but were not + * automatically detached by the kernel + */ + virtual void CleanupSpares() const; virtual void ProcessPoolEvent() const; + static VdevCallback_t TryDetach; }; #endif /*_ZFSD_EVENT_H_ */ From c3751ef3f17ca6d47dc4d4ff3e7bc74d770f4454 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Mon, 14 Oct 2013 23:41:55 +0000 Subject: [PATCH 80/89] Fix a bug in zfsd: zfsd will fail to autoreplace by physical path a drive if it had previously replaced that same drive when it belonged to a different pool. The solution is to delete case files when their pools are destroyed. A better fix would be to refactor zfsd to eliminate casefiles altogether, and use a more stateless approach. cddl/sbin/zfsd/case_file.cc cddl/sbin/zfsd/zfsd_event.cc Close a case file when its pool is destroyed. Log a warning when two open case files correspond to the same physical path. Submitted by: alans Approved by: ken (mentor) Sponsored by: Spectra Logic Corporation --- cddl/sbin/zfsd/case_file.cc | 20 ++++++++++++++++---- cddl/sbin/zfsd/case_file.h | 2 +- cddl/sbin/zfsd/zfsd_event.cc | 10 ++++++++++ 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/cddl/sbin/zfsd/case_file.cc b/cddl/sbin/zfsd/case_file.cc index 13bf92d458c..dbba13ab9fc 100644 --- a/cddl/sbin/zfsd/case_file.cc +++ b/cddl/sbin/zfsd/case_file.cc @@ -150,15 +150,23 @@ CaseFile::Find(Guid poolGUID, Guid vdevGUID) CaseFile * CaseFile::Find(const string &physPath) { + CaseFile *result = NULL; + for (CaseFileList::iterator curCase = s_activeCases.begin(); curCase != s_activeCases.end(); curCase++) { if ((*curCase)->PhysicalPath() != physPath) continue; - return (*curCase); + if (result != NULL) { + syslog(LOG_WARNING, "Multiple casefiles found for " + "physical path %s. " + "This is most likely a bug in zfsd", + physPath.c_str()); + } + result = *curCase; } - return (NULL); + return (result); } @@ -370,8 +378,12 @@ CaseFile::ReEvaluate(const ZfsEvent &event) Close(); return (/*consumed*/true); - } - else if (event.Value("type") == "misc.fs.zfs.config_sync") { + } else if (event.Value("type") == "misc.fs.zfs.pool_destroy") { + /* This Pool has been destroyed. Discard the case */ + Close(); + + return (/*consumed*/true); + } else if (event.Value("type") == "misc.fs.zfs.config_sync") { RefreshVdevState(); if (VdevState() < VDEV_STATE_HEALTHY) consumed = ActivateSpare(); diff --git a/cddl/sbin/zfsd/case_file.h b/cddl/sbin/zfsd/case_file.h index 0a05a0688d3..021b34a6830 100644 --- a/cddl/sbin/zfsd/case_file.h +++ b/cddl/sbin/zfsd/case_file.h @@ -296,7 +296,7 @@ protected: /** * \brief Unconditionally close a CaseFile. */ - void Close(); + virtual void Close(); /** * \brief Callout callback invoked when the remove timer grace diff --git a/cddl/sbin/zfsd/zfsd_event.cc b/cddl/sbin/zfsd/zfsd_event.cc index 69bc9eb1275..0d455a70927 100644 --- a/cddl/sbin/zfsd/zfsd_event.cc +++ b/cddl/sbin/zfsd/zfsd_event.cc @@ -195,6 +195,10 @@ DevfsEvent::Process() const "as a replace by physical path candidate.\n", devName.c_str()); } else if (havePhysPath && IsWholeDev()) { + /* + * TODO: attempt to resolve events using every casefile + * that matches this physpath + */ CaseFile *caseFile(CaseFile::Find(physPath)); if (caseFile != NULL) { syslog(LOG_INFO, @@ -371,6 +375,12 @@ ZfsEvent::ProcessPoolEvent() const { bool degradedDevice(false); + /* The pool is destroyed. Discard any open cases */ + if (Value("type") == "misc.fs.zfs.pool_destroy") { + CaseFile::ReEvaluateByGuid(PoolGUID(), *this); + return; + } + CaseFile *caseFile(CaseFile::Find(PoolGUID(), VdevGUID())); if (caseFile != NULL) { if (caseFile->VdevState() != VDEV_STATE_UNKNOWN From ee74a0c3cb12b5239e7e1decc3e99a6f63dc4c13 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Mon, 14 Oct 2013 23:43:30 +0000 Subject: [PATCH 81/89] sbin/zfsd/Makefile.common Add -lzfs_core for zfsd, because libzfs now requires it. Submitted by: will Approved by: ken (mentor) Sponsored by: Spectra Logic Corporation --- cddl/sbin/zfsd/Makefile.common | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cddl/sbin/zfsd/Makefile.common b/cddl/sbin/zfsd/Makefile.common index 65fecb5c5f2..e6ee4a46dd5 100644 --- a/cddl/sbin/zfsd/Makefile.common +++ b/cddl/sbin/zfsd/Makefile.common @@ -33,9 +33,9 @@ INCFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common/sys CFLAGS= -g -DNEED_SOLARIS_BOOLEAN ${INCFLAGS} -DPADD= ${LIBDEVCTL} ${LIBZFS} ${LIBUTIL} ${LIBGEOM} ${LIBBSDXML} ${LIBSBUF} \ - ${LIBNVPAIR} ${LIBUUTIL} -LDADD= -ldevctl -lzfs -lutil -lgeom -lbsdxml -lsbuf -lnvpair -luutil +DPADD= ${LIBDEVCTL} ${LIBZFS} ${LIBZFS_CORE} ${LIBUTIL} ${LIBGEOM} \ + ${LIBBSDXML} ${LIBSBUF} ${LIBNVPAIR} ${LIBUUTIL} +LDADD= -ldevctl -lzfs -lzfs_core -lutil -lgeom -lbsdxml -lsbuf -lnvpair -luutil cscope: find ${.CURDIR} -type f -a \( -name "*.[ch]" -o -name "*.cc" \) \ From a9a6b2894e1cca8c0719177ebdea57cba3ade413 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Mon, 14 Oct 2013 23:56:38 +0000 Subject: [PATCH 82/89] Emit many more ZFS events to devctl, especially for zfs(8) commands. The intention is to make it easier for a devctl consumer (such as zfsd or the zfs worker) to receive events for commands like zfs create/snapshot/destroy/clone/promote. Instead of polling "zfs list", such consumers can now receive notifications when the requested operations complete. While I'm here, make devctl/libdevctl support key/value strings that have spaces in them. Some of the key/value pairs in these events contain strings that screw up rendering & parsing. It seems more appropriate to pass on strings as they were provided than to require all emitters to adopt a "no spaces" rule. The logging format is open to modification/tweaking. Example event logs from zfsd: [root@ntier-verde ~]# zfs create tpool/foo Aug 2 20:46:35 ntier-verde zfsd: ZFS: Notify "dsid"="40" "dsname"="tpool/foo" "history internal str"="" "history txg"="90" "internal_name"="create" "pool_guid"="83985999112536669" "pool_name"="tpool" "subsystem"="ZFS" "timestamp"="1375476395" "type"="misc.fs.zfs.pool_history" [root@ntier-verde ~]# zfs create tpool/foo/0 Aug 2 20:46:36 ntier-verde zfsd: ZFS: Notify "dsid"="47" "dsname"="tpool/foo/0" "history internal str"="" "history txg"="91" "internal_name"="create" "pool_guid"="83985999112386669" "pool_name"="tpool" "subsystem"="ZFS" "timestamp"="1375476396" "type"="misc.fs.zfs.pool_history" [root@ntier-verde ~]# zfs snapshot tpool/foo/0@0 Aug 2 20:46:41 ntier-verde zfsd: ZFS: Notify "dsid"="50" "dsname"="tpool/foo/0@0" "history internal str"="" "history txg"="92" "internal_name"="snapshot" "pool_guid"="83985991253386669" "pool_name"="tpool" "subsystem"="ZFS" "timestamp"="1375476401" "type"="misc.fs.zfs.pool_history" [root@ntier-verde ~]# zfs destroy -r tpool/foo Aug 2 20:46:46 ntier-verde zfsd: ZFS: Notify "dsid"="50" "dsname"="tpool/foo/0@0" "history internal str"="" "history txg"="94" "internal_name"="destroy" "pool_guid"="83985999253386669" "pool_name"="tpool" "subsystem"="ZFS" "timestamp"="1375476406" "type"="misc.fs.zfs.pool_history" Aug 2 20:46:46 ntier-verde zfsd: ZFS: Notify "dsid"="47" "dsname"="tpool/foo/0" "history internal str"="" "history txg"="95" "internal_name"="destroy" "pool_guid"="83985999113386669" "pool_name"="tpool" "subsystem"="ZFS" "timestamp"="1375476406" "type"="misc.fs.zfs.pool_history" Aug 2 20:46:46 ntier-verde zfsd: ZFS: Notify "dsid"="40" "dsname"="tpool/foo" "history internal str"="" "history txg"="97" "internal_name"="destroy" "pool_guid"="83985999112586669" "pool_name"="tpool" "subsystem"="ZFS" "timestamp"="1375476406" "type"="misc.fs.zfs.pool_history" lib/libdevctl/event.cc: sys/cddl/compat/opensolaris/kern/opensolaris_sysevent.c: - Add a rudimentary mechanism for serializing and deserializing spaces in strings representing keys and their values, by converting spaces to %20, and %s to %%s, and vice versa. lib/libdevctl/event.cc: - Add normalize_string(), which deserializes inbound key and value strings, acting as a substitute for string.substr(), as it is used in DevCtl::Event::ParseEventString(). - DevCtl::Event::Log(): Quote key and value strings. sys/cddl/compat/opensolaris/kern/opensolaris_sysevent.c: - Add a common routine, log_sysevent_add_string(), which performs serialization of spaces and %s in strings. - Add log_sysevent_add_elem(), which uses log_sysevent_add_string() to provide the " =" part of the string. sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c: - spa_create(): Change the "create" history log generated here to "pool create", so it can be more easily distinguished from "create", which means "filesystem create". sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa.h: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_fm.c: sys/cddl/contrib/opensolaris/uts/common/sys/sysevent/eventdefs.h: - Add zfs_ereport_spa_history(), a new ZFS event call which allows emitting events for pool history logging. It takes a nvlist of metadata representing the history log, temporarily appends the pool name and guid, then forwards it on to ddi_log_sysevent(). The FreeBSD implementation of ddi_log_sysevent() then serializes the event and forwards the serialized string to devctl_notify(). sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_history.c: - log_internal(): When logging internal events, once the nvlist has been fully populated with data, call zfs_ereport_spa_history(). lib/libdevctl/consumer.cc - Demote ConnectToDevd() "Already connected." syslog from INFO to DEBUG. This function gets called by the devctl ruby gem whenever it polls devctl, resulting in useless log spamming in production. Submitted by: will Approved by: ken (mentor) Sponsored by: Spectra Logic Corporation --- lib/libdevctl/consumer.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/libdevctl/consumer.cc b/lib/libdevctl/consumer.cc index 4dc3a3eea9d..6040047990b 100644 --- a/lib/libdevctl/consumer.cc +++ b/lib/libdevctl/consumer.cc @@ -100,7 +100,7 @@ Consumer::ConnectToDevd() if (m_devdSockFD != -1) { /* Already connected. */ - syslog(LOG_INFO, "%s: Already connected.", __func__); + syslog(LOG_DEBUG, "%s: Already connected.", __func__); return (true); } syslog(LOG_INFO, "%s: Connecting to devd.", __func__); From daa02b1036dd0fa3553af0961feb210e0a340735 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Mon, 14 Oct 2013 23:59:10 +0000 Subject: [PATCH 83/89] Reduce zfsd logging. cddl/sbin/zfsd/zfsd_event.cc: Only syslog events for which zfsd takes some action. This preserves the data necessary for diagnosis of customer issues without filling the logs with every ZFS event type the kernel may issue (e.g. scrub status). Submitted by: gibbs Approved by: ken (mentor) Sponsored by: Spectra Logic Corporation --- cddl/sbin/zfsd/zfsd_event.cc | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/cddl/sbin/zfsd/zfsd_event.cc b/cddl/sbin/zfsd/zfsd_event.cc index 0d455a70927..5be4ee678cd 100644 --- a/cddl/sbin/zfsd/zfsd_event.cc +++ b/cddl/sbin/zfsd/zfsd_event.cc @@ -266,8 +266,6 @@ ZfsEvent::Process() const CaseFile::ReEvaluateByGuid(PoolGUID(), *this); } - Log(LOG_INFO); - if (Value("type").find("misc.fs.zfs.") == 0) { /* Configuration changes, resilver events, etc. */ ProcessPoolEvent(); @@ -281,6 +279,7 @@ ZfsEvent::Process() const CaseFile *caseFile(CaseFile::Find(PoolGUID(), VdevGUID())); if (caseFile != NULL) { + Log(LOG_INFO); syslog(LOG_INFO, "Evaluating existing case file\n"); caseFile->ReEvaluate(*this); return (false); @@ -293,6 +292,7 @@ ZfsEvent::Process() const stringstream msg; msg << "No replicas available for pool " << poolGUID; msg << ", ignoring"; + Log(LOG_INFO); syslog(LOG_INFO, "%s", msg.str().c_str()); return (false); } @@ -308,6 +308,7 @@ ZfsEvent::Process() const msg << "ZfsEvent::Process: Event for unknown pool "; msg << poolGUID << " "; msg << "queued"; + Log(LOG_INFO); syslog(priority, "%s", msg.str().c_str()); return (true); } @@ -319,19 +320,21 @@ ZfsEvent::Process() const msg << "ZfsEvent::Process: Event for unknown vdev "; msg << VdevGUID() << " "; msg << "queued"; + Log(LOG_INFO); syslog(priority, "%s", msg.str().c_str()); return (true); } Vdev vdev(zpl.front(), vdevConfig); caseFile = &CaseFile::Create(vdev); - if ( caseFile->ReEvaluate(*this) == false) { + if (caseFile->ReEvaluate(*this) == false) { stringstream msg; int priority = LOG_INFO; msg << "ZfsEvent::Process: Unconsumed event for vdev("; msg << zpool_get_name(zpl.front()) << ","; msg << vdev.GUID() << ") "; msg << "queued"; + Log(LOG_INFO); syslog(priority, "%s", msg.str().c_str()); return (true); } @@ -377,6 +380,7 @@ ZfsEvent::ProcessPoolEvent() const /* The pool is destroyed. Discard any open cases */ if (Value("type") == "misc.fs.zfs.pool_destroy") { + Log(LOG_INFO); CaseFile::ReEvaluateByGuid(PoolGUID(), *this); return; } @@ -387,6 +391,7 @@ ZfsEvent::ProcessPoolEvent() const && caseFile->VdevState() < VDEV_STATE_HEALTHY) degradedDevice = true; + Log(LOG_INFO); caseFile->ReEvaluate(*this); } else if (Value("type") == "misc.fs.zfs.resilver_finish") @@ -396,12 +401,15 @@ ZfsEvent::ProcessPoolEvent() const * corresponding casefile. For example, if a damaged pool were * exported, repaired, then reimported. */ + Log(LOG_INFO); CleanupSpares(); } if (Value("type") == "misc.fs.zfs.vdev_remove" && degradedDevice == false) { + /* See if any other cases can make use of this device. */ + Log(LOG_INFO); ZfsDaemon::RequestSystemRescan(); } } From 0ec3dc36e5bdcd84825f7ee5bb3df41b0dbdb471 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Tue, 15 Oct 2013 00:09:22 +0000 Subject: [PATCH 84/89] etc/rc.d/zfsd Remove set_rcvar, in accordance with rev 230103. Submitted by: will Approved by: ken (mentor) Sponsored by: Spectra Logic Corporation --- etc/rc.d/zfsd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/rc.d/zfsd b/etc/rc.d/zfsd index 3167a22505b..3ea64f380cc 100644 --- a/etc/rc.d/zfsd +++ b/etc/rc.d/zfsd @@ -10,7 +10,7 @@ . /etc/rc.subr name="zfsd" -rcvar=`set_rcvar` +rcvar="zfsd_enable" command="/sbin/${name}" load_rc_config $name From 87f040edd76e492465c4b912027c11a4e07cc9a9 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Fri, 18 Oct 2013 17:35:51 +0000 Subject: [PATCH 85/89] cddl/sbin/zfsd/callout.cc lib/libdevctl/consumer.cc lib/libdevctl/event.cc lib/libdevctl/event_factory.cc Include headers that are implicitly included by gcc and libstdc++ but must be explicitly included when using clang and libc++. Submitted by: alans Approved by: ken (mentor) Sponsored by: Spectra Logic Corporation --- cddl/sbin/zfsd/callout.cc | 1 + lib/libdevctl/consumer.cc | 2 ++ lib/libdevctl/event.cc | 1 + lib/libdevctl/event_factory.cc | 1 + 4 files changed, 5 insertions(+) diff --git a/cddl/sbin/zfsd/callout.cc b/cddl/sbin/zfsd/callout.cc index 1f1735cdcb3..0ab75582de1 100644 --- a/cddl/sbin/zfsd/callout.cc +++ b/cddl/sbin/zfsd/callout.cc @@ -42,6 +42,7 @@ #include #include +#include #include #include #include diff --git a/lib/libdevctl/consumer.cc b/lib/libdevctl/consumer.cc index 6040047990b..fe014b32852 100644 --- a/lib/libdevctl/consumer.cc +++ b/lib/libdevctl/consumer.cc @@ -37,11 +37,13 @@ #include #include #include +#include #include #include #include #include +#include #include #include diff --git a/lib/libdevctl/event.cc b/lib/libdevctl/event.cc index d7e61f1d2f2..38a0c2c1e19 100644 --- a/lib/libdevctl/event.cc +++ b/lib/libdevctl/event.cc @@ -44,6 +44,7 @@ #include #include #include +#include #include #include diff --git a/lib/libdevctl/event_factory.cc b/lib/libdevctl/event_factory.cc index 229233d3e89..364e0d09687 100644 --- a/lib/libdevctl/event_factory.cc +++ b/lib/libdevctl/event_factory.cc @@ -34,6 +34,7 @@ * \file event_factory.cc */ #include +#include #include #include From 02d28bcf093e40d6e5b6b714630f83104acc265f Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Fri, 18 Oct 2013 17:43:48 +0000 Subject: [PATCH 86/89] Add the zfsd unit tests. They require googletest and googlemock from ports. cddl/sbin/zfsd/tests cddl/sbin/zfsd/tests/libmocks.c cddl/sbin/zfsd/tests/zfsd_unittest.cc cddl/sbin/zfsd/tests/libmocks.h cddl/sbin/zfsd/tests/Makefile Add the zfsd unit tests. cddl/sbin/zfsd/tests/zfsd_unittest.supp Add a valgrind suppression file. cddl/sbin/zfsd/tests/zfsd_test.sh An ATF test program that runs the unit tests cddl/sbin/zfsd/Makefile.common Modify Makefile.common so that it can be used either to build zfsd or zfsd's unit tests. cddl/sbin/zfsd/Makefile Don't descend into the tests directory if googletest and googlemock are not installed, even if MK_TESTS=yes Submitted by: asomers Approved by: ken (mentor) Sponsored by: Spectra Logic Corporation --- cddl/sbin/zfsd/Makefile | 8 + cddl/sbin/zfsd/Makefile.common | 26 +- cddl/sbin/zfsd/tests/Makefile | 83 +++ cddl/sbin/zfsd/tests/libmocks.c | 24 + cddl/sbin/zfsd/tests/libmocks.h | 24 + cddl/sbin/zfsd/tests/zfsd_test.sh | 52 ++ cddl/sbin/zfsd/tests/zfsd_unittest.cc | 778 ++++++++++++++++++++++++ cddl/sbin/zfsd/tests/zfsd_unittest.supp | 104 ++++ 8 files changed, 1086 insertions(+), 13 deletions(-) create mode 100644 cddl/sbin/zfsd/tests/Makefile create mode 100644 cddl/sbin/zfsd/tests/libmocks.c create mode 100644 cddl/sbin/zfsd/tests/libmocks.h create mode 100644 cddl/sbin/zfsd/tests/zfsd_test.sh create mode 100644 cddl/sbin/zfsd/tests/zfsd_unittest.cc create mode 100644 cddl/sbin/zfsd/tests/zfsd_unittest.supp diff --git a/cddl/sbin/zfsd/Makefile b/cddl/sbin/zfsd/Makefile index 58caefb77a0..466ddef0ef9 100644 --- a/cddl/sbin/zfsd/Makefile +++ b/cddl/sbin/zfsd/Makefile @@ -1,7 +1,15 @@ # $FreeBSD$ +SRCDIR=${.CURDIR}/../../.. .include "Makefile.common" PROG_CXX= zfsd .include + +# Check for the existence of the googletest and googlemock header files, which +# come from ports. Don't compile the tests without them. +.if exists(${LOCALBASE}/include/gtest/gtest.h) && exists(${LOCALBASE}/include/gmock/gmock.h) +.else +SUBDIR= +.endif diff --git a/cddl/sbin/zfsd/Makefile.common b/cddl/sbin/zfsd/Makefile.common index e6ee4a46dd5..4c161c17e1d 100644 --- a/cddl/sbin/zfsd/Makefile.common +++ b/cddl/sbin/zfsd/Makefile.common @@ -17,19 +17,19 @@ WARNS?= 3 # Ignore warnings about Solaris specific pragmas. IGNORE_PRAGMA= YES -INCFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/lib/libzpool/common -INCFLAGS+= -I${.CURDIR}/../../../cddl/compat/opensolaris/include -INCFLAGS+= -I${.CURDIR}/../../../cddl/compat/opensolaris/lib/libumem -INCFLAGS+= -I${.CURDIR}/../../../sys/cddl/compat/opensolaris -INCFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/head -INCFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/lib/libuutil/common -INCFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/lib/libumem/common -INCFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/lib/libzfs/common -INCFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/lib/libnvpair -INCFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/common/zfs -INCFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common -INCFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common/fs/zfs -INCFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common/sys +INCFLAGS+= -I${SRCDIR}/cddl/contrib/opensolaris/lib/libzpool/common +INCFLAGS+= -I${SRCDIR}/cddl/compat/opensolaris/include +INCFLAGS+= -I${SRCDIR}/cddl/compat/opensolaris/lib/libumem +INCFLAGS+= -I${SRCDIR}/sys/cddl/compat/opensolaris +INCFLAGS+= -I${SRCDIR}/cddl/contrib/opensolaris/head +INCFLAGS+= -I${SRCDIR}/cddl/contrib/opensolaris/lib/libuutil/common +INCFLAGS+= -I${SRCDIR}/cddl/contrib/opensolaris/lib/libumem/common +INCFLAGS+= -I${SRCDIR}/cddl/contrib/opensolaris/lib/libzfs/common +INCFLAGS+= -I${SRCDIR}/cddl/contrib/opensolaris/lib/libnvpair +INCFLAGS+= -I${SRCDIR}/sys/cddl/contrib/opensolaris/common/zfs +INCFLAGS+= -I${SRCDIR}/sys/cddl/contrib/opensolaris/uts/common +INCFLAGS+= -I${SRCDIR}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs +INCFLAGS+= -I${SRCDIR}/sys/cddl/contrib/opensolaris/uts/common/sys CFLAGS= -g -DNEED_SOLARIS_BOOLEAN ${INCFLAGS} diff --git a/cddl/sbin/zfsd/tests/Makefile b/cddl/sbin/zfsd/tests/Makefile new file mode 100644 index 00000000000..3a1aa2baf5e --- /dev/null +++ b/cddl/sbin/zfsd/tests/Makefile @@ -0,0 +1,83 @@ +# $FreeBSD$ + +SRCDIR=${.CURDIR}/../../../.. +.include "${.CURDIR}/../Makefile.common" +.PATH: ${.CURDIR}/.. + +PROG_CXX= zfsd_unittest +SRCS:= ${SRCS:Nzfsd_main.cc} +SRCS+= libmocks.c zfsd_unittest.cc +CLEANFILES+= *.gcno *.gcda *.info +CLEANDIRS+= lcov-report + +# Use #include in test programs. +INCFLAGS+= -I ${.CURDIR}/../.. + +.if defined(DESTDIR) +INCFLAGS+= -I ${DESTDIR}/usr/include +LIBRARY_PATH= ${DESTDIR}/lib:${DESTDIR}/usr/lib +LDFLAGS+= -L ${DESTDIR}/lib -L ${DESTDIR}/usr/lib +.elif defined(WORLDTMP) +INCFLAGS+= -I ${WORLDTMP}/usr/include +LIBRARY_PATH= ${WORLDTMP}/lib:${WORLDTMP}/usr/lib +LDFLAGS+= -L ${WORLDTMP}/lib -L ${WORLDTMP}/usr/lib +.else +LIBRARY_PATH= +.endif +ZFSD_UNITTEST= env LD_LIBRARY_PATH=${LIBRARY_PATH} ./zfsd_unittest + +# Extra tools +LCOV= lcov + +# Googletest options +LOCALBASE?= /usr/local +INCFLAGS+= -I ${LOCALBASE}/include -D_THREAD_SAFE -pthread +LDFLAGS+= -L ${LOCALBASE}/lib -D_THREAD_SAFE -pthread +LDADD+= ${LOCALBASE}/lib/libgtest.a + +# GoogleMock options +LDADD+= ${LOCALBASE}/lib/libgmock.a ${LOCALBASE}/lib/libgmock_main.a + +# Googlemock fails if we don't have this line +# https://groups.google.com/forum/#!msg/googletestframework/h8ixEPCFm0o/amwfu4xGJb0J +CFLAGS+= -DGTEST_HAS_PTHREAD + +# GCOV options +CFLAGS+= -fprofile-arcs -ftest-coverage +LDADD+= -lgcov + +all: tests + +# Install the tests +TESTSBASE?= /usr/tests +TESTSDIR= ${TESTSBASE}/zfsd +# TODO: Convert from an ATF SH test to a Kyua plain test +# Long term TODO: Convert to a Kyua googletest test +TESTS_SH+= zfsd_test +BINDIR= ${TESTSDIR} + +# Install the gcov annotation files too +FILESDIR= ${TESTSDIR} +GCNOS= ${SRCS:C/.c+$/.gcno/} +${GCNOS}: ${SRCS:C/.c+$/.o/} +FILES= ${GCNOS} + + +# Run the tests and produce the coverage report +EXCLUDE_PATTERNS='/usr/include/*' '${LOCALBASE}/include/*' +EXCLUDE_PATTERNS+= '*/cddl/compat/opensolaris/include/*' +EXCLUDE_PATTERNS+= '*/tools/regression/zfsd/*' +.PHONY: tests +tests: zfsd_unittest + ${ZFSD_UNITTEST} --gmock_verbose=error + +.PHONY: lcov +lcov: zfsd_unittest + ${LCOV} -z -d . -f && \ + ${ZFSD_UNITTEST} --gmock_verbose=error + ${LCOV} -f -d . -c -o default.info && \ + ${LCOV} -r default.info $(EXCLUDE_PATTERNS) -o trimmed.info && \ + mkdir -p lcov-report && \ + genhtml -o lcov-report trimmed.info + +.include diff --git a/cddl/sbin/zfsd/tests/libmocks.c b/cddl/sbin/zfsd/tests/libmocks.c new file mode 100644 index 00000000000..258b712d3ff --- /dev/null +++ b/cddl/sbin/zfsd/tests/libmocks.c @@ -0,0 +1,24 @@ +#include +#include +#include "libmocks.h" + +/* + * This file mocks shared library functions that are used by zfsd. Every + * function present will be used for all tests in all test suites instead of the + * normal function. + */ + +int syslog_last_priority; +char syslog_last_message[4096]; +void syslog(int priority, const char* message, ...) { + va_list ap; + + syslog_last_priority = priority; + va_start(ap, message); + vsnprintf(syslog_last_message, 4096, message, ap); + va_end(ap); +} + +int zpool_iter(libzfs_handle_t* handle, zpool_iter_f iter, void* arg) { + return (0); +} diff --git a/cddl/sbin/zfsd/tests/libmocks.h b/cddl/sbin/zfsd/tests/libmocks.h new file mode 100644 index 00000000000..13524ee2908 --- /dev/null +++ b/cddl/sbin/zfsd/tests/libmocks.h @@ -0,0 +1,24 @@ +#ifndef _LIBMOCKS_H_ +#define _LIBMOCKS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +struct libzfs_handle; +typedef struct libzfs_handle libzfs_handle_t; +struct zpool_handle; +typedef struct zpool_handle zpool_handle_t; +typedef int (*zpool_iter_f)(zpool_handle_t *, void *); + +void syslog(int priority, const char* message, ...); +int zpool_iter(libzfs_handle_t*, zpool_iter_f, void*); + +extern int syslog_last_priority; +extern char syslog_last_message[4096]; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/cddl/sbin/zfsd/tests/zfsd_test.sh b/cddl/sbin/zfsd/tests/zfsd_test.sh new file mode 100644 index 00000000000..d549eebb265 --- /dev/null +++ b/cddl/sbin/zfsd/tests/zfsd_test.sh @@ -0,0 +1,52 @@ +# Copyright (c) 2013 Spectra Logic Corporation +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions, and the following disclaimer, +# without modification. +# 2. Redistributions in binary form must reproduce at minimum a disclaimer +# substantially similar to the "NO WARRANTY" disclaimer below +# ("Disclaimer") and any redistribution must be conditioned upon +# including a substantially similar Disclaimer requirement for further +# binary redistribution. +# +# NO WARRANTY +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGES. +# +# Authors: Alan Somers (Spectra Logic Corporation) +# +# $FreeBSD$ + +# +# Test Case: zfsd_unittest +# TODO: get coverage in cleanup +# +atf_test_case zfsd_unittest +zfsd_unittest_head() +{ + atf_set "descr" "Run zfsd unit tests" +} + + +zfsd_unittest_body() +{ + atf_check -s exit:0 -o ignore -e ignore $(atf_get_srcdir)/zfsd_unittest +} + +atf_init_test_cases() +{ + atf_add_test_case zfsd_unittest +} diff --git a/cddl/sbin/zfsd/tests/zfsd_unittest.cc b/cddl/sbin/zfsd/tests/zfsd_unittest.cc new file mode 100644 index 00000000000..ac425d17124 --- /dev/null +++ b/cddl/sbin/zfsd/tests/zfsd_unittest.cc @@ -0,0 +1,778 @@ +/*- + * Copyright (c) 2012, 2013 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Alan Somers (Spectra Logic Corporation) + */ +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libmocks.h" + +__FBSDID("$FreeBSD$"); + +/*================================== Macros ==================================*/ +#define NUM_ELEMENTS(x) (sizeof(x) / sizeof(*x)) + +/*============================ Namespace Control =============================*/ +using std::string; +using std::stringstream; + +using DevCtl::Event; +using DevCtl::EventBuffer; +using DevCtl::EventFactory; +using DevCtl::EventList; +using DevCtl::Guid; +using DevCtl::NVPairMap; + +/* redefine zpool_handle here because libzfs_impl.h is not includable */ +struct zpool_handle +{ + libzfs_handle_t *zpool_hdl; + zpool_handle_t *zpool_next; + char zpool_name[ZPOOL_MAXNAMELEN]; + int zpool_state; + size_t zpool_config_size; + nvlist_t *zpool_config; + nvlist_t *zpool_old_config; + nvlist_t *zpool_props; + diskaddr_t zpool_start_block; +}; + +class MockZfsEvent : public ZfsEvent +{ +public: + MockZfsEvent(Event::Type, NVPairMap&, const string&); + virtual ~MockZfsEvent() {} + + static BuildMethod MockZfsEventBuilder; + + MOCK_CONST_METHOD0(ProcessPoolEvent, void()); + + static EventFactory::Record s_buildRecords[]; +}; + +EventFactory::Record MockZfsEvent::s_buildRecords[] = +{ + { Event::NOTIFY, "ZFS", &MockZfsEvent::MockZfsEventBuilder } +}; + +MockZfsEvent::MockZfsEvent(Event::Type type, NVPairMap& map, + const string& str) + : ZfsEvent(type, map, str) +{ +} + +Event * +MockZfsEvent::MockZfsEventBuilder(Event::Type type, + NVPairMap &nvpairs, + const string &eventString) +{ + return (new MockZfsEvent(type, nvpairs, eventString)); +} + +/* + * A dummy Vdev class used for testing other classes + */ +class MockVdev : public Vdev +{ +public: + MockVdev(nvlist_t *vdevConfig); + virtual ~MockVdev() {} + + MOCK_CONST_METHOD0(GUID, Guid()); + MOCK_CONST_METHOD0(PoolGUID, Guid()); + MOCK_CONST_METHOD0(State, vdev_state()); + MOCK_CONST_METHOD0(PhysicalPath, string()); +}; + +MockVdev::MockVdev(nvlist_t *vdevConfig) + : Vdev(vdevConfig) +{ +} + +/* + * A CaseFile class with side effects removed, for testing + */ +class TestableCaseFile : public CaseFile +{ +public: + static TestableCaseFile &Create(Vdev &vdev); + TestableCaseFile(Vdev &vdev); + virtual ~TestableCaseFile() {} + + MOCK_METHOD0(Close, void()); + MOCK_METHOD1(RegisterCallout, void(const Event &event)); + MOCK_METHOD0(RefreshVdevState, bool()); + MOCK_METHOD1(ReEvaluate, bool(const ZfsEvent &event)); + + bool RealReEvaluate(const ZfsEvent &event) + { + return (CaseFile::ReEvaluate(event)); + } + + /* + * This splices the event lists, a procedure that would normally be done + * by OnGracePeriodEnded, but we don't necessarily call that in the + * unit tests + */ + void SpliceEvents(); + + /* + * Used by some of our expectations. CaseFile does not publicize this + */ + static int getActiveCases() + { + return (s_activeCases.size()); + } +}; + +TestableCaseFile::TestableCaseFile(Vdev &vdev) + : CaseFile(vdev) +{ +} + +TestableCaseFile & +TestableCaseFile::Create(Vdev &vdev) +{ + TestableCaseFile *newCase; + newCase = new TestableCaseFile(vdev); + return (*newCase); +} + +void +TestableCaseFile::SpliceEvents() +{ + m_events.splice(m_events.begin(), m_tentativeEvents); +} + + +/* + * Test class ZfsdException + */ +class ZfsdExceptionTest : public ::testing::Test +{ +protected: + virtual void SetUp() + { + ASSERT_EQ(0, nvlist_alloc(&poolConfig, NV_UNIQUE_NAME, 0)); + ASSERT_EQ(0, nvlist_add_string(poolConfig, + ZPOOL_CONFIG_POOL_NAME, "unit_test_pool")); + ASSERT_EQ(0, nvlist_add_uint64(poolConfig, + ZPOOL_CONFIG_POOL_GUID, 0x1234)); + + ASSERT_EQ(0, nvlist_alloc(&vdevConfig, NV_UNIQUE_NAME, 0)); + ASSERT_EQ(0, nvlist_add_uint64(vdevConfig, + ZPOOL_CONFIG_GUID, 0x5678)); + bzero(&poolHandle, sizeof(poolHandle)); + poolHandle.zpool_config = poolConfig; + } + + virtual void TearDown() + { + nvlist_free(poolConfig); + nvlist_free(vdevConfig); + } + + nvlist_t *poolConfig; + nvlist_t *vdevConfig; + zpool_handle_t poolHandle; +}; + +TEST_F(ZfsdExceptionTest, StringConstructorNull) +{ + ZfsdException ze(""); + EXPECT_STREQ("", ze.GetString().c_str()); +} + +TEST_F(ZfsdExceptionTest, StringConstructorFormatted) +{ + ZfsdException ze(" %d %s", 55, "hello world"); + EXPECT_STREQ(" 55 hello world", ze.GetString().c_str()); +} + +TEST_F(ZfsdExceptionTest, LogSimple) +{ + ZfsdException ze("unit test w/o vdev or pool"); + ze.Log(); + EXPECT_EQ(LOG_ERR, syslog_last_priority); + EXPECT_STREQ("unit test w/o vdev or pool\n", syslog_last_message); +} + +TEST_F(ZfsdExceptionTest, Pool) +{ + const char msg[] = "Exception with pool name"; + char expected[4096]; + sprintf(expected, "Pool unit_test_pool: %s\n", msg); + ZfsdException ze(poolConfig, msg); + ze.Log(); + EXPECT_STREQ(expected, syslog_last_message); +} + +TEST_F(ZfsdExceptionTest, PoolHandle) +{ + const char msg[] = "Exception with pool handle"; + char expected[4096]; + sprintf(expected, "Pool unit_test_pool: %s\n", msg); + ZfsdException ze(&poolHandle, msg); + ze.Log(); + EXPECT_STREQ(expected, syslog_last_message); +} + +/* + * Test class Vdev + */ +class VdevTest : public ::testing::Test +{ +protected: + virtual void SetUp() + { + ASSERT_EQ(0, nvlist_alloc(&m_poolConfig, NV_UNIQUE_NAME, 0)); + ASSERT_EQ(0, nvlist_add_uint64(m_poolConfig, + ZPOOL_CONFIG_POOL_GUID, + 0x1234)); + + ASSERT_EQ(0, nvlist_alloc(&m_vdevConfig, NV_UNIQUE_NAME, 0)); + ASSERT_EQ(0, nvlist_add_uint64(m_vdevConfig, ZPOOL_CONFIG_GUID, + 0x5678)); + } + + virtual void TearDown() + { + nvlist_free(m_poolConfig); + nvlist_free(m_vdevConfig); + } + + nvlist_t *m_poolConfig; + nvlist_t *m_vdevConfig; +}; + + +TEST_F(VdevTest, StateFromConfig) +{ + vdev_stat_t vs; + + vs.vs_state = VDEV_STATE_OFFLINE; + + ASSERT_EQ(0, nvlist_add_uint64_array(m_vdevConfig, + ZPOOL_CONFIG_VDEV_STATS, + (uint64_t*)&vs, + sizeof(vs) / sizeof(uint64_t))); + + Vdev vdev(m_poolConfig, m_vdevConfig); + + EXPECT_EQ(VDEV_STATE_OFFLINE, vdev.State()); +} + +TEST_F(VdevTest, StateFaulted) +{ + ASSERT_EQ(0, nvlist_add_uint64(m_vdevConfig, ZPOOL_CONFIG_FAULTED, 1)); + + Vdev vdev(m_poolConfig, m_vdevConfig); + + EXPECT_EQ(VDEV_STATE_FAULTED, vdev.State()); +} + +/* + * Test that we can construct a Vdev from the label information that is stored + * on an available spare drive + */ +TEST_F(VdevTest, ConstructAvailSpare) +{ + nvlist_t *labelConfig; + + ASSERT_EQ(0, nvlist_alloc(&labelConfig, NV_UNIQUE_NAME, 0)); + ASSERT_EQ(0, nvlist_add_uint64(labelConfig, ZPOOL_CONFIG_GUID, + 1948339428197961030)); + ASSERT_EQ(0, nvlist_add_uint64(labelConfig, ZPOOL_CONFIG_POOL_STATE, + POOL_STATE_SPARE)); + + EXPECT_NO_THROW(Vdev vdev(labelConfig)); + + nvlist_free(labelConfig); +} + +/* Available spares will always show the HEALTHY state */ +TEST_F(VdevTest, AvailSpareState) { + nvlist_t *labelConfig; + + ASSERT_EQ(0, nvlist_alloc(&labelConfig, NV_UNIQUE_NAME, 0)); + ASSERT_EQ(0, nvlist_add_uint64(labelConfig, ZPOOL_CONFIG_GUID, + 1948339428197961030)); + ASSERT_EQ(0, nvlist_add_uint64(labelConfig, ZPOOL_CONFIG_POOL_STATE, + POOL_STATE_SPARE)); + + Vdev vdev(labelConfig); + EXPECT_EQ(VDEV_STATE_HEALTHY, vdev.State()); + + nvlist_free(labelConfig); +} + +/* Test the Vdev::IsSpare method */ +TEST_F(VdevTest, IsSpare) { + Vdev* vdev; + + vdev = new Vdev(m_poolConfig, m_vdevConfig); + EXPECT_EQ(false, vdev->IsSpare()); + delete vdev; + + + ASSERT_EQ(0, nvlist_add_uint64(m_vdevConfig, ZPOOL_CONFIG_IS_SPARE, 1)); + vdev = new Vdev(m_poolConfig, m_vdevConfig); + EXPECT_EQ(true, vdev->IsSpare()); + delete vdev; +} + +/* + * Test class ZFSEvent + */ +class ZfsEventTest : public ::testing::Test +{ +protected: + virtual void SetUp() + { + m_eventFactory = new EventFactory(); + m_eventFactory->UpdateRegistry(MockZfsEvent::s_buildRecords, + NUM_ELEMENTS(MockZfsEvent::s_buildRecords)); + + m_event = NULL; + } + + virtual void TearDown() + { + delete m_eventFactory; + delete m_event; + } + + EventFactory *m_eventFactory; + Event *m_event; +}; + +TEST_F(ZfsEventTest, ProcessPoolEventGetsCalled) +{ + string evString("!system=ZFS " + "subsystem=ZFS " + "type=misc.fs.zfs.vdev_remove " + "pool_name=foo " + "pool_guid=9756779504028057996 " + "vdev_guid=1631193447431603339 " + "vdev_path=/dev/da1 " + "timestamp=1348871594"); + m_event = Event::CreateEvent(*m_eventFactory, evString); + MockZfsEvent *mock_event = static_cast(m_event); + + EXPECT_CALL(*mock_event, ProcessPoolEvent()).Times(1); + mock_event->Process(); +} + +/* + * Test class CaseFile + */ + +class CaseFileTest : public ::testing::Test +{ +protected: + virtual void SetUp() + { + m_eventFactory = new EventFactory(); + m_eventFactory->UpdateRegistry(MockZfsEvent::s_buildRecords, + NUM_ELEMENTS(MockZfsEvent::s_buildRecords)); + + m_event = NULL; + + nvlist_alloc(&m_vdevConfig, NV_UNIQUE_NAME, 0); + ASSERT_EQ(0, nvlist_add_uint64(m_vdevConfig, + ZPOOL_CONFIG_GUID, 0xbeef)); + m_vdev = new MockVdev(m_vdevConfig); + ON_CALL(*m_vdev, GUID()) + .WillByDefault(::testing::Return(Guid(123))); + ON_CALL(*m_vdev, PoolGUID()) + .WillByDefault(::testing::Return(Guid(456))); + ON_CALL(*m_vdev, State()) + .WillByDefault(::testing::Return(VDEV_STATE_HEALTHY)); + m_caseFile = &TestableCaseFile::Create(*m_vdev); + ON_CALL(*m_caseFile, ReEvaluate(::testing::_)) + .WillByDefault(::testing::Invoke(m_caseFile, &TestableCaseFile::RealReEvaluate)); + return; + } + + virtual void TearDown() + { + delete m_caseFile; + nvlist_free(m_vdevConfig); + delete m_vdev; + delete m_event; + delete m_eventFactory; + } + + nvlist_t *m_vdevConfig; + MockVdev *m_vdev; + TestableCaseFile *m_caseFile; + Event *m_event; + EventFactory *m_eventFactory; +}; + +/* + * A Vdev with no events should not be degraded or faulted + */ +TEST_F(CaseFileTest, HealthyVdev) +{ + EXPECT_FALSE(m_caseFile->ShouldDegrade()); + EXPECT_FALSE(m_caseFile->ShouldFault()); +} + +/* + * A Vdev with only one event should not be degraded or faulted + * For performance reasons, RefreshVdevState should not be called. + */ +TEST_F(CaseFileTest, HealthyishVdev) +{ + string evString("!system=ZFS " + "class=ereport.fs.zfs.io " + "ena=12091638756982918145 " + "parent_guid=13237004955564865395 " + "parent_type=raidz " + "pool=testpool.4415 " + "pool_context=0 " + "pool_failmode=wait " + "pool_guid=456 " + "subsystem=ZFS " + "timestamp=1348867914 " + "type=ereport.fs.zfs.io " + "vdev_guid=123 " + "vdev_path=/dev/da400 " + "vdev_type=disk " + "zio_blkid=622 " + "zio_err=1 " + "zio_level=-2 " + "zio_object=0 " + "zio_objset=37 " + "zio_offset=25598976 " + "zio_size=1024"); + m_event = Event::CreateEvent(*m_eventFactory, evString); + ZfsEvent *zfs_event = static_cast(m_event); + + EXPECT_CALL(*m_caseFile, RefreshVdevState()) + .Times(::testing::Exactly(0)); + EXPECT_TRUE(m_caseFile->ReEvaluate(*zfs_event)); + EXPECT_FALSE(m_caseFile->ShouldDegrade()); + EXPECT_FALSE(m_caseFile->ShouldFault()); +} + +/* The case file should be closed when its pool is destroyed */ +TEST_F(CaseFileTest, PoolDestroy) +{ + string evString("!system=ZFS " + "pool_name=testpool.4415 " + "pool_guid=456 " + "subsystem=ZFS " + "timestamp=1348867914 " + "type=misc.fs.zfs.pool_destroy "); + m_event = Event::CreateEvent(*m_eventFactory, evString); + ZfsEvent *zfs_event = static_cast(m_event); + EXPECT_CALL(*m_caseFile, Close()); + EXPECT_TRUE(m_caseFile->ReEvaluate(*zfs_event)); +} + +/* + * A Vdev with a very large number of IO errors should fault + * For performance reasons, RefreshVdevState should be called at most once + */ +TEST_F(CaseFileTest, VeryManyIOErrors) +{ + EXPECT_CALL(*m_caseFile, RefreshVdevState()) + .Times(::testing::AtMost(1)) + .WillRepeatedly(::testing::Return(true)); + + for(int i=0; i<100; i++) { + stringstream evStringStream; + evStringStream << + "!system=ZFS " + "class=ereport.fs.zfs.io " + "ena=12091638756982918145 " + "parent_guid=13237004955564865395 " + "parent_type=raidz " + "pool=testpool.4415 " + "pool_context=0 " + "pool_failmode=wait " + "pool_guid=456 " + "subsystem=ZFS " + "timestamp="; + evStringStream << i << " "; + evStringStream << + "type=ereport.fs.zfs.io " + "vdev_guid=123 " + "vdev_path=/dev/da400 " + "vdev_type=disk " + "zio_blkid=622 " + "zio_err=1 " + "zio_level=-2 " + "zio_object=0 " + "zio_objset=37 " + "zio_offset=25598976 " + "zio_size=1024"; + Event *event(Event::CreateEvent(*m_eventFactory, + evStringStream.str())); + ZfsEvent *zfs_event = static_cast(event); + EXPECT_TRUE(m_caseFile->ReEvaluate(*zfs_event)); + delete event; + } + + m_caseFile->SpliceEvents(); + EXPECT_FALSE(m_caseFile->ShouldDegrade()); + EXPECT_TRUE(m_caseFile->ShouldFault()); +} + +/* + * A Vdev with a very large number of checksum errors should degrade + * For performance reasons, RefreshVdevState should be called at most once + */ +TEST_F(CaseFileTest, VeryManyChecksumErrors) +{ + EXPECT_CALL(*m_caseFile, RefreshVdevState()) + .Times(::testing::AtMost(1)) + .WillRepeatedly(::testing::Return(true)); + + for(int i=0; i<100; i++) { + stringstream evStringStream; + evStringStream << + "!system=ZFS " + "bad_cleared_bits=03000000000000803f50b00000000000 " + "bad_range_clears=0000000e " + "bad_range_sets=00000000 " + "bad_ranges=0000000000000010 " + "bad_ranges_min_gap=8 " + "bad_set_bits=00000000000000000000000000000000 " + "class=ereport.fs.zfs.checksum " + "ena=12272856582652437505 " + "parent_guid=5838204195352909894 " + "parent_type=raidz pool=testpool.7640 " + "pool_context=0 " + "pool_failmode=wait " + "pool_guid=456 " + "subsystem=ZFS timestamp="; + evStringStream << i << " "; + evStringStream << + "type=ereport.fs.zfs.checksum " + "vdev_guid=123 " + "vdev_path=/mnt/tmp/file1.7702 " + "vdev_type=file " + "zio_blkid=0 " + "zio_err=0 " + "zio_level=0 " + "zio_object=3 " + "zio_objset=0 " + "zio_offset=16896 " + "zio_size=512"; + Event *event(Event::CreateEvent(*m_eventFactory, + evStringStream.str())); + ZfsEvent *zfs_event = static_cast(event); + EXPECT_TRUE(m_caseFile->ReEvaluate(*zfs_event)); + delete event; + } + + m_caseFile->SpliceEvents(); + EXPECT_TRUE(m_caseFile->ShouldDegrade()); + EXPECT_FALSE(m_caseFile->ShouldFault()); +} + +/* + * Test CaseFile::ReEvaluateByGuid + */ +class ReEvaluateByGuidTest : public ::testing::Test +{ +protected: + virtual void SetUp() + { + m_eventFactory = new EventFactory(); + m_eventFactory->UpdateRegistry(MockZfsEvent::s_buildRecords, + NUM_ELEMENTS(MockZfsEvent::s_buildRecords)); + m_event = Event::CreateEvent(*m_eventFactory, s_evString); + nvlist_alloc(&m_vdevConfig, NV_UNIQUE_NAME, 0); + ASSERT_EQ(0, nvlist_add_uint64(m_vdevConfig, + ZPOOL_CONFIG_GUID, 0xbeef)); + m_vdev456 = new ::testing::NiceMock(m_vdevConfig); + m_vdev789 = new ::testing::NiceMock(m_vdevConfig); + ON_CALL(*m_vdev456, GUID()) + .WillByDefault(::testing::Return(Guid(123))); + ON_CALL(*m_vdev456, PoolGUID()) + .WillByDefault(::testing::Return(Guid(456))); + ON_CALL(*m_vdev456, State()) + .WillByDefault(::testing::Return(VDEV_STATE_HEALTHY)); + ON_CALL(*m_vdev789, GUID()) + .WillByDefault(::testing::Return(Guid(123))); + ON_CALL(*m_vdev789, PoolGUID()) + .WillByDefault(::testing::Return(Guid(789))); + ON_CALL(*m_vdev789, State()) + .WillByDefault(::testing::Return(VDEV_STATE_HEALTHY)); + m_caseFile456 = NULL; + m_caseFile789 = NULL; + return; + } + + virtual void TearDown() + { + delete m_caseFile456; + delete m_caseFile789; + nvlist_free(m_vdevConfig); + delete m_vdev456; + delete m_vdev789; + delete m_event; + delete m_eventFactory; + } + + static string s_evString; + nvlist_t *m_vdevConfig; + ::testing::NiceMock *m_vdev456; + ::testing::NiceMock *m_vdev789; + TestableCaseFile *m_caseFile456; + TestableCaseFile *m_caseFile789; + Event *m_event; + EventFactory *m_eventFactory; +}; + +string ReEvaluateByGuidTest::s_evString( + "!system=ZFS " + "pool_guid=16271873792808333580 " + "pool_name=foo " + "subsystem=ZFS " + "timestamp=1360620391 " + "type=misc.fs.zfs.config_sync"); + + +/* + * Test the ReEvaluateByGuid method on an empty list of casefiles. + * We must create one event, even though it never gets used, because it will + * be passed by reference to ReEvaluateByGuid + */ +TEST_F(ReEvaluateByGuidTest, ReEvaluateByGuid_empty) +{ + ZfsEvent *zfs_event = static_cast(m_event); + + EXPECT_EQ(0, TestableCaseFile::getActiveCases()); + CaseFile::ReEvaluateByGuid(Guid(456), *zfs_event); + EXPECT_EQ(0, TestableCaseFile::getActiveCases()); +} + +/* + * Test the ReEvaluateByGuid method on a list of CaseFiles that contains only + * one CaseFile, which doesn't match the criteria + */ +TEST_F(ReEvaluateByGuidTest, ReEvaluateByGuid_oneFalse) +{ + m_caseFile456 = &TestableCaseFile::Create(*m_vdev456); + ZfsEvent *zfs_event = static_cast(m_event); + + EXPECT_EQ(1, TestableCaseFile::getActiveCases()); + EXPECT_CALL(*m_caseFile456, ReEvaluate(::testing::_)) + .Times(::testing::Exactly(0)); + CaseFile::ReEvaluateByGuid(Guid(789), *zfs_event); + EXPECT_EQ(1, TestableCaseFile::getActiveCases()); +} + +/* + * Test the ReEvaluateByGuid method on a list of CaseFiles that contains only + * one CaseFile, which does match the criteria + */ +TEST_F(ReEvaluateByGuidTest, ReEvaluateByGuid_oneTrue) +{ + m_caseFile456 = &TestableCaseFile::Create(*m_vdev456); + ZfsEvent *zfs_event = static_cast(m_event); + + EXPECT_EQ(1, TestableCaseFile::getActiveCases()); + EXPECT_CALL(*m_caseFile456, ReEvaluate(::testing::_)) + .Times(::testing::Exactly(1)) + .WillRepeatedly(::testing::Return(false)); + CaseFile::ReEvaluateByGuid(Guid(456), *zfs_event); + EXPECT_EQ(1, TestableCaseFile::getActiveCases()); +} + +/* + * Test the ReEvaluateByGuid method on a long list of CaseFiles that contains a + * few cases which meet the criteria + */ +TEST_F(ReEvaluateByGuidTest, ReEvaluateByGuid_five) +{ + TestableCaseFile *CaseFile1 = &TestableCaseFile::Create(*m_vdev456); + TestableCaseFile *CaseFile2 = &TestableCaseFile::Create(*m_vdev789); + TestableCaseFile *CaseFile3 = &TestableCaseFile::Create(*m_vdev456); + TestableCaseFile *CaseFile4 = &TestableCaseFile::Create(*m_vdev789); + TestableCaseFile *CaseFile5 = &TestableCaseFile::Create(*m_vdev789); + ZfsEvent *zfs_event = static_cast(m_event); + + EXPECT_EQ(5, TestableCaseFile::getActiveCases()); + EXPECT_CALL(*CaseFile1, ReEvaluate(::testing::_)) + .Times(::testing::Exactly(1)) + .WillRepeatedly(::testing::Return(false)); + EXPECT_CALL(*CaseFile3, ReEvaluate(::testing::_)) + .Times(::testing::Exactly(1)) + .WillRepeatedly(::testing::Return(false)); + EXPECT_CALL(*CaseFile2, ReEvaluate(::testing::_)) + .Times(::testing::Exactly(0)); + EXPECT_CALL(*CaseFile4, ReEvaluate(::testing::_)) + .Times(::testing::Exactly(0)); + EXPECT_CALL(*CaseFile5, ReEvaluate(::testing::_)) + .Times(::testing::Exactly(0)); + CaseFile::ReEvaluateByGuid(Guid(456), *zfs_event); + EXPECT_EQ(5, TestableCaseFile::getActiveCases()); + delete CaseFile1; + delete CaseFile2; + delete CaseFile3; + delete CaseFile4; + delete CaseFile5; +} diff --git a/cddl/sbin/zfsd/tests/zfsd_unittest.supp b/cddl/sbin/zfsd/tests/zfsd_unittest.supp new file mode 100644 index 00000000000..79697878520 --- /dev/null +++ b/cddl/sbin/zfsd/tests/zfsd_unittest.supp @@ -0,0 +1,104 @@ +# This is a valgrind suppression file used for running zfsd_unittest with +# valgrind. It suppress spurious errors generated by the googletest and +# googlemock libraries. +# +# To use, do: +# valgrind --suppressions=$PWD/zfsd_unittest.supp ./zfsd_unittest + +{ + + Memcheck:Free + fun:free + ... + fun:__cxa_finalize + fun:exit + fun:(below main) +} + +{ + + Memcheck:Free + fun:free + ... + fun:_ZN7testing8internal27PrettyUnitTestResultPrinter* + ... + ... + fun:main +} + +{ + + Memcheck:Free + fun:free + fun:_ZN7testing* + ... + fun:main +} + +{ + + Memcheck:Free + fun:free + ... + fun:_Z41__static_initialization_and_destruction_0ii + ... +} + +{ + + Memcheck:Free + fun:free + ... + fun:_ZN7testing8internal8MockSpec* + ... + fun:_ZN7testing4Test3RunEv + fun:_ZN7testing8internal12TestInfoImpl3RunEv + fun:_ZN7testing8TestCase3RunEv + fun:_ZN7testing8internal12UnitTestImpl11RunAllTestsEv +} + +{ + + Memcheck:Free + fun:free + ... + fun:_ZN7testing8internal14FunctionMocker* + ... +} + +{ + + Memcheck:Cond + obj:/lib/libc.so.7 + obj:/lib/libc.so.7 + fun:snprintf + fun:_ZN7testing45_GLOBAL__N_src_gmock_all.cc_00000000_917CAD5926PrintByteSegmentInObjectToEPKhmmPSo + fun:_ZN7testing9internal220PrintBytesInObjectToEPKhmPSo + fun:_ZN7testing9internal220TypeWithoutFormatterI8ZfsEventLb0EE10PrintValueERKS2_PSo + fun:_ZN7testing9internal2lsIcSt11char_traitsIcE8ZfsEventEERSt13basic_ostreamIT_T0_ES9_RKT1_ + fun:_ZN16testing_internal26DefaultPrintNonContainerToI8ZfsEventEEvRKT_PSo + fun:_ZN7testing8internal14DefaultPrintToI8ZfsEventEEvcNS0_13bool_constantILb0EEERKT_PSo + fun:_ZN7testing8internal7PrintToI8ZfsEventEEvRKT_PSo + fun:_ZN7testing8internal16UniversalPrinterIK8ZfsEventE5PrintERS3_PSo + fun:_ZN7testing8internal16UniversalPrinterIRK8ZfsEventE5PrintES4_PSo +} + +{ + + Memcheck:Cond + ... + fun:snprintf + ... + fun:_ZN7testing9internal220PrintBytesInObjectToEPKhmPSo + ... +} +{ + + Memcheck:Value8 + ... + fun:snprintf + ... + fun:_ZN7testing9internal220PrintBytesInObjectToEPKhmPSo + ... +} + From 9102691e9cde7b12ddd929d376e94051acdf5c8b Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Wed, 21 May 2014 23:04:47 +0000 Subject: [PATCH 87/89] sbin/devd/devd.8 sbin/devd/devd.cc Create a new socket, of type SOCK_SEQPACKET, for communicating with clients. SOCK_SEQPACKET sockets preserve record boundaries, simplying code in the client. The old SOCK_STREAM socket is retained for backwards-compatibility with existing clients. Sponsored by: Spectra Logic --- sbin/devd/devd.8 | 19 ++++++----- sbin/devd/devd.cc | 83 ++++++++++++++++++++++++++++++----------------- 2 files changed, 65 insertions(+), 37 deletions(-) diff --git a/sbin/devd/devd.8 b/sbin/devd/devd.8 index fa34df23ee1..57d4fe3dc94 100644 --- a/sbin/devd/devd.8 +++ b/sbin/devd/devd.8 @@ -55,9 +55,7 @@ If option .Fl f is specified more than once, the last file specified is used. .It Fl l Ar num -Limit concurrent -.Pa /var/run/devd.pipe -connections to +Limit concurrent socket connections to .Ar num . The default connection limit is 10. .It Fl n @@ -130,22 +128,27 @@ wish to hook into the system without modifying the user's other config files. .Pp -All messages that +Since +.Xr devctl 4 +allows only one active reader, .Nm -receives are forwarded to the +multiplexes it, forwarding all events to any number of connected clients. +Clients connect by opening the SOCK_SEQPACKET .Ux domain socket at -.Pa /var/run/devd.pipe . +.Pa /var/run/devd.seqpacket.pipe . .Sh FILES -.Bl -tag -width ".Pa /var/run/devd.pipe" -compact +.Bl -tag -width ".Pa /var/run/devd.seqpacket.pipe" -compact .It Pa /etc/devd.conf The default .Nm configuration file. -.It Pa /var/run/devd.pipe +.It Pa /var/run/devd.seqpacket.pipe The socket used by .Nm to communicate with its clients. +.It Pa /var/run/devd.pipe +An deprecated socket retained for use with old clients. .El .Sh SEE ALSO .Xr devctl 4 , diff --git a/sbin/devd/devd.cc b/sbin/devd/devd.cc index ce2a4f3482d..c770204c349 100644 --- a/sbin/devd/devd.cc +++ b/sbin/devd/devd.cc @@ -100,7 +100,8 @@ __FBSDID("$FreeBSD$"); #include "devd.h" /* C compatible definitions */ #include "devd.hh" /* C++ class definitions */ -#define PIPE "/var/run/devd.pipe" +#define STREAMPIPE "/var/run/devd.pipe" +#define SEQPACKETPIPE "/var/run/devd.seqpacket.pipe" #define CF "/etc/devd.conf" #define SYSCTL "hw.bus.devctl_queue" @@ -119,6 +120,11 @@ __FBSDID("$FreeBSD$"); using namespace std; +typedef struct client { + int fd; + int socktype; +} client_t; + extern FILE *yyin; extern int lineno; @@ -822,12 +828,12 @@ process_event(char *buffer) } int -create_socket(const char *name) +create_socket(const char *name, int socktype) { int fd, slen; struct sockaddr_un sun; - if ((fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) + if ((fd = socket(PF_LOCAL, socktype, 0)) < 0) err(1, "socket"); bzero(&sun, sizeof(sun)); sun.sun_family = AF_UNIX; @@ -846,12 +852,13 @@ create_socket(const char *name) unsigned int max_clients = 10; /* Default, can be overriden on cmdline. */ unsigned int num_clients; -list clients; + +list clients; void notify_clients(const char *data, int len) { - list::iterator i; + list::iterator i; /* * Deliver the data to all clients. Throw clients overboard at the @@ -861,11 +868,17 @@ notify_clients(const char *data, int len) * kernel memory or tie up the limited number of available connections). */ for (i = clients.begin(); i != clients.end(); ) { - if (write(*i, data, len) != len) { + int flags; + if (i->socktype == SOCK_SEQPACKET) + flags = MSG_EOR; + else + flags = 0; + + if (send(i->fd, data, len, flags) != len) { --num_clients; - close(*i); + close(i->fd); i = clients.erase(i); - devdlog(LOG_WARNING, "notify_clients: write() failed; " + devdlog(LOG_WARNING, "notify_clients: send() failed; " "dropping unresponsive client\n"); } else ++i; @@ -877,7 +890,7 @@ check_clients(void) { int s; struct pollfd pfd; - list::iterator i; + list::iterator i; /* * Check all existing clients to see if any of them have disappeared. @@ -888,12 +901,12 @@ check_clients(void) */ pfd.events = 0; for (i = clients.begin(); i != clients.end(); ) { - pfd.fd = *i; + pfd.fd = i->fd; s = poll(&pfd, 1, 0); if ((s < 0 && s != EINTR ) || (s > 0 && (pfd.revents & POLLHUP))) { --num_clients; - close(*i); + close(i->fd); i = clients.erase(i); devdlog(LOG_NOTICE, "check_clients: " "dropping disconnected client\n"); @@ -903,9 +916,9 @@ check_clients(void) } void -new_client(int fd) +new_client(int fd, int socktype) { - int s; + client_t s; int sndbuf_size; /* @@ -914,13 +927,14 @@ new_client(int fd) * by sending large buffers full of data we'll never read. */ check_clients(); - s = accept(fd, NULL, NULL); - if (s != -1) { + s.socktype = socktype; + s.fd = accept(fd, NULL, NULL); + if (s.fd != -1) { sndbuf_size = CLIENT_BUFSIZE; - if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &sndbuf_size, + if (setsockopt(s.fd, SOL_SOCKET, SO_SNDBUF, &sndbuf_size, sizeof(sndbuf_size))) err(1, "setsockopt"); - shutdown(s, SHUT_RD); + shutdown(s.fd, SHUT_RD); clients.push_back(s); ++num_clients; } else @@ -934,7 +948,7 @@ event_loop(void) int fd; char buffer[DEVCTL_MAXBUF]; int once = 0; - int server_fd, max_fd; + int stream_fd, seqpacket_fd, max_fd; int accepting; timeval tv; fd_set fds; @@ -942,9 +956,10 @@ event_loop(void) fd = open(PATH_DEVCTL, O_RDONLY | O_CLOEXEC); if (fd == -1) err(1, "Can't open devctl device %s", PATH_DEVCTL); - server_fd = create_socket(PIPE); + stream_fd = create_socket(STREAMPIPE, SOCK_STREAM); + seqpacket_fd = create_socket(SEQPACKETPIPE, SOCK_SEQPACKET); accepting = 1; - max_fd = max(fd, server_fd) + 1; + max_fd = max(fd, max(stream_fd, seqpacket_fd)) + 1; while (!romeo_must_die) { if (!once && !no_daemon && !daemonize_quick) { // Check to see if we have any events pending. @@ -965,24 +980,28 @@ event_loop(void) } /* * When we've already got the max number of clients, stop - * accepting new connections (don't put server_fd in the set), - * shrink the accept() queue to reject connections quickly, and - * poll the existing clients more often, so that we notice more - * quickly when any of them disappear to free up client slots. + * accepting new connections (don't put the listening sockets in + * the set), shrink the accept() queue to reject connections + * quickly, and poll the existing clients more often, so that we + * notice more quickly when any of them disappear to free up + * client slots. */ FD_ZERO(&fds); FD_SET(fd, &fds); if (num_clients < max_clients) { if (!accepting) { - listen(server_fd, max_clients); + listen(stream_fd, max_clients); + listen(seqpacket_fd, max_clients); accepting = 1; } - FD_SET(server_fd, &fds); + FD_SET(stream_fd, &fds); + FD_SET(seqpacket_fd, &fds); tv.tv_sec = 60; tv.tv_usec = 0; } else { if (accepting) { - listen(server_fd, 0); + listen(stream_fd, 0); + listen(seqpacket_fd, 0); accepting = 0; } tv.tv_sec = 2; @@ -1022,8 +1041,14 @@ event_loop(void) break; } } - if (FD_ISSET(server_fd, &fds)) - new_client(server_fd); + if (FD_ISSET(stream_fd, &fds)) + new_client(stream_fd, SOCK_STREAM); + /* + * Aside from the socket type, both sockets use the same + * protocol, so we can process clients the same way. + */ + if (FD_ISSET(seqpacket_fd, &fds)) + new_client(seqpacket_fd, SOCK_SEQPACKET); } close(fd); } From 6c96553e8a5ee6ec90f42db0e511879b69c8543a Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Fri, 23 May 2014 23:13:34 +0000 Subject: [PATCH 88/89] MFP4 zfsd-related changes Convert libdevctl to use devd's new SEQPACKET socket. lib/libdevctl/consumer.cc lib/libdevctl/event_buffer.cc lib/libdevctl/event_buffer.h lib/libdevctl/reader.cc lib/libdevctl/reader.h Read from the new /var/run/devd.seqpacket.pipe instead of /var/run/devd.pipe. Since it preserves record boundaries, we can eliminate all the repacketization code in EventBuffer::ExtractEvent as well as much supporting code from the Reader class. lib/libdevctl/consumer.cc Make the pipe nonblocking. Previously, we avoided blocking by using the FIONREAD ioctl, but this is simpler. cddl/sbin/zfsd/case_file.cc cddl/sbin/zfsd/tests/zfsd_unittest.cc cddl/sbin/zfsd/vdev.cc cddl/sbin/zfsd/zfsd.cc cddl/sbin/zfsd/zfsd.h cddl/sbin/zfsd/zfsd_event.cc Update zfsd according to the libdevctl changes. The only nontrivial change is to CaseFile::DeSerialize, which elimintes the use of IStreamReader. cddl/sbin/zfsd/case_file.cc For an unknown reason, sometimes the std::ios::failbit will get set on caseStream. Instead of checking for !eof(), check for good(). That method checks the eofbit, errorbit, and failbit. livdevctl cleanup (from gibbs) lib/libdevctl/event.cc: lib/libdevctl/event.h: Remove the event class's name from its Builder method. It's perfectly clear that DevfsEvent::Builder() is the DevfsEvent class's Builder function. cddl/sbin/zfsd/zfsd.cc: cddl/sbin/zfsd/zfsd_event.cc: cddl/sbin/zfsd/zfsd_event.h: Conform to new libdevct Builder naming convention. Fix autoreplace by physical path when a hotspare is present cddl/sbin/zfsd/case_file.cc Fix logic error in CaseFile::Replace regarding whether the replacement device is a spare or not. Reviewed by: gibbs Sponsored by: Spectra Logic --- cddl/sbin/zfsd/case_file.cc | 45 +++++++++----------- cddl/sbin/zfsd/tests/Makefile | 41 ++---------------- cddl/sbin/zfsd/tests/zfsd_test.sh | 21 +++++++++- cddl/sbin/zfsd/tests/zfsd_unittest.cc | 15 ++----- cddl/sbin/zfsd/vdev.cc | 1 - cddl/sbin/zfsd/zfsd.cc | 9 +--- cddl/sbin/zfsd/zfsd.h | 1 - cddl/sbin/zfsd/zfsd_event.cc | 11 +++-- cddl/sbin/zfsd/zfsd_event.h | 4 +- etc/mtree/BSD.tests.dist | 2 + lib/libdevctl/Makefile | 8 +--- lib/libdevctl/consumer.cc | 60 +++++++++++++++------------ lib/libdevctl/consumer.h | 25 ++++++++--- lib/libdevctl/event.cc | 42 ++++++++++++++++--- lib/libdevctl/event.h | 16 +++++-- lib/libdevctl/event_factory.cc | 4 +- lib/libdevctl/exception.cc | 2 + 17 files changed, 167 insertions(+), 140 deletions(-) diff --git a/cddl/sbin/zfsd/case_file.cc b/cddl/sbin/zfsd/case_file.cc index dbba13ab9fc..218c9d14979 100644 --- a/cddl/sbin/zfsd/case_file.cc +++ b/cddl/sbin/zfsd/case_file.cc @@ -59,11 +59,9 @@ #include #include -#include #include #include #include -#include #include "callout.h" #include "vdev_iterator.h" @@ -89,7 +87,6 @@ using DevCtl::EventBuffer; using DevCtl::EventFactory; using DevCtl::EventList; using DevCtl::Guid; -using DevCtl::IstreamReader; using DevCtl::ParseException; /*-------------------------- File-scoped classes ----------------------------*/ @@ -840,26 +837,28 @@ CaseFile::Serialize() close(fd); } +/* + * XXX: This method assumes that events may not contain embedded newlines. If + * ever events can contain embedded newlines, then CaseFile must switch + * serialization formats + */ void CaseFile::DeSerialize(ifstream &caseStream) { - stringstream fakeDevdSocket(stringstream::in|stringstream::out); - IstreamReader caseReader(&fakeDevdSocket); - EventBuffer eventBuffer(caseReader); string evString; + const EventFactory &factory(ZfsDaemon::Get().GetFactory()); caseStream >> std::noskipws >> std::ws; - while (!caseStream.eof()) { + while (caseStream.good()) { /* * Outline: * read the beginning of a line and check it for * "tentative". If found, discard "tentative". - * Shove into fakeDevdSocket. - * call ExtractEvent + * Create a new event * continue */ EventList* destEvents; - string tentFlag("tentative "); + const string tentFlag("tentative "); string line; std::stringbuf lineBuf; @@ -867,20 +866,16 @@ CaseFile::DeSerialize(ifstream &caseStream) caseStream.ignore(); /*discard the newline character*/ line = lineBuf.str(); if (line.compare(0, tentFlag.size(), tentFlag) == 0) { + /* Discard "tentative" */ line.erase(0, tentFlag.size()); destEvents = &m_tentativeEvents; } else { destEvents = &m_events; } - fakeDevdSocket << line; - fakeDevdSocket << '\n'; - const EventFactory &factory(ZfsDaemon::Get().GetFactory()); - while (eventBuffer.ExtractEvent(evString)) { - Event *event(Event::CreateEvent(factory, evString)); - if (event != NULL) { - destEvents->push_back(event); - RegisterCallout(*event); - } + Event *event(Event::CreateEvent(factory, line)); + if (event != NULL) { + destEvents->push_back(event); + RegisterCallout(*event); } } } @@ -1032,7 +1027,7 @@ CaseFile::Replace(const char* vdev_type, const char* path, bool isspare) { Vdev vd(zhp, CaseVdev(zhp)); Vdev replaced(BeingReplacedBy(zhp)); - if (!vd.IsSpare() && !replaced.DoesNotExist()) { + if (isspare && !vd.IsSpare() && !replaced.DoesNotExist()) { /* If we are already being replaced by a working spare, pass. */ if (replaced.IsResilvering() || replaced.State() == VDEV_STATE_HEALTHY) { @@ -1044,12 +1039,10 @@ CaseFile::Replace(const char* vdev_type, const char* path, bool isspare) { * If we have already been replaced by a spare, but that spare * is broken, we must spare the spare, not the original device. */ - if (isspare) { - oldstr = replaced.GUIDString(); - syslog(LOG_INFO, "CaseFile::Replace(%s->%s): sparing " - "broken spare %s instead", VdevGUIDString().c_str(), - path, oldstr.c_str()); - } + oldstr = replaced.GUIDString(); + syslog(LOG_INFO, "CaseFile::Replace(%s->%s): sparing " + "broken spare %s instead", VdevGUIDString().c_str(), + path, oldstr.c_str()); } /* diff --git a/cddl/sbin/zfsd/tests/Makefile b/cddl/sbin/zfsd/tests/Makefile index 3a1aa2baf5e..4d8c0b565b8 100644 --- a/cddl/sbin/zfsd/tests/Makefile +++ b/cddl/sbin/zfsd/tests/Makefile @@ -7,8 +7,6 @@ SRCDIR=${.CURDIR}/../../../.. PROG_CXX= zfsd_unittest SRCS:= ${SRCS:Nzfsd_main.cc} SRCS+= libmocks.c zfsd_unittest.cc -CLEANFILES+= *.gcno *.gcda *.info -CLEANDIRS+= lcov-report # Use #include in test programs. INCFLAGS+= -I ${.CURDIR}/../.. @@ -26,9 +24,6 @@ LIBRARY_PATH= .endif ZFSD_UNITTEST= env LD_LIBRARY_PATH=${LIBRARY_PATH} ./zfsd_unittest -# Extra tools -LCOV= lcov - # Googletest options LOCALBASE?= /usr/local INCFLAGS+= -I ${LOCALBASE}/include -D_THREAD_SAFE -pthread @@ -42,42 +37,12 @@ LDADD+= ${LOCALBASE}/lib/libgmock.a ${LOCALBASE}/lib/libgmock_main.a # https://groups.google.com/forum/#!msg/googletestframework/h8ixEPCFm0o/amwfu4xGJb0J CFLAGS+= -DGTEST_HAS_PTHREAD -# GCOV options -CFLAGS+= -fprofile-arcs -ftest-coverage -LDADD+= -lgcov - -all: tests - # Install the tests TESTSBASE?= /usr/tests -TESTSDIR= ${TESTSBASE}/zfsd +TESTSDIR= ${TESTSBASE}/cddl/sbin/zfsd # TODO: Convert from an ATF SH test to a Kyua plain test # Long term TODO: Convert to a Kyua googletest test -TESTS_SH+= zfsd_test +ATF_TESTS_SH+= zfsd_test BINDIR= ${TESTSDIR} -# Install the gcov annotation files too -FILESDIR= ${TESTSDIR} -GCNOS= ${SRCS:C/.c+$/.gcno/} -${GCNOS}: ${SRCS:C/.c+$/.o/} -FILES= ${GCNOS} - - -# Run the tests and produce the coverage report -EXCLUDE_PATTERNS='/usr/include/*' '${LOCALBASE}/include/*' -EXCLUDE_PATTERNS+= '*/cddl/compat/opensolaris/include/*' -EXCLUDE_PATTERNS+= '*/tools/regression/zfsd/*' -.PHONY: tests -tests: zfsd_unittest - ${ZFSD_UNITTEST} --gmock_verbose=error - -.PHONY: lcov -lcov: zfsd_unittest - ${LCOV} -z -d . -f && \ - ${ZFSD_UNITTEST} --gmock_verbose=error - ${LCOV} -f -d . -c -o default.info && \ - ${LCOV} -r default.info $(EXCLUDE_PATTERNS) -o trimmed.info && \ - mkdir -p lcov-report && \ - genhtml -o lcov-report trimmed.info - -.include +.include diff --git a/cddl/sbin/zfsd/tests/zfsd_test.sh b/cddl/sbin/zfsd/tests/zfsd_test.sh index d549eebb265..11690697339 100644 --- a/cddl/sbin/zfsd/tests/zfsd_test.sh +++ b/cddl/sbin/zfsd/tests/zfsd_test.sh @@ -43,7 +43,26 @@ zfsd_unittest_head() zfsd_unittest_body() { - atf_check -s exit:0 -o ignore -e ignore $(atf_get_srcdir)/zfsd_unittest + TESTPROG=$(atf_get_srcdir)/zfsd_unittest + if atf_config_has coverage_dir; then + # If coverage_dir is defined, then we want to save the .gcda + # and .gcno files for future analysis. Put them in a directory + # tree that resembles /usr/src, but is anchored at + # coverage_dir. + export GCOV_PREFIX=`atf_config_get coverage_dir` + # Examine zfsd_unittest to calculate the GCOV_PREFIX_STRIP + # The outer echo command is needed to strip off whitespace + # printed by wc + OLDGCDADIR=`strings $TESTPROG | grep 'zfsd.gcda'` + export GCOV_PREFIX_STRIP=$( echo $( echo $OLDGCDADIR | \ + sed -e 's:/cddl/sbin/zfsd.*::' -e 's:/: :g' | \ + wc -w ) ) + NEWGCDADIR=$GCOV_PREFIX/`dirname $OLDGCDADIR | \ + sed -e 's:.*\(cddl/sbin/zfsd\):\1:'` + mkdir -p $NEWGCDADIR + cp $(atf_get_srcdir)/*.gcno $NEWGCDADIR + fi + atf_check -s exit:0 -o ignore -e ignore $TESTPROG } atf_init_test_cases() diff --git a/cddl/sbin/zfsd/tests/zfsd_unittest.cc b/cddl/sbin/zfsd/tests/zfsd_unittest.cc index ac425d17124..6c9a4f3094b 100644 --- a/cddl/sbin/zfsd/tests/zfsd_unittest.cc +++ b/cddl/sbin/zfsd/tests/zfsd_unittest.cc @@ -47,11 +47,9 @@ #include #include -#include #include #include #include -#include #include #include @@ -365,17 +363,12 @@ TEST_F(VdevTest, AvailSpareState) { /* Test the Vdev::IsSpare method */ TEST_F(VdevTest, IsSpare) { - Vdev* vdev; - - vdev = new Vdev(m_poolConfig, m_vdevConfig); - EXPECT_EQ(false, vdev->IsSpare()); - delete vdev; - + Vdev notSpare(m_poolConfig, m_vdevConfig); + EXPECT_EQ(false, notSpare.IsSpare()); ASSERT_EQ(0, nvlist_add_uint64(m_vdevConfig, ZPOOL_CONFIG_IS_SPARE, 1)); - vdev = new Vdev(m_poolConfig, m_vdevConfig); - EXPECT_EQ(true, vdev->IsSpare()); - delete vdev; + Vdev isSpare(m_poolConfig, m_vdevConfig); + EXPECT_EQ(true, isSpare.IsSpare()); } /* diff --git a/cddl/sbin/zfsd/vdev.cc b/cddl/sbin/zfsd/vdev.cc index cbf668966c4..dbba3172d83 100644 --- a/cddl/sbin/zfsd/vdev.cc +++ b/cddl/sbin/zfsd/vdev.cc @@ -50,7 +50,6 @@ #include #include -#include #include #include #include diff --git a/cddl/sbin/zfsd/zfsd.cc b/cddl/sbin/zfsd/zfsd.cc index b16ecfb46c8..a4eda63b1ae 100644 --- a/cddl/sbin/zfsd/zfsd.cc +++ b/cddl/sbin/zfsd/zfsd.cc @@ -59,11 +59,9 @@ #include #include -#include #include #include #include -#include #include "callout.h" #include "vdev_iterator.h" @@ -82,11 +80,8 @@ __FBSDID("$FreeBSD$"); /*============================ Namespace Control =============================*/ using DevCtl::Event; -using DevCtl::EventBuffer; using DevCtl::EventFactory; using DevCtl::EventList; -using DevCtl::Guid; -using DevCtl::NVPairMap; /*================================ Global Data ===============================*/ int g_debug = 0; @@ -103,8 +98,8 @@ int ZfsDaemon::s_signalPipeFD[2]; bool ZfsDaemon::s_systemRescanRequested(false); EventFactory::Record ZfsDaemon::s_registryEntries[] = { - { Event::NOTIFY, "DEVFS", &DevfsEvent::DevfsEventBuilder }, - { Event::NOTIFY, "ZFS", &ZfsEvent::ZfsEventBuilder } + { Event::NOTIFY, "DEVFS", &DevfsEvent::Builder }, + { Event::NOTIFY, "ZFS", &ZfsEvent::Builder } }; //- ZfsDaemon Static Public Methods -------------------------------------------- diff --git a/cddl/sbin/zfsd/zfsd.h b/cddl/sbin/zfsd/zfsd.h index 94bbd83108e..c9135b3f7e7 100644 --- a/cddl/sbin/zfsd/zfsd.h +++ b/cddl/sbin/zfsd/zfsd.h @@ -52,7 +52,6 @@ * #include * #include * #include - * #include * * #include "vdev_iterator.h" */ diff --git a/cddl/sbin/zfsd/zfsd_event.cc b/cddl/sbin/zfsd/zfsd_event.cc index 5be4ee678cd..ad53383c69a 100644 --- a/cddl/sbin/zfsd/zfsd_event.cc +++ b/cddl/sbin/zfsd/zfsd_event.cc @@ -51,7 +51,6 @@ #include #include #include -#include #include "callout.h" #include "vdev_iterator.h" @@ -75,9 +74,9 @@ using std::stringstream; //- DevfsEvent Static Public Methods ------------------------------------------- Event * -DevfsEvent::DevfsEventBuilder(Event::Type type, - NVPairMap &nvPairs, - const string &eventString) +DevfsEvent::Builder(Event::Type type, + NVPairMap &nvPairs, + const string &eventString) { return (new DevfsEvent(type, nvPairs, eventString)); } @@ -230,8 +229,8 @@ DevfsEvent::DevfsEvent(const DevfsEvent &src) /*--------------------------------- ZfsEvent ---------------------------------*/ //- ZfsEvent Static Public Methods --------------------------------------------- DevCtl::Event * -ZfsEvent::ZfsEventBuilder(Event::Type type, NVPairMap &nvpairs, - const string &eventString) +ZfsEvent::Builder(Event::Type type, NVPairMap &nvpairs, + const string &eventString) { return (new ZfsEvent(type, nvpairs, eventString)); } diff --git a/cddl/sbin/zfsd/zfsd_event.h b/cddl/sbin/zfsd/zfsd_event.h index f2f7967ae7a..d4d28ee6b46 100644 --- a/cddl/sbin/zfsd/zfsd_event.h +++ b/cddl/sbin/zfsd/zfsd_event.h @@ -66,7 +66,7 @@ class DevfsEvent : public DevCtl::DevfsEvent { public: /** Specialized DevCtlEvent object factory for Devfs events. */ - static BuildMethod DevfsEventBuilder; + static BuildMethod Builder; virtual DevCtl::Event *DeepCopy() const; @@ -122,7 +122,7 @@ class ZfsEvent : public DevCtl::ZfsEvent { public: /** Specialized DevCtlEvent object factory for ZFS events. */ - static BuildMethod ZfsEventBuilder; + static BuildMethod Builder; virtual DevCtl::Event *DeepCopy() const; diff --git a/etc/mtree/BSD.tests.dist b/etc/mtree/BSD.tests.dist index 083054eb274..ae41c514310 100644 --- a/etc/mtree/BSD.tests.dist +++ b/etc/mtree/BSD.tests.dist @@ -54,6 +54,8 @@ lib .. sbin + zfsd + .. .. usr.bin .. diff --git a/lib/libdevctl/Makefile b/lib/libdevctl/Makefile index 250538f1ae2..9dace332736 100644 --- a/lib/libdevctl/Makefile +++ b/lib/libdevctl/Makefile @@ -3,18 +3,14 @@ LIB= devctl INCS= consumer.h \ event.h \ - event_buffer.h \ event_factory.h \ exception.h \ - guid.h \ - reader.h + guid.h SRCS= consumer.cc \ event.cc \ - event_buffer.cc \ event_factory.cc \ exception.cc \ - guid.cc \ - reader.cc + guid.cc INCSDIR= ${INCLUDEDIR}/devctl diff --git a/lib/libdevctl/consumer.cc b/lib/libdevctl/consumer.cc index fe014b32852..d325206ea31 100644 --- a/lib/libdevctl/consumer.cc +++ b/lib/libdevctl/consumer.cc @@ -37,24 +37,24 @@ #include #include #include -#include #include #include #include +#include #include #include +#include +#include #include #include #include #include "guid.h" #include "event.h" -#include "event_buffer.h" #include "event_factory.h" #include "exception.h" -#include "reader.h" #include "consumer.h" @@ -71,15 +71,13 @@ namespace DevCtl /*============================= Class Definitions ============================*/ /*----------------------------- DevCtl::Consumer -----------------------------*/ //- Consumer Static Private Data ----------------------------------------------- -const char Consumer::s_devdSockPath[] = "/var/run/devd.pipe"; +const char Consumer::s_devdSockPath[] = "/var/run/devd.seqpacket.pipe"; //- Consumer Public Methods ---------------------------------------------------- Consumer::Consumer(Event::BuildMethod *defBuilder, EventFactory::Record *regEntries, size_t numEntries) : m_devdSockFD(-1), - m_reader(NULL), - m_eventBuffer(NULL), m_eventFactory(defBuilder), m_replayingEvents(false) { @@ -89,8 +87,6 @@ Consumer::Consumer(Event::BuildMethod *defBuilder, Consumer::~Consumer() { DisconnectFromDevd(); - delete m_reader; - m_reader = NULL; } bool @@ -112,9 +108,11 @@ Consumer::ConnectToDevd() strlcpy(devdAddr.sun_path, s_devdSockPath, sizeof(devdAddr.sun_path)); sLen = SUN_LEN(&devdAddr); - m_devdSockFD = socket(AF_UNIX, SOCK_STREAM, 0); + m_devdSockFD = socket(AF_UNIX, SOCK_SEQPACKET, 0); if (m_devdSockFD == -1) err(1, "Unable to create socket"); + if (fcntl(m_devdSockFD, F_SETFL, O_NONBLOCK) < 0) + err(1, "fcntl"); result = connect(m_devdSockFD, reinterpret_cast(&devdAddr), sLen); @@ -124,9 +122,6 @@ Consumer::ConnectToDevd() return (false); } - /* Connect the stream to the file descriptor */ - m_reader = new FDReader(m_devdSockFD); - m_eventBuffer = new EventBuffer(*m_reader); syslog(LOG_INFO, "Connection to devd successful"); return (true); } @@ -137,14 +132,26 @@ Consumer::DisconnectFromDevd() if (m_devdSockFD != -1) syslog(LOG_INFO, "Disconnecting from devd."); - delete m_eventBuffer; - m_eventBuffer = NULL; - delete m_reader; - m_reader = NULL; close(m_devdSockFD); m_devdSockFD = -1; } +std::string +Consumer::ReadEvent() +{ + char buf[MAX_EVENT_SIZE + 1]; + ssize_t len; + + len = ::recv(m_devdSockFD, buf, MAX_EVENT_SIZE, MSG_WAITALL); + if (len == -1) + return (std::string("")); + else { + /* NULL-terminate the result */ + buf[len] = '\0'; + return (std::string(buf)); + } +} + void Consumer::ReplayUnconsumedEvents(bool discardUnconsumed) { @@ -178,20 +185,20 @@ Consumer::SaveEvent(const Event &event) } Event * -Consumer::NextEvent(EventBuffer *eventBuffer) +Consumer::NextEvent() { if (!Connected()) return(NULL); - if (eventBuffer == NULL) - eventBuffer = m_eventBuffer; - Event *event(NULL); try { string evString; - if (eventBuffer->ExtractEvent(evString)) + evString = ReadEvent(); + if (! evString.empty()) { + Event::TimestampEventString(evString); event = Event::CreateEvent(m_eventFactory, evString); + } } catch (const Exception &exp) { exp.Log(); DisconnectFromDevd(); @@ -201,10 +208,10 @@ Consumer::NextEvent(EventBuffer *eventBuffer) /* Capture and process buffered events. */ void -Consumer::ProcessEvents(EventBuffer *eventBuffer) +Consumer::ProcessEvents() { Event *event; - while ((event = NextEvent(eventBuffer)) != NULL) { + while ((event = NextEvent()) != NULL) { if (event->Process()) SaveEvent(*event); delete event; @@ -214,10 +221,11 @@ Consumer::ProcessEvents(EventBuffer *eventBuffer) void Consumer::FlushEvents() { - char discardBuf[256]; + std::string s; - while (m_reader->in_avail() > 0) - m_reader->read(discardBuf, sizeof(discardBuf)); + do + s = ReadEvent(); + while (! s.empty()) ; } bool diff --git a/lib/libdevctl/consumer.h b/lib/libdevctl/consumer.h index f8242425a3a..335bdaadf8e 100644 --- a/lib/libdevctl/consumer.h +++ b/lib/libdevctl/consumer.h @@ -81,14 +81,13 @@ public: */ void ReplayUnconsumedEvents(bool discardUnconsumed); - /** Return an event, if available, from the provided EventBuffer. */ - Event *NextEvent(EventBuffer *eventBuffer = NULL); + /** Return an event, if one is available. */ + Event *NextEvent(); /** - * Extract events from the provided eventBuffer and invoke - * each event's Process method. + * Extract events and invoke each event's Process method. */ - void ProcessEvents(EventBuffer *eventBuffer = NULL); + void ProcessEvents(); /** Discard all data pending in m_devdSockFD. */ void FlushEvents(); @@ -116,6 +115,22 @@ public: EventFactory GetFactory(); protected: + /** + * \brief Reads the most recent record + * + * On error, "" is returned, and errno will be set by the OS + * + * \returns A string containing the record + */ + std::string ReadEvent(); + + enum { + /* + * The maximum event size supported by libdevctl. + */ + MAX_EVENT_SIZE = 8192, + }; + static const char s_devdSockPath[]; /** diff --git a/lib/libdevctl/event.cc b/lib/libdevctl/event.cc index 38a0c2c1e19..07f67210313 100644 --- a/lib/libdevctl/event.cc +++ b/lib/libdevctl/event.cc @@ -40,12 +40,18 @@ #include #include #include +#include +#include +#include #include #include +#include #include #include +#include +#include #include #include #include @@ -86,8 +92,8 @@ Event::EventTypeRecord Event::s_typeTable[] = //- Event Static Public Methods ------------------------------------------------ Event * -Event::EventBuilder(Event::Type type, NVPairMap &nvPairs, - const string &eventString) +Event::Builder(Event::Type type, NVPairMap &nvPairs, + const string &eventString) { return (new Event(type, nvPairs, eventString)); } @@ -325,11 +331,35 @@ Event::ParseEventString(Event::Type type, } } +void +Event::TimestampEventString(std::string &eventString) +{ + if (eventString.size() > 0) { + /* + * Add a timestamp as the final field of the event if it is + * not already present. + */ + if (eventString.find("timestamp=") == string::npos) { + const size_t bufsize = 32; // Long enough for a 64-bit int + timeval now; + struct tm* time_s; + char timebuf[bufsize]; + + size_t eventEnd(eventString.find_last_not_of('\n') + 1); + if (gettimeofday(&now, NULL) != 0) + err(1, "gettimeofday"); + time_s = gmtime(&now.tv_sec); + strftime(timebuf, bufsize, " timestamp=%s", time_s); + eventString.insert(eventEnd, timebuf); + } + } +} + /*-------------------------------- DevfsEvent --------------------------------*/ //- DevfsEvent Static Public Methods ------------------------------------------- Event * -DevfsEvent::DevfsEventBuilder(Event::Type type, NVPairMap &nvPairs, - const string &eventString) +DevfsEvent::Builder(Event::Type type, NVPairMap &nvPairs, + const string &eventString) { return (new DevfsEvent(type, nvPairs, eventString)); } @@ -495,8 +525,8 @@ DevfsEvent::DevfsEvent(const DevfsEvent &src) /*--------------------------------- ZfsEvent ---------------------------------*/ //- ZfsEvent Static Public Methods --------------------------------------------- Event * -ZfsEvent::ZfsEventBuilder(Event::Type type, NVPairMap &nvpairs, - const string &eventString) +ZfsEvent::Builder(Event::Type type, NVPairMap &nvpairs, + const string &eventString) { return (new ZfsEvent(type, nvpairs, eventString)); } diff --git a/lib/libdevctl/event.h b/lib/libdevctl/event.h index 9f13f91db33..eac253cb5fc 100644 --- a/lib/libdevctl/event.h +++ b/lib/libdevctl/event.h @@ -95,7 +95,7 @@ public: typedef Event* (BuildMethod)(Type, NVPairMap &, const std::string &); /** Generic Event object factory. */ - static BuildMethod EventBuilder; + static BuildMethod Builder; static Event *CreateEvent(const EventFactory &factory, const std::string &eventString); @@ -189,6 +189,16 @@ public: */ timeval GetTimestamp() const; + /** + * Add a timestamp to the event string, if one does not already exist + * TODO: make this an instance method that operates on the std::map + * instead of the string. We must fix zfsd's CaseFile serialization + * routines first, so that they don't need the raw event string. + * + * \param[in,out] eventString The devd event string to modify + */ + static void TimestampEventString(std::string &eventString); + /** * Access all parsed key => value pairs. */ @@ -277,7 +287,7 @@ class DevfsEvent : public Event { public: /** Specialized Event object factory for Devfs events. */ - static BuildMethod DevfsEventBuilder; + static BuildMethod Builder; virtual Event *DeepCopy() const; @@ -326,7 +336,7 @@ class ZfsEvent : public Event { public: /** Specialized Event object factory for ZFS events. */ - static BuildMethod ZfsEventBuilder; + static BuildMethod Builder; virtual Event *DeepCopy() const; diff --git a/lib/libdevctl/event_factory.cc b/lib/libdevctl/event_factory.cc index 364e0d09687..d705ec8e35d 100644 --- a/lib/libdevctl/event_factory.cc +++ b/lib/libdevctl/event_factory.cc @@ -88,8 +88,10 @@ EventFactory::Build(Event::Type type, NVPairMap &nvpairs, if (foundMethod != m_registry.end()) buildMethod = foundMethod->second; - if (buildMethod == NULL) + if (buildMethod == NULL) { + delete &nvpairs; return (NULL); + } return (buildMethod(type, nvpairs, eventString)); } diff --git a/lib/libdevctl/exception.cc b/lib/libdevctl/exception.cc index 4d242df29f4..c20c2c5b6b3 100644 --- a/lib/libdevctl/exception.cc +++ b/lib/libdevctl/exception.cc @@ -37,6 +37,8 @@ #include +#include +#include #include #include From d931b81aeb0657b9739c3a80e9a83dba9118b30d Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Thu, 14 Aug 2014 18:39:13 +0000 Subject: [PATCH 89/89] sbin/devd/devd.8 Fix grammar bug. Reported by: ian Sponsored by: Spectra Logic Corporation --- sbin/devd/devd.8 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sbin/devd/devd.8 b/sbin/devd/devd.8 index 57d4fe3dc94..15be5719e96 100644 --- a/sbin/devd/devd.8 +++ b/sbin/devd/devd.8 @@ -148,7 +148,7 @@ The socket used by .Nm to communicate with its clients. .It Pa /var/run/devd.pipe -An deprecated socket retained for use with old clients. +A deprecated socket retained for use with old clients. .El .Sh SEE ALSO .Xr devctl 4 ,