diff options
author | vikram <none@none> | 2007-08-09 21:43:47 -0700 |
---|---|---|
committer | vikram <none@none> | 2007-08-09 21:43:47 -0700 |
commit | 25e8c5aa2b496d9026e958ac731a610167574f59 (patch) | |
tree | 48d445f55e23f769f3981231d5b06b0b35505b33 /usr/src/lib | |
parent | ffcd51f34e6cd303b9745909c4632da63426be17 (diff) | |
download | illumos-joyent-25e8c5aa2b496d9026e958ac731a610167574f59.tar.gz |
PSARC 2007/290 Retire Agent for I/O Devices
6464720 Deliver a FMA I/O retire agent
--HG--
rename : usr/src/cmd/fm/modules/common/io-retire/ior_main.c => deleted_files/usr/src/cmd/fm/modules/common/io-retire/ior_main.c
Diffstat (limited to 'usr/src/lib')
20 files changed, 1271 insertions, 51 deletions
diff --git a/usr/src/lib/cfgadm_plugins/scsi/common/cfga_list.c b/usr/src/lib/cfgadm_plugins/scsi/common/cfga_list.c index 6a5f716282..78910e04b1 100644 --- a/usr/src/lib/cfgadm_plugins/scsi/common/cfga_list.c +++ b/usr/src/lib/cfgadm_plugins/scsi/common/cfga_list.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 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -383,13 +382,18 @@ out: } +struct bus_state { + int b_state; + int b_retired; +}; + static scfga_ret_t do_stat_bus(scfga_list_t *lap, int limited_bus_stat) { cfga_list_data_t *clp = NULL; ldata_list_t *listp = NULL; int l_errno = 0; - uint_t devinfo_state = 0; + struct bus_state bstate = {0}; walkarg_t u; scfga_ret_t ret; @@ -399,10 +403,10 @@ do_stat_bus(scfga_list_t *lap, int limited_bus_stat) u.node_args.flags = 0; u.node_args.fcn = get_bus_state; - ret = walk_tree(lap->apidp->hba_phys, &devinfo_state, DINFOPROP, &u, + ret = walk_tree(lap->apidp->hba_phys, &bstate, DINFOPROP, &u, SCFGA_WALK_NODE, &l_errno); if (ret == SCFGA_OK) { - lap->hba_rstate = bus_devinfo_to_recep_state(devinfo_state); + lap->hba_rstate = bus_devinfo_to_recep_state(bstate.b_state); } else { lap->hba_rstate = CFGA_STAT_NONE; } @@ -428,7 +432,8 @@ do_stat_bus(scfga_list_t *lap, int limited_bus_stat) clp->ap_class[0] = '\0'; /* Filled by libcfgadm */ clp->ap_r_state = lap->hba_rstate; clp->ap_o_state = CFGA_STAT_NONE; /* filled in later by the plug-in */ - clp->ap_cond = CFGA_COND_UNKNOWN; + clp->ap_cond = + (bstate.b_retired) ? CFGA_COND_FAILED : CFGA_COND_UNKNOWN; clp->ap_busy = 0; clp->ap_status_time = (time_t)-1; clp->ap_info[0] = '\0'; @@ -446,9 +451,10 @@ do_stat_bus(scfga_list_t *lap, int limited_bus_stat) static int get_bus_state(di_node_t node, void *arg) { - uint_t *di_statep = (uint_t *)arg; + struct bus_state *bsp = (struct bus_state *)arg; - *di_statep = di_state(node); + bsp->b_state = di_state(node); + bsp->b_retired = di_retired(node); return (DI_WALK_TERMINATE); } @@ -512,7 +518,7 @@ do_stat_dev( clp->ap_class[0] = '\0'; /* Filled in by libcfgadm */ clp->ap_r_state = lap->hba_rstate; clp->ap_o_state = ostate; - clp->ap_cond = CFGA_COND_UNKNOWN; + clp->ap_cond = di_retired(node) ? CFGA_COND_FAILED : CFGA_COND_UNKNOWN; clp->ap_busy = 0; /* no way to determine state change */ clp->ap_status_time = (time_t)-1; diff --git a/usr/src/lib/fm/topo/libtopo/common/dev.c b/usr/src/lib/fm/topo/libtopo/common/dev.c index dddede8706..7a4cb4f959 100644 --- a/usr/src/lib/fm/topo/libtopo/common/dev.c +++ b/usr/src/lib/fm/topo/libtopo/common/dev.c @@ -445,9 +445,10 @@ dev_fmri_unusable(topo_mod_t *mod, tnode_t *node, topo_version_t version, return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM)); unusable = 1; } else { + uint_t retired = di_retired(dnode); state = di_state(dnode); - if (state & (DI_DEVICE_OFFLINE | DI_DEVICE_DOWN | - DI_BUS_QUIESCED | DI_BUS_DOWN)) + if (retired || (state & (DI_DEVICE_OFFLINE | DI_DEVICE_DOWN | + DI_BUS_QUIESCED | DI_BUS_DOWN))) unusable = 1; else unusable = 0; diff --git a/usr/src/lib/libcontract/Makefile b/usr/src/lib/libcontract/Makefile index a042993bfc..8d05db4980 100644 --- a/usr/src/lib/libcontract/Makefile +++ b/usr/src/lib/libcontract/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" @@ -32,7 +32,8 @@ HDRDIR = common SUBDIRS = $(MACH) $(BUILD64)SUBDIRS += $(MACH64) -MSGFILES = common/process_dump.c +MSGFILES = common/process_dump.c common/device_dump.c \ + common/libcontract_priv.c POFILE = libcontract.po all := TARGET = all @@ -45,7 +46,8 @@ lint := TARGET = lint all clean clobber install lint: $(SUBDIRS) -$(POFILE): pofile_MSGFILES +$(POFILE): $(MSGFILES) + $(BUILDPO.msgfiles) install_h: $(ROOTHDRS) diff --git a/usr/src/lib/libcontract/Makefile.com b/usr/src/lib/libcontract/Makefile.com index 7d5ab8b471..050d42944e 100644 --- a/usr/src/lib/libcontract/Makefile.com +++ b/usr/src/lib/libcontract/Makefile.com @@ -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" @@ -32,7 +32,9 @@ OBJECTS = \ libcontract.o \ libcontract_priv.o \ process.o \ - process_dump.o + process_dump.o \ + device.o \ + device_dump.o # include library definition include ../../Makefile.lib diff --git a/usr/src/lib/libcontract/common/device.c b/usr/src/lib/libcontract/common/device.c new file mode 100644 index 0000000000..99e9bd1203 --- /dev/null +++ b/usr/src/lib/libcontract/common/device.c @@ -0,0 +1,177 @@ +/* + * 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 <sys/ctfs.h> +#include <sys/contract.h> +#include <sys/contract/device.h> +#include <errno.h> +#include <unistd.h> +#include <string.h> +#include <libnvpair.h> +#include <limits.h> +#include <sys/stat.h> +#include <libcontract.h> +#include "libcontract_impl.h" + +/* + * Device contract template routines + */ + +int +ct_dev_tmpl_set_minor(int fd, char *minor) +{ + return (ct_tmpl_set_internal(fd, CTDP_MINOR, (uintptr_t)minor)); +} + +int +ct_dev_tmpl_set_aset(int fd, uint_t aset) +{ + return (ct_tmpl_set_internal(fd, CTDP_ACCEPT, aset)); +} + +int +ct_dev_tmpl_set_noneg(int fd) +{ + return (ct_tmpl_set_internal(fd, CTDP_NONEG, CTDP_NONEG_SET)); +} + +int +ct_dev_tmpl_clear_noneg(int fd) +{ + return (ct_tmpl_set_internal(fd, CTDP_NONEG, CTDP_NONEG_CLEAR)); +} + +int +ct_dev_tmpl_get_minor(int fd, char *buf, size_t *buflenp) +{ + char path[PATH_MAX]; + int error; + size_t len; + + error = ct_tmpl_get_internal_string(fd, CTDP_MINOR, path); + if (error) { + return (error); + } + + len = strlcpy(buf, path, *buflenp); + if (len >= *buflenp) { + *buflenp = len + 1; + return (EOVERFLOW); + } + + return (0); +} + +int +ct_dev_tmpl_get_aset(int fd, uint_t *aset) +{ + return (ct_tmpl_get_internal(fd, CTDP_ACCEPT, aset)); +} + +int +ct_dev_tmpl_get_noneg(int fd, uint_t *negp) +{ + return (ct_tmpl_get_internal(fd, CTDP_NONEG, negp)); +} + +/* + * Device contract event routines + */ + +/* + * No device contract specific event routines + */ + + +/* + * Device contract status routines + */ + +int +ct_dev_status_get_aset(ct_stathdl_t stathdl, uint_t *aset) +{ + struct ctlib_status_info *info = stathdl; + + if (info->status.ctst_type != CTT_DEVICE) + return (EINVAL); + + if (info->nvl == NULL) + return (ENOENT); + + return (nvlist_lookup_uint32(info->nvl, CTDS_ASET, aset)); +} + +int +ct_dev_status_get_noneg(ct_stathdl_t stathdl, uint_t *negp) +{ + struct ctlib_status_info *info = stathdl; + + if (info->status.ctst_type != CTT_DEVICE) + return (EINVAL); + + if (info->nvl == NULL) + return (ENOENT); + + return (nvlist_lookup_uint32(info->nvl, CTDS_NONEG, negp)); +} + +int +ct_dev_status_get_dev_state(ct_stathdl_t stathdl, uint_t *statep) +{ + struct ctlib_status_info *info = stathdl; + + if (info->status.ctst_type != CTT_DEVICE) + return (EINVAL); + + if (info->nvl == NULL) + return (ENOENT); + + return (nvlist_lookup_uint32(info->nvl, CTDS_STATE, statep)); +} + +int +ct_dev_status_get_minor(ct_stathdl_t stathdl, char **bufp) +{ + int error; + struct ctlib_status_info *info = stathdl; + + if (bufp == NULL) + return (EINVAL); + + if (info->status.ctst_type != CTT_DEVICE) + return (EINVAL); + + if (info->nvl == NULL) + return (ENOENT); + + error = nvlist_lookup_string(info->nvl, CTDS_MINOR, bufp); + if (error != 0) { + return (error); + } + + return (0); +} diff --git a/usr/src/lib/libcontract/common/device_dump.c b/usr/src/lib/libcontract/common/device_dump.c new file mode 100644 index 0000000000..fb6d45cf10 --- /dev/null +++ b/usr/src/lib/libcontract/common/device_dump.c @@ -0,0 +1,103 @@ +/* + * 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 <sys/contract/device.h> +#include <sys/wait.h> +#include <sys/ctfs.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <limits.h> +#include <stdio.h> +#include <assert.h> +#include <signal.h> +#include <libuutil.h> +#include <libintl.h> +#include <libcontract.h> +#include <libcontract_priv.h> +#include "libcontract_impl.h" +#include "libcontract_priv.h" + +/*ARGSUSED*/ +void +event_device(FILE *file, ct_evthdl_t ev, int verbose) +{ + uint_t type; + char *device; + char *s; + ctid_t ctid; + ct_stathdl_t stathdl; + int statfd; + + type = ct_event_get_type(ev); + ctid = ct_event_get_ctid(ev); + + statfd = contract_open(ctid, "device", "status", O_RDONLY); + if (statfd == -1) { + (void) fprintf(file, dgettext(TEXT_DOMAIN, "[bad contract]\n")); + return; + } + + if (ct_status_read(statfd, CTD_ALL, &stathdl) != 0) { + (void) fprintf(file, dgettext(TEXT_DOMAIN, "[status error]\n")); + return; + } + + if (ct_dev_status_get_minor(stathdl, &device) != 0) { + (void) fprintf(file, dgettext(TEXT_DOMAIN, "[bad status]\n")); + return; + } + + + switch (type) { + case CT_DEV_EV_OFFLINE: + s = dgettext(TEXT_DOMAIN, "device %s offlining\n"); + break; + case CT_DEV_EV_DEGRADED: + s = dgettext(TEXT_DOMAIN, "device %s degrading\n"); + break; + case CT_DEV_EV_ONLINE: + s = dgettext(TEXT_DOMAIN, "device %s online\n"); + break; + case CT_EV_NEGEND: + contract_negend_dump(file, ev); + s = NULL; + break; + default: + s = dgettext(TEXT_DOMAIN, "device %s sent an unknown event\n"); + break; + } + + if (s) { + /*LINTED*/ + (void) fprintf(file, s, device); + } + + ct_status_free(stathdl); + (void) close(statfd); +} diff --git a/usr/src/lib/libcontract/common/device_dump.h b/usr/src/lib/libcontract/common/device_dump.h new file mode 100644 index 0000000000..8c90400a52 --- /dev/null +++ b/usr/src/lib/libcontract/common/device_dump.h @@ -0,0 +1,43 @@ +/* + * 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 _DEVICE_DUMP_H +#define _DEVICE_DUMP_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "libcontract_impl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern void event_device(FILE *, ct_evthdl_t, int); + +#ifdef __cplusplus +} +#endif + +#endif /* _DEVICE_DUMP_H */ diff --git a/usr/src/lib/libcontract/common/libcontract.c b/usr/src/lib/libcontract/common/libcontract.c index 7cb35c4cfe..d2739cd1cd 100644 --- a/usr/src/lib/libcontract/common/libcontract.c +++ b/usr/src/lib/libcontract/common/libcontract.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 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -66,11 +65,11 @@ ct_tmpl_create(int fd, ctid_t *ctidp) } int -ct_tmpl_set_internal(int fd, uint_t id, uint_t value) +ct_tmpl_set_internal(int fd, uint_t id, uintptr_t value) { ct_param_t param; param.ctpm_id = id; - param.ctpm_value = value; + param.ctpm_value = (uint64_t)value; if (ioctl(fd, CT_TSET, ¶m) == -1) return (errno); return (0); @@ -112,6 +111,18 @@ ct_tmpl_get_internal(int fd, uint_t id, uint_t *value) } int +ct_tmpl_get_internal_string(int fd, uint_t id, char *value) +{ + ct_param_t param; + + param.ctpm_id = id; + param.ctpm_value = (uint64_t)(uintptr_t)value; + if (ioctl(fd, CT_TGET, ¶m) == -1) + return (errno); + return (0); +} + +int ct_tmpl_get_critical(int fd, uint_t *events) { return (ct_tmpl_get_internal(fd, CTP_EV_CRITICAL, events)); @@ -173,6 +184,14 @@ ct_ctl_ack(int fd, ctevid_t event) } int +ct_ctl_nack(int fd, ctevid_t event) +{ + if (ioctl(fd, CT_CNACK, &event) == -1) + return (errno); + return (0); +} + +int ct_ctl_qack(int fd, ctevid_t event) { if (ioctl(fd, CT_CQREQ, &event) == -1) diff --git a/usr/src/lib/libcontract/common/libcontract.h b/usr/src/lib/libcontract/common/libcontract.h index 98092b7db0..27453e5c83 100644 --- a/usr/src/lib/libcontract/common/libcontract.h +++ b/usr/src/lib/libcontract/common/libcontract.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. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -55,6 +54,7 @@ extern int ct_tmpl_get_informative(int, uint_t *); extern int ct_ctl_adopt(int); extern int ct_ctl_abandon(int); extern int ct_ctl_ack(int, ctevid_t); +extern int ct_ctl_nack(int, ctevid_t); extern int ct_ctl_qack(int, ctevid_t); extern int ct_ctl_newct(int, ctevid_t, int); @@ -113,6 +113,23 @@ extern int ct_pr_status_get_fatal(ct_stathdl_t, uint_t *); extern int ct_pr_status_get_members(ct_stathdl_t, pid_t **, uint_t *); extern int ct_pr_status_get_contracts(ct_stathdl_t, ctid_t **, uint_t *); +/* + * Device contract routines + */ +int ct_dev_tmpl_set_minor(int, char *); +int ct_dev_tmpl_set_aset(int, uint_t); +int ct_dev_tmpl_set_noneg(int); +int ct_dev_tmpl_clear_noneg(int); +int ct_dev_tmpl_get_minor(int, char *, size_t *); +int ct_dev_tmpl_get_aset(int, uint_t *); +int ct_dev_tmpl_get_noneg(int, uint_t *); +int ct_dev_status_get_aset(ct_stathdl_t, uint_t *); +int ct_dev_status_get_noneg(ct_stathdl_t, uint_t *); +int ct_dev_status_get_dev_state(ct_stathdl_t, uint_t *); +int ct_dev_status_get_minor(ct_stathdl_t, char **); + + + #ifdef __cplusplus } #endif diff --git a/usr/src/lib/libcontract/common/libcontract_impl.h b/usr/src/lib/libcontract/common/libcontract_impl.h index d8504cb5cf..ad50cd3dcc 100644 --- a/usr/src/lib/libcontract/common/libcontract_impl.h +++ b/usr/src/lib/libcontract/common/libcontract_impl.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. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -48,8 +47,9 @@ struct ctlib_event_info { nvlist_t *nvl; }; -extern int ct_tmpl_set_internal(int, uint_t, uint_t); +extern int ct_tmpl_set_internal(int, uint_t, uintptr_t); extern int ct_tmpl_get_internal(int, uint_t, uint_t *); +extern int ct_tmpl_get_internal_string(int, uint_t, char *); typedef struct contract_type { const char *type_name; diff --git a/usr/src/lib/libcontract/common/libcontract_priv.c b/usr/src/lib/libcontract/common/libcontract_priv.c index 1db8ea2d95..d74e8409c6 100644 --- a/usr/src/lib/libcontract/common/libcontract_priv.c +++ b/usr/src/lib/libcontract/common/libcontract_priv.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 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -34,16 +33,19 @@ #include <stdio.h> #include <assert.h> #include <libuutil.h> +#include <libintl.h> #include <string.h> #include <procfs.h> #include <libcontract.h> #include <libcontract_priv.h> #include "libcontract_impl.h" #include "process_dump.h" +#include "device_dump.h" contract_type_t types[CTT_MAXTYPE] = { - { "process", event_process } + { "process", event_process }, + { "device", event_device } }; static int @@ -147,3 +149,23 @@ contract_event_dump(FILE *file, ct_evthdl_t hdl, int verbose) type = info->event.ctev_cttype; types[type].type_event(file, hdl, verbose); } + +void +contract_negend_dump(FILE *file, ct_evthdl_t ev) +{ + ctevid_t nevid = 0; + ctid_t my_ctid = ct_event_get_ctid(ev); + ctid_t new_ctid = 0; + char *s; + + (void) ct_event_get_nevid(ev, &nevid); + (void) ct_event_get_newct(ev, &new_ctid); + + if (new_ctid != my_ctid) { + s = dgettext(TEXT_DOMAIN, "negotiation %llu succeeded\n"); + } else { + s = dgettext(TEXT_DOMAIN, "negotiation %llu failed\n"); + } + /*LINTED*/ + (void) fprintf(file, s, (unsigned long long)nevid); +} diff --git a/usr/src/lib/libcontract/common/libcontract_priv.h b/usr/src/lib/libcontract/common/libcontract_priv.h index a1069efb35..639f190aff 100644 --- a/usr/src/lib/libcontract/common/libcontract_priv.h +++ b/usr/src/lib/libcontract/common/libcontract_priv.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. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -42,6 +41,7 @@ extern int contract_open(ctid_t, const char *, const char *, int); extern int contract_abandon_id(ctid_t); extern ctid_t getctid(void); extern void contract_event_dump(FILE *, ct_evthdl_t, int); +extern void contract_negend_dump(FILE *, ct_evthdl_t); #ifdef __cplusplus } diff --git a/usr/src/lib/libcontract/common/mapfile-vers b/usr/src/lib/libcontract/common/mapfile-vers index 2f220b60ad..a64cbfd047 100644 --- a/usr/src/lib/libcontract/common/mapfile-vers +++ b/usr/src/lib/libcontract/common/mapfile-vers @@ -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" @@ -29,6 +29,7 @@ SUNW_1.1 { global: ct_ctl_abandon; ct_ctl_ack; + ct_ctl_nack; ct_ctl_adopt; ct_ctl_newct; ct_ctl_qack; @@ -85,6 +86,17 @@ SUNW_1.1 { ct_tmpl_set_cookie; ct_tmpl_set_critical; ct_tmpl_set_informative; + ct_dev_tmpl_set_minor; + ct_dev_tmpl_set_aset; + ct_dev_tmpl_set_noneg; + ct_dev_tmpl_clear_noneg; + ct_dev_tmpl_get_minor; + ct_dev_tmpl_get_aset; + ct_dev_tmpl_get_noneg; + ct_dev_status_get_aset; + ct_dev_status_get_noneg; + ct_dev_status_get_dev_state; + ct_dev_status_get_minor; }; SUNWprivate_1.1 { diff --git a/usr/src/lib/libdevinfo/Makefile.com b/usr/src/lib/libdevinfo/Makefile.com index c1db80004a..7c10f0ba47 100644 --- a/usr/src/lib/libdevinfo/Makefile.com +++ b/usr/src/lib/libdevinfo/Makefile.com @@ -30,7 +30,9 @@ VERS= .1 OBJECTS= devfsinfo.o devinfo.o devinfo_prop_decode.o devinfo_devlink.o \ devinfo_devperm.o devfsmap.o devinfo_devname.o \ - devinfo_finddev.o devinfo_dli.o devinfo_dim.o devinfo_realpath.o + devinfo_finddev.o devinfo_dli.o devinfo_dim.o \ + devinfo_realpath.o devinfo_retire.o + include ../../Makefile.lib include ../../Makefile.rootfs diff --git a/usr/src/lib/libdevinfo/devinfo.c b/usr/src/lib/libdevinfo/devinfo.c index c9179e0d1f..8c103d2f7a 100644 --- a/usr/src/lib/libdevinfo/devinfo.c +++ b/usr/src/lib/libdevinfo/devinfo.c @@ -998,6 +998,12 @@ di_flags(di_node_t node) return (DI_NODE(node)->flags); } +uint_t +di_retired(di_node_t node) +{ + return (di_flags(node) & DEVI_RETIRED); +} + ddi_devid_t di_devid(di_node_t node) { diff --git a/usr/src/lib/libdevinfo/devinfo_retire.c b/usr/src/lib/libdevinfo/devinfo_retire.c new file mode 100644 index 0000000000..8bcb77a730 --- /dev/null +++ b/usr/src/lib/libdevinfo/devinfo_retire.c @@ -0,0 +1,785 @@ +/* + * 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 <libdevinfo.h> +#include <sys/modctl.h> +#include <sys/stat.h> +#include <string.h> +#include <librcm.h> +#include <dlfcn.h> + +#undef NDEBUG +#include <assert.h> + +typedef struct rio_path { + char rpt_path[PATH_MAX]; + struct rio_path *rpt_next; +} rio_path_t; + +typedef struct rcm_arg { + char *rcm_root; + di_node_t rcm_node; + int rcm_supp; + rcm_handle_t *rcm_handle; + int rcm_retcode; + di_retire_t *rcm_dp; + rio_path_t *rcm_cons_nodes; + rio_path_t *rcm_rsrc_minors; + int (*rcm_offline)(); + int (*rcm_online)(); + int (*rcm_remove)(); +} rcm_arg_t; + +typedef struct selector { + char *sel_name; + int (*sel_selector)(di_node_t node, rcm_arg_t *rp); +} di_selector_t; + +static void rio_assert(di_retire_t *dp, const char *EXstr, int line, + const char *file); + +#define LIBRCM_PATH "/usr/lib/librcm.so" +#define RIO_ASSERT(d, x) \ + {if (!(x)) rio_assert(d, #x, __LINE__, __FILE__); } + +static int disk_select(di_node_t node, rcm_arg_t *rp); +static int nexus_select(di_node_t node, rcm_arg_t *rp); + +di_selector_t supported_devices[] = { + {"disk", disk_select}, + {"nexus", nexus_select}, + {NULL, NULL} +}; + +void * +s_calloc(size_t nelem, size_t elsize, int fail) +{ + if (fail) { + errno = ENOMEM; + return (NULL); + } else { + return (calloc(nelem, elsize)); + } +} + +static void +rio_assert(di_retire_t *dp, const char *EXstr, int line, const char *file) +{ + char buf[PATH_MAX]; + + if (dp->rt_abort == NULL) + assert(0); + + (void) snprintf(buf, sizeof (buf), + "Assertion failed: %s, file %s, line %d\n", + EXstr, file, line); + dp->rt_abort(dp->rt_hdl, buf); +} + +/*ARGSUSED*/ +static int +disk_minor(di_node_t node, di_minor_t minor, void *arg) +{ + rcm_arg_t *rp = (rcm_arg_t *)arg; + di_retire_t *dp = rp->rcm_dp; + + if (di_minor_spectype(minor) == S_IFBLK) { + rp->rcm_supp = 1; + dp->rt_debug(dp->rt_hdl, "[INFO]: disk_minor: is disk minor. " + "IDed this node as disk\n"); + return (DI_WALK_TERMINATE); + } + + dp->rt_debug(dp->rt_hdl, "[INFO]: disk_minor: Not a disk minor. " + "Continuing minor walk\n"); + return (DI_WALK_CONTINUE); +} + +static int +disk_select(di_node_t node, rcm_arg_t *rp) +{ + rcm_arg_t rarg; + di_retire_t *dp = rp->rcm_dp; + + rarg.rcm_dp = dp; + + /* + * Check if this is a disk minor. If any one minor is DDI_NT_BLOCK + * we assume it is a disk + */ + rarg.rcm_supp = 0; + if (di_walk_minor(node, DDI_NT_BLOCK, 0, &rarg, disk_minor) != 0) { + dp->rt_debug(dp->rt_hdl, "[INFO]: disk_select: di_walk_minor " + "failed. Returning NOTSUP\n"); + return (0); + } + + return (rarg.rcm_supp); +} + +static int +nexus_select(di_node_t node, rcm_arg_t *rp) +{ + int select; + char *path; + + di_retire_t *dp = rp->rcm_dp; + + path = di_devfs_path(node); + if (path == NULL) { + dp->rt_debug(dp->rt_hdl, "[INFO]: nexus_select: " + "di_devfs_path() is NULL. Returning NOTSUP\n"); + return (0); + } + + /* + * Check if it is a nexus + */ + if (di_driver_ops(node) & DI_BUS_OPS) { + dp->rt_debug(dp->rt_hdl, "[INFO]: nexus_select: is nexus %s\n", + path); + select = 1; + } else { + dp->rt_debug(dp->rt_hdl, "[INFO]: nexus_select: not nexus %s\n", + path); + select = 0; + } + + di_devfs_path_free(path); + + return (select); +} + +static int +node_select(di_node_t node, void *arg) +{ + rcm_arg_t *rp = (rcm_arg_t *)arg; + di_retire_t *dp; + int sel; + int i; + char *path; + uint_t state; + + dp = rp->rcm_dp; + + /* skip pseudo nodes - we only retire real hardware */ + path = di_devfs_path(node); + if (strncmp(path, "/pseudo/", strlen("/pseudo/")) == 0 || + strcmp(path, "/pseudo") == 0) { + dp->rt_debug(dp->rt_hdl, "[INFO]: node_select: " + "pseudo device in subtree - returning NOTSUP: %s\n", + path); + rp->rcm_supp = 0; + di_devfs_path_free(path); + return (DI_WALK_TERMINATE); + } + di_devfs_path_free(path); + + /* + * If a device is offline/detached/down it is + * retireable irrespective of the type of device, + * presumably the system is able to function without + * it. + */ + state = di_state(node); + if ((state & DI_DRIVER_DETACHED) || (state & DI_DEVICE_OFFLINE) || + (state & DI_BUS_DOWN)) { + dp->rt_debug(dp->rt_hdl, "[INFO]: node_select: device " + "is offline/detached. Assuming retire supported\n"); + return (DI_WALK_CONTINUE); + } + + sel = 0; + for (i = 0; supported_devices[i].sel_name != NULL; i++) { + sel = supported_devices[i].sel_selector(node, rp); + if (sel == 1) { + dp->rt_debug(dp->rt_hdl, "[INFO]: node_select: " + "found supported device: %s\n", + supported_devices[i].sel_name); + break; + } + } + + if (sel != 1) { + /* + * This node is not a supported device. Retire cannot proceed + */ + dp->rt_debug(dp->rt_hdl, "[INFO]: node_select: found " + "unsupported device. Returning NOTSUP\n"); + rp->rcm_supp = 0; + return (DI_WALK_TERMINATE); + } + + /* + * This node is supported. Check other nodes in this subtree. + */ + dp->rt_debug(dp->rt_hdl, "[INFO]: node_select: This node supported. " + "Checking other nodes in subtree: %s\n", rp->rcm_root); + return (DI_WALK_CONTINUE); +} + + + +/* + * when in doubt assume that retire is not supported for this device. + */ +static int +retire_supported(rcm_arg_t *rp) +{ + di_retire_t *dp; + di_node_t rnode = rp->rcm_node; + + dp = rp->rcm_dp; + + /* + * We should not be here if devinfo snapshot is NULL. + */ + RIO_ASSERT(dp, rnode != DI_NODE_NIL); + + /* + * Note: We initally set supported to 1, then walk the + * subtree rooted at devpath, allowing each node the + * opportunity to veto the support. We cannot do things + * the other way around i.e. assume "not supported" and + * let individual nodes indicate that they are supported. + * In the latter case, the supported flag would be set + * if any one node in the subtree was supported which is + * not what we want. + */ + rp->rcm_supp = 1; + if (di_walk_node(rnode, DI_WALK_CLDFIRST, rp, node_select) != 0) { + dp->rt_debug(dp->rt_hdl, "[ERROR]: retire_supported: " + "di_walk_node: failed. Returning NOTSUP\n"); + rp->rcm_supp = 0; + } + + if (rp->rcm_supp) { + dp->rt_debug(dp->rt_hdl, "[INFO]: retire IS supported\n"); + } + + return (rp->rcm_supp); +} + +static void +rcm_finalize(rcm_arg_t *rp, int retcode) +{ + rio_path_t *p; + rio_path_t *tmp; + int flags = RCM_RETIRE_NOTIFY; + int retval; + int error; + di_retire_t *dp; + + dp = rp->rcm_dp; + + RIO_ASSERT(dp, retcode == 0 || retcode == -1); + + dp->rt_debug(dp->rt_hdl, "[INFO]: rcm_finalize: retcode=%d: dev=%s\n", + retcode, rp->rcm_root); + + for (p = rp->rcm_cons_nodes; p; ) { + tmp = p; + p = tmp->rpt_next; + free(tmp); + } + rp->rcm_cons_nodes = NULL; + + dp->rt_debug(dp->rt_hdl, "[INFO]: rcm_finalize: cons_nodes NULL\n"); + + for (p = rp->rcm_rsrc_minors; p; ) { + tmp = p; + p = tmp->rpt_next; + if (retcode == 0) { + retval = rp->rcm_remove(rp->rcm_handle, + tmp->rpt_path, flags, NULL); + error = errno; + } else { + RIO_ASSERT(dp, retcode == -1); + retval = rp->rcm_online(rp->rcm_handle, + tmp->rpt_path, flags, NULL); + error = errno; + } + if (retval != RCM_SUCCESS) { + dp->rt_debug(dp->rt_hdl, "[ERROR]: rcm_finalize: " + "rcm_%s: retval=%d: error=%s: path=%s\n", + retcode == 0 ? "remove" : "online", retval, + strerror(error), tmp->rpt_path); + } else { + dp->rt_debug(dp->rt_hdl, "[INFO]: rcm_finalize: " + "rcm_%s: SUCCESS: path=%s\n", + retcode == 0 ? "remove" : "online", tmp->rpt_path); + } + free(tmp); + } + rp->rcm_rsrc_minors = NULL; +} +/*ARGSUSED*/ +static int +call_offline(di_node_t node, di_minor_t minor, void *arg) +{ + rcm_arg_t *rp = (rcm_arg_t *)arg; + di_retire_t *dp = rp->rcm_dp; + char *mnp; + rio_path_t *rpt; + int retval; + + mnp = di_devfs_minor_path(minor); + if (mnp == NULL) { + dp->rt_debug(dp->rt_hdl, "[ERROR]: di_devfs_minor_path " + "failed. Returning RCM FAILURE: %s\n", rp->rcm_root); + rp->rcm_retcode = RCM_FAILURE; + return (DI_WALK_TERMINATE); + } + + rpt = s_calloc(1, sizeof (rio_path_t), 0); + if (rpt == NULL) { + dp->rt_debug(dp->rt_hdl, "[ERROR]: calloc failed. " + "Returning RCM FAILURE: %s\n", rp->rcm_root); + di_devfs_path_free(mnp); + rp->rcm_retcode = RCM_FAILURE; + return (DI_WALK_TERMINATE); + } + + (void) snprintf(rpt->rpt_path, sizeof (rpt->rpt_path), + "/devices%s", mnp); + + di_devfs_path_free(mnp); + + retval = rp->rcm_offline(rp->rcm_handle, rpt->rpt_path, + RCM_RETIRE_REQUEST, NULL); + + rpt->rpt_next = rp->rcm_rsrc_minors; + rp->rcm_rsrc_minors = rpt; + + if (retval == RCM_FAILURE) { + dp->rt_debug(dp->rt_hdl, "[ERROR]: RCM OFFLINE failed " + "for: %s\n", rpt->rpt_path); + rp->rcm_retcode = RCM_FAILURE; + return (DI_WALK_TERMINATE); + } else if (retval == RCM_SUCCESS) { + rp->rcm_retcode = RCM_SUCCESS; + dp->rt_debug(dp->rt_hdl, "[INFO]: RCM OFFLINE returned " + "RCM_SUCCESS: %s\n", rpt->rpt_path); + } else if (retval != RCM_NO_CONSTRAINT) { + dp->rt_debug(dp->rt_hdl, "[ERROR]: RCM OFFLINE returned " + "invalid value for: %s\n", rpt->rpt_path); + rp->rcm_retcode = RCM_FAILURE; + return (DI_WALK_TERMINATE); + } else { + dp->rt_debug(dp->rt_hdl, "[INFO]: RCM OFFLINE returned " + "RCM_NO_CONSTRAINT: %s\n", rpt->rpt_path); + } + + return (DI_WALK_CONTINUE); +} + +static int +offline_one(di_node_t node, void *arg) +{ + rcm_arg_t *rp = (rcm_arg_t *)arg; + rio_path_t *rpt; + di_retire_t *dp = rp->rcm_dp; + char *path; + + /* + * We should already have terminated the walk + * in case of failure + */ + RIO_ASSERT(dp, rp->rcm_retcode == RCM_SUCCESS || + rp->rcm_retcode == RCM_NO_CONSTRAINT); + + dp->rt_debug(dp->rt_hdl, "[INFO]: offline_one: entered\n"); + + rp->rcm_retcode = RCM_NO_CONSTRAINT; + + rpt = s_calloc(1, sizeof (rio_path_t), 0); + if (rpt == NULL) { + dp->rt_debug(dp->rt_hdl, "[ERROR]: rio_path_t calloc " + "failed: error: %s\n", strerror(errno)); + goto fail; + } + + path = di_devfs_path(node); + if (path == NULL) { + dp->rt_debug(dp->rt_hdl, "[ERROR]: di_devfs_path " + "failed: error: %s\n", strerror(errno)); + free(rpt); + goto fail; + } + + (void) strlcpy(rpt->rpt_path, path, sizeof (rpt->rpt_path)); + + di_devfs_path_free(path); + + if (di_walk_minor(node, NULL, 0, rp, call_offline) != 0) { + dp->rt_debug(dp->rt_hdl, "[ERROR]: di_walk_minor " + "failed: error: %s: %s\n", strerror(errno), path); + free(rpt); + goto fail; + } + + if (rp->rcm_retcode == RCM_FAILURE) { + dp->rt_debug(dp->rt_hdl, "[ERROR]: di_walk_minor " + "returned: RCM_FAILURE: %s\n", rpt->rpt_path); + free(rpt); + goto fail; + } else if (rp->rcm_retcode == RCM_SUCCESS) { + dp->rt_debug(dp->rt_hdl, "[INFO]: di_walk_minor " + "returned: RCM_SUCCESS: %s\n", rpt->rpt_path); + rpt->rpt_next = rp->rcm_cons_nodes; + rp->rcm_cons_nodes = rpt; + } else if (rp->rcm_retcode != RCM_NO_CONSTRAINT) { + dp->rt_debug(dp->rt_hdl, "[ERROR]: di_walk_minor " + "returned: unknown RCM error code: %d, %s\n", + rp->rcm_retcode, rpt->rpt_path); + free(rpt); + goto fail; + } else { + dp->rt_debug(dp->rt_hdl, "[INFO]: di_walk_minor " + "returned: RCM_NO_CONSTRAINT: %s\n", rpt->rpt_path); + free(rpt); + } + + /* + * RCM_SUCCESS or RCM_NO_CONSTRAINT. + * RCM_SUCCESS implies we overcame a constraint, so keep walking. + * RCM_NO_CONSTRAINT implies no constraints applied via RCM. + * Continue walking in the hope that contracts or LDI will + * apply constraints + * set retcode to RCM_SUCCESS to show that at least 1 node + * completely walked + */ + rp->rcm_retcode = RCM_SUCCESS; + return (DI_WALK_CONTINUE); + +fail: + rp->rcm_retcode = RCM_FAILURE; + return (DI_WALK_TERMINATE); +} + +/* + * Returns: + * RCM_SUCCESS: RCM constraints (if any) were applied. The + * device paths for which constraints were applied is passed + * back via the pp argument + * + * RCM_FAILURE: Either RCM constraints prevent a retire or + * an error occurred + */ +static int +rcm_notify(rcm_arg_t *rp, char **pp, size_t *clen) +{ + size_t len; + rio_path_t *p; + rio_path_t *tmp; + char *plistp; + char *s; + di_retire_t *dp; + di_node_t rnode; + + dp = rp->rcm_dp; + + dp->rt_debug(dp->rt_hdl, "[INFO]: rcm_notify() entered\n"); + + RIO_ASSERT(dp, rp->rcm_root); + + *pp = NULL; + + rnode = rp->rcm_node; + if (rnode == DI_NODE_NIL) { + dp->rt_debug(dp->rt_hdl, "[ERROR]: devinfo snapshot " + "NULL. Returning no RCM constraint: %s\n", rp->rcm_root); + return (RCM_NO_CONSTRAINT); + } + + rp->rcm_retcode = RCM_NO_CONSTRAINT; + rp->rcm_cons_nodes = NULL; + rp->rcm_rsrc_minors = NULL; + if (di_walk_node(rnode, DI_WALK_CLDFIRST, rp, offline_one) != 0) { + dp->rt_debug(dp->rt_hdl, "[ERROR]: di_walk_node " + "failed: error: %s: %s\n", strerror(errno), rp->rcm_root); + /* online is idempotent - safe to online non-offlined nodes */ + rcm_finalize(rp, -1); + rp->rcm_retcode = RCM_FAILURE; + goto out; + } + + if (rp->rcm_retcode == RCM_FAILURE) { + dp->rt_debug(dp->rt_hdl, "[ERROR]: walk_node " + "returned retcode of RCM_FAILURE: %s\n", rp->rcm_root); + rcm_finalize(rp, -1); + goto out; + } + + if (rp->rcm_retcode == RCM_NO_CONSTRAINT) { + dp->rt_debug(dp->rt_hdl, "[ERROR]: di_walk_node " + " - no nodes walked: RCM_NO_CONSTRAINT: %s\n", + rp->rcm_root); + } else { + dp->rt_debug(dp->rt_hdl, "[INFO]: walk_node: RCM_SUCCESS\n"); + } + + /* + * Convert to a sequence of NUL separated strings terminated by '\0'\0' + */ + for (len = 0, p = rp->rcm_cons_nodes; p; p = p->rpt_next) { + RIO_ASSERT(dp, p->rpt_path); + RIO_ASSERT(dp, strlen(p->rpt_path) > 0); + len += (strlen(p->rpt_path) + 1); + } + len++; /* list terminating '\0' */ + + dp->rt_debug(dp->rt_hdl, "[INFO]: len of constraint str = %lu\n", len); + + plistp = s_calloc(1, len, 0); + if (plistp == NULL) { + dp->rt_debug(dp->rt_hdl, "[ERROR]: fail to alloc " + "constraint list: error: %s: %s\n", strerror(errno), + rp->rcm_root); + rcm_finalize(rp, -1); + rp->rcm_retcode = RCM_FAILURE; + goto out; + } + + for (s = plistp, p = rp->rcm_cons_nodes; p; ) { + tmp = p; + p = tmp->rpt_next; + (void) strcpy(s, tmp->rpt_path); + s += strlen(s) + 1; + RIO_ASSERT(dp, s - plistp < len); + free(tmp); + } + rp->rcm_cons_nodes = NULL; + RIO_ASSERT(dp, s - plistp == len - 1); + *s = '\0'; + + dp->rt_debug(dp->rt_hdl, "[INFO]: constraint str = %p\n", plistp); + + *pp = plistp; + *clen = len; + + rp->rcm_retcode = RCM_SUCCESS; +out: + return (rp->rcm_retcode); +} + + +/*ARGSUSED*/ +int +di_retire_device(char *devpath, di_retire_t *dp, int flags) +{ + char path[PATH_MAX]; + struct stat sb; + int retval = EINVAL; + char *constraint = NULL; + size_t clen; + void *librcm_hdl; + rcm_arg_t rarg = {0}; + int (*librcm_alloc_handle)(); + int (*librcm_free_handle)(); + + if (dp == NULL || dp->rt_debug == NULL || dp->rt_hdl == NULL) + return (EINVAL); + + if (devpath == NULL || devpath[0] == '\0') { + dp->rt_debug(dp->rt_hdl, "[ERROR]: NULL argument(s)\n"); + return (EINVAL); + } + + if (devpath[0] != '/' || strlen(devpath) >= PATH_MAX || + strncmp(devpath, "/devices/", strlen("/devices/")) == 0 || + strstr(devpath, "../devices/") || strrchr(devpath, ':')) { + dp->rt_debug(dp->rt_hdl, "[ERROR]: invalid devpath: %s\n", + devpath); + return (EINVAL); + } + + if (flags != 0) { + dp->rt_debug(dp->rt_hdl, "[ERROR]: flags should be 0: %d\n", + flags); + return (EINVAL); + } + + /* + * dlopen rather than link against librcm since libdevinfo + * resides in / and librcm resides in /usr. The dlopen is + * safe to do since fmd which invokes the retire code + * resides on /usr and will not come here until /usr is + * mounted. + */ + librcm_hdl = dlopen(LIBRCM_PATH, RTLD_LAZY); + if (librcm_hdl == NULL) { + char *errstr = dlerror(); + dp->rt_debug(dp->rt_hdl, "[ERROR]: Cannot dlopen librcm: %s\n", + errstr ? errstr : "Unknown error"); + return (ENOSYS); + } + + librcm_alloc_handle = (int (*)())dlsym(librcm_hdl, "rcm_alloc_handle"); + rarg.rcm_offline = (int (*)())dlsym(librcm_hdl, "rcm_request_offline"); + rarg.rcm_online = (int (*)())dlsym(librcm_hdl, "rcm_notify_online"); + rarg.rcm_remove = (int (*)())dlsym(librcm_hdl, "rcm_notify_remove"); + librcm_free_handle = (int (*)())dlsym(librcm_hdl, "rcm_free_handle"); + + if (librcm_alloc_handle == NULL || + rarg.rcm_offline == NULL || + rarg.rcm_online == NULL || + rarg.rcm_remove == NULL || + librcm_free_handle == NULL) { + dp->rt_debug(dp->rt_hdl, "[ERROR]: dlsym failed\n"); + retval = ENOSYS; + goto out; + } + + /* + * Take a libdevinfo snapshot here because we cannot do so + * after device is retired. If device doesn't attach, we retire + * anyway i.e. it is not fatal. + */ + rarg.rcm_node = di_init(devpath, DINFOCPYALL); + if (rarg.rcm_node == DI_NODE_NIL) { + dp->rt_debug(dp->rt_hdl, "[ERROR]: device doesn't attach, " + "retiring anyway: %s\n", devpath); + } + + rarg.rcm_handle = NULL; + if (librcm_alloc_handle(NULL, 0, NULL, &rarg.rcm_handle) + != RCM_SUCCESS) { + retval = errno; + dp->rt_debug(dp->rt_hdl, "[ERROR]: failed to alloc " + "RCM handle. Returning RCM failure: %s\n", devpath); + rarg.rcm_handle = NULL; + goto out; + } + + rarg.rcm_root = devpath; + rarg.rcm_dp = dp; + + /* + * If device is already detached/nonexistent and cannot be + * attached, allow retire without checking device type. + * XXX + * Else, check if retire is supported for this device type. + */ + (void) snprintf(path, sizeof (path), "/devices%s", devpath); + if (stat(path, &sb) == -1 || !S_ISDIR(sb.st_mode)) { + dp->rt_debug(dp->rt_hdl, "[ERROR]: detached or nonexistent " + "device. Bypassing retire_supported: %s\n", devpath); + } else if (!retire_supported(&rarg)) { + dp->rt_debug(dp->rt_hdl, "[ERROR]: retire not supported for " + "device type: %s\n", devpath); + retval = ENOTSUP; + goto out; + } + + clen = 0; + constraint = NULL; + retval = rcm_notify(&rarg, &constraint, &clen); + if (retval == RCM_FAILURE) { + /* retire not permitted */ + dp->rt_debug(dp->rt_hdl, "[ERROR]: RCM constraints block " + "retire: %s\n", devpath); + retval = EBUSY; + goto out; + } else if (retval == RCM_SUCCESS) { + dp->rt_debug(dp->rt_hdl, "[INFO]: RCM constraints applied" + ": %s\n", devpath); + } else if (retval == RCM_NO_CONSTRAINT) { + dp->rt_debug(dp->rt_hdl, "[INFO]: No RCM constraints applied" + ": %s\n", devpath); + } else { + dp->rt_debug(dp->rt_hdl, "[ERROR]: notify returned unknown " + "return code: %d: %s\n", retval, devpath); + retval = ESRCH; + goto out; + } + + if (modctl(MODRETIRE, devpath, constraint, clen) != 0) { + retval = errno; + dp->rt_debug(dp->rt_hdl, "[ERROR]: retire modctl() failed: " + "%s: %s\n", devpath, strerror(retval)); + rcm_finalize(&rarg, -1); + goto out; + } + + dp->rt_debug(dp->rt_hdl, "[INFO]: retire modctl() succeeded: %s\n", + devpath); + + rcm_finalize(&rarg, 0); + + retval = 0; + +out: + if (rarg.rcm_handle) + (void) librcm_free_handle(rarg.rcm_handle); + + RIO_ASSERT(dp, rarg.rcm_cons_nodes == NULL); + RIO_ASSERT(dp, rarg.rcm_rsrc_minors == NULL); + + (void) dlclose(librcm_hdl); + + free(constraint); + + if (rarg.rcm_node != DI_NODE_NIL) + di_fini(rarg.rcm_node); + + return (retval); +} + +/*ARGSUSED*/ +int +di_unretire_device(char *devpath, di_retire_t *dp) +{ + if (dp == NULL || dp->rt_debug == NULL || dp->rt_hdl == NULL) + return (EINVAL); + + if (devpath == NULL || devpath[0] == '\0') { + dp->rt_debug(dp->rt_hdl, "[ERROR]: NULL devpath\n"); + return (EINVAL); + } + + if (devpath[0] != '/' || strlen(devpath) >= PATH_MAX || + strncmp(devpath, "/devices/", strlen("/devices/")) == 0 || + strstr(devpath, "../devices/") || strrchr(devpath, ':')) { + dp->rt_debug(dp->rt_hdl, "[ERROR]: invalid devpath: %s\n", + devpath); + return (EINVAL); + } + + if (modctl(MODUNRETIRE, devpath) != 0) { + int err = errno; + dp->rt_debug(dp->rt_hdl, "[ERROR]: unretire modctl() failed: " + "%s: %s\n", devpath, strerror(err)); + return (err); + } + + dp->rt_debug(dp->rt_hdl, "[INFO]: unretire modctl() done: %s\n", + devpath); + + return (0); +} diff --git a/usr/src/lib/libdevinfo/libdevinfo.h b/usr/src/lib/libdevinfo/libdevinfo.h index ad08502628..bdb4fa2238 100644 --- a/usr/src/lib/libdevinfo/libdevinfo.h +++ b/usr/src/lib/libdevinfo/libdevinfo.h @@ -355,6 +355,11 @@ extern void *di_parent_private_data(di_node_t node); extern void *di_driver_private_data(di_node_t node); /* + * The value of the dip's devi_flags field + */ +uint_t di_flags(di_node_t node); + +/* * Types of links for devlink lookup */ #define DI_PRIMARY_LINK 0x01 @@ -412,6 +417,19 @@ extern int di_devlink_cache_walk(di_devlink_handle_t hdp, const char *re, int (*devlink_callback)(di_devlink_t, void *)); /* + * Private interfaces for I/O retire + */ +typedef struct di_retire { + void *rt_hdl; + void (*rt_abort)(void *hdl, const char *format, ...); + void (*rt_debug)(void *hdl, const char *format, ...); +} di_retire_t; + +extern int di_retire_device(char *path, di_retire_t *dp, int flags); +extern int di_unretire_device(char *path, di_retire_t *dp); +extern uint_t di_retired(di_node_t node); + +/* * Private interfaces for /etc/logindevperm */ extern int di_devperm_login(const char *, uid_t, gid_t, void (*)(char *)); diff --git a/usr/src/lib/libdevinfo/mapfile-vers b/usr/src/lib/libdevinfo/mapfile-vers index c941cd3efe..c2d82dcb4b 100644 --- a/usr/src/lib/libdevinfo/mapfile-vers +++ b/usr/src/lib/libdevinfo/mapfile-vers @@ -211,6 +211,9 @@ SUNWprivate_1.1 { finddev_close; finddev_next; di_flags; + di_retire_device; + di_unretire_device; + di_retired; local: *; }; diff --git a/usr/src/lib/librcm/librcm.h b/usr/src/lib/librcm/librcm.h index d830ea375e..be57013b0a 100644 --- a/usr/src/lib/librcm/librcm.h +++ b/usr/src/lib/librcm/librcm.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. */ @@ -57,6 +57,8 @@ extern "C" { #define RCM_REGISTER_EVENT 0x2000 /* private */ #define RCM_REGISTER_CAPACITY 0x4000 /* private */ #define RCM_SUSPENDED 0x8000 /* private */ +#define RCM_RETIRE_REQUEST 0x10000 +#define RCM_RETIRE_NOTIFY 0x20000 /* * RCM return values @@ -64,6 +66,7 @@ extern "C" { #define RCM_SUCCESS 0 #define RCM_FAILURE -1 #define RCM_CONFLICT -2 +#define RCM_NO_CONSTRAINT -3 /* * RCM resource states diff --git a/usr/src/lib/librcm/librcm_impl.h b/usr/src/lib/librcm/librcm_impl.h index a534d22e1e..b096ffba0d 100644 --- a/usr/src/lib/librcm/librcm_impl.h +++ b/usr/src/lib/librcm/librcm_impl.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. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -69,8 +68,8 @@ extern "C" { #define RCM_REGISTER_MASK (RCM_FILESYS|RCM_REGISTER_DR|\ RCM_REGISTER_EVENT|RCM_REGISTER_CAPACITY) #define RCM_REQUEST_MASK (RCM_QUERY|RCM_SCOPE|RCM_FORCE|RCM_FILESYS|\ - RCM_QUERY_CANCEL) -#define RCM_NOTIFY_MASK (RCM_FILESYS) + RCM_QUERY_CANCEL|RCM_RETIRE_REQUEST) +#define RCM_NOTIFY_MASK (RCM_FILESYS|RCM_RETIRE_NOTIFY) /* event data names */ #define RCM_CMD "rcm.cmd" |