diff options
author | eschrock <none@none> | 2007-05-10 09:20:21 -0700 |
---|---|---|
committer | eschrock <none@none> | 2007-05-10 09:20:21 -0700 |
commit | 24db46411fd54f70c35b94bb952eb7ba040e43b4 (patch) | |
tree | 4d6a0b15db5040a8b878a0f7238edd0635a0769b /usr/src | |
parent | 9e9e6ab82d4247028c312ff50a65b8a05a194b33 (diff) | |
download | illumos-gate-24db46411fd54f70c35b94bb952eb7ba040e43b4.tar.gz |
PSARC 2007/202 FMA Generic Disk Monitoring Events
6521578 fmd dev scheme should leverage libtopo
6521579 libtopo dev enumerator should support TOPO_METH_PRESENT
6521582 dev scheme should respect FM_FMRI_DEV_ID
6521586 need generic disk status monitoring and diagnosis
6521591 fmd should provide a method for generating ENAs
6521600 sata libtopo module should generate dev:// ASRUs
6529061 fmd should provide an entry point for topology changes
6532208 fmd resource cache should be updated in response to EC_DEVFS sysevents
6537251 fmd dumped core while trying to print an error
6537305 ::topo_node is broken
6537762 fmd should not automatically mark faults as repaired on removal
6544740 fmd should reference count topo handles
6544741 dev scheme should support unusable method
6545681 libtopo should prevent invalid serial numbers in authority
6551464 topo_fmri_fru() is broken
6551466 hc_is_present() is broken
--HG--
rename : usr/src/cmd/fm/modules/i86pc/sfx4500-disk/fault_analyze.c => deleted_files/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/fault_analyze.c
rename : usr/src/cmd/fm/modules/i86pc/sfx4500-disk/fault_analyze.h => deleted_files/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/fault_analyze.h
rename : usr/src/cmd/fm/modules/i86pc/sfx4500-disk/fault_mgr.c => deleted_files/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/fault_mgr.c
rename : usr/src/cmd/fm/modules/i86pc/sfx4500-disk/fault_mgr.h => deleted_files/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/fault_mgr.h
rename : usr/src/cmd/fm/modules/i86pc/sfx4500-disk/fm_disk_events.h => deleted_files/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/fm_disk_events.h
rename : usr/src/cmd/fm/modules/i86pc/sfx4500-disk/scsi_util.c => deleted_files/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/scsi_util.c
rename : usr/src/cmd/fm/modules/i86pc/sfx4500-disk/scsi_util.h => deleted_files/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/scsi_util.h
Diffstat (limited to 'usr/src')
89 files changed, 4812 insertions, 4685 deletions
diff --git a/usr/src/cmd/fm/eversholt/files/common/disk.esc b/usr/src/cmd/fm/eversholt/files/common/disk.esc new file mode 100644 index 0000000000..6316f1d9de --- /dev/null +++ b/usr/src/cmd/fm/eversholt/files/common/disk.esc @@ -0,0 +1,67 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * The disk DE provides a very simple 1-to-1 mapping between SCSI disk events + * generated by the disk-transport fmd module, and the resulting faults. + */ + +#pragma dictionary "DISK" + +#define P disk + +fru P; +asru P; + +/* + * Fault events. + */ +event fault.io.disk.over-temperature@P, + FITrate=10, FRU=P, ASRU=P; +event fault.io.disk.predictive-failure@P, FITrate=10, + FITrate=10, FRU=P, ASRU=P; +event fault.io.disk.self-test-failure@P, FITrate=10, + FITrate=10, FRU=P, ASRU=P; + +/* + * ereports. + */ +event ereport.io.scsi.disk.over-temperature@P; +event ereport.io.scsi.disk.predictive-failure@P; +event ereport.io.scsi.disk.self-test-failure@P; + +/* + * Propagations. + */ +prop fault.io.disk.over-temperature@P -> + ereport.io.scsi.disk.over-temperature@P; + +prop fault.io.disk.self-test-failure@P -> + ereport.io.scsi.disk.self-test-failure@P; + +prop fault.io.disk.predictive-failure@P -> + ereport.io.scsi.disk.predictive-failure@P; diff --git a/usr/src/cmd/fm/eversholt/files/i386/Makefile b/usr/src/cmd/fm/eversholt/files/i386/Makefile index 2d071da63b..f0b5a31fc7 100644 --- a/usr/src/cmd/fm/eversholt/files/i386/Makefile +++ b/usr/src/cmd/fm/eversholt/files/i386/Makefile @@ -19,13 +19,13 @@ # CDDL HEADER END # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" SUBDIRS=i86pc -EFT_COMMON_FILES= pci.eft pciex.eft sca500.eft sca1000.eft +EFT_COMMON_FILES= pci.eft pciex.eft sca500.eft sca1000.eft disk.eft include ../../../Makefile.subdirs diff --git a/usr/src/cmd/fm/eversholt/files/sparc/Makefile b/usr/src/cmd/fm/eversholt/files/sparc/Makefile index e951084fd1..41f59a99e8 100644 --- a/usr/src/cmd/fm/eversholt/files/sparc/Makefile +++ b/usr/src/cmd/fm/eversholt/files/sparc/Makefile @@ -19,13 +19,13 @@ # CDDL HEADER END # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" SUBDIRS=sun4u sun4v SUNW,Sun-Fire-15000 -EFT_COMMON_FILES= pci.eft pciex.eft sca500.eft sca1000.eft +EFT_COMMON_FILES= pci.eft pciex.eft sca500.eft sca1000.eft disk.eft include ../../../Makefile.subdirs diff --git a/usr/src/cmd/fm/fmd/common/fmd.c b/usr/src/cmd/fm/fmd/common/fmd.c index 902995da14..5d6b4d55b9 100644 --- a/usr/src/cmd/fm/fmd/common/fmd.c +++ b/usr/src/cmd/fm/fmd/common/fmd.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -69,7 +69,7 @@ extern const nv_alloc_ops_t fmd_nv_alloc_ops; /* see fmd_nv.c */ -const char _fmd_version[] = "1.1"; /* daemon version string */ +const char _fmd_version[] = "1.2"; /* daemon version string */ static char _fmd_plat[MAXNAMELEN]; /* native platform string */ static char _fmd_isa[MAXNAMELEN]; /* native instruction set */ static struct utsname _fmd_uts; /* native uname(2) info */ diff --git a/usr/src/cmd/fm/fmd/common/fmd_api.c b/usr/src/cmd/fm/fmd/common/fmd_api.c index 89c9a44869..b9a9e69dff 100644 --- a/usr/src/cmd/fm/fmd/common/fmd_api.c +++ b/usr/src/cmd/fm/fmd/common/fmd_api.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -34,6 +34,7 @@ #include <limits.h> #include <syslog.h> #include <alloca.h> +#include <stddef.h> #include <fmd_module.h> #include <fmd_api.h> @@ -368,7 +369,7 @@ fmd_hdl_register(fmd_hdl_t *hdl, int version, const fmd_hdl_info_t *mip) * the module thread to which we assigned this client handle. The info * provided for the handle must be valid and have the minimal settings. */ - if (version > FMD_API_VERSION_3) + if (version > FMD_API_VERSION_4) return (fmd_hdl_register_error(mp, EFMD_VER_NEW)); if (version < FMD_API_VERSION_1) @@ -392,7 +393,10 @@ fmd_hdl_register(fmd_hdl_t *hdl, int version, const fmd_hdl_info_t *mip) bzero(&ops, sizeof (ops)); if (version < FMD_API_VERSION_3) - bcopy(mip->fmdi_ops, &ops, sizeof (ops) - sizeof (void *)); + bcopy(mip->fmdi_ops, &ops, offsetof(fmd_hdl_ops_t, fmdo_send)); + else if (version < FMD_API_VERSION_4) + bcopy(mip->fmdi_ops, &ops, + offsetof(fmd_hdl_ops_t, fmdo_topo)); else bcopy(mip->fmdi_ops, &ops, sizeof (ops)); @@ -408,6 +412,8 @@ fmd_hdl_register(fmd_hdl_t *hdl, int version, const fmd_hdl_info_t *mip) ops.fmdo_gc = (void (*)())fmd_hdl_nop; if (ops.fmdo_send == NULL) ops.fmdo_send = (int (*)())fmd_hdl_nop; + if (ops.fmdo_topo == NULL) + ops.fmdo_topo = (void (*)())fmd_hdl_nop; /* * Make two passes through the property array to initialize the formals @@ -691,7 +697,7 @@ fmd_hdl_opendict(fmd_hdl_t *hdl, const char *dict) } topo_hdl_t * -fmd_hdl_topology(fmd_hdl_t *hdl, int v) +fmd_hdl_topo_hold(fmd_hdl_t *hdl, int v) { fmd_module_t *mp = fmd_api_module_lock(hdl); topo_hdl_t *thp; @@ -701,12 +707,25 @@ fmd_hdl_topology(fmd_hdl_t *hdl, int v) "fmd version %d != client version %d\n", TOPO_VERSION, v); } - thp = fmd_topo_handle(v); + thp = fmd_module_topo_hold(mp); + ASSERT(thp != NULL); fmd_module_unlock(mp); return (thp); } +void +fmd_hdl_topo_rele(fmd_hdl_t *hdl, topo_hdl_t *thp) +{ + fmd_module_t *mp = fmd_api_module_lock(hdl); + + if (fmd_module_topo_rele(mp, thp) != 0) + fmd_api_error(mp, EFMD_MOD_TOPO, "failed to release invalid " + "topo handle: %p\n", (void *)thp); + + fmd_module_unlock(mp); +} + void * fmd_hdl_alloc(fmd_hdl_t *hdl, size_t size, int flags) { @@ -1668,12 +1687,12 @@ fmd_nvl_create_fault(fmd_hdl_t *hdl, const char *class, char *loc = NULL; int err; - thp = fmd_hdl_topology(hdl, TOPO_VERSION); - mp = fmd_api_module_lock(hdl); if (class == NULL || class[0] == '\0') fmd_api_error(mp, EFMD_NVL_INVAL, "invalid fault class\n"); + thp = fmd_module_topo_hold(mp); + /* * Try to find the location label for this resource */ @@ -1681,11 +1700,14 @@ fmd_nvl_create_fault(fmd_hdl_t *hdl, const char *class, nvl = fmd_protocol_fault(class, certainty, asru, fru, rsrc, loc); - fmd_module_unlock(mp); - if (loc != NULL) topo_hdl_strfree(thp, loc); + err = fmd_module_topo_rele(mp, thp); + ASSERT(err == 0); + + fmd_module_unlock(mp); + return (nvl); } @@ -1834,6 +1856,13 @@ fmd_event_local(fmd_hdl_t *hdl, fmd_event_t *ep) return (((fmd_event_impl_t *)ep)->ev_flags & FMD_EVF_LOCAL); } +/*ARGSUSED*/ +uint64_t +fmd_event_ena_create(fmd_hdl_t *hdl) +{ + return (fmd_ena()); +} + fmd_xprt_t * fmd_xprt_open(fmd_hdl_t *hdl, uint_t flags, nvlist_t *auth, void *data) { diff --git a/usr/src/cmd/fm/fmd/common/fmd_api.h b/usr/src/cmd/fm/fmd/common/fmd_api.h index 556d5641e8..00513f298a 100644 --- a/usr/src/cmd/fm/fmd/common/fmd_api.h +++ b/usr/src/cmd/fm/fmd/common/fmd_api.h @@ -20,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -50,8 +50,9 @@ extern "C" { #define FMD_API_VERSION_1 1 #define FMD_API_VERSION_2 2 #define FMD_API_VERSION_3 3 +#define FMD_API_VERSION_4 4 -#define FMD_API_VERSION FMD_API_VERSION_3 +#define FMD_API_VERSION FMD_API_VERSION_4 typedef struct fmd_hdl fmd_hdl_t; typedef struct fmd_event fmd_event_t; @@ -107,6 +108,7 @@ typedef struct fmd_hdl_ops { void (*fmdo_stats)(fmd_hdl_t *); void (*fmdo_gc)(fmd_hdl_t *); int (*fmdo_send)(fmd_hdl_t *, fmd_xprt_t *, fmd_event_t *, nvlist_t *); + void (*fmdo_topo)(fmd_hdl_t *, struct topo_hdl *); } fmd_hdl_ops_t; #define FMD_SEND_SUCCESS 0 /* fmdo_send queued event */ @@ -133,7 +135,8 @@ extern void fmd_hdl_setspecific(fmd_hdl_t *, void *); extern void *fmd_hdl_getspecific(fmd_hdl_t *); extern void fmd_hdl_opendict(fmd_hdl_t *, const char *); -extern struct topo_hdl *fmd_hdl_topology(fmd_hdl_t *, int); +extern struct topo_hdl *fmd_hdl_topo_hold(fmd_hdl_t *, int); +extern void fmd_hdl_topo_rele(fmd_hdl_t *, struct topo_hdl *); #define FMD_NOSLEEP 0x0 /* do not sleep or retry on failure */ #define FMD_SLEEP 0x1 /* sleep or retry if alloc fails */ @@ -227,6 +230,8 @@ extern int fmd_nvl_fmri_contains(fmd_hdl_t *, nvlist_t *, nvlist_t *); extern nvlist_t *fmd_nvl_fmri_translate(fmd_hdl_t *, nvlist_t *, nvlist_t *); extern int fmd_event_local(fmd_hdl_t *, fmd_event_t *); +extern uint64_t fmd_event_ena_create(fmd_hdl_t *); + #define FMD_XPRT_RDONLY 0x1 /* transport is read-only */ #define FMD_XPRT_RDWR 0x3 /* transport is read-write */ diff --git a/usr/src/cmd/fm/fmd/common/fmd_api.map b/usr/src/cmd/fm/fmd/common/fmd_api.map index 7d56fe4f92..6acf6a003d 100644 --- a/usr/src/cmd/fm/fmd/common/fmd_api.map +++ b/usr/src/cmd/fm/fmd/common/fmd_api.map @@ -2,9 +2,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -19,7 +18,7 @@ # # CDDL HEADER END # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -52,6 +51,7 @@ fmd_case_uulookup = FUNCTION extern; fmd_event_local = FUNCTION extern; + fmd_event_ena_create = FUNCTION extern; fmd_hdl_abort = FUNCTION extern; fmd_hdl_alloc = FUNCTION extern; @@ -65,7 +65,8 @@ fmd_hdl_strdup = FUNCTION extern; fmd_hdl_strfree = FUNCTION extern; fmd_hdl_subscribe = FUNCTION extern; - fmd_hdl_topology = FUNCTION extern; + fmd_hdl_topo_hold = FUNCTION extern; + fmd_hdl_topo_rele = FUNCTION extern; fmd_hdl_unregister = FUNCTION extern; fmd_hdl_unsubscribe = FUNCTION extern; fmd_hdl_vabort = FUNCTION extern; diff --git a/usr/src/cmd/fm/fmd/common/fmd_dr.c b/usr/src/cmd/fm/fmd/common/fmd_dr.c index d5fc48e23f..46fbe24bca 100644 --- a/usr/src/cmd/fm/fmd/common/fmd_dr.c +++ b/usr/src/cmd/fm/fmd/common/fmd_dr.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -40,13 +40,21 @@ * avoids the complexity of direct participation in DR, avoids the need for * resource-specific processing of DR events, and is relatively easy to port * to other systems that support dynamic reconfiguration. + * + * The dr generation is only incremented in response to hardware changes. Since + * ASRUs can be in any scheme, including the device scheme, we must also be + * aware of software configuration changes which may affect the resource cache. + * In addition, we take a snapshot of the topology whenever a reconfiguration + * event occurs and notify any modules of the change. */ #include <sys/types.h> +#include <sys/sunddi.h> #include <sys/sysevent/dr.h> #include <sys/sysevent/eventdefs.h> #include <stdio.h> +#include <string.h> #include <unistd.h> #include <libsysevent.h> @@ -56,59 +64,76 @@ #include <fmd_asru.h> #include <fmd_error.h> +#include <fmd_event.h> #include <fmd_fmri.h> +#include <fmd_module.h> #include <fmd_subr.h> +#include <fmd_topo.h> #include <fmd.h> static void -fmd_dr_repair_containee(fmd_asru_t *ee, void *er) -{ - if ((ee->asru_flags & FMD_ASRU_FAULTY) && - fmd_fmri_contains(er, ee->asru_fmri) > 0) - (void) fmd_asru_clrflags(ee, FMD_ASRU_FAULTY, NULL, NULL); -} - -/*ARGSUSED*/ -static void -fmd_dr_rcache_sync(fmd_asru_t *ap, void *arg) +fmd_dr_event(sysevent_t *sep) { - if (fmd_fmri_present(ap->asru_fmri) != 0) - return; - - if (!fmd_asru_clrflags(ap, FMD_ASRU_FAULTY, NULL, NULL)) - return; + uint64_t gen; + fmd_event_t *e; + const char *class = sysevent_get_class_name(sep); + hrtime_t evtime; + fmd_topo_t *ftp, *prev; + boolean_t update_topo = B_FALSE; /* - * We've located the requested ASRU, and have repaired it. Now - * traverse the ASRU cache, looking for any faulty entries that - * are contained by this one. If we find any, repair them too. + * The dr generation is only changed in response to DR events. */ - fmd_asru_hash_apply(fmd.d_asrus, fmd_dr_repair_containee, - ap->asru_fmri); -} + if (strcmp(class, EC_DR) == 0) { + update_topo = B_TRUE; -static void -fmd_dr_event(sysevent_t *sep) -{ - uint64_t gen; + (void) pthread_mutex_lock(&fmd.d_stats_lock); + gen = fmd.d_stats->ds_dr_gen.fmds_value.ui64++; + (void) pthread_mutex_unlock(&fmd.d_stats_lock); + + TRACE((FMD_DBG_XPRT, "dr event %p, gen=%llu", + (void *)sep, gen)); + } /* - * If the event target is in the R$ and this sysevent indicates it was - * removed, remove it from the R$ also. + * Take a topo snapshot and notify modules of the change. Picking an + * accurate time here is difficult. On one hand, we have the timestamp + * of the underlying sysevent, indicating when the reconfiguration event + * occurred. On the other hand, we are taking the topo snapshot + * asynchronously, and hence the timestamp of the snapshot is the + * current time. Pretending this topo snapshot was valid at the time + * the sysevent was posted seems wrong, so we instead opt for the + * current time as an upper bound on the snapshot validity. + * + * Along these lines, we keep track of the last time we dispatched a + * topo snapshot. If the sysevent occurred before the last topo + * snapshot, then don't bother dispatching another topo change event. + * We've already indicated (to the best of our ability) the change in + * topology. This prevents endless topo snapshots in response to a + * flurry of sysevents. */ - (void) fmd_asru_hash_apply(fmd.d_asrus, fmd_dr_rcache_sync, NULL); + sysevent_get_time(sep, &evtime); + prev = fmd_topo_hold(); + if (evtime <= prev->ft_time && + fmd.d_clockops == &fmd_timeops_native) { + fmd_topo_rele(prev); + return; + } + fmd_topo_rele(prev); - (void) pthread_mutex_lock(&fmd.d_stats_lock); - gen = fmd.d_stats->ds_dr_gen.fmds_value.ui64++; - (void) pthread_mutex_unlock(&fmd.d_stats_lock); + if (update_topo) + fmd_topo_update(); - TRACE((FMD_DBG_XPRT, "dr event %p, gen=%llu", (void *)sep, gen)); + ftp = fmd_topo_hold(); + e = fmd_event_create(FMD_EVT_TOPO, ftp->ft_time, NULL, ftp); + fmd_modhash_dispatch(fmd.d_mod_hash, e); } void fmd_dr_init(void) { - const char *subclass = ESC_DR_AP_STATE_CHANGE; + const char *drsubclass = ESC_DR_AP_STATE_CHANGE; + const char *devsubclass = EC_SUB_ALL; if (geteuid() != 0) return; /* legacy sysevent mechanism is still root-only */ @@ -116,8 +141,12 @@ fmd_dr_init(void) if ((fmd.d_dr_hdl = sysevent_bind_handle(fmd_dr_event)) == NULL) fmd_error(EFMD_EXIT, "failed to bind handle for DR sysevent"); - if (sysevent_subscribe_event(fmd.d_dr_hdl, EC_DR, &subclass, 1) == -1) - fmd_error(EFMD_EXIT, "failed to subscribe for DR sysevent"); + if (sysevent_subscribe_event(fmd.d_dr_hdl, EC_DR, &drsubclass, 1) == -1) + fmd_error(EFMD_EXIT, "failed to subscribe to DR sysevent"); + + if (sysevent_subscribe_event(fmd.d_dr_hdl, EC_DEVFS, + &devsubclass, 1) == -1) + fmd_error(EFMD_EXIT, "failed to subscribe to devfs sysevent"); } void diff --git a/usr/src/cmd/fm/fmd/common/fmd_event.c b/usr/src/cmd/fm/fmd/common/fmd_event.c index 41ece9012f..eddd1e2b7a 100644 --- a/usr/src/cmd/fm/fmd/common/fmd_event.c +++ b/usr/src/cmd/fm/fmd/common/fmd_event.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -37,6 +37,7 @@ #include <fmd_case.h> #include <fmd_log.h> #include <fmd_time.h> +#include <fmd_topo.h> #include <fmd_ctl.h> #include <fmd.h> @@ -216,6 +217,9 @@ fmd_event_destroy(fmd_event_t *e) case FMD_EVT_CTL: fmd_ctl_fini(ep->ev_data); break; + case FMD_EVT_TOPO: + fmd_topo_rele(ep->ev_data); + break; } if (ep->ev_nvl != NULL) diff --git a/usr/src/cmd/fm/fmd/common/fmd_event.h b/usr/src/cmd/fm/fmd/common/fmd_event.h index dd5c07bb82..56d7c20c11 100644 --- a/usr/src/cmd/fm/fmd/common/fmd_event.h +++ b/usr/src/cmd/fm/fmd/common/fmd_event.h @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -21,7 +20,7 @@ */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -65,7 +64,8 @@ typedef struct fmd_event_impl { #define FMD_EVT_GC 4 /* garbage collection request */ #define FMD_EVT_CTL 5 /* fmd control event (see fmd_ctl.c) */ #define FMD_EVT_PUBLISH 6 /* case publish request */ -#define FMD_EVT_NTYPES 7 /* number of event types */ +#define FMD_EVT_TOPO 7 /* topology change notification */ +#define FMD_EVT_NTYPES 8 /* number of event types */ #define FMD_EVS_DISCARDED 0 /* discarded by all subscribers */ #define FMD_EVS_RECEIVED 1 /* received but not yet processed */ diff --git a/usr/src/cmd/fm/fmd/common/fmd_fmri.c b/usr/src/cmd/fm/fmd/common/fmd_fmri.c index 6953247df5..e3630e1025 100644 --- a/usr/src/cmd/fm/fmd/common/fmd_fmri.c +++ b/usr/src/cmd/fm/fmd/common/fmd_fmri.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -82,7 +82,7 @@ fmd_fmri_warn(const char *format, ...) * Convert an input string to a URI escaped string and return the new string. * RFC2396 Section 2.4 says that data must be escaped if it does not have a * representation using an unreserved character, where an unreserved character - * is one that is either alphanumberic or one of the marks defined in S2.3. + * is one that is either alphanumeric or one of the marks defined in S2.3. */ static size_t fmd_fmri_uriescape(const char *s, const char *xmark, char *buf, size_t len) @@ -230,9 +230,22 @@ fmd_fmri_get_drgen(void) } struct topo_hdl * -fmd_fmri_topology(int version) +fmd_fmri_topo_hold(int version) { - return (fmd_topo_handle(version)); + fmd_topo_t *ftp; + + if (version != TOPO_VERSION) + return (NULL); + + ftp = fmd_topo_hold(); + + return (ftp->ft_hdl); +} + +void +fmd_fmri_topo_rele(struct topo_hdl *thp) +{ + fmd_topo_rele_hdl(thp); } /* diff --git a/usr/src/cmd/fm/fmd/common/fmd_fmri.h b/usr/src/cmd/fm/fmd/common/fmd_fmri.h index e16af0fbfa..b5bbc0fcc6 100644 --- a/usr/src/cmd/fm/fmd/common/fmd_fmri.h +++ b/usr/src/cmd/fm/fmd/common/fmd_fmri.h @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -21,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -81,7 +80,8 @@ extern const char *fmd_fmri_get_platform(void); extern uint64_t fmd_fmri_get_drgen(void); -extern struct topo_hdl *fmd_fmri_topology(int); +extern struct topo_hdl *fmd_fmri_topo_hold(int); +extern void fmd_fmri_topo_rele(struct topo_hdl *); /* * The following entry points are to be implemented by each scheme: diff --git a/usr/src/cmd/fm/fmd/common/fmd_fmri.map b/usr/src/cmd/fm/fmd/common/fmd_fmri.map index 51ac2f9114..aec0c40e3a 100644 --- a/usr/src/cmd/fm/fmd/common/fmd_fmri.map +++ b/usr/src/cmd/fm/fmd/common/fmd_fmri.map @@ -1,13 +1,12 @@ # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -37,5 +36,6 @@ fmd_fmri_get_rootdir = FUNCTION extern; fmd_fmri_get_platform = FUNCTION extern; fmd_fmri_get_drgen = FUNCTION extern; - fmd_fmri_topology = FUNCTION extern; + fmd_fmri_topo_hold = FUNCTION extern; + fmd_fmri_topo_rele = FUNCTION extern; }; diff --git a/usr/src/cmd/fm/fmd/common/fmd_mdb.c b/usr/src/cmd/fm/fmd/common/fmd_mdb.c index 0a20867e7c..d2788559cb 100644 --- a/usr/src/cmd/fm/fmd/common/fmd_mdb.c +++ b/usr/src/cmd/fm/fmd/common/fmd_mdb.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -21,7 +20,7 @@ */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -516,6 +515,9 @@ fmd_event(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) case FMD_EVT_PUBLISH: (void) strcpy(type, "PUBL"); break; + case FMD_EVT_TOPO: + (void) strcpy(type, "TOPO"); + break; default: (void) mdb_snprintf(type, sizeof (type), "%u", ev.ev_type); } diff --git a/usr/src/cmd/fm/fmd/common/fmd_module.c b/usr/src/cmd/fm/fmd/common/fmd_module.c index c0e52a4b59..ad72e3c9ce 100644 --- a/usr/src/cmd/fm/fmd/common/fmd_module.c +++ b/usr/src/cmd/fm/fmd/common/fmd_module.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -21,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -50,6 +49,7 @@ #include <fmd_buf.h> #include <fmd_ckpt.h> #include <fmd_xprt.h> +#include <fmd_topo.h> #include <fmd.h> @@ -208,6 +208,8 @@ fmd_module_create(const char *path, const fmd_modops_t *ops) fmd_buf_hash_create(&mp->mod_bufs); fmd_serd_hash_create(&mp->mod_serds); + mp->mod_topo_current = fmd_topo_hold(); + (void) pthread_mutex_lock(&fmd.d_mod_lock); fmd_list_append(&fmd.d_mod_list, mp); (void) pthread_mutex_unlock(&fmd.d_mod_lock); @@ -337,6 +339,8 @@ fmd_module_untimeout(fmd_idspace_t *ids, id_t id, fmd_module_t *mp) void fmd_module_unload(fmd_module_t *mp) { + fmd_modtopo_t *mtp; + (void) pthread_mutex_lock(&mp->mod_lock); if (mp->mod_flags & FMD_MOD_QUIT) { @@ -386,6 +390,12 @@ fmd_module_unload(fmd_module_t *mp) (void) fmd_buf_hash_destroy(&mp->mod_bufs); fmd_serd_hash_destroy(&mp->mod_serds); + while ((mtp = fmd_list_next(&mp->mod_topolist)) != NULL) { + fmd_list_delete(&mp->mod_topolist, mtp); + fmd_topo_rele(mtp->mt_topo); + fmd_free(mtp, sizeof (fmd_modtopo_t)); + } + fmd_module_unlock(mp); fmd_dprintf(FMD_DBG_MOD, "unloaded module %s\n", mp->mod_name); } @@ -416,6 +426,9 @@ fmd_module_destroy(fmd_module_t *mp) fmd_list_delete(&fmd.d_mod_list, mp); (void) pthread_mutex_unlock(&fmd.d_mod_lock); + if (mp->mod_topo_current != NULL) + fmd_topo_rele(mp->mod_topo_current); + /* * Once the module is no longer processing events and no longer visible * through any program data structures, we can free all of its content. @@ -548,6 +561,12 @@ fmd_module_dispatch(fmd_module_t *mp, fmd_event_t *e) case FMD_EVT_PUBLISH: fmd_case_publish(ep->ev_data, FMD_CASE_CURRENT); break; + case FMD_EVT_TOPO: + fmd_topo_rele(mp->mod_topo_current); + mp->mod_topo_current = (fmd_topo_t *)ep->ev_data; + fmd_topo_addref(mp->mod_topo_current); + ops->fmdo_topo(hdl, mp->mod_topo_current->ft_hdl); + break; } } @@ -1311,3 +1330,40 @@ fmd_modstat_snapshot(fmd_module_t *mp, fmd_ustat_snap_t *uss) return (err); } + +struct topo_hdl * +fmd_module_topo_hold(fmd_module_t *mp) +{ + fmd_modtopo_t *mtp; + + ASSERT(fmd_module_locked(mp)); + + mtp = fmd_zalloc(sizeof (fmd_modtopo_t), FMD_SLEEP); + mtp->mt_topo = mp->mod_topo_current; + fmd_topo_addref(mtp->mt_topo); + fmd_list_prepend(&mp->mod_topolist, mtp); + + return (mtp->mt_topo->ft_hdl); +} + +int +fmd_module_topo_rele(fmd_module_t *mp, struct topo_hdl *hdl) +{ + fmd_modtopo_t *mtp; + + ASSERT(fmd_module_locked(mp)); + + for (mtp = fmd_list_next(&mp->mod_topolist); mtp != NULL; + mtp = fmd_list_next(mtp)) { + if (mtp->mt_topo->ft_hdl == hdl) + break; + } + + if (mtp == NULL) + return (-1); + + fmd_list_delete(&mp->mod_topolist, mtp); + fmd_topo_rele(mtp->mt_topo); + fmd_free(mtp, sizeof (fmd_modtopo_t)); + return (0); +} diff --git a/usr/src/cmd/fm/fmd/common/fmd_module.h b/usr/src/cmd/fm/fmd/common/fmd_module.h index d7b4de3af1..a927847e5f 100644 --- a/usr/src/cmd/fm/fmd/common/fmd_module.h +++ b/usr/src/cmd/fm/fmd/common/fmd_module.h @@ -20,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -44,6 +44,7 @@ extern "C" { #include <fmd_buf.h> #include <fmd_api.h> #include <fmd_eventq.h> +#include <fmd_topo.h> struct fmd_module; /* see below */ struct fmd_thread; /* see <fmd_thread.h> */ @@ -133,6 +134,8 @@ typedef struct fmd_module { fmd_buf_hash_t mod_bufs; /* hash of bufs owned by this module */ fmd_serd_hash_t mod_serds; /* hash of serd engs owned by module */ fmd_list_t mod_transports; /* list of transports owned by module */ + fmd_list_t mod_topolist; /* list of held topo handles */ + fmd_topo_t *mod_topo_current; /* current libtopo snapshot */ } fmd_module_t; #define FMD_MOD_INIT 0x001 /* mod_ops->mop_init() has completed */ @@ -152,6 +155,11 @@ typedef struct fmd_modtimer { id_t mt_id; /* timer ID (or -1 if still pending) */ } fmd_modtimer_t; +typedef struct fmd_modtopo { + fmd_list_t mt_link; /* link on module topo list */ + fmd_topo_t *mt_topo; /* topo handle */ +} fmd_modtopo_t; + extern const fmd_modops_t fmd_bltin_ops; /* see fmd/common/fmd_builtin.c */ extern const fmd_modops_t fmd_rtld_ops; /* see fmd/common/fmd_rtld.c */ extern const fmd_modops_t fmd_proc_ops; /* see fmd/common/fmd_proc.c */ @@ -208,6 +216,9 @@ extern void fmd_modhash_dispatch(fmd_modhash_t *, fmd_event_t *); extern void fmd_modstat_publish(fmd_module_t *); extern int fmd_modstat_snapshot(fmd_module_t *, struct fmd_ustat_snap *); +extern struct topo_hdl *fmd_module_topo_hold(fmd_module_t *); +extern int fmd_module_topo_rele(fmd_module_t *, struct topo_hdl *); + #ifdef __cplusplus } #endif diff --git a/usr/src/cmd/fm/fmd/common/fmd_subr.c b/usr/src/cmd/fm/fmd/common/fmd_subr.c index 5de02d2912..46878ba0f4 100644 --- a/usr/src/cmd/fm/fmd/common/fmd_subr.c +++ b/usr/src/cmd/fm/fmd/common/fmd_subr.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -21,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -183,7 +182,8 @@ fmd_verror(int err, const char *format, va_list ap) * FMD_LF_BUSY flag is set, we can't attempt to log the event because * a replay is running and we will deadlock on ourself in log_append. */ - if (!fmd.d_fg && fmd.d_running && tp->thr_errdepth == 1 && + if (!fmd.d_fg && fmd.d_running && + tp != NULL && tp->thr_errdepth == 1 && (nvl = fmd_protocol_fmderror(err, format, ap)) != NULL) { (void) nvlist_lookup_string(nvl, FM_CLASS, &class); diff --git a/usr/src/cmd/fm/fmd/common/fmd_topo.c b/usr/src/cmd/fm/fmd/common/fmd_topo.c index bef8f4fbdd..2f76fccaeb 100644 --- a/usr/src/cmd/fm/fmd/common/fmd_topo.c +++ b/usr/src/cmd/fm/fmd/common/fmd_topo.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -51,15 +51,29 @@ #include <fm/libtopo.h> static void +fmd_topo_rele_locked(fmd_topo_t *ftp) +{ + ASSERT(MUTEX_HELD(&fmd.d_topo_lock)); + + if (--ftp->ft_refcount == 0) { + fmd_list_delete(&fmd.d_topo_list, ftp); + topo_close(ftp->ft_hdl); + fmd_free(ftp, sizeof (fmd_topo_t)); + } +} + +void fmd_topo_update(void) { int err; topo_hdl_t *tp; - fmd_topo_t *ftp; + fmd_topo_t *ftp, *prev; char *id; const char *name; - ASSERT(MUTEX_HELD(&fmd.d_topo_lock)); + (void) pthread_mutex_lock(&fmd.d_topo_lock); + + fmd.d_stats->ds_topo_drgen.fmds_value.ui64 = fmd_fmri_get_drgen(); name = fmd.d_rootdir != NULL && *fmd.d_rootdir != '\0' ? fmd.d_rootdir : NULL; @@ -79,41 +93,77 @@ fmd_topo_update(void) ftp = fmd_alloc(sizeof (fmd_topo_t), FMD_SLEEP); ftp->ft_hdl = tp; + ftp->ft_time = fmd_time_gethrtime(); fmd.d_stats->ds_topo_gen.fmds_value.ui64++; + + /* + * We always keep a reference count on the last topo snapshot taken. + * Release the previous snapshot (if present), and set the current + * reference count to 1. + */ + if ((prev = fmd_list_next(&fmd.d_topo_list)) != NULL) + fmd_topo_rele_locked(prev); + ftp->ft_refcount = 1; fmd_list_prepend(&fmd.d_topo_list, ftp); + (void) pthread_mutex_unlock(&fmd.d_topo_lock); } -topo_hdl_t * -fmd_topo_handle(int version) +fmd_topo_t * +fmd_topo_hold(void) { - uint64_t curgen; fmd_topo_t *ftp; - if (version != TOPO_VERSION) - return (NULL); - (void) pthread_mutex_lock(&fmd.d_topo_lock); - if ((curgen = fmd_fmri_get_drgen()) > - fmd.d_stats->ds_topo_drgen.fmds_value.ui64) { - fmd.d_stats->ds_topo_drgen.fmds_value.ui64 = curgen; - fmd_topo_update(); - } ftp = fmd_list_next(&fmd.d_topo_list); + ftp->ft_refcount++; (void) pthread_mutex_unlock(&fmd.d_topo_lock); - return ((topo_hdl_t *)ftp->ft_hdl); + return (ftp); } void -fmd_topo_init(void) +fmd_topo_addref(fmd_topo_t *ftp) { (void) pthread_mutex_lock(&fmd.d_topo_lock); - fmd_topo_update(); + ftp->ft_refcount++; + (void) pthread_mutex_unlock(&fmd.d_topo_lock); +} + +void +fmd_topo_rele(fmd_topo_t *ftp) +{ + (void) pthread_mutex_lock(&fmd.d_topo_lock); + + fmd_topo_rele_locked(ftp); + (void) pthread_mutex_unlock(&fmd.d_topo_lock); } void +fmd_topo_rele_hdl(topo_hdl_t *thp) +{ + fmd_topo_t *ftp; + + (void) pthread_mutex_lock(&fmd.d_topo_lock); + for (ftp = fmd_list_next(&fmd.d_topo_list); ftp != NULL; + ftp = fmd_list_next(ftp)) { + if (ftp->ft_hdl == thp) + break; + } + ASSERT(ftp != NULL); + + fmd_topo_rele_locked(ftp); + (void) pthread_mutex_unlock(&fmd.d_topo_lock); +} + +void +fmd_topo_init(void) +{ + fmd_topo_update(); +} + +void fmd_topo_fini(void) { fmd_topo_t *ftp; diff --git a/usr/src/cmd/fm/fmd/common/fmd_topo.h b/usr/src/cmd/fm/fmd/common/fmd_topo.h index f5e1bc2f8e..6bb84d7f92 100644 --- a/usr/src/cmd/fm/fmd/common/fmd_topo.h +++ b/usr/src/cmd/fm/fmd/common/fmd_topo.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -38,13 +38,20 @@ extern "C" { extern void fmd_topo_init(void); extern void fmd_topo_fini(void); -extern topo_hdl_t *fmd_topo_handle(int); typedef struct fmd_topo { fmd_list_t ft_list; topo_hdl_t *ft_hdl; + uint32_t ft_refcount; + hrtime_t ft_time; } fmd_topo_t; +extern void fmd_topo_update(void); +extern fmd_topo_t *fmd_topo_hold(void); +extern void fmd_topo_addref(fmd_topo_t *); +extern void fmd_topo_rele(fmd_topo_t *); +extern void fmd_topo_rele_hdl(topo_hdl_t *); + #ifdef __cplusplus } #endif diff --git a/usr/src/cmd/fm/fmdump/common/scheme.c b/usr/src/cmd/fm/fmdump/common/scheme.c index fd388807cc..425fbd302c 100644 --- a/usr/src/cmd/fm/fmdump/common/scheme.c +++ b/usr/src/cmd/fm/fmdump/common/scheme.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -302,12 +301,14 @@ fmd_fmri_warn(const char *format, ...) va_end(ap); } -/*ARGSUSED*/ struct topo_hdl * -fmd_fmri_topology(int version) +fmd_fmri_topo_hold(int version) { int err; + if (version != TOPO_VERSION) + return (NULL); + if (g_thp == NULL) { if ((g_thp = topo_open(TOPO_VERSION, "/", &err)) == NULL) { (void) fprintf(stderr, "topo_open failed: %s\n", @@ -318,3 +319,10 @@ fmd_fmri_topology(int version) return (g_thp); } + +/*ARGSUSED*/ +void +fmd_fmri_topo_rele(struct topo_hdl *thp) +{ + /* nothing to do */ +} diff --git a/usr/src/cmd/fm/modules/common/Makefile b/usr/src/cmd/fm/modules/common/Makefile index 852e6da8f1..36ba57d618 100644 --- a/usr/src/cmd/fm/modules/common/Makefile +++ b/usr/src/cmd/fm/modules/common/Makefile @@ -18,7 +18,6 @@ # # CDDL HEADER END # - # # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. @@ -26,14 +25,15 @@ # ident "%Z%%M% %I% %E% SMI" # -SUBDIRS = cpumem-retire \ - eversholt \ - io-retire \ - ip-transport \ - snmp-trapgen \ - sp-monitor \ - syslog-msgs \ - zfs-diagnosis \ +SUBDIRS = cpumem-retire \ + disk-transport \ + eversholt \ + io-retire \ + ip-transport \ + snmp-trapgen \ + sp-monitor \ + syslog-msgs \ + zfs-diagnosis \ zfs-retire include ../../Makefile.subdirs diff --git a/usr/src/cmd/fm/modules/common/cpumem-retire/cma_page.c b/usr/src/cmd/fm/modules/common/cpumem-retire/cma_page.c index 47f7f87cb2..9c81bf79f6 100644 --- a/usr/src/cmd/fm/modules/common/cpumem-retire/cma_page.c +++ b/usr/src/cmd/fm/modules/common/cpumem-retire/cma_page.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -170,13 +170,15 @@ cma_page_retire(fmd_hdl_t *hdl, nvlist_t *nvl, nvlist_t *asru, const char *uuid) strncmp(unumstr, "hc:/", 4) == 0) { int err; nvlist_t *unumfmri; - struct topo_hdl *thp = fmd_hdl_topology(hdl, TOPO_VERSION); + struct topo_hdl *thp = fmd_hdl_topo_hold(hdl, TOPO_VERSION); if (topo_fmri_str2nvl(thp, unumstr, &unumfmri, &err) != 0) { + fmd_hdl_topo_rele(hdl, thp); fmd_hdl_debug(hdl, "page retire str2nvl failed: %s\n", topo_strerror(err)); return (CMA_RA_FAILURE); } + fmd_hdl_topo_rele(hdl, thp); if (nvlist_dup(asru, &asrucp, 0) != 0) { fmd_hdl_debug(hdl, "page retire nvlist dup failed\n"); diff --git a/usr/src/cmd/fm/modules/common/disk-transport/Makefile b/usr/src/cmd/fm/modules/common/disk-transport/Makefile new file mode 100644 index 0000000000..2325d423b5 --- /dev/null +++ b/usr/src/cmd/fm/modules/common/disk-transport/Makefile @@ -0,0 +1,36 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +MODULE = disk-transport +CLASS = common +SRCS = disk_transport.c + +include ../../Makefile.plugin + +CFLAGS += $(INCS) +LINTFLAGS += $(INCS) +LDLIBS += -L$(ROOTLIB)/fm -ldiskstatus -ltopo +LDFLAGS += -R/usr/lib/fm diff --git a/usr/src/cmd/fm/modules/common/disk-transport/disk-transport.conf b/usr/src/cmd/fm/modules/common/disk-transport/disk-transport.conf new file mode 100644 index 0000000000..c8396be904 --- /dev/null +++ b/usr/src/cmd/fm/modules/common/disk-transport/disk-transport.conf @@ -0,0 +1,25 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" diff --git a/usr/src/cmd/fm/modules/common/disk-transport/disk_transport.c b/usr/src/cmd/fm/modules/common/disk-transport/disk_transport.c new file mode 100644 index 0000000000..0950a919da --- /dev/null +++ b/usr/src/cmd/fm/modules/common/disk-transport/disk_transport.c @@ -0,0 +1,355 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Disk error transport module + * + * This transport module is responsible for translating between disk errors + * and FMA ereports. It is a read-only transport module, and checks for the + * following failures: + * + * - overtemp + * - predictive failure + * - self-test failure + * + * These failures are detected via the TOPO_METH_DISK_STATUS method, which + * leverages libdiskstatus to do the actual analysis. This transport module is + * in charge of the following tasks: + * + * - discovering available devices + * - periodically checking devices + * - managing device addition/removal + */ + +#include <ctype.h> +#include <fm/fmd_api.h> +#include <fm/libdiskstatus.h> +#include <fm/libtopo.h> +#include <fm/topo_hc.h> +#include <fm/topo_mod.h> +#include <limits.h> +#include <string.h> +#include <sys/fm/io/scsi.h> +#include <sys/fm/protocol.h> + +static struct dt_stat { + fmd_stat_t dropped; +} dt_stats = { + { "dropped", FMD_TYPE_UINT64, "number of dropped ereports" } +}; + +typedef struct disk_monitor { + fmd_hdl_t *dm_hdl; + fmd_xprt_t *dm_xprt; + id_t dm_timer; + hrtime_t dm_interval; + char *dm_sim_search; + char *dm_sim_file; + boolean_t dm_timer_istopo; +} disk_monitor_t; + +static void +dt_post_ereport(fmd_hdl_t *hdl, fmd_xprt_t *xprt, const char *protocol, + const char *faultname, uint64_t ena, nvlist_t *detector, nvlist_t *payload) +{ + nvlist_t *nvl; + int e = 0; + char fullclass[PATH_MAX]; + + (void) snprintf(fullclass, sizeof (fullclass), "%s.io.%s.disk.%s", + FM_EREPORT_CLASS, protocol, faultname); + + if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) == 0) { + e |= nvlist_add_string(nvl, FM_CLASS, fullclass); + e |= nvlist_add_uint8(nvl, FM_VERSION, FM_EREPORT_VERSION); + e |= nvlist_add_uint64(nvl, FM_EREPORT_ENA, ena); + e |= nvlist_add_nvlist(nvl, FM_EREPORT_DETECTOR, detector); + e |= nvlist_merge(nvl, payload, 0); + + if (e == 0) { + fmd_xprt_post(hdl, xprt, nvl, 0); + } else { + nvlist_free(nvl); + dt_stats.dropped.fmds_value.ui64++; + } + } else { + dt_stats.dropped.fmds_value.ui64++; + } +} + +/* + * Check a single topo node for failure. This simply invokes the disk status + * method, and generates any ereports as necessary. + */ +static int +dt_analyze_disk(topo_hdl_t *thp, tnode_t *node, void *arg) +{ + nvlist_t *result; + nvlist_t *fmri, *faults; + char *protocol; + int err; + disk_monitor_t *dmp = arg; + uint64_t ena; + nvpair_t *elem; + boolean_t fault; + nvlist_t *details; + char *fmristr; + nvlist_t *in = NULL; + + if (topo_node_resource(node, &fmri, &err) != 0) { + fmd_hdl_error(dmp->dm_hdl, "failed to get fmri: %s\n", + topo_strerror(err)); + return (TOPO_WALK_ERR); + } + + if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0) { + nvlist_free(fmri); + return (TOPO_WALK_ERR); + } + + if (dmp->dm_sim_search) { + fmristr = NULL; + if (topo_fmri_nvl2str(thp, fmri, &fmristr, &err) == 0 && + strstr(fmristr, dmp->dm_sim_search) != 0) + (void) nvlist_add_string(in, "path", dmp->dm_sim_file); + topo_hdl_strfree(thp, fmristr); + } + + /* + * Try to invoke the method. If this fails (most likely because the + * method is not supported), then ignore this node. + */ + if (topo_method_invoke(node, TOPO_METH_DISK_STATUS, + TOPO_METH_DISK_STATUS_VERSION, in, &result, &err) != 0) { + nvlist_free(fmri); + nvlist_free(in); + return (TOPO_WALK_NEXT); + } + + nvlist_free(in); + + ena = fmd_event_ena_create(dmp->dm_hdl); + + /* + * Add any faults. + */ + if (nvlist_lookup_nvlist(result, "faults", &faults) == 0 && + nvlist_lookup_string(result, "protocol", &protocol) == 0) { + elem = NULL; + while ((elem = nvlist_next_nvpair(faults, elem)) != NULL) { + if (nvpair_type(elem) != DATA_TYPE_BOOLEAN_VALUE) + continue; + + (void) nvpair_value_boolean_value(elem, &fault); + if (!fault || + nvlist_lookup_nvlist(result, nvpair_name(elem), + &details) != 0) + continue; + + dt_post_ereport(dmp->dm_hdl, dmp->dm_xprt, protocol, + nvpair_name(elem), ena, fmri, details); + } + } + + nvlist_free(result); + nvlist_free(fmri); + + return (TOPO_WALK_NEXT); +} + +/* + * Periodic timeout. Iterates over all hc:// topo nodes, calling + * dt_analyze_disk() for each one. + */ +/*ARGSUSED*/ +static void +dt_timeout(fmd_hdl_t *hdl, id_t id, void *data) +{ + topo_hdl_t *thp; + topo_walk_t *twp; + int err; + disk_monitor_t *dmp = fmd_hdl_getspecific(hdl); + + dmp->dm_hdl = hdl; + + thp = fmd_hdl_topo_hold(hdl, TOPO_VERSION); + if ((twp = topo_walk_init(thp, FM_FMRI_SCHEME_HC, dt_analyze_disk, + dmp, &err)) == NULL) { + fmd_hdl_topo_rele(hdl, thp); + fmd_hdl_error(hdl, "failed to get topology: %s\n", + topo_strerror(err)); + return; + } + + if (topo_walk_step(twp, TOPO_WALK_CHILD) == TOPO_WALK_ERR) { + topo_walk_fini(twp); + fmd_hdl_topo_rele(hdl, thp); + fmd_hdl_error(hdl, "failed to walk topology\n"); + return; + } + + topo_walk_fini(twp); + fmd_hdl_topo_rele(hdl, thp); + + dmp->dm_timer = fmd_timer_install(hdl, NULL, NULL, dmp->dm_interval); + dmp->dm_timer_istopo = B_FALSE; +} + +/* + * Called when the topology may have changed. We want to examine all disks in + * case a new one has been inserted, but we don't want to overwhelm the system + * in the event of a flurry of topology changes, as most likely only a small + * number of disks are changing. To avoid this, we set the timer for a small + * but non-trivial interval (by default 1 minute), and ignore intervening + * changes during this period. This still gives us a reasonable response time + * to newly inserted devices without overwhelming the system if lots of hotplug + * activity is going on. + */ +/*ARGSUSED*/ +static void +dt_topo_change(fmd_hdl_t *hdl, topo_hdl_t *thp) +{ + disk_monitor_t *dmp = fmd_hdl_getspecific(hdl); + + if (dmp->dm_timer_istopo) + return; + + fmd_timer_remove(hdl, dmp->dm_timer); + dmp->dm_timer = fmd_timer_install(hdl, NULL, NULL, + fmd_prop_get_int64(hdl, "min-interval")); + dmp->dm_timer_istopo = B_TRUE; +} + +static const fmd_prop_t fmd_props[] = { + { "interval", FMD_TYPE_TIME, "1h" }, + { "min-interval", FMD_TYPE_TIME, "1min" }, + { "simulate", FMD_TYPE_STRING, "" }, + { NULL, 0, NULL } +}; + +static const fmd_hdl_ops_t fmd_ops = { + NULL, /* fmdo_recv */ + dt_timeout, /* fmdo_timeout */ + NULL, /* fmdo_close */ + NULL, /* fmdo_stats */ + NULL, /* fmdo_gc */ + NULL, /* fmdo_send */ + dt_topo_change, /* fmdo_topo_change */ +}; + +static const fmd_hdl_info_t fmd_info = { + "Disk Transport Agent", "1.0", &fmd_ops, fmd_props +}; + +void +_fmd_init(fmd_hdl_t *hdl) +{ + disk_monitor_t *dmp; + char *simulate; + + if (fmd_hdl_register(hdl, FMD_API_VERSION, &fmd_info) != 0) + return; + + (void) fmd_stat_create(hdl, FMD_STAT_NOALLOC, + sizeof (dt_stats) / sizeof (fmd_stat_t), + (fmd_stat_t *)&dt_stats); + + dmp = fmd_hdl_zalloc(hdl, sizeof (disk_monitor_t), FMD_SLEEP); + fmd_hdl_setspecific(hdl, dmp); + + dmp->dm_xprt = fmd_xprt_open(hdl, FMD_XPRT_RDONLY, NULL, NULL); + dmp->dm_interval = fmd_prop_get_int64(hdl, "interval"); + + /* + * Determine if we have the simulate property set. This property allows + * the developer to substitute a faulty device based off all or part of + * an FMRI string. For example, one could do: + * + * setprop simulate "sata-port=4/disk=4 /path/to/sim.so" + * + * When the transport module encounters an FMRI containing the given + * string, then it will open the simulator file instead of the + * corresponding device. This can be any file, but is intended to be a + * libdiskstatus simulator shared object, capable of faking up SCSI + * responses. + * + * The property consists of two strings, an FMRI fragment and an + * absolute path, separated by whitespace. + */ + simulate = fmd_prop_get_string(hdl, "simulate"); + if (simulate[0] != '\0') { + const char *sep; + size_t len; + + for (sep = simulate; *sep != '\0'; sep++) { + if (isspace(*sep)) + break; + } + + if (*sep != '\0') { + len = sep - simulate; + + dmp->dm_sim_search = fmd_hdl_alloc(hdl, + len + 1, FMD_SLEEP); + (void) memcpy(dmp->dm_sim_search, simulate, len); + dmp->dm_sim_search[len] = '\0'; + } + + for (; *sep != '\0'; sep++) { + if (!isspace(*sep)) + break; + } + + if (*sep != '\0') { + dmp->dm_sim_file = fmd_hdl_strdup(hdl, sep, FMD_SLEEP); + } else if (dmp->dm_sim_search) { + fmd_hdl_strfree(hdl, dmp->dm_sim_search); + dmp->dm_sim_search = NULL; + } + } + fmd_prop_free_string(hdl, simulate); + + /* + * Call our initial timer routine. This will do an initial check of all + * the disks, and then start the periodic timeout. + */ + dmp->dm_timer = fmd_timer_install(hdl, NULL, NULL, 0); +} + +void +_fmd_fini(fmd_hdl_t *hdl) +{ + disk_monitor_t *dmp; + + dmp = fmd_hdl_getspecific(hdl); + if (dmp) { + fmd_xprt_close(hdl, dmp->dm_xprt); + fmd_hdl_strfree(hdl, dmp->dm_sim_search); + fmd_hdl_strfree(hdl, dmp->dm_sim_file); + fmd_hdl_free(hdl, dmp, sizeof (*dmp)); + } +} diff --git a/usr/src/cmd/fm/modules/common/eversholt/platform.c b/usr/src/cmd/fm/modules/common/eversholt/platform.c index 3aa54e413e..9d5c499f2b 100644 --- a/usr/src/cmd/fm/modules/common/eversholt/platform.c +++ b/usr/src/cmd/fm/modules/common/eversholt/platform.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * platform.c -- interfaces to the platform's configuration information @@ -151,7 +151,7 @@ void platform_init(void) { (void) nv_alloc_init(&Eft_nv_hdl, &Eft_nv_alloc_ops); - Eft_topo_hdl = fmd_hdl_topology(Hdl, TOPO_VERSION); + Eft_topo_hdl = fmd_hdl_topo_hold(Hdl, TOPO_VERSION); platform_globals(); out(O_ALTFP, "platform_init() sucessful"); @@ -169,6 +169,7 @@ platform_fini(void) Initcfg = NULL; } + fmd_hdl_topo_rele(Hdl, Eft_topo_hdl); platform_free_globals(); (void) nv_alloc_fini(&Eft_nv_hdl); @@ -218,8 +219,8 @@ hc_fmri_nodeize(nvlist_t *hcfmri) } sname = stable(ename); tmpn = tree_name_iterator( - tree_name(sname, IT_VERTICAL, NULL, 0), - tree_num(eid, NULL, 0)); + tree_name(sname, IT_VERTICAL, NULL, 0), + tree_num(eid, NULL, 0)); if (pathtree == NULL) pathtree = tmpn; @@ -566,7 +567,8 @@ platform_config_snapshot(void) out(O_ALTFP, "platform_config_snapshot(): topo snapshot"); - Eft_topo_hdl = fmd_hdl_topology(Hdl, TOPO_VERSION); + fmd_hdl_topo_rele(Hdl, Eft_topo_hdl); + Eft_topo_hdl = fmd_hdl_topo_hold(Hdl, TOPO_VERSION); if ((twp = topo_walk_init(Eft_topo_hdl, FM_FMRI_SCHEME_HC, cfgcollect, Lastcfg, &err)) == NULL) { @@ -793,7 +795,7 @@ platform_get_files(const char *dirname[], const char *fnstr, int nodups) /* allocate ten more slots */ slots += 10; files = (char **)REALLOC(files, - slots * sizeof (char *)); + slots * sizeof (char *)); } /* prepend directory name and / */ totlen = strlen(dirname[i]) + 1; @@ -897,16 +899,16 @@ forkandexecve(const char *path, char *const argv[], char *const envp[], */ if (pipe(outpipe) < 0) if (strlcat(errbuf, ": pipe(outpipe) failed", - errbuflen) >= errbuflen) + errbuflen) >= errbuflen) return (1); if (pipe(errpipe) < 0) if (strlcat(errbuf, ": pipe(errpipe) failed", - errbuflen) >= errbuflen) + errbuflen) >= errbuflen) return (1); - if ((pid = fork()) < 0) + if ((pid = fork()) < 0) { rt = (int)strlcat(errbuf, ": fork() failed", errbuflen); - else if (pid) { + } else if (pid) { int wstat, count; /* parent */ @@ -916,7 +918,7 @@ forkandexecve(const char *path, char *const argv[], char *const envp[], /* PHASE2 need to guard against hang in child? */ if (waitpid(pid, &wstat, 0) < 0) if (strlcat(errbuf, ": waitpid() failed", - errbuflen) >= errbuflen) + errbuflen) >= errbuflen) return (1); /* check for stderr contents */ @@ -928,7 +930,7 @@ forkandexecve(const char *path, char *const argv[], char *const envp[], * reading */ if (strlcat(errbuf, ": read(errpipe) failed", - errbuflen) >= errbuflen) + errbuflen) >= errbuflen) return (1); } /* @@ -942,11 +944,11 @@ forkandexecve(const char *path, char *const argv[], char *const envp[], errbuf[count] = '\0'; } else if (WIFSIGNALED(wstat)) if (strlcat(errbuf, ": signaled", - errbuflen) >= errbuflen) + errbuflen) >= errbuflen) return (1); else if (WIFEXITED(wstat) && WEXITSTATUS(wstat)) if (strlcat(errbuf, ": abnormal exit", - errbuflen) >= errbuflen) + errbuflen) >= errbuflen) return (1); /* check for stdout contents */ @@ -958,7 +960,7 @@ forkandexecve(const char *path, char *const argv[], char *const envp[], * reading */ if (strlcat(errbuf, ": read(outpipe) failed", - errbuflen) >= errbuflen) + errbuflen) >= errbuflen) return (1); } /* @@ -1009,7 +1011,7 @@ arglist2argv(struct node *np, struct lut **globals, struct config *croot, break; case T_LIST: if (arglist2argv(np->u.expr.left, globals, croot, arrowp, - argv, argc, argvlen)) + argv, argc, argvlen)) return (1); /* * only leftmost element of a list can provide the command @@ -1017,7 +1019,7 @@ arglist2argv(struct node *np, struct lut **globals, struct config *croot, */ ASSERT(*argc > 0); if (arglist2argv(np->u.expr.right, globals, croot, arrowp, - argv, argc, argvlen)) + argv, argc, argvlen)) return (1); break; case T_FUNC: @@ -1048,7 +1050,7 @@ arglist2argv(struct node *np, struct lut **globals, struct config *croot, struct evalue value; if (!eval_expr(np, NULL, NULL, globals, croot, arrowp, - 0, &value)) + 0, &value)) return (1); switch (value.t) { @@ -1125,7 +1127,7 @@ arglist2argv(struct node *np, struct lut **globals, struct config *croot, */ *argvlen += 10; *argv = (char **)REALLOC(*argv, - sizeof (char *) * *argvlen); + sizeof (char *) * *argvlen); } (*argv)[*argc] = addthisarg; (*argc)++; @@ -1170,7 +1172,7 @@ generate_envp(struct arrow *arrowp, char ***envp, int *envc, int *envplen) /* large enough for max int */ envvalues[3] = MALLOC(sizeof (char) * 25); (void) snprintf(envvalues[3], sizeof (envvalues[3]), "%d", - arrowp->head->myevent->enode->line); + arrowp->head->myevent->enode->line); } for (i = 0; envnames[i] != NULL && i < *envc; i++) { @@ -1249,8 +1251,8 @@ platform_call(struct node *np, struct lut **globals, struct config *croot, errbuf[0] = '\0'; ret = forkandexecve((const char *) argv[0], (char *const *) argv, - (char *const *) envp, outbuf, sizeof (outbuf), - errbuf, sizeof (errbuf)); + (char *const *) envp, outbuf, sizeof (outbuf), + errbuf, sizeof (errbuf)); for (i = 0; i < envc; i++) FREE(envp[i]); @@ -1259,7 +1261,7 @@ platform_call(struct node *np, struct lut **globals, struct config *croot, if (ret) { outfl(O_OK, np->file, np->line, - "call: failure in fork + exec of %s", argv[0]); + "call: failure in fork + exec of %s", argv[0]); } else { char *ptr; @@ -1276,8 +1278,8 @@ platform_call(struct node *np, struct lut **globals, struct config *croot, if (errbuf[0] != '\0') { ret = 1; outfl(O_OK, np->file, np->line, - "call: unexpected stderr output from %s: %s", - argv[0], errbuf); + "call: unexpected stderr output from %s: %s", + argv[0], errbuf); } for (i = 0; i < argc; i++) @@ -1544,11 +1546,11 @@ platform_payloadprop(struct node *np, struct evalue *valuep) while ((w = strtok(NULL, ".")) != NULL) { if (get_array_info(lastnameptr, &nameptr, &index)) { ier = nvlist_lookup_nvlist(basenvp, - lastnameptr, &basenvp); + lastnameptr, &basenvp); } else { /* handle array of nvlists */ ier = nvlist_lookup_nvlist_array(basenvp, - nameptr, &arraynvp, &nelem); + nameptr, &arraynvp, &nelem); if (ier == 0) { if ((uint_t)index > nelem - 1) ier = 1; diff --git a/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/Makefile b/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/Makefile index f68a66f659..0571a078f3 100644 --- a/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/Makefile +++ b/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/Makefile @@ -28,8 +28,8 @@ MODULE = sfx4500-disk CLASS = arch ARCH = i86pc SRCS = sfx4500-disk.c diskmon_conf.c topo_gather.c \ - fault_mgr.c hotplug_mgr.c dm_platform.c \ - schg_mgr.c fault_analyze.c scsi_util.c util.c + hotplug_mgr.c dm_platform.c \ + schg_mgr.c util.c include ../../Makefile.plugin LINTFLAGS += -I. -I$(SRC)/lib/fm/topo/modules/i86pc/sata -L$(ROOT)/usr/lib/fm diff --git a/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/diskmon_conf.c b/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/diskmon_conf.c index 4bf0e0c229..b02273006a 100644 --- a/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/diskmon_conf.c +++ b/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/diskmon_conf.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -354,24 +354,12 @@ new_diskmon(nvlist_t *app_props, indicator_t *indp, indrule_t *indrp, dmp->initial_configuration = B_TRUE; - dmp->fmip = NULL; - - dmp->faults_outstanding = B_FALSE; dm_assert(pthread_mutex_init(&dmp->fault_indicator_mutex, NULL) == 0); dmp->fault_indicator_state = INDICATOR_UNKNOWN; - dm_assert(pthread_mutex_init(&dmp->disk_faults_mutex, NULL) == 0); - dmp->disk_faults = DISK_FAULT_SOURCE_NONE; - dmp->due = (time_t)0; - dmp->fault_inject_count = 0; - dmp->analysis_generation = 0; dmp->configured_yet = B_FALSE; dmp->state_change_count = 0; - dmp->disk_res_fmri = NULL; - dmp->asru_fmri = NULL; - dmp->fru_fmri = NULL; - dm_assert(pthread_mutex_init(&dmp->fru_mutex, NULL) == 0); dmp->frup = NULL; @@ -398,14 +386,6 @@ diskmon_free(diskmon_t *dmp) indrule_free(dmp->indrule_list); if (dmp->app_props) nvlist_free(dmp->app_props); - if (dmp->fmip) - disk_fault_uninit(dmp); - if (dmp->disk_res_fmri) - nvlist_free(dmp->disk_res_fmri); - if (dmp->asru_fmri) - nvlist_free(dmp->asru_fmri); - if (dmp->fru_fmri) - nvlist_free(dmp->fru_fmri); if (dmp->frup) dmfru_free(dmp->frup); dfree(dmp, sizeof (diskmon_t)); @@ -463,36 +443,6 @@ cfgdata_free(cfgdata_t *cdp) dfree(cdp, sizeof (cfgdata_t)); } -void -diskmon_add_asru(diskmon_t *dmp, nvlist_t *fmri) -{ - if (dmp->asru_fmri) { - nvlist_free(dmp->asru_fmri); - dmp->asru_fmri = NULL; - } - (void) nvlist_dup(fmri, &dmp->asru_fmri, 0); -} - -void -diskmon_add_fru(diskmon_t *dmp, nvlist_t *fmri) -{ - if (dmp->fru_fmri) { - nvlist_free(dmp->fru_fmri); - dmp->fru_fmri = NULL; - } - (void) nvlist_dup(fmri, &dmp->fru_fmri, 0); -} - -void -diskmon_add_disk_fmri(diskmon_t *dmp, nvlist_t *fmri) -{ - if (dmp->disk_res_fmri) { - nvlist_free(dmp->disk_res_fmri); - dmp->disk_res_fmri = NULL; - } - (void) nvlist_dup(fmri, &dmp->disk_res_fmri, 0); -} - conf_err_t check_indactions(ind_action_t *indrp) { diff --git a/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/diskmon_conf.h b/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/diskmon_conf.h index e8623dad48..4c0a9bec83 100644 --- a/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/diskmon_conf.h +++ b/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/diskmon_conf.h @@ -20,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -42,8 +42,6 @@ extern "C" { #include <libnvpair.h> #include <fm/fmd_api.h> #include "dm_types.h" -#include "scsi_util.h" -#include "fault_analyze.h" #include "util.h" #ifndef MIN @@ -56,9 +54,6 @@ extern "C" { #define DEVICES_PREFIX "/devices" -#define GLOBAL_PROP_FAULT_POLL "fault-polling-interval" -#define GLOBAL_PROP_FAULT_INJ "fault-inject-max-reps" -#define GLOBAL_PROP_FAULT_OPTIONS "fault-analyze-options" #define GLOBAL_PROP_LOG_LEVEL "log-level" /* Property names (and values) for the disk configuration file entity */ @@ -128,31 +123,6 @@ typedef struct indicator { struct indicator *next; } indicator_t; -typedef struct fault_monitor_info { - int mode_length; - logpage_supp_e log_pages_supported; - modepage_supp_e mode_pages_supported; - disk_option_e options; - disk_extension_e extensions; /* Vendor extensions supported */ - uint_t update_interval; - - /* Protects fault_list and disk_fault_srcs: */ - pthread_mutex_t fault_data_mutex; - disk_flt_src_e disk_fault_srcs; - struct disk_fault *fault_list; - - uint_t reference_temp; - - uint_t last_rs_key; - uint_t last_rs_asc; - uint_t last_rs_ascq; - - /* XXX - may not need these long-term: */ - struct scsi_ms_hdrs hdrs; - struct info_except_page iec_current; - struct info_except_page iec_changeable; -} fault_monitor_info_t; - typedef struct diskmon { /* * Static configuration data @@ -186,45 +156,17 @@ typedef struct diskmon { */ boolean_t initial_configuration; - - /* For the fault manager: */ + /* For the state-change manager: */ /* - * Set to TRUE when the fault manager adds faults to the diskmon - * that are processed by the state manager. Once the state - * manager generates ereports and clears the disk_faults member, - * it clears this flag, allowing the fault manager to add new - * faults, when they are detected. + * Current state of the fault indicator. */ - boolean_t faults_outstanding; pthread_mutex_t fault_indicator_mutex; ind_state_t fault_indicator_state; - /* Bitmap of accumulated faults: */ - pthread_mutex_t disk_faults_mutex; - disk_flt_src_e disk_faults; - - /* The time the next fault analysis is due: */ - time_t due; - /* - * The number of analysis generations after which fake faults - * are injected. - */ - uint_t fault_inject_count; - /* - * The current analysis generation (number of times the fault - * analysis algorithm was run). Used to determine when to do - * fault injection, when fault injection is enabled. - */ - uint_t analysis_generation; - - /* For the state-change manager: */ - /* * Set to TRUE when a disk transitions to the CONFIGURED state - * and remains TRUE until the disk is physically removed. Used - * to detect the first configuration of a disk so that fault - * state can be collected. + * and remains TRUE until the disk is physically removed. */ boolean_t configured_yet; @@ -234,21 +176,6 @@ typedef struct diskmon { */ uint_t state_change_count; - /* - * FMRI (nvlist and string versions) for populating - * ereports and faults - */ - nvlist_t *disk_res_fmri; - nvlist_t *asru_fmri; - nvlist_t *fru_fmri; - - /* - * The following member holds details about what faults were - * detected, and their details (see above for the structure - * definition) - */ - fault_monitor_info_t *fmip; - /* Disk FRU (model, manufacturer, etc) information */ pthread_mutex_t fru_mutex; dm_fru_t *frup; @@ -302,10 +229,6 @@ extern conf_err_t check_indrules(indrule_t *indrp, extern conf_err_t check_consistent_ind_indrules(indicator_t *indp, indrule_t *indrp, ind_action_t **offender); -extern void diskmon_add_asru(diskmon_t *dmp, nvlist_t *fmri); -extern void diskmon_add_fru(diskmon_t *dmp, nvlist_t *fmri); -extern void diskmon_add_disk_fmri(diskmon_t *dmp, nvlist_t *fmri); - extern void cfgdata_add_diskmon(cfgdata_t *cfgp, diskmon_t *dmp); extern void conf_error_msg(conf_err_t err, char *buf, int buflen, diff --git a/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/fault_analyze.c b/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/fault_analyze.c deleted file mode 100644 index de79ac11f8..0000000000 --- a/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/fault_analyze.c +++ /dev/null @@ -1,1698 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License (the "License"). - * You may not use this file except in compliance with the License. - * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] - * - * CDDL HEADER END - */ - -/* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -#pragma ident "%Z%%M% %I% %E% SMI" - -#include <sys/types.h> -#include <sys/byteorder.h> -#include <fcntl.h> -#include <limits.h> -#include <stdlib.h> -#include <string.h> -#include <strings.h> -#include <unistd.h> -#include <errno.h> -#include <utility.h> - -#include "util.h" -#include "sfx4500-disk.h" - -/* - * The functions defined below are used to query SCSI (or SCSI-like) - * disk devices for their Information Exceptions (IE) page via LOG SENSE. - * SATA disks in Solaris implement command translation that transforms - * the SATA SMART information into the appropriate IE page data. - * - * The general algorithm for determining if a disk has detected an imminent - * failure via the IE mechanism is as follows: - * - * STEP 1 - INITIALIZATION - * 1) Check to see if the IE mechanism is enabled via MODE SENSE for the - * IE Control page (page 0x1C), checking the DEXCPT field (1 = IE is - * disabled). If it is enabled, goto step 3; else if there was an error - * getting the mode page, abort IE processing, otherwise, continue to - * step 2. - * 2) Enable the IE mechanism by sending a MODE SELECT for page 0x1C - * with the DEXCPT = 0, PERF = 1, MRIE = 6, EWASC = 1, TEST = 0, - * REPORT COUNT = 0001h, LOGERR = 1 (enable IE, minimize delay associated - * with SMART processing, only report IE condition on request, - * enable warnings, testing disabled, limit to 1 the number - * of times to report each IE, and enable logging of errors). - * 3) Check to see if the IE log page is supported by issuing a LOG - * SENSE with page == 0x2F. If the page list returned includes the - * IE page, examine the log page and ensure that the parameter 0 length - * is at least 4 (some drives that are pre-SCSI3 return smaller lengths - * with non-sensical values for parameter 0). - * Check for the IBM extensions to the IE log page (the first byte of the - * vendor-specific area is non-zero if the temperature is present). - * and make a note of it. - * If there is no support for the IE Log page, we can still check SMART - * status by issuing a REQUEST SENSE by itself (since that's how we - * configured the MRIE field in the IE Control mode page). The presence - * of the IE log page makes life easier by aggregating almost all the - * information we need (the ASC/ASCQ of the predictive failure mode and - * the temperature information). - * 4) Check for self-test logs by issuing a LOG SENSE for page 0x10 and - * examining the returned page. If the page makes sense, make a note - * of it. - * 5) Check for a temperature log page. If it exists, make a note of it. - * (Prefer the temperature log page for monitoring because the SCSI-3 spec - * specifies an optional threshold temperature parameter (and most - * drives that support the temperature log page include the threshold). - * [Relying on the IE Log page for temperature constraint information - * is not reliable because the threshold information in the IE log - * page is an IBM extension and is not present on all drives. Plus, - * not many drives actually implement the IE log page.)] - * 6) Clear the GLTSD bit in Control mode page 0xA. This will allow the - * drive to save each of the log pages described above to nonvolatile - * storage. This is essential if the drive is to remember its failures - * across power-offs (it would be very bad for a previously-bad drive to - * go through another set of failures, just to recognize its badness after - * a power cycle). If the MODE SELECT for this operation fails, issue a - * warning, but continue anyway. - * - * STEP 2 - MONITORING - * 1) To determine if a predictable failure is imminent, either send the - * device an unsolicited REQUEST SENSE command or a LOG SENSE for the - * Informational Exceptions page, and use the sense information from - * either of the sources to determine if a failure is imminent. - * (SK=NO SENSE/ASC=0x5D/ASCQ=[0..0x6C] (real impending failures) or - * SK=NO SENSE/ASC=0x5D/ASCQ=0xFF (FALSE impending failure)). - * 2) If self-test logs are present, check them. If a self-test occurred - * since the last time the monitoring function was called, check to see its - * results. If there was a self-test failure, a self-test failure is - * returned. - * 3) Based on the available temperature information from the drive (either - * from the temperature log page or from the temperature information - * available on the IE page), determine if the drive has exceeded its - * maximum operating temperature. If so, a drive over-temp failure is - * returned. (If the drive is within 5% of its maximum operating - * temperature, return a warning). If there is no threshold, use the - * threshold value passed in. - * - */ - -#define RQBUF_LEN 255 /* Length of the request-sense buffer (max) */ - -static int logpage_ie_param_verify(diskmon_t *diskinfop, - struct log_parameter_header *lphp); -static int logpage_temp_param_verify(diskmon_t *diskinfop, - struct log_parameter_header *lphp); -static int logpage_selftest_param_verify(diskmon_t *diskinfop, - struct log_parameter_header *lphp); - -static int logpage_ie_param_analyze(diskmon_t *diskinfop, - struct log_parameter_header *lphp); -static int logpage_temp_param_analyze(diskmon_t *diskinfop, - struct log_parameter_header *lphp); -static int logpage_selftest_param_analyze(diskmon_t *diskinfop, - struct log_parameter_header *lphp); - - -static struct logpage_validation_entry logpage_validation_list[] = { - { LOGPAGE_IE, LOGPAGE_SUPP_IE, PC_CUMULATIVE, - "Informational Exceptions", B_TRUE, - logpage_ie_param_verify, logpage_ie_param_analyze }, - - { LOGPAGE_TEMP, LOGPAGE_SUPP_TEMP, PC_CUMULATIVE, - "Temperature", B_TRUE, - logpage_temp_param_verify, logpage_temp_param_analyze }, - - { LOGPAGE_SELFTEST, LOGPAGE_SUPP_SELFTEST, PC_CUMULATIVE, - "Self-test", B_TRUE, - logpage_selftest_param_verify, logpage_selftest_param_analyze }, - - { 0xFF, 0, 0, - NULL, B_FALSE, - NULL, NULL } -}; - -static char * -dm_get_disk_logphys(diskmon_t *diskp, int *buflen) -{ - char *path, *sep; - - path = (char *)dm_prop_lookup(diskp->props, DISK_PROP_LOGNAME); - if (path != NULL) { - *buflen = strlen(path) + 1; - return (dstrdup(path)); - } - - path = (char *)dm_prop_lookup(diskp->props, DISK_PROP_DEVPATH); - - dm_assert(path != NULL); - - *buflen = strlen(path) + 1; - path = dstrdup(path); - - if ((sep = strchr(path, DEVPATH_MINOR_SEPARATOR)) != NULL) - *sep = 0; - - return (path); -} - -static void -disk_err(diskmon_t *diskinfop, const char *fmt, ...) -{ - va_list ap; - char *path; - int pathlen; - - path = dm_get_disk_logphys(diskinfop, &pathlen); - - log_msg(MM_ERR|MM_SCSI, "ERROR: Disk %s (location: %s): ", - path, - diskinfop->location); - - va_start(ap, fmt); - vcont(MM_ERR|MM_SCSI, fmt, ap); - va_end(ap); - - dfree(path, pathlen); -} - -static void -disk_warn(diskmon_t *diskinfop, const char *fmt, ...) -{ - va_list ap; - char *path; - int pathlen; - - path = dm_get_disk_logphys(diskinfop, &pathlen); - - log_msg(MM_WARN|MM_SCSI, "WARNING: Disk %s (location: %s): ", - path, - diskinfop->location); - - va_start(ap, fmt); - vcont(MM_WARN|MM_SCSI, fmt, ap); - va_end(ap); - - dfree(path, pathlen); -} - -static void -disk_note(diskmon_t *diskinfop, const char *fmt, ...) -{ - va_list ap; - char *path; - int pathlen; - - path = dm_get_disk_logphys(diskinfop, &pathlen); - - log_msg(MM_SCSI, "NOTICE: Disk %s (location: %s): ", - path, - diskinfop->location); - - va_start(ap, fmt); - vcont(MM_SCSI, fmt, ap); - va_end(ap); - - dfree(path, pathlen); -} - -static int -disk_mode_select(int cdb_len, int fd, uchar_t page_code, int options, - void *buf, uint_t buflen, struct scsi_ms_hdrs *headers, uint_t *skp, - uint_t *ascp, uint_t *ascqp) -{ - int result; - struct scsi_extended_sense sense; - int senselen = sizeof (struct scsi_extended_sense); - struct mode_page *mp = (struct mode_page *)buf; - - dm_assert(cdb_len == MODE_CMD_LEN_6 || cdb_len == MODE_CMD_LEN_10); - dm_assert(headers->length == cdb_len); - - bzero(&sense, sizeof (struct scsi_extended_sense)); - - if (mp->ps) { - options |= MODE_SELECT_SP; - mp->ps = 0; - } else - options &= ~MODE_SELECT_SP; - - - if (cdb_len == MODE_CMD_LEN_6) { - - /* The following fields are reserved during mode select: */ - headers->h.g0.mode_header.length = 0; - headers->h.g0.mode_header.device_specific = 0; - - result = uscsi_mode_select(fd, page_code, options, buf, - buflen, &headers->h.g0, &sense, &senselen); - - } else if (cdb_len == MODE_CMD_LEN_10) { - - /* The following fields are reserved during mode select: */ - headers->h.g1.mode_header.length = 0; - headers->h.g1.mode_header.device_specific = 0; - - result = uscsi_mode_select_10(fd, page_code, options, buf, - buflen, &headers->h.g1, &sense, &senselen); - } - - if (result != 0) - scsi_translate_error(&sense, skp, ascp, ascqp); - - return (result); -} - -static int -disk_mode_sense(int cdb_len, int fd, uchar_t page_code, uchar_t pc, - void *buf, uint_t buflen, struct scsi_ms_hdrs *headers, uint_t *skp, - uint_t *ascp, uint_t *ascqp) -{ - int result; - struct scsi_extended_sense sense; - int senselen = sizeof (struct scsi_extended_sense); - - dm_assert(cdb_len == MODE_CMD_LEN_6 || cdb_len == MODE_CMD_LEN_10); - - bzero(&sense, sizeof (struct scsi_extended_sense)); - - (void) memset(headers, 0, sizeof (struct scsi_ms_hdrs)); - headers->length = cdb_len; - - if (cdb_len == MODE_CMD_LEN_6) { - result = uscsi_mode_sense(fd, page_code, pc, buf, buflen, - &headers->h.g0, &sense, &senselen); - } else if (cdb_len == MODE_CMD_LEN_10) { - result = uscsi_mode_sense_10(fd, page_code, pc, buf, buflen, - &headers->h.g1, &sense, &senselen); - } - - if (result != 0) - scsi_translate_error(&sense, skp, ascp, ascqp); - - return (result); -} - -static int -disk_request_sense(int fd, uint_t *skp, uint_t *ascp, uint_t *ascqp) -{ - struct scsi_extended_sense sense, sensebuf; - int senselen = sizeof (struct scsi_extended_sense); - int sensebuflen = sizeof (struct scsi_extended_sense); - int result; - - bzero(&sense, sizeof (struct scsi_extended_sense)); - bzero(&sensebuf, sizeof (struct scsi_extended_sense)); - - result = uscsi_request_sense(fd, (caddr_t)&sensebuf, sensebuflen, - &sense, &senselen); - - if (result == 0) - scsi_translate_error(&sensebuf, skp, ascp, ascqp); - else - scsi_translate_error(&sense, skp, ascp, ascqp); - - return (result); -} - -static int -scsi_enable_ie(int fd, diskmon_t *diskinfop, uint_t *skp, uint_t *ascp, - uint_t *ascqp, int test_mode, int perf_mode, boolean_t *changed) -{ - struct info_except_page new_iec_page; - struct scsi_ms_hdrs hdrs; - fault_monitor_info_t *fip = diskinfop->fmip; - int result; - - bzero(&new_iec_page, sizeof (struct info_except_page)); - bzero(&hdrs, sizeof (struct scsi_ms_hdrs)); - - (void) memcpy(&new_iec_page, &fip->iec_current, - sizeof (struct info_except_page)); - - /* - * Enable IE reporting: - * - * (1) DEXCPT = 0 - * (2) PERF = <as passed in> (minimize delay due to IE processing) - * (3) MRIE = 6 (IE_REPORT_ON_REQUEST) - * (4) EWASC = 1 - * (5) TEST = <as passed in> - * (6) REPORT COUNT = 0x0001 - * (7) LOGERR = 1 - * - */ - - new_iec_page.dexcpt = 0; - new_iec_page.mrie = IE_REPORT_ON_REQUEST; - - if (IEC_PERF_CHANGEABLE(fip->iec_changeable)) - new_iec_page.perf = perf_mode ? 1 : 0; - - if (IEC_EWASC_CHANGEABLE(fip->iec_changeable)) - new_iec_page.ewasc = 1; - - if (IEC_TEST_CHANGEABLE(fip->iec_changeable)) - new_iec_page.test = test_mode ? 1 : 0; - - if (IEC_RPTCNT_CHANGEABLE(fip->iec_changeable)) - new_iec_page.report_count = BE_32(1); - - if (IEC_LOGERR_CHANGEABLE(fip->iec_changeable)) - new_iec_page.logerr = 1; - - /* - * Now compare the new mode page with the existing one. - * if there's no difference, there's no need for a mode select - */ - if (memcmp(&new_iec_page, &fip->iec_current, - MODEPAGE_INFO_EXCPT_LEN) == 0) { - *changed = B_FALSE; - result = 0; - } else { - - (void) memcpy(&hdrs, &fip->hdrs, sizeof (struct scsi_ms_hdrs)); - - if ((result = disk_mode_select(fip->mode_length, fd, - MODEPAGE_INFO_EXCPT, MODE_SELECT_PF, &new_iec_page, - MODEPAGE_INFO_EXCPT_LEN, &hdrs, skp, ascp, ascqp)) == 0) { - - *changed = B_TRUE; - } - } - - return (result); -} - -static boolean_t -modepagelist_find(uchar_t *pgdata, uint_t pgdatalen, uchar_t pagecode) -{ - uint_t i = 0; - struct mode_page *pg; - boolean_t found = B_FALSE; - - /* - * The mode page list contains all mode pages supported by - * the device, one after the other. Since the pages have headers - * that describe the page code and their length, we can use pointer - * arithmetic to hop to the next page. - */ - while (i < pgdatalen) { - pg = (struct mode_page *)&pgdata[i]; - - if (pg->code == pagecode) { - found = B_TRUE; - break; - } - - i += MODESENSE_PAGE_LEN(pg); - } - - return (found); -} - -/* - * Figure out which MODE SENSE/SELECT to use (the 6-byte or 10-byte - * version) by executing a MODE SENSE command for a page that should be - * implemented by the lun. If the lun doesn't support the Return All Pages - * mode page (0x3F), then that information is returned as an invalid field in - * cdb error. This function updates the diskinfo structure with the command - * length that's supported. - */ -static int -modepages_init(int fd, diskmon_t *diskinfop, uint_t *skeyp, - uint_t *ascp, uint_t *ascqp) -{ - /* - * allpages_buflen is USHRT_MAX - size of the header because some luns - * return nothing if the buffer length is too big -- it must be sized - * properly (the maximum buffer size is therefore the maximum that - * will fit in a 16-bit integer minus the size of the header.) - */ - int allpages_buflen = USHRT_MAX - sizeof (struct scsi_ms_header_g1); - uchar_t *allpages = (uchar_t *)dzmalloc(allpages_buflen); - fault_monitor_info_t *fip = diskinfop->fmip; - struct scsi_ms_header smh; - struct scsi_ms_header_g1 smh_g1; - struct scsi_extended_sense sense; - int resid; - int result; - uint_t sk, a, aq; - uint_t datalength = 0; - - bzero(&smh, sizeof (struct scsi_ms_header)); - bzero(&smh_g1, sizeof (struct scsi_ms_header_g1)); - bzero(&sense, sizeof (struct scsi_extended_sense)); - - /* - * Attempt a mode sense(6). If that fails, try a mode sense(10) - * - * allpages is allocated to be of the maximum size for either a - * mode sense(6) or mode sense(10) MODEPAGE_ALLPAGES response. - * - * Note that the length passed into uscsi_mode_sense should be - * set to the maximum size of the parameter response, which in - * this case is UCHAR_MAX - the size of the headers/block descriptors. - * - */ - - resid = sizeof (struct scsi_extended_sense); - if ((result = uscsi_mode_sense(fd, MODEPAGE_ALLPAGES, PC_CURRENT, - (caddr_t)allpages, UCHAR_MAX - sizeof (struct scsi_ms_header), - &smh, &sense, &resid)) == 0) { - - fip->mode_length = MODE_CMD_LEN_6; - - /* - * Compute the data length of the page that contains all - * mode sense pages. This is a bit tricky because the - * format of the response from the lun is: - * - * header: <length> <medium type byte> <dev specific byte> - * <block descriptor length> - * [<optional block descriptor>] - * data: [<mode page data> <mode page data> ...] - * - * Since the length field in the header describes the - * length of the entire response (including the header, - * but NOT including itself (1 or 2 bytes depending on - * which mode sense type (6- or 10- byte) being executed). - * - * So, the data length equals the length value in the header - * plus 1 (because the length byte was not included in the - * length count), minus [[the sum of the length of the - * header and the length of the block descriptor]]. - */ - - datalength = (smh.mode_header.length + - sizeof (smh.mode_header.length)) - - (sizeof (struct mode_header) + - smh.mode_header.bdesc_length); - - } else { - scsi_translate_error(&sense, &sk, &a, &aq); - if (SCSI_INVALID_OPCODE(sk, a, aq)) { - - resid = sizeof (struct scsi_extended_sense); - result = uscsi_mode_sense_10(fd, MODEPAGE_ALLPAGES, - PC_CURRENT, (caddr_t)allpages, allpages_buflen, - &smh_g1, &sense, &resid); - - if (result == 0) { - fip->mode_length = MODE_CMD_LEN_10; - - datalength = - (BE_16(smh_g1.mode_header.length) + - sizeof (smh_g1.mode_header.length)) - - (sizeof (struct mode_header_g1) + - BE_16(smh_g1.mode_header.bdesc_length)); - - } else - fip->mode_length = MODE_CMD_LEN_UNKNOWN; - } - } - - if (result == 0) { - - /* - * One of the sets of the commands (above) succeeded, so now - * look for the mode pages we need and record them appropriately - */ - - if (modepagelist_find(allpages, datalength, - MODEPAGE_INFO_EXCPT)) - fip->mode_pages_supported |= MODEPAGE_SUPP_IEC; - - } else /* result != 0 */ - scsi_translate_error(&sense, skeyp, ascp, ascqp); - - dfree(allpages, allpages_buflen); - return (result); -} - -static int -load_iec_modepages(int fd, diskmon_t *diskinfop, uint_t *skeyp, - uint_t *ascp, uint_t *ascqp) -{ - fault_monitor_info_t *fip = diskinfop->fmip; - struct scsi_ms_hdrs junk_hdrs; - int result; - - (void) memset(&fip->iec_current, 0, - sizeof (struct info_except_page)); - (void) memset(&fip->iec_changeable, 0, - sizeof (struct info_except_page)); - - if ((result = disk_mode_sense(fip->mode_length, fd, - MODEPAGE_INFO_EXCPT, PC_CURRENT, &fip->iec_current, - MODEPAGE_INFO_EXCPT_LEN, &fip->hdrs, skeyp, ascp, ascqp)) - == 0) { - - result = disk_mode_sense(fip->mode_length, fd, - MODEPAGE_INFO_EXCPT, PC_CHANGEABLE, - &fip->iec_changeable, - MODEPAGE_INFO_EXCPT_LEN, &junk_hdrs, skeyp, ascp, ascqp); - } - - return (result); -} - -static int -clear_gltsd(int fd, diskmon_t *diskinfop, uint_t *skp, uint_t *ascp, - uint_t *ascqp) -{ - fault_monitor_info_t *fip = diskinfop->fmip; - struct scsi_ms_hdrs hdrs, junk_hdrs; - struct mode_control_scsi3 control_pg_cur, control_pg_chg; - int result; - - bzero(&hdrs, sizeof (struct scsi_ms_hdrs)); - bzero(&control_pg_cur, sizeof (struct mode_control_scsi3)); - bzero(&control_pg_chg, sizeof (struct mode_control_scsi3)); - - result = disk_mode_sense(fip->mode_length, fd, - MODEPAGE_CTRL_MODE, PC_CURRENT, &control_pg_cur, - MODEPAGE_CTRL_MODE_LEN, &hdrs, skp, ascp, ascqp); - - if (result != 0) { - - disk_note(diskinfop, "Mode sense failed for the " - "current Control mode page -- skipping GLTSD " - "initialization.\n"); - - } else if (control_pg_cur.mode_page.length != - PAGELENGTH_MODE_CONTROL_SCSI3) { - - disk_note(diskinfop, "Disk does not support SCSI-3 " - "Control mode page -- skipping GLTSD " - "initialization.\n"); - - } else if ((result = disk_mode_sense(fip->mode_length, fd, - MODEPAGE_CTRL_MODE, PC_CHANGEABLE, &control_pg_chg, - MODEPAGE_CTRL_MODE_LEN, &junk_hdrs, skp, ascp, ascqp)) - != 0) { - - disk_note(diskinfop, "Mode sense failed for the " - "changeable Control mode page -- skipping GLTSD " - "initialization.\n"); - - } else if (control_pg_cur.gltsd && !GLTSD_CHANGEABLE(control_pg_chg)) { - - disk_note(diskinfop, "GLTSD is set and is not " - "changeable. This disk will not save log " - "parameters implicitly.\n"); - - } else if (control_pg_cur.gltsd) { - control_pg_cur.gltsd = 0; - result = disk_mode_select(fip->mode_length, fd, - MODEPAGE_CTRL_MODE, MODE_SELECT_PF, &control_pg_cur, - MODEPAGE_CTRL_MODE_LEN, &hdrs, skp, ascp, ascqp); - } - - return (result); -} - -static int -ie_enable_and_save(int fd, diskmon_t *diskinfop, uint_t *skp, - uint_t *ascp, uint_t *ascqp, int test_mode, int perf_mode) -{ - fault_monitor_info_t *fip = diskinfop->fmip; - int return_code = IE_SUCCESS; - boolean_t changed; - - /* - * Now that we know we can execute a valid mode sense command - * (and that the IE control mode page is supported), load the IEC page - * so we can check is IE is disabled. If it is disabled and it's - * NOT changeable, then we can't do anything else here. - */ - if (load_iec_modepages(fd, diskinfop, skp, ascp, ascqp) != 0) { - - /* - * Something went wrong when grabbing the IEC mode page, - * so bail out. - */ - - return_code = IE_OTHER_ERROR; - - } else if ((!IEC_IE_ENABLED(fip->iec_current) && - !IEC_IE_CHANGEABLE(fip->iec_changeable)) || - (fip->iec_current.mrie != IE_REPORT_ON_REQUEST && - !IEC_MRIE_CHANGEABLE(fip->iec_changeable))) { - - /* - * We need to be able to change the IE disable bit if - * IEs are currently disabled. We also need to be able to - * change the MRIE bits if they're not set to the right values, - * so if we can't enable IEs properly, we're done here. - */ - return_code = IE_CANNOT_BE_ENABLED; - - } else if (scsi_enable_ie(fd, diskinfop, skp, ascp, ascqp, - test_mode, perf_mode, &changed) != 0) { - - return_code = IE_ENABLE_FAILED; - - } else if (changed && load_iec_modepages(fd, diskinfop, skp, ascp, - ascqp) != 0) { - - /* - * Something went wrong when grabbing the IEC mode page (again), - * so bail out. - */ - - return_code = IE_OTHER_ERROR; - - } else if (!IEC_IE_ENABLED(fip->iec_current)) { - - return_code = IE_ENABLE_DIDNT_STICK; - - } else if (clear_gltsd(fd, diskinfop, skp, ascp, ascqp) != 0) { - - /* - * NOTE: Failed to clear the GLTSD bit in the control page; - * it's OK if the asc/ascq indicates invalid field in cdb, - * meaning this disk doesn't support the GLTSD flag. - */ - if (*ascp != ASC_INVALID_CDB_FIELD) - disk_note(diskinfop, "Could not clear the GLTSD bit " - "[KEY=0x%x ASC=0x%x ASCQ=0x%x]. Disk " - "failures may not be recognized after a power " - "cycle.\n", *skp, *ascp, *ascqp); - } - - if (return_code == IE_SUCCESS) { - /* Save the update interval */ - fip->update_interval = - BE_32(fip->iec_current.interval_timer); - } - - return (return_code); -} - -static int -log_page_to_supp_bit(uchar_t logpage) -{ - int i; - - for (i = 0; logpage_validation_list[i].analyze_fn != NULL; i++) { - if (logpage_validation_list[i].logpage_code == logpage) - return (logpage_validation_list[i].supp_bit); - } - - return (0); -} - - -static logpage_validation_fn_t -lookup_logpage_validation_fn(uchar_t logpage_code) -{ - int i; - - for (i = 0; logpage_validation_list[i].analyze_fn != NULL; i++) { - if (logpage_validation_list[i].logpage_code == logpage_code) - return (logpage_validation_list[i].validate_fn); - } - - return (NULL); -} - -static logpage_analyze_fn_t -lookup_logpage_analyze_fn(uchar_t logpage_code) -{ - int i; - - for (i = 0; logpage_validation_list[i].analyze_fn != NULL; i++) { - if (logpage_validation_list[i].logpage_code == logpage_code) - return (logpage_validation_list[i].analyze_fn); - } - - return (NULL); -} - -static uchar_t -logpage_pc_for_verify(uchar_t logpage_code) -{ - int i; - - for (i = 0; logpage_validation_list[i].analyze_fn != NULL; i++) { - if (logpage_validation_list[i].logpage_code == logpage_code) - return (logpage_validation_list[i].pc); - } - - /* If no PC is specifically defined for this page code, use current */ - return (PC_CURRENT); -} - -static int -supported_log_pages(int fd, diskmon_t *diskinfop, uint_t *skp, - uint_t *ascp, uint_t *ascqp) -{ - /* - * buflen is USHRT_MAX - size of the header because some luns - * return nothing if the buffer length is too big -- it must be sized - * properly (the maximum buffer size is therefore the maximum that - * will fit in a 16-bit integer minus the size of the header.) - */ - int buflen = USHRT_MAX - sizeof (struct log_header); - struct supported_log_pages *sp = dzmalloc(buflen); - struct scsi_extended_sense sense; - int resid = sizeof (struct scsi_extended_sense); - fault_monitor_info_t *fip = diskinfop->fmip; - int result; - int bitset; - - bzero(&sense, sizeof (struct scsi_extended_sense)); - - if ((result = uscsi_log_sense(fd, LOGPAGE_SUPP_LIST, PC_CUMULATIVE, - (caddr_t)sp, buflen, &sense, &resid)) == 0) { - - int pagecount = BE_16(sp->hdr.length); - int i = 0; - - while (i < pagecount) { - - bitset = log_page_to_supp_bit(sp->pages[i]); - - fip->log_pages_supported |= bitset; - - i++; - } - } - - dfree(sp, buflen); - if (result != 0) - scsi_translate_error(&sense, skp, ascp, ascqp); - return (result); -} - -static int -logpage_ie_param_verify(diskmon_t *diskinfop, struct log_parameter_header *lphp) -{ - fault_monitor_info_t *fip = diskinfop->fmip; - struct info_excpt_log_param *iep; - int result = 0; - - iep = (struct info_excpt_log_param *)lphp; - - /* - * Ensure that parameter code 0 has a length of - * at LEAST 4 as per the SCSI SPC3 spec. If it - * does not, don't use this log page (its format - * is unknown). - */ - if (BE_16(lphp->param_code) == LOGPARAM_IE) { - if (lphp->length < LOGPARAM_IE_MIN_LEN) { - - disk_note(diskinfop, "IE log page format is unknown -- " - "not using it.\n"); - - result = -1; - - } else if (lphp->length > LOGPARAM_IE_WITH_TEMP_MIN_LEN) { - - /* - * Determine if the vendor-specific area lists a - * temperature threshold - */ - if (iep->ex_temp_threshold != 0) - fip->extensions |= EXTN_IE_TEMP_THRESHOLD; - } - } - - return (result); -} - -static int -logpage_temp_param_verify(diskmon_t *diskinfop, - struct log_parameter_header *lphp) -{ - int result = 0; - fault_monitor_info_t *fip = diskinfop->fmip; - ushort_t param_code = BE_16(lphp->param_code); - struct temperature_log_param_reftemp *rtp; - - /* The temperature log page has two parameter codes defined: 0 & 1 */ - /* 0 is current temperature, and 1 is the threshold (but is optional) */ - - /* - * Don't compare the current temperature to 0xff; we don't flag that - * as an error now because the condition that caused the drive not to - * be able to report a temperature reading could be transitory. - */ - - switch (param_code) { - case LOGPARAM_TEMP_CURTEMP: - if (lphp->length != LOGPARAM_TEMP_CURTEMP_LEN) { - result = -1; - } - break; - - case LOGPARAM_TEMP_REFTEMP: - rtp = (struct temperature_log_param_reftemp *)lphp; - - if (lphp->length != LOGPARAM_TEMP_REFTEMP_LEN) { - result = -1; - } else if (rtp->reference_temp != REFTEMP_INVALID) { - fip->extensions |= EXTN_TEMPLOG_TEMP_THRESHOLD; - fip->reference_temp = rtp->reference_temp; - } - break; - } - - if (result < 0) - disk_note(diskinfop, "Temperature log page format is unknown " - "-- not using it.\n"); - - return (result); -} - -static int -logpage_selftest_param_verify(diskmon_t *diskinfop, - struct log_parameter_header *lphp) -{ - int result = 0; - ushort_t param_code = BE_16(lphp->param_code); - - /* Parameter codes range from 0x01-0x14 */ - if (param_code < LOGPAGE_SELFTEST_MIN_PARAM_CODE || - param_code > LOGPAGE_SELFTEST_MAX_PARAM_CODE) { - - result = -1; - - } else if (lphp->length != LOGPAGE_SELFTEST_PARAM_LEN) { - - disk_note(diskinfop, "Bad parameter length for self-test " - "parameter %d\n", lphp->param_code); - - result = -1; - } - - return (result); -} - -static fault_monitor_info_t * -new_disk_fault_info(void) -{ - int opts; - fault_monitor_info_t *fmi = - (fault_monitor_info_t *)dzmalloc(sizeof (fault_monitor_info_t)); - - /* - * This will always succeed. See sfx4500-disk.c for the default values. - */ - (void) dm_prop_lookup_int(dm_global_proplist(), - GLOBAL_PROP_FAULT_OPTIONS, &opts); - - fmi->options = opts; - - dm_assert(pthread_mutex_init(&fmi->fault_data_mutex, NULL) == 0); - fmi->fault_list = NULL; - - return (fmi); -} - -void -free_disk_fault_list(fault_monitor_info_t *fmip) -{ - struct disk_fault *cur, *next; - - cur = fmip->fault_list; - - while (cur != NULL) { - next = cur->next; - if (cur->msg != NULL) - dstrfree(cur->msg); - dfree(cur, sizeof (struct disk_fault)); - cur = next; - } - fmip->fault_list = NULL; - fmip->disk_fault_srcs = DISK_FAULT_SOURCE_NONE; -} - -static void -free_disk_fault_info(fault_monitor_info_t **fmipp) -{ - free_disk_fault_list(*fmipp); - dm_assert(pthread_mutex_destroy(&(*fmipp)->fault_data_mutex) == 0); - dfree(*fmipp, sizeof (fault_monitor_info_t)); - *fmipp = NULL; -} - -static void -add_disk_fault(diskmon_t *diskinfop, disk_flt_src_e fltsrc, - const char *msg, uchar_t sensekey, uchar_t asc, uchar_t ascq, - uint16_t selftestresultcode, int curtemp, int threshtemp) -{ - fault_monitor_info_t *fip = diskinfop->fmip; - struct disk_fault *flt; - struct disk_fault *newflt; - - /* Do not add duplicate faults */ - if (diskinfop->disk_faults & fltsrc || - fip->disk_fault_srcs & fltsrc) - return; - - newflt = (struct disk_fault *) - dzmalloc(sizeof (struct disk_fault)); - - newflt->fault_src = fltsrc; - /* If the message is NULL, look it up by asc/ascq */ - - newflt->selftest_code = selftestresultcode; - newflt->cur_temp = curtemp; - newflt->thresh_temp = threshtemp; - - if (asc != 0 || ascq != 0) { - newflt->sense_valid = B_TRUE; - newflt->sense_key = sensekey; - newflt->asc = asc; - newflt->ascq = ascq; - if (msg == NULL) { - const char *scsi_msg = scsi_asc_ascq_string(asc, ascq); - newflt->msg = (scsi_msg == NULL) ? NULL : - dstrdup(scsi_msg); - } else - newflt->msg = dstrdup(msg); - } else { - newflt->sense_valid = B_FALSE; - newflt->msg = (msg == NULL) ? NULL : dstrdup(msg); - } - - fip->disk_fault_srcs |= fltsrc; - - if (fip->fault_list == NULL) - fip->fault_list = newflt; - else { - flt = fip->fault_list; - - while (flt->next != NULL) - flt = flt->next; - - flt->next = newflt; - } -} - -void -create_fake_faults(diskmon_t *diskp) -{ - add_disk_fault(diskp, DISK_FAULT_SOURCE_INFO_EXCPT, - "Fake SMART impending failure fault", 0, - 0x5D /* IE Failure threshold exceeded */, - 0xFF /* false positive */, 0, 0, 0); - - add_disk_fault(diskp, DISK_FAULT_SOURCE_SELFTEST, - "Fake self-test failure fault", - 0, 0, 0, SELFTEST_FAILURE_SEG_FIRST, 0, 0); - - add_disk_fault(diskp, DISK_FAULT_SOURCE_OVERTEMP, - "Fake disk overtemp fault", - 0, - 0xb /* Warning */, - 1 /* specified temperature exceeded */, 0, - 0xff /* curtemp */, 0xfe /* threshold */); -} - -static int -logpage_ie_param_analyze(diskmon_t *diskinfop, - struct log_parameter_header *lphp) -{ - fault_monitor_info_t *fip = diskinfop->fmip; - struct info_excpt_log_param *iep; - int result = 0; - char buf[MSG_BUFLEN]; - ushort_t length = BE_16(lphp->length); - - iep = (struct info_excpt_log_param *)lphp; - - if (lphp->param_code == LOGPARAM_IE) { - /* - * There are two faults that the IE parameter helps - * detect -- the general IE predictive failure, and - * an overtemp failure (but only if the temperature - * threshold information is included. - */ - if (iep->ie_asc != 0) { - add_disk_fault(diskinfop, DISK_FAULT_SOURCE_INFO_EXCPT, - NULL, INVALID_SENSE_KEY, iep->ie_asc, iep->ie_ascq, - 0, 0, 0); - - result = -1; - } - - /* - * If the length of this parameter includes the temperature - * threshold, use it to compare the temperature, but only if - * there is no temperature log page supported (or, if there - * is a temperature log page but no reference temperature in - * the temperature log page). - */ - - if ((!LOG_PAGE_SUPPORTED(fip, LOGPAGE_SUPP_TEMP) || - !EXTN_SUPPORTED(fip, EXTN_TEMPLOG_TEMP_THRESHOLD)) && - (length > LOGPARAM_IE_WITH_TEMP_MIN_LEN) && - (iep->ex_temp_threshold != 0) && - (iep->ex_temp_threshold != INVALID_TEMPERATURE) && - (iep->last_temp >= iep->ex_temp_threshold)) { - - (void) snprintf(buf, MSG_BUFLEN, "Disk temperature (%d " - "celsius) is above the threshold (%d celsius)", - iep->last_temp, iep->ex_temp_threshold); - - add_disk_fault(diskinfop, DISK_FAULT_SOURCE_OVERTEMP, - buf, INVALID_SENSE_KEY, 0, 0, 0, iep->last_temp, - iep->ex_temp_threshold); - - result = -1; - } - } - - return (result); -} - -static int -logpage_temp_param_analyze(diskmon_t *diskinfop, - struct log_parameter_header *lphp) -{ - char buf[MSG_BUFLEN]; - int result = 0; - fault_monitor_info_t *fip = diskinfop->fmip; - ushort_t param_code = BE_16(lphp->param_code); - struct temperature_log_param_curtemp *ctp = - (struct temperature_log_param_curtemp *)lphp; - - /* - * If this log page has a reference temperature, it must have - * been recorded in the diskinfo structure, so use it - * to compare the current temperature reading (if the - * reading is valid). - */ - - /* The temperature log page has two parameter codes defined: 0 & 1 */ - /* 0 is current temperature, and 1 is the threshold (but is optional) */ - - /* - * Don't compare the current temperature to 0xff; we don't flag that - * as an error now because the condition that caused the drive not to - * be able to report a temperature reading could be transitory. - */ - - if (param_code == LOGPARAM_TEMP_CURTEMP && - ctp->current_temp != INVALID_TEMPERATURE && - EXTN_SUPPORTED(fip, EXTN_TEMPLOG_TEMP_THRESHOLD) && - ctp->current_temp >= fip->reference_temp) { - - (void) snprintf(buf, MSG_BUFLEN, "Disk temperature (%d " - "celsius) is above the threshold (%d celsius)", - ctp->current_temp, fip->reference_temp); - - add_disk_fault(diskinfop, DISK_FAULT_SOURCE_OVERTEMP, - buf, INVALID_SENSE_KEY, 0, 0, 0, ctp->current_temp, - fip->reference_temp); - - result = -1; - } - - return (result); -} - -static char * -disk_selftest_result_string(struct selftest_log_parameter *stlp, char *buf, - int buflen) -{ - const char *s; - - switch (stlp->results) { - case SELFTEST_FAILURE_INCOMPLETE: - s = "An unknown error occurred while the " - "device server was processing the self-test " - "and the device server was unable to complete " - "the self-test."; - (void) snprintf(buf, buflen, s); - break; - - case SELFTEST_FAILURE_SEG_UNKNOWN: - s = "The self-test completed with a failure in a test " - "segment, and the test segment that failed is not known."; - (void) snprintf(buf, buflen, s); - break; - - case SELFTEST_FAILURE_SEG_FIRST: - s = "The first segment of the self-test failed."; - (void) snprintf(buf, buflen, s); - break; - - case SELFTEST_FAILURE_SEG_SECOND: - s = "The second segment of the self-test failed."; - (void) snprintf(buf, buflen, s); - break; - - case SELFTEST_FAILURE_SEG_OTHER: - /* If the test number was 0, the failure segment is unknown */ - if (stlp->test_number == 0) - s = "The self-test failed in an unknown test segment."; - else - s = "The self-test failed in test segment %d."; - (void) snprintf(buf, buflen, s, stlp->test_number); - break; - - default: - s = "Unknown self-test result code (0x%x (%d))"; - (void) snprintf(buf, buflen, s, stlp->results, stlp->results); - break; - } - - return (buf); -} - -static int -logpage_selftest_param_analyze(diskmon_t *diskinfop, - struct log_parameter_header *lphp) -{ - struct selftest_log_parameter *stlp = - (struct selftest_log_parameter *)lphp; - int result = 0; - const char *fmt; - char buf[MSG_BUFLEN]; - char tsstring[MSG_BUFLEN]; - char lbastring[MSG_BUFLEN]; - char stcause[MSG_BUFLEN]; - ushort_t param_code = BE_16(lphp->param_code); - - /* - * If the self-test failed, log a fault. - */ - if (param_code >= LOGPAGE_SELFTEST_MIN_PARAM_CODE && - param_code <= LOGPAGE_SELFTEST_MAX_PARAM_CODE && - stlp->results >= SELFTEST_FAILURE_INCOMPLETE && - stlp->results <= SELFTEST_FAILURE_SEG_OTHER) { - - uint16_t timestamp = BE_16(stlp->timestamp); - uint64_t lbaaddr = BE_64(stlp->lba_of_first_failure); - - fmt = (timestamp == UINT16_MAX) ? ">= %u disk-hours" : - "%u disk-hours"; - (void) snprintf(tsstring, MSG_BUFLEN, fmt, timestamp); - - /* The lba failure field is only valid if it's not all 1's */ - fmt = (lbaaddr != UINT64_MAX) ? " LBA address of first " - "failure: 0x%llx (%llu)" : ""; - (void) snprintf(lbastring, MSG_BUFLEN, fmt, lbaaddr, lbaaddr); - - (void) snprintf(buf, MSG_BUFLEN, "Disk self-test failed " - "[self-test parameter #%d, time of failure: %s%s]: %s", - param_code, tsstring, lbastring, - disk_selftest_result_string(stlp, stcause, MSG_BUFLEN)); - - add_disk_fault(diskinfop, DISK_FAULT_SOURCE_SELFTEST, - buf, stlp->sense_key, stlp->asc, stlp->ascq, - stlp->results, 0, 0); - - result = -1; - } - - return (result); -} - - -static int -verify_logpage(uchar_t logpage_code, int fd, diskmon_t *diskinfop, - uint_t *skp, uint_t *ascp, uint_t *ascqp) -{ - fault_monitor_info_t *fip = diskinfop->fmip; - struct log_header *lhp; - struct log_parameter_header *lphp; - struct scsi_extended_sense sense; - logpage_validation_fn_t validate_fn; - int buflen; - int resid; - int log_length; - int result = 0; - int i = 0; - int this_param_len = 0; - - /* - * buflen is USHRT_MAX - size of the header because some luns - * return nothing if the buffer length is too big -- it must be sized - * properly (the maximum buffer size is therefore the maximum that - * will fit in a 16-bit integer minus the size of the header.) - */ - buflen = USHRT_MAX - sizeof (struct log_header); - resid = sizeof (struct scsi_extended_sense); - lhp = dzmalloc(buflen); - validate_fn = lookup_logpage_validation_fn(logpage_code); - bzero(&sense, sizeof (struct scsi_extended_sense)); - - if ((validate_fn != NULL) && - ((result = uscsi_log_sense(fd, logpage_code, - logpage_pc_for_verify(logpage_code), - (caddr_t)lhp, buflen, &sense, &resid)) == 0) && - ((log_length = BE_16(lhp->length)) > 0)) { - - lphp = (struct log_parameter_header *)(((uchar_t *)lhp) + - sizeof (struct log_header)); - - while (i < log_length) { - - lphp = (struct log_parameter_header *) - (((uchar_t *)lphp) + this_param_len); - - /* - * If the validation fn returns a negative value, - * that's the signal to clear the supported bit - * for this log page and break out of the loop. - */ - if ((*validate_fn)(diskinfop, lphp) < 0) { - fip->log_pages_supported &= - ~log_page_to_supp_bit(logpage_code); - break; - } - - this_param_len = lphp->length + - sizeof (struct log_parameter_header); - - i += this_param_len; - } - } - - dfree(lhp, buflen); - if (result != 0) - scsi_translate_error(&sense, skp, ascp, ascqp); - return (result); -} - -static int -verify_logpages(int fd, diskmon_t *diskinfop, uint_t *skp, - uint_t *ascp, uint_t *ascqp) -{ - fault_monitor_info_t *fip = diskinfop->fmip; - int result = 0; - int i; - - for (i = 0; logpage_validation_list[i].analyze_fn != NULL; i++) { - - if ((fip->log_pages_supported & - logpage_validation_list[i].supp_bit) == 0) { - - continue; - } - - /* - * verify_logpage will clear the bit from - * log_pages_supported if verification fails - * (which means that the page is not usable) - */ - if (verify_logpage(logpage_validation_list[i].logpage_code, - fd, diskinfop, skp, ascp, ascqp) != 0) { - /* - * If something goes wrong here, this is not a fatal - * error -- just log the error and continue. - */ - log_warn("Error during %s log page verification: " - "KEY=0x%x ASC=0x%x ASCQ=0x%x", - logpage_validation_list[i].descr, *skp, *ascp, - *ascqp); - - result -= 1; - } - } - - return (result); -} - -/* - * This function calls the analysis function that corresponds to the log page - * passed-in. If the analysis function detects a fault in the log page - * parameter it was called with, it fills-in the disk_fault structure passed-in - * with the fault specifics, and log parameter processing stops. - */ -static int -fault_analyze_logpage(uchar_t logpage_code, int fd, diskmon_t *diskinfop, - uint_t *skp, uint_t *ascp, uint_t *ascqp) -{ - struct log_header *lhp; - struct log_parameter_header *lphp; - struct scsi_extended_sense sense; - logpage_analyze_fn_t analyze_fn; - int buflen; - int resid; - int log_length; - int result = 0; - int i = 0; - int this_param_len = 0; - - /* - * buflen is USHRT_MAX - size of the header because some luns - * return nothing if the buffer length is too big -- it must be sized - * properly (the maximum buffer size is therefore the maximum that - * will fit in a 16-bit integer minus the size of the header.) - */ - buflen = USHRT_MAX - sizeof (struct log_header); - resid = sizeof (struct scsi_extended_sense); - lhp = dzmalloc(buflen); - analyze_fn = lookup_logpage_analyze_fn(logpage_code); - bzero(&sense, sizeof (struct scsi_extended_sense)); - - if ((analyze_fn != NULL) && - ((result = uscsi_log_sense(fd, logpage_code, - logpage_pc_for_verify(logpage_code), - (caddr_t)lhp, buflen, &sense, &resid)) == 0) && - ((log_length = BE_16(lhp->length)) > 0)) { - - lphp = (struct log_parameter_header *)(((uchar_t *)lhp) + - sizeof (struct log_header)); - - while (i < log_length) { - - lphp = (struct log_parameter_header *) - (((uchar_t *)lphp) + this_param_len); - - /* - * If the analysis fn returns a negative value, - * then a disk fault identified with this page - * has been identified. - */ - if ((*analyze_fn)(diskinfop, lphp) < 0) - disk_warn(diskinfop, "fault found: log page " - "0x%x, parameter 0x%x.\n", logpage_code, - BE_16(lphp->param_code)); - - this_param_len = lphp->length + - sizeof (struct log_parameter_header); - - i += this_param_len; - } - } - - dfree(lhp, buflen); - if (result != 0) - scsi_translate_error(&sense, skp, ascp, ascqp); - return (result); -} - -static int -fault_analyze_logpages(int fd, diskmon_t *diskinfop, int *failidx, - uint_t *skp, uint_t *ascp, uint_t *ascqp) -{ - fault_monitor_info_t *fip = diskinfop->fmip; - int result = 0; - int i; - - for (i = 0; logpage_validation_list[i].analyze_fn != NULL; i++) { - - if ((fip->log_pages_supported & - logpage_validation_list[i].supp_bit) == 0) { - - continue; - } - - /* - * analyze_logpage will return a negative value if something - * went wrong during a LOG SENSE of the current log page. - */ - if (fault_analyze_logpage( - logpage_validation_list[i].logpage_code, fd, diskinfop, - skp, ascp, ascqp) != 0) { - - /* - * If something goes wrong here, this is not a fatal - * error -- just remember it and continue. - */ - *failidx = i; - result -= 1; - } - } - - return (result); -} - -static int -scsi_request_sense(int fd, diskmon_t *diskinfop, uint_t *skp, - uint_t *ascp, uint_t *ascqp) -{ - fault_monitor_info_t *fip = diskinfop->fmip; - int result; - - result = disk_request_sense(fd, skp, ascp, ascqp); - - /* - * Save the result of a successful REQUEST SENSE - * because error information is cleared after it's - * sent to the host. - */ - if (result == 0) { - fip->last_rs_key = *skp; - fip->last_rs_asc = *ascp; - fip->last_rs_ascq = *ascqp; - } - - return (result); -} - -void -disk_fault_uninit(diskmon_t *diskinfop) -{ - /* - * The only thing that consumes memory is the fault list, so free - * that now: - */ - if (diskinfop->fmip != NULL) { - free_disk_fault_list(diskinfop->fmip); - free_disk_fault_info(&diskinfop->fmip); - } - dm_assert(pthread_mutex_lock(&diskinfop->disk_faults_mutex) == 0); - diskinfop->disk_faults = DISK_FAULT_SOURCE_NONE; - dm_assert(pthread_mutex_unlock(&diskinfop->disk_faults_mutex) == 0); -} - -int -disk_fault_init(diskmon_t *diskinfop) -{ - fault_monitor_info_t *fip; - int fd; - char path[MAXPATHLEN]; - uint_t sense_key = 0, asc = 0, ascq = 0; - int return_code = IE_SUCCESS; - - (void) snprintf(path, MAXPATHLEN, "/devices%s", - dm_prop_lookup(diskinfop->props, DISK_PROP_DEVPATH)); - - if ((fd = open(path, O_RDWR)) < 0) { - disk_warn(diskinfop, "disk_fault_init: Error opening disk " - "node"); - return (-1); - } - - /* Reset fault-tracking statistics */ - diskinfop->due = (time_t)0; - diskinfop->analysis_generation = 0; - - diskinfop->fmip = new_disk_fault_info(); - - fip = diskinfop->fmip; - - /* Initialize key fields: */ - /* Assume we support no extensions */ - fip->extensions = 0; - /* Assume we support no log pages */ - fip->log_pages_supported = 0; - /* Assume we support no mode pages */ - fip->mode_pages_supported = 0; - - if (modepages_init(fd, diskinfop, &sense_key, &asc, &ascq) - != 0) { - - /* - * If the error was an invalid opcode, then mode sense - * isn't supported, and, by extension, IE isn't supported. - * If the error is "mode page unsupported", then this lun - * is equally as useless. - */ - if (SCSI_INVALID_OPCODE(sense_key, asc, ascq) || - MODE_PAGE_UNSUPPORTED(sense_key, asc, ascq)) - return_code = IE_NOT_SUPPORTED; - else { - log_err("modepages_init failed: " - "KEY=0x%x ASC=0x%x ASCQ=0x%x", sense_key, asc, - ascq); - return_code = IE_OTHER_ERROR; - } - - } else if (!MODE_PAGE_SUPPORTED(fip, MODEPAGE_SUPP_IEC)) { - - disk_note(diskinfop, - "No IEC mode page present -- IE (SMART) not supported.\n"); - return_code = IE_NOT_SUPPORTED; - - } else if ((return_code = ie_enable_and_save(fd, diskinfop, &sense_key, - &asc, &ascq, OPT_ENABLED(fip, OPTION_TEST_MODE), - OPT_ENABLED(fip, OPTION_PERF_MODE))) != 0) { - - log_err("Error during IEC mode page read/update: " - "KEY=0x%x ASC=0x%x ASCQ=0x%x", sense_key, asc, ascq); - - } else if (supported_log_pages(fd, diskinfop, &sense_key, &asc, &ascq) - != 0) { - - /* - * If there's an error retrieving the list of supported log - * pages, then continue with a warning. - */ - - disk_warn(diskinfop, - "Error during LOG SENSE of supported pages: " - "KEY=0x%x ASC=0x%x ASCQ=0x%x -- not using any " - "log pages for fault monitoring.\n", sense_key, asc, ascq); - - } else { - (void) verify_logpages(fd, diskinfop, &sense_key, &asc, &ascq); - } - - (void) close(fd); - - return (return_code); -} - -static int -disk_check_ie_sense(int fd, diskmon_t *diskinfop, uint_t *skp, - uint_t *ascp, uint_t *ascqp) -{ - int result; - uint_t sense_key, asc, ascq; - - if (scsi_request_sense(fd, diskinfop, &sense_key, &asc, &ascq) != 0) { - - *skp = sense_key; - *ascp = asc; - *ascqp = ascq; - - result = -1; - } else { - - /* - * If the sense key is NO SENSE, and the ASC is - * any nonzero value, then we have an impending failure - */ - if (sense_key == KEY_NO_SENSE && asc != 0) { - - add_disk_fault(diskinfop, DISK_FAULT_SOURCE_INFO_EXCPT, - NULL, sense_key, asc, ascq, 0, 0, 0); - } - - result = 0; - } - - return (result); -} - -/* - * Returns n>0 if there are disk faults (n faults) - * 0 if there are no disk faults - * <0 if there was a problem accessing the disk - */ -int -disk_fault_analyze(diskmon_t *diskinfop) -{ - int i, fd; - int faults = 0; - uint_t sk, asc, ascq; - struct disk_fault *flt; - boolean_t print_msg; - fault_monitor_info_t *fip = diskinfop->fmip; - char path[MAXPATHLEN]; - disk_flt_src_e before_disk_fault_srcs = fip->disk_fault_srcs; - - (void) snprintf(path, MAXPATHLEN, "/devices%s", - dm_prop_lookup(diskinfop->props, DISK_PROP_DEVPATH)); - - if ((fd = open(path, O_RDWR)) < 0) { - disk_err(diskinfop, "disk_fault_analyze: Error opening disk " - "node"); - return (-1); - } - - /* - * Grab the fault list mutex here because any of the functions below - * can add to it. - */ - dm_assert(pthread_mutex_lock(&fip->fault_data_mutex) == 0); - - if (fault_analyze_logpages(fd, diskinfop, &i, &sk, &asc, &ascq) != 0) { - disk_warn(diskinfop, "Error during %s log page analysis: " - "KEY=0x%x ASC=0x%x ASCQ=0x%x\n", - logpage_validation_list[i].descr, sk, asc, ascq); - } - - /* - * We only need the unsolicited request-sense if we don't have the - * IE log page. - */ - if (!LOG_PAGE_SUPPORTED(fip, LOGPAGE_SUPP_IE) && - disk_check_ie_sense(fd, diskinfop, &sk, &asc, &ascq) != 0) { - - disk_err(diskinfop, "Request Sense failure: " - "KEY=0x%x ASC=0x%x ASCQ=0x%x\n", sk, asc, ascq); - } - - (void) close(fd); - - /* - * If any disk faults were added to the diskinfo structure, then - * we may have a disk fault condition. - */ - - if (before_disk_fault_srcs == fip->disk_fault_srcs) { - dm_assert(pthread_mutex_unlock(&fip->fault_data_mutex) == 0); - return (0); - } - - - flt = fip->fault_list; - while (flt != NULL) { - if (OPT_ENABLED(fip, OPTION_SELFTEST_ERRS_ARE_FATAL) && - flt->fault_src == DISK_FAULT_SOURCE_SELFTEST) { - - faults++; - print_msg = B_TRUE; - - } else if (OPT_ENABLED(fip, - OPTION_OVERTEMP_ERRS_ARE_FATAL) && - flt->fault_src == DISK_FAULT_SOURCE_OVERTEMP) { - - faults++; - print_msg = B_TRUE; - - } else if (flt->fault_src == DISK_FAULT_SOURCE_INFO_EXCPT) { - - faults++; - print_msg = B_TRUE; - } else - print_msg = B_FALSE; - - if (print_msg) - disk_err(diskinfop, "%s\n", flt->msg); - - flt = flt->next; - } - dm_assert(pthread_mutex_unlock(&fip->fault_data_mutex) == 0); - - return (faults); -} diff --git a/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/fault_analyze.h b/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/fault_analyze.h deleted file mode 100644 index 5a8833462d..0000000000 --- a/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/fault_analyze.h +++ /dev/null @@ -1,154 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License (the "License"). - * You may not use this file except in compliance with the License. - * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] - * - * CDDL HEADER END - */ - -/* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -#ifndef _FAULT_ANALYZE_H -#define _FAULT_ANALYZE_H - -#pragma ident "%Z%%M% %I% %E% SMI" - -/* - * Definitions for data structures used in the SCSI IE module - */ - -#ifdef __cplusplus -extern "C" { -#endif - -#include "dm_types.h" - -#define MSG_BUFLEN 256 /* Message buffer length */ - -#define IE_SUCCESS 0 -#define IE_NOT_SUPPORTED 1 -#define IE_CANNOT_BE_ENABLED 2 -#define IE_ENABLE_FAILED 3 -#define IE_ENABLE_DIDNT_STICK 4 -#define IE_OTHER_ERROR 10 - -#define MODE_CMD_LEN_UNKNOWN 0 -#define MODE_CMD_LEN_6 1 -#define MODE_CMD_LEN_10 2 - -typedef enum { - MODEPAGE_SUPP_IEC = 0x00000001 -} modepage_supp_e; - -typedef enum { - LOGPAGE_SUPP_IE = 0x00000001, - LOGPAGE_SUPP_TEMP = 0x00000002, - LOGPAGE_SUPP_SELFTEST = 0x00000004 -} logpage_supp_e; - -typedef enum { - EXTN_IE_TEMP_THRESHOLD = 0x00000001, - EXTN_TEMPLOG_TEMP_THRESHOLD = 0x00000002 -} disk_extension_e; - -typedef enum { - OPTION_PERF_MODE = 0x00000001, - OPTION_TEST_MODE = 0x00000002, - OPTION_SELFTEST_ERRS_ARE_FATAL = 0x00000004, - OPTION_OVERTEMP_ERRS_ARE_FATAL = 0x00000008 -} disk_option_e; - -/* This MUST be a MASK: */ -typedef enum { - DISK_FAULT_SOURCE_NONE = 0x00000000, - DISK_FAULT_SOURCE_SELFTEST = 0x00000001, - DISK_FAULT_SOURCE_OVERTEMP = 0x00000002, - DISK_FAULT_SOURCE_INFO_EXCPT = 0x00000004 -} disk_flt_src_e; - -#define LOG_PAGE_SUPPORTED(di, supp) ((di)->log_pages_supported & (supp)) -#define MODE_PAGE_SUPPORTED(di, supp) ((di)->mode_pages_supported & (supp)) -#define EXTN_SUPPORTED(di, extn) ((di)->extensions & (extn)) -#define OPT_ENABLED(di, opt) ((di)->options & (opt)) - -struct scsi_ms_hdrs { - int length; - union { - struct scsi_ms_header g0; - struct scsi_ms_header_g1 g1; - } h; -}; - -/* Yea, this can be a union... */ -struct disk_fault { - disk_flt_src_e fault_src; - char *msg; - - /* Predictive failure information */ - boolean_t sense_valid; - uchar_t sense_key; - uchar_t asc; - uchar_t ascq; - - /* Self-test failure information */ - uint16_t selftest_code; - - /* Temperature information */ - int cur_temp; - int thresh_temp; - struct disk_fault *next; -}; - -struct diskmon; -struct fault_monitor_info; - -/* - * Each of the following validation or analysis functions are called once - * for each parameter type in the log page for which the function is - * written. If there's a problem with the parameter passed-in, the - * function returns a negative number. - */ - -typedef int (*logpage_validation_fn_t)(struct diskmon *, - struct log_parameter_header *); - -typedef int (*logpage_analyze_fn_t)(struct diskmon *, - struct log_parameter_header *); - -struct logpage_validation_entry { - uchar_t logpage_code; - int supp_bit; - uchar_t pc; - const char *descr; - boolean_t enabled; - logpage_validation_fn_t validate_fn; - logpage_analyze_fn_t analyze_fn; -}; - -extern int disk_fault_init(struct diskmon *diskp); -extern void disk_fault_uninit(struct diskmon *diskp); -extern int disk_fault_analyze(struct diskmon *diskp); -extern void free_disk_fault_list(struct fault_monitor_info *fmip); -extern void create_fake_faults(struct diskmon *diskp); - -#ifdef __cplusplus -} -#endif - -#endif /* _FAULT_ANALYZE_H */ diff --git a/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/fault_mgr.c b/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/fault_mgr.c deleted file mode 100644 index 6caed02dca..0000000000 --- a/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/fault_mgr.c +++ /dev/null @@ -1,261 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License (the "License"). - * You may not use this file except in compliance with the License. - * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] - * - * CDDL HEADER END - */ - -/* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -#pragma ident "%Z%%M% %I% %E% SMI" - -#include <atomic.h> -#include <sys/types.h> -#include <time.h> - -#include "sfx4500-disk.h" -#include "fault_mgr.h" -#include "schg_mgr.h" - -/* Fault-polling thread data */ -static pthread_t g_fmt_tid; -static thread_state_t g_fmt_req_state = TS_NOT_RUNNING; -static pthread_cond_t g_fmt_cvar = PTHREAD_COND_INITIALIZER; -static pthread_mutex_t g_fmt_mutex = PTHREAD_MUTEX_INITIALIZER; -static boolean_t g_fmt_spawned = B_FALSE; - -static boolean_t -disk_is_faulty(diskmon_t *diskp) -{ - /* - * Errors accessing the disk are not counted as faults: - */ - return (disk_fault_analyze(diskp) > 0 ? B_TRUE : B_FALSE); -} - -static void -setup_fault_injection(diskmon_t *disklistp, int i) -{ - uint_t seed; - - while (disklistp != NULL) { - /* We just want the low bits of hrtime anyway */ - seed = (uint_t)gethrtime(); - - disklistp->fault_inject_count = (rand_r(&seed) % (i + 1)) + 1; - - log_msg(MM_FAULTMGR, "[%s] Injecting a fault every %u " - "analyses.\n", disklistp->location, - disklistp->fault_inject_count); - - disklistp = disklistp->next; - } -} - -static void -disk_fault_monitor_analyze_disk(diskmon_t *diskp) -{ - atomic_inc_uint(&diskp->analysis_generation); - - log_msg(MM_FAULTMGR, "[%s] Analyzing disk for faults\n", - diskp->location); - - if (diskp->fmip && disk_is_faulty(diskp)) { - - diskp->faults_outstanding = B_TRUE; - log_msg(MM_FAULTMGR, "[%s] Disk fault(s) detected...\n", - diskp->location); - dm_state_change(diskp, HPS_FAULTED); - - } else if (diskp->fault_inject_count != 0 && - (diskp->analysis_generation % diskp->fault_inject_count) == 0) { - - diskp->analysis_generation = 0; - - log_msg(MM_FAULTMGR, "[%s] FAULT INJECTED\n", diskp->location); - - create_fake_faults(diskp); - dm_state_change(diskp, HPS_FAULTED); - - } else { - log_msg(MM_FAULTMGR, "[%s] No faults detected\n", - diskp->location); - } -} - -/* - * The fault monitor thread polls each disk in the disk list, at the - * fault polling frequency specified in the global property (or the default - * if no such property exists). This thread is also responsible for injecting - * fake faults, in accordance with the global fault injection property. - * - * When the thread starts, it performs a fault analysis on each disk whose - * `due' time is 0 (disks that have not yet been analyzed), then sets the - * due time to the current time + the fault polling interval. - */ -static void -disk_fault_monitor_thread(void *vdisklistp) -{ - diskmon_t *disklistp = (diskmon_t *)vdisklistp; - diskmon_t *diskp; - time_t fault_polling_interval = (time_t)DEFAULT_FAULT_POLLING_INTERVAL; - time_t earliest_due; - time_t curtime; - time_t nexttime; - struct timespec tspec; - int i; - - if (dm_prop_lookup_int(dm_global_proplist(), GLOBAL_PROP_FAULT_POLL, &i) - == 0) - fault_polling_interval = (time_t)i; - - if (dm_prop_lookup_int(dm_global_proplist(), GLOBAL_PROP_FAULT_INJ, &i) - == 0 && i > 0) { - setup_fault_injection(disklistp, i); - } - - dm_assert(pthread_mutex_lock(&g_fmt_mutex) == 0); - while (g_fmt_req_state != TS_EXIT_REQUESTED) { - - /* - * Analyze all disks that are due for analysis - */ - diskp = disklistp; - earliest_due = -1; - while (g_fmt_req_state != TS_EXIT_REQUESTED && diskp != NULL) { - - curtime = time(0); - dm_assert(pthread_mutex_lock(&diskp->manager_mutex) - == 0); - - /* - * If the disk is configured (it has a device node - * associated with it that we can talk to), and if - * there are no faults outstanding (faults that we - * previously informed the state-change thread about - * but that may not have been consumed yet), and - * if we're due for a fault analysis, then do one. - */ - if (DISK_STATE(diskp->state) == HPS_CONFIGURED && - !diskp->faults_outstanding && - (diskp->due == 0 || diskp->due <= curtime)) { - - log_msg(MM_FAULTMGR, "Analyzing disk %s...\n", - diskp->location); - - disk_fault_monitor_analyze_disk(diskp); - diskp->due = time(0) + fault_polling_interval; - } - - /* Keep track of the earliest next due time */ - if (diskp->due > 0) - earliest_due = (earliest_due < 0) ? diskp->due : - MIN(earliest_due, diskp->due); - - dm_assert(pthread_mutex_unlock(&diskp->manager_mutex) - == 0); - - diskp = diskp->next; - } - - /* - * earliest_due can be < 0 (if no disks were fault-analyzed) - * but it should NEVER be == 0. - */ - if (earliest_due < 0) { - nexttime = time(0) + fault_polling_interval; - earliest_due = nexttime; - } else if (earliest_due == 0) { - nexttime = time(0) + fault_polling_interval; - log_warn("BUG: earliest_due time is == 0-- resetting " - "to %ld\n", nexttime); - earliest_due = nexttime; - } - - tspec.tv_sec = earliest_due; - tspec.tv_nsec = 0; - (void) pthread_cond_timedwait(&g_fmt_cvar, - &g_fmt_mutex, &tspec); - } - dm_assert(pthread_mutex_unlock(&g_fmt_mutex) == 0); - - log_msg(MM_FAULTMGR, "Fault monitor polling thread exiting...\n"); -} - -static int -create_fault_monitor_thread(diskmon_t *disklistp) -{ - /* fmt_thr_create() is guaranteed to succeed or abort */ - g_fmt_tid = fmd_thr_create(g_fm_hdl, disk_fault_monitor_thread, - disklistp); - g_fmt_spawned = B_TRUE; - - return (0); -} - -static void -collect_fault_monitor_thread(void) -{ - if (g_fmt_spawned) { - - g_fmt_req_state = TS_EXIT_REQUESTED; - dm_assert(pthread_mutex_lock(&g_fmt_mutex) == 0); - dm_assert(pthread_cond_broadcast(&g_fmt_cvar) == 0); - dm_assert(pthread_mutex_unlock(&g_fmt_mutex) == 0); - fmd_thr_signal(g_fm_hdl, g_fmt_tid); - fmd_thr_destroy(g_fm_hdl, g_fmt_tid); - g_fmt_req_state = TS_NOT_RUNNING; - g_fmt_tid = NULL; - g_fmt_spawned = B_FALSE; - } -} - -int -init_fault_manager(cfgdata_t *cfgdatap) -{ - int i; - - if (dm_prop_lookup_int(dm_global_proplist(), GLOBAL_PROP_FAULT_POLL, &i) - == 0 && i > 0) - return (create_fault_monitor_thread(cfgdatap->disk_list)); - else { - g_fmt_spawned = B_FALSE; - return (0); - } -} - -/* - * fault_manager_poke wakes up the fault manager thread so it can - * perform initial fault analysis on new disks. - */ -void -fault_manager_poke(void) -{ - dm_assert(pthread_mutex_lock(&g_fmt_mutex) == 0); - dm_assert(pthread_cond_broadcast(&g_fmt_cvar) == 0); - dm_assert(pthread_mutex_unlock(&g_fmt_mutex) == 0); -} - -/*ARGSUSED*/ -void -cleanup_fault_manager(cfgdata_t *cfgdatap) -{ - collect_fault_monitor_thread(); -} diff --git a/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/fm_disk_events.h b/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/fm_disk_events.h deleted file mode 100644 index b5140c65f8..0000000000 --- a/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/fm_disk_events.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License (the "License"). - * You may not use this file except in compliance with the License. - * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] - * - * CDDL HEADER END - */ - -/* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -#ifndef _FM_DISK_EVENTS_H -#define _FM_DISK_EVENTS_H - -#pragma ident "%Z%%M% %I% %E% SMI" - -/* - * Event class names and payload member name definitions - */ - -#ifdef __cplusplus -extern "C" { -#endif - -#include <sys/fm/protocol.h> - -/* - * SATA Disk EREPORTS and their Payload members - */ -#define EREPORT_SATA FM_EREPORT_CLASS "." FM_ERROR_IO ".sata" - -#define EREPORT_SATA_PREDFAIL EREPORT_SATA ".predictive-failure" -#define EV_PAYLOAD_ASC "additional-sense-code" -#define EV_PAYLOAD_ASCQ "additional-sense-code-qualifier" - -#define EREPORT_SATA_OVERTEMP EREPORT_SATA ".over-temperature" -#define EV_PAYLOAD_CURTEMP "current-temp" -#define EV_PAYLOAD_THRESH "threshold-temp" - -#define EREPORT_SATA_STFAIL EREPORT_SATA ".self-test-failure" -#define EV_PAYLOAD_STCODE "self-test-result-code" - -/* - * Disk FAULT events - */ -#define FAULT_DISK FM_FAULT_CLASS "." FM_ERROR_IO ".disk" -#define FAULT_DISK_PREDFAIL FAULT_DISK ".predictive-failure" -#define FAULT_DISK_OVERTEMP FAULT_DISK ".over-temperature" -#define FAULT_DISK_STFAIL FAULT_DISK ".self-test-failure" - -#ifdef __cplusplus -} -#endif - -#endif /* _FM_DISK_EVENTS_H */ diff --git a/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/schg_mgr.c b/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/schg_mgr.c index ea144e54f7..45911a4b53 100644 --- a/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/schg_mgr.c +++ b/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/schg_mgr.c @@ -35,10 +35,7 @@ #include "sfx4500-disk.h" #include "schg_mgr.h" #include "hotplug_mgr.h" -#include "fault_mgr.h" -#include "fault_analyze.h" #include "topo_gather.h" -#include "fm_disk_events.h" #include "dm_platform.h" /* State-change event processing thread data */ @@ -247,137 +244,6 @@ schg_update_fru_info(diskmon_t *diskp) } } -/* - * [Lifted from fmd] - * Create a local ENA value for fmd-generated ereports. We use ENA Format 1 - * with the low bits of gethrtime() and pthread_self() as the processor ID. - */ -static uint64_t -dm_gen_ena(void) -{ - hrtime_t hrt = gethrtime(); - - return ((uint64_t)((FM_ENA_FMT1 & ENA_FORMAT_MASK) | - ((pthread_self() << ENA_FMT1_CPUID_SHFT) & ENA_FMT1_CPUID_MASK) | - ((hrt << ENA_FMT1_TIME_SHFT) & ENA_FMT1_TIME_MASK))); -} - - -static void -dm_send_ereport(char *class, uint64_t ena, nvlist_t *detector, - nvlist_t *payload) -{ - nvlist_t *nvl; - int e = 0; - - if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) == 0) { - /* - * An ereport nvlist consists of: - * - * FM_CLASS class - * FM_VERSION FM_EREPORT_VERSION - * FM_EREPORT_ENA ena - * FM_EREPORT_DETECTOR detector - * <Other payload members> - * - */ - e |= nvlist_add_string(nvl, FM_CLASS, class); - e |= nvlist_add_uint8(nvl, FM_VERSION, FM_EREPORT_VERSION); - e |= nvlist_add_uint64(nvl, FM_EREPORT_ENA, ena); - e |= nvlist_add_nvlist(nvl, FM_EREPORT_DETECTOR, detector); - e |= nvlist_merge(nvl, payload, 0); - - if (e == 0) - fmd_xprt_post(g_fm_hdl, g_xprt_hdl, nvl, 0); - else - nvlist_free(nvl); - } -} - -static void -schg_consume_faults(diskmon_t *diskp) -{ - struct disk_fault *faults; - nvlist_t *nvl; - uint64_t ena; - char *er_class; - int e; - - dm_assert(diskp->fmip != NULL); - - /* Use the same ENA for all current faults */ - ena = dm_gen_ena(); - - dm_assert(pthread_mutex_lock(&diskp->fmip->fault_data_mutex) == 0); - faults = diskp->fmip->fault_list; - - /* Go through the list of faults, executing actions if present */ - while (faults != NULL) { - - dm_assert(pthread_mutex_lock(&diskp->disk_faults_mutex) == 0); - diskp->disk_faults |= faults->fault_src; - dm_assert(pthread_mutex_unlock(&diskp->disk_faults_mutex) == 0); - - er_class = NULL; - e = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0); - - switch (faults->fault_src) { - case DISK_FAULT_SOURCE_SELFTEST: - if (e == 0) { - er_class = EREPORT_SATA_STFAIL; - e |= nvlist_add_uint16(nvl, - EV_PAYLOAD_STCODE, - faults->selftest_code); - } - break; - - case DISK_FAULT_SOURCE_OVERTEMP: - if (e == 0) { - er_class = EREPORT_SATA_OVERTEMP; - e |= nvlist_add_uint16(nvl, - EV_PAYLOAD_CURTEMP, - faults->cur_temp); - e |= nvlist_add_uint16(nvl, - EV_PAYLOAD_THRESH, - faults->thresh_temp); - } - break; - - case DISK_FAULT_SOURCE_INFO_EXCPT: - er_class = EREPORT_SATA_PREDFAIL; - if (e == 0 && faults->sense_valid) { - e |= nvlist_add_uint16(nvl, - EV_PAYLOAD_ASC, - faults->asc); - e |= nvlist_add_uint16(nvl, - EV_PAYLOAD_ASCQ, - faults->ascq); - } - break; - - default: - break; - } - - if (e == 0 && er_class != NULL) { - log_msg(MM_SCHGMGR, "[%s] Sending ereport %s\n", - diskp->location, er_class); - - dm_send_ereport(er_class, ena, - diskp->disk_res_fmri, nvl); - } - - if (nvl) - nvlist_free(nvl); - - faults = faults->next; - } - - free_disk_fault_list(diskp->fmip); - - dm_assert(pthread_mutex_unlock(&diskp->fmip->fault_data_mutex) == 0); -} - void block_state_change_events(void) { @@ -421,9 +287,7 @@ disk_state_change_thread(void *vdisklistp) diskmon_t *disklistp = (diskmon_t *)vdisklistp; diskmon_t *diskp; disk_statechg_t *dscp; - int err; hotplug_state_t nextstate; - boolean_t poke_fault_manager; const char *pth; /* @@ -453,8 +317,6 @@ disk_state_change_thread(void *vdisklistp) continue; } - poke_fault_manager = B_FALSE; - diskp = dscp->diskp; /* @@ -501,20 +363,7 @@ disk_state_change_thread(void *vdisklistp) * */ - if (dscp->newstate == HPS_FAULTED) { - - /* - * fmip can be NULL here if the DE is processing - * replayed faults. In this case, no need to - * do anything. - */ - if (diskp->fmip) { - schg_consume_faults(diskp); - - diskp->faults_outstanding = B_FALSE; - } - - } else if (dscp->newstate != HPS_FAULTED && + if (dscp->newstate != HPS_FAULTED && DISK_STATE(nextstate) != HPS_UNKNOWN && dscp->newstate != HPS_REPAIRED) { @@ -533,8 +382,7 @@ disk_state_change_thread(void *vdisklistp) * the disk is UNCONFIGURED, there's another * state change somewhere later in the queue), then * it's possible for the disk path property to not - * exist -- so check it, and if it doesn't exist, - * do not try to do disk_fault_init. + * exist. */ if (dm_prop_lookup(diskp->props, DISK_PROP_DEVPATH) == NULL) { @@ -543,30 +391,8 @@ disk_state_change_thread(void *vdisklistp) "Processed stale state change " "for disk %s\n", diskp->location); - } else if ((err = disk_fault_init(diskp)) != 0) { - - if (err != IE_NOT_SUPPORTED) - log_warn("Error initializing fault " - "monitor for disk in %s.\n", - diskp->location); } else { diskp->configured_yet = B_TRUE; - - /* - * Now that the fault info for the disk is - * initialized, we can request that the fault - * manager do an analysis immediately. This is - * an initial analysis, so it will only happen - * the first time the disk enters the CONFIGURED - * state. After that, it is polled at the - * fault-polling interval. - * Note that the poking of the fault manager - * MUST occur AFTER the disk state is set - * (below), otherwise, the fault manager could - * ignore the disk if it analyzes it before - * this thread can set the state to configured. - */ - poke_fault_manager = B_TRUE; } } @@ -593,35 +419,11 @@ disk_state_change_thread(void *vdisklistp) */ dm_assert(DISK_STATE(nextstate) != HPS_CONFIGURED); - disk_fault_uninit(diskp); - diskp->configured_yet = B_FALSE; - } else if (dscp->newstate == HPS_REPAIRED) { - /* - * Drop the current list of faults. There may be - * other faults pending in the fault list, and that's - * OK. - */ - dm_assert(pthread_mutex_lock(&diskp->disk_faults_mutex) - == 0); - diskp->disk_faults = DISK_FAULT_SOURCE_NONE; - dm_assert( - pthread_mutex_unlock(&diskp->disk_faults_mutex) - == 0); } dm_assert(pthread_mutex_unlock(&diskp->manager_mutex) == 0); - - if (poke_fault_manager) { - /* - * Now we can wake up the fault monitor thread - * to do an initial analysis of the disk: - */ - poke_fault_manager = B_FALSE; - fault_manager_poke(); - } - pth = dm_prop_lookup(diskp->props, DISK_PROP_DEVPATH); log_msg(MM_SCHGMGR, diff --git a/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/scsi_util.h b/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/scsi_util.h deleted file mode 100644 index c42e168459..0000000000 --- a/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/scsi_util.h +++ /dev/null @@ -1,400 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License (the "License"). - * You may not use this file except in compliance with the License. - * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] - * - * CDDL HEADER END - */ - -/* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -#ifndef _SCSI_UTIL_H -#define _SCSI_UTIL_H - -#pragma ident "%Z%%M% %I% %E% SMI" - -/* - * Definitions for data structures used in the SCSI IE module - */ - -#ifdef __cplusplus -extern "C" { -#endif - -#include <sys/types.h> -#include <sys/byteorder.h> -#include <sys/scsi/scsi.h> - -/* - * Log page structures - */ -#pragma pack(1) - -struct log_header { - -#if defined(_BIT_FIELDS_LTOH) - - uint8_t code : 6, - rsrvd : 2; - -#elif defined(_BIT_FIELDS_HTOL) - - uint8_t rsrvd : 2, - code : 6; - -#else -#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined -#endif /* _BIT_FIELDS_LTOH */ - - uint8_t reserved; - uint16_t length; -}; - -struct log_parameter_header { - - uint16_t param_code; - -#if defined(_BIT_FIELDS_LTOH) - - uint8_t lp : 1, - lbin : 1, - tmc : 2, - etc : 1, - tsd : 1, - ds : 1, - du : 1; - -#elif defined(_BIT_FIELDS_HTOL) - - uint8_t du : 1, - ds : 1, - tsd : 1, - etc : 1, - tmc : 2, - lbin : 1, - lp : 1; - -#else -#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined -#endif /* _BIT_FIELDS_LTOH */ - - uint8_t length; -}; - - -struct supported_log_pages { - struct log_header hdr; - uchar_t pages[1]; -}; - -/* - * Specific log page parameters: - */ -struct info_excpt_log_param { - struct log_parameter_header hdr; - uchar_t ie_asc; - uchar_t ie_ascq; - uchar_t last_temp; - - /* - * The following fields may or may not exist -- depending on the - * length specified in the log parameter header. - */ - union { - struct ie_ibm_extensions { - uchar_t ibm_temp_threshold; - } ibm_e; - } vnd; -#define ex_temp_threshold vnd.ibm_e.ibm_temp_threshold -}; - -/* - * The SCSI-3 SPC document states that IE log page (0x2F) parameter 0 - * must have a length of at least 4 (including the length byte). - * Drives that provide 5 bytes use the 5th byte as the temperature - * threshold (reference) value. - */ -#define LOGPARAM_IE_MIN_LEN 2 /* the asc and ascq fields */ -#define LOGPARAM_IE_WITH_TEMP_MIN_LEN (LOGPARAM_IE_MIN_LEN + 1) - -#define INVALID_TEMPERATURE 0xff - -#define LOGPARAM_IE 0x0000 - - -struct temperature_log_param_curtemp { - struct log_parameter_header hdr; - uchar_t rsrvd; - uchar_t current_temp; -}; - -struct temperature_log_param_reftemp { - struct log_parameter_header hdr; - uchar_t rsrvd2; - uchar_t reference_temp; - -#define REFTEMP_INVALID 0xff -}; - -struct selftest_log_parameter { - struct log_parameter_header hdr; - -#if defined(_BIT_FIELDS_LTOH) - - uint8_t results : 4, - rsrvd : 1, - testcode : 3; - -#elif defined(_BIT_FIELDS_HTOL) - - uint8_t testcode : 3, - rsrvd : 1, - results : 4; - -#endif /* _BIT_FIELDS_LTOH */ - - uint8_t test_number; - uint16_t timestamp; - uint64_t lba_of_first_failure; - -#if defined(_BIT_FIELDS_LTOH) - - uint8_t sense_key : 4, - rsrvd1 : 4; - -#elif defined(_BIT_FIELDS_HTOL) - - uint8_t rsrvd1 : 4, - sense_key : 4; - -#endif /* _BIT_FIELDS_LTOH */ - - uint8_t asc; - uint8_t ascq; - uint8_t vendor_specific; -}; - -/* The results field of the self-test log parameter */ -#define SELFTEST_FAILURE_INCOMPLETE 3 -#define SELFTEST_FAILURE_SEG_UNKNOWN 4 -#define SELFTEST_FAILURE_SEG_FIRST 5 -#define SELFTEST_FAILURE_SEG_SECOND 6 -#define SELFTEST_FAILURE_SEG_OTHER 7 - -#define LOGPARAM_TEMP_CURTEMP 0x0000 -#define LOGPARAM_TEMP_REFTEMP 0x0001 - -#define LOGPARAM_TEMP_CURTEMP_LEN \ - (sizeof (struct temperature_log_param_curtemp) - \ - sizeof (struct log_parameter_header)) - -#define LOGPARAM_TEMP_REFTEMP_LEN \ - (sizeof (struct temperature_log_param_reftemp) - \ - sizeof (struct log_parameter_header)) - -/* - * Mode sense/select page header information - */ -struct scsi_ms_header { - struct mode_header mode_header; - struct block_descriptor block_descriptor; -}; - -struct scsi_ms_header_g1 { - struct mode_header_g1 mode_header; - struct block_descriptor block_descriptor; -}; - -struct info_except_page { - struct mode_page mp; - -#if defined(_BIT_FIELDS_LTOH) - - uint8_t logerr : 1, /* Errors should be logged */ - rsrvd1 : 1, - test : 1, /* Enable test gen of IEs */ - dexcpt : 1, /* Disable exceptions */ - ewasc : 1, /* Enable warning generation */ - ebf : 1, /* enable backgrnd functions */ - rsrvd2 : 1, - perf : 1; /* No delays during excptns */ - - uint8_t mrie : 4, /* Method/reporting excptons */ - rsrvd3 : 4; - -#elif defined(_BIT_FIELDS_HTOL) - - uint8_t perf : 1; /* No delays during excptons */ - rsrvd2 : 1, - ebf : 1, /* enable background funcs */ - ewasc : 1, /* Enable warning generation */ - dexcpt : 1, /* Disable exceptions */ - test : 1, /* Enable test gen of IEs */ - rsrvd1 : 1, - logerr : 1, /* Errors should be logged */ - - uint8_t rsrvd3 : 4; - mrie : 4, /* Method of report excptns */ - - -#else -#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined -#endif /* _BIT_FIELDS_LTOH */ - - uint32_t interval_timer; /* reporting grnulrty for IEs */ - uint32_t report_count; /* # of times to report an IE */ -}; - -#pragma pack() - -#define MODEPAGE_INFO_EXCPT_LEN (sizeof (struct info_except_page)) - -#define IEC_IE_ENABLED(ies) ((ies).dexcpt == 0) -#define IEC_IE_CHANGEABLE(ies) ((ies).dexcpt == 1) -#define IEC_MRIE_CHANGEABLE(ies) ((ies).mrie == 0xf) -#define IEC_PERF_CHANGEABLE(ies) ((ies).perf == 1) -#define IEC_EWASC_CHANGEABLE(ies) ((ies).ewasc == 1) -#define IEC_TEST_CHANGEABLE(ies) ((ies).test == 1) -#define IEC_RPTCNT_CHANGEABLE(ies) ((ies).report_count == BE_32(0xffffffff)) -#define IEC_LOGERR_CHANGEABLE(ies) ((ies).logerr == 1) - -/* - * Values for the MSIE field of the informational exceptions control mode page - */ -#define IE_REPORT_NONE 0 -#define IE_REPORT_ASYNCH 1 -#define IE_REPORT_UNIT_ATTN 2 -#define IE_REPORT_RECOV_ERR_COND 3 -#define IE_REPORT_RECOV_ERR_ALWAYS 4 -#define IE_REPORT_NO_SENSE 5 -#define IE_REPORT_ON_REQUEST 6 - -/* - * Constants in support of the CONTROL MODE mode page (page 0xA) - */ -#define MODEPAGE_CTRL_MODE_LEN (sizeof (struct mode_control_scsi3)) -#define GLTSD_CHANGEABLE(chg) ((chg).gltsd == 1) - -#define LOGPAGE_SELFTEST_MIN_PARAM_CODE 0x0001 -#define LOGPAGE_SELFTEST_MAX_PARAM_CODE 0x0014 - -#define LOGPAGE_SELFTEST_PARAM_LEN \ - ((sizeof (struct selftest_log_parameter)) - \ - (sizeof (struct log_parameter_header))) - -/* - * Macro to extract the length of a mode sense page - * as returned by a target. - */ -#define MODESENSE_PAGE_LEN(p) (((int)((struct mode_page *)p)->length) + \ - sizeof (struct mode_page)) - -/* - * Mode Select options - */ -#define MODE_SELECT_SP 0x01 -#define MODE_SELECT_PF 0x10 - - -/* - * Mode Sense Page Control - */ -#define PC_CURRENT (0 << 6) -#define PC_CHANGEABLE (1 << 6) -#define PC_DEFAULT (2 << 6) -#define PC_SAVED (3 << 6) - -/* - * Log Sense Page Control - */ -#define PC_CUMULATIVE (1 << 6) - -/* - * These flags are used to control disk command execution. - */ -#define F_NORMAL 0x00 /* normal operation */ -#define F_SILENT 0x01 /* no error msgs at all */ -#define F_ALLERRS 0x02 /* return any error, not just fatal */ -#define F_RQENABLE 0x04 /* no error msgs at all */ - -#define INVALID_SENSE_KEY 0x10 /* An impossible sense key */ - -/* - * LOG page codes - */ -#define LOGPAGE_SUPP_LIST 0x00 -#define LOGPAGE_TEMP 0x0d -#define LOGPAGE_SELFTEST 0x10 -#define LOGPAGE_IE 0x2f - -/* - * "impossible" status value - */ -#define IMPOSSIBLE_SCSI_STATUS 0xff - -/* - * Minimum length of Request Sense data that we can accept - */ -#define MIN_REQUEST_SENSE_LEN 18 - -/* - * Rounded parameter, as returned in Extended Sense information - */ -#define ROUNDED_PARAMETER 0x37 - - -/* ASC constants */ -#define ASC_INVALID_OPCODE 0x20 -#define ASC_INVALID_CDB_FIELD 0x24 -#define ASC_FAILURE_PREDICTION_THRESHOLD_EXCEEDED 0x5d - -/* ASCQ constants */ -#define ASCQ_INVALID_OPCODE 0 - -/* Error tests: */ -#define SCSI_INVALID_OPCODE(s, a, aq) \ - (((s) == KEY_ILLEGAL_REQUEST) && ((a) == ASC_INVALID_OPCODE) && \ - ((aq) == ASCQ_INVALID_OPCODE)) - -#define MODE_PAGE_UNSUPPORTED(s, a, aq) \ - (((s) == KEY_ILLEGAL_REQUEST) && ((a) == ASC_INVALID_CDB_FIELD)) - -int uscsi_mode_sense(int fd, int page_code, int page_control, caddr_t page_data, - int page_size, struct scsi_ms_header *header, void *rqbuf, int *rqblen); -int uscsi_mode_sense_10(int fd, int page_code, int page_control, - caddr_t page_data, int page_size, struct scsi_ms_header_g1 *header, - void *rqbuf, int *rqblen); -int uscsi_mode_select(int fd, int page_code, int options, caddr_t page_data, - int page_size, struct scsi_ms_header *header, void *rqbuf, int *rqblen); -int uscsi_mode_select_10(int fd, int page_code, int options, - caddr_t page_data, int page_size, struct scsi_ms_header_g1 *header, - void *rqbuf, int *rqblen); -int uscsi_log_sense(int fd, int page_code, int page_control, caddr_t page_data, - int page_size, void *rqbuf, int *rqblen); -int uscsi_request_sense(int fd, caddr_t buf, int buflen, void *rqbuf, - int *rqblen); -void scsi_translate_error(struct scsi_extended_sense *rq, uint_t *skeyp, - uint_t *ascp, uint_t *ascqp); -const char *scsi_asc_ascq_string(uint_t asc, uint_t ascq); - -#ifdef __cplusplus -} -#endif - -#endif /* _SCSI_UTIL_H */ diff --git a/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/sfx4500-disk.c b/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/sfx4500-disk.c index 256293b276..1accd24c4f 100644 --- a/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/sfx4500-disk.c +++ b/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/sfx4500-disk.c @@ -45,13 +45,12 @@ #include <fm/fmd_api.h> #include <fm/fmd_fmri.h> #include <sys/fm/protocol.h> +#include <sys/fm/io/disk.h> #include <fm/libtopo.h> #include "sfx4500-disk.h" -#include "fault_mgr.h" #include "hotplug_mgr.h" #include "schg_mgr.h" -#include "fm_disk_events.h" #include "topo_gather.h" #include "dm_platform.h" @@ -59,10 +58,8 @@ static enum sfx4500_init_state { INIT_STATE_NONE = 0, - PLUGIN_MGR_INITTED = 1, STATE_CHANGE_MGR_INITTED = 2, - HOTPLUG_MGR_INITTED = 4, - FAULT_MGR_INITTED = 8 + HOTPLUG_MGR_INITTED = 4 } g_init_state = INIT_STATE_NONE; typedef enum { @@ -77,7 +74,6 @@ typedef enum { */ log_class_t g_verbose = 0; cfgdata_t *config_data = NULL; -fmd_xprt_t *g_xprt_hdl = NULL; fmd_hdl_t *g_fm_hdl = NULL; static const fmd_prop_t fmd_props[]; @@ -85,12 +81,6 @@ static const fmd_prop_t fmd_props[]; static void diskmon_teardown_all(void) { - /* - * Cleanup the fault manager first -- it depends - * on the state change manager and may race with - * its cleanup if left around. - */ - cleanup_fault_manager(config_data); cleanup_hotplug_manager(); cleanup_state_change_manager(config_data); config_fini(); @@ -133,11 +123,6 @@ diskmon_init(void) else g_init_state |= STATE_CHANGE_MGR_INITTED; - if (init_fault_manager(config_data) != 0) - goto cleanup; - else - g_init_state |= FAULT_MGR_INITTED; - return (E_SUCCESS); cleanup: @@ -148,8 +133,6 @@ cleanup: * The cleanup order here does matter, due to dependencies between the * managers. */ - if (g_init_state & FAULT_MGR_INITTED) - cleanup_fault_manager(config_data); if (g_init_state & HOTPLUG_MGR_INITTED) cleanup_hotplug_manager(); if (g_init_state & STATE_CHANGE_MGR_INITTED) @@ -159,37 +142,8 @@ cleanup: return (E_ERROR); } -static boolean_t -dm_suspect_de_is_me(nvlist_t *suspectnvl) -{ - nvlist_t *de_nvl; - char *modname; - - if (nvlist_lookup_nvlist(suspectnvl, FM_SUSPECT_DE, &de_nvl) != 0 || - nvlist_lookup_string(de_nvl, FM_FMRI_FMD_NAME, &modname) != 0 || - strcmp(modname, THIS_FMD_MODULE_NAME) != 0) - return (B_FALSE); - else - return (B_TRUE); -} - -static disk_flt_src_e -nvl2fltsrc(fmd_hdl_t *hdl, nvlist_t *nvl) -{ - if (fmd_nvl_class_match(hdl, nvl, FAULT_DISK_OVERTEMP)) - return (DISK_FAULT_SOURCE_OVERTEMP); - - if (fmd_nvl_class_match(hdl, nvl, FAULT_DISK_STFAIL)) - return (DISK_FAULT_SOURCE_SELFTEST); - - if (fmd_nvl_class_match(hdl, nvl, FAULT_DISK_PREDFAIL)) - return (DISK_FAULT_SOURCE_INFO_EXCPT); - - return (DISK_FAULT_SOURCE_NONE); -} - static void -dm_fault_execute_actions(diskmon_t *diskp, disk_flt_src_e fsrc) +dm_fault_execute_actions(fmd_hdl_t *hdl, diskmon_t *diskp, nvlist_t *nvl) { const char *action_prop = NULL; const char *action_string; @@ -198,17 +152,13 @@ dm_fault_execute_actions(diskmon_t *diskp, disk_flt_src_e fsrc) * The predictive failure action is the activation of the fault * indicator. */ - switch (fsrc) { - case DISK_FAULT_SOURCE_OVERTEMP: - + if (fmd_nvl_class_match(hdl, nvl, + DISK_ERROR_CLASS "." FM_FAULT_DISK_OVERTEMP)) action_prop = DISK_PROP_OTEMPACTION; - break; - - case DISK_FAULT_SOURCE_SELFTEST: + if (fmd_nvl_class_match(hdl, nvl, + DISK_ERROR_CLASS "." FM_FAULT_DISK_TESTFAIL)) action_prop = DISK_PROP_STFAILACTION; - break; - } dm_fault_indicator_set(diskp, INDICATOR_ON); @@ -289,14 +239,12 @@ diskmon_agent_suspect(fmd_hdl_t *hdl, nvlist_t *nvl) continue; /* Execute the actions associated with this fault */ - dm_fault_execute_actions(diskp, nvl2fltsrc(hdl, fltnvl)); + dm_fault_execute_actions(hdl, diskp, fltnvl); /* - * If the fault wasn't generated by this module, send a - * state change event to the state change manager + * Send a state change event to the state change manager */ - if (!dm_suspect_de_is_me(nvl)) - dm_state_change(diskp, HPS_FAULTED); + dm_state_change(diskp, HPS_FAULTED); } if (!fmd_case_uuclosed(hdl, uuid)) { @@ -305,23 +253,16 @@ diskmon_agent_suspect(fmd_hdl_t *hdl, nvlist_t *nvl) } } +/*ARGSUSED*/ static void diskmon_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class) { diskmon_t *diskp; nvlist_t *fmri; - nvlist_t *fltnvl; - boolean_t ismyereport; - nvlist_t *det_nvl; - fmd_case_t *cs; - const char *fltclass; - disk_flt_src_e fsrc; if (g_verbose & MM_MAIN) nvlist_print(stderr, nvl); - ismyereport = fmd_nvl_class_match(hdl, nvl, EREPORT_SATA ".*"); - /* * Act on the fault suspect list or repaired list (embedded agent * action). @@ -342,7 +283,7 @@ diskmon_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class) * flag for the appropriate fault, then change the diskmon's state * to faulted. */ - if (fmd_nvl_class_match(hdl, nvl, FAULT_DISK ".*")) { + if (fmd_nvl_class_match(hdl, nvl, DISK_ERROR_CLASS ".*")) { if (nvlist_lookup_nvlist(nvl, FM_FAULT_RESOURCE, &fmri) != 0) @@ -351,14 +292,8 @@ diskmon_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class) if ((diskp = dm_fmri_to_diskmon(hdl, fmri)) == NULL) return; - fsrc = nvl2fltsrc(hdl, nvl); - /* Execute the actions associated with this fault */ - dm_fault_execute_actions(diskp, fsrc); - - dm_assert(pthread_mutex_lock(&diskp->disk_faults_mutex) == 0); - diskp->disk_faults |= fsrc; - dm_assert(pthread_mutex_unlock(&diskp->disk_faults_mutex) == 0); + dm_fault_execute_actions(hdl, diskp, nvl); /* * If the fault wasn't generated by this module, send a @@ -367,45 +302,6 @@ diskmon_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class) dm_state_change(diskp, HPS_FAULTED); return; } - - if (!ismyereport) { - log_msg(MM_MAIN, "Unknown class = %s\n", class); - return; - } - - if (nvlist_lookup_nvlist(nvl, FM_EREPORT_DETECTOR, &det_nvl) != 0) { - log_msg(MM_MAIN, "Cannot handle event %s: Couldn't get " - FM_EREPORT_DETECTOR "\n", class); - return; - } - - if ((diskp = dm_fmri_to_diskmon(hdl, det_nvl)) == NULL) - return; - - fltclass = - fmd_nvl_class_match(hdl, nvl, EREPORT_SATA_PREDFAIL) ? - FAULT_DISK_PREDFAIL : - fmd_nvl_class_match(hdl, nvl, EREPORT_SATA_OVERTEMP) ? - FAULT_DISK_OVERTEMP : - fmd_nvl_class_match(hdl, nvl, EREPORT_SATA_STFAIL) ? - FAULT_DISK_STFAIL : NULL; - - if (fltclass != NULL) { - - /* - * Create and solve a new case by adding the ereport - * and its corresponding fault to the case and - * solving it. It'll be closed when we get the - * list.suspect event later. - */ - cs = fmd_case_open(hdl, NULL); - fmd_case_add_ereport(hdl, cs, ep); - fltnvl = fmd_nvl_create_fault(hdl, fltclass, 100, - diskp->asru_fmri, diskp->fru_fmri, - diskp->disk_res_fmri); - fmd_case_add_suspect(hdl, cs, fltnvl); - fmd_case_solve(hdl, cs); - } } static const fmd_hdl_ops_t fmd_ops = { @@ -417,15 +313,7 @@ static const fmd_hdl_ops_t fmd_ops = { }; static const fmd_prop_t fmd_props[] = { - { GLOBAL_PROP_FAULT_POLL, FMD_TYPE_UINT32, "3600" }, - { GLOBAL_PROP_FAULT_INJ, FMD_TYPE_UINT32, "0" }, { GLOBAL_PROP_LOG_LEVEL, FMD_TYPE_UINT32, "0" }, - /* - * Default fault monitoring options are: - * (OPTION_SELFTEST_ERRS_ARE_FATAL | - * OPTION_OVERTEMP_ERRS_ARE_FATAL) == 0xC - */ - { GLOBAL_PROP_FAULT_OPTIONS, FMD_TYPE_UINT32, "0xC" }, { NULL, 0, NULL } }; @@ -470,10 +358,7 @@ _fmd_init(fmd_hdl_t *hdl) return; } - g_xprt_hdl = fmd_xprt_open(hdl, FMD_XPRT_RDONLY, NULL, NULL); if (diskmon_init() == E_ERROR) { - fmd_xprt_close(hdl, g_xprt_hdl); - g_xprt_hdl = NULL; config_fini(); fmd_hdl_unregister(hdl); return; @@ -495,11 +380,10 @@ _fmd_init(fmd_hdl_t *hdl) } } +/*ARGSUSED*/ void _fmd_fini(fmd_hdl_t *hdl) { diskmon_teardown_all(); - fmd_xprt_close(hdl, g_xprt_hdl); g_fm_hdl = NULL; - g_xprt_hdl = NULL; } diff --git a/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/sfx4500-disk.h b/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/sfx4500-disk.h index 4528980f59..1c2e9c335e 100644 --- a/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/sfx4500-disk.h +++ b/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/sfx4500-disk.h @@ -20,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -45,7 +45,6 @@ extern "C" { #define E_ERROR 1 extern cfgdata_t *config_data; -extern fmd_xprt_t *g_xprt_hdl; extern fmd_hdl_t *g_fm_hdl; diff --git a/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/topo_gather.c b/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/topo_gather.c index 9e9816f2a7..3f48eab691 100644 --- a/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/topo_gather.c +++ b/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/topo_gather.c @@ -114,11 +114,13 @@ dm_fmri_to_diskmon(fmd_hdl_t *hdl, nvlist_t *fmri) (void) nvlist_remove(dupfmri, FM_FMRI_HC_SERIAL_ID, DATA_TYPE_STRING); (void) nvlist_remove(dupfmri, FM_FMRI_HC_PART, DATA_TYPE_STRING); - thdl = fmd_hdl_topology(hdl, TOPO_VERSION); + thdl = fmd_hdl_topo_hold(hdl, TOPO_VERSION); if (topo_fmri_nvl2str(thdl, dupfmri, &buf, &err) != 0) { + fmd_hdl_topo_rele(hdl, thdl); nvlist_free(dupfmri); return (NULL); } + fmd_hdl_topo_rele(hdl, thdl); diskp = dm_fmristring_to_diskmon(buf); @@ -218,52 +220,6 @@ fmri2ptr(topo_hdl_t *thp, tnode_t *node, char **str, int *err) return (p); } -static void -transform_model_string(char *manuf, char *model, char **finalstring, - int *finalstringbuflen) -{ - int buflen = 0; - int i = 0; - int j = 0; - char *buf; - char *finalmodelstring; - - if (manuf != NULL) - buflen += strlen(manuf); - if (model != NULL) - buflen += strlen(model); - buflen += 2; - - buf = dmalloc(buflen); - finalmodelstring = dmalloc(buflen); - if (manuf != NULL && model != NULL) - (void) snprintf(buf, buflen, "%s-%s", manuf, model); - else - (void) snprintf(buf, buflen, "%s", - manuf == NULL ? model : manuf); - - while (buf[i] != 0) { - - if (isspace(buf[i])) { - finalmodelstring[j++] = '-'; - /* - * Advance past spaces, but not past the - * end of the string (since isspace('\0') - * returns FALSE). - */ - while (isspace(buf[i])) - i++; - } else - finalmodelstring[j++] = buf[i++]; - } - - finalmodelstring[j] = 0; - - dfree(buf, buflen); - *finalstring = finalmodelstring; - *finalstringbuflen = buflen; -} - typedef struct walk_diskmon { diskmon_t *target; char *pfmri; @@ -273,19 +229,14 @@ static int topo_add_disk(topo_hdl_t *thp, tnode_t *node, walk_diskmon_t *wdp) { diskmon_t *target_diskp = wdp->target; - nvlist_t *fmri = NULL; - nvlist_t *asru_fmri; - nvlist_t *fru_fmri; char *devpath = NULL; char *capacity = NULL; char *firmrev = NULL; char *serial = NULL; char *manuf = NULL; char *model = NULL; - char *buf; char *label; uint64_t ptr = 0; - int buflen; int err; dm_fru_t *frup; diskmon_t *diskp; @@ -314,26 +265,6 @@ topo_add_disk(topo_hdl_t *thp, tnode_t *node, walk_diskmon_t *wdp) } /* - * Update the diskmon's ASRU and FRU with our information (this - * information is cached in the diskmon so we don't have to do a - * time-consuming topo traversal when we get an ereport). - */ - if (topo_prop_get_fmri(node, TOPO_PGROUP_PROTOCOL, TOPO_PROP_ASRU, - &asru_fmri, &err) == 0) { - diskmon_add_asru(diskp, asru_fmri); - nvlist_free(asru_fmri); - } - if (topo_prop_get_fmri(node, TOPO_PGROUP_PROTOCOL, TOPO_PROP_FRU, - &fru_fmri, &err) == 0) { - diskmon_add_fru(diskp, fru_fmri); - nvlist_free(fru_fmri); - } - if (topo_node_resource(node, &fmri, &err) == 0) { - diskmon_add_disk_fmri(diskp, fmri); - nvlist_free(fmri); - } - - /* * Update the diskmon's location field with the disk's label */ if (diskp->location) @@ -393,60 +324,16 @@ topo_add_disk(topo_hdl_t *thp, tnode_t *node, walk_diskmon_t *wdp) frup = new_dmfru(manuf, model, firmrev, serial, capacity == NULL ? 0 : strtoull(capacity, 0, 0)); - /* - * Update the disk's resource FMRI with the - * SunService-required members: - * FM_FMRI_HC_SERIAL_ID, FM_FMRI_HC_PART, and - * FM_FMRI_HC_REVISION - */ - (void) nvlist_add_string(diskp->disk_res_fmri, - FM_FMRI_HC_SERIAL_ID, serial); - - transform_model_string(manuf, model, &buf, &buflen); - - (void) nvlist_add_string(diskp->disk_res_fmri, - FM_FMRI_HC_PART, buf); - - /* - * Add the serial number to the ASRU so that when the resource - * is marked faulty in the fmd resource cache, the hc scheme - * plugin can detect when the disk is no longer installed (and so, - * can clear the faulty state automatically across fmd restarts). - * - * The serial number is only updated when a disk comes online - * because that's when the disk node exists in the topo tree. - * It's ok to keep a stale value in the ASRU when the disk is removed - * because it's only used as part of fault creation when the disk - * is configured (online), at which point it will be updated with - * the (current) serial number of the disk inserted. - */ - (void) nvlist_add_string(diskp->asru_fmri, - FM_FMRI_HC_SERIAL_ID, serial); - - dfree(buf, buflen); - - (void) nvlist_add_string(diskp->disk_res_fmri, - FM_FMRI_HC_REVISION, firmrev); - - if (model) { + if (model) topo_hdl_strfree(thp, model); - } - - if (manuf) { + if (manuf) topo_hdl_strfree(thp, manuf); - } - - if (serial) { + if (serial) topo_hdl_strfree(thp, serial); - } - - if (firmrev) { + if (firmrev) topo_hdl_strfree(thp, firmrev); - } - - if (capacity) { + if (capacity) topo_hdl_strfree(thp, capacity); - } /* Add the fru information to the diskmon: */ dm_assert(pthread_mutex_lock(&diskp->fru_mutex) == 0); @@ -855,24 +742,16 @@ update_configuration_from_topo(fmd_hdl_t *hdl, diskmon_t *diskp) topo_hdl_t *thp; topo_walk_t *twp; walk_diskmon_t wd; - char *uuid; - if ((thp = topo_open(TOPO_VERSION, NULL, &err)) == NULL) { + if ((thp = fmd_hdl_topo_hold(hdl, TOPO_VERSION)) == NULL) { return (TOPO_OPEN_ERROR); } - if ((uuid = topo_snap_hold(thp, NULL, &err)) == NULL) { - topo_close(thp); - return (TOPO_SNAP_ERROR); - } - - topo_hdl_strfree(thp, uuid); - wd.target = diskp; wd.pfmri = NULL; if ((twp = topo_walk_init(thp, FM_FMRI_SCHEME_HC, gather_topo_cfg, &wd, &err)) == NULL) { - topo_close(thp); /* topo_close() will release the snapshot */ + fmd_hdl_topo_rele(hdl, thp); return (err ? TOPO_WALK_INIT_ERROR : TOPO_SUCCESS); } @@ -882,12 +761,12 @@ update_configuration_from_topo(fmd_hdl_t *hdl, diskmon_t *diskp) if (wd.pfmri != NULL) dstrfree(wd.pfmri); - topo_close(thp); + fmd_hdl_topo_rele(hdl, thp); return (TOPO_WALK_ERROR); } topo_walk_fini(twp); - topo_close(thp); + fmd_hdl_topo_rele(hdl, thp); if (wd.pfmri != NULL) dstrfree(wd.pfmri); diff --git a/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/util.c b/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/util.c index c4cbd3fcb2..1046574b1d 100644 --- a/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/util.c +++ b/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/util.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -46,129 +46,6 @@ extern log_class_t g_verbose; static pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER; -static void log_msg_nl(log_class_t cl, const char *fmt, ...); - -/* - * Search a string list for a particular value. - * Return the string associated with that value. - */ -char * -find_string(slist_t *slist, int match_value) -{ - for (; slist->str != NULL; slist++) { - if (slist->value == match_value) { - return (slist->str); - } - } - - return ((char *)NULL); -} - -/* - * Produce a pretty dump of memory - * <X> <Byte_i-1> <Byte_i> <Byte_i+1> ... [TEXT REPRESENTATION] - */ -static void -dump(log_class_t cl, char *label, char *start, unsigned length) -{ - int byte_count; - int i; -#define LINEBUFLEN 128 - char linebuf[LINEBUFLEN]; - char *linep; - int bufleft, len; - - if (label != NULL) - log_msg_nl(cl, "%s\n", label); - - linep = linebuf; - bufleft = LINEBUFLEN; - - for (byte_count = 0; byte_count < length; byte_count += i) { - - (void) snprintf(linep, bufleft, "%s0x%08" PRIX64 " ", "", - (uint64_t)byte_count); - len = strlen(linep); - bufleft -= len; - linep += len; - - /* - * Inner loop processes 16 bytes at a time, or less - * if we have less than 16 bytes to go - */ - for (i = 0; (i < 16) && ((byte_count + i) < length); i++) { - (void) snprintf(linep, bufleft, "%02X", (unsigned int) - (unsigned char) start[byte_count + i]); - - len = strlen(linep); - bufleft -= len; - linep += len; - - if (bufleft >= 2) { - if (i == 7) - *linep = '-'; - else - *linep = ' '; - - --bufleft; - ++linep; - } - } - - /* - * If i is less than 16, then we had less than 16 bytes - * written to the output. We need to fixup the alignment - * to allow the "text" output to be aligned - */ - if (i < 16) { - int numspaces = (16 - i) * 3; - while (numspaces-- > 0) { - if (bufleft >= 2) { - *linep = ' '; - --bufleft; - linep++; - } - } - } - - if (bufleft >= 2) { - *linep = ' '; - --bufleft; - ++linep; - } - - for (i = 0; (i < 16) && ((byte_count + i) < length); i++) { - int subscript = byte_count + i; - char ch = (isprint(start[subscript]) ? - start[subscript] : '.'); - - if (bufleft >= 2) { - *linep = ch; - --bufleft; - ++linep; - } - } - - linebuf[LINEBUFLEN - bufleft] = 0; - - log_msg_nl(cl, "%s\n", linebuf); - - linep = linebuf; - bufleft = LINEBUFLEN; - } -} - -void -log_dump(log_class_t cl, char *label, char *start, unsigned length) -{ - if ((g_verbose & cl) != cl) - return; - - dm_assert(pthread_mutex_lock(&log_mutex) == 0); - dump(cl, label, start, length); - dm_assert(pthread_mutex_unlock(&log_mutex) == 0); -} - static void verror(const char *fmt, va_list ap) { @@ -224,19 +101,6 @@ vcont(log_class_t cl, const char *fmt, va_list ap) dm_assert(pthread_mutex_unlock(&log_mutex) == 0); } -static void -log_msg_nl(log_class_t cl, const char *fmt, ...) -{ - va_list ap; - - if ((g_verbose & cl) != cl) - return; - - va_start(ap, fmt); - fmd_hdl_vdebug(g_fm_hdl, fmt, ap); - va_end(ap); -} - void log_msg(log_class_t cl, const char *fmt, ...) { diff --git a/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/util.h b/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/util.h index efb9d91423..d562aa0f16 100644 --- a/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/util.h +++ b/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/util.h @@ -57,15 +57,6 @@ extern int _dm_assert(const char *assertion, const char *file, int line, _dm_assert("EX", __FILE__, __LINE__, NULL)) #endif /* __STDC__ */ - -/* - * List of strings with arbitrary matching values - */ -typedef struct slist { - char *str; - int value; -} slist_t; - /* * The following structures comprise the implementation of the * queue structure that's used to construct the list of state @@ -93,11 +84,8 @@ typedef struct q_head { typedef enum log_class { MM_CONF = 0x0001, - MM_FAULTMGR = 0x0002, MM_HPMGR = 0x0004, MM_SCHGMGR = 0x0008, - MM_FLTANALYZE = 0x0010, - MM_SCSI = 0x0020, MM_MAIN = 0x0040, MM_TOPO = 0x0100, MM_ERR = 0x0200, @@ -112,10 +100,6 @@ extern qu_t *new_queue(boolean_t block_on_empty, void *(*nodealloc)(size_t), void (*nodefree)(void *, size_t), void (*deallocator)(void *)); extern void queue_free(qu_t **qp); -extern char *find_string(slist_t *slist, int match_value); - -extern void log_dump(log_class_t cl, char *label, char *start, unsigned length); - extern void *dmalloc(size_t sz); extern void *dzmalloc(size_t sz); extern char *dstrdup(const char *s); diff --git a/usr/src/cmd/fm/modules/sun4v/cpumem-retire/cma_page.c b/usr/src/cmd/fm/modules/sun4v/cpumem-retire/cma_page.c index ba2274897d..286ec18740 100644 --- a/usr/src/cmd/fm/modules/sun4v/cpumem-retire/cma_page.c +++ b/usr/src/cmd/fm/modules/sun4v/cpumem-retire/cma_page.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -127,14 +127,17 @@ cma_page_retire(fmd_hdl_t *hdl, nvlist_t *nvl, nvlist_t *asru, const char *uuid) strncmp(unumstr, "hc:/", 4) == 0) { int err; nvlist_t *unumfmri; - struct topo_hdl *thp = fmd_hdl_topology(hdl, TOPO_VERSION); + struct topo_hdl *thp = fmd_hdl_topo_hold(hdl, TOPO_VERSION); if (topo_fmri_str2nvl(thp, unumstr, &unumfmri, &err) != 0) { fmd_hdl_debug(hdl, "page retire str2nvl failed: %s\n", topo_strerror(err)); + fmd_hdl_topo_rele(hdl, thp); return (CMA_RA_FAILURE); } + fmd_hdl_topo_rele(hdl, thp); + if (nvlist_dup(asru, &asrucp, 0) != 0) { fmd_hdl_debug(hdl, "page retire nvlist dup failed\n"); nvlist_free(unumfmri); diff --git a/usr/src/cmd/fm/schemes/cpu/cpu.c b/usr/src/cmd/fm/schemes/cpu/cpu.c index 6f767a5d3e..0ec721c52a 100644 --- a/usr/src/cmd/fm/schemes/cpu/cpu.c +++ b/usr/src/cmd/fm/schemes/cpu/cpu.c @@ -172,6 +172,7 @@ fmd_fmri_expand(nvlist_t *nvl) uint64_t serialid; char *serstr, serbuf[21]; /* sizeof (UINT64_MAX) + '\0' */ int rc, err; + topo_hdl_t *thp; if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) != 0) @@ -180,7 +181,11 @@ fmd_fmri_expand(nvlist_t *nvl) /* * If the cpu-scheme topology exports this method expand(), invoke it. */ - rc = topo_fmri_expand(fmd_fmri_topology(TOPO_VERSION), nvl, &err); + if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL) + return (fmd_fmri_set_errno(EINVAL)); + + rc = topo_fmri_expand(thp, nvl, &err); + fmd_fmri_topo_rele(thp); if (err != ETOPO_METHOD_NOTSUP) return (rc); @@ -225,6 +230,7 @@ fmd_fmri_present(nvlist_t *nvl) uint32_t cpuid; uint64_t nvlserid, curserid; char *nvlserstr, curserbuf[21]; /* sizeof (UINT64_MAX) + '\0' */ + topo_hdl_t *thp; if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) != 0) @@ -233,7 +239,10 @@ fmd_fmri_present(nvlist_t *nvl) /* * If the cpu-scheme topology exports this method present(), invoke it. */ - rc = topo_fmri_present(fmd_fmri_topology(TOPO_VERSION), nvl, &err); + if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL) + return (fmd_fmri_set_errno(EINVAL)); + rc = topo_fmri_present(thp, nvl, &err); + fmd_fmri_topo_rele(thp); if (err != ETOPO_METHOD_NOTSUP) return (rc); @@ -272,6 +281,7 @@ fmd_fmri_unusable(nvlist_t *nvl) int rc, err; uint8_t version; uint32_t cpuid; + topo_hdl_t *thp; if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || version > FM_CPU_SCHEME_VERSION || @@ -281,7 +291,10 @@ fmd_fmri_unusable(nvlist_t *nvl) /* * If the cpu-scheme topology exports this method unusable(), invoke it. */ - rc = topo_fmri_unusable(fmd_fmri_topology(TOPO_VERSION), nvl, &err); + if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL) + return (fmd_fmri_set_errno(EINVAL)); + rc = topo_fmri_unusable(thp, nvl, &err); + fmd_fmri_topo_rele(thp); if (err != ETOPO_METHOD_NOTSUP) return (rc); diff --git a/usr/src/cmd/fm/schemes/dev/amd64/Makefile b/usr/src/cmd/fm/schemes/dev/amd64/Makefile index b257694c9b..d7eed9a250 100644 --- a/usr/src/cmd/fm/schemes/dev/amd64/Makefile +++ b/usr/src/cmd/fm/schemes/dev/amd64/Makefile @@ -20,7 +20,7 @@ # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -30,4 +30,8 @@ include ../Makefile.com include $(SRC)/Makefile.master.64 include ../../Makefile.targ +LDLIBS += -ltopo +LDFLAGS += -L$(ROOT)/usr/lib/fm/amd64 -R/usr/lib/fm/amd64 +LINTFLAGS += -L$(ROOT)/usr/lib/fm/amd64 + install: all $(ROOTPROG64) diff --git a/usr/src/cmd/fm/schemes/dev/i386/Makefile b/usr/src/cmd/fm/schemes/dev/i386/Makefile index 67dd8f8ffd..cc9200a467 100644 --- a/usr/src/cmd/fm/schemes/dev/i386/Makefile +++ b/usr/src/cmd/fm/schemes/dev/i386/Makefile @@ -20,7 +20,7 @@ # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -29,4 +29,8 @@ include ../Makefile.com include ../../Makefile.targ +LDLIBS += -ltopo +LDFLAGS += -L$(ROOT)/usr/lib/fm -R/usr/lib/fm +LINTFLAGS += -L$(ROOT)/usr/lib/fm + install: all $(ROOTPROG) diff --git a/usr/src/cmd/fm/schemes/dev/scheme.c b/usr/src/cmd/fm/schemes/dev/scheme.c index 309d09471d..be00fa5931 100644 --- a/usr/src/cmd/fm/schemes/dev/scheme.c +++ b/usr/src/cmd/fm/schemes/dev/scheme.c @@ -20,226 +20,94 @@ */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include <fm/fmd_fmri.h> +#include <fm/libtopo.h> +#include <fm/topo_mod.h> #include <libdevinfo.h> #include <alloca.h> #include <string.h> -/* - * buf_append -- Append str to buf (if it's non-NULL). Place prepend - * in buf in front of str and append behind it (if they're non-NULL). - * Continue to update size even if we run out of space to actually - * stuff characters in the buffer. - */ -static void -buf_append(ssize_t *sz, char *buf, size_t buflen, char *str, - char *prepend, char *append) +int +fmd_fmri_init(void) { - ssize_t left; - - if (str == NULL) - return; - - if (buflen == 0 || (left = buflen - *sz) < 0) - left = 0; - - if (buf != NULL && left != 0) - buf += *sz; - - if (prepend == NULL && append == NULL) - *sz += snprintf(buf, left, "%s", str); - else if (append == NULL) - *sz += snprintf(buf, left, "%s%s", prepend, str); - else if (prepend == NULL) - *sz += snprintf(buf, left, "%s%s", str, append); - else - *sz += snprintf(buf, left, "%s%s%s", prepend, str, append); + return (0); } +void +fmd_fmri_fini(void) +{ +} ssize_t fmd_fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen) { - nvlist_t *anvl = NULL; - uint8_t version; - ssize_t size = 0; - char *devid = NULL; - char *devpath = NULL; - char *achas = NULL; - char *adom = NULL; - char *aprod = NULL; - char *asrvr = NULL; - char *ahost = NULL; - int more_auth = 0; int err; + ssize_t len; + topo_hdl_t *thp; + char *str; - if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || - version > FM_DEV_SCHEME_VERSION) + if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL) return (fmd_fmri_set_errno(EINVAL)); - /* Get authority, if present */ - err = nvlist_lookup_nvlist(nvl, FM_FMRI_AUTHORITY, &anvl); - if (err != 0 && err != ENOENT) - return (fmd_fmri_set_errno(err)); - - /* Get devid, if present */ - err = nvlist_lookup_string(nvl, FM_FMRI_DEV_ID, &devid); - if (err != 0 && err != ENOENT) - return (fmd_fmri_set_errno(err)); - - /* There must be a device path present */ - err = nvlist_lookup_string(nvl, FM_FMRI_DEV_PATH, &devpath); - if (err != 0 || devpath == NULL) + if (topo_fmri_nvl2str(thp, nvl, &str, &err) != 0) { + fmd_fmri_topo_rele(thp); return (fmd_fmri_set_errno(EINVAL)); - - if (anvl != NULL) { - (void) nvlist_lookup_string(anvl, - FM_FMRI_AUTH_PRODUCT, &aprod); - (void) nvlist_lookup_string(anvl, - FM_FMRI_AUTH_CHASSIS, &achas); - (void) nvlist_lookup_string(anvl, - FM_FMRI_AUTH_DOMAIN, &adom); - (void) nvlist_lookup_string(anvl, - FM_FMRI_AUTH_SERVER, &asrvr); - (void) nvlist_lookup_string(anvl, - FM_FMRI_AUTH_HOST, &ahost); - if (aprod != NULL) - more_auth++; - if (achas != NULL) - more_auth++; - if (adom != NULL) - more_auth++; - if (asrvr != NULL) - more_auth++; - if (ahost != NULL) - more_auth++; } - /* dev:// */ - buf_append(&size, buf, buflen, FM_FMRI_SCHEME_DEV, NULL, "://"); - - /* authority, if any */ - if (aprod != NULL) - buf_append(&size, buf, buflen, aprod, FM_FMRI_AUTH_PRODUCT "=", - --more_auth > 0 ? "," : NULL); - if (achas != NULL) - buf_append(&size, buf, buflen, achas, FM_FMRI_AUTH_CHASSIS "=", - --more_auth > 0 ? "," : NULL); - if (adom != NULL) - buf_append(&size, buf, buflen, adom, FM_FMRI_AUTH_DOMAIN "=", - --more_auth > 0 ? "," : NULL); - if (asrvr != NULL) - buf_append(&size, buf, buflen, asrvr, FM_FMRI_AUTH_SERVER "=", - --more_auth > 0 ? "," : NULL); - if (ahost != NULL) - buf_append(&size, buf, buflen, ahost, FM_FMRI_AUTH_HOST "=", - NULL); - - /* device-id part */ - buf_append(&size, buf, buflen, devid, "/:" FM_FMRI_DEV_ID "=", NULL); - - /* device-path part */ - buf_append(&size, buf, buflen, devpath, "/", NULL); - - return (size); -} + if (buf != NULL) + len = snprintf(buf, buflen, "%s", str); + else + len = strlen(str); -/* - * callback routine for di_walk_minor() - */ -struct walkinfo { - int matched; - const char *path; - int len; -}; - -static int -dev_match(di_node_t node, void *arg) -{ - struct walkinfo *wip = (struct walkinfo *)arg; - char *path = di_devfs_path(node); - - if (path != NULL && strncmp(path, wip->path, wip->len) == 0) { - /* - * found the match we were looking for, set matched - * flag and terminate the walk. - */ - wip->matched = 1; - di_devfs_path_free(path); - return (DI_WALK_TERMINATE); - } + topo_hdl_strfree(thp, str); + fmd_fmri_topo_rele(thp); - if (path != NULL) - di_devfs_path_free(path); - return (DI_WALK_CONTINUE); + return (len); } -/* - * For now we only check for the presence of the device in the device - * tree. This is somewhat unsophisticated, because a device may have - * been inserted into the same slot as the previous ASRU and we don't - * know how to tell them apart yet. - */ int fmd_fmri_present(nvlist_t *nvl) { - di_node_t parent; - uint8_t version; - char *devpath = NULL; - char *parentpath; - char *cp; - struct walkinfo walkinfo; + int err, present; + topo_hdl_t *thp; - if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || - version > FM_DEV_SCHEME_VERSION || - nvlist_lookup_string(nvl, FM_FMRI_DEV_PATH, &devpath) != 0) + if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL) return (fmd_fmri_set_errno(EINVAL)); + err = 0; + present = topo_fmri_present(thp, nvl, &err); + fmd_fmri_topo_rele(thp); - if (devpath == NULL || (walkinfo.len = strlen(devpath)) == 0) - return (fmd_fmri_set_errno(EINVAL)); - - /* strip off last component of path */ - parentpath = alloca(walkinfo.len + 1); - (void) strcpy(parentpath, devpath); - if ((cp = strrchr(parentpath, '/')) == NULL) - parentpath = "/"; + if (err != 0) + return (0); else - *cp = '\0'; - - /* if the result is an empty path, start walk at "/" */ - if (*parentpath == '\0') - parentpath = "/"; - - if ((parent = di_init(parentpath, DINFOSUBTREE)) == DI_NODE_NIL) - return (errno == ENXIO ? 0 : -1); - - walkinfo.matched = 0; - walkinfo.path = devpath; - (void) di_walk_node(parent, - DI_WALK_SIBFIRST, (void *)&walkinfo, dev_match); - di_fini(parent); - - return (walkinfo.matched); + return (present); } -/* - * We presently don't have a good indication of the usability of an - * ASRU in the dev scheme, so we'll assume its usable. - */ int fmd_fmri_unusable(nvlist_t *nvl) { uint8_t version; + int err, unusable; + topo_hdl_t *thp; if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || version > FM_DEV_SCHEME_VERSION) return (fmd_fmri_set_errno(EINVAL)); - return (0); + if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL) + return (fmd_fmri_set_errno(EINVAL)); + err = 0; + unusable = topo_fmri_unusable(thp, nvl, &err); + fmd_fmri_topo_rele(thp); + + if (err != 0) + return (0); + else + return (unusable); } diff --git a/usr/src/cmd/fm/schemes/dev/sparc/Makefile b/usr/src/cmd/fm/schemes/dev/sparc/Makefile index 67dd8f8ffd..cc9200a467 100644 --- a/usr/src/cmd/fm/schemes/dev/sparc/Makefile +++ b/usr/src/cmd/fm/schemes/dev/sparc/Makefile @@ -20,7 +20,7 @@ # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -29,4 +29,8 @@ include ../Makefile.com include ../../Makefile.targ +LDLIBS += -ltopo +LDFLAGS += -L$(ROOT)/usr/lib/fm -R/usr/lib/fm +LINTFLAGS += -L$(ROOT)/usr/lib/fm + install: all $(ROOTPROG) diff --git a/usr/src/cmd/fm/schemes/dev/sparcv9/Makefile b/usr/src/cmd/fm/schemes/dev/sparcv9/Makefile index b257694c9b..145b89b8d3 100644 --- a/usr/src/cmd/fm/schemes/dev/sparcv9/Makefile +++ b/usr/src/cmd/fm/schemes/dev/sparcv9/Makefile @@ -20,7 +20,7 @@ # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -30,4 +30,8 @@ include ../Makefile.com include $(SRC)/Makefile.master.64 include ../../Makefile.targ +LDLIBS += -ltopo +LDFLAGS += -L$(ROOT)/usr/lib/fm/sparcv9 -R/usr/lib/fm/sparcv9 +LINTFLAGS += -L$(ROOT)/usr/lib/fm/sparcv9 + install: all $(ROOTPROG64) diff --git a/usr/src/cmd/fm/schemes/hc/scheme.c b/usr/src/cmd/fm/schemes/hc/scheme.c index 11b840c2b8..0c0e6ac692 100644 --- a/usr/src/cmd/fm/schemes/hc/scheme.c +++ b/usr/src/cmd/fm/schemes/hc/scheme.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -54,9 +54,12 @@ fmd_fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen) version > FM_HC_SCHEME_VERSION) return (fmd_fmri_set_errno(EINVAL)); - thp = fmd_fmri_topology(TOPO_VERSION); - if (topo_fmri_nvl2str(thp, nvl, &str, &err) != 0) + if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL) return (fmd_fmri_set_errno(EINVAL)); + if (topo_fmri_nvl2str(thp, nvl, &str, &err) != 0) { + fmd_fmri_topo_rele(thp); + return (fmd_fmri_set_errno(EINVAL)); + } if (buf != NULL) len = snprintf(buf, buflen, "%s", str); @@ -64,117 +67,11 @@ fmd_fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen) len = strlen(str); topo_hdl_strfree(thp, str); + fmd_fmri_topo_rele(thp); return (len); } -typedef struct hc_walk_arg { - void *p; - int *resultp; -} hc_walk_arg_t; - -static int -hc_topo_walk(topo_hdl_t *thp, topo_walk_cb_t fn, void *arg, int *resultp) -{ - int err, rv; - topo_walk_t *twp; - hc_walk_arg_t hcarg; - - hcarg.p = arg; - hcarg.resultp = resultp; - - if ((twp = topo_walk_init(thp, FM_FMRI_SCHEME_HC, fn, - &hcarg, &err)) == NULL) - return (-1); - - rv = (topo_walk_step(twp, TOPO_WALK_CHILD) == TOPO_WALK_ERR) - ? -1 : 0; - - topo_walk_fini(twp); - return (rv); -} - -/*ARGSUSED*/ -static int -hc_topo_present(topo_hdl_t *thp, tnode_t *node, void *arg) -{ - int cmp, err; - nvlist_t *out, *asru; - hc_walk_arg_t *hcargp = (hc_walk_arg_t *)arg; - - /* - * Only care about sata-ports and disks - */ - if (strcmp(topo_node_name(node), SATA_PORT) != 0 && - strcmp(topo_node_name(node), DISK) != 0) - return (TOPO_WALK_NEXT); - - if (topo_node_asru(node, &asru, NULL, &err) != 0 || - asru == NULL) { - return (TOPO_WALK_NEXT); - } - - /* - * Check if the ASRU of this node matches the ASRU passed in - */ - cmp = topo_fmri_compare(thp, asru, (nvlist_t *)hcargp->p, &err); - - nvlist_free(asru); - - if (cmp <= 0) - return (TOPO_WALK_NEXT); - - /* - * Yes, so try to execute the topo-present method. - */ - if (topo_method_invoke(node, TOPO_METH_PRESENT, - TOPO_METH_PRESENT_VERSION, (nvlist_t *)hcargp->p, &out, &err) - == 0) { - (void) nvlist_lookup_uint32(out, TOPO_METH_PRESENT_RET, - (uint32_t *)hcargp->resultp); - nvlist_free(out); - return (TOPO_WALK_TERMINATE); - } else { - return (TOPO_WALK_ERR); - } - -} - -/* - * The SATA disk topology permits an ASRU to be declared as a pseudo-hc - * FMRI, something like this: - * - * hc:///motherboard=0/hostbridge=0/pcibus=0/pcidev=1/pcifn=0/sata-port=1 - * ASRU: hc:///component=sata0/1 - * FRU: hc:///component=MB - * Label: sata0/1 - * - * This is a hack to support cfgadm attachment point ASRUs without defining - * a new scheme. As a result, we need to support an is_present function for - * something * that begins with hc:///component=. To do this, we compare the - * nvlist provided by the caller against the ASRU property for all possible - * topology nodes. - * - * The SATA phase 2 project will address the lack of a proper FMRI scheme - * for cfgadm attachment points. This code may be removed when the SATA - * phase 2 FMA work is completed. - */ -static int -hc_sata_hack(nvlist_t *nvl) -{ - int ispresent = 1; - topo_hdl_t *thp; - - /* - * If there's an error during the topology update, punt by - * indicating presence. - */ - thp = fmd_fmri_topology(TOPO_VERSION); - (void) hc_topo_walk(thp, hc_topo_present, nvl, &ispresent); - - return (ispresent); -} - int fmd_fmri_present(nvlist_t *nvl) { @@ -189,11 +86,10 @@ fmd_fmri_present(nvlist_t *nvl) if (err != 0) return (0); - if (strcmp(nm, "component") == 0) - return (hc_sata_hack(nvl)); - - thp = fmd_fmri_topology(TOPO_VERSION); + if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL) + return (fmd_fmri_set_errno(EINVAL)); present = topo_fmri_present(thp, nvl, &err); + fmd_fmri_topo_rele(thp); if (err != 0) return (present); diff --git a/usr/src/cmd/fm/schemes/mem/mem_unum.c b/usr/src/cmd/fm/schemes/mem/mem_unum.c index 793761d7fe..5e8c10b1c3 100644 --- a/usr/src/cmd/fm/schemes/mem/mem_unum.c +++ b/usr/src/cmd/fm/schemes/mem/mem_unum.c @@ -406,10 +406,15 @@ mem_unum_rewrite(nvlist_t *nvl, nvlist_t **rnvl) !ISHCUNUM(unumstr)) return (0); - thp = fmd_fmri_topology(TOPO_VERSION); + if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL) + return (EINVAL); - if (topo_fmri_str2nvl(thp, unumstr, &unum, &err) != 0) + if (topo_fmri_str2nvl(thp, unumstr, &unum, &err) != 0) { + fmd_fmri_topo_rele(thp); return (EINVAL); + } + + fmd_fmri_topo_rele(thp); if ((err = nvlist_dup(nvl, rnvl, 0)) != 0) { nvlist_free(unum); diff --git a/usr/src/cmd/mdb/common/modules/libtopo/libtopo.c b/usr/src/cmd/mdb/common/modules/libtopo/libtopo.c index 1634e0c95d..f5e288fbd7 100644 --- a/usr/src/cmd/mdb/common/modules/libtopo/libtopo.c +++ b/usr/src/cmd/mdb/common/modules/libtopo/libtopo.c @@ -31,6 +31,7 @@ #include <topo_mod.h> #include <topo_tree.h> #include <topo_module.h> +#include <stddef.h> /* @@ -131,9 +132,9 @@ topo_handle(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) mdb_printf("%-12s %-36s %-30s\n", "th_trees", "", "Scheme-specific topo tree list"); mdb_printf(" %-12s 0x%-34p %-30s\n", "l_prev", th.th_trees.l_prev, - ""); + ""); mdb_printf(" %-12s 0x%-34p %-30s\n", "l_next", th.th_trees.l_next, - ""); + ""); mdb_printf("%-12s 0x%-34p %-30s\n", "th_alloc", th.th_alloc, "Allocators"); mdb_printf("%-12s %-36d %-30s\n", "tm_ernno", th.th_errno, "errno"); @@ -177,57 +178,57 @@ topo_module(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) */ if (DCMD_HDRSPEC(flags)) { mdb_printf("%<u>%-12s %-36s %-30s%</u>\n", - "FIELD", "VALUE", "DESCR"); + "FIELD", "VALUE", "DESCR"); } mdb_printf("%-12s 0x%-34p %-30s\n", "tm_lock", tm.tm_lock, - "Lock for tm_cv/owner/flags/refs"); + "Lock for tm_cv/owner/flags/refs"); mdb_printf("%-12s 0x%-34p %-30s\n", "tm_cv", tm.tm_cv, - "Module condition variable"); + "Module condition variable"); mdb_printf("%-12s %-36s %-30s\n", "tm_busy", tm.tm_busy, - "Busy indicator"); + "Busy indicator"); mdb_printf("%-12s 0x%-34p %-30s\n", "tm_next", tm.tm_next, - "Next module in hash chain"); + "Next module in hash chain"); mdb_printf("%-12s 0x%-34p %-30s\n", "tm_hdl", tm.tm_hdl, - "Topo handle for this module"); + "Topo handle for this module"); mdb_printf("%-12s 0x%-34p %-30s\n", "tm_alloc", tm.tm_alloc, - "Allocators"); + "Allocators"); mdb_printf("%-12s %-36s %-30s\n", "tm_name", name, - "Basename of module"); + "Basename of module"); mdb_printf("%-12s %-36s %-30s\n", "tm_path", path, - "Full pathname of module"); + "Full pathname of module"); mdb_printf("%-12s %-36s %-30s\n", "tm_rootdir", root, - "Relative root directory of module"); + "Relative root directory of module"); mdb_printf("%-12s %-36u %-30s\n", "tm_refs", tm.tm_refs, - "Module reference count"); + "Module reference count"); mdb_printf("%-12s %-36u %-30s\n", "tm_flags", tm.tm_flags, - "Module flags"); + "Module flags"); if (TOPO_MOD_INIT & tm.tm_flags) { mdb_printf("%-12s %-36s %-30s\n", "", "TOPO_MOD_INIT", - "Module init completed"); + "Module init completed"); } if (TOPO_MOD_FINI & tm.tm_flags) { mdb_printf("%-12s %-36s %-30s\n", "", "TOPO_MOD_FINI", - "Module fini completed"); + "Module fini completed"); } if (TOPO_MOD_REG & tm.tm_flags) { mdb_printf("%-12s %-36s %-30s\n", "", "TOPO_MOD_REG", - "Module registered"); + "Module registered"); } if (TOPO_MOD_UNREG & tm.tm_flags) { mdb_printf("%-12s %-36s %-30s\n", "", "TOPO_MOD_UNREG", - "Module unregistered"); + "Module unregistered"); } mdb_printf("%-12s %-36u %-30s\n", "tm_debug", tm.tm_debug, - "Debug printf mask"); + "Debug printf mask"); mdb_printf("%-12s 0x%-34p %-30s\n", "tm_data", tm.tm_data, - "Private rtld/builtin data"); + "Private rtld/builtin data"); mdb_printf("%-12s 0x%-34p %-30s\n", "tm_mops", tm.tm_mops, - "Module class ops vector"); + "Module class ops vector"); mdb_printf("%-12s 0x%-34p %-30s\n", "tm_info", tm.tm_info, - "Module info registered with handle"); + "Module info registered with handle"); mdb_printf("%-12s %-36d %-30s\n", "tm_ernno", tm.tm_errno, - "Module errno"); + "Module errno"); return (DCMD_OK); } @@ -264,14 +265,15 @@ topo_node(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) "FIELD", "VALUE", "DESCR"); } - mdb_printf("%-12s 0x%-34p %-30s\n", "tn_lock", tn.tn_lock, - "Mutex lock protecting node members"); + mdb_printf("%-12s 0x%-34p %-30s\n", "tn_lock", + addr + offsetof(tnode_t, tn_lock), + "Lock protecting node members"); mdb_printf("%-12s %-36s %-30s\n", "tn_name", name, - "Node name"); + "Node name"); mdb_printf("%-12s %-36d %-30s\n", "tn_instance", tn.tn_instance, - "Node instance"); + "Node instance"); mdb_printf("%-12s %-36d %-30s\n", "tn_state", tn.tn_state, - "Node state"); + "Node state"); if (TOPO_NODE_INIT & tn.tn_state) { mdb_printf("%-12s %-36s %-30s\n", "", "TOPO_NODE_INIT", ""); } @@ -285,29 +287,27 @@ topo_node(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) mdb_printf("%-12s %-36s %-30s\n", "", "TOPO_NODE_LINKED", ""); } mdb_printf("%-12s %-36d %-30s\n", "tn_fflags", tn.tn_fflags, - "FMRI flags"); + "FMRI flags"); mdb_printf("%-12s 0x%-34p %-30s\n", "tn_parent", tn.tn_parent, - "Node parent"); + "Node parent"); mdb_printf("%-12s 0x%-34p %-30s\n", "tn_phash", tn.tn_phash, - "Parent hash bucket"); + "Parent hash bucket"); mdb_printf("%-12s 0x%-34p %-30s\n", "tn_hdl", tn.tn_hdl, - "Topo handle"); + "Topo handle"); mdb_printf("%-12s 0x%-34p %-30s\n", "tn_enum", tn.tn_enum, - "Enumerator module"); + "Enumerator module"); mdb_printf("%-12s %-36s %-30s\n", "tn_children", "", - "Hash table of child nodes"); - mdb_printf(" %-12s 0x%-34p %-30s\n", "l_prev", tn.tn_children.l_prev, - ""); - mdb_printf(" %-12s 0x%-34p %-30s\n", "l_next", tn.tn_children.l_next, - ""); + "Hash table of child nodes"); + mdb_printf(" %-12s 0x%-34p\n", "l_prev", tn.tn_children.l_prev); + mdb_printf(" %-12s 0x%-34p\n", "l_next", tn.tn_children.l_next); mdb_printf("%-12s 0x%-34p %-30s\n", "tn_pgroups", &(tn.tn_pgroups), - "Property group list"); + "Property group list"); mdb_printf("%-12s 0x%-34p %-30s\n", "tn_methods", &(tn.tn_methods), - "Registered method list"); + "Registered method list"); mdb_printf("%-12s 0x%-34p %-30s\n", "tn_priv", tn.tn_priv, - "Private enumerator data"); + "Private enumerator data"); mdb_printf("%-12s %-36d %-30s\n", "tn_refs", tn.tn_refs, - "Node reference count"); + "Node reference count"); return (DCMD_OK); } diff --git a/usr/src/lib/fm/Makefile b/usr/src/lib/fm/Makefile index 925e471c74..046d9346cb 100644 --- a/usr/src/lib/fm/Makefile +++ b/usr/src/lib/fm/Makefile @@ -36,6 +36,7 @@ i386_SUBDIRS = SUBDIRS = \ libdiagcode \ + libdiskstatus \ libfmd_adm \ libfmd_log \ libfmd_msg \ diff --git a/usr/src/lib/fm/libdiskstatus/Makefile b/usr/src/lib/fm/libdiskstatus/Makefile new file mode 100644 index 0000000000..b051273f20 --- /dev/null +++ b/usr/src/lib/fm/libdiskstatus/Makefile @@ -0,0 +1,56 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +include ../../Makefile.lib +include ../Makefile.lib + +HDRDIR= common +FMHDRS= libdiskstatus.h + +SUBDIRS= $(MACH) +$(BUILD64)SUBDIRS += $(MACH64) + +all := TARGET= all +clean := TARGET= clean +clobber := TARGET= clobber +install := TARGET= install +lint := TARGET= lint + +.KEEP_STATE: + +all clean clobber install lint: $(SUBDIRS) + +install_h: $(ROOTFMHDRS) + +check: $(CHECKHDRS) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + +include ../../Makefile.targ +include ../Makefile.targ diff --git a/usr/src/lib/fm/libdiskstatus/Makefile.com b/usr/src/lib/fm/libdiskstatus/Makefile.com new file mode 100644 index 0000000000..ee2fa3725a --- /dev/null +++ b/usr/src/lib/fm/libdiskstatus/Makefile.com @@ -0,0 +1,56 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +LIBRARY= libdiskstatus.a +VERS= .1 + +OBJECTS= libdiskstatus.o \ + ds_scsi.o \ + ds_scsi_sim.o \ + ds_scsi_uscsi.o \ + ds_util.o + +include ../../../Makefile.lib +include ../../Makefile.lib + +LIBS= $(DYNLIB) $(LINTLIB) + +SRCDIR= ../common + +INCS += -I$(SRCDIR) +LDLIBS += -lc -lnvpair +CPPFLAGS += $(INCS) + +$(LINTLIB) := SRCS= $(SRCDIR)/$(LINTSRC) + +.KEEP_STATE: + +all: $(LIBS) + +lint: lintcheck + +include ../../../Makefile.targ +include ../../Makefile.targ diff --git a/usr/src/lib/fm/libdiskstatus/amd64/Makefile b/usr/src/lib/fm/libdiskstatus/amd64/Makefile new file mode 100644 index 0000000000..5375e35120 --- /dev/null +++ b/usr/src/lib/fm/libdiskstatus/amd64/Makefile @@ -0,0 +1,30 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +include ../Makefile.com +include ../../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) diff --git a/usr/src/lib/fm/libdiskstatus/common/ds_impl.h b/usr/src/lib/fm/libdiskstatus/common/ds_impl.h new file mode 100644 index 0000000000..34f8b15d75 --- /dev/null +++ b/usr/src/lib/fm/libdiskstatus/common/ds_impl.h @@ -0,0 +1,74 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _DS_IMPL_H +#define _DS_IMPL_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <dlfcn.h> +#include <libnvpair.h> +#include <sys/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +struct disk_status; + +typedef struct ds_transport { + void *(*dt_open)(struct disk_status *); + void (*dt_close)(void *); + int (*dt_scan)(void *); +} ds_transport_t; + +struct disk_status { + char *ds_path; /* path to device */ + int ds_fd; /* device file descriptor */ + ds_transport_t *ds_transport; /* associated transport */ + void *ds_data; /* transport-specific data */ + int ds_faults; /* mask of current faults */ + nvlist_t *ds_overtemp; /* overtemp */ + nvlist_t *ds_predfail; /* predict fail */ + nvlist_t *ds_testfail; /* self test fail */ + int ds_error; /* last error */ + nvlist_t *ds_state; /* protocol state */ +}; + +#define DS_FAULT_OVERTEMP 0x1 +#define DS_FAULT_PREDFAIL 0x2 +#define DS_FAULT_TESTFAIL 0x4 + +extern void dprintf(const char *, ...); +extern void ddump(const char *, const void *, size_t); +extern boolean_t ds_debug; + +extern int ds_set_errno(struct disk_status *, int); + +#ifdef __cplusplus +} +#endif + +#endif /* _DS_IMPL_H */ diff --git a/usr/src/lib/fm/libdiskstatus/common/ds_scsi.c b/usr/src/lib/fm/libdiskstatus/common/ds_scsi.c new file mode 100644 index 0000000000..0b80f4d4c2 --- /dev/null +++ b/usr/src/lib/fm/libdiskstatus/common/ds_scsi.c @@ -0,0 +1,1352 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <assert.h> +#include <errno.h> +#include <libdiskstatus.h> +#include <limits.h> +#include <stdlib.h> +#include <strings.h> +#include <sys/fm/io/scsi.h> + +#include "ds_scsi.h" +#include "ds_scsi_sim.h" +#include "ds_scsi_uscsi.h" + +typedef struct ds_scsi_info { + disk_status_t *si_dsp; + void *si_sim; + int si_cdblen; + int si_supp_mode; + int si_supp_log; + int si_extensions; + int si_reftemp; + scsi_ms_hdrs_t si_hdrs; + scsi_ie_page_t si_iec_current; + scsi_ie_page_t si_iec_changeable; + nvlist_t *si_state_modepage; + nvlist_t *si_state_logpage; + nvlist_t *si_state_iec; +} ds_scsi_info_t; + +#define scsi_set_errno(sip, errno) (ds_set_errno((sip)->si_dsp, (errno))) + +/* + * Table to validate log pages + */ +typedef int (*logpage_validation_fn_t)(ds_scsi_info_t *, + scsi_log_parameter_header_t *, int, nvlist_t *); +typedef int (*logpage_analyze_fn_t)(ds_scsi_info_t *, + scsi_log_parameter_header_t *, int); + +typedef struct logpage_validation_entry { + uchar_t ve_code; + int ve_supported; + const char *ve_desc; + logpage_validation_fn_t ve_validate; + logpage_analyze_fn_t ve_analyze; +} logpage_validation_entry_t; + +static int logpage_ie_verify(ds_scsi_info_t *, + scsi_log_parameter_header_t *, int, nvlist_t *); +static int logpage_temp_verify(ds_scsi_info_t *, + scsi_log_parameter_header_t *, int, nvlist_t *); +static int logpage_selftest_verify(ds_scsi_info_t *, + scsi_log_parameter_header_t *, int, nvlist_t *); + +static int logpage_ie_analyze(ds_scsi_info_t *, + scsi_log_parameter_header_t *, int); +static int logpage_temp_analyze(ds_scsi_info_t *, + scsi_log_parameter_header_t *, int); +static int logpage_selftest_analyze(ds_scsi_info_t *, + scsi_log_parameter_header_t *, int); + +static struct logpage_validation_entry log_validation[] = { + { LOGPAGE_IE, LOGPAGE_SUPP_IE, + "informational-exceptions", + logpage_ie_verify, logpage_ie_analyze }, + { LOGPAGE_TEMP, LOGPAGE_SUPP_TEMP, + "temperature", + logpage_temp_verify, logpage_temp_analyze }, + { LOGPAGE_SELFTEST, LOGPAGE_SUPP_SELFTEST, + "self-test", + logpage_selftest_verify, logpage_selftest_analyze } +}; + +#define NLOG_VALIDATION (sizeof (log_validation) / sizeof (log_validation[0])) + +/* + * Given an extended sense page, retrieves the sense key, as well as the + * additional sense code information. + */ +static void +scsi_translate_error(struct scsi_extended_sense *rq, uint_t *skeyp, + uint_t *ascp, uint_t *ascqp) +{ + struct scsi_descr_sense_hdr *sdsp = + (struct scsi_descr_sense_hdr *)rq; + + *skeyp = rq->es_key; + + /* + * Get asc, ascq and info field from sense data. There are two + * possible formats (fixed sense data and descriptor sense data) + * depending on the value of es_code. + */ + switch (rq->es_code) { + case CODE_FMT_DESCR_CURRENT: + case CODE_FMT_DESCR_DEFERRED: + + *ascp = sdsp->ds_add_code; + *ascqp = sdsp->ds_qual_code; + break; + + case CODE_FMT_FIXED_CURRENT: + case CODE_FMT_FIXED_DEFERRED: + default: + + if (rq->es_add_len >= 6) { + *ascp = rq->es_add_code; + *ascqp = rq->es_qual_code; + } else { + *ascp = 0xff; + *ascqp = 0xff; + } + break; + } +} + +/* + * Routines built atop the bare uscsi commands, which take into account the + * command length, automatically translate any scsi errors, and transparently + * call into the simulator if active. + */ +static int +scsi_mode_select(ds_scsi_info_t *sip, uchar_t page_code, int options, + void *buf, uint_t buflen, scsi_ms_hdrs_t *headers, uint_t *skp, + uint_t *ascp, uint_t *ascqp) +{ + int result; + struct scsi_extended_sense sense; + int senselen = sizeof (struct scsi_extended_sense); + struct mode_page *mp = (struct mode_page *)buf; + + assert(sip->si_cdblen == MODE_CMD_LEN_6 || + sip->si_cdblen == MODE_CMD_LEN_10); + assert(headers->ms_length == sip->si_cdblen); + + bzero(&sense, sizeof (struct scsi_extended_sense)); + + if (mp->ps) { + options |= MODE_SELECT_SP; + mp->ps = 0; + } else { + options &= ~MODE_SELECT_SP; + } + + if (sip->si_cdblen == MODE_CMD_LEN_6) { + /* The following fields are reserved during mode select: */ + headers->ms_hdr.g0.ms_header.length = 0; + headers->ms_hdr.g0.ms_header.device_specific = 0; + + if (sip->si_sim) + result = simscsi_mode_select(sip->si_sim, + page_code, options, buf, buflen, + &headers->ms_hdr.g0, &sense, &senselen); + else + result = uscsi_mode_select(sip->si_dsp->ds_fd, + page_code, options, buf, buflen, + &headers->ms_hdr.g0, &sense, &senselen); + } else { + /* The following fields are reserved during mode select: */ + headers->ms_hdr.g1.ms_header.length = 0; + headers->ms_hdr.g1.ms_header.device_specific = 0; + + if (sip->si_sim) + result = simscsi_mode_select_10(sip->si_sim, + page_code, options, buf, buflen, + &headers->ms_hdr.g1, &sense, &senselen); + else + result = uscsi_mode_select_10(sip->si_dsp->ds_fd, + page_code, options, buf, buflen, + &headers->ms_hdr.g1, &sense, &senselen); + } + + if (result != 0) + scsi_translate_error(&sense, skp, ascp, ascqp); + + return (result); +} + +static int +scsi_mode_sense(ds_scsi_info_t *sip, uchar_t page_code, uchar_t pc, + void *buf, uint_t buflen, scsi_ms_hdrs_t *headers, uint_t *skp, + uint_t *ascp, uint_t *ascqp) +{ + int result; + struct scsi_extended_sense sense; + int senselen = sizeof (struct scsi_extended_sense); + + assert(sip->si_cdblen == MODE_CMD_LEN_6 || + sip->si_cdblen == MODE_CMD_LEN_10); + + bzero(&sense, sizeof (struct scsi_extended_sense)); + + bzero(headers, sizeof (scsi_ms_hdrs_t)); + headers->ms_length = sip->si_cdblen; + + if (sip->si_cdblen == MODE_CMD_LEN_6) { + if (sip->si_sim) + result = simscsi_mode_sense(sip->si_sim, + page_code, pc, buf, buflen, &headers->ms_hdr.g0, + &sense, &senselen); + else + result = uscsi_mode_sense(sip->si_dsp->ds_fd, page_code, + pc, buf, buflen, &headers->ms_hdr.g0, &sense, + &senselen); + } else { + if (sip->si_sim) + result = simscsi_mode_sense_10(sip->si_sim, + page_code, pc, buf, buflen, &headers->ms_hdr.g1, + &sense, &senselen); + else + result = uscsi_mode_sense_10(sip->si_dsp->ds_fd, + page_code, pc, buf, buflen, &headers->ms_hdr.g1, + &sense, &senselen); + } + + if (result != 0) + scsi_translate_error(&sense, skp, ascp, ascqp); + + return (result); +} + +static int +scsi_request_sense(ds_scsi_info_t *sip, uint_t *skp, uint_t *ascp, + uint_t *ascqp) +{ + struct scsi_extended_sense sense, sensebuf; + int senselen = sizeof (struct scsi_extended_sense); + int sensebuflen = sizeof (struct scsi_extended_sense); + int result; + + bzero(&sense, sizeof (struct scsi_extended_sense)); + bzero(&sensebuf, sizeof (struct scsi_extended_sense)); + + if (sip->si_sim) + result = simscsi_request_sense(sip->si_sim, + (caddr_t)&sensebuf, sensebuflen, &sense, &senselen); + else + result = uscsi_request_sense(sip->si_dsp->ds_fd, + (caddr_t)&sensebuf, sensebuflen, &sense, &senselen); + + if (result == 0) + scsi_translate_error(&sensebuf, skp, ascp, ascqp); + else + scsi_translate_error(&sense, skp, ascp, ascqp); + + return (result); +} + +static int +scsi_log_sense(ds_scsi_info_t *sip, int page_code, int page_control, + caddr_t page_data, int page_size, uint_t *skp, uint_t *ascp, uint_t *ascqp) +{ + int result; + struct scsi_extended_sense sense; + int senselen = sizeof (struct scsi_extended_sense); + + if (sip->si_sim) + result = simscsi_log_sense(sip->si_sim, + page_code, page_control, page_data, page_size, &sense, + &senselen); + else + result = uscsi_log_sense(sip->si_dsp->ds_fd, + page_code, page_control, page_data, page_size, &sense, + &senselen); + + if (result != 0) + scsi_translate_error(&sense, skp, ascp, ascqp); + + return (result); +} + +/* + * Given a list of supported mode pages, determine if the given page is present. + */ +static boolean_t +mode_page_present(uchar_t *pgdata, uint_t pgdatalen, uchar_t pagecode) +{ + uint_t i = 0; + struct mode_page *pg; + boolean_t found = B_FALSE; + + /* + * The mode page list contains all mode pages supported by the device, + * one after the other. + */ + while (i < pgdatalen) { + pg = (struct mode_page *)&pgdata[i]; + + if (pg->code == pagecode) { + found = B_TRUE; + break; + } + + i += MODESENSE_PAGE_LEN(pg); + } + + return (found); +} + +/* + * Load mode pages and check that the appropriate pages are supported. + * + * As part of this process, we determine which form of the MODE SENSE / MODE + * SELECT command to use (the 6-byte or 10-byte version) by executing a MODE + * SENSE command for a page that should be implemented by the device. + */ +static int +load_modepages(ds_scsi_info_t *sip) +{ + int allpages_buflen; + uchar_t *allpages; + scsi_ms_hdrs_t headers; + int result; + uint_t skey, asc, ascq; + int datalength = 0; + scsi_ms_header_t *smh = &headers.ms_hdr.g0; + scsi_ms_header_g1_t *smh_g1 = &headers.ms_hdr.g1; + nvlist_t *nvl; + + allpages_buflen = MAX_BUFLEN(scsi_ms_header_g1_t); + if ((allpages = calloc(allpages_buflen, 1)) == NULL) + return (scsi_set_errno(sip, EDS_NOMEM)); + + bzero(&headers, sizeof (headers)); + + /* + * Attempt a mode sense(6). If that fails, try a mode sense(10) + * + * allpages is allocated to be of the maximum size for either a mode + * sense(6) or mode sense(10) MODEPAGE_ALLPAGES response. + * + * Note that the length passed into uscsi_mode_sense should be set to + * the maximum size of the parameter response, which in this case is + * UCHAR_MAX - the size of the headers/block descriptors. + */ + sip->si_cdblen = MODE_CMD_LEN_6; + if ((result = scsi_mode_sense(sip, MODEPAGE_ALLPAGES, PC_CURRENT, + (caddr_t)allpages, UCHAR_MAX - sizeof (scsi_ms_header_t), + &headers, &skey, &asc, &ascq)) == 0) { + /* + * Compute the data length of the page that contains all mode + * sense pages. This is a bit tricky because the format of the + * response from the lun is: + * + * header: <length> <medium type byte> <dev specific byte> + * <block descriptor length> + * [<optional block descriptor>] + * data: [<mode page data> <mode page data> ...] + * + * Since the length field in the header describes the length of + * the entire response. This includes the header, but NOT + * the length field itself, which is 1 or 2 bytes depending on + * which mode sense type (6- or 10- byte) is being executed. + * + * So, the data length equals the length value in the header + * plus 1 (because the length byte was not included in the + * length count), minus [[the sum of the length of the header + * and the length of the block descriptor]]. + */ + datalength = (smh->ms_header.length + + sizeof (smh->ms_header.length)) - + (sizeof (struct mode_header) + + smh->ms_header.bdesc_length); + } else if (SCSI_INVALID_OPCODE(skey, asc, ascq)) { + /* + * Fallback and try the 10-byte version of the command. + */ + sip->si_cdblen = MODE_CMD_LEN_10; + result = scsi_mode_sense(sip, MODEPAGE_ALLPAGES, + PC_CURRENT, (caddr_t)allpages, allpages_buflen, + &headers, &skey, &asc, &ascq); + + if (result == 0) { + datalength = (BE_16(smh_g1->ms_header.length) + + sizeof (smh_g1->ms_header.length)) - + (sizeof (struct mode_header_g1) + + BE_16(smh_g1->ms_header.bdesc_length)); + + } + } + + if (result == 0 && datalength >= 0) { + if (nvlist_add_int8(sip->si_dsp->ds_state, "command-length", + sip->si_cdblen == MODE_CMD_LEN_6 ? 6 : 10) != 0) { + free(allpages); + return (scsi_set_errno(sip, EDS_NOMEM)); + } + + nvl = NULL; + if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 || + nvlist_add_nvlist(sip->si_dsp->ds_state, "modepages", + nvl) != 0) { + free(allpages); + nvlist_free(nvl); + return (scsi_set_errno(sip, EDS_NOMEM)); + } + + nvlist_free(nvl); + result = nvlist_lookup_nvlist(sip->si_dsp->ds_state, + "modepages", &sip->si_state_modepage); + assert(result == 0); + + /* + * One of the sets of the commands (above) succeeded, so now + * look for the mode pages we need and record them appropriately + */ + if (mode_page_present(allpages, datalength, + MODEPAGE_INFO_EXCPT)) { + + nvl = NULL; + if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 || + nvlist_add_nvlist(sip->si_state_modepage, + "informational-exceptions", nvl) != 0) { + free(allpages); + nvlist_free(nvl); + return (scsi_set_errno(sip, EDS_NOMEM)); + } + nvlist_free(nvl); + sip->si_supp_mode |= MODEPAGE_SUPP_IEC; + result = nvlist_lookup_nvlist(sip->si_state_modepage, + "informational-exceptions", &sip->si_state_iec); + assert(result == 0); + } + + } else { + /* + * If the device failed to respond to one of the basic commands, + * then assume it's not a SCSI device or otherwise doesn't + * support the necessary transport. + */ + if (datalength < 0) + dprintf("command returned invalid data length (%d)\n", + datalength); + else + dprintf("failed to load modepages (KEY=0x%x " + "ASC=0x%x ASCQ=0x%x)\n", skey, asc, ascq); + + result = scsi_set_errno(sip, EDS_NO_TRANSPORT); + } + + free(allpages); + return (result); +} + +/* + * Verify a single logpage. This will do some generic validation and then call + * the logpage-specific function for further verification. + */ +static int +verify_logpage(ds_scsi_info_t *sip, logpage_validation_entry_t *lp) +{ + scsi_log_header_t *lhp; + struct scsi_extended_sense sense; + int buflen; + int log_length; + int result = 0; + uint_t kp, asc, ascq; + nvlist_t *nvl; + + buflen = MAX_BUFLEN(scsi_log_header_t); + if ((lhp = calloc(buflen, 1)) == NULL) + return (scsi_set_errno(sip, EDS_NOMEM)); + bzero(&sense, sizeof (struct scsi_extended_sense)); + + nvl = NULL; + if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 || + nvlist_add_nvlist(sip->si_state_logpage, lp->ve_desc, nvl) != 0) { + nvlist_free(nvl); + free(lhp); + return (scsi_set_errno(sip, EDS_NOMEM)); + } + nvlist_free(nvl); + result = nvlist_lookup_nvlist(sip->si_state_logpage, lp->ve_desc, &nvl); + assert(result == 0); + + result = scsi_log_sense(sip, lp->ve_code, + PC_CUMULATIVE, (caddr_t)lhp, buflen, &kp, &asc, &ascq); + + if (result == 0) { + log_length = BE_16(lhp->lh_length); + if (nvlist_add_uint16(nvl, "length", log_length) != 0) { + free(lhp); + return (scsi_set_errno(sip, EDS_NOMEM)); + } + + if (lp->ve_validate(sip, (scsi_log_parameter_header_t *) + (((char *)lhp) + sizeof (scsi_log_header_t)), + log_length, nvl) != 0) { + free(lhp); + return (-1); + } + } else { + dprintf("failed to load %s log page (KEY=0x%x " + "ASC=0x%x ASCQ=0x%x)\n", lp->ve_desc, kp, asc, ascq); + } + + free(lhp); + return (0); +} + +/* + * Load log pages and determine which pages are supported. + */ +static int +load_logpages(ds_scsi_info_t *sip) +{ + int buflen; + scsi_supported_log_pages_t *sp; + struct scsi_extended_sense sense; + int result; + uint_t sk, asc, ascq; + int i, j; + nvlist_t *nvl; + + buflen = MAX_BUFLEN(scsi_log_header_t); + if ((sp = calloc(buflen, 1)) == NULL) + return (scsi_set_errno(sip, EDS_NOMEM)); + + bzero(&sense, sizeof (struct scsi_extended_sense)); + + if ((result = scsi_log_sense(sip, LOGPAGE_SUPP_LIST, + PC_CUMULATIVE, (caddr_t)sp, buflen, &sk, &asc, &ascq)) == 0) { + int pagecount = BE_16(sp->slp_hdr.lh_length); + + for (i = 0; i < pagecount; i++) { + for (j = 0; j < NLOG_VALIDATION; j++) { + if (log_validation[j].ve_code == + sp->slp_pages[i]) + sip->si_supp_log |= + log_validation[j].ve_supported; + } + } + } + + free(sp); + if (result == 0) { + nvl = NULL; + if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 || + nvlist_add_nvlist(sip->si_dsp->ds_state, "logpages", + nvl) != 0) { + nvlist_free(nvl); + return (scsi_set_errno(sip, EDS_NOMEM)); + } + + nvlist_free(nvl); + result = nvlist_lookup_nvlist(sip->si_dsp->ds_state, + "logpages", &sip->si_state_logpage); + assert(result == 0); + + /* + * Validate the logpage contents. + */ + for (i = 0; i < NLOG_VALIDATION; i++) { + if ((sip->si_supp_log & + log_validation[i].ve_supported) == 0) + continue; + + /* + * verify_logpage will clear the supported bit if + * verification fails. + */ + if (verify_logpage(sip, &log_validation[i]) != 0) + return (-1); + } + + } else { + dprintf("failed to get log pages " + "(KEY=0x%x ASC=0x%x ASCq=0x%x)\n", sk, asc, ascq); + } + + /* + * We always return 0 here, even if the required log pages aren't + * supported. + */ + return (0); +} + +/* + * Verify that the IE log page is sane. This log page is potentially chock-full + * of vendor specific information that we do not know how to access. All we can + * do is check for the generic predictive failure bit. If this log page is not + * well-formed, then bail out. + */ +static int +logpage_ie_verify(ds_scsi_info_t *sip, scsi_log_parameter_header_t *lphp, + int log_length, nvlist_t *nvl) +{ + int i, plen = 0; + boolean_t seen = B_FALSE; + scsi_ie_log_param_t *iep = + (scsi_ie_log_param_t *)lphp; + + for (i = 0; i < log_length; i += plen) { + iep = (scsi_ie_log_param_t *)((char *)iep + plen); + + if (BE_16(iep->ie_hdr.lph_param) == LOGPARAM_IE) { + if (nvlist_add_boolean_value(nvl, "general", + B_TRUE) != 0) + return (scsi_set_errno(sip, EDS_NOMEM)); + + if (lphp->lph_length < LOGPARAM_IE_MIN_LEN) { + if (nvlist_add_uint8(nvl, + "invalid-length", lphp->lph_length) != 0) + return (scsi_set_errno(sip, EDS_NOMEM)); + } else { + seen = B_TRUE; + } + break; + } + + plen = iep->ie_hdr.lph_length + + sizeof (scsi_log_parameter_header_t); + } + + if (!seen) { + sip->si_supp_log &= ~LOGPAGE_SUPP_IE; + dprintf("IE logpage validation failed\n"); + } + + return (0); +} + +/* + * Verify the contents of the temperature log page. The temperature log page + * contains two log parameters: the current temperature, and (optionally) the + * reference temperature. For the verification phase, we check that the two + * parameters we care about are well-formed. If there is no reference + * temperature, then we cannot use the page for monitoring purposes. + */ +static int +logpage_temp_verify(ds_scsi_info_t *sip, + scsi_log_parameter_header_t *lphp, int log_length, nvlist_t *nvl) +{ + int i, plen = 0; + boolean_t has_reftemp = B_FALSE; + boolean_t bad_length = B_FALSE; + ushort_t param_code; + + for (i = 0; i < log_length; i += plen) { + lphp = (scsi_log_parameter_header_t *)((char *)lphp + plen); + param_code = BE_16(lphp->lph_param); + + switch (param_code) { + case LOGPARAM_TEMP_CURTEMP: + if (nvlist_add_boolean_value(nvl, "current-temperature", + B_TRUE) != 0) + return (scsi_set_errno(sip, EDS_NOMEM)); + if (lphp->lph_length != LOGPARAM_TEMP_LEN) { + if (nvlist_add_uint8(nvl, + "invalid-length", lphp->lph_length) != 0) + return (scsi_set_errno(sip, EDS_NOMEM)); + bad_length = B_TRUE; + } + break; + + case LOGPARAM_TEMP_REFTEMP: + if (nvlist_add_boolean_value(nvl, + "reference-temperature", B_TRUE) != 0) + return (scsi_set_errno(sip, EDS_NOMEM)); + if (lphp->lph_length != LOGPARAM_TEMP_LEN) { + if (nvlist_add_uint8(nvl, + "invalid-length", lphp->lph_length) != 0) + return (scsi_set_errno(sip, EDS_NOMEM)); + bad_length = B_TRUE; + } + has_reftemp = B_TRUE; + break; + } + + plen = lphp->lph_length + + sizeof (scsi_log_parameter_header_t); + } + + if (bad_length || !has_reftemp) { + sip->si_supp_log &= ~LOGPAGE_SUPP_TEMP; + dprintf("temperature logpage validation failed\n"); + } + + return (0); +} + +/* + * Verify the contents of the self test log page. The log supports a maximum of + * 20 entries, where each entry's parameter code is its index in the log. We + * check that the parameter codes fall within this range, and that the size of + * each page is what we expect. It's perfectly acceptable for there to be no + * entries in this log, so we must also be sure to validate the contents as part + * of the analysis phase. + */ +static int +logpage_selftest_verify(ds_scsi_info_t *sip, + scsi_log_parameter_header_t *lphp, int log_length, nvlist_t *nvl) +{ + int i, plen = 0; + boolean_t bad = B_FALSE; + int entries = 0; + ushort_t param_code; + + for (i = 0; i < log_length; i += plen, entries++) { + lphp = (scsi_log_parameter_header_t *)((char *)lphp + plen); + param_code = BE_16(lphp->lph_param); + + if (param_code < LOGPAGE_SELFTEST_MIN_PARAM_CODE || + param_code > LOGPAGE_SELFTEST_MAX_PARAM_CODE) { + if (nvlist_add_uint16(nvl, "invalid-param-code", + param_code) != 0) + return (scsi_set_errno(sip, EDS_NOMEM)); + bad = B_TRUE; + break; + } + + if (lphp->lph_length != LOGPAGE_SELFTEST_PARAM_LEN) { + if (nvlist_add_uint8(nvl, "invalid-length", + lphp->lph_length) != 0) + return (scsi_set_errno(sip, EDS_NOMEM)); + bad = B_TRUE; + break; + + } + + plen = lphp->lph_length + + sizeof (scsi_log_parameter_header_t); + } + + if (bad) { + sip->si_supp_log &= ~LOGPAGE_SUPP_SELFTEST; + dprintf("selftest logpage validation failed\n"); + } + + return (0); +} + +/* + * Load the current IE mode pages + */ +static int +load_ie_modepage(ds_scsi_info_t *sip) +{ + struct scsi_ms_hdrs junk_hdrs; + int result; + uint_t skey, asc, ascq; + + if (!(sip->si_supp_mode & MODEPAGE_SUPP_IEC)) + return (0); + + bzero(&sip->si_iec_current, sizeof (sip->si_iec_current)); + bzero(&sip->si_iec_changeable, sizeof (sip->si_iec_changeable)); + + if ((result = scsi_mode_sense(sip, + MODEPAGE_INFO_EXCPT, PC_CURRENT, &sip->si_iec_current, + MODEPAGE_INFO_EXCPT_LEN, &sip->si_hdrs, &skey, &asc, + &ascq)) == 0) { + result = scsi_mode_sense(sip, + MODEPAGE_INFO_EXCPT, PC_CHANGEABLE, + &sip->si_iec_changeable, + MODEPAGE_INFO_EXCPT_LEN, &junk_hdrs, &skey, &asc, &ascq); + } + + if (result != 0) { + dprintf("failed to get IEC modepage (KEY=0x%x " + "ASC=0x%x ASCQ=0x%x)", skey, asc, ascq); + sip->si_supp_mode &= ~MODEPAGE_SUPP_IEC; + } else { + if (nvlist_add_boolean_value(sip->si_state_iec, + "dexcpt", sip->si_iec_current.ie_dexcpt) != 0 || + nvlist_add_boolean_value(sip->si_state_iec, + "logerr", sip->si_iec_current.ie_logerr) != 0 || + nvlist_add_uint8(sip->si_state_iec, + "mrie", sip->si_iec_current.ie_mrie) != 0 || + nvlist_add_boolean_value(sip->si_state_iec, + "test", sip->si_iec_current.ie_test) != 0 || + nvlist_add_boolean_value(sip->si_state_iec, + "ewasc", sip->si_iec_current.ie_ewasc) != 0 || + nvlist_add_boolean_value(sip->si_state_iec, + "perf", sip->si_iec_current.ie_perf) != 0 || + nvlist_add_boolean_value(sip->si_state_iec, + "ebf", sip->si_iec_current.ie_ebf) != 0 || + nvlist_add_uint32(sip->si_state_iec, + "interval-timer", + BE_32(sip->si_iec_current.ie_interval_timer)) != 0 || + nvlist_add_uint32(sip->si_state_iec, + "report-count", + BE_32(sip->si_iec_current.ie_report_count)) != 0) + return (scsi_set_errno(sip, EDS_NOMEM)); + } + + return (0); +} + +/* + * Enable IE reporting. We prefer the following settings: + * + * (1) DEXCPT = 0 + * (3) MRIE = 6 (IE_REPORT_ON_REQUEST) + * (4) EWASC = 1 + * (6) REPORT COUNT = 0x00000001 + * (7) LOGERR = 1 + * + * However, not all drives support changing these values, and the current state + * may be useful enough as-is. For example, some drives support IE logging, but + * don't support changing the MRIE. In this case, we can still use the + * information provided by the log page. + */ +static int +scsi_enable_ie(ds_scsi_info_t *sip, boolean_t *changed) +{ + scsi_ie_page_t new_iec_page; + scsi_ms_hdrs_t hdrs; + uint_t skey, asc, ascq; + + if (!(sip->si_supp_mode & MODEPAGE_SUPP_IEC)) + return (0); + + bzero(&new_iec_page, sizeof (new_iec_page)); + bzero(&hdrs, sizeof (hdrs)); + + (void) memcpy(&new_iec_page, &sip->si_iec_current, + sizeof (new_iec_page)); + + if (IEC_IE_CHANGEABLE(sip->si_iec_changeable)) + new_iec_page.ie_dexcpt = 0; + + if (IEC_MRIE_CHANGEABLE(sip->si_iec_changeable)) + new_iec_page.ie_mrie = IE_REPORT_ON_REQUEST; + + /* + * We only want to enable warning reporting if we are able to change the + * mrie to report on request. Otherwise, we risk unnecessarily + * interrupting normal SCSI commands with a CHECK CONDITION code. + */ + if (IEC_EWASC_CHANGEABLE(sip->si_iec_changeable)) { + if (new_iec_page.ie_mrie == IE_REPORT_ON_REQUEST) + new_iec_page.ie_ewasc = 1; + else + new_iec_page.ie_ewasc = 0; + } + + if (IEC_RPTCNT_CHANGEABLE(sip->si_iec_changeable)) + new_iec_page.ie_report_count = BE_32(1); + + if (IEC_LOGERR_CHANGEABLE(sip->si_iec_changeable)) + new_iec_page.ie_logerr = 1; + + /* + * Now compare the new mode page with the existing one. + * if there's no difference, there's no need for a mode select + */ + if (memcmp(&new_iec_page, &sip->si_iec_current, + MODEPAGE_INFO_EXCPT_LEN) == 0) { + *changed = B_FALSE; + } else { + (void) memcpy(&hdrs, &sip->si_hdrs, sizeof (sip->si_hdrs)); + + if (scsi_mode_select(sip, + MODEPAGE_INFO_EXCPT, MODE_SELECT_PF, &new_iec_page, + MODEPAGE_INFO_EXCPT_LEN, &hdrs, &skey, &asc, &ascq) == 0) { + *changed = B_TRUE; + } else { + dprintf("failed to enable IE (KEY=0x%x " + "ASC=0x%x ASCQ=0x%x)\n", skey, asc, ascq); + *changed = B_FALSE; + } + } + + if (nvlist_add_boolean_value(sip->si_state_iec, "changed", + *changed) != 0) + return (scsi_set_errno(sip, EDS_NOMEM)); + + return (0); +} + +/* + * Clear the GLTSD bit, indicating log pages should be saved to non-volatile + * storage. + */ +static int +clear_gltsd(ds_scsi_info_t *sip) +{ + scsi_ms_hdrs_t hdrs, junk_hdrs; + struct mode_control_scsi3 control_pg_cur, control_pg_chg; + int result; + uint_t skey, asc, ascq; + + bzero(&hdrs, sizeof (hdrs)); + bzero(&control_pg_cur, sizeof (control_pg_cur)); + bzero(&control_pg_chg, sizeof (control_pg_chg)); + + result = scsi_mode_sense(sip, + MODEPAGE_CTRL_MODE, PC_CURRENT, &control_pg_cur, + MODEPAGE_CTRL_MODE_LEN, &hdrs, &skey, &asc, &ascq); + + if (result != 0) { + dprintf("failed to read Control mode page (KEY=0x%x " + "ASC=0x%x ASCQ=0x%x)\n", skey, asc, ascq); + } else if (control_pg_cur.mode_page.length != + PAGELENGTH_MODE_CONTROL_SCSI3) { + dprintf("SCSI-3 control mode page not supported\n"); + } else if ((result = scsi_mode_sense(sip, + MODEPAGE_CTRL_MODE, PC_CHANGEABLE, &control_pg_chg, + MODEPAGE_CTRL_MODE_LEN, &junk_hdrs, &skey, &asc, &ascq)) + != 0) { + dprintf("failed to read changeable Control mode page (KEY=0x%x " + "ASC=0x%x ASCQ=0x%x)\n", skey, asc, ascq); + } else if (control_pg_cur.gltsd && !GLTSD_CHANGEABLE(control_pg_chg)) { + dprintf("gltsd is set and not changeable\n"); + if (nvlist_add_boolean_value(sip->si_dsp->ds_state, + "gltsd", control_pg_cur.gltsd) != 0) + return (scsi_set_errno(sip, EDS_NOMEM)); + } else if (control_pg_cur.gltsd) { + control_pg_cur.gltsd = 0; + result = scsi_mode_select(sip, + MODEPAGE_CTRL_MODE, MODE_SELECT_PF, &control_pg_cur, + MODEPAGE_CTRL_MODE_LEN, &hdrs, &skey, &asc, &ascq); + if (result != 0) + dprintf("failed to enable GLTSD (KEY=0x%x " + "ASC=0x%x ASCQ=0x%x\n", skey, asc, ascq); + if (nvlist_add_boolean_value(sip->si_dsp->ds_state, + "gltsd", control_pg_cur.gltsd) != 0) + return (scsi_set_errno(sip, EDS_NOMEM)); + } + + return (0); +} + +/* + * Fetch the contents of the logpage, and then call the logpage-specific + * analysis function. The analysis function is responsible for detecting any + * faults and filling in the details. + */ +static int +analyze_one_logpage(ds_scsi_info_t *sip, logpage_validation_entry_t *entry) +{ + scsi_log_header_t *lhp; + scsi_log_parameter_header_t *lphp; + int buflen; + int log_length; + uint_t skey, asc, ascq; + int result; + + buflen = MAX_BUFLEN(scsi_log_header_t); + if ((lhp = calloc(buflen, 1)) == NULL) + return (scsi_set_errno(sip, EDS_NOMEM)); + + result = scsi_log_sense(sip, entry->ve_code, + PC_CUMULATIVE, (caddr_t)lhp, buflen, &skey, &asc, &ascq); + + if (result == 0) { + log_length = BE_16(lhp->lh_length); + lphp = (scsi_log_parameter_header_t *)(((uchar_t *)lhp) + + sizeof (scsi_log_header_t)); + + result = entry->ve_analyze(sip, lphp, log_length); + } else { + result = scsi_set_errno(sip, EDS_IO); + } + + free(lhp); + return (result); +} + +/* + * Analyze the IE logpage. If we find an IE log record with a non-zero 'asc', + * then we have a fault. + */ +static int +logpage_ie_analyze(ds_scsi_info_t *sip, scsi_log_parameter_header_t *lphp, + int log_length) +{ + int i, plen = 0; + scsi_ie_log_param_t *iep = (scsi_ie_log_param_t *)lphp; + nvlist_t *nvl; + + assert(sip->si_dsp->ds_predfail == NULL); + if (nvlist_alloc(&sip->si_dsp->ds_predfail, NV_UNIQUE_NAME, 0) != 0) + return (scsi_set_errno(sip, EDS_NOMEM)); + nvl = sip->si_dsp->ds_predfail; + + for (i = 0; i < log_length; i += plen) { + iep = (scsi_ie_log_param_t *)((char *)iep + plen); + + /* + * Even though we validated the length during the initial phase, + * never trust the device. + */ + if (BE_16(iep->ie_hdr.lph_param) == LOGPARAM_IE && + iep->ie_hdr.lph_length >= LOGPARAM_IE_MIN_LEN) { + if (nvlist_add_uint8(nvl, FM_EREPORT_PAYLOAD_SCSI_ASC, + iep->ie_asc) != 0 || + nvlist_add_uint8(nvl, FM_EREPORT_PAYLOAD_SCSI_ASCQ, + iep->ie_ascq) != 0) + return (scsi_set_errno(sip, EDS_NOMEM)); + + if (iep->ie_asc != 0) + sip->si_dsp->ds_faults |= + DS_FAULT_PREDFAIL; + break; + } + plen = iep->ie_hdr.lph_length + + sizeof (scsi_log_parameter_header_t); + } + + return (0); +} + +static int +logpage_temp_analyze(ds_scsi_info_t *sip, scsi_log_parameter_header_t *lphp, + int log_length) +{ + int i, plen = 0; + uint8_t reftemp, curtemp; + ushort_t param_code; + scsi_temp_log_param_t *temp; + nvlist_t *nvl; + + assert(sip->si_dsp->ds_overtemp == NULL); + if (nvlist_alloc(&sip->si_dsp->ds_overtemp, NV_UNIQUE_NAME, 0) != 0) + return (scsi_set_errno(sip, EDS_NOMEM)); + nvl = sip->si_dsp->ds_overtemp; + + reftemp = curtemp = INVALID_TEMPERATURE; + for (i = 0; i < log_length; i += plen) { + lphp = (scsi_log_parameter_header_t *)((char *)lphp + plen); + param_code = BE_16(lphp->lph_param); + temp = (scsi_temp_log_param_t *)lphp; + + switch (param_code) { + case LOGPARAM_TEMP_CURTEMP: + if (lphp->lph_length != LOGPARAM_TEMP_LEN) + break; + + if (nvlist_add_uint8(nvl, + FM_EREPORT_PAYLOAD_SCSI_CURTEMP, + temp->t_temp) != 0) + return (scsi_set_errno(sip, EDS_NOMEM)); + curtemp = temp->t_temp; + break; + + case LOGPARAM_TEMP_REFTEMP: + if (lphp->lph_length != LOGPARAM_TEMP_LEN) + break; + + if (nvlist_add_uint8(nvl, + FM_EREPORT_PAYLOAD_SCSI_THRESHTEMP, + temp->t_temp) != 0) + return (scsi_set_errno(sip, EDS_NOMEM)); + reftemp = temp->t_temp; + break; + } + + plen = lphp->lph_length + + sizeof (scsi_log_parameter_header_t); + } + + if (reftemp != INVALID_TEMPERATURE && curtemp != INVALID_TEMPERATURE && + curtemp > reftemp) + sip->si_dsp->ds_faults |= DS_FAULT_OVERTEMP; + + return (0); +} + +static int +logpage_selftest_analyze(ds_scsi_info_t *sip, scsi_log_parameter_header_t *lphp, + int log_length) +{ + int i, plen = 0; + int entries = 0; + ushort_t param_code; + scsi_selftest_log_param_t *stp; + nvlist_t *nvl; + + assert(sip->si_dsp->ds_testfail == NULL); + if (nvlist_alloc(&sip->si_dsp->ds_testfail, NV_UNIQUE_NAME, 0) != 0) + return (scsi_set_errno(sip, EDS_NOMEM)); + nvl = sip->si_dsp->ds_testfail; + + for (i = 0; i < log_length; i += plen, entries++) { + lphp = (scsi_log_parameter_header_t *)((char *)lphp + plen); + param_code = BE_16(lphp->lph_param); + stp = (scsi_selftest_log_param_t *)lphp; + + if (param_code >= LOGPAGE_SELFTEST_MIN_PARAM_CODE && + param_code <= LOGPAGE_SELFTEST_MAX_PARAM_CODE && + lphp->lph_length >= LOGPAGE_SELFTEST_PARAM_LEN) { + /* + * We always log the last result, or the result of the + * last completed test. + */ + if ((param_code == 1 || + SELFTEST_COMPLETE(stp->st_results))) { + if (nvlist_add_uint8(nvl, + FM_EREPORT_PAYLOAD_SCSI_RESULTCODE, + stp->st_results) != 0 || + nvlist_add_uint16(nvl, + FM_EREPORT_PAYLOAD_SCSI_TIMESTAMP, + BE_16(stp->st_timestamp)) != 0 || + nvlist_add_uint8(nvl, + FM_EREPORT_PAYLOAD_SCSI_SEGMENT, + stp->st_number) != 0 || + nvlist_add_uint64(nvl, + FM_EREPORT_PAYLOAD_SCSI_ADDRESS, + BE_64(stp->st_lba)) != 0) + return (scsi_set_errno(sip, + EDS_NOMEM)); + + if (SELFTEST_COMPLETE(stp->st_results)) { + if (stp->st_results != SELFTEST_OK) + sip->si_dsp->ds_faults |= + DS_FAULT_TESTFAIL; + return (0); + } + } + } + + plen = lphp->lph_length + + sizeof (scsi_log_parameter_header_t); + } + + return (0); +} + +/* + * Analyze the IE mode sense page explicitly. This is only needed if the IE log + * page is not supported. + */ +static int +analyze_ie_sense(ds_scsi_info_t *sip) +{ + uint_t skey, asc, ascq; + nvlist_t *nvl; + + /* + * Don't bother checking if we weren't able to set our MRIE correctly. + */ + if (sip->si_iec_current.ie_mrie != IE_REPORT_ON_REQUEST) + return (0); + + if (scsi_request_sense(sip, &skey, &asc, &ascq) != 0) { + dprintf("failed to request IE page (KEY=0x%x ASC=0x%x " + "ASCQ=0x%x)\n", skey, asc, ascq); + return (scsi_set_errno(sip, EDS_IO)); + } else if (skey == KEY_NO_SENSE) { + assert(sip->si_dsp->ds_predfail == NULL); + if (nvlist_alloc(&sip->si_dsp->ds_predfail, + NV_UNIQUE_NAME, 0) != 0) + return (scsi_set_errno(sip, EDS_NOMEM)); + nvl = sip->si_dsp->ds_predfail; + + if (nvlist_add_uint8(nvl, + FM_EREPORT_PAYLOAD_SCSI_ASC, asc) != 0 || + nvlist_add_uint8(nvl, + FM_EREPORT_PAYLOAD_SCSI_ASCQ, ascq) != 0) { + nvlist_free(nvl); + return (scsi_set_errno(sip, EDS_NOMEM)); + } + + if (asc != 0) + sip->si_dsp->ds_faults |= DS_FAULT_PREDFAIL; + } + + return (0); +} + +/* + * Clean up the scsi-specific information structure. + */ +static void +ds_scsi_close(void *arg) +{ + ds_scsi_info_t *sip = arg; + if (sip->si_sim) + (void) dlclose(sip->si_sim); + + free(sip); +} + +/* + * Initialize a single disk. Initialization consists of: + * + * 1. Check to see if the IE mechanism is enabled via MODE SENSE for the IE + * Control page (page 0x1C). + * + * 2. If the IE page is available, try to set the following parameters: + * + * DEXCPT 0 Enable exceptions + * MRIE 6 Only report IE information on request + * EWASC 1 Enable warning reporting + * REPORT COUNT 1 Only report an IE exception once + * LOGERR 1 Enable logging of errors + * + * The remaining fields are left as-is, preserving the current values. If we + * cannot set some of these fields, then we do our best. Some drives may + * have a static configuration which still allows for some monitoring. + * + * 3. Check to see if the IE log page (page 0x2F) is supported by issuing a + * LOG SENSE command. + * + * 4. Check to see if the self-test log page (page 0x10) is supported. + * + * 5. Check to see if the temperature log page (page 0x0D) is supported, and + * contains a reference temperature. + * + * 6. Clear the GLTSD bit in control mode page 0xA. This will allow the drive + * to save each of the log pages described above to nonvolatile storage. + * This is essential if the drive is to remember its failures across + * loss of power. + */ +static void * +ds_scsi_open_common(disk_status_t *dsp, ds_scsi_info_t *sip) +{ + boolean_t changed; + + sip->si_dsp = dsp; + + /* Load and validate mode pages */ + if (load_modepages(sip) != 0) { + ds_scsi_close(sip); + return (NULL); + } + + /* Load and validate log pages */ + if (load_logpages(sip) != 0) { + ds_scsi_close(sip); + return (NULL); + } + + /* Load IE state */ + if (load_ie_modepage(sip) != 0 || + scsi_enable_ie(sip, &changed) != 0 || + (changed && load_ie_modepage(sip) != 0)) { + ds_scsi_close(sip); + return (NULL); + } + + /* Clear the GLTSD bit in the control page */ + if (sip->si_supp_log != 0 && clear_gltsd(sip) != 0) { + ds_scsi_close(sip); + return (NULL); + } + + return (sip); +} + +static void * +ds_scsi_open_uscsi(disk_status_t *dsp) +{ + ds_scsi_info_t *sip; + + if ((sip = calloc(sizeof (ds_scsi_info_t), 1)) == NULL) { + (void) ds_set_errno(dsp, EDS_NOMEM); + return (NULL); + } + + return (ds_scsi_open_common(dsp, sip)); +} + +static void * +ds_scsi_open_sim(disk_status_t *dsp) +{ + ds_scsi_info_t *sip; + + if ((sip = calloc(sizeof (ds_scsi_info_t), 1)) == NULL) { + (void) ds_set_errno(dsp, EDS_NOMEM); + return (NULL); + } + + if ((sip->si_sim = dlopen(dsp->ds_path, RTLD_LAZY)) == NULL) { + (void) ds_set_errno(dsp, EDS_NO_TRANSPORT); + free(sip); + return (NULL); + } + + return (ds_scsi_open_common(dsp, sip)); +} + + +/* + * Scan for any faults. The following steps are performed: + * + * 1. If the temperature log page is supported, check the current temperature + * and threshold. If the current temperature exceeds the threshold, report + * and overtemp fault. + * + * 2. If the selftest log page is supported, check to the last completed self + * test. If the last completed test resulted in failure, report a selftest + * fault. + * + * 3. If the IE log page is supported, check to see if failure is predicted. If + * so, indicate a predictive failure fault. + * + * 4. If the IE log page is not supported, but the mode page supports report on + * request mode, then issue a REQUEST SENSE for the mode page. Indicate a + * predictive failure fault if necessary. + */ +static int +ds_scsi_scan(void *arg) +{ + ds_scsi_info_t *sip = arg; + int i; + + for (i = 0; i < NLOG_VALIDATION; i++) { + if ((sip->si_supp_log & log_validation[i].ve_supported) == 0) + continue; + + if (analyze_one_logpage(sip, &log_validation[i]) != 0) + return (-1); + } + + if (!(sip->si_supp_log & LOGPAGE_SUPP_IE) && + (sip->si_supp_mode & MODEPAGE_SUPP_IEC) && + analyze_ie_sense(sip) != 0) + return (-1); + + return (0); +} + +ds_transport_t ds_scsi_uscsi_transport = { + ds_scsi_open_uscsi, + ds_scsi_close, + ds_scsi_scan +}; + +ds_transport_t ds_scsi_sim_transport = { + ds_scsi_open_sim, + ds_scsi_close, + ds_scsi_scan +}; diff --git a/usr/src/lib/fm/libdiskstatus/common/ds_scsi.h b/usr/src/lib/fm/libdiskstatus/common/ds_scsi.h new file mode 100644 index 0000000000..6d2648f06b --- /dev/null +++ b/usr/src/lib/fm/libdiskstatus/common/ds_scsi.h @@ -0,0 +1,327 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _DS_SCSI_H +#define _DS_SCSI_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <sys/byteorder.h> +#include <sys/scsi/scsi.h> + +#include "ds_impl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(_BIT_FIELDS_LTOH) && !defined(_BIT_FIELDS_HTOL) +#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined +#endif + +/* + * Log page structures + */ +#pragma pack(1) + +typedef struct scsi_log_header { +#if defined(_BIT_FIELDS_LTOH) + uint8_t lh_code : 6, + __reserved : 2; +#else + uint8_t __reserved : 2, + lh_code : 6; +#endif + uint8_t __reserved2; + uint16_t lh_length; +} scsi_log_header_t; + +typedef struct scsi_log_parameter_header { + uint16_t lph_param; +#if defined(_BIT_FIELDS_LTOH) + uint8_t lph_lp : 1, + lph_lbin : 1, + lph_tmc : 2, + lph_etc : 1, + lph_tsd : 1, + lph_ds : 1, + lph_du : 1; +#else + uint8_t lph_du : 1, + lph_ds : 1, + lph_tsd : 1, + lph_etc : 1, + lph_tmc : 2, + lph_lbin : 1, + lph_lp : 1; +#endif + uint8_t lph_length; +} scsi_log_parameter_header_t; + +typedef struct scsi_supported_log_pages { + scsi_log_header_t slp_hdr; + uchar_t slp_pages[1]; +} scsi_supported_log_pages_t; + +typedef struct scsi_ie_log_param { + scsi_log_parameter_header_t ie_hdr; + uchar_t ie_asc; + uchar_t ie_ascq; +} scsi_ie_log_param_t; + +/* + * The SCSI-3 SPC document states that IE log page (0x2F) parameter 0 + * must have a length of at least 4 (including the length byte). + */ +#define LOGPARAM_IE_MIN_LEN 2 /* the asc and ascq fields */ + +#define INVALID_TEMPERATURE 0xff + +#define LOGPARAM_IE 0x0000 + +typedef struct scsi_temp_log_param { + scsi_log_parameter_header_t t_hdr; + uchar_t __reserved; + uchar_t t_temp; +} scsi_temp_log_param_t; + +typedef struct scsi_selftest_log_param { + scsi_log_parameter_header_t st_hdr; +#if defined(_BIT_FIELDS_LTOH) + uint8_t st_results : 4, + __reserved1 : 1, + st_testcode : 3; +#else + uint8_t st_testcode : 3, + __reserved1 : 1, + st_results : 4; +#endif + uint8_t st_number; + uint16_t st_timestamp; + uint64_t st_lba; +#if defined(_BIT_FIELDS_LTOH) + uint8_t st_sensekey : 4, + __reserved2 : 4; +#else + uint8_t __reserved2 : 4, + st_sensekey : 4; +#endif + uint8_t st_asc; + uint8_t st_ascq; + uint8_t st_vendor; +} scsi_selftest_log_param_t; + +/* The results field of the self-test log parameter */ +#define SELFTEST_OK 0x0 +#define SELFTEST_ABORT_REQUEST 0x1 +#define SELFTEST_ABORT_OTHER 0x2 +#define SELFTEST_FAILURE_INCOMPLETE 0x3 +#define SELFTEST_FAILURE_SEG_UNKNOWN 0x4 +#define SELFTEST_FAILURE_SEG_FIRST 0x5 +#define SELFTEST_FAILURE_SEG_SECOND 0x6 +#define SELFTEST_FAILURE_SEG_OTHER 0x7 +#define SELFTEST_INPROGRESS 0xf + +#define SELFTEST_COMPLETE(code) \ + ((code) == SELFTEST_OK || \ + ((code) >= SELFTEST_FAILURE_INCOMPLETE && \ + ((code) <= SELFTEST_FAILURE_SEG_OTHER))) + +#define LOGPARAM_TEMP_CURTEMP 0x0000 +#define LOGPARAM_TEMP_REFTEMP 0x0001 + +#define LOGPARAM_TEMP_LEN \ + (sizeof (scsi_temp_log_param_t) - \ + sizeof (scsi_log_parameter_header_t)) + +/* + * Mode sense/select page header information + */ +typedef struct scsi_ms_header { + struct mode_header ms_header; + struct block_descriptor ms_descriptor; +} scsi_ms_header_t; + +typedef struct scsi_ms_header_g1 { + struct mode_header_g1 ms_header; + struct block_descriptor ms_descriptor; +} scsi_ms_header_g1_t; + +typedef struct scsi_ms_hdrs { + int ms_length; + union { + scsi_ms_header_t g0; + scsi_ms_header_g1_t g1; + } ms_hdr; +} scsi_ms_hdrs_t; + +typedef struct scsi_ie_page { + struct mode_page ie_mp; +#if defined(_BIT_FIELDS_LTOH) + uint8_t ie_logerr : 1, /* Errors should be logged */ + __reserved1 : 1, + ie_test : 1, /* Enable test gen of IEs */ + ie_dexcpt : 1, /* Disable exceptions */ + ie_ewasc : 1, /* Enable warning generation */ + ie_ebf : 1, /* enable backgrnd functions */ + __reserved2 : 1, + ie_perf : 1; /* No delays during excptns */ + uint8_t ie_mrie : 4, /* Method/reporting excptons */ + __reserved3 : 4; +#else + uint8_t ie_perf : 1, /* No delays during excptons */ + __reserved2 : 1, + ie_ebf : 1, /* enable background funcs */ + ie_ewasc : 1, /* Enable warning generation */ + ie_dexcpt : 1, /* Disable exceptions */ + ie_test : 1, /* Enable test gen of IEs */ + __reserved1 : 1, + ie_logerr : 1; /* Errors should be logged */ + uint8_t __reserved3 : 4, + ie_mrie : 4; /* Method of report excptns */ +#endif + uint32_t ie_interval_timer; /* reporting interval for IEs */ + uint32_t ie_report_count; /* # of times to report an IE */ +} scsi_ie_page_t; + +#pragma pack() + +#define MODEPAGE_INFO_EXCPT_LEN (sizeof (scsi_ie_page_t)) + +#define IEC_IE_ENABLED(ies) ((ies).ie_dexcpt == 0) +#define IEC_IE_CHANGEABLE(ies) ((ies).ie_dexcpt == 1) +#define IEC_MRIE_CHANGEABLE(ies) ((ies).ie_mrie == 0xf) +#define IEC_PERF_CHANGEABLE(ies) ((ies).ie_perf == 1) +#define IEC_EWASC_CHANGEABLE(ies) ((ies).ie_ewasc == 1) +#define IEC_TEST_CHANGEABLE(ies) ((ies).ie_test == 1) +#define IEC_RPTCNT_CHANGEABLE(ies) ((ies).ie_report_count == BE_32(0xffffffff)) +#define IEC_LOGERR_CHANGEABLE(ies) ((ies).ie_logerr == 1) + +/* + * Values for the MRIE field of the informational exceptions control mode page + */ +#define IE_REPORT_NONE 0 +#define IE_REPORT_ASYNCH 1 +#define IE_REPORT_UNIT_ATTN 2 +#define IE_REPORT_RECOV_ERR_COND 3 +#define IE_REPORT_RECOV_ERR_ALWAYS 4 +#define IE_REPORT_NO_SENSE 5 +#define IE_REPORT_ON_REQUEST 6 + +/* + * Constants in support of the CONTROL MODE mode page (page 0xA) + */ +#define MODEPAGE_CTRL_MODE_LEN (sizeof (struct mode_control_scsi3)) +#define GLTSD_CHANGEABLE(chg) ((chg).gltsd == 1) + +#define LOGPAGE_SELFTEST_MIN_PARAM_CODE 0x0001 +#define LOGPAGE_SELFTEST_MAX_PARAM_CODE 0x0014 + +#define LOGPAGE_SELFTEST_PARAM_LEN \ + ((sizeof (scsi_selftest_log_param_t)) - \ + (sizeof (scsi_log_parameter_header_t))) + +/* + * Macro to extract the length of a mode sense page + * as returned by a target. + */ +#define MODESENSE_PAGE_LEN(p) (((int)((struct mode_page *)p)->length) + \ + sizeof (struct mode_page)) + +/* + * Mode Select options + */ +#define MODE_SELECT_SP 0x01 +#define MODE_SELECT_PF 0x10 + + +/* + * Mode Sense Page Control + */ +#define PC_CURRENT (0 << 6) +#define PC_CHANGEABLE (1 << 6) +#define PC_DEFAULT (2 << 6) +#define PC_SAVED (3 << 6) + +/* + * Log Sense Page Control + */ +#define PC_CUMULATIVE (1 << 6) + +/* + * LOG page codes + */ +#define LOGPAGE_SUPP_LIST 0x00 +#define LOGPAGE_TEMP 0x0d +#define LOGPAGE_SELFTEST 0x10 +#define LOGPAGE_IE 0x2f + +/* ASC constants */ +#define ASC_INVALID_OPCODE 0x20 +#define ASC_INVALID_CDB_FIELD 0x24 +#define ASC_FAILURE_PREDICTION_THRESHOLD_EXCEEDED 0x5d + +/* ASCQ constants */ +#define ASCQ_INVALID_OPCODE 0 + +/* Error tests */ +#define SCSI_INVALID_OPCODE(s, a, aq) \ + (((s) == KEY_ILLEGAL_REQUEST) && ((a) == ASC_INVALID_OPCODE) && \ + ((aq) == ASCQ_INVALID_OPCODE)) + +#define MODE_PAGE_UNSUPPORTED(s, a, aq) \ + (((s) == KEY_ILLEGAL_REQUEST) && ((a) == ASC_INVALID_CDB_FIELD)) + +/* command length to use */ +#define MODE_CMD_LEN_UNKNOWN 0 +#define MODE_CMD_LEN_6 1 +#define MODE_CMD_LEN_10 2 + +/* supported modepages bitmask */ +#define MODEPAGE_SUPP_IEC 0x1 + +/* supported logpages bitmask */ +#define LOGPAGE_SUPP_IE 0x1 +#define LOGPAGE_SUPP_TEMP 0x2 +#define LOGPAGE_SUPP_SELFTEST 0x4 + +#define MSG_BUFLEN 256 + +/* + * For SCSI commands which want to accept arbitrary length responses, we need to + * allocate an appropriate sized buffer. The maximum length is USHRT_MAX, + * because some devices return nothing if the buffer length is too big. + */ +#define MAX_BUFLEN(type) (USHRT_MAX - sizeof (type)) + +extern ds_transport_t ds_scsi_uscsi_transport; +extern ds_transport_t ds_scsi_sim_transport; + +#ifdef __cplusplus +} +#endif + +#endif /* _DS_SCSI_H */ diff --git a/usr/src/lib/fm/libdiskstatus/common/ds_scsi_sim.c b/usr/src/lib/fm/libdiskstatus/common/ds_scsi_sim.c new file mode 100644 index 0000000000..1750f67c27 --- /dev/null +++ b/usr/src/lib/fm/libdiskstatus/common/ds_scsi_sim.c @@ -0,0 +1,179 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SCSI simulator. + * + * For testing purposes, we need a way to simulate arbitrary SCSI responses. A + * completely flexible SCSI simulation language would be a large undertaking, + * given the number of possible outcomes. Instead, we opt for the simpler route + * of using a shared object which implements versions of these functions. + * + * If a shared object doesn't implement a given function, or if the function + * returns non-zero, then the simulator will provide a suitable response + * indicating the functionality isn't supported. + */ + +#include <libdiskstatus.h> + +#include "ds_scsi.h" +#include "ds_scsi_sim.h" + +static int +check_invalid_code(int ret, void *rqbuf) +{ + if (ret != 0) { + struct scsi_extended_sense *sensep = rqbuf; + + sensep->es_key = KEY_ILLEGAL_REQUEST; + sensep->es_add_len = 6; + sensep->es_code = CODE_FMT_FIXED_CURRENT; + sensep->es_add_code = ASC_INVALID_OPCODE; + sensep->es_qual_code = ASCQ_INVALID_OPCODE; + ret = -1; + } + + return (ret); +} + +typedef int (*scsi_mode_sense_f)(int, int, caddr_t, int, scsi_ms_header_t *, + void *, int *); + +int +simscsi_mode_sense(void *hdl, int page_code, int page_control, + caddr_t page_data, int page_size, scsi_ms_header_t *header, + void *rqbuf, int *rqblen) +{ + scsi_mode_sense_f dscsi_mode_sense; + int ret = -1; + + dscsi_mode_sense = (scsi_mode_sense_f)dlsym(hdl, "scsi_mode_sense"); + + if (dscsi_mode_sense != NULL) + ret = (*dscsi_mode_sense)(page_code, page_control, page_data, + page_size, header, rqbuf, rqblen); + + return (check_invalid_code(ret, rqbuf)); +} + +typedef int (*scsi_mode_sense_10_f)(int, int, caddr_t, int, + scsi_ms_header_g1_t *, void *, int *); + +int +simscsi_mode_sense_10(void *hdl, int page_code, int page_control, + caddr_t page_data, int page_size, scsi_ms_header_g1_t *header, + void *rqbuf, int *rqblen) +{ + scsi_mode_sense_10_f dscsi_mode_sense_10; + int ret = -1; + + dscsi_mode_sense_10 = (scsi_mode_sense_10_f)dlsym(hdl, + "scsi_mode_sense_10"); + + if (dscsi_mode_sense_10 != NULL) + ret = (*dscsi_mode_sense_10)(page_code, page_control, page_data, + page_size, header, rqbuf, rqblen); + + return (check_invalid_code(ret, rqbuf)); +} + +typedef int (*scsi_mode_select_f)(int, int, caddr_t, int, scsi_ms_header_t *, + void *, int *); + +int +simscsi_mode_select(void *hdl, int page_code, int options, caddr_t page_data, + int page_size, scsi_ms_header_t *header, void *rqbuf, int *rqblen) +{ + scsi_mode_select_f dscsi_mode_select; + int ret = -1; + + dscsi_mode_select = (scsi_mode_select_f)(dlsym(hdl, + "scsi_mode_select")); + + if (dscsi_mode_select != NULL) + ret = (*dscsi_mode_select)(page_code, options, page_data, + page_size, header, rqbuf, rqblen); + + return (check_invalid_code(ret, rqbuf)); +} + +typedef int (*scsi_mode_select_10_f)(int, int, caddr_t, int, + scsi_ms_header_g1_t *, void *, int *); + +int +simscsi_mode_select_10(void *hdl, int page_code, int options, + caddr_t page_data, int page_size, scsi_ms_header_g1_t *header, + void *rqbuf, int *rqblen) +{ + scsi_mode_select_10_f dscsi_mode_select_10; + int ret = -1; + + dscsi_mode_select_10 = (scsi_mode_select_10_f)dlsym(hdl, + "scsi_mode_select_10"); + + if (dscsi_mode_select_10 != NULL) + ret = (*dscsi_mode_select_10)(page_code, options, page_data, + page_size, header, rqbuf, rqblen); + + return (check_invalid_code(ret, rqbuf)); +} + +typedef int (*scsi_log_sense_f)(int, int, caddr_t, int, void *, int *); + +int +simscsi_log_sense(void *hdl, int page_code, int page_control, + caddr_t page_data, int page_size, void *rqbuf, int *rqblen) +{ + scsi_log_sense_f dscsi_log_sense; + int ret = -1; + + dscsi_log_sense = (scsi_log_sense_f)dlsym(hdl, "scsi_log_sense"); + + if (dscsi_log_sense != NULL) + ret = (*dscsi_log_sense)(page_code, page_control, page_data, + page_size, rqbuf, rqblen); + + return (check_invalid_code(ret, rqbuf)); +} + +typedef int (*scsi_request_sense_f)(caddr_t, int, void *, int *); + +int +simscsi_request_sense(void *hdl, caddr_t buf, int buflen, + void *rqbuf, int *rqblen) +{ + scsi_request_sense_f dscsi_request_sense; + int ret = -1; + + dscsi_request_sense = (scsi_request_sense_f)dlsym(hdl, + "scsi_request_sense"); + + if (dscsi_request_sense != NULL) + ret = (*dscsi_request_sense)(buf, buflen, rqbuf, rqblen); + + return (check_invalid_code(ret, rqbuf)); +} diff --git a/usr/src/lib/fm/libdiskstatus/common/ds_scsi_sim.h b/usr/src/lib/fm/libdiskstatus/common/ds_scsi_sim.h new file mode 100644 index 0000000000..7190dd97ff --- /dev/null +++ b/usr/src/lib/fm/libdiskstatus/common/ds_scsi_sim.h @@ -0,0 +1,50 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _DS_SCSI_SIM_H +#define _DS_SCSI_SIM_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +int simscsi_mode_sense(void *, int, int, caddr_t, int, scsi_ms_header_t *, + void *, int *); +int simscsi_mode_sense_10(void *, int, int, caddr_t, int, + scsi_ms_header_g1_t *, void *, int *); +int simscsi_mode_select(void *, int, int, caddr_t, int, + scsi_ms_header_t *, void *, int *); +int simscsi_mode_select_10(void *, int, int, caddr_t, int, + scsi_ms_header_g1_t *, void *, int *); +int simscsi_log_sense(void *, int, int, caddr_t, int, void *, int *); +int simscsi_request_sense(void *, caddr_t, int, void *, int *); + +#ifdef __cplusplus +} +#endif + +#endif /* _DS_SCSI_SIM_H */ diff --git a/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/scsi_util.c b/usr/src/lib/fm/libdiskstatus/common/ds_scsi_uscsi.c index a87fc34441..81f62ad0cf 100644 --- a/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/scsi_util.c +++ b/usr/src/lib/fm/libdiskstatus/common/ds_scsi_uscsi.c @@ -20,12 +20,18 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" +/* + * This file contains routines for sending and receiving SCSI commands. The + * higher level logic is contained in ds_scsi.c. + */ + +#include <assert.h> #include <sys/types.h> #include <sys/param.h> #include <inttypes.h> @@ -40,11 +46,8 @@ #include <stropts.h> #include <alloca.h> -#include "util.h" -#include "sfx4500-disk.h" -#include "scsi_util.h" - -extern log_class_t g_verbose; +#include "ds_scsi.h" +#include "ds_scsi_uscsi.h" #define MSGBUFLEN 64 #define USCSI_DEFAULT_TIMEOUT 45 @@ -56,6 +59,23 @@ static void scsi_print_extended_sense(struct scsi_extended_sense *rq, int rqlen); static void scsi_print_descr_sense(struct scsi_descr_sense_hdr *rq, int rqlen); +typedef struct slist { + char *str; + int value; +} slist_t; + +static char * +find_string(slist_t *slist, int match_value) +{ + for (; slist->str != NULL; slist++) { + if (slist->value == match_value) { + return (slist->str); + } + } + + return ((char *)NULL); +} + /* * Strings for printing mode sense page control values */ @@ -611,6 +631,10 @@ static struct _scsi_asq_key_strings { { 0xffff, 0xffff, NULL } }; +/* + * Given an asc (Additional Sense Code) and ascq (Additional Sense Code + * Qualifier), return a string describing the error information. + */ static char * scsi_util_asc_ascq_name(uint_t asc, uint_t ascq, char *buf, int buflen) { @@ -628,38 +652,20 @@ scsi_util_asc_ascq_name(uint_t asc, uint_t ascq, char *buf, int buflen) return (buf); } -const char * -scsi_asc_ascq_string(uint_t asc, uint_t ascq) -{ - int i = 0; - - while (extended_sense_list[i].asc != 0xffff) { - if ((asc == extended_sense_list[i].asc) && - ((ascq == extended_sense_list[i].ascq) || - (extended_sense_list[i].ascq == 0xffff))) { - return (extended_sense_list[i].message); - } - i++; - } - return (NULL); -} - +/* + * Dumps detailed information about a particular SCSI error condition. + */ static void -scsi_printerr(ucmd, rq, rqlen) - struct uscsi_cmd *ucmd; - struct scsi_extended_sense *rq; - int rqlen; +scsi_printerr(struct uscsi_cmd *ucmd, struct scsi_extended_sense *rq, int rqlen) { diskaddr_t blkno; - struct scsi_descr_sense_hdr *sdsp = - (struct scsi_descr_sense_hdr *)rq; + struct scsi_descr_sense_hdr *sdsp = (struct scsi_descr_sense_hdr *)rq; char msgbuf[MSGBUFLEN]; - if (find_string(sensekey_strings, rq->es_key) == NULL) { - log_warn("Unknown error"); - } + if (find_string(sensekey_strings, rq->es_key) == NULL) + dprintf("unknown error"); - log_warn("During %s:", + dprintf("during %s:", find_string(scsi_cmdname_strings, ucmd->uscsi_cdb[0])); /* @@ -670,20 +676,17 @@ scsi_printerr(ucmd, rq, rqlen) switch (rq->es_code) { case CODE_FMT_DESCR_CURRENT: case CODE_FMT_DESCR_DEFERRED: - blkno = - (diskaddr_t)scsi_extract_sense_info_descr(sdsp, rqlen); + blkno = (diskaddr_t)scsi_extract_sense_info_descr(sdsp, rqlen); if (blkno != (diskaddr_t)-1) - log_warn(": block %lld (0x%llx)", blkno, blkno); - - log_warn("\n"); - - log_warn("ASC: 0x%x ASCQ: 0x%x (%s)\n", - sdsp->ds_add_code, - sdsp->ds_qual_code, + dprintf(": block %lld (0x%llx)", blkno, blkno); + dprintf("\n"); + dprintf("ASC: 0x%x ASCQ: 0x%x (%s)\n", + sdsp->ds_add_code, sdsp->ds_qual_code, scsi_util_asc_ascq_name(sdsp->ds_add_code, - sdsp->ds_qual_code, msgbuf, MSGBUFLEN)); + sdsp->ds_qual_code, msgbuf, MSGBUFLEN)); break; + case CODE_FMT_FIXED_CURRENT: case CODE_FMT_FIXED_DEFERRED: default: @@ -691,28 +694,26 @@ scsi_printerr(ucmd, rq, rqlen) blkno = (rq->es_info_1 << 24) | (rq->es_info_2 << 16) | (rq->es_info_3 << 8) | rq->es_info_4; - log_warn(": block %lld (0x%llx)", blkno, blkno); + dprintf(": block %lld (0x%llx)", blkno, blkno); } - - log_warn("\n"); - + dprintf("\n"); if (rq->es_add_len >= 6) { - log_warn("ASC: 0x%x ASCQ: 0x%x (%s)\n", + dprintf("ASC: 0x%x ASCQ: 0x%x (%s)\n", rq->es_add_code, rq->es_qual_code, scsi_util_asc_ascq_name(rq->es_add_code, - rq->es_qual_code, msgbuf, MSGBUFLEN)); + rq->es_qual_code, msgbuf, MSGBUFLEN)); } break; } if (rq->es_key == KEY_ILLEGAL_REQUEST) { - log_dump(MM_SCSI, "cmd:", (caddr_t)ucmd, + ddump("cmd:", (caddr_t)ucmd, sizeof (struct uscsi_cmd)); - log_dump(MM_SCSI, "cdb:", (caddr_t)ucmd->uscsi_cdb, + ddump("cdb:", (caddr_t)ucmd->uscsi_cdb, ucmd->uscsi_cdblen); } - log_dump(MM_SCSI, "sense:", (caddr_t)rq, rqlen); + ddump("sense:", (caddr_t)rq, rqlen); switch (rq->es_code) { case CODE_FMT_DESCR_CURRENT: @@ -727,49 +728,10 @@ scsi_printerr(ucmd, rq, rqlen) } } -void -scsi_translate_error(struct scsi_extended_sense *rq, uint_t *skeyp, - uint_t *ascp, uint_t *ascqp) -{ - struct scsi_descr_sense_hdr *sdsp = - (struct scsi_descr_sense_hdr *)rq; - - *skeyp = rq->es_key; - - /* - * Get asc, ascq and info field from sense data. There are two - * possible formats (fixed sense data and descriptor sense data) - * depending on the value of es_code. - */ - switch (rq->es_code) { - case CODE_FMT_DESCR_CURRENT: - case CODE_FMT_DESCR_DEFERRED: - - *ascp = sdsp->ds_add_code; - *ascqp = sdsp->ds_qual_code; - break; - - case CODE_FMT_FIXED_CURRENT: - case CODE_FMT_FIXED_DEFERRED: - default: - - if (rq->es_add_len >= 6) { - *ascp = rq->es_add_code; - *ascqp = rq->es_qual_code; - } else { - *ascp = 0xff; - *ascqp = 0xff; - } - break; - } -} - - /* - * Retrieve "information" field from descriptor format - * sense data. Iterates through each sense descriptor - * looking for the information descriptor and returns - * the information field from that descriptor. + * Retrieve "information" field from descriptor format sense data. Iterates + * through each sense descriptor looking for the information descriptor and + * returns the information field from that descriptor. */ static diskaddr_t scsi_extract_sense_info_descr(struct scsi_descr_sense_hdr *sdsp, int rqlen) @@ -788,27 +750,25 @@ scsi_extract_sense_info_descr(struct scsi_descr_sense_hdr *sdsp, int rqlen) /* * The first descriptor will immediately follow the header */ - descr_offset = (uint8_t *)(sdsp+1); /* Pointer arithmetic */ + descr_offset = (uint8_t *)(sdsp+1); /* * Calculate the amount of valid sense data */ valid_sense_length = MIN((sizeof (struct scsi_descr_sense_hdr) + - sdsp->ds_addl_sense_length), - rqlen); + sdsp->ds_addl_sense_length), rqlen); /* - * Iterate through the list of descriptors, stopping when we - * run out of sense data + * Iterate through the list of descriptors, stopping when we run out of + * sense data */ while ((descr_offset + sizeof (struct scsi_information_sense_descr)) <= (uint8_t *)sdsp + valid_sense_length) { /* - * Check if this is an information descriptor. We can - * use the scsi_information_sense_descr structure as a - * template since the first two fields are always the - * same + * Check if this is an information descriptor. We can use the + * scsi_information_sense_descr structure as a template since + * the first two fields are always the same */ isd = (struct scsi_information_sense_descr *)descr_offset; if (isd->isd_descr_type == DESCR_INFORMATION) { @@ -819,21 +779,21 @@ scsi_extract_sense_info_descr(struct scsi_descr_sense_hdr *sdsp, int rqlen) */ result = (((diskaddr_t)isd->isd_information[0] << 56) | - ((diskaddr_t)isd->isd_information[1] << 48) | - ((diskaddr_t)isd->isd_information[2] << 40) | - ((diskaddr_t)isd->isd_information[3] << 32) | - ((diskaddr_t)isd->isd_information[4] << 24) | - ((diskaddr_t)isd->isd_information[5] << 16) | - ((diskaddr_t)isd->isd_information[6] << 8) | - ((diskaddr_t)isd->isd_information[7])); + ((diskaddr_t)isd->isd_information[1] << 48) | + ((diskaddr_t)isd->isd_information[2] << 40) | + ((diskaddr_t)isd->isd_information[3] << 32) | + ((diskaddr_t)isd->isd_information[4] << 24) | + ((diskaddr_t)isd->isd_information[5] << 16) | + ((diskaddr_t)isd->isd_information[6] << 8) | + ((diskaddr_t)isd->isd_information[7])); break; } /* - * Get pointer to the next descriptor. The "additional - * length" field holds the length of the descriptor except - * for the "type" and "additional length" fields, so - * we need to add 2 to get the total length. + * Get pointer to the next descriptor. The "additional length" + * field holds the length of the descriptor except for the + * "type" and "additional length" fields, so we need to add 2 to + * get the total length. */ descr_offset += (isd->isd_addl_length + 2); } @@ -848,21 +808,21 @@ static void scsi_print_extended_sense(struct scsi_extended_sense *rq, int rqlen) { static char *scsi_extended_sense_labels[] = { - "Request sense valid: ", - "Error class and code: ", - "Segment number: ", - "Filemark: ", - "End-of-medium: ", - "Incorrect length indicator: ", - "Sense key: ", - "Information field: ", - "Additional sense length: ", - "Command-specific information: ", - "Additional sense code: ", - "Additional sense code qualifier: ", - "Field replaceable unit code: ", - "Sense-key specific: ", - "Additional sense bytes: " + "Request sense valid: ", + "Error class and code: ", + "Segment number: ", + "Filemark: ", + "End-of-medium: ", + "Incorrect length indicator: ", + "Sense key: ", + "Information field: ", + "Additional sense length: ", + "Command-specific information: ", + "Additional sense code: ", + "Additional sense code qualifier: ", + "Field replaceable unit code: ", + "Sense-key specific: ", + "Additional sense bytes: " }; char **p = scsi_extended_sense_labels; @@ -876,78 +836,73 @@ scsi_print_extended_sense(struct scsi_extended_sense *rq, int rqlen) return; } - log_msg(MM_SCSI, "\n%s%s\n", *p++, rq->es_valid ? "yes" : "no"); - log_msg(MM_SCSI, "%s0x%02x\n", *p++, (rq->es_class << 4) + rq->es_code); - log_msg(MM_SCSI, "%s%d\n", *p++, rq->es_segnum); - log_msg(MM_SCSI, "%s%s\n", *p++, rq->es_filmk ? "yes" : "no"); - log_msg(MM_SCSI, "%s%s\n", *p++, rq->es_eom ? "yes" : "no"); - log_msg(MM_SCSI, "%s%s\n", *p++, rq->es_ili ? "yes" : "no"); - log_msg(MM_SCSI, "%s%d\n", *p++, rq->es_key); - - log_msg(MM_SCSI, "%s0x%02x 0x%02x 0x%02x 0x%02x\n", *p++, rq->es_info_1, - rq->es_info_2, rq->es_info_3, rq->es_info_4); - log_msg(MM_SCSI, "%s%d\n", *p++, rq->es_add_len); - log_msg(MM_SCSI, "%s0x%02x 0x%02x 0x%02x 0x%02x\n", *p++, - rq->es_cmd_info[0], rq->es_cmd_info[1], rq->es_cmd_info[2], - rq->es_cmd_info[3]); - log_msg(MM_SCSI, "%s0x%02x = %d\n", *p++, rq->es_add_code, - rq->es_add_code); - log_msg(MM_SCSI, "%s0x%02x = %d\n", *p++, rq->es_qual_code, - rq->es_qual_code); - log_msg(MM_SCSI, "%s%d\n", *p++, rq->es_fru_code); - log_msg(MM_SCSI, "%s0x%02x 0x%02x 0x%02x\n", *p++, - rq->es_skey_specific[0], rq->es_skey_specific[1], - rq->es_skey_specific[2]); + dprintf("\n%s%s\n", *p++, rq->es_valid ? "yes" : "no"); + dprintf("%s0x%02x\n", *p++, (rq->es_class << 4) + rq->es_code); + dprintf("%s%d\n", *p++, rq->es_segnum); + dprintf("%s%s\n", *p++, rq->es_filmk ? "yes" : "no"); + dprintf("%s%s\n", *p++, rq->es_eom ? "yes" : "no"); + dprintf("%s%s\n", *p++, rq->es_ili ? "yes" : "no"); + dprintf("%s%d\n", *p++, rq->es_key); + + dprintf("%s0x%02x 0x%02x 0x%02x 0x%02x\n", *p++, rq->es_info_1, + rq->es_info_2, rq->es_info_3, rq->es_info_4); + dprintf("%s%d\n", *p++, rq->es_add_len); + dprintf("%s0x%02x 0x%02x 0x%02x 0x%02x\n", *p++, + rq->es_cmd_info[0], rq->es_cmd_info[1], rq->es_cmd_info[2], + rq->es_cmd_info[3]); + dprintf("%s0x%02x = %d\n", *p++, rq->es_add_code, + rq->es_add_code); + dprintf("%s0x%02x = %d\n", *p++, rq->es_qual_code, + rq->es_qual_code); + dprintf("%s%d\n", *p++, rq->es_fru_code); + dprintf("%s0x%02x 0x%02x 0x%02x\n", *p++, + rq->es_skey_specific[0], rq->es_skey_specific[1], + rq->es_skey_specific[2]); if (rqlen >= sizeof (*rq)) { - log_msg(MM_SCSI, "%s0x%02x 0x%02x%s\n", *p, rq->es_add_info[0], - rq->es_add_info[1], (rqlen > sizeof (*rq)) ? " ..." : ""); + dprintf("%s0x%02x 0x%02x%s\n", *p, rq->es_add_info[0], + rq->es_add_info[1], (rqlen > sizeof (*rq)) ? " ..." : ""); } - log_msg(MM_SCSI, "\n"); + dprintf("\n"); } /* * Display the full descriptor sense data as returned by the device */ - static void scsi_print_descr_sense(struct scsi_descr_sense_hdr *rq, int rqlen) { -/* - * Labels for the various fields of the scsi_descr_sense_hdr structure - */ + /* + * Labels for the various fields of the scsi_descr_sense_hdr structure + */ static char *scsi_descr_sense_labels[] = { - "Error class and code: ", - "Sense key: ", - "Additional sense length: ", - "Additional sense code: ", - "Additional sense code qualifier: ", - "Additional sense bytes: " + "Error class and code: ", + "Sense key: ", + "Additional sense length: ", + "Additional sense code: ", + "Additional sense code qualifier: ", + "Additional sense bytes: " }; struct scsi_information_sense_descr *isd; uint8_t *descr_offset; - int valid_sense_length; - char **p = scsi_descr_sense_labels; - + int valid_sense_length; + char **p = scsi_descr_sense_labels; - if (rqlen < sizeof (struct scsi_descr_sense_hdr)) { - /* - * target must return at least 8 bytes of data - */ + /* Target must return at least 8 bytes of data */ + if (rqlen < sizeof (struct scsi_descr_sense_hdr)) return; - } /* Print descriptor sense header */ - log_msg(MM_SCSI, "%s0x%02x\n", *p++, (rq->ds_class << 4) + rq->ds_code); - log_msg(MM_SCSI, "%s%d\n", *p++, rq->ds_key); + dprintf("%s0x%02x\n", *p++, (rq->ds_class << 4) + rq->ds_code); + dprintf("%s%d\n", *p++, rq->ds_key); - log_msg(MM_SCSI, "%s%d\n", *p++, rq->ds_addl_sense_length); - log_msg(MM_SCSI, "%s0x%02x = %d\n", *p++, rq->ds_add_code, + dprintf("%s%d\n", *p++, rq->ds_addl_sense_length); + dprintf("%s0x%02x = %d\n", *p++, rq->ds_add_code, rq->ds_add_code); - log_msg(MM_SCSI, "%s0x%02x = %d\n", *p++, rq->ds_qual_code, + dprintf("%s0x%02x = %d\n", *p++, rq->ds_qual_code, rq->ds_qual_code); - log_msg(MM_SCSI, "\n"); + dprintf("\n"); /* * Now print any sense descriptors. The first descriptor will @@ -960,7 +915,7 @@ scsi_print_descr_sense(struct scsi_descr_sense_hdr *rq, int rqlen) */ valid_sense_length = MIN((sizeof (struct scsi_descr_sense_hdr) + - rq->ds_addl_sense_length), rqlen); + rq->ds_addl_sense_length), rqlen); /* * Iterate through the list of descriptors, stopping when we @@ -983,14 +938,14 @@ scsi_print_descr_sense(struct scsi_descr_sense_hdr *rq, int rqlen) information = (((uint64_t)isd->isd_information[0] << 56) | - ((uint64_t)isd->isd_information[1] << 48) | - ((uint64_t)isd->isd_information[2] << 40) | - ((uint64_t)isd->isd_information[3] << 32) | - ((uint64_t)isd->isd_information[4] << 24) | - ((uint64_t)isd->isd_information[5] << 16) | - ((uint64_t)isd->isd_information[6] << 8) | - ((uint64_t)isd->isd_information[7])); - log_msg(MM_SCSI, "Information field: " + ((uint64_t)isd->isd_information[1] << 48) | + ((uint64_t)isd->isd_information[2] << 40) | + ((uint64_t)isd->isd_information[3] << 32) | + ((uint64_t)isd->isd_information[4] << 24) | + ((uint64_t)isd->isd_information[5] << 16) | + ((uint64_t)isd->isd_information[6] << 8) | + ((uint64_t)isd->isd_information[7])); + dprintf("Information field: " "%0" PRIx64 "\n", information); break; } @@ -1001,14 +956,14 @@ scsi_print_descr_sense(struct scsi_descr_sense_hdr *rq, int rqlen) cmd_specific = (((uint64_t)c->css_cmd_specific_info[0] << 56) | - ((uint64_t)c->css_cmd_specific_info[1] << 48) | - ((uint64_t)c->css_cmd_specific_info[2] << 40) | - ((uint64_t)c->css_cmd_specific_info[3] << 32) | - ((uint64_t)c->css_cmd_specific_info[4] << 24) | - ((uint64_t)c->css_cmd_specific_info[5] << 16) | - ((uint64_t)c->css_cmd_specific_info[6] << 8) | - ((uint64_t)c->css_cmd_specific_info[7])); - log_msg(MM_SCSI, "Command-specific information: " + ((uint64_t)c->css_cmd_specific_info[1] << 48) | + ((uint64_t)c->css_cmd_specific_info[2] << 40) | + ((uint64_t)c->css_cmd_specific_info[3] << 32) | + ((uint64_t)c->css_cmd_specific_info[4] << 24) | + ((uint64_t)c->css_cmd_specific_info[5] << 16) | + ((uint64_t)c->css_cmd_specific_info[6] << 8) | + ((uint64_t)c->css_cmd_specific_info[7])); + dprintf("Command-specific information: " "%0" PRIx64 "\n", cmd_specific); break; } @@ -1016,7 +971,7 @@ scsi_print_descr_sense(struct scsi_descr_sense_hdr *rq, int rqlen) struct scsi_sk_specific_sense_descr *ssd = (struct scsi_sk_specific_sense_descr *)isd; uint8_t *sk_spec_ptr = (uint8_t *)&ssd->sss_data; - log_msg(MM_SCSI, "Sense-key specific: " + dprintf("Sense-key specific: " "0x%02x 0x%02x 0x%02x\n", sk_spec_ptr[0], sk_spec_ptr[1], sk_spec_ptr[2]); break; @@ -1024,14 +979,14 @@ scsi_print_descr_sense(struct scsi_descr_sense_hdr *rq, int rqlen) case DESCR_FRU: { struct scsi_fru_sense_descr *fsd = (struct scsi_fru_sense_descr *)isd; - log_msg(MM_SCSI, "Field replaceable unit code: " + dprintf("Field replaceable unit code: " "%d\n", fsd->fs_fru_code); break; } case DESCR_BLOCK_COMMANDS: { struct scsi_block_cmd_sense_descr *bsd = (struct scsi_block_cmd_sense_descr *)isd; - log_msg(MM_SCSI, "Incorrect length indicator: " + dprintf("Incorrect length indicator: " "%s\n", bsd->bcs_ili ? "yes" : "no"); break; } @@ -1049,7 +1004,7 @@ scsi_print_descr_sense(struct scsi_descr_sense_hdr *rq, int rqlen) descr_offset += (isd->isd_addl_length + 2); } - log_msg(MM_SCSI, "\n"); + dprintf("\n"); } static int @@ -1076,32 +1031,25 @@ uscsi_timeout(void) } /* - * Execute a command and determine the result. - * Uses the "uscsi" ioctl interface, which is - * fully supported. + * Execute a command and determine the result. Uses the "uscsi" ioctl + * interface, which is fully supported. * - * If the user wants request sense data to be returned - * in case of error then , the "uscsi_cmd" structure - * should have the request sense buffer allocated in + * If the user wants request sense data to be returned in case of error then , + * the "uscsi_cmd" structure should have the request sense buffer allocated in * uscsi_rqbuf. - * */ static int -uscsi_cmd(int fd, struct uscsi_cmd *ucmd, int flags, void *rqbuf, int *rqlen) +uscsi_cmd(int fd, struct uscsi_cmd *ucmd, void *rqbuf, int *rqlen) { - struct scsi_extended_sense *rq; - int status; + struct scsi_extended_sense *rq; + int status; /* * Set function flags for driver. */ ucmd->uscsi_flags = USCSI_ISOLATE; - if (flags & F_SILENT) { + if (!ds_debug) ucmd->uscsi_flags |= USCSI_SILENT; - } - if (flags & F_RQENABLE) { - ucmd->uscsi_flags |= USCSI_RQENABLE; - } /* * If this command will perform a read, set the USCSI_READ flag @@ -1122,9 +1070,12 @@ uscsi_cmd(int fd, struct uscsi_cmd *ucmd, int flags, void *rqbuf, int *rqlen) case SCMD_MODE_SELECT: case SCMD_MODE_SELECT_G1: - /*LINTED*/ + /* LINTED */ ucmd->uscsi_flags |= USCSI_WRITE; break; + default: + assert(0); + break; } } @@ -1134,13 +1085,14 @@ uscsi_cmd(int fd, struct uscsi_cmd *ucmd, int flags, void *rqbuf, int *rqlen) /* * Set up Request Sense buffer */ - ucmd->uscsi_flags |= USCSI_RQENABLE; if (ucmd->uscsi_rqbuf == NULL) { ucmd->uscsi_rqbuf = rqbuf; ucmd->uscsi_rqlen = *rqlen; ucmd->uscsi_rqresid = *rqlen; } + if (ucmd->uscsi_rqbuf) + ucmd->uscsi_flags |= USCSI_RQENABLE; ucmd->uscsi_rqstatus = IMPOSSIBLE_SCSI_STATUS; if (ucmd->uscsi_rqbuf != NULL && ucmd->uscsi_rqlen > 0) @@ -1150,42 +1102,39 @@ uscsi_cmd(int fd, struct uscsi_cmd *ucmd, int flags, void *rqbuf, int *rqlen) * Execute the ioctl */ status = ioctl(fd, USCSICMD, ucmd); - if (status == 0 && ucmd->uscsi_status == 0) { + if (status == 0 && ucmd->uscsi_status == 0) return (status); - } /* - * If an automatic Request Sense gave us valid - * info about the error, we may be able to use - * that to print a reasonable error msg. + * If an automatic Request Sense gave us valid info about the error, we + * may be able to use that to print a reasonable error msg. */ if (ucmd->uscsi_rqstatus == IMPOSSIBLE_SCSI_STATUS) { - log_msg(MM_SCSI, "No request sense for command %s\n", - find_string(scsi_cmdname_strings, - ucmd->uscsi_cdb[0])); + dprintf("No request sense for command %s\n", + find_string(scsi_cmdname_strings, + ucmd->uscsi_cdb[0])); return (-1); } if (ucmd->uscsi_rqstatus != STATUS_GOOD) { - log_msg(MM_SCSI, "Request sense status for command %s: 0x%x\n", + dprintf("Request sense status for command %s: 0x%x\n", find_string(scsi_cmdname_strings, ucmd->uscsi_cdb[0]), ucmd->uscsi_rqstatus); - return (-1); } + rq = (struct scsi_extended_sense *)ucmd->uscsi_rqbuf; *rqlen = ucmd->uscsi_rqlen - ucmd->uscsi_rqresid; - if ((((int)rq->es_add_len) + 8) < MIN_REQUEST_SENSE_LEN || - rq->es_class != CLASS_EXTENDED_SENSE || - *rqlen < MIN_REQUEST_SENSE_LEN) { - log_msg(MM_SCSI, - "Request sense for command %s failed\n", - find_string(scsi_cmdname_strings, - ucmd->uscsi_cdb[0])); + if ((((int)rq->es_add_len) + 8) < MIN_REQUEST_SENSE_LEN || + rq->es_class != CLASS_EXTENDED_SENSE || + *rqlen < MIN_REQUEST_SENSE_LEN) { + dprintf("Request sense for command %s failed\n", + find_string(scsi_cmdname_strings, + ucmd->uscsi_cdb[0])); - log_msg(MM_SCSI, "Sense data:\n"); - log_dump(MM_SCSI, NULL, (caddr_t)rqbuf, *rqlen); + dprintf("Sense data:\n"); + ddump(NULL, (caddr_t)rqbuf, *rqlen); return (-1); } @@ -1200,97 +1149,78 @@ uscsi_cmd(int fd, struct uscsi_cmd *ucmd, int flags, void *rqbuf, int *rqlen) if (ucmd->uscsi_cdb[0] == SCMD_MODE_SELECT || ucmd->uscsi_cdb[0] == SCMD_MODE_SELECT_G1) { if (rq->es_key == KEY_RECOVERABLE_ERROR && - rq->es_add_code == ROUNDED_PARAMETER && - rq->es_qual_code == 0) { - return (0); + rq->es_add_code == ROUNDED_PARAMETER && + rq->es_qual_code == 0) { + return (0); } } - if (!(flags & F_SILENT)) { + if (ds_debug) scsi_printerr(ucmd, rq, *rqlen); - } - if ((rq->es_key != KEY_RECOVERABLE_ERROR) || (flags & F_ALLERRS)) { + if (rq->es_key != KEY_RECOVERABLE_ERROR) return (-1); - } return (0); } int uscsi_request_sense(int fd, caddr_t buf, int buflen, void *rqbuf, int *rqblen) { - struct uscsi_cmd ucmd; - union scsi_cdb cdb; - int status; + struct uscsi_cmd ucmd; + union scsi_cdb cdb; + int status; - /* - * Build and execute the uscsi ioctl - */ (void) memset(buf, 0, buflen); - (void) memset((char *)&ucmd, 0, sizeof (ucmd)); - (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb)); + (void) memset(&ucmd, 0, sizeof (ucmd)); + (void) memset(&cdb, 0, sizeof (union scsi_cdb)); cdb.scc_cmd = SCMD_REQUEST_SENSE; FORMG0COUNT(&cdb, (uchar_t)buflen); ucmd.uscsi_cdb = (caddr_t)&cdb; ucmd.uscsi_cdblen = CDB_GROUP0; ucmd.uscsi_bufaddr = buf; ucmd.uscsi_buflen = buflen; - status = uscsi_cmd(fd, &ucmd, - (g_verbose & MM_SCSI) ? F_NORMAL : F_SILENT, rqbuf, rqblen); - if (status) { - log_msg(MM_SCSI, "Request sense failed\n"); - } + status = uscsi_cmd(fd, &ucmd, rqbuf, rqblen); + if (status) + dprintf("Request sense failed\n"); if (status == 0) - log_dump(MM_SCSI, "Request Sense data:", buf, buflen); + ddump("Request Sense data:", buf, buflen); return (status); } - /* - * Execute a uscsi mode sense command. - * This can only be used to return one page at a time. - * Return the mode header/block descriptor and the actual - * page data separately - this allows us to support - * devices which return either 0 or 1 block descriptors. - * Whatever a device gives us in the mode header/block descriptor - * will be returned to it upon subsequent mode selects. + * Execute a uscsi mode sense command. This can only be used to return one page + * at a time. Return the mode header/block descriptor and the actual page data + * separately - this allows us to support devices which return either 0 or 1 + * block descriptors. Whatever a device gives us in the mode header/block + * descriptor will be returned to it upon subsequent mode selects. */ int uscsi_mode_sense(int fd, int page_code, int page_control, caddr_t page_data, int page_size, struct scsi_ms_header *header, void *rqbuf, int *rqblen) { - caddr_t mode_sense_buf; - struct mode_header *hdr; - struct mode_page *pg; - int nbytes; - struct uscsi_cmd ucmd; - union scsi_cdb cdb; - int status; - int maximum; - char *pc; - - dm_assert(page_size >= 0 && page_size < 256); - dm_assert(page_control == PC_CURRENT || - page_control == PC_CHANGEABLE || - page_control == PC_DEFAULT || - page_control == PC_SAVED); - /* - * Allocate a buffer for the mode sense headers - * and mode sense data itself. - */ - nbytes = sizeof (struct scsi_ms_header) + page_size; + caddr_t mode_sense_buf; + struct mode_header *hdr; + struct mode_page *pg; + int nbytes; + struct uscsi_cmd ucmd; + union scsi_cdb cdb; + int status; + int maximum; + char *pc; + + assert(page_size >= 0 && page_size < 256); + assert(page_control == PC_CURRENT || page_control == PC_CHANGEABLE || + page_control == PC_DEFAULT || page_control == PC_SAVED); - if ((mode_sense_buf = alloca((uint_t)nbytes)) == NULL) { - log_warn("cannot alloca %d bytes\n", nbytes); - return (-1); - } + nbytes = sizeof (struct scsi_ms_header) + page_size; + mode_sense_buf = alloca((uint_t)nbytes); /* * Build and execute the uscsi ioctl */ (void) memset(mode_sense_buf, 0, nbytes); - (void) memset((char *)&ucmd, 0, sizeof (ucmd)); - (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb)); + (void) memset(&ucmd, 0, sizeof (ucmd)); + (void) memset(&cdb, 0, sizeof (union scsi_cdb)); cdb.scc_cmd = SCMD_MODE_SENSE; FORMG0COUNT(&cdb, (uchar_t)nbytes); cdb.cdb_opaque[2] = page_control | page_code; @@ -1298,139 +1228,116 @@ uscsi_mode_sense(int fd, int page_code, int page_control, caddr_t page_data, ucmd.uscsi_cdblen = CDB_GROUP0; ucmd.uscsi_bufaddr = mode_sense_buf; ucmd.uscsi_buflen = nbytes; - status = uscsi_cmd(fd, &ucmd, - (g_verbose & MM_SCSI) ? F_NORMAL : F_SILENT, rqbuf, rqblen); + status = uscsi_cmd(fd, &ucmd, rqbuf, rqblen); if (status) { - log_msg(MM_SCSI, "Mode sense page 0x%x failed\n", page_code); + dprintf("Mode sense page 0x%x failed\n", page_code); return (-1); } - log_dump(MM_SCSI, "RAW MODE SENSE BUFFER", mode_sense_buf, nbytes); + ddump("RAW MODE SENSE BUFFER", mode_sense_buf, nbytes); /* - * Verify that the returned data looks reasonable, - * find the actual page data, and copy it into the - * user's buffer. Copy the mode_header and block_descriptor - * into the header structure, which can then be used to + * Verify that the returned data looks reasonable, find the actual page + * data, and copy it into the user's buffer. Copy the mode_header and + * block_descriptor into the header structure, which can then be used to * return the same data to the drive when issuing a mode select. */ hdr = (struct mode_header *)mode_sense_buf; (void) memset((caddr_t)header, 0, sizeof (struct scsi_ms_header)); if (hdr->bdesc_length != sizeof (struct block_descriptor) && - hdr->bdesc_length != 0) { - - log_msg(MM_SCSI, "\ -\nMode sense page 0x%x: block descriptor length %d incorrect\n", - page_code, hdr->bdesc_length); - log_dump(MM_SCSI, "Mode sense:", mode_sense_buf, nbytes); + hdr->bdesc_length != 0) { + dprintf("\nMode sense page 0x%x: block descriptor " + "length %d incorrect\n", page_code, hdr->bdesc_length); + ddump("Mode sense:", mode_sense_buf, nbytes); return (-1); } (void) memcpy((caddr_t)header, mode_sense_buf, - (int)(MODE_HEADER_LENGTH + hdr->bdesc_length)); + (int)(MODE_HEADER_LENGTH + hdr->bdesc_length)); pg = (struct mode_page *)((ulong_t)mode_sense_buf + - MODE_HEADER_LENGTH + hdr->bdesc_length); + MODE_HEADER_LENGTH + hdr->bdesc_length); - if (page_code == MODEPAGE_ALLPAGES) { /* special case */ + if (page_code == MODEPAGE_ALLPAGES) { + /* special case */ (void) memcpy(page_data, (caddr_t)pg, - (hdr->length + sizeof (header->mode_header.length)) - + (hdr->length + sizeof (header->ms_header.length)) - (MODE_HEADER_LENGTH + hdr->bdesc_length)); pc = find_string(page_control_strings, page_control); - log_msg(MM_SCSI, "\nMode sense page 0x%x (%s):\n", page_code, - pc != NULL ? pc : ""); - log_dump(MM_SCSI, "header:", (caddr_t)header, - sizeof (struct scsi_ms_header)); - log_dump(MM_SCSI, "data:", page_data, + dprintf("\nMode sense page 0x%x (%s):\n", page_code, + pc != NULL ? pc : ""); + ddump("header:", (caddr_t)header, + sizeof (struct scsi_ms_header)); + ddump("data:", page_data, (hdr->length + - sizeof (header->mode_header.length)) - + sizeof (header->ms_header.length)) - (MODE_HEADER_LENGTH + hdr->bdesc_length)); return (0); } if (pg->code != page_code) { - - log_msg(MM_SCSI, "\ -\nMode sense page 0x%x: incorrect page code 0x%x\n", - page_code, pg->code); - log_dump(MM_SCSI, - "Mode sense:", mode_sense_buf, nbytes); + dprintf("\nMode sense page 0x%x: incorrect page code 0x%x\n", + page_code, pg->code); + ddump("Mode sense:", mode_sense_buf, nbytes); return (-1); } + /* - * Accept up to "page_size" bytes of mode sense data. - * This allows us to accept both CCS and SCSI-2 - * structures, as long as we request the greater - * of the two. + * Accept up to "page_size" bytes of mode sense data. This allows us to + * accept both CCS and SCSI-2 structures, as long as we request the + * greater of the two. */ maximum = page_size - sizeof (struct mode_page); if (((int)pg->length) > maximum) { - log_msg(MM_SCSI, "\ -Mode sense page 0x%x: incorrect page length %d - expected max %d\n", + dprintf("Mode sense page 0x%x: incorrect page " + "length %d - expected max %d\n", page_code, pg->length, maximum); - log_dump(MM_SCSI, "Mode sense:", mode_sense_buf, - nbytes); + ddump("Mode sense:", mode_sense_buf, nbytes); return (-1); } (void) memcpy(page_data, (caddr_t)pg, MODESENSE_PAGE_LEN(pg)); pc = find_string(page_control_strings, page_control); - log_msg(MM_SCSI, "\nMode sense page 0x%x (%s):\n", page_code, - pc != NULL ? pc : ""); - log_dump(MM_SCSI, "header:", (caddr_t)header, - sizeof (struct scsi_ms_header)); - log_dump(MM_SCSI, "data:", page_data, - MODESENSE_PAGE_LEN(pg)); + dprintf("\nMode sense page 0x%x (%s):\n", page_code, + pc != NULL ? pc : ""); + ddump("header:", (caddr_t)header, sizeof (struct scsi_ms_header)); + ddump("data:", page_data, MODESENSE_PAGE_LEN(pg)); return (0); } /* - * Execute a uscsi MODE SENSE(10) command. - * This can only be used to return one page at a time. - * Return the mode header/block descriptor and the actual - * page data separately - this allows us to support - * devices which return either 0 or 1 block descriptors. - * Whatever a device gives us in the mode header/block descriptor - * will be returned to it upon subsequent mode selects. + * Execute a uscsi MODE SENSE(10) command. This can only be used to return one + * page at a time. Return the mode header/block descriptor and the actual page + * data separately - this allows us to support devices which return either 0 or + * 1 block descriptors. Whatever a device gives us in the mode header/block + * descriptor will be returned to it upon subsequent mode selects. */ int uscsi_mode_sense_10(int fd, int page_code, int page_control, caddr_t page_data, int page_size, struct scsi_ms_header_g1 *header, void *rqbuf, int *rqblen) { - caddr_t mode_sense_buf; - struct mode_header_g1 *hdr; - struct mode_page *pg; - int nbytes; - struct uscsi_cmd ucmd; - union scsi_cdb cdb; - int status; - int maximum; - ushort_t length, bdesc_length; - char *pc; - - dm_assert(page_size >= 0 && page_size < UINT16_MAX); - dm_assert(page_control == PC_CURRENT || - page_control == PC_CHANGEABLE || - page_control == PC_DEFAULT || - page_control == PC_SAVED); - /* - * Allocate a buffer for the mode sense headers - * and mode sense data itself. - */ - nbytes = sizeof (struct scsi_ms_header_g1) + page_size; + caddr_t mode_sense_buf; + struct mode_header_g1 *hdr; + struct mode_page *pg; + int nbytes; + struct uscsi_cmd ucmd; + union scsi_cdb cdb; + int status; + int maximum; + ushort_t length, bdesc_length; + char *pc; + + assert(page_size >= 0 && page_size < UINT16_MAX); + assert(page_control == PC_CURRENT || page_control == PC_CHANGEABLE || + page_control == PC_DEFAULT || page_control == PC_SAVED); - if ((mode_sense_buf = alloca((uint_t)nbytes)) == NULL) { - log_warn("cannot alloca %d bytes\n", nbytes); - return (-1); - } + nbytes = sizeof (struct scsi_ms_header_g1) + page_size; + mode_sense_buf = alloca((uint_t)nbytes); - /* - * Build and execute the uscsi ioctl - */ (void) memset(mode_sense_buf, 0, nbytes); (void) memset((char *)&ucmd, 0, sizeof (ucmd)); (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb)); @@ -1442,25 +1349,22 @@ uscsi_mode_sense_10(int fd, int page_code, int page_control, ucmd.uscsi_bufaddr = mode_sense_buf; ucmd.uscsi_buflen = nbytes; - status = uscsi_cmd(fd, &ucmd, - (g_verbose & MM_SCSI) ? F_NORMAL : F_SILENT, rqbuf, rqblen); + status = uscsi_cmd(fd, &ucmd, rqbuf, rqblen); if (status) { - log_msg(MM_SCSI, "Mode sense(10) page 0x%x failed\n", + dprintf("Mode sense(10) page 0x%x failed\n", page_code); return (-1); } - log_dump(MM_SCSI, "RAW MODE SENSE(10) BUFFER", mode_sense_buf, - nbytes); + ddump("RAW MODE SENSE(10) BUFFER", mode_sense_buf, nbytes); /* - * Verify that the returned data looks reasonable, - * find the actual page data, and copy it into the - * user's buffer. Copy the mode_header and block_descriptor - * into the header structure, which can then be used to + * Verify that the returned data looks reasonable, find the actual page + * data, and copy it into the user's buffer. Copy the mode_header and + * block_descriptor into the header structure, which can then be used to * return the same data to the drive when issuing a mode select. */ - /*LINTED*/ + /* LINTED */ hdr = (struct mode_header_g1 *)mode_sense_buf; length = BE_16(hdr->length); @@ -1468,79 +1372,71 @@ uscsi_mode_sense_10(int fd, int page_code, int page_control, (void) memset((caddr_t)header, 0, sizeof (struct scsi_ms_header_g1)); if (bdesc_length != sizeof (struct block_descriptor) && - bdesc_length != 0) { - - log_msg(MM_SCSI, "\ -\nMode sense(10) page 0x%x: block descriptor length %d incorrect\n", - page_code, bdesc_length); - log_dump(MM_SCSI, "Mode sense(10):", mode_sense_buf, nbytes); + bdesc_length != 0) { + dprintf("\nMode sense(10) page 0x%x: block descriptor " + "length %d incorrect\n", page_code, bdesc_length); + ddump("Mode sense(10):", mode_sense_buf, nbytes); return (-1); } (void) memcpy((caddr_t)header, mode_sense_buf, - (int)(MODE_HEADER_LENGTH_G1 + bdesc_length)); + (int)(MODE_HEADER_LENGTH_G1 + bdesc_length)); pg = (struct mode_page *)((ulong_t)mode_sense_buf + - MODE_HEADER_LENGTH_G1 + bdesc_length); - + MODE_HEADER_LENGTH_G1 + bdesc_length); - if (page_code == MODEPAGE_ALLPAGES) { /* special case */ + if (page_code == MODEPAGE_ALLPAGES) { + /* special case */ (void) memcpy(page_data, (caddr_t)pg, - (length + sizeof (header->mode_header.length)) - + (length + sizeof (header->ms_header.length)) - (MODE_HEADER_LENGTH_G1 + bdesc_length)); - pc = find_string(page_control_strings, - page_control); - log_msg(MM_SCSI, "\nMode sense(10) page 0x%x (%s):\n", + pc = find_string(page_control_strings, page_control); + dprintf("\nMode sense(10) page 0x%x (%s):\n", page_code, pc != NULL ? pc : ""); - log_dump(MM_SCSI, "header:", (caddr_t)header, + ddump("header:", (caddr_t)header, MODE_HEADER_LENGTH_G1 + bdesc_length); - log_dump(MM_SCSI, "data:", page_data, - (length + sizeof (header->mode_header.length)) - + ddump("data:", page_data, + (length + sizeof (header->ms_header.length)) - (MODE_HEADER_LENGTH_G1 + bdesc_length)); return (0); } if (pg->code != page_code) { - log_msg(MM_SCSI, "\ -\nMode sense(10) page 0x%x: incorrect page code 0x%x\n", - page_code, pg->code); - log_dump(MM_SCSI, "Mode sense(10):", mode_sense_buf, nbytes); - + dprintf("\nMode sense(10) page 0x%x: incorrect page " + "code 0x%x\n", page_code, pg->code); + ddump("Mode sense(10):", mode_sense_buf, nbytes); return (-1); } + /* - * Accept up to "page_size" bytes of mode sense data. - * This allows us to accept both CCS and SCSI-2 - * structures, as long as we request the greater - * of the two. + * Accept up to "page_size" bytes of mode sense data. This allows us to + * accept both CCS and SCSI-2 structures, as long as we request the + * greater of the two. */ maximum = page_size - sizeof (struct mode_page); if (((int)pg->length) > maximum) { - - log_msg(MM_SCSI, "\ -Mode sense(10) page 0x%x: incorrect page length %d - expected max %d\n", - page_code, pg->length, maximum); - log_dump(MM_SCSI, "Mode sense(10):", mode_sense_buf, + dprintf("Mode sense(10) page 0x%x: incorrect page " + "length %d - expected max %d\n", + page_code, pg->length, maximum); + ddump("Mode sense(10):", mode_sense_buf, nbytes); - return (-1); } (void) memcpy(page_data, (caddr_t)pg, MODESENSE_PAGE_LEN(pg)); pc = find_string(page_control_strings, page_control); - log_msg(MM_SCSI, "\nMode sense(10) page 0x%x (%s):\n", page_code, + dprintf("\nMode sense(10) page 0x%x (%s):\n", page_code, pc != NULL ? pc : ""); - log_dump(MM_SCSI, "header:", (caddr_t)header, + ddump("header:", (caddr_t)header, sizeof (struct scsi_ms_header_g1)); - log_dump(MM_SCSI, "data:", page_data, MODESENSE_PAGE_LEN(pg)); + ddump("data:", page_data, MODESENSE_PAGE_LEN(pg)); return (0); } - /* * Execute a uscsi mode select command. */ @@ -1548,70 +1444,39 @@ int uscsi_mode_select(int fd, int page_code, int options, caddr_t page_data, int page_size, struct scsi_ms_header *header, void *rqbuf, int *rqblen) { - caddr_t mode_select_buf; - int nbytes; - struct uscsi_cmd ucmd; - union scsi_cdb cdb; - int status; - char *s; - - dm_assert(((struct mode_page *)page_data)->ps == 0); - dm_assert(header->mode_header.length == 0); - dm_assert(header->mode_header.device_specific == 0); - dm_assert((options & ~(MODE_SELECT_SP|MODE_SELECT_PF)) == 0); + caddr_t mode_select_buf; + int nbytes; + struct uscsi_cmd ucmd; + union scsi_cdb cdb; + int status; + char *s; + + assert(((struct mode_page *)page_data)->ps == 0); + assert(header->ms_header.length == 0); + assert(header->ms_header.device_specific == 0); + assert((options & ~(MODE_SELECT_SP|MODE_SELECT_PF)) == 0); - /* - * Allocate a buffer for the mode select header and data - */ nbytes = sizeof (struct scsi_ms_header) + page_size; - if ((mode_select_buf = alloca((uint_t)nbytes)) == NULL) { - log_warn("cannot alloca %d bytes\n", nbytes); - return (-1); - } + mode_select_buf = alloca((uint_t)nbytes); /* - * Build the mode select data out of the header and page data - * This allows us to support devices which return either - * 0 or 1 block descriptors. + * Build the mode select data out of the header and page data This + * allows us to support devices which return either 0 or 1 block + * descriptors. */ (void) memset(mode_select_buf, 0, nbytes); nbytes = MODE_HEADER_LENGTH; - if (header->mode_header.bdesc_length == - sizeof (struct block_descriptor)) { + if (header->ms_header.bdesc_length == + sizeof (struct block_descriptor)) { nbytes += sizeof (struct block_descriptor); } - /* - * Dump the structures if anyone's interested - */ s = find_string(mode_select_strings, - options & (MODE_SELECT_SP|MODE_SELECT_PF)); - log_msg(MM_SCSI, "\nMode select page 0x%x%s:\n", page_code, - s != NULL ? s : ""); - log_dump(MM_SCSI, "header:", (caddr_t)header, nbytes); - log_dump(MM_SCSI, "data:", (caddr_t)page_data, page_size); - - /* - * Fix the code for byte ordering -- all other page types are - * assumed to be formatted properly (byte-order-wise) - */ - - switch (page_code) { - case MODEPAGE_PDEVICE: - { - struct mode_pdevice *pd; - pd = (struct mode_pdevice *)(void *)page_data; - pd->if_ident = BE_16(pd->if_ident); - break; - } - case MODEPAGE_CTRL_MODE: - { - struct mode_control *pd; - pd = (struct mode_control *)(void *)page_data; - pd->ready_aen_holdoff = BE_16(pd->ready_aen_holdoff); - break; - } - } + options & (MODE_SELECT_SP|MODE_SELECT_PF)); + dprintf("\nMode select page 0x%x%s:\n", page_code, + s != NULL ? s : ""); + ddump("header:", (caddr_t)header, nbytes); + ddump("data:", (caddr_t)page_data, page_size); /* * Put the header and data together @@ -1632,17 +1497,14 @@ uscsi_mode_select(int fd, int page_code, int options, caddr_t page_data, ucmd.uscsi_cdblen = CDB_GROUP0; ucmd.uscsi_bufaddr = mode_select_buf; ucmd.uscsi_buflen = nbytes; - status = uscsi_cmd(fd, &ucmd, - (g_verbose & MM_SCSI) ? F_NORMAL : F_SILENT, rqbuf, rqblen); + status = uscsi_cmd(fd, &ucmd, rqbuf, rqblen); - if (status) { - log_msg(MM_SCSI, "Mode select page 0x%x failed\n", page_code); - } + if (status) + dprintf("Mode select page 0x%x failed\n", page_code); return (status); } - /* * Execute a uscsi mode select(10) command. */ @@ -1658,19 +1520,13 @@ uscsi_mode_select_10(int fd, int page_code, int options, int status; char *s; - dm_assert(((struct mode_page *)page_data)->ps == 0); - dm_assert(header->mode_header.length == 0); - dm_assert(header->mode_header.device_specific == 0); - dm_assert((options & ~(MODE_SELECT_SP|MODE_SELECT_PF)) == 0); + assert(((struct mode_page *)page_data)->ps == 0); + assert(header->ms_header.length == 0); + assert(header->ms_header.device_specific == 0); + assert((options & ~(MODE_SELECT_SP|MODE_SELECT_PF)) == 0); - /* - * Allocate a buffer for the mode select header and data - */ nbytes = sizeof (struct scsi_ms_header_g1) + page_size; - if ((mode_select_buf = alloca((uint_t)nbytes)) == NULL) { - log_warn("cannot alloca %d bytes\n", nbytes); - return (-1); - } + mode_select_buf = alloca((uint_t)nbytes); /* * Build the mode select data out of the header and page data @@ -1679,42 +1535,20 @@ uscsi_mode_select_10(int fd, int page_code, int options, */ (void) memset(mode_select_buf, 0, nbytes); nbytes = sizeof (struct mode_header_g1); - if (BE_16(header->mode_header.bdesc_length) == - sizeof (struct block_descriptor)) { + if (BE_16(header->ms_header.bdesc_length) == + sizeof (struct block_descriptor)) { nbytes += sizeof (struct block_descriptor); } /* - * Dump the structures if anyone's interested + * Dump the structures */ s = find_string(mode_select_strings, - options & (MODE_SELECT_SP|MODE_SELECT_PF)); - log_msg(MM_SCSI, "\nMode select(10) page 0x%x%s:\n", page_code, - s != NULL ? s : ""); - log_dump(MM_SCSI, "header:", (caddr_t)header, nbytes); - log_dump(MM_SCSI, "data:", (caddr_t)page_data, page_size); - - /* - * Fix the code for byte ordering -- all other page types are - * assumed to be formatted properly (byte-order-wise) - */ - - switch (page_code) { - case MODEPAGE_PDEVICE: - { - struct mode_pdevice *pd; - pd = (struct mode_pdevice *)(void *)page_data; - pd->if_ident = BE_16(pd->if_ident); - break; - } - case MODEPAGE_CTRL_MODE: - { - struct mode_control *pd; - pd = (struct mode_control *)(void *)page_data; - pd->ready_aen_holdoff = BE_16(pd->ready_aen_holdoff); - break; - } - } + options & (MODE_SELECT_SP|MODE_SELECT_PF)); + dprintf("\nMode select(10) page 0x%x%s:\n", page_code, + s != NULL ? s : ""); + ddump("header:", (caddr_t)header, nbytes); + ddump("data:", (caddr_t)page_data, page_size); /* * Put the header and data together @@ -1735,13 +1569,10 @@ uscsi_mode_select_10(int fd, int page_code, int options, ucmd.uscsi_cdblen = CDB_GROUP1; ucmd.uscsi_bufaddr = mode_select_buf; ucmd.uscsi_buflen = nbytes; - status = uscsi_cmd(fd, &ucmd, - (g_verbose & MM_SCSI) ? F_NORMAL : F_SILENT, rqbuf, rqblen); + status = uscsi_cmd(fd, &ucmd, rqbuf, rqblen); - if (status) { - log_msg(MM_SCSI, "Mode select(10) page 0x%x failed\n", - page_code); - } + if (status) + dprintf("Mode select(10) page 0x%x failed\n", page_code); return (status); } @@ -1750,31 +1581,22 @@ int uscsi_log_sense(int fd, int page_code, int page_control, caddr_t page_data, int page_size, void *rqbuf, int *rqblen) { - caddr_t log_sense_buf; - struct log_header *hdr; - struct uscsi_cmd ucmd; - union scsi_cdb cdb; - int status; - ushort_t len; - char *pc; - - dm_assert(page_size >= 0 && page_size < UINT16_MAX); - dm_assert(page_control == PC_CURRENT || - page_control == PC_CHANGEABLE || - page_control == PC_DEFAULT || - page_control == PC_SAVED); - - if (page_size < sizeof (struct log_header)) + caddr_t log_sense_buf; + scsi_log_header_t *hdr; + struct uscsi_cmd ucmd; + union scsi_cdb cdb; + int status; + ushort_t len; + char *pc; + + assert(page_size >= 0 && page_size < UINT16_MAX); + assert(page_control == PC_CURRENT || page_control == PC_CHANGEABLE || + page_control == PC_DEFAULT || page_control == PC_SAVED); + + if (page_size < sizeof (scsi_log_header_t)) return (-1); - /* - * Allocate a buffer for the log sense header - * and log sense data. - */ - if ((log_sense_buf = alloca((uint_t)page_size)) == NULL) { - log_warn("cannot alloca %d bytes\n", page_size); - return (-1); - } + log_sense_buf = alloca((uint_t)page_size); /* * Build and execute the uscsi ioctl @@ -1789,51 +1611,48 @@ uscsi_log_sense(int fd, int page_code, int page_control, caddr_t page_data, ucmd.uscsi_cdblen = CDB_GROUP1; ucmd.uscsi_bufaddr = log_sense_buf; ucmd.uscsi_buflen = page_size; - status = uscsi_cmd(fd, &ucmd, - (g_verbose & MM_SCSI) ? F_NORMAL : F_SILENT, rqbuf, rqblen); + status = uscsi_cmd(fd, &ucmd, rqbuf, rqblen); if (status) { - log_msg(MM_SCSI, "Log sense page 0x%x failed\n", page_code); + dprintf("Log sense page 0x%x failed\n", page_code); return (-1); } /* - * Verify that the returned data looks reasonable, - * then copy it into the user's buffer. + * Verify that the returned data looks reasonable, then copy it into the + * user's buffer. */ - hdr = (struct log_header *)log_sense_buf; + hdr = (scsi_log_header_t *)log_sense_buf; /* * Ensure we have a host-understandable length field */ - len = BE_16(hdr->length); + len = BE_16(hdr->lh_length); - if (hdr->code != page_code) { - log_msg(MM_SCSI, "\ -\nLog sense page 0x%x: incorrect page code 0x%x\n", - page_code, hdr->code); - log_dump(MM_SCSI, "Log sense:", log_sense_buf, page_size); + if (hdr->lh_code != page_code) { + dprintf("\nLog sense page 0x%x: incorrect page code 0x%x\n", + page_code, hdr->lh_code); + ddump("Log sense:", log_sense_buf, page_size); return (-1); } - log_dump(MM_SCSI, "LOG SENSE RAW OUTPUT", log_sense_buf, - sizeof (struct log_header) + len); + ddump("LOG SENSE RAW OUTPUT", log_sense_buf, + sizeof (scsi_log_header_t) + len); /* - * Accept up to "page_size" bytes of mode sense data. - * This allows us to accept both CCS and SCSI-2 - * structures, as long as we request the greater - * of the two. + * Accept up to "page_size" bytes of mode sense data. This allows us to + * accept both CCS and SCSI-2 structures, as long as we request the + * greater of the two. */ (void) memcpy(page_data, (caddr_t)hdr, len + - sizeof (struct log_header)); + sizeof (scsi_log_header_t)); pc = find_string(page_control_strings, page_control); - log_msg(MM_SCSI, "\nLog sense page 0x%x (%s):\n", page_code, - pc != NULL ? pc : ""); - log_dump(MM_SCSI, "header:", (caddr_t)hdr, - sizeof (struct log_header)); - log_dump(MM_SCSI, "data:", (caddr_t)hdr + - sizeof (struct log_header), len); + dprintf("\nLog sense page 0x%x (%s):\n", page_code, + pc != NULL ? pc : ""); + ddump("header:", (caddr_t)hdr, + sizeof (scsi_log_header_t)); + ddump("data:", (caddr_t)hdr + + sizeof (scsi_log_header_t), len); return (0); } diff --git a/usr/src/lib/fm/libdiskstatus/common/ds_scsi_uscsi.h b/usr/src/lib/fm/libdiskstatus/common/ds_scsi_uscsi.h new file mode 100644 index 0000000000..e46caa4766 --- /dev/null +++ b/usr/src/lib/fm/libdiskstatus/common/ds_scsi_uscsi.h @@ -0,0 +1,65 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _DS_SCSI_USCSI_H +#define _DS_SCSI_USCSI_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * "impossible" status value + */ +#define IMPOSSIBLE_SCSI_STATUS 0xff + +/* + * Minimum length of Request Sense data that we can accept + */ +#define MIN_REQUEST_SENSE_LEN 18 + +/* + * Rounded parameter, as returned in Extended Sense information + */ +#define ROUNDED_PARAMETER 0x37 + +int uscsi_mode_sense(int, int, int, caddr_t, int, scsi_ms_header_t *, + void *, int *); +int uscsi_mode_sense_10(int, int, int, caddr_t, int, scsi_ms_header_g1_t *, + void *, int *); +int uscsi_mode_select(int, int, int, caddr_t, int, scsi_ms_header_t *, + void *, int *); +int uscsi_mode_select_10(int, int, int, caddr_t, int, scsi_ms_header_g1_t *, + void *, int *); +int uscsi_log_sense(int, int, int, caddr_t, int, void *, int *); +int uscsi_request_sense(int, caddr_t, int, void *, int *); + +#ifdef __cplusplus +} +#endif + +#endif /* _DS_SCSI_USCSI_H */ diff --git a/usr/src/lib/fm/libdiskstatus/common/ds_util.c b/usr/src/lib/fm/libdiskstatus/common/ds_util.c new file mode 100644 index 0000000000..fe59b12801 --- /dev/null +++ b/usr/src/lib/fm/libdiskstatus/common/ds_util.c @@ -0,0 +1,172 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <ctype.h> +#include <libdiskstatus.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> + +#include "ds_impl.h" + +boolean_t ds_debug; + +/*PRINTFLIKE1*/ +void +dprintf(const char *fmt, ...) +{ + va_list ap; + + if (!ds_debug) + return; + + va_start(ap, fmt); + (void) vprintf(fmt, ap); + va_end(ap); +} + +void +ddump(const char *label, const void *data, size_t length) +{ + int byte_count; + int i; +#define LINEBUFLEN 128 + char linebuf[LINEBUFLEN]; + char *linep; + int bufleft, len; + const char *start = data; + + if (!ds_debug) + return; + + if (label != NULL) + dprintf("%s\n", label); + + linep = linebuf; + bufleft = LINEBUFLEN; + + for (byte_count = 0; byte_count < length; byte_count += i) { + + (void) snprintf(linep, bufleft, "0x%08x ", byte_count); + len = strlen(linep); + bufleft -= len; + linep += len; + + /* + * Inner loop processes 16 bytes at a time, or less + * if we have less than 16 bytes to go + */ + for (i = 0; (i < 16) && ((byte_count + i) < length); i++) { + (void) snprintf(linep, bufleft, "%02X", (unsigned int) + (unsigned char) start[byte_count + i]); + + len = strlen(linep); + bufleft -= len; + linep += len; + + if (bufleft >= 2) { + if (i == 7) + *linep = '-'; + else + *linep = ' '; + + --bufleft; + ++linep; + } + } + + /* + * If i is less than 16, then we had less than 16 bytes + * written to the output. We need to fixup the alignment + * to allow the "text" output to be aligned + */ + if (i < 16) { + int numspaces = (16 - i) * 3; + while (numspaces-- > 0) { + if (bufleft >= 2) { + *linep = ' '; + --bufleft; + linep++; + } + } + } + + if (bufleft >= 2) { + *linep = ' '; + --bufleft; + ++linep; + } + + for (i = 0; (i < 16) && ((byte_count + i) < length); i++) { + int subscript = byte_count + i; + char ch = (isprint(start[subscript]) ? + start[subscript] : '.'); + + if (bufleft >= 2) { + *linep = ch; + --bufleft; + ++linep; + } + } + + linebuf[LINEBUFLEN - bufleft] = 0; + + dprintf("%s\n", linebuf); + + linep = linebuf; + bufleft = LINEBUFLEN; + } + +} + +const char * +disk_status_errmsg(int error) +{ + switch (error) { + case EDS_NOMEM: + return ("memory allocation failure"); + case EDS_CANT_OPEN: + return ("failed to open device"); + case EDS_NO_TRANSPORT: + return ("no supported communication protocol"); + case EDS_NOT_SUPPORTED: + return ("disk status information not supported"); + case EDS_NOT_SIMULATOR: + return ("not a valid simulator file"); + case EDS_IO: + return ("I/O error from device"); + default: + return ("unknown error"); + } +} + +int +ds_set_errno(disk_status_t *dsp, int error) +{ + dsp->ds_error = error; + return (-1); +} diff --git a/usr/src/lib/fm/libdiskstatus/common/libdiskstatus.c b/usr/src/lib/fm/libdiskstatus/common/libdiskstatus.c new file mode 100644 index 0000000000..e2cf5ad7a2 --- /dev/null +++ b/usr/src/lib/fm/libdiskstatus/common/libdiskstatus.c @@ -0,0 +1,238 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Disk status library + * + * This library is responsible for querying health and other status information + * from disk drives. It is intended to be a generic interface, however only + * SCSI (and therefore SATA) disks are currently supported. The library is + * capable of detecting the following status conditions: + * + * - Predictive failure + * - Overtemp + * - Self-test failure + */ + +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <libdevinfo.h> +#include <libdiskstatus.h> +#include <stdlib.h> +#include <string.h> +#include <sys/fm/io/scsi.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "ds_impl.h" +#include "ds_scsi.h" + +static ds_transport_t *ds_transports[] = { + &ds_scsi_sim_transport, + &ds_scsi_uscsi_transport +}; + +#define NTRANSPORTS (sizeof (ds_transports) / sizeof (ds_transports[0])) + +/* + * Open a handle to a disk. This will fail if the device cannot be opened, or + * if no suitable transport exists for communicating with the device. + */ +disk_status_t * +disk_status_open(const char *path, int *error) +{ + disk_status_t *dsp; + ds_transport_t *t; + int i; + + if ((dsp = calloc(sizeof (disk_status_t), 1)) == NULL) { + *error = EDS_NOMEM; + return (NULL); + } + + if ((dsp->ds_fd = open(path, O_RDWR)) < 0) { + *error = EDS_CANT_OPEN; + free(dsp); + return (NULL); + } + + if ((dsp->ds_path = strdup(path)) == NULL) { + *error = EDS_NOMEM; + disk_status_close(dsp); + return (NULL); + } + + for (i = 0; i < NTRANSPORTS; i++) { + t = ds_transports[i]; + + dsp->ds_transport = t; + + nvlist_free(dsp->ds_state); + dsp->ds_state = NULL; + if (nvlist_alloc(&dsp->ds_state, NV_UNIQUE_NAME, 0) != 0) { + *error = EDS_NOMEM; + disk_status_close(dsp); + return (NULL); + } + + if ((dsp->ds_data = t->dt_open(dsp)) == NULL) { + if (dsp->ds_error != EDS_NO_TRANSPORT) { + *error = dsp->ds_error; + disk_status_close(dsp); + return (NULL); + } + } else { + dsp->ds_error = 0; + break; + } + } + + if (dsp->ds_error == EDS_NO_TRANSPORT) { + *error = dsp->ds_error; + disk_status_close(dsp); + return (NULL); + } + + return (dsp); +} + +/* + * Close a handle to a disk. + */ +void +disk_status_close(disk_status_t *dsp) +{ + nvlist_free(dsp->ds_state); + nvlist_free(dsp->ds_predfail); + nvlist_free(dsp->ds_overtemp); + nvlist_free(dsp->ds_testfail); + if (dsp->ds_data) + dsp->ds_transport->dt_close(dsp->ds_data); + (void) close(dsp->ds_fd); + free(dsp->ds_path); + free(dsp); +} + +void +disk_status_set_debug(boolean_t value) +{ + ds_debug = value; +} + +/* + * Query basic information + */ +const char * +disk_status_path(disk_status_t *dsp) +{ + return (dsp->ds_path); +} + +int +disk_status_errno(disk_status_t *dsp) +{ + return (dsp->ds_error); +} + +nvlist_t * +disk_status_get(disk_status_t *dsp) +{ + nvlist_t *nvl = NULL; + nvlist_t *faults = NULL; + int err; + + /* + * Scan (or rescan) the current device. + */ + nvlist_free(dsp->ds_testfail); + nvlist_free(dsp->ds_predfail); + nvlist_free(dsp->ds_overtemp); + dsp->ds_testfail = dsp->ds_overtemp = dsp->ds_predfail = NULL; + dsp->ds_faults = 0; + + /* + * Even if there is an I/O failure when trying to scan the device, we + * can still return the current state. + */ + if (dsp->ds_transport->dt_scan(dsp->ds_data) != 0 && + dsp->ds_error != EDS_IO) + return (NULL); + + if ((err = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0)) != 0) + goto nverror; + + if ((err = nvlist_add_string(nvl, "protocol", "scsi")) != 0 || + (err = nvlist_add_nvlist(nvl, "status", dsp->ds_state)) != 0) + goto nverror; + + /* + * Construct the list of faults. + */ + if ((err = nvlist_alloc(&faults, NV_UNIQUE_NAME, 0)) != 0) + goto nverror; + + if (dsp->ds_predfail != NULL) { + if ((err = nvlist_add_boolean_value(faults, + FM_EREPORT_SCSI_PREDFAIL, + (dsp->ds_faults & DS_FAULT_PREDFAIL) != 0)) != 0 || + (err = nvlist_add_nvlist(nvl, FM_EREPORT_SCSI_PREDFAIL, + dsp->ds_predfail)) != 0) + goto nverror; + } + + if (dsp->ds_testfail != NULL) { + if ((err = nvlist_add_boolean_value(faults, + FM_EREPORT_SCSI_TESTFAIL, + (dsp->ds_faults & DS_FAULT_TESTFAIL) != 0)) != 0 || + (err = nvlist_add_nvlist(nvl, FM_EREPORT_SCSI_TESTFAIL, + dsp->ds_testfail)) != 0) + goto nverror; + } + + if (dsp->ds_overtemp != NULL) { + if ((err = nvlist_add_boolean_value(faults, + FM_EREPORT_SCSI_OVERTEMP, + (dsp->ds_faults & DS_FAULT_OVERTEMP) != 0)) != 0 || + (err = nvlist_add_nvlist(nvl, FM_EREPORT_SCSI_OVERTEMP, + dsp->ds_overtemp)) != 0) + goto nverror; + } + + if ((err = nvlist_add_nvlist(nvl, "faults", faults)) != 0) + goto nverror; + + nvlist_free(faults); + return (nvl); + +nverror: + assert(err == ENOMEM); + nvlist_free(nvl); + nvlist_free(faults); + (void) ds_set_errno(dsp, EDS_NOMEM); + return (NULL); +} diff --git a/usr/src/lib/fm/libdiskstatus/common/libdiskstatus.h b/usr/src/lib/fm/libdiskstatus/common/libdiskstatus.h new file mode 100644 index 0000000000..b659de316e --- /dev/null +++ b/usr/src/lib/fm/libdiskstatus/common/libdiskstatus.h @@ -0,0 +1,82 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _LIBDISKSTATUS_H +#define _LIBDISKSTATUS_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <libnvpair.h> +#include <sys/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct disk_status disk_status_t; + +/* + * Error definitions + */ +#define EDS_BASE 2000 + +enum { + EDS_NOMEM = EDS_BASE, /* memory allocation failure */ + EDS_CANT_OPEN, /* failed to open device */ + EDS_NO_TRANSPORT, /* no supported transport */ + EDS_NOT_SUPPORTED, /* status information not supported */ + EDS_NOT_SIMULATOR, /* not a valid simulator file */ + EDS_IO /* I/O error */ +}; + +/* + * Basic library functions + */ +extern disk_status_t *disk_status_open(const char *, int *); +extern void disk_status_close(disk_status_t *); +extern const char *disk_status_errmsg(int); +extern void disk_status_set_debug(boolean_t); +extern int disk_status_errno(disk_status_t *); + +/* + * Miscellaneous functions. + */ +extern const char *disk_status_path(disk_status_t *); + +/* + * Main entry point. + */ +extern nvlist_t *disk_status_get(disk_status_t *); + +/* + * Utility function to simulate predictive failure (device-specific). + */ +extern int disk_status_test_predfail(disk_status_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBDISKSTATUS_H */ diff --git a/usr/src/lib/fm/libdiskstatus/common/llib-ldiskstatus b/usr/src/lib/fm/libdiskstatus/common/llib-ldiskstatus new file mode 100644 index 0000000000..a20222355f --- /dev/null +++ b/usr/src/lib/fm/libdiskstatus/common/llib-ldiskstatus @@ -0,0 +1,31 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/*LINTLIBRARY*/ +/*PROTOLIB1*/ + +#include <libdiskstatus.h> diff --git a/usr/src/lib/fm/libdiskstatus/common/mapfile-vers b/usr/src/lib/fm/libdiskstatus/common/mapfile-vers new file mode 100644 index 0000000000..912475031e --- /dev/null +++ b/usr/src/lib/fm/libdiskstatus/common/mapfile-vers @@ -0,0 +1,38 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +SUNWprivate_1.1 { + global: + disk_status_close; + disk_status_errmsg; + disk_status_errno; + disk_status_get; + disk_status_open; + disk_status_path; + disk_status_set_debug; + local: + *; +}; diff --git a/usr/src/lib/fm/libdiskstatus/i386/Makefile b/usr/src/lib/fm/libdiskstatus/i386/Makefile new file mode 100644 index 0000000000..741cbcaecd --- /dev/null +++ b/usr/src/lib/fm/libdiskstatus/i386/Makefile @@ -0,0 +1,29 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/fm/libdiskstatus/sparc/Makefile b/usr/src/lib/fm/libdiskstatus/sparc/Makefile new file mode 100644 index 0000000000..741cbcaecd --- /dev/null +++ b/usr/src/lib/fm/libdiskstatus/sparc/Makefile @@ -0,0 +1,29 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/fm/libdiskstatus/sparcv9/Makefile b/usr/src/lib/fm/libdiskstatus/sparcv9/Makefile new file mode 100644 index 0000000000..5375e35120 --- /dev/null +++ b/usr/src/lib/fm/libdiskstatus/sparcv9/Makefile @@ -0,0 +1,30 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +include ../Makefile.com +include ../../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) diff --git a/usr/src/lib/fm/libfmd_snmp/common/mapfile-vers b/usr/src/lib/fm/libfmd_snmp/common/mapfile-vers index 4af3e953f4..c2d380e684 100644 --- a/usr/src/lib/fm/libfmd_snmp/common/mapfile-vers +++ b/usr/src/lib/fm/libfmd_snmp/common/mapfile-vers @@ -38,7 +38,8 @@ SUNWprivate { fmd_fmri_strdup; fmd_fmri_strescape; fmd_fmri_strfree; - fmd_fmri_topology; + fmd_fmri_topo_hold; + fmd_fmri_topo_rele; fmd_fmri_warn; fmd_fmri_zalloc; init_sunFM; diff --git a/usr/src/lib/fm/libfmd_snmp/common/scheme.c b/usr/src/lib/fm/libfmd_snmp/common/scheme.c index a9b776b6bf..55c45a7ae3 100644 --- a/usr/src/lib/fm/libfmd_snmp/common/scheme.c +++ b/usr/src/lib/fm/libfmd_snmp/common/scheme.c @@ -299,12 +299,14 @@ fmd_fmri_warn(const char *format, ...) { } -/*ARGSUSED*/ struct topo_hdl * -fmd_fmri_topology(int version) +fmd_fmri_topo_hold(int version) { int err; + if (version != TOPO_VERSION) + return (NULL); + if (g_thp == NULL) { if ((g_thp = topo_open(TOPO_VERSION, "/", &err)) == NULL) { DEBUGMSGTL((MODNAME_STR, "topo_open failed: %s\n", @@ -315,3 +317,10 @@ fmd_fmri_topology(int version) return (g_thp); } + +/*ARGSUSED*/ +void +fmd_fmri_topo_rele(struct topo_hdl *thp) +{ + /* nothing to do */ +} diff --git a/usr/src/lib/fm/topo/libtopo/Makefile.com b/usr/src/lib/fm/topo/libtopo/Makefile.com index 9725caf851..d9d09adf4b 100644 --- a/usr/src/lib/fm/topo/libtopo/Makefile.com +++ b/usr/src/lib/fm/topo/libtopo/Makefile.com @@ -80,7 +80,8 @@ LINTFLAGS = -msux LINTFLAGS64 = -msux -Xarch=$(MACH64:sparcv9=v9) $(DYNLIB) := LDLIBS += \ - -lnvpair -lelf -lumem -lxml2 -lkstat -luuid -ldevinfo -lsmbios -lc + -lnvpair -lelf -lumem -lxml2 -lkstat -luuid -ldevinfo \ + -lsmbios -lc -ldevid $(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC) $(LINTLIB) := LINTFLAGS = -nsvx diff --git a/usr/src/lib/fm/topo/libtopo/common/dev.c b/usr/src/lib/fm/topo/libtopo/common/dev.c index 18defd40d3..abbf934245 100644 --- a/usr/src/lib/fm/topo/libtopo/common/dev.c +++ b/usr/src/lib/fm/topo/libtopo/common/dev.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -32,6 +32,7 @@ #include <unistd.h> #include <stdio.h> #include <alloca.h> +#include <devid.h> #include <libnvpair.h> #include <fm/topo_mod.h> #include <sys/fm/protocol.h> @@ -49,6 +50,10 @@ static int dev_fmri_str2nvl(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, nvlist_t **); static int dev_fmri_create_meth(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, nvlist_t **); +static int dev_fmri_present(topo_mod_t *, tnode_t *, topo_version_t, + nvlist_t *, nvlist_t **); +static int dev_fmri_unusable(topo_mod_t *, tnode_t *, topo_version_t, + nvlist_t *, nvlist_t **); static const topo_method_t dev_methods[] = { { TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION, @@ -57,6 +62,11 @@ static const topo_method_t dev_methods[] = { TOPO_STABILITY_INTERNAL, dev_fmri_str2nvl }, { TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION, TOPO_STABILITY_INTERNAL, dev_fmri_create_meth }, + { TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC, TOPO_METH_PRESENT_VERSION, + TOPO_STABILITY_INTERNAL, dev_fmri_present }, + { TOPO_METH_UNUSABLE, TOPO_METH_UNUSABLE_DESC, + TOPO_METH_UNUSABLE_VERSION, TOPO_STABILITY_INTERNAL, + dev_fmri_unusable }, { NULL } }; @@ -290,6 +300,170 @@ dev_fmri_str2nvl(topo_mod_t *mod, tnode_t *node, topo_version_t version, return (0); } +/* + * callback routine for di_walk_minor() + */ +struct walkinfo { + int matched; + const char *path; + const char *devid; + int len; +}; + +static int +dev_match(di_node_t node, void *arg) +{ + struct walkinfo *wip = (struct walkinfo *)arg; + char *path = di_devfs_path(node); + ddi_devid_t devid; + char *devidstr = NULL; + + if (path != NULL && strncmp(path, wip->path, wip->len) == 0) { + /* + * If we found the match we were looking for, check to see if a + * devid was specified. If so, then this must also match. + */ + if (wip->devid) { + if ((devid = di_devid(node)) == NULL || + (devidstr = devid_str_encode(devid, NULL)) == NULL) + goto out; + + if (strcmp(devidstr, wip->devid) != 0) + goto out; + } + + /* + * Either the devid wasn't specified, or it correctly matched. + * In this case, indicate a successful match and terminate the + * walk. + */ + wip->matched = 1; + di_devfs_path_free(path); + devid_str_free(devidstr); + return (DI_WALK_TERMINATE); + } + +out: + if (path != NULL) + di_devfs_path_free(path); + devid_str_free(devidstr); + return (DI_WALK_CONTINUE); +} + +/*ARGSUSED*/ +static int +dev_fmri_present(topo_mod_t *mod, tnode_t *node, topo_version_t version, + nvlist_t *in, nvlist_t **out) +{ + di_node_t parent; + uint8_t fmversion; + char *devpath = NULL; + char *parentpath; + char *cp; + struct walkinfo walkinfo; + uint32_t present; + char *devid = NULL; + + if (version > TOPO_METH_PRESENT_VERSION) + return (topo_mod_seterrno(mod, EMOD_VER_NEW)); + + if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 || + fmversion > FM_DEV_SCHEME_VERSION || + nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0) + return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); + + (void) nvlist_lookup_string(in, FM_FMRI_DEV_ID, &devid); + + if (devpath == NULL || (walkinfo.len = strlen(devpath)) == 0) + return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); + + /* strip off last component of path */ + parentpath = alloca(walkinfo.len + 1); + (void) strcpy(parentpath, devpath); + if ((cp = strrchr(parentpath, '/')) == NULL) + parentpath = "/"; + else + *cp = '\0'; + + /* if the result is an empty path, start walk at "/" */ + if (*parentpath == '\0') + parentpath = "/"; + + /* + * DINFOFORCE is required for the devid to be present. + */ + if ((parent = di_init(parentpath, + DINFOSUBTREE | DINFOFORCE)) == DI_NODE_NIL) { + if (errno != ENXIO) + return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM)); + present = 0; + } else { + walkinfo.matched = 0; + walkinfo.path = devpath; + walkinfo.devid = devid; + (void) di_walk_node(parent, + DI_WALK_SIBFIRST, (void *)&walkinfo, dev_match); + di_fini(parent); + + present = walkinfo.matched; + } + + if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) + return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); + if (nvlist_add_uint32(*out, TOPO_METH_PRESENT_RET, present) != 0) { + nvlist_free(*out); + return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); + } + + return (0); +} + +/*ARGSUSED*/ +static int +dev_fmri_unusable(topo_mod_t *mod, tnode_t *node, topo_version_t version, + nvlist_t *in, nvlist_t **out) +{ + di_node_t dnode; + uint8_t fmversion; + char *devpath = NULL; + uint32_t unusable; + uint_t state; + + if (version > TOPO_METH_PRESENT_VERSION) + return (topo_mod_seterrno(mod, EMOD_VER_NEW)); + + if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 || + fmversion > FM_DEV_SCHEME_VERSION || + nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0) + return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); + + if (devpath == NULL) + return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); + + if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL) { + if (errno != ENXIO) + return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM)); + unusable = 1; + } else { + state = di_state(dnode); + if (state & (DI_DEVICE_OFFLINE | DI_DEVICE_DOWN | + DI_BUS_QUIESCED | DI_BUS_DOWN)) + unusable = 1; + else + unusable = 0; + di_fini(dnode); + } + + if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) + return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); + if (nvlist_add_uint32(*out, TOPO_METH_PRESENT_RET, unusable) != 0) { + nvlist_free(*out); + return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); + } + + return (0); +} + static nvlist_t * dev_fmri_create(topo_mod_t *mp, const char *id, const char *path) { diff --git a/usr/src/lib/fm/topo/libtopo/common/hc.c b/usr/src/lib/fm/topo/libtopo/common/hc.c index 1df1fa04f0..4567fd80b7 100644 --- a/usr/src/lib/fm/topo/libtopo/common/hc.c +++ b/usr/src/lib/fm/topo/libtopo/common/hc.c @@ -751,7 +751,8 @@ char **rev, nvlist_t **auth) if (na == NULL) { if (topo_mod_nvalloc(mod, &na, NV_UNIQUE_NAME) == 0) { - (void) nvlist_add_string(na, aname, aid); + (void) nvlist_add_string(na, aname, + aid); } } else { (void) nvlist_add_string(na, aname, aid); @@ -1375,7 +1376,7 @@ hc_is_present(topo_mod_t *mod, tnode_t *node, void *pdata) return (ETOPO_PROP_NVL); } - return (err); + return (0); } static int diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_fmri.c b/usr/src/lib/fm/topo/libtopo/common/topo_fmri.c index 7fc51be9c8..1847757626 100644 --- a/usr/src/lib/fm/topo/libtopo/common/topo_fmri.c +++ b/usr/src/lib/fm/topo/libtopo/common/topo_fmri.c @@ -359,7 +359,7 @@ topo_fmri_fru(topo_hdl_t *thp, nvlist_t *nvl, nvlist_t **fru, int *err) nvlist_t *fp, *prop = NULL; if (fmri_prop(thp, nvl, TOPO_PGROUP_PROTOCOL, TOPO_PROP_FRU, - nvl, fru, err) < 0) + nvl, &prop, err) < 0) return (set_error(thp, *err, err, "topo_fmri_fru", NULL)); if (nvlist_lookup_nvlist(prop, TOPO_PROP_VAL_VAL, &fp) != 0) diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_mod.c b/usr/src/lib/fm/topo/libtopo/common/topo_mod.c index 5318bd1c12..04b6043648 100644 --- a/usr/src/lib/fm/topo/libtopo/common/topo_mod.c +++ b/usr/src/lib/fm/topo/libtopo/common/topo_mod.c @@ -41,14 +41,14 @@ * * Module Plugin API * - * Enumerators must provide entry points for intialization and clean-up + * Enumerators must provide entry points for initialization and clean-up * (_topo_init() and _topo_fini()). In their _topo_init() function, an * enumerator should register (topo_mod_register()) its enumeration callback * and allocate resources required for a subsequent call to the callback. * Optionally, methods may also be registered with topo_method_register(). * * In its enumeration callback routine, the module should search for resources - * within its realm of resposibility and create any node ranges, + * within its realm of responsibility and create any node ranges, * topo_node_range_create() and nodes, topo_node_bind(). The Enumerator * module is handed a node to which it may begin attaching additional * topology nodes. The enumerator may only access those nodes within its @@ -306,7 +306,7 @@ topo_mod_hcfmri(topo_mod_t *mod, tnode_t *pnode, int version, const char *name, if (pnode != NULL || auth != NULL || part != NULL || rev != NULL || serial != NULL || hc_specific != NULL) { if (topo_mod_nvalloc(mod, &args, NV_UNIQUE_NAME) != 0) - return (set_fmri_err(mod, EMOD_FMRI_NVL)); + return (set_fmri_err(mod, EMOD_FMRI_NVL)); } if (pnode != NULL) { @@ -686,7 +686,8 @@ topo_mod_csn(topo_mod_t *mod) return (NULL); } - return (topo_mod_strdup(mod, csn)); + + return (topo_cleanup_auth_str(mod->tm_hdl, csn)); } nvlist_t * diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_mod.h b/usr/src/lib/fm/topo/libtopo/common/topo_mod.h index 43d217330a..d3f46d1b51 100644 --- a/usr/src/lib/fm/topo/libtopo/common/topo_mod.h +++ b/usr/src/lib/fm/topo/libtopo/common/topo_mod.h @@ -153,6 +153,10 @@ extern nvlist_t *topo_mod_auth(topo_mod_t *, tnode_t *); #define TOPO_METH_FRU_COMPUTE_VERSION 0 #define TOPO_METH_FRU_COMPUTE_DESC "Dynamic FRU constructor" +#define TOPO_METH_DISK_STATUS "topo_disk_status" +#define TOPO_METH_DISK_STATUS_VERSION 0 +#define TOPO_METH_DISK_STATUS_DESC "Disk status" + extern void *topo_mod_alloc(topo_mod_t *, size_t); extern void *topo_mod_zalloc(topo_mod_t *, size_t); extern void topo_mod_free(topo_mod_t *, void *, size_t); diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_snap.c b/usr/src/lib/fm/topo/libtopo/common/topo_snap.c index ec333ee7c3..1645fb5a50 100644 --- a/usr/src/lib/fm/topo/libtopo/common/topo_snap.c +++ b/usr/src/lib/fm/topo/libtopo/common/topo_snap.c @@ -102,37 +102,6 @@ set_open_errno(topo_hdl_t *thp, int *errp, int err) return (NULL); } -static char * -smbios_fix(topo_hdl_t *thp, char *begin) -{ - char buf[MAXNAMELEN]; - size_t count; - char *str, *end, *pp; - - end = begin + strlen(begin); - - while (begin < end && isspace(*begin)) - begin++; - while (begin < end && isspace(*(end - 1))) - end--; - - if (begin >= end) - return (NULL); - - count = end - begin; - count += 1; - - if (count > sizeof (buf)) - return (NULL); - - (void) snprintf(buf, count, "%s", begin); - while ((str = strchr(buf, ' ')) != NULL) - *str = '-'; - - pp = topo_hdl_strdup(thp, buf); - return (pp); -} - topo_hdl_t * topo_open(int version, const char *rootdir, int *errp) { @@ -218,7 +187,7 @@ topo_open(int version, const char *rootdir, int *errp) if (strcmp(s2.smbi_product, SMB_DEFAULT1) != 0 && strcmp(s2.smbi_product, SMB_DEFAULT2) != 0) { - thp->th_product = smbios_fix(thp, + thp->th_product = topo_cleanup_auth_str(thp, (char *)s2.smbi_product); } } diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_subr.c b/usr/src/lib/fm/topo/libtopo/common/topo_subr.c index 50f69156c4..d89ecbb1fa 100644 --- a/usr/src/lib/fm/topo/libtopo/common/topo_subr.c +++ b/usr/src/lib/fm/topo/libtopo/common/topo_subr.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -313,3 +313,40 @@ topo_search_path(topo_mod_t *mod, const char *rootdir, const char *file) return (pp); } + +/* + * SMBIOS serial numbers can contain characters (particularly ':' and ' ') + * that are invalid for the authority and can break FMRI parsing. We translate + * any invalid characters to a safe '-', as well as trimming any leading or + * trailing whitespace. + */ +char * +topo_cleanup_auth_str(topo_hdl_t *thp, char *begin) +{ + char buf[MAXNAMELEN]; + size_t count; + char *str, *end, *pp; + + end = begin + strlen(begin); + + while (begin < end && isspace(*begin)) + begin++; + while (begin < end && isspace(*(end - 1))) + end--; + + if (begin >= end) + return (NULL); + + count = end - begin; + count += 1; + + if (count > sizeof (buf)) + return (NULL); + + (void) snprintf(buf, count, "%s", begin); + while ((str = strpbrk(buf, " :=")) != NULL) + *str = '-'; + + pp = topo_hdl_strdup(thp, buf); + return (pp); +} diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_subr.h b/usr/src/lib/fm/topo/libtopo/common/topo_subr.h index e3df346dc2..d3e56e5921 100644 --- a/usr/src/lib/fm/topo/libtopo/common/topo_subr.h +++ b/usr/src/lib/fm/topo/libtopo/common/topo_subr.h @@ -90,6 +90,8 @@ extern void topo_fmristr_build(ssize_t *, char *, size_t, char *, char *, extern int topo_walk_byid(topo_walk_t *wp, const char *name, topo_instance_t); +extern char *topo_cleanup_auth_str(topo_hdl_t *, char *); + #ifdef __cplusplus } #endif diff --git a/usr/src/lib/fm/topo/modules/i86pc/sata/Makefile b/usr/src/lib/fm/topo/modules/i86pc/sata/Makefile index bd57a80a5f..cce64c561c 100644 --- a/usr/src/lib/fm/topo/modules/i86pc/sata/Makefile +++ b/usr/src/lib/fm/topo/modules/i86pc/sata/Makefile @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -31,4 +31,4 @@ MODULESRCS = sata.c sfx4500_props.c include ../../Makefile.plugin -LDLIBS += -lcfgadm -lsmbios -ldevinfo +LDLIBS += -lcfgadm -lsmbios -ldevinfo -ldiskstatus -ldevid diff --git a/usr/src/lib/fm/topo/modules/i86pc/sata/sata.c b/usr/src/lib/fm/topo/modules/i86pc/sata/sata.c index 5a3ea47846..a1329eac7b 100644 --- a/usr/src/lib/fm/topo/modules/i86pc/sata/sata.c +++ b/usr/src/lib/fm/topo/modules/i86pc/sata/sata.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -54,6 +54,9 @@ #include <assert.h> #include <sys/dkio.h> #include <pthread.h> +#include <fm/libdiskstatus.h> +#include <sys/fm/io/scsi.h> +#include <devid.h> #include "sata.h" #include "sfx4500_props.h" @@ -63,6 +66,18 @@ #define MAX_MACHNAMELEN 256 +/* + * Given a /devices path for a whole disk, appending this extension gives the + * path to a raw device that can be opened. + */ +#if defined(__i386) || defined(__amd64) +#define PHYS_EXTN ":q,raw" +#elif defined(__sparc) || defined(__sparcv9) +#define PHYS_EXTN ":c,raw" +#else +#error Unknown architecture +#endif + struct sata_machine_specific_properties *machprops[] = { &SFX4500_machprops, NULL }; @@ -86,13 +101,12 @@ static tnode_t *node_create(topo_mod_t *mod, tnode_t *pnode, const char *name, char *label, cfga_list_data_t *, int *err); static char *trimdup(const char *s, int *slen, topo_mod_t *mod); static boolean_t get_machine_name(char **name, int *namelen, topo_mod_t *mod); -static int make_legacyhc_fmri(topo_mod_t *mod, const char *str, - nvlist_t **fmri); static int sata_minorname_to_ap(char *minorname, char **ap, int *apbuflen, cfga_list_data_t **list_array, topo_mod_t *mod); static sata_dev_prop_t *lookup_sdp_by_minor(char *minorpath); static boolean_t find_physical_disk_node(char *physpath, int pathbuflen, int portnum, topo_mod_t *mod); +static char *devpath_to_devid(char *devpath); static int sata_maximum_port(char *dpath, topo_mod_t *mod); static void sata_add_port_props(tnode_t *cnode, sata_dev_prop_t *sdp, int *err); static void sata_port_add_private_props(tnode_t *cnode, char *minorpath, @@ -100,34 +114,32 @@ static void sata_port_add_private_props(tnode_t *cnode, char *minorpath, static void sata_info_to_fru(char *info, char **model, int *modlen, char **manuf, int *manulen, char **serial, int *serlen, char **firm, int *firmlen, topo_mod_t *mod); -static void sata_add_disk_props(tnode_t *cnode, int portnum, +static void sata_add_disk_props(tnode_t *cnode, const char *physpath, cfga_list_data_t *cfgap, int *err, topo_mod_t *mod); static boolean_t is_sata_controller(char *dpath); -static int sata_disks_create(topo_mod_t *mod, tnode_t *pnode, nvlist_t *pasru, - const char *name, int portnum, cfga_list_data_t *cfglist, int ndisks, - int *err); +static int sata_disks_create(topo_mod_t *mod, tnode_t *pnode, const char *name, + int portnum, cfga_list_data_t *cfglist, int ndisks, int *err); static int sata_port_create(topo_mod_t *mod, tnode_t *pnode, const char *name, topo_instance_t min, topo_instance_t max); static int sata_enum(topo_mod_t *mod, tnode_t *pnode, const char *name, topo_instance_t min, topo_instance_t max, void *notused1, void *notused2); -static int sata_present(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, +static int sata_disk_status(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, nvlist_t **); static void sata_release(topo_mod_t *mod, tnode_t *nodep); /* - * The topo method is bound to the sata-port nodes because we want - * the hc scheme's lookup to find the method and execute it. If it - * were bound to the disk nodes, and if a disk were not inserted, - * the present method would never execute, and the hc scheme plugin - * would return PRESENT (because other hc-schemed FMRIs do not implement - * a presence method, and in those cases, the correct default is to - * assert presence). + * Methods for SATA disks. This is used by the disk-transport module to + * generate ereports based off SCSI disk status. Unlike the present method, + * this can only function when we have a /devices path (i.e. the disk is + * configured) and so must be a function of the disk itself. */ -static const topo_method_t SATA_METHODS[] = { - { TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC, TOPO_METH_PRESENT_VERSION, - TOPO_STABILITY_INTERNAL, sata_present }, +static const topo_method_t sata_disk_methods[] = { + { TOPO_METH_DISK_STATUS, TOPO_METH_DISK_STATUS_DESC, + TOPO_METH_DISK_STATUS_VERSION, TOPO_STABILITY_INTERNAL, + sata_disk_status }, { NULL } }; + /* * Since the global data is only initialized ONCE in _topo_init * and multiple threads that use libtopo can cause _topo_init to @@ -156,10 +168,7 @@ static const topo_modinfo_t sata_info = static void sata_release(topo_mod_t *mod, tnode_t *nodep) { - /* Only need to unregister methods for the sata port nodes */ - if (strcmp(topo_node_name(nodep), SATA_PORT) == 0) - topo_method_unregister_all(mod, nodep); - + topo_method_unregister_all(mod, nodep); topo_node_unbind(nodep); } @@ -373,17 +382,6 @@ _topo_fini(topo_mod_t *mod) } static int -make_legacyhc_fmri(topo_mod_t *mod, const char *str, nvlist_t **fmri) -{ - char buf[PATH_MAX]; - - (void) snprintf(buf, PATH_MAX, "hc:///component=%s", str); - - return (topo_mod_str2nvl(mod, buf, fmri)); -} - - -static int config_list_ext_poll(int num, char * const *path, cfga_list_data_t **list_array, int *nlist) { @@ -428,113 +426,6 @@ config_list_ext_poll(int num, char * const *path, return (e); } -static boolean_t -sata_serial_match(topo_mod_t *mod, char *sn, char *ap_lname, char *info) -{ - char *p, *apsn; - boolean_t rv = B_FALSE; - int buflen; - - /* Some cfgadm plugins provide serial number information (e.g. sata) */ - if (strncmp(ap_lname, "sata", 4) == 0 && isdigit(ap_lname[4])) { - /* The sata plugin provides the serial number after "SN: " */ - if ((p = strstr(info, "SN: ")) != NULL) { - - p += 4 /* strlen("SN: ") */; - - /* - * Terminate the string at any spaces (the serial - * number is a string with no spaces) - */ - buflen = strlen(p) + 1; - apsn = topo_mod_alloc(mod, buflen); - (void) strcpy(apsn, p); - if ((p = strpbrk(apsn, " \t")) != NULL) - *p = 0; - - rv = (strcasecmp(apsn, sn) == 0); - - topo_mod_free(mod, apsn, buflen); - } - } else /* Unknown cfgadm plugin ap type -- assume sn matches */ - rv = B_TRUE; - - return (rv); -} - -/*ARGSUSED*/ -static int -sata_present(topo_mod_t *mod, tnode_t *nodep, topo_version_t vers, - nvlist_t *nvl, nvlist_t **out_nvl) -{ - nvlist_t **hcprs = NULL; - uint8_t version; - char *nm = NULL; - char *id = NULL; - uint_t hcnprs; - int err; - cfga_list_data_t *list_array = NULL; - int nlist; - char *ap_path[1]; - int present = 1; - char *sn; - - /* - * If this FMRI is a legacy hc FMRI with a component - * looking like an attachment point, use libcfgadm to - * detect its state: - */ - - if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || - version > FM_HC_SCHEME_VERSION) { - - return (topo_mod_seterrno(mod, EINVAL)); - } - - err = nvlist_lookup_nvlist_array(nvl, FM_FMRI_HC_LIST, &hcprs, &hcnprs); - if (err != 0 || hcprs == NULL) { - - return (topo_mod_seterrno(mod, EINVAL)); - } - - (void) nvlist_lookup_string(hcprs[0], FM_FMRI_HC_NAME, &nm); - (void) nvlist_lookup_string(hcprs[0], FM_FMRI_HC_ID, &id); - /* The hc-list must have 1 entry and that entry must be a component */ - if (hcnprs == 1 && nm != NULL && id != NULL && - strcmp(nm, FM_FMRI_LEGACY_HC) == 0 && strchr(id, '/') != NULL) { - ap_path[0] = id; - - if (config_list_ext_poll(1, ap_path, &list_array, &nlist) - == CFGA_OK) { - - if (nvlist_lookup_string(nvl, FM_FMRI_HC_SERIAL_ID, &sn) - != 0) - sn = NULL; - - /* - * If a serial number is included in the FMRI, use it - * to verify presence - */ - present = (list_array[0].ap_r_state - == CFGA_STAT_CONNECTED && (sn == NULL || - (sn != NULL && - sata_serial_match(mod, sn, list_array[0].ap_log_id, - list_array[0].ap_info)))); - - free(list_array); - } - } - - if (topo_mod_nvalloc(mod, out_nvl, NV_UNIQUE_NAME) != 0) - return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); - if (nvlist_add_uint32(*out_nvl, TOPO_METH_PRESENT_RET, present) != 0) { - nvlist_free(*out_nvl); - return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); - } - - return (0); -} - /* * Translates a device minor name into an attachment point (`sataX/Y`) * Allocates memory and returns the attachment point name in *ap. @@ -635,6 +526,27 @@ find_physical_disk_node(char *physpath, int pathbuflen, int portnum, return (found); } +static char * +devpath_to_devid(char *devpath) +{ + di_node_t node; + ddi_devid_t devid; + char *devidstr = NULL; + + if ((node = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL) + return (NULL); + + if ((devid = di_devid(node)) == NULL) { + di_fini(node); + return (NULL); + } + + devidstr = devid_str_encode(devid, NULL); + di_fini(node); + + return (devidstr); +} + static int sata_maximum_port(char *dpath, topo_mod_t *mod) { @@ -837,39 +749,25 @@ sata_info_to_fru(char *info, char **model, int *modlen, char **manuf, } static void -sata_add_disk_props(tnode_t *cnode, int portnum, cfga_list_data_t *cfgap, - int *err, topo_mod_t *mod) +sata_add_disk_props(tnode_t *cnode, const char *physpath, + cfga_list_data_t *cfgap, int *err, topo_mod_t *mod) { - char physpath[PATH_MAX]; char *ldev, *p; char *model = NULL, *manuf = NULL, *serial = NULL, *firm = NULL; - boolean_t physpath_found = B_FALSE; int manuf_len, model_len, serial_len, firm_len; - /* - * The physical path to the drive can be derived by - * taking the attachment point physical path, chopping off the - * minor portion, and looking for the target node of the form - * <nodename>@<X>,0 (where <X> is the port number) - */ - strncpy(physpath, cfgap->ap_phys_id, PATH_MAX); - - /* The AP phys path MUST have a colon: */ - *strchr(physpath, ':') = 0; - - if (find_physical_disk_node(physpath, PATH_MAX, portnum, mod)) { + (void) topo_pgroup_create(cnode, &storage_pgroup, err); - (void) topo_pgroup_create(cnode, &io_pgroup, err); + if (physpath) { + (void) topo_pgroup_create(cnode, &io_pgroup, + err); (void) topo_prop_set_string(cnode, TOPO_PGROUP_IO, TOPO_IO_DEV_PATH, TOPO_PROP_IMMUTABLE, physpath + 8 /* strlen("/devices") */, err); - physpath_found = B_TRUE; } - (void) topo_pgroup_create(cnode, &storage_pgroup, err); - if ((ldev = strstr(cfgap->ap_log_id, "::")) != NULL) { ldev += 2 /* strlen("::") */; if ((p = strchr(ldev, '/')) != NULL) { @@ -903,22 +801,21 @@ sata_add_disk_props(tnode_t *cnode, int portnum, cfga_list_data_t *cfgap, topo_mod_free(mod, firm, firm_len); } -#if defined(__i386) || defined(__amd64) /* * Try to get the disk capacity and store it in a property if the * device is accessible */ - if (physpath_found) { + if (physpath) { int fd; struct dk_minfo dkmi; uint64_t capacity = 0; char capstr[32]; - /* - * On x86, for disk target drivers, `:q,raw' means "access the - * character device that corresponds to the whole disk". - */ - strncat(physpath, ":q,raw", PATH_MAX); - if ((fd = open(physpath, O_RDONLY)) >= 0) { + char buf[PATH_MAX]; + + (void) snprintf(buf, sizeof (buf), "%s%s", physpath, + PHYS_EXTN); + + if ((fd = open(buf, O_RDONLY)) >= 0) { if (ioctl(fd, DKIOCGMEDIAINFO, &dkmi) == 0) { capacity = dkmi.dki_capacity * (uint64_t)dkmi.dki_lbsize; @@ -933,7 +830,6 @@ sata_add_disk_props(tnode_t *cnode, int portnum, cfga_list_data_t *cfgap, err); } } -#endif } static boolean_t @@ -957,14 +853,17 @@ is_sata_controller(char *dpath) } static int -sata_disks_create(topo_mod_t *mod, tnode_t *pnode, nvlist_t *pasru, - const char *name, int portnum, cfga_list_data_t *cfglist, - int ndisks, int *err) +sata_disks_create(topo_mod_t *mod, tnode_t *pnode, const char *name, + int portnum, cfga_list_data_t *cfglist, int ndisks, int *err) { tnode_t *cnode; sata_dev_prop_t *sdp; nvlist_t *fru = NULL; int i, nerrs = 0; + char physpath_buf[PATH_MAX]; + char *physpath, *devpath; + char *devid; + nvlist_t *asru; if (topo_node_range_create(mod, pnode, name, 0, ndisks - 1) < 0) { topo_mod_dprintf(mod, "Unable to create " @@ -981,33 +880,57 @@ sata_disks_create(topo_mod_t *mod, tnode_t *pnode, nvlist_t *pasru, (cfglist[i].ap_o_state == CFGA_STAT_CONFIGURED || cfglist[i].ap_o_state == CFGA_STAT_UNCONFIGURED)) { - if ((sdp = lookup_sdp_by_minor(cfglist[i].ap_phys_id)) - != NULL) { - if (make_legacyhc_fmri(mod, sdp->label, &fru) - != 0) { - topo_mod_dprintf(mod, "Error creating " - "FRU while creating " SATA_DISK - " nodes: %s\n", - topo_mod_errmsg(mod)); - } - } + sdp = lookup_sdp_by_minor(cfglist[i].ap_phys_id); /* - * If this disk is one which we're decorating with - * machine-specific properties, set the FRU to - * a legacy-hc FMRI with the label as the component, - * the ASRU as the parent's ASRU (the ASRU of the - * sata port, e.g. the attachment point); otherwise, - * use the node's FMRI as its FRU (fru will be NULL - * in that case, causing node_create to use the FMRI). + * The physical path to the drive can be derived by + * taking the attachment point physical path, chopping + * off the minor portion, and looking for the target + * node of the form <nodename>@<X>,0 (where <X> is the + * port number) */ + strncpy(physpath_buf, cfglist[i].ap_phys_id, PATH_MAX); + + /* The AP phys path MUST have a colon: */ + *strchr(physpath_buf, ':') = 0; + + /* + * Create the ASRU. If the device is attached to the + * system but unconfigured, then it will have no + * associated ASRU. + */ + if (find_physical_disk_node(physpath_buf, PATH_MAX, + portnum, mod)) { + physpath = physpath_buf; + devpath = physpath_buf + + sizeof ("/devices") - 1; + /* + * Given the /devices path, attempt to find an + * associated devid. + */ + devid = devpath_to_devid(devpath); + asru = topo_mod_devfmri(mod, + FM_DEV_SCHEME_VERSION, devpath, devid); + devid_str_free(devid); + } else { + physpath = NULL; + asru = NULL; + } + if ((cnode = node_create(mod, pnode, name, i, - B_TRUE, fru, pasru, sdp ? (char *)sdp->label : NULL, + B_TRUE, fru, asru, sdp ? (char *)sdp->label : NULL, &cfglist[i], err)) != NULL) { - sata_add_disk_props(cnode, portnum, &cfglist[i], - err, mod); + sata_add_disk_props(cnode, physpath, + &cfglist[i], err, mod); + if (topo_method_register(mod, cnode, + sata_disk_methods) < 0) { + topo_mod_dprintf(mod, + "topo_method_register failed: %s\n", + topo_strerror(topo_mod_errno(mod))); + ++nerrs; + } } else { topo_mod_dprintf(mod, "Error creating disk " "node for port %d: %s\n", portnum, @@ -1015,8 +938,8 @@ sata_disks_create(topo_mod_t *mod, tnode_t *pnode, nvlist_t *pasru, ++nerrs; } - if (fru) - nvlist_free(fru); + nvlist_free(fru); + nvlist_free(asru); } } @@ -1032,7 +955,7 @@ static int sata_port_create(topo_mod_t *mod, tnode_t *pnode, const char *name, topo_instance_t min, topo_instance_t max) { - nvlist_t *asru, *fru; + nvlist_t *asru; tnode_t *cnode; int err = 0, nerr = 0; int i; @@ -1066,15 +989,6 @@ sata_port_create(topo_mod_t *mod, tnode_t *pnode, const char *name, return (-1); } - /* Create the FRU - a legacy component FMRI */ - if (make_legacyhc_fmri(mod, "motherboard", &fru) != 0) { - topo_mod_dprintf(mod, "Unable to create legacy FRU FMRI for " - "node " SATA_PORT ": %s\n", - topo_strerror(err)); - topo_mod_free(mod, dpath, dpathlen); - return (-1); - } - for (i = min; i <= max; i++) { /* The minor node name = "/devices" + dpath + ":" + i */ @@ -1088,8 +1002,9 @@ sata_port_create(topo_mod_t *mod, tnode_t *pnode, const char *name, continue; } - /* Create the ASRU - a legacy component FMRI */ - if (make_legacyhc_fmri(mod, ap, &asru) != 0) { + /* Create the ASRU - a dev:// FMRI */ + if ((asru = topo_mod_devfmri(mod, FM_DEV_SCHEME_VERSION, + dpath, NULL)) == NULL) { free(cfglist); topo_mod_free(mod, ap, apbuflen); topo_mod_dprintf(mod, "failed to make ASRU FMRI: " @@ -1099,12 +1014,10 @@ sata_port_create(topo_mod_t *mod, tnode_t *pnode, const char *name, } /* - * The ASRU is a legacy-hc component FMRI with the - * component being the attachment point - * The FRU is a legacy-hc component FMRI with the - * component being "MB". + * The ASRU is the devices path + * The FRU is a the component FRMI */ - if ((cnode = node_create(mod, pnode, name, i, B_TRUE, fru, + if ((cnode = node_create(mod, pnode, name, i, B_TRUE, NULL, asru, ap, NULL, &err)) == NULL) { nvlist_free(asru); free(cfglist); @@ -1115,24 +1028,13 @@ sata_port_create(topo_mod_t *mod, tnode_t *pnode, const char *name, continue; } - if (topo_method_register(mod, cnode, SATA_METHODS) < 0) { - topo_mod_dprintf(mod, "topo_method_register failed: " - "%s\n", topo_strerror(topo_mod_errno(mod))); - topo_node_unbind(cnode); - nvlist_free(asru); - free(cfglist); - topo_mod_free(mod, ap, apbuflen); - ++nerr; - continue; - } - topo_mod_free(mod, ap, apbuflen); /* For now, ignore errors from private property creation */ sata_port_add_private_props(cnode, minorname, &err); /* Create the disk node(s) under this sata-port: */ - if (sata_disks_create(mod, cnode, asru, SATA_DISK, i, cfglist, + if (sata_disks_create(mod, cnode, SATA_DISK, i, cfglist, 1 /* one disk for now */, &err) != 0) { topo_mod_dprintf(mod, "Error while creating " SATA_DISK " node(s): %s\n", topo_strerror(err)); @@ -1144,7 +1046,6 @@ sata_port_create(topo_mod_t *mod, tnode_t *pnode, const char *name, nvlist_free(asru); } - nvlist_free(fru); topo_mod_free(mod, dpath, dpathlen); if (nerr != 0) { @@ -1164,3 +1065,91 @@ sata_enum(topo_mod_t *mod, tnode_t *pnode, const char *name, return (0); } + +/* + * Query the current disk status. If successful, the disk status is returned as + * an nvlist consisting of at least the following members: + * + * protocol string Supported protocol (currently "scsi") + * + * status nvlist Arbitrary protocol-specific information + * about the current state of the disk. + * + * faults nvlist A list of supported faults. Each + * element of this list is a boolean value. + * An element's existence indicates that + * the drive supports detecting this fault, + * and the value indicates the current + * state of the fault. + * + * <fault-name> nvlist For each fault named in 'faults', a + * nvlist describing protocol-specific + * attributes of the fault. + * + * This method relies on the libdiskstatus library to query this information. + */ +static int +sata_disk_status(topo_mod_t *mod, tnode_t *nodep, topo_version_t vers, + nvlist_t *in_nvl, nvlist_t **out_nvl) +{ + disk_status_t *dsp; + char *devpath, *fullpath; + size_t pathlen; + int err; + nvlist_t *status; + *out_nvl = NULL; + + if (vers != TOPO_METH_DISK_STATUS_VERSION) + return (topo_mod_seterrno(mod, EMOD_VER_NEW)); + + /* + * If the caller specifies the "path" parameter, then this indicates + * that we should use this instead of deriving it from the topo node + * itself. + */ + if (nvlist_lookup_string(in_nvl, "path", &fullpath) == 0) { + devpath = NULL; + } else { + /* + * Get the /devices path and attempt to open the disk status + * handle. + */ + if (topo_prop_get_string(nodep, TOPO_PGROUP_IO, + TOPO_IO_DEV_PATH, &devpath, &err) != 0) + return (topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP)); + + /* + * Note that sizeof(string) includes the terminating NULL byte + */ + pathlen = strlen(devpath) + sizeof ("/devices") + + sizeof (PHYS_EXTN) - 1; + + if ((fullpath = topo_mod_alloc(mod, pathlen)) == NULL) + return (topo_mod_seterrno(mod, EMOD_NOMEM)); + + (void) snprintf(fullpath, pathlen, "/devices%s%s", devpath, + PHYS_EXTN); + + topo_mod_strfree(mod, devpath); + } + + if ((dsp = disk_status_open(fullpath, &err)) == NULL) { + topo_mod_free(mod, fullpath, pathlen); + return (topo_mod_seterrno(mod, err == EDS_NOMEM ? + EMOD_NOMEM : EMOD_METHOD_NOTSUP)); + } + + if (devpath) + topo_mod_free(mod, fullpath, pathlen); + + if ((status = disk_status_get(dsp)) == NULL) { + err = (disk_status_errno(dsp) == EDS_NOMEM ? + EMOD_NOMEM : EMOD_METHOD_NOTSUP); + disk_status_close(dsp); + return (topo_mod_seterrno(mod, err)); + } + + *out_nvl = status; + disk_status_close(dsp); + return (0); +} diff --git a/usr/src/pkgdefs/SUNWfmd/prototype_com b/usr/src/pkgdefs/SUNWfmd/prototype_com index 3047687f43..55695182e7 100644 --- a/usr/src/pkgdefs/SUNWfmd/prototype_com +++ b/usr/src/pkgdefs/SUNWfmd/prototype_com @@ -34,6 +34,7 @@ d none usr 755 root sys d none usr/include 755 root bin d none usr/include/fm 755 root bin f none usr/include/fm/diagcode.h 644 root bin +f none usr/include/fm/libdiskstatus.h 644 root bin f none usr/include/fm/fmd_adm.h 644 root bin f none usr/include/fm/fmd_api.h 644 root bin f none usr/include/fm/fmd_fmri.h 644 root bin @@ -53,6 +54,7 @@ f none usr/lib/fm/dict/PCI.dict 444 root bin f none usr/lib/fm/dict/ZFS.dict 444 root bin f none usr/lib/fm/dict/PCIEX.dict 444 root bin d none usr/lib/fm/eft 755 root bin +f none usr/lib/fm/eft/disk.eft 444 root bin f none usr/lib/fm/eft/pci.eft 444 root bin f none usr/lib/fm/eft/pciex.eft 444 root bin d none usr/lib/fm/fmd 755 root bin @@ -63,6 +65,8 @@ f none usr/lib/fm/fmd/fmtopo 555 root bin d none usr/lib/fm/fmd/plugins 755 root bin f none usr/lib/fm/fmd/plugins/cpumem-retire.conf 644 root bin f none usr/lib/fm/fmd/plugins/cpumem-retire.so 555 root bin +f none usr/lib/fm/fmd/plugins/disk-transport.conf 644 root bin +f none usr/lib/fm/fmd/plugins/disk-transport.so 555 root bin f none usr/lib/fm/fmd/plugins/eft.conf 644 root bin f none usr/lib/fm/fmd/plugins/eft.so 555 root bin f none usr/lib/fm/fmd/plugins/io-retire.conf 644 root bin @@ -91,8 +95,12 @@ f none usr/lib/fm/fmd/schemes/pkg.so 555 root bin f none usr/lib/fm/fmd/schemes/zfs.so 555 root bin f none usr/lib/fm/libdiagcode.so.1 755 root bin s none usr/lib/fm/libdiagcode.so=libdiagcode.so.1 +f none usr/lib/fm/libdiskstatus.so.1 755 root bin +s none usr/lib/fm/libdiskstatus.so=libdiskstatus.so.1 f none usr/lib/fm/llib-ldiagcode 644 root bin f none usr/lib/fm/llib-ldiagcode.ln 644 root bin +f none usr/lib/fm/llib-ldiskstatus 644 root bin +f none usr/lib/fm/llib-ldiskstatus.ln 644 root bin f none usr/lib/fm/libfmd_adm.so.1 755 root bin s none usr/lib/fm/libfmd_adm.so=libfmd_adm.so.1 f none usr/lib/fm/llib-lfmd_adm 644 root bin diff --git a/usr/src/pkgdefs/SUNWfmd/prototype_i386 b/usr/src/pkgdefs/SUNWfmd/prototype_i386 index ab9372b801..256f7a6e1f 100644 --- a/usr/src/pkgdefs/SUNWfmd/prototype_i386 +++ b/usr/src/pkgdefs/SUNWfmd/prototype_i386 @@ -31,6 +31,8 @@ d none usr/lib/fm/amd64 755 root bin f none usr/lib/fm/amd64/libdiagcode.so.1 755 root bin s none usr/lib/fm/amd64/libdiagcode.so=./libdiagcode.so.1 +f none usr/lib/fm/amd64/libdiskstatus.so.1 755 root bin +s none usr/lib/fm/amd64/libdiskstatus.so=./libdiskstatus.so.1 f none usr/lib/fm/amd64/libfmd_adm.so.1 755 root bin s none usr/lib/fm/amd64/libfmd_adm.so=./libfmd_adm.so.1 f none usr/lib/fm/amd64/libfmd_log.so.1 755 root bin @@ -42,6 +44,7 @@ s none usr/lib/fm/amd64/libfmd_snmp.so=./libfmd_snmp.so.1 f none usr/lib/fm/amd64/libtopo.so.1 755 root bin s none usr/lib/fm/amd64/libtopo.so=libtopo.so.1 f none usr/lib/fm/amd64/llib-ldiagcode.ln 644 root bin +f none usr/lib/fm/amd64/llib-ldiskstatus.ln 644 root bin f none usr/lib/fm/amd64/llib-lfmd_adm.ln 644 root bin f none usr/lib/fm/amd64/llib-lfmd_log.ln 644 root bin f none usr/lib/fm/amd64/llib-lfmd_msg.ln 644 root bin diff --git a/usr/src/pkgdefs/SUNWfmd/prototype_sparc b/usr/src/pkgdefs/SUNWfmd/prototype_sparc index f2fea84e7d..90d353eabb 100644 --- a/usr/src/pkgdefs/SUNWfmd/prototype_sparc +++ b/usr/src/pkgdefs/SUNWfmd/prototype_sparc @@ -53,7 +53,10 @@ f none usr/lib/fm/fmd/schemes/sparcv9/zfs.so 555 root bin d none usr/lib/fm/sparcv9 755 root bin f none usr/lib/fm/sparcv9/libdiagcode.so.1 755 root bin s none usr/lib/fm/sparcv9/libdiagcode.so=libdiagcode.so.1 +f none usr/lib/fm/sparcv9/libdiskstatus.so.1 755 root bin +s none usr/lib/fm/sparcv9/libdiskstatus.so=libdiskstatus.so.1 f none usr/lib/fm/sparcv9/llib-ldiagcode.ln 644 root bin +f none usr/lib/fm/sparcv9/llib-ldiskstatus.ln 644 root bin f none usr/lib/fm/sparcv9/libfmd_adm.so.1 755 root bin s none usr/lib/fm/sparcv9/libfmd_adm.so=libfmd_adm.so.1 f none usr/lib/fm/sparcv9/llib-lfmd_adm.ln 644 root bin diff --git a/usr/src/pkgdefs/SUNWhea/prototype_com b/usr/src/pkgdefs/SUNWhea/prototype_com index f9e30c4285..9cc7ac5f66 100644 --- a/usr/src/pkgdefs/SUNWhea/prototype_com +++ b/usr/src/pkgdefs/SUNWhea/prototype_com @@ -693,8 +693,10 @@ d none usr/include/sys/fm/fs 755 root bin f none usr/include/sys/fm/fs/zfs.h 644 root bin d none usr/include/sys/fm/io 755 root bin f none usr/include/sys/fm/io/ddi.h 644 root bin +f none usr/include/sys/fm/io/disk.h 644 root bin f none usr/include/sys/fm/io/opl_mc_fm.h 644 root bin f none usr/include/sys/fm/io/pci.h 644 root bin +f none usr/include/sys/fm/io/scsi.h 644 root bin f none usr/include/sys/fm/io/sun4upci.h 644 root bin f none usr/include/sys/fork.h 644 root bin f none usr/include/sys/frame.h 644 root bin diff --git a/usr/src/uts/common/sys/Makefile b/usr/src/uts/common/sys/Makefile index eb3a21935c..211ec8611f 100644 --- a/usr/src/uts/common/sys/Makefile +++ b/usr/src/uts/common/sys/Makefile @@ -694,7 +694,9 @@ FMFSHDRS= \ FMIOHDRS= \ ddi.h \ + disk.h \ pci.h \ + scsi.h \ sun4upci.h \ opl_mc_fm.h diff --git a/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/fault_mgr.h b/usr/src/uts/common/sys/fm/io/disk.h index 5a0a31b099..6e56bb93e0 100644 --- a/usr/src/cmd/fm/modules/i86pc/sfx4500-disk/fault_mgr.h +++ b/usr/src/uts/common/sys/fm/io/disk.h @@ -18,31 +18,28 @@ * * CDDL HEADER END */ - /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#ifndef _FAULT_MGR_H -#define _FAULT_MGR_H +#ifndef _SYS_FM_IO_DISK_H +#define _SYS_FM_IO_DISK_H #pragma ident "%Z%%M% %I% %E% SMI" -/* - * Fault Manager declarations - */ - #ifdef __cplusplus extern "C" { #endif -int init_fault_manager(cfgdata_t *cfgdatap); -void fault_manager_poke(void); -void cleanup_fault_manager(cfgdata_t *cfgdatap); +#define DISK_ERROR_CLASS "io.disk" + +#define FM_FAULT_DISK_PREDFAIL "predictive-failure" +#define FM_FAULT_DISK_OVERTEMP "over-temperature" +#define FM_FAULT_DISK_TESTFAIL "self-test-failure" #ifdef __cplusplus } #endif -#endif /* _FAULT_MGR_H */ +#endif /* _SYS_FM_IO_DISK_H */ diff --git a/usr/src/uts/common/sys/fm/io/scsi.h b/usr/src/uts/common/sys/fm/io/scsi.h new file mode 100644 index 0000000000..b2b6db5fb1 --- /dev/null +++ b/usr/src/uts/common/sys/fm/io/scsi.h @@ -0,0 +1,61 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_FM_IO_SCSI_H +#define _SYS_FM_IO_SCSI_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * The following ereports are generated by the userland disk-transport module in + * response to errors reported by the disks. + */ +#define SCSI_ERROR_CLASS "io.scsi" + +#define SCSI_DISK_CLASS "disk" + +#define FM_EREPORT_SCSI_PREDFAIL "predictive-failure" +#define FM_EREPORT_PAYLOAD_SCSI_ASC "additional-sense-code" +#define FM_EREPORT_PAYLOAD_SCSI_ASCQ "additional-sense-code-qualifier" + +#define FM_EREPORT_SCSI_OVERTEMP "over-temperature" +#define FM_EREPORT_PAYLOAD_SCSI_CURTEMP "current-temperature" +#define FM_EREPORT_PAYLOAD_SCSI_THRESHTEMP "threshold-temperature" + +#define FM_EREPORT_SCSI_TESTFAIL "self-test-failure" +#define FM_EREPORT_PAYLOAD_SCSI_RESULTCODE "result-code" +#define FM_EREPORT_PAYLOAD_SCSI_ADDRESS "address" +#define FM_EREPORT_PAYLOAD_SCSI_TIMESTAMP "timestamp" +#define FM_EREPORT_PAYLOAD_SCSI_SEGMENT "segment" + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_FM_IO_SCSI_H */ |