diff options
Diffstat (limited to 'usr/src')
35 files changed, 5022 insertions, 376 deletions
diff --git a/usr/src/cmd/fm/fmd/common/fmd.c b/usr/src/cmd/fm/fmd/common/fmd.c index 96b3d7d933..903ad1d9b5 100644 --- a/usr/src/cmd/fm/fmd/common/fmd.c +++ b/usr/src/cmd/fm/fmd/common/fmd.c @@ -24,8 +24,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/types.h> #include <sys/utsname.h> #include <sys/param.h> @@ -241,7 +239,7 @@ static const fmd_conf_formal_t _fmd_conf[] = { { "client.error", &fmd_cerror_ops, "unload" }, /* client error policy */ { "client.memlim", &fmd_conf_size, "10m" }, /* client allocation limit */ { "client.evqlim", &fmd_conf_uint32, "256" }, /* client event queue limit */ -{ "client.thrlim", &fmd_conf_uint32, "8" }, /* client aux thread limit */ +{ "client.thrlim", &fmd_conf_uint32, "20" }, /* client aux thread limit */ { "client.thrsig", &fmd_conf_signal, "SIGUSR1" }, /* fmd_thr_signal() value */ { "client.tmrlim", &fmd_conf_uint32, "1024" }, /* client pending timer limit */ { "client.xprtlim", &fmd_conf_uint32, "256" }, /* client transport limit */ diff --git a/usr/src/cmd/fm/fmd/common/fmd_api.c b/usr/src/cmd/fm/fmd/common/fmd_api.c index 1a2ab860ec..dfea6c9a02 100644 --- a/usr/src/cmd/fm/fmd/common/fmd_api.c +++ b/usr/src/cmd/fm/fmd/common/fmd_api.c @@ -1731,6 +1731,23 @@ fmd_thr_signal(fmd_hdl_t *hdl, pthread_t tid) fmd_module_unlock(mp); } +void +fmd_thr_checkpoint(fmd_hdl_t *hdl) +{ + fmd_module_t *mp = fmd_api_module_lock(hdl); + pthread_t tid = pthread_self(); + + if (tid == mp->mod_thread->thr_tid || + fmd_idspace_getspecific(mp->mod_threads, tid) == NULL) { + fmd_api_error(mp, EFMD_THR_INVAL, "tid %u is not a valid " + "auxiliary thread id for module %s\n", tid, mp->mod_name); + } + + fmd_ckpt_save(mp); + + fmd_module_unlock(mp); +} + id_t fmd_timer_install(fmd_hdl_t *hdl, void *arg, fmd_event_t *ep, hrtime_t delta) { @@ -2257,7 +2274,27 @@ fmd_xprt_post(fmd_hdl_t *hdl, fmd_xprt_t *xp, nvlist_t *nvl, hrtime_t hrt) "fmd_xprt_post() cannot be called from _fmd_init()\n"); } - fmd_xprt_recv(xp, nvl, hrt); + fmd_xprt_recv(xp, nvl, hrt, FMD_B_FALSE); +} + +void +fmd_xprt_log(fmd_hdl_t *hdl, fmd_xprt_t *xp, nvlist_t *nvl, hrtime_t hrt) +{ + fmd_xprt_impl_t *xip = fmd_api_transport_impl(hdl, xp); + + /* + * fmd_xprt_recv() must block during startup waiting for fmd to globally + * clear FMD_XPRT_DSUSPENDED. As such, we can't allow it to be called + * from a module's _fmd_init() routine, because that would block + * fmd from completing initial module loading, resulting in a deadlock. + */ + if ((xip->xi_flags & FMD_XPRT_ISUSPENDED) && + (pthread_self() == xip->xi_queue->eq_mod->mod_thread->thr_tid)) { + fmd_api_error(fmd_api_module_lock(hdl), EFMD_XPRT_INVAL, + "fmd_xprt_log() cannot be called from _fmd_init()\n"); + } + + fmd_xprt_recv(xp, nvl, hrt, FMD_B_TRUE); } void diff --git a/usr/src/cmd/fm/fmd/common/fmd_api.h b/usr/src/cmd/fm/fmd/common/fmd_api.h index 897405a24a..2dd2c7e3a6 100644 --- a/usr/src/cmd/fm/fmd/common/fmd_api.h +++ b/usr/src/cmd/fm/fmd/common/fmd_api.h @@ -213,6 +213,7 @@ extern int fmd_serd_empty(fmd_hdl_t *, const char *); extern pthread_t fmd_thr_create(fmd_hdl_t *, void (*)(void *), void *); extern void fmd_thr_destroy(fmd_hdl_t *, pthread_t); extern void fmd_thr_signal(fmd_hdl_t *, pthread_t); +extern void fmd_thr_checkpoint(fmd_hdl_t *); extern id_t fmd_timer_install(fmd_hdl_t *, void *, fmd_event_t *, hrtime_t); extern void fmd_timer_remove(fmd_hdl_t *, id_t); @@ -252,6 +253,7 @@ extern uint64_t fmd_event_ena_create(fmd_hdl_t *); extern fmd_xprt_t *fmd_xprt_open(fmd_hdl_t *, uint_t, nvlist_t *, void *); extern void fmd_xprt_close(fmd_hdl_t *, fmd_xprt_t *); extern void fmd_xprt_post(fmd_hdl_t *, fmd_xprt_t *, nvlist_t *, hrtime_t); +extern void fmd_xprt_log(fmd_hdl_t *, fmd_xprt_t *, nvlist_t *, hrtime_t); extern void fmd_xprt_suspend(fmd_hdl_t *, fmd_xprt_t *); extern void fmd_xprt_resume(fmd_hdl_t *, fmd_xprt_t *); extern int fmd_xprt_error(fmd_hdl_t *, fmd_xprt_t *); diff --git a/usr/src/cmd/fm/fmd/common/fmd_api.map b/usr/src/cmd/fm/fmd/common/fmd_api.map index 2c1eac554e..268c26dbb0 100644 --- a/usr/src/cmd/fm/fmd/common/fmd_api.map +++ b/usr/src/cmd/fm/fmd/common/fmd_api.map @@ -109,6 +109,7 @@ fmd_thr_create = FUNCTION extern; fmd_thr_destroy = FUNCTION extern; fmd_thr_signal = FUNCTION extern; + fmd_thr_checkpoint = FUNCTION extern; fmd_timer_install = FUNCTION extern; fmd_timer_remove = FUNCTION extern; @@ -116,6 +117,7 @@ fmd_xprt_close = FUNCTION extern; fmd_xprt_error = FUNCTION extern; fmd_xprt_getspecific = FUNCTION extern; + fmd_xprt_log = FUNCTION extern; fmd_xprt_open = FUNCTION extern; fmd_xprt_post = FUNCTION extern; fmd_xprt_resume = FUNCTION extern; diff --git a/usr/src/cmd/fm/fmd/common/fmd_xprt.c b/usr/src/cmd/fm/fmd/common/fmd_xprt.c index ff58021f29..18e25f7efe 100644 --- a/usr/src/cmd/fm/fmd/common/fmd_xprt.c +++ b/usr/src/cmd/fm/fmd/common/fmd_xprt.c @@ -20,12 +20,10 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * FMD Transport Subsystem * @@ -999,7 +997,7 @@ fmd_xprt_send(fmd_xprt_t *xp) } void -fmd_xprt_recv(fmd_xprt_t *xp, nvlist_t *nvl, hrtime_t hrt) +fmd_xprt_recv(fmd_xprt_t *xp, nvlist_t *nvl, hrtime_t hrt, boolean_t logonly) { fmd_xprt_impl_t *xip = (fmd_xprt_impl_t *)xp; const fmd_xprt_rule_t *xrp; @@ -1007,7 +1005,7 @@ fmd_xprt_recv(fmd_xprt_t *xp, nvlist_t *nvl, hrtime_t hrt) fmd_event_t *e; char *class, *uuid, *code; - int isproto; + boolean_t isproto, isereport; uint64_t *tod; uint8_t ttl; @@ -1051,7 +1049,27 @@ fmd_xprt_recv(fmd_xprt_t *xp, nvlist_t *nvl, hrtime_t hrt) goto done; } - fmd_dprintf(FMD_DBG_XPRT, "xprt %u posting %s\n", xip->xi_id, class); + fmd_dprintf(FMD_DBG_XPRT, "xprt %u %s %s\n", xip->xi_id, + ((logonly == FMD_B_TRUE) ? "logging" : "posting"), class); + + isereport = (strncmp(class, FM_EREPORT_CLASS, + sizeof (FM_EREPORT_CLASS - 1)) == 0) ? FMD_B_TRUE : FMD_B_FALSE; + + /* + * The logonly flag should only be set for ereports. + */ + if ((logonly == FMD_B_TRUE) && (isereport == FMD_B_FALSE)) { + fmd_error(EFMD_XPRT_INVAL, "discarding nvlist %p: " + "logonly flag is not valid for class %s", + (void *)nvl, class); + + (void) pthread_mutex_lock(&xip->xi_stats_lock); + xip->xi_stats->xs_discarded.fmds_value.ui64++; + (void) pthread_mutex_unlock(&xip->xi_stats_lock); + + nvlist_free(nvl); + goto done; + } /* * If a time-to-live value is present in the event and is zero, drop @@ -1088,7 +1106,9 @@ fmd_xprt_recv(fmd_xprt_t *xp, nvlist_t *nvl, hrtime_t hrt) * control event. If a FMD_EVN_TOD member is found, create a protocol * event using this time. Otherwise create a protocol event using hrt. */ - if ((isproto = strncmp(class, FMD_CTL_CLASS, FMD_CTL_CLASS_LEN)) == 0) + isproto = (strncmp(class, FMD_CTL_CLASS, FMD_CTL_CLASS_LEN) == 0) ? + FMD_B_FALSE : FMD_B_TRUE; + if (isproto == FMD_B_FALSE) e = fmd_event_create(FMD_EVT_CTL, hrt, nvl, fmd_ctl_init(nvl)); else if (nvlist_lookup_uint64_array(nvl, FMD_EVN_TOD, &tod, &n) != 0) e = fmd_event_create(FMD_EVT_PROTOCOL, hrt, nvl, class); @@ -1135,7 +1155,7 @@ fmd_xprt_recv(fmd_xprt_t *xp, nvlist_t *nvl, hrtime_t hrt) * Record the event in the errlog if it is an ereport. This code will * be replaced later with a per-transport intent log instead. */ - if (fmd_event_match(e, FMD_EVT_PROTOCOL, FM_EREPORT_CLASS ".*")) { + if (isereport == FMD_B_TRUE) { (void) pthread_rwlock_rdlock(&dp->d_log_lock); fmd_log_append(dp->d_errlog, e, NULL); (void) pthread_rwlock_unlock(&dp->d_log_lock); @@ -1156,7 +1176,10 @@ fmd_xprt_recv(fmd_xprt_t *xp, nvlist_t *nvl, hrtime_t hrt) fmd_module_unlock(xip->xi_queue->eq_mod); } - if (isproto) + if (logonly == FMD_B_TRUE) { + fmd_event_hold(e); + fmd_event_rele(e); + } else if (isproto == FMD_B_TRUE) fmd_dispq_dispatch(dp->d_disp, e, class); else fmd_modhash_dispatch(dp->d_mod_hash, e); diff --git a/usr/src/cmd/fm/fmd/common/fmd_xprt.h b/usr/src/cmd/fm/fmd/common/fmd_xprt.h index 777e61daab..41054fdc35 100644 --- a/usr/src/cmd/fm/fmd/common/fmd_xprt.h +++ b/usr/src/cmd/fm/fmd/common/fmd_xprt.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,14 +20,13 @@ */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _FMD_XPRT_H #define _FMD_XPRT_H -#pragma ident "%Z%%M% %I% %E% SMI" #include <pthread.h> #include <libnvpair.h> @@ -138,7 +136,7 @@ extern void fmd_xprt_destroy(fmd_xprt_t *); extern void fmd_xprt_xsuspend(fmd_xprt_t *, uint_t); extern void fmd_xprt_xresume(fmd_xprt_t *, uint_t); extern void fmd_xprt_send(fmd_xprt_t *); -extern void fmd_xprt_recv(fmd_xprt_t *, nvlist_t *, hrtime_t); +extern void fmd_xprt_recv(fmd_xprt_t *, nvlist_t *, hrtime_t, boolean_t); extern void fmd_xprt_uuclose(fmd_xprt_t *, const char *); extern void fmd_xprt_subscribe(fmd_xprt_t *, const char *); diff --git a/usr/src/cmd/fm/modules/sun4v/etm/Makefile b/usr/src/cmd/fm/modules/sun4v/etm/Makefile index 35a58794ce..d4d21abf5e 100644 --- a/usr/src/cmd/fm/modules/sun4v/etm/Makefile +++ b/usr/src/cmd/fm/modules/sun4v/etm/Makefile @@ -19,19 +19,22 @@ # CDDL HEADER END # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#ident "%Z%%M% %I% %E% SMI" MODULE = etm CLASS = arch ARCH = sun4v -SRCS = etm.c etm_xport_api_dd.c +SRCS = etm.c etm_xport_api_dd.c etm_filter.c etm_ckpt.c include ../../Makefile.plugin -CPPFLAGS += -I$(SRC)/uts/sun4v -I$(ROOT)/usr/platform/sun4v/include -LDLIBS += -L$(ROOT)/usr/lib/fm -lldom +CPPFLAGS += -I$(SRC)/uts/sun4v \ + -I$(SRC)/uts/common \ + -I$(ROOT)/usr/platform/sun4v/include +LDLIBS += -L$(ROOT)/usr/lib/fm -lldom -ltopo LDFLAGS += -R/usr/lib/fm + +DYNFLAGS += -R/usr/lib diff --git a/usr/src/cmd/fm/modules/sun4v/etm/etm.c b/usr/src/cmd/fm/modules/sun4v/etm/etm.c index 6362b09225..95458b3adf 100644 --- a/usr/src/cmd/fm/modules/sun4v/etm/etm.c +++ b/usr/src/cmd/fm/modules/sun4v/etm/etm.c @@ -31,8 +31,6 @@ * plugin for sending/receiving FMA events to/from service processor */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * --------------------------------- includes -------------------------------- */ @@ -42,12 +40,16 @@ #include <sys/fm/ldom.h> #include <sys/strlog.h> #include <sys/syslog.h> +#include <sys/libds.h> #include <netinet/in.h> #include <fm/fmd_api.h> #include "etm_xport_api.h" #include "etm_etm_proto.h" #include "etm_impl.h" +#include "etm_iosvc.h" +#include "etm_filter.h" +#include "etm_ckpt.h" #include <pthread.h> #include <signal.h> @@ -60,6 +62,8 @@ #include <values.h> #include <alloca.h> #include <errno.h> +#include <dlfcn.h> +#include <link.h> #include <fcntl.h> #include <time.h> @@ -70,34 +74,44 @@ static void etm_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class); +static int +etm_send(fmd_hdl_t *hdl, fmd_xprt_t *xp, fmd_event_t *event, nvlist_t *nvl); + +static void +etm_send_to_remote_root(void *arg); + +static void +etm_recv_from_remote_root(void *arg); + /* * ------------------------- data structs for FMD ---------------------------- */ -static const fmd_hdl_ops_t fmd_ops = { +static fmd_hdl_ops_t fmd_ops = { etm_recv, /* fmdo_recv */ NULL, /* fmdo_timeout */ NULL, /* fmdo_close */ NULL, /* fmdo_stats */ NULL, /* fmdo_gc */ - NULL, /* fmdo_send */ + etm_send, /* fmdo_send */ }; static const fmd_prop_t fmd_props[] = { - { ETM_PROP_NM_XPORT_ADDRS, FMD_TYPE_STRING, "" }, - { ETM_PROP_NM_DEBUG_LVL, FMD_TYPE_INT32, "0" }, - { ETM_PROP_NM_DEBUG_MAX_EV_CNT, FMD_TYPE_INT32, "-1" }, - { ETM_PROP_NM_CONSOLE, FMD_TYPE_BOOL, "false" }, - { ETM_PROP_NM_SYSLOGD, FMD_TYPE_BOOL, "true" }, - { ETM_PROP_NM_FACILITY, FMD_TYPE_STRING, "LOG_DAEMON" }, - { ETM_PROP_NM_MAX_RESP_Q_LEN, FMD_TYPE_UINT32, "512" }, - { ETM_PROP_NM_BAD_ACC_TO_SEC, FMD_TYPE_UINT32, "1" }, + { ETM_PROP_NM_XPORT_ADDRS, FMD_TYPE_STRING, "" }, + { ETM_PROP_NM_DEBUG_LVL, FMD_TYPE_INT32, "0" }, + { ETM_PROP_NM_DEBUG_MAX_EV_CNT, FMD_TYPE_INT32, "-1" }, + { ETM_PROP_NM_CONSOLE, FMD_TYPE_BOOL, "false" }, + { ETM_PROP_NM_SYSLOGD, FMD_TYPE_BOOL, "true" }, + { ETM_PROP_NM_FACILITY, FMD_TYPE_STRING, "LOG_DAEMON" }, + { ETM_PROP_NM_MAX_RESP_Q_LEN, FMD_TYPE_UINT32, "512" }, + { ETM_PROP_NM_BAD_ACC_TO_SEC, FMD_TYPE_UINT32, "1" }, + { ETM_PROP_NM_FMA_RESP_WAIT_TIME, FMD_TYPE_INT32, "240" }, { NULL, 0, NULL } }; static const fmd_hdl_info_t fmd_info = { - "FMA Event Transport Module", "1.1", &fmd_ops, fmd_props + "FMA Event Transport Module", "1.2", &fmd_ops, fmd_props }; /* @@ -108,6 +122,9 @@ static const fmd_hdl_info_t fmd_info = { #define ETM_MISC_BUF_SZ (4 * 1024) +static uint32_t +etm_ldom_type = LDOM_TYPE_LEGACY; + /* try limit for IO operations w/ capped exp backoff sleep on retry */ /* @@ -129,7 +146,7 @@ static const fmd_hdl_info_t fmd_info = { /* amount to increment protocol transaction id on each new send */ -#define ETM_XID_INC (2) +#define ETM_XID_INC (2) typedef struct etm_resp_q_ele { @@ -147,7 +164,7 @@ typedef struct etm_resp_q_ele { */ static fmd_hdl_t -*init_hdl = NULL; /* used in mem allocator at init time */ +*init_hdl = NULL; /* used in mem allocator and several other places */ static int etm_debug_lvl = 0; /* debug level: 0 is off, 1 is on, 2 is more, etc */ @@ -198,7 +215,8 @@ static uint32_t etm_xid_ver_negot = 0; /* xid of last CONTROL msg sent requesting ver negot */ static uint32_t -etm_xid_posted_ev = 0; /* xid of last FMA_EVENT msg/event posted OK to FMD */ +etm_xid_posted_logged_ev = 0; + /* xid of last FMA_EVENT msg/event posted OK to FMD */ static uint32_t etm_xid_posted_sa = 0; /* xid of last ALERT msg/event posted OK to syslog */ @@ -206,6 +224,9 @@ etm_xid_posted_sa = 0; /* xid of last ALERT msg/event posted OK to syslog */ static uint8_t etm_resp_ver = ETM_PROTO_V1; /* proto ver [negotiated] for msg sends */ +static uint32_t +etm_fma_resp_wait_time = 30; /* time (sec) wait for fma event resp */ + static pthread_mutex_t etm_write_lock = PTHREAD_MUTEX_INITIALIZER; /* for write operations */ @@ -324,7 +345,6 @@ static struct stats { /* FMD entry point bad arguments */ - fmd_stat_t etm_fmd_recv_badargs; fmd_stat_t etm_fmd_init_badargs; fmd_stat_t etm_fmd_fini_badargs; @@ -486,12 +506,10 @@ static struct stats { /* FMD entry point bad arguments */ - { "etm_fmd_recv_badargs", FMD_TYPE_UINT64, - "bad arguments from fmd_recv entry point" }, { "etm_fmd_init_badargs", FMD_TYPE_UINT64, - "bad arguments from fmd_init entry point" }, + "bad arguments from fmd_init entry point" }, { "etm_fmd_fini_badargs", FMD_TYPE_UINT64, - "bad arguments from fmd_fini entry point" }, + "bad arguments from fmd_fini entry point" }, /* Alert logging errors */ @@ -506,6 +524,132 @@ static struct stats { "xport resets after xport API failure" } }; + +/* + * -------------------- global data for Root ldom------------------------- + */ + +ldom_hdl_t +*etm_lhp = NULL; /* ldom pointer */ + +static void *etm_dl_hdl = (void *)NULL; +static const char *etm_dl_path = "libds.so.1"; +static int etm_dl_mode = (RTLD_NOW | RTLD_LOCAL); + +static int(*etm_ds_svc_reg)(ds_capability_t *cap, ds_ops_t *ops) = + (int (*)(ds_capability_t *cap, ds_ops_t *ops))NULL; +static int(*etm_ds_clnt_reg)(ds_capability_t *cap, ds_ops_t *ops) = + (int (*)(ds_capability_t *cap, ds_ops_t *ops))NULL; +static int(*etm_ds_send_msg)(ds_hdl_t hdl, void *buf, size_t buflen) = + (int (*)(ds_hdl_t hdl, void *buf, size_t buflen))NULL; +static int(*etm_ds_recv_msg)(ds_hdl_t hdl, void *buf, size_t buflen, + size_t *msglen) = + (int (*)(ds_hdl_t hdl, void *buf, size_t buflen, size_t *msglen))NULL; +static int (*etm_ds_fini)(void) = (int (*)(void))NULL; + +static pthread_mutex_t +iosvc_list_lock = PTHREAD_MUTEX_INITIALIZER; + +static pthread_t +etm_async_e_tid = NULL; /* thread id of io svc async event handler */ + +static etm_proto_v1_ev_hdr_t iosvc_hdr = { + ETM_PROTO_MAGIC_NUM, /* magic number */ + ETM_PROTO_V1, /* default to V1, not checked */ + ETM_MSG_TYPE_FMA_EVENT, /* Root Domain inteoduces only FMA events */ + 0, /* sub-type */ + 0, /* pad */ + 0, /* add the xid at the Q send time */ + ETM_PROTO_V1_TIMEOUT_NONE, + 0 /* ev_lens, 0-termed, after 1 FMA event */ +}; + +/* + * static iosvc_list + */ +static etm_iosvc_t iosvc_list[NUM_OF_ROOT_DOMAINS] = { + {"", 0}, {"", 0}, {"", 0}, {"", 0}, {"", 0}, {"", 0}, + {"", 0}, {"", 0} +}; + +static etm_iosvc_t io_svc = { + "\0", /* ldom_name */ + PTHREAD_COND_INITIALIZER, /* nudges */ + PTHREAD_MUTEX_INITIALIZER, /* protects the iosvc msg Q */ + NULL, /* iosvc msg Q head */ + NULL, /* iosvc msg Q tail */ + 0, /* msg Q current length */ + 100, /* msg Q max length */ + 0, /* current transaction id */ + 0, /* xid of last event posted to FMD */ + DS_INVALID_HDL, /* DS handle */ + NULL, /* fmd xprt handle */ + NULL, /* tid 4 send to remote RootDomain */ + NULL, /* tid 4 recv from remote RootDomain */ + PTHREAD_COND_INITIALIZER, /* nudges etm_send_to_remote_root */ + PTHREAD_MUTEX_INITIALIZER, /* protects msg_ack_cv */ + 0, /* send/recv threads are not dying */ + 0, /* flag for start sending msg Q */ + 0 /* indicate if the ACK has come */ +}; +etm_iosvc_t *io_svc_p = &io_svc; + + +static uint32_t +flags; /* flags for fmd_xprt_open */ + +static etm_async_event_ele_t +async_event_q[ASYNC_EVENT_Q_SIZE]; /* holds the async events */ + +static uint32_t +etm_async_q_head = 0; /* ptr to cur head of async event queue */ + +static uint32_t +etm_async_q_tail = 0; /* ptr to cur tail of async event queue */ + +static uint32_t +etm_async_q_cur_len = 0; /* cur length (ele cnt) of async event queue */ + +static uint32_t +etm_async_q_max_len = ASYNC_EVENT_Q_SIZE; + /* max length (ele cnt) of async event queue */ + +static pthread_cond_t +etm_async_event_q_cv = PTHREAD_COND_INITIALIZER; + /* nudges async event handler */ + +static pthread_mutex_t +etm_async_event_q_lock = PTHREAD_MUTEX_INITIALIZER; + /* protects async event q */ + +static ds_ver_t +etm_iosvc_vers[] = { { 1, 0} }; + +#define ETM_NVERS (sizeof (etm_iosvc_vers) / sizeof (ds_ver_t)) + +static ds_capability_t +iosvc_caps = { + "ETM", /* svc_id */ + etm_iosvc_vers, /* vers */ + ETM_NVERS /* number of vers */ +}; + +static void +etm_iosvc_reg_handler(ds_hdl_t hdl, ds_cb_arg_t arg, ds_ver_t *ver, + ds_domain_hdl_t did); + +static void +etm_iosvc_unreg_handler(ds_hdl_t hdl, ds_cb_arg_t arg); + +static ds_ops_t +iosvc_ops = { + etm_iosvc_reg_handler, /* ds_reg_cb */ + etm_iosvc_unreg_handler, /* ds_unreg_cb */ + NULL, /* ds_data_cb */ + NULL /* cb_arg */ +}; + + /* * -------------------------- support functions ------------------------------ */ @@ -519,6 +663,7 @@ static struct stats { * the outputted hex digits, while Linux and VxWorks do. */ + /* * etm_show_time - display the current time of day (for debugging) using * the given FMD module handle and annotation string @@ -681,18 +826,18 @@ etm_io_op(fmd_hdl_t *hdl, char *err_substr, etm_xport_conn_t conn, return (-EINVAL); } switch (io_op) { - case ETM_IO_OP_RD: - io_func_ptr = etm_xport_read; - io_retry_stat = etm_stats.etm_xport_rd_retry; - io_fail_stat = etm_stats.etm_xport_rd_fail; - break; - case ETM_IO_OP_WR: - io_func_ptr = etm_xport_write; - io_retry_stat = etm_stats.etm_xport_wr_retry; - io_fail_stat = etm_stats.etm_xport_wr_fail; - break; - default: - return (-EINVAL); + case ETM_IO_OP_RD: + io_func_ptr = etm_xport_read; + io_retry_stat = etm_stats.etm_xport_rd_retry; + io_fail_stat = etm_stats.etm_xport_rd_fail; + break; + case ETM_IO_OP_WR: + io_func_ptr = etm_xport_write; + io_retry_stat = etm_stats.etm_xport_wr_retry; + io_fail_stat = etm_stats.etm_xport_wr_fail; + break; + default: + return (-EINVAL); } if (byte_cnt == 0) { return (byte_cnt); /* nop */ @@ -1166,7 +1311,7 @@ etm_hdr_write(fmd_hdl_t *hdl, etm_xport_conn_t conn, nvlist_t *evp, */ static int -etm_post_to_fmd(fmd_hdl_t *hdl, nvlist_t *evp) +etm_post_to_fmd(fmd_hdl_t *hdl, fmd_xprt_t *fmd_xprt, nvlist_t *evp) { ssize_t ev_sz; /* sizeof *evp */ @@ -1175,7 +1320,7 @@ etm_post_to_fmd(fmd_hdl_t *hdl, nvlist_t *evp) if (etm_debug_lvl >= 2) { etm_show_time(hdl, "ante ev post"); } - fmd_xprt_post(hdl, etm_fmd_xprt, evp, 0); + fmd_xprt_post(hdl, fmd_xprt, evp, 0); etm_stats.etm_wr_fmd_fmaevent.fmds_value.ui64++; etm_stats.etm_wr_fmd_bytes.fmds_value.ui64 += ev_sz; if (etm_debug_lvl >= 1) { @@ -1341,6 +1486,483 @@ func_ret: } /* etm_req_ver_negot() */ + + +/* + * etm_iosvc_msg_enq - add element to tail of ETM iosvc msg queue + * etm_iosvc_msg_deq - del element from head of ETM iosvc msg queue + * need to grab the mutex lock before calling this routine + * return >0 for success, or -errno value + */ +static int +etm_iosvc_msg_enq(fmd_hdl_t *hdl, etm_iosvc_t *iosvc, etm_iosvc_q_ele_t *msgp) +{ + etm_iosvc_q_ele_t *newp; /* ptr to new msg q ele */ + + if (iosvc->msg_q_cur_len >= iosvc->msg_q_max_len) { + fmd_hdl_debug(hdl, "warning: enq to full msg queue\n"); + return (-E2BIG); + } + + newp = fmd_hdl_zalloc(hdl, sizeof (*newp), FMD_SLEEP); + (void) memcpy(newp, msgp, sizeof (*newp)); + newp->msg_nextp = NULL; + + if (iosvc->msg_q_cur_len == 0) { + iosvc->msg_q_head = newp; + } else { + iosvc->msg_q_tail->msg_nextp = newp; + } + + iosvc->msg_q_tail = newp; + iosvc->msg_q_cur_len++; + fmd_hdl_debug(hdl, "info: current msg queue length %d\n", + iosvc->msg_q_cur_len); + + return (1); + +} /* etm_iosvc_msg_enq() */ + +static int +etm_iosvc_msg_deq(fmd_hdl_t *hdl, etm_iosvc_t *iosvc, etm_iosvc_q_ele_t *msgp) +{ + etm_iosvc_q_ele_t *oldp; /* ptr to old msg q ele */ + + if (iosvc->msg_q_cur_len == 0) { + fmd_hdl_debug(hdl, "warning: deq from empty responder queue\n"); + return (-ENOENT); + } + + (void) memcpy(msgp, iosvc->msg_q_head, sizeof (*msgp)); + msgp->msg_nextp = NULL; + + oldp = iosvc->msg_q_head; + iosvc->msg_q_head = iosvc->msg_q_head->msg_nextp; + + /* + * free the mem alloc-ed in etm_iosvc_msg_enq() + */ + fmd_hdl_free(hdl, oldp, sizeof (*oldp)); + + iosvc->msg_q_cur_len--; + if (iosvc->msg_q_cur_len == 0) { + iosvc->msg_q_tail = NULL; + } + + return (1); + +} /* etm_iosvc_msg_deq() */ + + +/* + * etm_msg_enq_head(): + * enq the msg to the head of the Q. + * If the Q is full, drop the msg at the tail then enq the msg at head. + * need to grab mutex lock iosvc->msg_q_lock before calling this routine. + */ +static void +etm_msg_enq_head(fmd_hdl_t *fmd_hdl, etm_iosvc_t *iosvc, + etm_iosvc_q_ele_t *msg_ele) +{ + + etm_iosvc_q_ele_t *newp; /* iosvc msg ele ptr */ + + if (iosvc->msg_q_cur_len >= iosvc->msg_q_max_len) { + fmd_hdl_debug(fmd_hdl, + "warning: add to head of a full msg queue." + " Drop the msg at the tail\n"); + /* + * drop the msg at the tail + */ + newp = iosvc->msg_q_head; + while (newp->msg_nextp != iosvc->msg_q_tail) { + newp = newp->msg_nextp; + } + + /* + * free the msg in iosvc->msg_q_tail->msg + * free the mem pointed to by iosvc->msg_q_tail + */ + fmd_hdl_free(fmd_hdl, iosvc->msg_q_tail->msg, + iosvc->msg_q_tail->msg_size); + fmd_hdl_free(fmd_hdl, iosvc->msg_q_tail, sizeof (*newp)); + iosvc->msg_q_tail = newp; + iosvc->msg_q_tail->msg_nextp = NULL; + iosvc->msg_q_cur_len--; + } + + /* + * enq the msg to the head + */ + newp = fmd_hdl_zalloc(fmd_hdl, sizeof (*newp), FMD_SLEEP); + (void) memcpy(newp, msg_ele, sizeof (*newp)); + if (iosvc->msg_q_cur_len == 0) { + newp->msg_nextp = NULL; + iosvc->msg_q_tail = newp; + } else { + newp->msg_nextp = iosvc->msg_q_head; + } + iosvc->msg_q_head = newp; + iosvc->msg_q_cur_len++; +} /* etm_msg_enq_head() */ + +/* + * etm_isovc_cleanup(): + * clean up what's in the passed-in iosvc struct, including the msg Q. + */ +static void +etm_iosvc_cleanup(fmd_hdl_t *fmd_hdl, etm_iosvc_t *iosvc) +{ + + etm_iosvc_q_ele_t msg_ele; /* io svc msg Q ele */ + + iosvc->thr_is_dying = 1; + + if (iosvc->send_tid != NULL) { + fmd_thr_signal(fmd_hdl, iosvc->send_tid); + fmd_thr_destroy(fmd_hdl, iosvc->send_tid); + iosvc->send_tid = NULL; + } /* if io svc send thread was created ok */ + + if (iosvc->recv_tid != NULL) { + fmd_thr_signal(fmd_hdl, iosvc->recv_tid); + fmd_thr_destroy(fmd_hdl, iosvc->recv_tid); + iosvc->recv_tid = NULL; + } /* if root domain recv thread was created */ + + iosvc->ldom_name[0] = '\0'; + + iosvc->ds_hdl = DS_INVALID_HDL; + + if (iosvc->fmd_xprt != NULL) { + fmd_xprt_close(fmd_hdl, iosvc->fmd_xprt); + iosvc->fmd_xprt = NULL; + } /* if fmd-xprt has been opened */ + + (void) pthread_mutex_lock(&iosvc->msg_q_lock); + while (iosvc->msg_q_cur_len > 0) { + (void) etm_iosvc_msg_deq(fmd_hdl, iosvc, &msg_ele); + fmd_hdl_free(fmd_hdl, msg_ele.msg, msg_ele.msg_size); + } + (void) pthread_mutex_unlock(&iosvc->msg_q_lock); + + return; + +} /* etm_iosvc_cleanup() */ + +/* + * etm_iosvc_lookup(using ldom_name or ds_hdl when ldom_name is empty) + * not found, create one, add to iosvc_list + */ +etm_iosvc_t * +etm_iosvc_lookup(fmd_hdl_t *fmd_hdl, char *ldom_name, ds_hdl_t ds_hdl, + boolean_t iosvc_create) +{ + uint32_t i; /* for loop var */ + int32_t first_empty_slot = -1; /* remember that */ + + for (i = 0; i < NUM_OF_ROOT_DOMAINS; i++) { + if (ldom_name[0] == '\0') { + /* + * search by hdl passed in + * the only time this is used is at ds_unreg_cb time. + * there is no ldom name, only the valid ds_hdl. + * find an iosvc with the matching ds_hdl. + * ignore the iosvc_create flag, should never need to + * create an iosvc for ds_unreg_cb + */ + if (ds_hdl == iosvc_list[i].ds_hdl) { + if (etm_debug_lvl >= 2) { + fmd_hdl_debug(fmd_hdl, + "info: found an iosvc at slot %d w/ ds_hdl %d \n", + i, iosvc_list[i].ds_hdl); + } + if (iosvc_list[i].ldom_name[0] != '\0') + if (etm_debug_lvl >= 2) { + fmd_hdl_debug(fmd_hdl, + "info: found an iosvc w/ ldom_name %s \n", + iosvc_list[i].ldom_name); + } + return (&iosvc_list[i]); + } else { + continue; + } + } else if (iosvc_list[i].ldom_name[0] != '\0') { + /* + * this is an non-empty iosvc structure slot + */ + if (strcmp(ldom_name, iosvc_list[i].ldom_name) == 0) { + /* + * found an iosvc structure that matches the + * passed in ldom_name, return the ptr + */ + if (etm_debug_lvl >= 2) { + fmd_hdl_debug(fmd_hdl, "info: found an " + "iosvc at slot %d w/ ds_hdl %d \n", + i, iosvc_list[i].ds_hdl); + fmd_hdl_debug(fmd_hdl, "info: found an " + "iosvc w/ ldom_name %s \n", + iosvc_list[i].ldom_name); + } + return (&iosvc_list[i]); + } else { + /* + * non-empty slot with no-matching name, + * move on to next slot. + */ + continue; + } + } else { + /* + * found the 1st slot with ldom name being empty + * remember the slot #, will be used for creating one + */ + if (first_empty_slot == -1) { + first_empty_slot = i; + } + } + } + if (iosvc_create == B_TRUE && first_empty_slot >= 0) { + /* + * this is the case we need to add an iosvc at first_empty_slot + * for the ldom_name at iosvc_list[first_empty_slot] + */ + fmd_hdl_debug(fmd_hdl, + "info: create an iosvc with ldom name %s\n", + ldom_name); + i = first_empty_slot; + (void) memcpy(&iosvc_list[i], &io_svc, sizeof (etm_iosvc_t)); + (void) strcpy(iosvc_list[i].ldom_name, ldom_name); + fmd_hdl_debug(fmd_hdl, "info: iosvc #%d has ldom name %s\n", + i, iosvc_list[i].ldom_name); + return (&iosvc_list[i]); + } else { + return (NULL); + } + +} /* etm_iosvc_lookup() */ + + +/* + * etm_ckpt_remove: + * remove the ckpt for the iosvc element + */ +static void +etm_ckpt_remove(fmd_hdl_t *hdl, etm_iosvc_q_ele_t *ele) { + int err; /* temp error */ + nvlist_t *evp = NULL; /* event pointer */ + etm_proto_v1_ev_hdr_t *hdrp; /* hdr for FMA_EVENT */ + char *buf; /* packed event pointer */ + + if ((ele->ckpt_flag == ETM_CKPT_NOOP) || + (etm_ldom_type != LDOM_TYPE_CONTROL)) { + return; + } + + /* the pointer to the packed event in the etm message */ + hdrp = (etm_proto_v1_ev_hdr_t *)((ptrdiff_t)ele->msg); + buf = (char *)((ptrdiff_t)hdrp + sizeof (*hdrp) + + (1 * sizeof (hdrp->ev_lens[0]))); + + /* unpack it, then uncheckpoited it */ + if ((err = nvlist_unpack(buf, hdrp->ev_lens[0], &evp, 0)) != 0) { + fmd_hdl_debug(hdl, "failed to unpack event(rc=%d)\n", err); + return; + } + (void) etm_ckpt_delete(hdl, evp); + nvlist_free(evp); +} + +/* + * etm_send_ds_msg() + * call ds_send_msg() to send the msg passed in. + * timedcond_wait for the ACK to come back. + * if the ACK doesn't come in the specified time, retrun -EAGAIN. + * other wise, return 1. + */ +int +etm_send_ds_msg(fmd_hdl_t *fmd_hdl, boolean_t ckpt_remove, etm_iosvc_t *iosvc, + etm_iosvc_q_ele_t *msg_ele, etm_proto_v1_ev_hdr_t *evhdrp) +{ + uint32_t rc; /* for return code */ + + struct timeval tv; + struct timespec timeout; + + + /* + * call ds_send_msg(). Return (-EAGAIN) if not successful + */ + if ((rc = (*etm_ds_send_msg)(iosvc->ds_hdl, msg_ele->msg, + msg_ele->msg_size)) != 0) { + fmd_hdl_debug(fmd_hdl, "info: ds_send_msg rc %d xid %d\n", + rc, evhdrp->ev_pp.pp_xid); + return (-EAGAIN); + } + + /* + * wait on the cv for resp msg for cur_send_xid + */ + (void *) pthread_mutex_lock(&iosvc->msg_ack_lock); + + (void) gettimeofday(&tv, 0); + timeout.tv_sec = tv.tv_sec + etm_fma_resp_wait_time; + timeout.tv_nsec = 0; + + fmd_hdl_debug(fmd_hdl, "info: waiting on msg_ack_cv for ldom %s\n", + iosvc->ldom_name); + rc = pthread_cond_timedwait(&iosvc->msg_ack_cv, &iosvc->msg_ack_lock, + &timeout); + (void *) pthread_mutex_unlock(&iosvc->msg_ack_lock); + fmd_hdl_debug(fmd_hdl, "info: msg_ack_cv returns with rc %d\n", rc); + + /* + * check to see if ack_ok is non-zero + * if non-zero, resp msg has been received + */ + if (iosvc->ack_ok != 0) { + /* + * ACK came ok, this send is successful, + * tell the caller ready to send next. + * free mem alloc-ed in + * etm_pack_ds_msg + */ + if (ckpt_remove == B_TRUE && + etm_ldom_type == LDOM_TYPE_CONTROL) { + etm_ckpt_remove(fmd_hdl, msg_ele); + } + fmd_hdl_free(fmd_hdl, msg_ele->msg, msg_ele->msg_size); + iosvc->cur_send_xid++; + return (1); + } else { + /* + * the ACK did not come on time + * tell the caller to resend cur_send_xid + */ + return (-EAGAIN); + } /* iosvc->ack_ok != 0 */ +} /* etm_send_ds_msg() */ + +/* + * both events from fmdo_send entry point and from SP are using the + * etm_proto_v1_ev_hdr_t as its header and it will be the same header for all + * ds send/recv msgs. + * Idealy, we should use the hdr coming with the SP FMA event. Since fmdo_send + * entry point can be called before FMA events from SP, we can't rely on + * the SP FMA event hdr. Use the static hdr for packing ds msgs for fmdo_send + * events. + * return >0 for success, or -errno value + * Design assumption: there is one FMA event per ds msg + */ +int +etm_pack_ds_msg(fmd_hdl_t *fmd_hdl, etm_iosvc_t *iosvc, + etm_proto_v1_ev_hdr_t *ev_hdrp, size_t hdr_sz, nvlist_t *evp, + etm_pack_msg_type_t msg_type, uint_t ckpt_opt) +{ + etm_proto_v1_ev_hdr_t *hdrp; /* for FMA_EVENT msg */ + uint32_t *lenp; /* ptr to FMA event length */ + size_t evsz; /* packed FMA event size */ + char *buf; + uint32_t rc; /* for return code */ + char *msg; /* body of msg to be Qed */ + + etm_iosvc_q_ele_t msg_ele; /* io svc msg Q ele */ + etm_proto_v1_ev_hdr_t *evhdrp; + + + if (ev_hdrp == NULL) { + hdrp = &iosvc_hdr; + } else { + hdrp = ev_hdrp; + } + + /* + * determine hdr_sz if 0, otherwise use the one passed in hdr_sz + */ + + if (hdr_sz == 0) { + hdr_sz = sizeof (*hdrp) + (1 * sizeof (hdrp->ev_lens[0])); + } + + /* + * determine evp size + */ + (void) nvlist_size(evp, &evsz, NV_ENCODE_XDR); + + /* indicate 1 FMA event, no network encoding, and 0-terminate */ + lenp = &hdrp->ev_lens[0]; + *lenp = evsz; + + /* + * now the total of mem needs to be alloc-ed/ds msg size is + * hdr_sz + evsz + * msg will be freed in etm_send_to_remote_root() after ds_send_msg() + */ + msg = fmd_hdl_zalloc(fmd_hdl, hdr_sz + evsz, FMD_SLEEP); + + + /* + * copy hdr, 0 terminate the length vector, and then evp + */ + (void) memcpy(msg, hdrp, sizeof (*hdrp)); + hdrp = (etm_proto_v1_ev_hdr_t *)((ptrdiff_t)msg); + lenp = &hdrp->ev_lens[0]; + lenp++; + *lenp = 0; + + buf = fmd_hdl_zalloc(fmd_hdl, evsz, FMD_SLEEP); + (void) nvlist_pack(evp, (char **)&buf, &evsz, NV_ENCODE_XDR, 0); + (void) memcpy(msg + hdr_sz, buf, evsz); + fmd_hdl_free(fmd_hdl, buf, evsz); + + fmd_hdl_debug(fmd_hdl, "info: hdr_sz= %d evsz= %d in etm_pack_ds_msg" + "for ldom %s\n", hdr_sz, evsz, iosvc->ldom_name); + msg_ele.msg = msg; + msg_ele.msg_size = hdr_sz + evsz; + msg_ele.ckpt_flag = ckpt_opt; + + /* + * decide what to do with the msg: + * if SP ereports (msg_type == SP_MSG), always enq the msg + * if not SP ereports, ie, fmd xprt control msgs, enq it _only_ after + * resource.fm.xprt.run has been sent (which sets start_sending_Q to 1) + */ + if ((msg_type == SP_MSG) || + (msg_type != SP_MSG) && (iosvc->start_sending_Q == 1)) { + /* + * this is the case when the msg needs to be enq-ed + */ + (void) pthread_mutex_lock(&iosvc->msg_q_lock); + rc = etm_iosvc_msg_enq(fmd_hdl, iosvc, &msg_ele); + if ((rc > 0) && (ckpt_opt & ETM_CKPT_SAVE) && + (etm_ldom_type == LDOM_TYPE_CONTROL)) { + (void) etm_ckpt_add(fmd_hdl, evp); + } + if (iosvc->msg_q_cur_len == 1) + (void) pthread_cond_signal(&iosvc->msg_q_cv); + (void) pthread_mutex_unlock(&iosvc->msg_q_lock); + } else { + /* + * fmd RDWR xprt procotol startup msgs, send it now! + */ + iosvc->ack_ok = 0; + evhdrp = (etm_proto_v1_ev_hdr_t *)((ptrdiff_t)msg_ele.msg); + evhdrp->ev_pp.pp_xid = iosvc->cur_send_xid + 1; + while (!iosvc->ack_ok && iosvc->ds_hdl != DS_INVALID_HDL && + !etm_is_dying) { + if (etm_send_ds_msg(fmd_hdl, B_FALSE, iosvc, &msg_ele, + evhdrp) < 0) { + continue; + } + } + if (msg_type == FMD_XPRT_RUN_MSG) + iosvc->start_sending_Q = 1; + } + + return (rc); + +} /* etm_pack_ds_msg() */ + /* * Design_Note: For all etm_resp_q_*() functions and etm_resp_q_* globals, * the mutex etm_resp_q_lock must be held by the caller. @@ -1601,9 +2223,11 @@ etm_handle_new_conn(fmd_hdl_t *hdl, etm_xport_conn_t conn) etm_proto_v1_ctl_hdr_t *ctl_hdrp; /* for CONTROL msg */ etm_proto_v1_resp_hdr_t *resp_hdrp; /* for RESPONSE msg */ etm_proto_v3_sa_hdr_t *sa_hdrp; /* for ALERT msg */ + etm_iosvc_t *iosvc; /* iosvc data structure */ int32_t resp_code; /* response code */ ssize_t enq_rv; /* resp_q enqueue status */ size_t hdr_sz; /* sizeof header */ + size_t evsz; /* FMA event size */ uint8_t *body_buf; /* msg body buffer */ uint32_t body_sz; /* sizeof body_buf */ uint32_t ev_cnt; /* count of FMA events */ @@ -1612,6 +2236,10 @@ etm_handle_new_conn(fmd_hdl_t *hdl, etm_xport_conn_t conn) char *class; /* FMA event class */ ssize_t i, n; /* gen use */ int should_reset_xport; /* bool to reset xport */ + char ldom_name[MAX_LDOM_NAME]; /* ldom name */ + int rc; /* return code */ + uint64_t did; /* domain id */ + if (etm_debug_lvl >= 2) { etm_show_time(hdl, "ante conn handle"); @@ -1686,11 +2314,11 @@ etm_handle_new_conn(fmd_hdl_t *hdl, etm_xport_conn_t conn) * if a dup then resend response but skip repost to FMD */ - if (ev_hdrp->ev_pp.pp_xid == etm_xid_posted_ev) { + if (ev_hdrp->ev_pp.pp_xid == etm_xid_posted_logged_ev) { enq_rv = etm_maybe_enq_response(hdl, conn, ev_hdrp, hdr_sz, 0); fmd_hdl_debug(hdl, "info: skipping dup FMA event post " - "xid 0x%x\n", etm_xid_posted_ev); + "xid 0x%x\n", etm_xid_posted_logged_ev); etm_stats.etm_rd_dup_fmaevent.fmds_value.ui64++; goto func_ret; } @@ -1720,6 +2348,7 @@ etm_handle_new_conn(fmd_hdl_t *hdl, etm_xport_conn_t conn) bp += ev_hdrp->ev_lens[i]; continue; } + if (etm_debug_lvl >= 1) { (void) nvlist_lookup_string(evp, FM_CLASS, &class); @@ -1729,10 +2358,61 @@ etm_handle_new_conn(fmd_hdl_t *hdl, etm_xport_conn_t conn) fmd_hdl_debug(hdl, "info: FMA event %p " "class %s\n", evp, class); } - resp_code = etm_post_to_fmd(hdl, evp); - if (resp_code >= 0) { - etm_xid_posted_ev = ev_hdrp->ev_pp.pp_xid; + + rc = nvlist_size(evp, &evsz, NV_ENCODE_XDR); + fmd_hdl_debug(hdl, + "info: evp size before pack ds msg %d\n", evsz); + ldom_name[0] = '\0'; + rc = etm_filter_find_ldom_id(hdl, evp, ldom_name, + MAX_LDOM_NAME, &did); + + /* + * if rc is zero and the ldom_name is not "primary", + * the evp belongs to a root domain, put the evp in an + * outgoing etm queue, + * in all other cases, whether ldom_name is primary or + * can't find a ldom name, call etm_post_to_fmd + */ + if ((rc == 0) && strcmp(ldom_name, "primary") && + strcmp(ldom_name, "")) { + /* + * use the ldom_name, guaranteered at this point + * to be a valid ldom name/non-NULL, to find the + * iosvc data. + * add an iosvc struct if can not find one + */ + (void) pthread_mutex_unlock(&iosvc_list_lock); + iosvc = etm_iosvc_lookup(hdl, ldom_name, + DS_INVALID_HDL, B_TRUE); + (void) pthread_mutex_unlock(&iosvc_list_lock); + if (iosvc == NULL) { + fmd_hdl_debug(hdl, + "error: can't find iosvc for ldom " + "name %s\n", ldom_name); + } else { + resp_code = 0; + (void) etm_pack_ds_msg(hdl, iosvc, + ev_hdrp, hdr_sz, evp, + SP_MSG, ETM_CKPT_SAVE); + /* + * call the new fmd_xprt_log() + */ + fmd_xprt_log(hdl, etm_fmd_xprt, evp, 0); + etm_xid_posted_logged_ev = + ev_hdrp->ev_pp.pp_xid; + } + } else { + /* + * post the fma event to the control fmd + */ + resp_code = etm_post_to_fmd(hdl, etm_fmd_xprt, + evp); + if (resp_code >= 0) { + etm_xid_posted_logged_ev = + ev_hdrp->ev_pp.pp_xid; + } } + evp = NULL; enq_rv = etm_maybe_enq_response(hdl, conn, ev_hdrp, hdr_sz, resp_code); @@ -1943,6 +2623,12 @@ etm_server(void *arg) fmd_hdl_debug(hdl, "info: connection server starting\n"); + /* + * Restore the checkpointed events and dispatch them before starting to + * receive more events from the sp. + */ + etm_ckpt_recover(hdl); + while (!etm_is_dying) { if ((conn = etm_xport_accept(hdl, NULL)) == NULL) { @@ -2080,6 +2766,945 @@ etm_init_free(void *addr, size_t size) } /* + * ---------------------root ldom support functions ----------------------- + */ + +/* + * use a static array async_event_q instead of dynamicaly allocated mem queue + * for etm_async_q_enq and etm_async_q_deq. + * This is not running in an fmd aux thread, can't use the fmd_hdl_* funcs. + * caller needs to grab the mutex lock before calling this func. + * return >0 for success, or -errno value + */ +static int +etm_async_q_enq(etm_async_event_ele_t *async_e) +{ + + if (etm_async_q_cur_len >= etm_async_q_max_len) { + /* etm_stats.etm_enq_drop_async_q.fmds_value.ui64++; */ + return (-E2BIG); + } + + (void) memcpy(&async_event_q[etm_async_q_tail], async_e, + sizeof (*async_e)); + + etm_async_q_tail++; + if (etm_async_q_tail == etm_async_q_max_len) { + etm_async_q_tail = 0; + } + etm_async_q_cur_len++; + +/* etm_stats.etm_async_q_cur_len.fmds_value.ui64 = etm_async_q_cur_len; */ + + return (1); + +} /* etm_async_q_enq() */ + + +static int +etm_async_q_deq(etm_async_event_ele_t *async_e) +{ + + if (etm_async_q_cur_len == 0) { + /* etm_stats.etm_deq_drop_async_q.fmds_value.ui64++; */ + return (-ENOENT); + } + + (void) memcpy(async_e, &async_event_q[etm_async_q_head], + sizeof (*async_e)); + + etm_async_q_head++; + if (etm_async_q_head == etm_async_q_max_len) { + etm_async_q_head = 0; + } + etm_async_q_cur_len--; +/* etm_stats.etm_async__q_cur_len.fmds_value.ui64 = etm_async_q_cur_len; */ + + return (1); +} /* etm_async_q_deq */ + + +/* + * ds userland interface ds_reg_cb callback func + */ + +/* ARGSUSED */ +static void +etm_iosvc_reg_handler(ds_hdl_t ds_hdl, ds_cb_arg_t arg, ds_ver_t *ver, + ds_domain_hdl_t dhdl) +{ + etm_async_event_ele_t async_ele; + + + /* + * do version check here. + * checked the ver received here against etm_iosvc_vers here + */ + if (etm_iosvc_vers[0].major != ver->major || + etm_iosvc_vers[0].minor != ver->minor) { + /* + * can't log an fmd debug msg, + * not running in an fmd aux thread + */ + return; + } + + /* + * the callback should have a valid ldom_name + * can't log fmd debugging msg here since this is not in an fmd aux + * thread. log fmd debug msg in etm_async_event_handle() + */ + async_ele.ds_hdl = ds_hdl; + async_ele.dhdl = dhdl; + async_ele.ldom_name[0] = '\0'; + async_ele.event_type = ETM_ASYNC_EVENT_DS_REG_CB; + (void) pthread_mutex_lock(&etm_async_event_q_lock); + (void) etm_async_q_enq(&async_ele); + if (etm_async_q_cur_len == 1) + (void) pthread_cond_signal(&etm_async_event_q_cv); + (void) pthread_mutex_unlock(&etm_async_event_q_lock); + +} /* etm_iosvc_reg_handler */ + + +/* + * ds userland interface ds_unreg_cb callback func + */ + +/*ARGSUSED*/ +static void +etm_iosvc_unreg_handler(ds_hdl_t hdl, ds_cb_arg_t arg) +{ + etm_async_event_ele_t async_ele; + + /* + * fill in async_ele and enqueue async_ele + */ + async_ele.ldom_name[0] = '\0'; + async_ele.ds_hdl = hdl; + async_ele.event_type = ETM_ASYNC_EVENT_DS_UNREG_CB; + (void) pthread_mutex_lock(&etm_async_event_q_lock); + (void) etm_async_q_enq(&async_ele); + if (etm_async_q_cur_len == 1) + (void) pthread_cond_signal(&etm_async_event_q_cv); + (void) pthread_mutex_unlock(&etm_async_event_q_lock); +} /* etm_iosvc_unreg_handler */ + +/* + * ldom event registration callback func + */ + +/* ARGSUSED */ +static void +ldom_event_handler(char *ldom_name, ldom_event_t event, ldom_cb_arg_t data) +{ + etm_async_event_ele_t async_ele; + + /* + * the callback will have a valid ldom_name + */ + async_ele.ldom_name[0] = '\0'; + if (ldom_name) + (void) strcpy(async_ele.ldom_name, ldom_name); + async_ele.ds_hdl = DS_INVALID_HDL; + + /* + * fill in async_ele and enq async_ele + */ + switch (event) { + case LDOM_EVENT_BIND: + async_ele.event_type = ETM_ASYNC_EVENT_LDOM_BIND; + break; + case LDOM_EVENT_UNBIND: + async_ele.event_type = ETM_ASYNC_EVENT_LDOM_UNBIND; + break; + case LDOM_EVENT_ADD: + async_ele.event_type = ETM_ASYNC_EVENT_LDOM_ADD; + break; + case LDOM_EVENT_REMOVE: + async_ele.event_type = ETM_ASYNC_EVENT_LDOM_REMOVE; + break; + default: + /* + * for all other ldom events, do nothing + */ + return; + } /* switch (event) */ + + (void) pthread_mutex_lock(&etm_async_event_q_lock); + (void) etm_async_q_enq(&async_ele); + if (etm_async_q_cur_len == 1) + (void) pthread_cond_signal(&etm_async_event_q_cv); + (void) pthread_mutex_unlock(&etm_async_event_q_lock); + +} /* ldom_event_handler */ + + +/* + * This is running as an fmd aux thread. + * This is the func that actually handle the events, which include: + * 1. ldom events. ldom events are on Control Domain only + * 2. any DS userland callback funcs + * these events are already Q-ed in the async_event_ele_q + * deQ and process the events accordingly + */ +static void +etm_async_event_handler(void *arg) +{ + + fmd_hdl_t *fmd_hdl = (fmd_hdl_t *)arg; + etm_iosvc_t *iosvc; /* ptr 2 iosvc struct */ + etm_async_event_ele_t async_e; + + fmd_hdl_debug(fmd_hdl, "info: etm_async_event_handler starting\n"); + /* + * handle etm is not dying and Q len > 0 + */ + while (!etm_is_dying) { + /* + * grab the lock to check the Q len + */ + (void) pthread_mutex_lock(&etm_async_event_q_lock); + fmd_hdl_debug(fmd_hdl, "info: etm_async_q_cur_len %d\n", + etm_async_q_cur_len); + + while (etm_async_q_cur_len > 0) { + (void) etm_async_q_deq(&async_e); + (void) pthread_mutex_unlock(&etm_async_event_q_lock); + fmd_hdl_debug(fmd_hdl, + "info: processing an async event type %d ds_hdl" + " %d\n", async_e.event_type, async_e.ds_hdl); + if (async_e.ldom_name[0] != '\0') { + fmd_hdl_debug(fmd_hdl, + "info: procssing async evt ldom_name %s\n", + async_e.ldom_name); + } + + /* + * at this point, if async_e.ldom_name is not NULL, + * we have a valid iosvc strcut ptr. + * the only time async_e.ldom_name is NULL is at + * ds_unreg_cb() + */ + switch (async_e.event_type) { + case ETM_ASYNC_EVENT_LDOM_UNBIND: + case ETM_ASYNC_EVENT_LDOM_REMOVE: + /* + * we have a valid ldom_name, + * etm_lookup_struct(ldom_name) + * do nothing if can't find an iosvc + * no iosvc clean up to do + */ + (void) pthread_mutex_lock( + &iosvc_list_lock); + iosvc = etm_iosvc_lookup(fmd_hdl, + async_e.ldom_name, + async_e.ds_hdl, B_FALSE); + if (iosvc == NULL) { + fmd_hdl_debug(fmd_hdl, + "error: can't find iosvc for ldom " + "name %s\n", + async_e.ldom_name); + (void) pthread_mutex_unlock( + &iosvc_list_lock); + break; + } + etm_iosvc_cleanup(fmd_hdl, iosvc); + (void) pthread_mutex_unlock( + &iosvc_list_lock); + break; + + case ETM_ASYNC_EVENT_LDOM_BIND: + + /* + * create iosvc if it has not been + * created + * async_e.ds_hdl is invalid + * async_e.ldom_name is valid ldom_name + */ + (void) pthread_mutex_lock( + &iosvc_list_lock); + iosvc = etm_iosvc_lookup(fmd_hdl, + async_e.ldom_name, + async_e.ds_hdl, B_TRUE); + if (iosvc == NULL) { + fmd_hdl_debug(fmd_hdl, + "error: can't create iosvc for " + "async evnt %d\n", + async_e.event_type); + (void) pthread_mutex_unlock( + &iosvc_list_lock); + break; + } + (void) strcpy(iosvc->ldom_name, + async_e.ldom_name); + iosvc->ds_hdl = async_e.ds_hdl; + (void) pthread_mutex_unlock( + &iosvc_list_lock); + break; + + case ETM_ASYNC_EVENT_DS_REG_CB: + if (etm_ldom_type == LDOM_TYPE_CONTROL) { + /* + * find the root ldom name from + * ldom domain hdl/id + */ + if (etm_filter_find_ldom_name( + fmd_hdl, async_e.dhdl, + async_e.ldom_name, + MAX_LDOM_NAME) != 0) { + fmd_hdl_debug(fmd_hdl, + "error: can't find root " + "domain name from did %d\n", + async_e.dhdl); + break; + } else { + fmd_hdl_debug(fmd_hdl, + "info: etm_filter_find_" + "ldom_name returned %s\n", + async_e.ldom_name); + } + /* + * now we should have a valid + * root domain name. + * lookup the iosvc struct + * associated with the ldom_name + * and init the iosvc struct + */ + (void) pthread_mutex_lock( + &iosvc_list_lock); + iosvc = etm_iosvc_lookup( + fmd_hdl, async_e.ldom_name, + async_e.ds_hdl, B_TRUE); + if (iosvc == NULL) { + fmd_hdl_debug(fmd_hdl, + "error: can't create iosvc " + "for async evnt %d\n", + async_e.event_type); + (void) pthread_mutex_unlock( + &iosvc_list_lock); + break; + } + iosvc->ds_hdl = async_e.ds_hdl; + iosvc->cur_send_xid = 0; + + /* + * open the fmd xprt if it + * hasn't been previously opened + */ + iosvc->start_sending_Q = 0; + fmd_hdl_debug(fmd_hdl, + "info: before fmd_xprt_open" + "ldom_name is %s\n", + async_e.ldom_name); + if (iosvc->fmd_xprt == NULL) { + iosvc->fmd_xprt = + fmd_xprt_open( + fmd_hdl, + flags, NULL, + iosvc); + } + + iosvc->thr_is_dying = 0; + if (iosvc->recv_tid == NULL) { + iosvc->recv_tid = + fmd_thr_create( + fmd_hdl, + etm_recv_from_remote_root, + iosvc); + } + if (iosvc->send_tid == NULL) { + iosvc->send_tid = + fmd_thr_create( + fmd_hdl, + etm_send_to_remote_root, + iosvc); + } + + (void) pthread_mutex_unlock( + &iosvc_list_lock); + } else { + iosvc = &io_svc; + (void) strcpy(iosvc->ldom_name, + async_e.ldom_name); + iosvc->ds_hdl = async_e.ds_hdl; + iosvc->cur_send_xid = 0; + iosvc->start_sending_Q = 0; + + /* + * open the fmd xprt if it + * hasn't been previously opened + */ + if (iosvc->fmd_xprt == NULL) { + iosvc->fmd_xprt = + fmd_xprt_open( + fmd_hdl, + flags, NULL, + iosvc); + } + + iosvc->thr_is_dying = 0; + if (iosvc->recv_tid == NULL) { + iosvc->recv_tid = + fmd_thr_create( + fmd_hdl, + etm_recv_from_remote_root, + iosvc); + } + if (iosvc->send_tid == NULL) { + iosvc->send_tid = + fmd_thr_create( + fmd_hdl, + etm_send_to_remote_root, + iosvc); + } + } + break; + + case ETM_ASYNC_EVENT_DS_UNREG_CB: + /* + * decide which iosvc struct to perform + * this UNREG callback on. + */ + if (etm_ldom_type == LDOM_TYPE_CONTROL) { + (void) pthread_mutex_lock( + &iosvc_list_lock); + /* + * lookup the iosvc struct w/ + * ds_hdl + */ + iosvc = etm_iosvc_lookup( + fmd_hdl, async_e.ldom_name, + async_e.ds_hdl, B_FALSE); + if (iosvc == NULL) { + fmd_hdl_debug(fmd_hdl, + "error: can't find iosvc " + "for async evnt %d\n", + async_e.event_type); + (void) pthread_mutex_unlock( + &iosvc_list_lock); + break; + } + + /* + * ds_hdl and fmd_xprt_open + * go hand to hand together + * after unreg_cb, + * ds_hdl is INVALID and + * fmd_xprt is closed. + * the ldom name and the msg Q + * remains in iosvc_list + */ + iosvc->ds_hdl = DS_INVALID_HDL; + if (iosvc->fmd_xprt != NULL) + fmd_xprt_close(fmd_hdl, + iosvc->fmd_xprt); + iosvc->fmd_xprt = NULL; + + if (iosvc->ldom_name != '\0') + fmd_hdl_debug(fmd_hdl, + "info: iosvc w/ ldom_name " + "%s \n", iosvc->ldom_name); + + /* + * destroy send/recv threads + * on Control side. + */ + iosvc->thr_is_dying = 1; + if (iosvc->send_tid != NULL) { + fmd_thr_signal(fmd_hdl, + iosvc->send_tid); + fmd_thr_destroy(fmd_hdl, + iosvc->send_tid); + iosvc->send_tid = NULL; + } /* if send tid was created */ + + if (iosvc->recv_tid != NULL) { + fmd_thr_signal(fmd_hdl, + iosvc->recv_tid); + fmd_thr_destroy(fmd_hdl, + iosvc->recv_tid); + iosvc->recv_tid = NULL; + } /* if recv tid was created */ + + (void) pthread_mutex_unlock( + &iosvc_list_lock); + } else { + iosvc = &io_svc; + /* + * destroy send/recv threads + * on Root side. + */ + iosvc->thr_is_dying = 1; + if (iosvc->send_tid != NULL) { + fmd_thr_signal(fmd_hdl, + iosvc->send_tid); + fmd_thr_destroy(fmd_hdl, + iosvc->send_tid); + iosvc->send_tid = NULL; + } /* if send tid was created */ + + if (iosvc->recv_tid != NULL) { + fmd_thr_signal(fmd_hdl, + iosvc->recv_tid); + fmd_thr_destroy(fmd_hdl, + iosvc->recv_tid); + iosvc->recv_tid = NULL; + } /* if recv tid was created */ + + iosvc->ds_hdl = DS_INVALID_HDL; + if (iosvc->fmd_xprt != NULL) + fmd_xprt_close(fmd_hdl, + iosvc->fmd_xprt); + iosvc->fmd_xprt = NULL; + } + break; + + default: + /* + * for all other events, etm doesn't care. + * already logged an fmd info msg w/ + * the event type. Do nothing here. + */ + break; + } /* switch (async_e.event_type) */ + + if (etm_ldom_type == LDOM_TYPE_CONTROL) { + etm_filter_handle_ldom_event(fmd_hdl, + async_e.event_type, async_e.ldom_name); + } + + /* + * grab the lock to check the q length again + */ + (void) pthread_mutex_lock(&etm_async_event_q_lock); + + if (etm_is_dying) { + break; + } + } /* etm_async_q_cur_len */ + + /* + * we have the mutex lock at this point, whether + * . etm_is_dying and/or + * . q_len == 0 + */ + if (!etm_is_dying && etm_async_q_cur_len == 0) { + fmd_hdl_debug(fmd_hdl, + "info: cond wait on async_event_q_cv\n"); + (void) pthread_cond_wait(&etm_async_event_q_cv, + &etm_async_event_q_lock); + fmd_hdl_debug(fmd_hdl, + "info: cond wait on async_event_q_cv rtns\n"); + } + (void) pthread_mutex_unlock(&etm_async_event_q_lock); + } /* etm_is_dying */ + + fmd_hdl_debug(fmd_hdl, + "info: etm async event handler thread exiting\n"); + +} /* etm_async_event_handler */ + +/* + * deQ what's in iosvc msg Q + * send iosvc_msgp to the remote io svc ldom by calling ds_send_msg() + * the iosvc_msgp already has the packed msg, which is hdr + 1 fma event + */ +static void +etm_send_to_remote_root(void *arg) +{ + + etm_iosvc_t *iosvc = (etm_iosvc_t *)arg; /* iosvc ptr */ + etm_iosvc_q_ele_t msg_ele; /* iosvc msg ele */ + etm_proto_v1_ev_hdr_t *ev_hdrp; /* hdr for FMA_EVENT */ + fmd_hdl_t *fmd_hdl = init_hdl; /* fmd handle */ + + + fmd_hdl_debug(fmd_hdl, + "info: send to remote iosvc starting w/ ldom_name %s\n", + iosvc->ldom_name); + + /* + * loop forever until etm_is_dying or thr_is_dying + */ + while (!etm_is_dying && !iosvc->thr_is_dying) { + if (iosvc->ds_hdl != DS_INVALID_HDL && + iosvc->start_sending_Q > 0) { + (void) pthread_mutex_lock(&iosvc->msg_q_lock); + while (iosvc->msg_q_cur_len > 0 && + iosvc->ds_hdl != DS_INVALID_HDL) { + (void) etm_iosvc_msg_deq(fmd_hdl, iosvc, + &msg_ele); + if (etm_debug_lvl >= 3) { + fmd_hdl_debug(fmd_hdl, "info: valid " + "ds_hdl before ds_send_msg \n"); + } + (void) pthread_mutex_unlock(&iosvc->msg_q_lock); + + iosvc->ack_ok = 0; + ev_hdrp = (etm_proto_v1_ev_hdr_t *) + ((ptrdiff_t)msg_ele.msg); + ev_hdrp->ev_pp.pp_xid = iosvc->cur_send_xid + 1; + while (!iosvc->ack_ok && + iosvc->ds_hdl != DS_INVALID_HDL && + !etm_is_dying) { + /* + * call ds_send_msg() to send the msg, + * wait for the recv end to send the + * resp msg back. + * If resp msg is recv-ed, ack_ok + * will be set to 1. + * otherwise, retry. + */ + if (etm_send_ds_msg(fmd_hdl, B_TRUE, + iosvc, &msg_ele, ev_hdrp) < 0) { + continue; + } + + if (etm_is_dying || iosvc->thr_is_dying) + break; + } + + /* + * if out of the while loop but !ack_ok, ie, + * ds_hdl becomes invalid at some point + * while waiting the resp msg, we need to put + * the msg back to the head of the Q. + */ + if (!iosvc->ack_ok) { + (void) pthread_mutex_lock( + &iosvc->msg_q_lock); + /* + * put the msg back to the head of Q. + * If the Q is full at this point, + * drop the msg at the tail, enq this + * msg to the head. + */ + etm_msg_enq_head(fmd_hdl, iosvc, + &msg_ele); + (void) pthread_mutex_unlock( + &iosvc->msg_q_lock); + } + + /* + * + * grab the lock to check the Q len again + */ + (void) pthread_mutex_lock(&iosvc->msg_q_lock); + if (etm_is_dying || iosvc->thr_is_dying) { + break; + } + } /* while dequeing iosvc msgs to send */ + + /* + * we have the mutex lock for msg_q_lock at this point + * we are here because + * 1) q_len == 0: then wait on the cv for Q to be filled + * 2) etm_is_dying + */ + if (!etm_is_dying && !iosvc->thr_is_dying && + iosvc->msg_q_cur_len == 0) { + fmd_hdl_debug(fmd_hdl, + "info: waiting on msg_q_cv\n"); + (void) pthread_cond_wait(&iosvc->msg_q_cv, + &iosvc->msg_q_lock); + } + (void) pthread_mutex_unlock(&iosvc->msg_q_lock); + if (etm_is_dying || iosvc->thr_is_dying) { + break; + } + } else { + (void) etm_sleep(1); + } /* wait for the start_sendingQ > 0 */ + } /* etm_is_dying or thr_is_dying */ + fmd_hdl_debug(fmd_hdl, "info; etm send thread exiting \n"); +} /* etm_send_to_remote_root */ + + +/* + * receive etm msgs from the remote root ldom by calling ds_recv_msg() + * if FMA events/ereports, call fmd_xprt_post() to post to fmd + * send ACK back by calling ds_send_msg() + */ +static void +etm_recv_from_remote_root(void *arg) +{ + etm_iosvc_t *iosvc = (etm_iosvc_t *)arg; /* iosvc ptr */ + etm_proto_v1_pp_t *pp; /* protocol preamble */ + etm_proto_v1_ev_hdr_t *ev_hdrp; /* for FMA_EVENT msg */ + etm_proto_v1_resp_hdr_t *resp_hdrp; /* for RESPONSE msg */ + int32_t resp_code = 0; /* default is success */ + int32_t rc; /* return value */ + size_t maxlen = MAXLEN; + /* max msg len */ + char msgbuf[MAXLEN]; /* recv msg buf */ + size_t msg_size; /* recv msg size */ + size_t hdr_sz; /* sizeof *hdrp */ + size_t evsz; /* sizeof *evp */ + size_t fma_event_size; /* sizeof FMA event */ + nvlist_t *evp; /* ptr to the nvlist */ + char *buf; /* ptr to the nvlist */ + static uint32_t mem_alloc = 0; /* indicate if alloc mem */ + char *msg; /* ptr to alloc mem */ + fmd_hdl_t *fmd_hdl = init_hdl; + + + + fmd_hdl_debug(fmd_hdl, + "info: recv from remote iosvc starting with ldom name %s \n", + iosvc->ldom_name); + + /* + * loop forever until etm_is_dying or the thread is dying + */ + + msg = msgbuf; + while (!etm_is_dying && !iosvc->thr_is_dying) { + if (iosvc->ds_hdl == DS_INVALID_HDL) { + fmd_hdl_debug(fmd_hdl, + "info: ds_hdl is invalid in recv thr\n"); + (void) etm_sleep(1); + continue; + } + + /* + * for now, there are FMA_EVENT and ACK msg type. + * use FMA_EVENT buf as the maxlen, hdr+1 fma event. + * FMA_EVENT is big enough to hold an ACK msg. + * the actual msg size received is in msg_size. + */ + rc = (*etm_ds_recv_msg)(iosvc->ds_hdl, msg, maxlen, &msg_size); + if (rc == EFBIG) { + fmd_hdl_debug(fmd_hdl, + "info: ds_recv_msg needs mem the size of %d\n", + msg_size); + msg = fmd_hdl_zalloc(fmd_hdl, msg_size, FMD_SLEEP); + mem_alloc = 1; + } else if (rc == 0) { + fmd_hdl_debug(fmd_hdl, + "info: ds_recv_msg received a msg ok\n"); + /* + * check the magic # in msg.hdr + */ + pp = (etm_proto_v1_pp_t *)((ptrdiff_t)msg); + if (pp->pp_magic_num != ETM_PROTO_MAGIC_NUM) { + fmd_hdl_debug(fmd_hdl, + "info: bad ds recv on magic\n"); + continue; + } + + /* + * check the msg type against msg_size to be sure + * that received msg is not a truncated msg + */ + if (pp->pp_msg_type == ETM_MSG_TYPE_FMA_EVENT) { + + ev_hdrp = (etm_proto_v1_ev_hdr_t *) + ((ptrdiff_t)msg); + fmd_hdl_debug(fmd_hdl, "info: ds received " + "FMA EVENT xid=%d msg_size=%d\n", + ev_hdrp->ev_pp.pp_xid, msg_size); + hdr_sz = sizeof (*ev_hdrp) + + 1*(sizeof (ev_hdrp->ev_lens[0])); + fma_event_size = hdr_sz + ev_hdrp->ev_lens[0]; + if (fma_event_size != msg_size) { + fmd_hdl_debug(fmd_hdl, "info: wrong " + "ev msg size received\n"); + continue; + /* + * Simply do nothing. The send side + * will timedcond_wait waiting on the + * resp msg will timeout and + * re-send the same msg. + */ + } + if (etm_debug_lvl >= 3) { + fmd_hdl_debug(fmd_hdl, "info: recv msg" + " size %d hdrsz %d evp size %d\n", + msg_size, hdr_sz, + ev_hdrp->ev_lens[0]); + } + + if (ev_hdrp->ev_pp.pp_xid != + iosvc->xid_posted_ev) { + /* + * different from last xid posted to + * fmd, post to fmd now. + */ + buf = msg + hdr_sz; + rc = nvlist_unpack(buf, + ev_hdrp->ev_lens[0], &evp, 0); + rc = nvlist_size(evp, &evsz, + NV_ENCODE_XDR); + fmd_hdl_debug(fmd_hdl, + "info: evp size %d before fmd" + "post\n", evsz); + + if ((rc = etm_post_to_fmd(fmd_hdl, + iosvc->fmd_xprt, evp)) >= 0) { + fmd_hdl_debug(fmd_hdl, + "info: xid posted to fmd %d" + "\n", + ev_hdrp->ev_pp.pp_xid); + iosvc->xid_posted_ev = + ev_hdrp->ev_pp.pp_xid; + } + } + + /* + * ready to send the RESPONSE msg back + * reuse the msg buffer as the response buffer + */ + resp_hdrp = (etm_proto_v1_resp_hdr_t *) + ((ptrdiff_t)msg); + resp_hdrp->resp_pp.pp_msg_type = + ETM_MSG_TYPE_RESPONSE; + + resp_hdrp->resp_code = resp_code; + resp_hdrp->resp_len = sizeof (*resp_hdrp); + + /* + * send the whole response msg in one send + */ + if ((*etm_ds_send_msg)(iosvc->ds_hdl, msg, + sizeof (*resp_hdrp)) != 0) { + fmd_hdl_debug(fmd_hdl, + "info: send response msg failed\n"); + } else { + fmd_hdl_debug(fmd_hdl, + "info: ds send resp msg ok" + "size %d\n", sizeof (*resp_hdrp)); + } + } else if (pp->pp_msg_type == ETM_MSG_TYPE_RESPONSE) { + fmd_hdl_debug(fmd_hdl, + "info: ds received respond msg xid=%d" + "msg_size=%d for ldom %s\n", pp->pp_xid, + msg_size, iosvc->ldom_name); + if (sizeof (*resp_hdrp) != msg_size) { + fmd_hdl_debug(fmd_hdl, + "info: wrong resp msg size" + "received\n"); + fmd_hdl_debug(fmd_hdl, + "info: resp msg size %d recv resp" + "msg size %d\n", + sizeof (*resp_hdrp), msg_size); + continue; + } + /* + * is the pp.pp_xid == iosvc->cur_send_xid+1, + * if so, nudge the send routine to send next + */ + if (pp->pp_xid != iosvc->cur_send_xid+1) { + fmd_hdl_debug(fmd_hdl, + "info: ds received resp msg xid=%d " + "doesn't match cur_send_id=%d\n", + pp->pp_xid, iosvc->cur_send_xid+1); + continue; + } + (void) pthread_mutex_lock(&iosvc->msg_ack_lock); + iosvc->ack_ok = 1; + (void) pthread_cond_signal(&iosvc->msg_ack_cv); + (void) pthread_mutex_unlock( + &iosvc->msg_ack_lock); + fmd_hdl_debug(fmd_hdl, + "info: signaling msg_ack_cv\n"); + } else { + /* + * place holder for future msg types + */ + fmd_hdl_debug(fmd_hdl, + "info: ds received unrecognized msg\n"); + } + if (mem_alloc) { + fmd_hdl_free(fmd_hdl, msg, msg_size); + mem_alloc = 0; + msg = msgbuf; + } + } else { + if (etm_debug_lvl >= 3) { + fmd_hdl_debug(fmd_hdl, + "info: ds_recv_msg() failed\n"); + } + } /* ds_recv_msg() returns */ + } /* etm_is_dying */ + + /* + * need to free the mem allocated in msg upon exiting the thread + */ + if (mem_alloc) { + fmd_hdl_free(fmd_hdl, msg, msg_size); + mem_alloc = 0; + msg = msgbuf; + } + fmd_hdl_debug(fmd_hdl, "info; etm recv thread exiting \n"); +} /* etm_recv_from_remote_root */ + + + +/* + * etm_ds_init + * initialize DS services function pointers by calling + * dlopen() followed by dlsym() for each ds func. + * if any dlopen() or dlsym() call fails, return -ENOENT + * return >0 for successs, -ENOENT for failure + */ +static int +etm_ds_init(fmd_hdl_t *hdl) +{ + int rc = 0; + + if ((etm_dl_hdl = dlopen(etm_dl_path, etm_dl_mode)) == NULL) { + fmd_hdl_debug(hdl, "error: failed to dlopen %s\n", etm_dl_path); + return (-ENOENT); + } + + etm_ds_svc_reg = (int (*)(ds_capability_t *cap, ds_ops_t *ops)) + dlsym(etm_dl_hdl, "ds_svc_reg"); + if (etm_ds_svc_reg == NULL) { + fmd_hdl_debug(hdl, + "error: failed to dlsym ds_svc_reg() w/ error %s\n", + dlerror()); + rc = -ENOENT; + } + + + etm_ds_clnt_reg = (int (*)(ds_capability_t *cap, ds_ops_t *ops)) + dlsym(etm_dl_hdl, "ds_clnt_reg"); + if (etm_ds_clnt_reg == NULL) { + fmd_hdl_debug(hdl, + "error: dlsym(ds_clnt_reg) failed w/ errno %d\n", errno); + rc = -ENOENT; + } + + etm_ds_send_msg = (int (*)(ds_hdl_t hdl, void *buf, size_t buflen)) + dlsym(etm_dl_hdl, "ds_send_msg"); + if (etm_ds_send_msg == NULL) { + fmd_hdl_debug(hdl, "error: dlsym(ds_send_msg) failed\n"); + rc = -ENOENT; + } + + etm_ds_recv_msg = (int (*)(ds_hdl_t hdl, void *buf, size_t buflen, + size_t *msglen))dlsym(etm_dl_hdl, "ds_recv_msg"); + if (etm_ds_recv_msg == NULL) { + fmd_hdl_debug(hdl, "error: dlsym(ds_recv_msg) failed\n"); + rc = -ENOENT; + } + + etm_ds_fini = (int (*)(void))dlsym(etm_dl_hdl, "ds_fini"); + if (etm_ds_fini == NULL) { + fmd_hdl_debug(hdl, "error: dlsym(ds_fini) failed\n"); + rc = -ENOENT; + } + + if (rc == -ENOENT) { + (void) dlclose(etm_dl_hdl); + } + return (rc); + +} /* etm_ds_init() */ + + +/* * -------------------------- FMD entry points ------------------------------- */ @@ -2095,9 +3720,11 @@ _fmd_init(fmd_hdl_t *hdl) { struct timeval tmv; /* timeval */ ssize_t n; /* gen use */ - ldom_hdl_t *lhp; /* ldom pointer */ const struct facility *fp; /* syslog facility matching */ char *facname; /* syslog facility property */ + uint32_t type_mask; /* type of the local host */ + int rc; /* funcs return code */ + if (fmd_hdl_register(hdl, FMD_API_VERSION, &fmd_info) != 0) { return; /* invalid data in configuration file */ @@ -2106,109 +3733,232 @@ _fmd_init(fmd_hdl_t *hdl) fmd_hdl_debug(hdl, "info: module initializing\n"); init_hdl = hdl; - lhp = ldom_init(etm_init_alloc, etm_init_free); + etm_lhp = ldom_init(etm_init_alloc, etm_init_free); /* - * Do not load this module if it is runing on a guest ldom. + * decide the ldom type, do initialization accordingly */ - if (ldom_major_version(lhp) == 1 && ldom_on_service(lhp) == 0) { + if ((rc = ldom_get_type(etm_lhp, &type_mask)) != 0) { + fmd_hdl_debug(hdl, "error: can't decide ldom type\n"); fmd_hdl_debug(hdl, "info: module unregistering\n"); - ldom_fini(lhp); + ldom_fini(etm_lhp); fmd_hdl_unregister(hdl); return; - } else { - ldom_fini(lhp); } - /* setup statistics and properties from FMD */ + if ((type_mask & LDOM_TYPE_LEGACY) || (type_mask & LDOM_TYPE_CONTROL)) { + if (type_mask & LDOM_TYPE_LEGACY) { + /* + * running on a legacy sun4v domain, + * act as the the old sun4v + */ + etm_ldom_type = LDOM_TYPE_LEGACY; + fmd_hdl_debug(hdl, "info: running as the old sun4v\n"); + ldom_fini(etm_lhp); + } else if (type_mask & LDOM_TYPE_CONTROL) { + etm_ldom_type = LDOM_TYPE_CONTROL; + fmd_hdl_debug(hdl, "info: running as control domain\n"); + + /* + * looking for libds.so.1. + * If not found, don't do DS registration. As a result, + * there will be no DS callbacks or other DS services. + */ + if (etm_ds_init(hdl) >= 0) { + etm_filter_init(hdl); + etm_ckpt_init(hdl); + + flags = FMD_XPRT_RDWR | FMD_XPRT_ACCEPT; + + /* + * ds client registration + */ + if ((rc = (*etm_ds_clnt_reg)(&iosvc_caps, + &iosvc_ops))) { + fmd_hdl_debug(hdl, + "error: ds_clnt_reg(): errno %d\n", rc); + } + } else { + fmd_hdl_debug(hdl, "error: dlopen() libds " + "failed, continue without the DS services"); + } - (void) fmd_stat_create(hdl, FMD_STAT_NOALLOC, - sizeof (etm_stats) / sizeof (fmd_stat_t), (fmd_stat_t *)&etm_stats); + /* + * register for ldom status events + */ + if ((rc = ldom_register_event(etm_lhp, + ldom_event_handler, hdl))) { + fmd_hdl_debug(hdl, + "error: ldom_register_event():" + " errno %d\n", rc); + } - etm_debug_lvl = fmd_prop_get_int32(hdl, ETM_PROP_NM_DEBUG_LVL); - etm_debug_max_ev_cnt = fmd_prop_get_int32(hdl, - ETM_PROP_NM_DEBUG_MAX_EV_CNT); - fmd_hdl_debug(hdl, "info: etm_debug_lvl %d " - "etm_debug_max_ev_cnt %d\n", etm_debug_lvl, etm_debug_max_ev_cnt); + /* + * create the thread for handling both the ldom status + * change and service events + */ + etm_async_e_tid = fmd_thr_create(hdl, + etm_async_event_handler, hdl); + } - etm_resp_q_max_len = fmd_prop_get_int32(hdl, - ETM_PROP_NM_MAX_RESP_Q_LEN); - etm_stats.etm_resp_q_max_len.fmds_value.ui64 = etm_resp_q_max_len; - etm_bad_acc_to_sec = fmd_prop_get_int32(hdl, - ETM_PROP_NM_BAD_ACC_TO_SEC); + /* setup statistics and properties from FMD */ - /* obtain an FMD transport handle so we can post FMA events later */ + (void) fmd_stat_create(hdl, FMD_STAT_NOALLOC, + sizeof (etm_stats) / sizeof (fmd_stat_t), + (fmd_stat_t *)&etm_stats); - etm_fmd_xprt = fmd_xprt_open(hdl, FMD_XPRT_RDONLY, NULL, NULL); + etm_fma_resp_wait_time = fmd_prop_get_int32(hdl, + ETM_PROP_NM_FMA_RESP_WAIT_TIME); + etm_debug_lvl = fmd_prop_get_int32(hdl, ETM_PROP_NM_DEBUG_LVL); + etm_debug_max_ev_cnt = fmd_prop_get_int32(hdl, + ETM_PROP_NM_DEBUG_MAX_EV_CNT); + fmd_hdl_debug(hdl, "info: etm_debug_lvl %d " + "etm_debug_max_ev_cnt %d\n", etm_debug_lvl, + etm_debug_max_ev_cnt); - /* encourage protocol transaction id to be unique per module load */ + etm_resp_q_max_len = fmd_prop_get_int32(hdl, + ETM_PROP_NM_MAX_RESP_Q_LEN); + etm_stats.etm_resp_q_max_len.fmds_value.ui64 = + etm_resp_q_max_len; + etm_bad_acc_to_sec = fmd_prop_get_int32(hdl, + ETM_PROP_NM_BAD_ACC_TO_SEC); - (void) gettimeofday(&tmv, NULL); - etm_xid_cur = (uint32_t)((tmv.tv_sec << 10) | - ((unsigned long)tmv.tv_usec >> 10)); + /* + * obtain an FMD transport handle so we can post + * FMA events later + */ - /* init the ETM transport */ + etm_fmd_xprt = fmd_xprt_open(hdl, FMD_XPRT_RDONLY, NULL, NULL); - if ((n = etm_xport_init(hdl)) != 0) { - fmd_hdl_error(hdl, "error: bad xport init errno %d\n", (-n)); - fmd_hdl_unregister(hdl); - return; - } + /* + * encourage protocol transaction id to be unique per module + * load + */ - /* - * Cache any properties we use every time we receive an alert. - */ - syslog_file = fmd_prop_get_int32(hdl, ETM_PROP_NM_SYSLOGD); - syslog_cons = fmd_prop_get_int32(hdl, ETM_PROP_NM_CONSOLE); + (void) gettimeofday(&tmv, NULL); + etm_xid_cur = (uint32_t)((tmv.tv_sec << 10) | + ((unsigned long)tmv.tv_usec >> 10)); - if (syslog_file && (syslog_logfd = open("/dev/conslog", - O_WRONLY | O_NOCTTY)) == -1) { - fmd_hdl_error(hdl, "error: failed to open /dev/conslog"); - syslog_file = 0; - } + /* init the ETM transport */ - if (syslog_cons && (syslog_msgfd = open("/dev/sysmsg", - O_WRONLY | O_NOCTTY)) == -1) { - fmd_hdl_error(hdl, "error: failed to open /dev/sysmsg"); - syslog_cons = 0; - } + if ((n = etm_xport_init(hdl)) != 0) { + fmd_hdl_error(hdl, "error: bad xport init errno %d\n", + (-n)); + fmd_hdl_unregister(hdl); + return; + } - if (syslog_file) { /* - * Look up the value of the "facility" property and use it to - * determine * what syslog LOG_* facility value we use to - * fill in our log_ctl_t. + * Cache any properties we use every time we receive an alert. */ - facname = fmd_prop_get_string(hdl, ETM_PROP_NM_FACILITY); + syslog_file = fmd_prop_get_int32(hdl, ETM_PROP_NM_SYSLOGD); + syslog_cons = fmd_prop_get_int32(hdl, ETM_PROP_NM_CONSOLE); - for (fp = syslog_facs; fp->fac_name != NULL; fp++) { - if (strcmp(fp->fac_name, facname) == 0) - break; + if (syslog_file && (syslog_logfd = open("/dev/conslog", + O_WRONLY | O_NOCTTY)) == -1) { + fmd_hdl_error(hdl, + "error: failed to open /dev/conslog"); + syslog_file = 0; } - if (fp->fac_name == NULL) { - fmd_hdl_error(hdl, "error: invalid 'facility'" - " setting: %s\n", facname); - syslog_file = 0; - } else { - syslog_facility = fp->fac_value; - syslog_ctl.flags = SL_CONSOLE | SL_LOGONLY; + if (syslog_cons && (syslog_msgfd = open("/dev/sysmsg", + O_WRONLY | O_NOCTTY)) == -1) { + fmd_hdl_error(hdl, "error: failed to open /dev/sysmsg"); + syslog_cons = 0; } - fmd_prop_free_string(hdl, facname); - } + if (syslog_file) { + /* + * Look up the value of the "facility" property and + * use it to determine * what syslog LOG_* facility + * value we use to fill in our log_ctl_t. + */ + facname = fmd_prop_get_string(hdl, + ETM_PROP_NM_FACILITY); + + for (fp = syslog_facs; fp->fac_name != NULL; fp++) { + if (strcmp(fp->fac_name, facname) == 0) + break; + } - /* - * start the message responder and the connection acceptance server; - * request protocol version be negotiated after waiting a second - * for the receiver to be ready to start handshaking - */ + if (fp->fac_name == NULL) { + fmd_hdl_error(hdl, "error: invalid 'facility'" + " setting: %s\n", facname); + syslog_file = 0; + } else { + syslog_facility = fp->fac_value; + syslog_ctl.flags = SL_CONSOLE | SL_LOGONLY; + } + + fmd_prop_free_string(hdl, facname); + } + + /* + * start the message responder and the connection acceptance + * server; request protocol version be negotiated after waiting + * a second for the receiver to be ready to start handshaking + */ + + etm_resp_tid = fmd_thr_create(hdl, etm_responder, hdl); + etm_svr_tid = fmd_thr_create(hdl, etm_server, hdl); - etm_resp_tid = fmd_thr_create(hdl, etm_responder, hdl); - etm_svr_tid = fmd_thr_create(hdl, etm_server, hdl); + (void) etm_sleep(ETM_SLEEP_QUIK); + etm_req_ver_negot(hdl); - (void) etm_sleep(ETM_SLEEP_QUIK); - etm_req_ver_negot(hdl); + } else if (type_mask & LDOM_TYPE_ROOT) { + etm_ldom_type = LDOM_TYPE_ROOT; + fmd_hdl_debug(hdl, "info: running as root domain\n"); + + /* + * looking for libds.so.1. + * If not found, don't do DS registration. As a result, + * there will be no DS callbacks or other DS services. + */ + if (etm_ds_init(hdl) < 0) { + fmd_hdl_debug(hdl, + "error: dlopen() libds failed, " + "module unregistering\n"); + ldom_fini(etm_lhp); + fmd_hdl_unregister(hdl); + return; + } + + /* + * DS service registration + */ + if ((rc = (*etm_ds_svc_reg)(&iosvc_caps, &iosvc_ops))) { + fmd_hdl_debug(hdl, "error: ds_svc_reg(): errno %d\n", + rc); + } + + /* + * this thread is created for ds_reg_cb/ds_unreg_cb + */ + etm_async_e_tid = fmd_thr_create(hdl, + etm_async_event_handler, hdl); + + flags = FMD_XPRT_RDWR; + } else if ((type_mask & LDOM_TYPE_IO) || (type_mask == 0)) { + /* + * Do not load this module if it is + * . runing on a non-root ldom + * . the domain owns no io devices + */ + fmd_hdl_debug(hdl, + "info: non-root ldom, module unregistering\n"); + ldom_fini(etm_lhp); + fmd_hdl_unregister(hdl); + return; + } else { + /* + * place holder, all other cases. unload etm for now + */ + fmd_hdl_debug(hdl, + "info: other ldom type, module unregistering\n"); + ldom_fini(etm_lhp); + fmd_hdl_unregister(hdl); + return; + } fmd_hdl_debug(hdl, "info: module initialized ok\n"); @@ -2231,6 +3981,13 @@ etm_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *evp, const char *class) size_t buflen; /* size of packed FMA event */ uint8_t *buf; /* tmp buffer for packed FMA event */ + /* + * if this is running on a Root Domain, ignore the events, + * return right away + */ + if (etm_ldom_type == LDOM_TYPE_ROOT) + return; + buflen = 0; if ((n = nvlist_size(evp, &buflen, NV_ENCODE_XDR)) != 0) { fmd_hdl_error(hdl, "error: FMA event dropped: " @@ -2267,6 +4024,20 @@ etm_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *evp, const char *class) buf = fmd_hdl_zalloc(hdl, buflen, FMD_SLEEP); + /* + * increment the ttl value if the event is from remote (a root domain) + * uncomment this when enabling fault forwarding from Root domains + * to Control domain. + * + * uint8_t ttl; + * if (fmd_event_local(hdl, evp) != FMD_EVF_LOCAL) { + * if (nvlist_lookup_uint8(evp, FMD_EVN_TTL, &ttl) == 0) { + * (void) nvlist_remove(evp, FMD_EVN_TTL, DATA_TYPE_UINT8); + * (void) nvlist_add_uint8(evp, FMD_EVN_TTL, ttl + 1); + * } + * } + */ + if ((n = nvlist_pack(evp, (char **)&buf, &buflen, NV_ENCODE_XDR, 0)) != 0) { fmd_hdl_error(hdl, "error: FMA event dropped: " @@ -2348,6 +4119,106 @@ etm_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *evp, const char *class) } /* etm_recv() */ + +/* + * etm_send - receive an FMA event from FMD and enQ it in the iosvc.Q. + * etm_send_to_remote_root() deQ and xprt the FMA events to a + * remote root domain + * return FMD_SEND_SUCCESS for success, + * FMD_SEND_FAILED for error + */ + +/*ARGSUSED*/ +int +etm_send(fmd_hdl_t *fmd_hdl, fmd_xprt_t *xp, fmd_event_t *ep, nvlist_t *nvl) +{ + uint32_t pack_it; /* whether to pack/enq the event */ + etm_pack_msg_type_t msg_type; + /* tell etm_pack_ds_msg() what to do */ + etm_iosvc_t *iosvc; /* ptr to cur iosvc struct */ + char *class; /* nvlist class name */ + + pack_it = 1; + msg_type = FMD_XPRT_OTHER_MSG; + + (void) nvlist_lookup_string(nvl, FM_CLASS, &class); + if (class == NULL) { + pack_it = 0; + } else { + if (etm_debug_lvl >= 1) { + fmd_hdl_debug(fmd_hdl, + "info: evp class= %s in etm_send\n", class); + } + + if (etm_ldom_type == LDOM_TYPE_CONTROL) { + iosvc = + (etm_iosvc_t *)fmd_xprt_getspecific(fmd_hdl, xp); + + /* + * check the flag FORWARDING_FAULTS_TO_CONTROL to + * decide if or not to drop fault subscription + * control msgs + */ + if (strcmp(class, "resource.fm.xprt.subscribe") == 0) { + pack_it = 0; + /* + * if (FORWARDING_FAULTS_TO_CONTROL == 1) { + * (void) nvlist_lookup_string(nvl, + * FM_RSRC_XPRT_SUBCLASS, &subclass); + * if (strcmp(subclass, "list.suspect") + * == 0) { + * pack_it = 1; + * msg_action = FMD_XPRT_OTHER_MSG; + * } + * if (strcmp(subclass, "list.repaired") + * == 0) { + * pack_it = 1; + * msg_action = FMD_XPRT_OTHER_MSG; + * } + * } + */ + } + if (strcmp(class, "resource.fm.xprt.run") == 0) { + pack_it = 1; + msg_type = FMD_XPRT_RUN_MSG; + } + } else { /* has to be the root domain ldom */ + iosvc = &io_svc; + /* + * drop all ereport and fault subscriptions + * are we dropping too much here, more than just ereport + * and fault subscriptions? need to check + */ + if (strcmp(class, "resource.fm.xprt.subscribe") == 0) + pack_it = 0; + if (strcmp(class, "resource.fm.xprt.run") == 0) { + pack_it = 1; + msg_type = FMD_XPRT_RUN_MSG; + } + } + } + + if (pack_it) { + if (etm_debug_lvl >= 1) { + fmd_hdl_debug(fmd_hdl, + "info: ldom name returned from xprt get specific=" + "%s xprt=%lld\n", iosvc->ldom_name, xp); + } + /* + * pack the etm msg for the DS library and enq in io_svc->Q + * when the hdrp is NULL, the packing func will use the static + * iosvc_hdr + */ + (void) etm_pack_ds_msg(fmd_hdl, iosvc, NULL, 0, nvl, msg_type, + ETM_CKPT_NOOP); + } + + return (FMD_SEND_SUCCESS); + +} /* etm_send() */ + + + /* * _fmd_fini - stop the server daemon and teardown the transport */ @@ -2355,7 +4226,10 @@ etm_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *evp, const char *class) void _fmd_fini(fmd_hdl_t *hdl) { - ssize_t n; /* gen use */ + ssize_t n; /* gen use */ + etm_iosvc_t *iosvc; /* ptr to insvc struct */ + etm_iosvc_q_ele_t msg_ele; /* iosvc msg ele */ + uint32_t i; /* for loop var */ fmd_hdl_debug(hdl, "info: module finalizing\n"); @@ -2375,20 +4249,96 @@ _fmd_fini(fmd_hdl_t *hdl) etm_resp_tid = NULL; } /* if responder thread was successfully created */ - /* teardown the transport and cleanup syslogging */ + if (etm_async_e_tid != NULL) { + fmd_thr_signal(hdl, etm_async_e_tid); + fmd_thr_destroy(hdl, etm_async_e_tid); + etm_async_e_tid = NULL; + } /* if async event handler thread was successfully created */ - if ((n = etm_xport_fini(hdl)) != 0) { - fmd_hdl_error(hdl, "warning: xport fini errno %d\n", (-n)); - } - if (etm_fmd_xprt != NULL) { - fmd_xprt_close(hdl, etm_fmd_xprt); + + if ((etm_ldom_type == LDOM_TYPE_LEGACY) || + (etm_ldom_type == LDOM_TYPE_CONTROL)) { + + /* teardown the transport and cleanup syslogging */ + if ((n = etm_xport_fini(hdl)) != 0) { + fmd_hdl_error(hdl, "warning: xport fini errno %d\n", + (-n)); + } + if (etm_fmd_xprt != NULL) { + fmd_xprt_close(hdl, etm_fmd_xprt); + } + + if (syslog_logfd != -1) { + (void) close(syslog_logfd); + } + if (syslog_msgfd != -1) { + (void) close(syslog_msgfd); + } } - if (syslog_logfd != -1) { - (void) close(syslog_logfd); + if (etm_ldom_type == LDOM_TYPE_CONTROL) { + if (ldom_unregister_event(etm_lhp)) + fmd_hdl_debug(hdl, "ldom_unregister_event() failed\n"); + + /* + * on control side, need to go thru every iosvc struct to + * 1) process remaining events in the iosvc Q: + * for plan A: + * discard remaining events in the Q/free the memory, + * since fmd_xprt_log() already logged in Control D's FMD + * 2) unregister the ds_hdl if valid + * 3) close the fmd_xprt if it has not been closed + */ + for (i = 0; i < NUM_OF_ROOT_DOMAINS; i++) { + if (iosvc_list[i].ldom_name[0] != '\0') { + /* + * found an iosvc struct for a root domain + */ + iosvc = &iosvc_list[i]; + (void) pthread_mutex_lock(&iosvc_list_lock); + etm_iosvc_cleanup(hdl, iosvc); + (void) pthread_mutex_unlock(&iosvc_list_lock); + + } else { + /* + * reach the end of existing iosvc structures + */ + continue; + } + } /* for i<NUM_OF_ROOT_DOMAINS */ + etm_ckpt_fini(hdl); + etm_filter_fini(hdl); + + ldom_fini(etm_lhp); + + } else if (etm_ldom_type == LDOM_TYPE_ROOT) { + iosvc = &io_svc; + if (iosvc->send_tid != NULL) { + fmd_thr_signal(hdl, iosvc->send_tid); + fmd_thr_destroy(hdl, iosvc->send_tid); + iosvc->send_tid = NULL; + } /* if io svc send thread was successfully created */ + + if (iosvc->recv_tid != NULL) { + fmd_thr_signal(hdl, iosvc->recv_tid); + fmd_thr_destroy(hdl, iosvc->recv_tid); + iosvc->recv_tid = NULL; + } /* if io svc receive thread was successfully created */ + + (void) pthread_mutex_lock(&iosvc->msg_q_lock); + while (iosvc->msg_q_cur_len > 0) { + (void) etm_iosvc_msg_deq(hdl, iosvc, &msg_ele); + fmd_hdl_free(hdl, msg_ele.msg, msg_ele.msg_size); + } + (void) pthread_mutex_unlock(&iosvc->msg_q_lock); + + if (iosvc->fmd_xprt != NULL) + fmd_xprt_close(hdl, iosvc->fmd_xprt); + ldom_fini(etm_lhp); } - if (syslog_msgfd != -1) { - (void) close(syslog_msgfd); + if (etm_ds_fini) { + (*etm_ds_fini)(); + (void) dlclose(etm_dl_hdl); } fmd_hdl_debug(hdl, "info: module finalized ok\n"); diff --git a/usr/src/cmd/fm/modules/sun4v/etm/etm_ckpt.c b/usr/src/cmd/fm/modules/sun4v/etm/etm_ckpt.c new file mode 100644 index 0000000000..36edbd8332 --- /dev/null +++ b/usr/src/cmd/fm/modules/sun4v/etm/etm_ckpt.c @@ -0,0 +1,681 @@ +/* + * 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 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * etm_ckpt.c + * Description: + * Checkpoint the ereport events for persitence across fmd restart. + * + * Each ereport is stored in a named buffer. Each ereport is uniquely + * indentified by a id which is consists of a number of ereport fields. The + * name of the buffer is derived from the id. + * + * All ereport ids are stored in the circular list which is saved in a + * separate buffer. + */ + +#include <assert.h> +#include <stdio.h> +#include <pthread.h> +#include <strings.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/fm/ldom.h> +#include <sys/fm/protocol.h> +#include <fm/fmd_api.h> +#include <fm/libtopo.h> +#include <fm/topo_hc.h> + +#include "etm_etm_proto.h" +#include "etm_iosvc.h" +#include "etm_ckpt.h" +#include "etm_filter.h" + +#define ETM_ATTR_PRIMARY "primary" +#define ETM_ATTR_TOD "__tod" +#define ETM_LDOM_PRIMARY "primary" + +/* + * -------------------------- private variables ------------------------------ + */ + +static etm_ckpt_id_list_t *etm_id_lst = NULL; /* list of ereports ids */ + +static pthread_mutex_t etm_id_lst_lock; /* list lock */ + +/* + * -------------------------- functions -------------------------------------- + */ + +/* + * etm_ckpt_str_hash() + * Description: + * Hash a class name to a number + */ +static uint_t +etm_ckpt_str_hash(char *str) +{ + uint_t hash = 0; /* hash value */ + + if (str == NULL) + return (0); + + while (*str != '\0') + hash += *str++; + + return (hash); +} + +/* + * etm_ckpt_id2str() + * Description: + * Get the string of an ereport id. It is used as the named buffer that + * store the ereport. + */ +static void +etm_ckpt_id2str(etm_ckpt_erpt_id_t *id, char *str, size_t size) { + (void) snprintf(str, size, "%s_%llx_%d_%x_%d", ETM_CKPT_ERPT_PREFIX, + id->ei_ena, id->ei_hash, id->ei_tod1, id->ei_pri); +} + +/* + * etm_ckpt_erpt2id() + * Description: + * Get the buffer name and ereport id of a given ereport + */ +static int +etm_ckpt_erpt2id(fmd_hdl_t *hdl, nvlist_t *erpt, etm_ckpt_erpt_id_t *id, + char *str, int size) { + char *class = NULL; + uint64_t *tod; + uint_t sz; + boolean_t pri = B_FALSE; + + bzero(id, sizeof (etm_ckpt_erpt_id_t)); + + /* ena */ + if (nvlist_lookup_uint64(erpt, FM_EREPORT_ENA, &id->ei_ena) != 0) { + fmd_hdl_debug(hdl, "Ena not found\n"); + return (-1); + } + + /* class name */ + (void) nvlist_lookup_string(erpt, FM_CLASS, &class); + if (class == NULL) { + fmd_hdl_debug(hdl, "%s not found\n", FM_CLASS); + return (-1); + } + if (strncmp(class, FM_EREPORT_CLASS, strlen(FM_EREPORT_CLASS)) != 0) { + fmd_hdl_debug(hdl, "Only support checkpointing %s\n", + FM_EREPORT_CLASS); + return (-1); + } + id->ei_hash = etm_ckpt_str_hash(class); + + /* tod[1]: fractional of a second */ + if (nvlist_lookup_uint64_array(erpt, ETM_ATTR_TOD, &tod, &sz) == 0) { + if (sz >= 2) { + id->ei_tod1 = (uint32_t)tod[1]; + } + } + + /* primary flag */ + if (nvlist_lookup_boolean_value(erpt, ETM_ATTR_PRIMARY, &pri) == 0) { + id->ei_pri = pri ? 1 : 0; + } + + etm_ckpt_id2str(id, str, size); + + return (0); +} + +/* + * etm_ckpt_il_equal() + * Description: + * Test if two ereport ids are equal. + */ +static boolean_t +etm_ckpt_il_equal(etm_ckpt_erpt_id_t *i1, etm_ckpt_erpt_id_t *i2) +{ + return ((i1->ei_ena == i2->ei_ena) && (i1->ei_tod1 == i2->ei_tod1) && + (i1->ei_pri == i2->ei_pri) && (i1->ei_hash == i2->ei_hash)); +} + +/* + * etm_ckpt_il_resize() + * Description: + * Increase the size of the circular list and pack its entries. + */ +static void +etm_ckpt_il_resize(fmd_hdl_t *hdl, uint_t factor) +{ + etm_ckpt_id_list_t *il1, *il2; /* temp lists */ + size_t sz1, sz2; /* sizes of lists */ + int i, next; /* temp counters */ + etm_ckpt_erpt_id_t *p1, *p2, *s1, *s2; /* temp id pointers */ + etm_ckpt_erpt_id_t blank; /* blank ereport id */ + + if (factor == 0) + return; + + /* the present queue */ + il1 = etm_id_lst; + sz1 = sizeof (etm_ckpt_id_list_t) + il1->il_ids_sz; + + /* Create an empty queue with a new size */ + sz2 = sizeof (etm_ckpt_id_list_t) + (factor * il1->il_ids_sz); + il2 = fmd_hdl_zalloc(hdl, sz2, FMD_SLEEP); + il2->il_ver = ETM_CKPT_VERSION; + il2->il_max = factor * etm_id_lst->il_max; + il2->il_ids_sz = factor * il1->il_ids_sz; + + /* pointers to the two arrays of entries */ + bzero(&blank, sizeof (blank)); + s1 = (etm_ckpt_erpt_id_t *) + ((ptrdiff_t)il1 + sizeof (etm_ckpt_id_list_t)); + s2 = (etm_ckpt_erpt_id_t *) + ((ptrdiff_t)il2 + sizeof (etm_ckpt_id_list_t)); + + /* copy non-empty ereport ids from list il1 to il2. Toss the blank. */ + if (il1->il_head != il1->il_tail) { + for (i = il1->il_head; i != il1->il_tail; i = next) { + next = (i + 1) % il1->il_max; + p1 = s1 + next; + if (!etm_ckpt_il_equal(p1, &blank)) { + /* copy non-empty entries */ + il2->il_tail = (il2->il_tail + 1) % il2->il_max; + fmd_hdl_debug(hdl, "Copying entry %d to %d\n", + next, il2->il_tail); + p2 = s2 + il2->il_tail; + *p2 = *p1; + il2->il_cnt++; + } + } + } + + if (factor == 1) { + /* both lists have the same size, update the present list */ + bcopy(il2, il1, sz1); + fmd_hdl_free(hdl, il2, sz2); + fmd_buf_write(hdl, NULL, ETM_CKPT_IL_BUF, (void *) il1, sz1); + } else { + /* replace the present list */ + etm_id_lst = il2; + fmd_hdl_free(hdl, il1, sz1); + /* write to new buffer */ + fmd_buf_destroy(hdl, NULL, ETM_CKPT_IL_BUF); + fmd_buf_create(hdl, NULL, ETM_CKPT_IL_BUF, sz2); + fmd_buf_write(hdl, NULL, ETM_CKPT_IL_BUF, (void *) il2, sz2); + } +} + +/* + * etm_ckpt_il_find() + * Description: + * Find the ereport id in the list. + */ +/* ARGSUSED */ +static int +etm_ckpt_il_find(fmd_hdl_t *hdl, etm_ckpt_erpt_id_t *id) +{ + int i, next; /* temp counter */ + etm_ckpt_erpt_id_t *p, *s; /* temp erpt id */ + + fmd_hdl_debug(hdl, "etm_ckpt_il_find()\n"); + + /* empty list */ + if (etm_id_lst->il_head == etm_id_lst->il_tail) { + fmd_hdl_debug(hdl, "find an empty list\n"); + return (-1); + } + s = (etm_ckpt_erpt_id_t *)((ptrdiff_t)etm_id_lst + + sizeof (etm_ckpt_id_list_t)); + for (i = etm_id_lst->il_head; i != etm_id_lst->il_tail; i = next) { + next = (i + 1) % etm_id_lst->il_max; + p = s + next; + if (etm_ckpt_il_equal(p, id)) + return (i); + } + + return (-1); +} + +/* + * etm_ckpt_il_add() + * Description: + * Add an ereport id in the list. + */ +static int +etm_ckpt_il_add(fmd_hdl_t *hdl, etm_ckpt_erpt_id_t *id) { + int next; + etm_ckpt_erpt_id_t *p, *s; /* temp id */ + + /* + * resize the q if it is full. + * If the capacity is less 80%, purge the emtpy entries to make more + * room for new entries. Otherwise, double the queue size. + */ + next = (etm_id_lst->il_tail + 1) % etm_id_lst->il_max; + if (next == etm_id_lst->il_head) { + if ((etm_id_lst->il_cnt * 1.0 / etm_id_lst->il_max) < 0.8) { + etm_ckpt_il_resize(hdl, 1); + } else { + etm_ckpt_il_resize(hdl, 2); + } + + /* test if the list again */ + next = (etm_id_lst->il_tail + 1) % etm_id_lst->il_max; + if (next == etm_id_lst->il_head) { + fmd_hdl_error(hdl, "List is full %d %d\n", + etm_id_lst->il_head, etm_id_lst->il_tail); + } + } + + /* Add the id entry at the head */ + s = (etm_ckpt_erpt_id_t *)((ptrdiff_t)etm_id_lst + + sizeof (etm_ckpt_id_list_t)); + etm_id_lst->il_tail = (etm_id_lst->il_tail + 1) % etm_id_lst->il_max; + p = s + etm_id_lst->il_tail; + *p = *id; + etm_id_lst->il_cnt++; + + return (etm_id_lst->il_tail); +} + +/* + * etm_ckpt_il_delete() + * Description: + * Delete an ereport id from the list. + */ +int +etm_ckpt_il_delete(fmd_hdl_t *hdl, etm_ckpt_erpt_id_t *id) { + + int i, next; /* temp counter */ + etm_ckpt_erpt_id_t *p, *s; /* temp id pointers */ + etm_ckpt_erpt_id_t blank; /* blank id */ + + /* empty list */ + if (etm_id_lst->il_tail == etm_id_lst->il_head) { + fmd_hdl_debug(hdl, "Empty queue(%d)\n", etm_id_lst->il_head); + return (-1); + } + + bzero(&blank, sizeof (blank)); + s = (etm_ckpt_erpt_id_t *)((ptrdiff_t)etm_id_lst + + sizeof (etm_ckpt_id_list_t)); + + /* delete leading empty entries */ + for (i = etm_id_lst->il_head; i != etm_id_lst->il_tail; i = next) { + next = (i + 1) % etm_id_lst->il_max; + p = s + next; + if (!etm_ckpt_il_equal(p, &blank)) { + break; + } + etm_id_lst->il_cnt--; + etm_id_lst->il_head = next; + } + + /* empty queue */ + if (etm_id_lst->il_head == etm_id_lst->il_tail) { + fmd_hdl_debug(hdl, "Empty queue(%d)\n", etm_id_lst->il_head); + return (-1); + } + + /* find the entry and clear it */ + for (i = etm_id_lst->il_head; i != etm_id_lst->il_tail; i = next) { + next = (i + 1) % etm_id_lst->il_max; + p = s + next; + if (etm_ckpt_il_equal(p, id)) { + /* clear the entry */ + *p = blank; + etm_id_lst->il_cnt--; + + /* remove the entry if it is the last one */ + if (i == etm_id_lst->il_head) { + etm_id_lst->il_head = next; + } + return (i); + } + } + + return (-1); +} + + +/* + * etm_ckpt_il_restore() + * Description: + * Restore the idlist named buffer which is the circular list of the + * the ereport ids. + */ +void +etm_ckpt_il_restore(fmd_hdl_t *hdl) +{ + size_t size; /* buffer size */ + + /* get the buffer of the id list */ + size = fmd_buf_size(hdl, NULL, ETM_CKPT_IL_BUF); + if (size < sizeof (etm_ckpt_id_list_t)) { + fmd_hdl_debug(hdl, "Buffer name %s do not exist\n", + ETM_CKPT_IL_BUF); + return; + } + etm_id_lst = (etm_ckpt_id_list_t *)fmd_hdl_zalloc(hdl, size, FMD_SLEEP); + fmd_buf_read(hdl, NULL, ETM_CKPT_IL_BUF, (void *) etm_id_lst, size); + + /* check version */ + if (etm_id_lst->il_ver > ETM_CKPT_VERSION) { + + fmd_hdl_error(hdl, "Unsupport checkpoint version (%#x)\n", + etm_id_lst->il_ver); + fmd_hdl_free(hdl, (void *) etm_id_lst, size); + etm_id_lst = NULL; + return; + } + + /* check the length */ + if (etm_id_lst->il_ids_sz != (size - sizeof (etm_ckpt_id_list_t))) { + fmd_hdl_debug(hdl, "Invalid ids buffer size (%d, %d)\n", + etm_id_lst->il_ids_sz, size); + fmd_hdl_free(hdl, (void *) etm_id_lst, size); + etm_id_lst = NULL; + return; + } +} + +/* + * etm_ckpt_recover() + * Description: + * Recover ereports from the checkpointed data and dispatch them to the + * ldom queue(s). + */ +void +etm_ckpt_recover(fmd_hdl_t *hdl) +{ + int size; /* buffer size */ + int i, next; /* temp counter */ + boolean_t dirty = B_FALSE; /* dirty flag */ + uint64_t did; /* domain id */ + char name[ETM_LINE_LEN]; /* temp str */ + char ldom[ETM_LINE_LEN]; /* ldom id */ + etm_ckpt_erpt_id_t *p, *s; /* temp ereport id */ + etm_ckpt_erpt_id_t blank; /* blank ereport id */ + etm_ckpt_erpt_buf_t *ep; /* ereport buffer */ + size_t sz; /* size of ep */ + char *buf; /* temp buf */ + nvlist_t *nvl; /* ereport */ + etm_iosvc_t *iosvc; /* iosvc data struct */ + + /* + * restore the circular list of ereport ids + */ + etm_ckpt_il_restore(hdl); + if (etm_id_lst == NULL) { + fmd_hdl_debug(hdl, "Initialize a new id list\n"); + size = sizeof (etm_ckpt_id_list_t) + + ETM_CKPT_IL_MIN_SIZE * sizeof (etm_ckpt_erpt_id_t); + etm_id_lst = fmd_hdl_zalloc(hdl, size, FMD_SLEEP); + etm_id_lst->il_ver = ETM_CKPT_VERSION; + etm_id_lst->il_max = ETM_CKPT_IL_MIN_SIZE; + etm_id_lst->il_head = 0; + etm_id_lst->il_tail = 0; + etm_id_lst->il_ids_sz = + ETM_CKPT_IL_MIN_SIZE * sizeof (etm_ckpt_erpt_id_t); + fmd_buf_destroy(hdl, NULL, ETM_CKPT_IL_BUF); + fmd_buf_create(hdl, NULL, ETM_CKPT_IL_BUF, size); + fmd_buf_write(hdl, NULL, ETM_CKPT_IL_BUF, (void *) etm_id_lst, + size); + + /* commit */ + fmd_thr_checkpoint(hdl); + + return; + } + + /* Empty list */ + if ((etm_id_lst->il_head == etm_id_lst->il_tail) || + (etm_id_lst->il_cnt == 0)) { + return; + } + + /* Visit all the entries in the list */ + bzero(&blank, sizeof (blank)); + s = (etm_ckpt_erpt_id_t *)((ptrdiff_t)etm_id_lst + + sizeof (etm_ckpt_id_list_t)); + for (i = etm_id_lst->il_head; i != etm_id_lst->il_tail; i = next) { + next = (i + 1) % etm_id_lst->il_max; + p = s + next; + if (etm_ckpt_il_equal(p, &blank)) { + fmd_hdl_debug(hdl, "Skip empty entry %d\n", i); + continue; + } + + etm_ckpt_id2str(p, name, sizeof (name)); + fmd_hdl_debug(hdl, "Restoring entry %s\n", name); + if ((sz = fmd_buf_size(hdl, NULL, name)) == 0) { + fmd_hdl_error(hdl, "Clear the stale entry %s\n", name); + *p = blank; + continue; + } + ep = (etm_ckpt_erpt_buf_t *)fmd_hdl_zalloc(hdl, sz, FMD_SLEEP); + fmd_buf_read(hdl, NULL, name, (void *) ep, sz); + buf = (char *)((ptrdiff_t)ep + sizeof (etm_ckpt_erpt_buf_t)); + nvl = NULL; + if (nvlist_unpack(buf, ep->eb_len, &nvl, 0)) { + fmd_hdl_debug(hdl, "failed to unpack %s\n", name); + fmd_hdl_free(hdl, ep, sz); + continue; + } + fmd_hdl_free(hdl, ep, sz); + if (etm_filter_find_ldom_id(hdl, nvl, ldom, ETM_LINE_LEN, + &did) || (strcmp(name, ETM_LDOM_PRIMARY) == 0)) { + fmd_hdl_debug(hdl, "Discard event %s\n", name); + fmd_buf_destroy(hdl, NULL, name); + *p = blank; + nvlist_free(nvl); + dirty = B_TRUE; + continue; + } + + fmd_hdl_debug(hdl, "Dispatch %s to ldom %s\n", name, ldom); + + /* + * Find the queue of the ldom, create it if not exist. + * Then insert this event into the queue. + */ + iosvc = etm_iosvc_lookup(hdl, ldom, DS_INVALID_HDL, B_TRUE); + if (iosvc != NULL) { + (void) etm_pack_ds_msg(hdl, iosvc, NULL, 0, nvl, SP_MSG, + ETM_CKPT_RESTORE); + } + nvlist_free(nvl); + } + if (dirty) { + /* update the buffer of the queue */ + size = sizeof (etm_ckpt_id_list_t) + etm_id_lst->il_ids_sz; + fmd_buf_write(hdl, NULL, ETM_CKPT_IL_BUF, (void *) etm_id_lst, + size); + + /* commit */ + fmd_thr_checkpoint(hdl); + } + +} /* etm_ckpt_recover */ + + +/* + * etm_ckpt_add_entry() + * Description: + * Save an ereport for persistence. + */ +int +etm_ckpt_add_entry(fmd_hdl_t *hdl, nvlist_t *erpt) { + etm_ckpt_erpt_id_t id; + char name[ETM_LINE_LEN]; + int rc; /* gen use */ + size_t sz; /* size */ + size_t buflen; /* sz of packed erpt */ + uint8_t *buf; /* buffer of erpt */ + etm_ckpt_erpt_buf_t *hdr; + + /* map ereport to id */ + bzero(name, ETM_LINE_LEN); + rc = etm_ckpt_erpt2id(hdl, erpt, &id, name, ETM_LINE_LEN); + if (rc != 0) { + fmd_hdl_debug(hdl, "Invalid ereport\n"); + return (rc); + } + + /* + * check for a duplicate entry in the id list + * find the ereport buffer and search for the id + */ + if (fmd_buf_size(hdl, NULL, name) > 0 && + etm_ckpt_il_find(hdl, &id) >= 0) { + fmd_hdl_debug(hdl, "Duplicate id %s\n", name); + return (-1); + } + + /* Create the ereport buffer */ + if (nvlist_size(erpt, &buflen, NV_ENCODE_XDR) != 0) { + fmd_hdl_debug(hdl, "nvlist_size fails\n"); + return (-1); + } + sz = sizeof (etm_ckpt_erpt_buf_t) + buflen; + hdr = (etm_ckpt_erpt_buf_t *)fmd_hdl_zalloc(hdl, sz, FMD_SLEEP); + buf = (uint8_t *)((ptrdiff_t)hdr + sizeof (etm_ckpt_erpt_buf_t)); + hdr->eb_ver = ETM_CKPT_VERSION; + hdr->eb_len = buflen; + if (nvlist_pack(erpt, (char **)&buf, &buflen, NV_ENCODE_XDR, 0) != 0) { + fmd_hdl_free(hdl, hdr, sz); + fmd_hdl_debug(hdl, "unpack fails\n"); + return (-1); + } + fmd_hdl_debug(hdl, "Add ckpt event(%s, %d)\n", name, sz); + fmd_buf_create(hdl, NULL, name, sz); + fmd_buf_write(hdl, NULL, name, hdr, sz); + fmd_hdl_free(hdl, hdr, sz); + + /* Insert the ereport id into the id list */ + if (etm_ckpt_il_add(hdl, &id) < 0) { + fmd_hdl_debug(hdl, "Insert id %s failed\n", name); + fmd_buf_destroy(hdl, NULL, name); + return (-1); + } + + /* update the buffer of the queue */ + sz = sizeof (etm_ckpt_id_list_t) + etm_id_lst->il_ids_sz; + fmd_buf_write(hdl, NULL, ETM_CKPT_IL_BUF, (void *) etm_id_lst, sz); + + /* commit */ + fmd_thr_checkpoint(hdl); + + return (0); +} + +/* + * etm_ckpt_delete_entry() + * Description: + * Delete an ereport id in the list. + */ +static int +etm_ckpt_delete_entry(fmd_hdl_t *hdl, nvlist_t *erpt) { + etm_ckpt_erpt_id_t id; + char name[ETM_LINE_LEN]; + int rc; /* return code */ + size_t sz; /* size */ + + /* get id, id name */ + bzero(name, ETM_LINE_LEN); + if (etm_ckpt_erpt2id(hdl, erpt, &id, name, ETM_LINE_LEN) != 0) { + fmd_hdl_debug(hdl, "Invalid ereport\n"); + return (-1); + } + fmd_hdl_debug(hdl, "Delete ckpt event(%s)\n", name); + + /* delete the ereport buffer */ + if (fmd_buf_size(hdl, NULL, name) > 0) { + fmd_buf_destroy(hdl, NULL, name); + } + + rc = etm_ckpt_il_delete(hdl, &id); + if (rc < 0) { + fmd_hdl_debug(hdl, "Delete id %s failed\n", name); + return (rc); + } + + /* update the buffer of the queue */ + sz = sizeof (etm_ckpt_id_list_t) + etm_id_lst->il_ids_sz; + fmd_buf_write(hdl, NULL, ETM_CKPT_IL_BUF, (void *) etm_id_lst, sz); + + /* commit */ + fmd_thr_checkpoint(hdl); + + return (rc); +} + +int +etm_ckpt_add(fmd_hdl_t *hdl, nvlist_t *erpt) { + + int rc; /* return code */ + + (void) pthread_mutex_lock(&etm_id_lst_lock); + + rc = etm_ckpt_add_entry(hdl, erpt); + + (void) pthread_mutex_unlock(&etm_id_lst_lock); + + return (rc >= 0 ? 0 : rc); +} + +int +etm_ckpt_delete(fmd_hdl_t *hdl, nvlist_t *erpt) { + int rc; /* return code */ + + (void) pthread_mutex_lock(&etm_id_lst_lock); + + rc = etm_ckpt_delete_entry(hdl, erpt); + + (void) pthread_mutex_unlock(&etm_id_lst_lock); + + return (rc >= 0 ? 0 : rc); +} + +/* ARGSUSED */ +void +etm_ckpt_init(fmd_hdl_t *hdl) { + (void) pthread_mutex_init(&etm_id_lst_lock, NULL); + etm_id_lst = NULL; +} + +void +etm_ckpt_fini(fmd_hdl_t *hdl) { + if (etm_id_lst != NULL) { + fmd_hdl_free(hdl, etm_id_lst, + sizeof (etm_ckpt_id_list_t) + etm_id_lst->il_ids_sz); + } + (void) pthread_mutex_destroy(&etm_id_lst_lock); +} diff --git a/usr/src/cmd/fm/modules/sun4v/etm/etm_ckpt.h b/usr/src/cmd/fm/modules/sun4v/etm/etm_ckpt.h new file mode 100644 index 0000000000..d25be56d22 --- /dev/null +++ b/usr/src/cmd/fm/modules/sun4v/etm/etm_ckpt.h @@ -0,0 +1,108 @@ +/* + * 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 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * etm_ckpt.h + * + * Header file of checkpointing ereports for persistence + * + */ + +#ifndef _ETM_CKPT_H +#define _ETM_CKPT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <fm/fmd_api.h> + +#define ETM_CKPT_VERSION 0x10 +#define ETM_CKPT_ERPT_PREFIX "ev" +#define ETM_LINE_LEN 256 + +/* + * Format of a named buffer that stores an ereport. + */ +typedef struct etm_ckpt_erpt_buf { + uint8_t eb_ver; /* version major.minor */ + uint8_t eb_rev; /* reserved field */ + uint16_t eb_len; /* size of packed ereport */ + /* nvlist packed erpt event */ +} etm_ckpt_erpt_buf_t; + +/* + * Ereport id + * Each ereport, which is stored in a named buffer, is uniquely identified by + * fields in the ereport. The named buffer name is derived from this struct + * as following + * ev_${ena}_${hash{class)}_${tod[1]}_${primary} + */ +typedef struct etm_ckpt_erpt_id { + uint64_t ei_ena; /* ereport ena */ + uint32_t ei_tod1; /* tod[1]: fractional second */ + uint16_t ei_hash; /* hash(ereport class name) */ + uint8_t ei_pri; /* primary field */ + uint8_t ei_rev; /* reserved field */ +} etm_ckpt_erpt_id_t; + +/* + * A circular list of ereport ids + */ +typedef struct etm_ckpt_id_list { + uint8_t il_ver; /* version major.minor */ + uint8_t il_rev1; /* reserve field */ + uint16_t il_max; /* max number of erpt ids in list */ + uint16_t il_cnt; /* number of valid ids in list */ + uint16_t il_head; /* head of the list */ + uint16_t il_tail; /* tail of the list */ + uint16_t il_ids_sz; /* size of the array of ids */ + uint32_t il_rev2; /* reserve field */ + /* array of ids */ +} etm_ckpt_id_list_t; + +#define ETM_CKPT_IL_BUF "idlist" +#define ETM_CKPT_IL_MIN_SIZE 0x8 + +/* + * Checkpoint options + */ +#define ETM_CKPT_NOOP 0x0 +#define ETM_CKPT_SAVE 0x1 +#define ETM_CKPT_RESTORE 0x2 + +void etm_ckpt_recover(fmd_hdl_t *hdl); +int etm_ckpt_add(fmd_hdl_t *hdl, nvlist_t *evp); +int etm_ckpt_delete(fmd_hdl_t *hdl, nvlist_t *evp); + +void etm_ckpt_init(fmd_hdl_t *hdl); +void etm_ckpt_fini(fmd_hdl_t *hdl); + +#ifdef __cplusplus +} +#endif + +#endif /* _ETM_CKPT_H */ diff --git a/usr/src/cmd/fm/modules/sun4v/etm/etm_filter.c b/usr/src/cmd/fm/modules/sun4v/etm/etm_filter.c new file mode 100644 index 0000000000..cb1cfb27c7 --- /dev/null +++ b/usr/src/cmd/fm/modules/sun4v/etm/etm_filter.c @@ -0,0 +1,366 @@ +/* + * 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 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * etm_filter.c + * Description: + * Find the ldom that own the resource specified in the detector field + * of the ereport. + */ + +#include <pthread.h> +#include <stdio.h> +#include <strings.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/fm/ldom.h> +#include <sys/fm/protocol.h> +#include <fm/fmd_api.h> +#include <fm/libtopo.h> +#include <fm/topo_hc.h> + +#include "etm_filter.h" + +static etm_prc_t *etm_rcs; /* vector of root complexes */ +static uint16_t etm_rc_cnt; /* count of rc entries in rcs */ +static uint16_t etm_rc_max; /* max entries allowed in rcs */ +static pthread_mutex_t etm_rc_lock; /* lock of the rc vector */ + + +extern ldom_hdl_t *etm_lhp; /* libldom handle */ + +/* ARGSUSED */ +static int +etm_pciexrc_walker(topo_hdl_t *thp, tnode_t *node, void *arg) +{ + int i; /* temp counter */ + int n; /* temp size of new vector */ + int err; /* temp error var */ + char *str; /* topo node value */ + fmd_hdl_t *hdl = arg; /* etm mod hdl */ + etm_prc_t *rcl; /* root complex vector */ + etm_prc_t *p; /* temp pointer */ + topo_instance_t ins; /* rc id */ + uint64_t ba; /* bus address */ + + /* pciexrc node */ + if (strcmp(topo_node_name(node), PCIEX_ROOT) != 0) + return (TOPO_WALK_NEXT); + + if (topo_prop_get_string(node, TOPO_PGROUP_IO, TOPO_IO_DEV, &str, + &err) != 0) + return (TOPO_WALK_NEXT); + + /* physical id and bus address of a root complex */ + ins = topo_node_instance(node); + (void) sscanf(str, "/pci@%llx", &ba); + + /* + * prc vector is full, so double its size + */ + if (etm_rc_cnt >= etm_rc_max) { + n = (etm_rc_max == 0) ? 1 : 2 * etm_rc_max; + rcl = fmd_hdl_zalloc(hdl, n * sizeof (etm_prc_t), FMD_SLEEP); + for (i = 0, p = rcl; i < n; i++, p++) { + p->prc_id = -1; + p->prc_status = -1; + } + if (etm_rcs != NULL) { + bcopy(etm_rcs, rcl, etm_rc_max * sizeof (etm_prc_t)); + fmd_hdl_free(hdl, etm_rcs, + etm_rc_max * sizeof (etm_prc_t)); + } + etm_rcs = rcl; + etm_rc_max = n; + } + + if (etm_rc_cnt >= etm_rc_max) { + fmd_hdl_abort(hdl, "rcs is full. Expect counter value %d<%d\n", + etm_rc_cnt, etm_rc_max); + } + + /* Add the rc at the end of the list */ + p = etm_rcs + etm_rc_cnt; + p->prc_id = ins; + p->prc_cfg_handle = ba; + etm_rc_cnt++; + + return (TOPO_WALK_NEXT); +} + +/* + * etm_pciexrc_init() + * Description: + * Walk through the topology to find the pciexrc nodes. Then save the + * physical instances and bus addreses in a vector. + */ +static void +etm_pciexrc_init(fmd_hdl_t *hdl) +{ + topo_hdl_t *thp; /* topo handle */ + topo_walk_t *twp; /* topo walk handle */ + int err; /* topo error */ + + if ((thp = fmd_hdl_topo_hold(hdl, TOPO_VERSION)) == NULL) + return; + twp = topo_walk_init(thp, FM_FMRI_SCHEME_HC, etm_pciexrc_walker, + (void *) hdl, &err); + if (twp == NULL) { + fmd_hdl_topo_rele(hdl, thp); + return; + } + (void) topo_walk_step(twp, TOPO_WALK_CHILD); + topo_walk_fini(twp); + fmd_hdl_topo_rele(hdl, thp); +} + +/* + * etm_update_prc() + * Description: + * Query ldmd for the ldom id + */ +void +etm_update_prc(fmd_hdl_t *hdl, etm_prc_t *prc) +{ + char name[MAX_LDOM_NAME]; /* domain name */ + uint64_t virt_cfg_handle; /* bus address from ldmd */ + uint64_t did; /* domain id */ + + if (prc == NULL) + return; + + /* call libldom to find the ldom id */ + prc->prc_status = ldom_find_id(etm_lhp, prc->prc_cfg_handle, + LDOM_RSRC_PCI, &virt_cfg_handle, name, MAX_LDOM_NAME, &did); + if (prc->prc_status) { + return; + } + + /* cache the ldom id */ + prc->prc_did = did; + if (prc->prc_name != NULL) { + fmd_hdl_free(hdl, prc->prc_name, prc->prc_name_sz); + } + prc->prc_name_sz = strlen(name) + 1; + prc->prc_name = fmd_hdl_zalloc(hdl, prc->prc_name_sz, FMD_SLEEP); + (void) strncpy(prc->prc_name, name, prc->prc_name_sz); +} + +/* + * etm_find_ldom_id() + * Description: + * Find the ldom name and the domain id that owns the resource specified in + * the ereport detector + */ +int +etm_filter_find_ldom_id(fmd_hdl_t *hdl, nvlist_t *evp, char *name, + int name_size, uint64_t *did) +{ + char *str; /* temp string */ + char *s; /* temp string */ + int i; /* loop counter */ + int ins; /* instance number */ + nvlist_t *det; /* ereport detector */ + nvlist_t **hcl; /* hc name-value pair list */ + uint_t sz; /* size of hcl */ + etm_prc_t *prc; /* root complex */ + + /* check paramters */ + if (name == NULL || name_size <= 0) { + fmd_hdl_debug(hdl, "Invalid parameters"); + return (-1); + } + + /* must be an ereport */ + if ((nvlist_lookup_string(evp, FM_CLASS, &str) != 0) || + (strncmp(str, FM_EREPORT_CLASS, strlen(FM_EREPORT_CLASS)) != 0)) { + fmd_hdl_debug(hdl, "not an ereport"); + return (-1); + } + + /* the detector is of hc-scheme */ + if (nvlist_lookup_nvlist(evp, FM_EREPORT_DETECTOR, &det) != 0) { + fmd_hdl_debug(hdl, "ereport detector not found"); + return (-1); + } + if ((nvlist_lookup_string(det, FM_FMRI_SCHEME, &str) != 0) || + (strcmp(str, FM_FMRI_SCHEME_HC) != 0)) { + fmd_hdl_debug(hdl, "detector is not hc-schemed\n"); + return (-1); + } + + /* + * Find the pciexrc and extract the instance number + */ + if (nvlist_lookup_nvlist_array(det, FM_FMRI_HC_LIST, &hcl, &sz) != 0) { + fmd_hdl_debug(hdl, "%s is not found\n", FM_FMRI_HC_LIST); + return (-1); + } + for (i = 0; i < sz; i++) { + if (nvlist_lookup_string(hcl[i], FM_FMRI_HC_NAME, &str) == 0 && + nvlist_lookup_string(hcl[i], FM_FMRI_HC_ID, &s) == 0 && + strcmp(str, PCIEX_ROOT) == 0) { + (void) sscanf(s, "%d", &ins); + break; + } + } + if (i >= sz) { + fmd_hdl_debug(hdl, "%s not found\n", PCIEX_ROOT); + return (-1); + } + + (void) pthread_mutex_lock(&etm_rc_lock); + + /* search the entry by the physical instance number */ + for (i = 0, prc = etm_rcs; prc != NULL && i < etm_rc_cnt; + i++, prc++) { + if (prc->prc_id == ins) { + /* update the cached entry */ + if (prc->prc_status != 0) { + etm_update_prc(hdl, prc); + } + /* check for cached ldom name */ + if (prc->prc_status == 0 && prc->prc_name != NULL) { + *did = prc->prc_did; + (void) strncpy(name, prc->prc_name, name_size); + (void) pthread_mutex_unlock(&etm_rc_lock); + return (0); + } + break; + } + } + if (i >= etm_rc_cnt) { + fmd_hdl_debug(hdl, "prc[%d] not found\n", ins); + } + + (void) pthread_mutex_unlock(&etm_rc_lock); + + return (-1); +} /* etm_find_ldom_id */ + +/* + * etm_find_ldom_name() + * Description: + * Find the ldom name of a given domain id (did) + */ +int +etm_filter_find_ldom_name(fmd_hdl_t *hdl, uint64_t did, char *name, + int name_size) +{ + int rc = -1; /* return value */ + int i; /* loop counter */ + etm_prc_t *prc; /* root complex */ + + (void) pthread_mutex_lock(&etm_rc_lock); + + /* visit all the root complexes to find an entry that matches the did */ + for (i = 0, prc = etm_rcs; prc != NULL && i < etm_rc_cnt; + i++, prc++) { + /* update the cached entry */ + if (prc->prc_status != 0) { + etm_update_prc(hdl, prc); + } + /* find the cached ldom name */ + if (prc->prc_status == 0 && prc->prc_did == did) { + rc = 0; + (void) strncpy(name, prc->prc_name ? prc->prc_name : "", + name_size); + break; + } + } + + (void) pthread_mutex_unlock(&etm_rc_lock); + + return (rc); +} /* etm_find_ldom_name */ + +/* + * etm_filter_handle_ldom_event() + * Description: + * Invalidate the ldom name in the physical root complex vector. + */ +void +etm_filter_handle_ldom_event(fmd_hdl_t *hdl, etm_async_event_type_t event, + char *name) { + int i; /* loop counter */ + etm_prc_t *prc; /* root complex */ + + /* + * Clear the cached ldom name + */ + switch (event) { + case ETM_ASYNC_EVENT_LDOM_ADD: + case ETM_ASYNC_EVENT_LDOM_REMOVE: + case ETM_ASYNC_EVENT_LDOM_BIND: + case ETM_ASYNC_EVENT_LDOM_UNBIND: + (void) pthread_mutex_lock(&etm_rc_lock); + for (i = 0, prc = etm_rcs; prc != NULL && i < etm_rc_cnt; + i++, prc++) { + if (prc->prc_name != NULL && + strcmp(prc->prc_name, name) == 0) { + fmd_hdl_free(hdl, prc->prc_name, + prc->prc_name_sz); + prc->prc_name = NULL; + prc->prc_name_sz = 0; + prc->prc_status = -1; + } + } + (void) pthread_mutex_unlock(&etm_rc_lock); + break; + default: + break; + } +} + +/* ARGSUSED */ +void +etm_filter_init(fmd_hdl_t *hdl) { + etm_rcs = NULL; + etm_rc_cnt = 0; + etm_rc_max = 0; + (void) pthread_mutex_init(&etm_rc_lock, NULL); + etm_pciexrc_init(hdl); +} + +void +etm_filter_fini(fmd_hdl_t *hdl) { + int i; /* loop counter */ + etm_prc_t *prc; /* root complex pointer */ + + for (i = 0, prc = etm_rcs; prc != NULL && i < etm_rc_cnt; + i++, prc++) { + if (prc->prc_name != NULL) { + fmd_hdl_free(hdl, prc->prc_name, prc->prc_name_sz); + prc->prc_name = NULL; + prc->prc_name_sz = 0; + prc->prc_status = -1; + } + } + if (etm_rcs != NULL && etm_rc_max > 0) { + fmd_hdl_free(hdl, etm_rcs, etm_rc_max * sizeof (etm_prc_t)); + } + (void) pthread_mutex_destroy(&etm_rc_lock); +} diff --git a/usr/src/cmd/fm/modules/sun4v/etm/etm_filter.h b/usr/src/cmd/fm/modules/sun4v/etm/etm_filter.h new file mode 100644 index 0000000000..ef27f4d468 --- /dev/null +++ b/usr/src/cmd/fm/modules/sun4v/etm/etm_filter.h @@ -0,0 +1,69 @@ +/* + * 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 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * etm_filter.h + * + * Header file of the event filter + * + */ + +#ifndef _ETM_FILTER_H +#define _ETM_FILTER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <fm/fmd_api.h> + +#include "etm_iosvc.h" + +/* A physical root complex */ +typedef struct etm_prc { + int32_t prc_id; /* physical id of the rc */ + uint64_t prc_cfg_handle; /* bus address */ + char *prc_name; /* bound ldom name */ + size_t prc_name_sz; /* size of name */ + int prc_status; /* ldom query status */ + uint64_t prc_did; /* ldom id */ +} etm_prc_t; + +void etm_filter_init(fmd_hdl_t *hdl); +void etm_filter_fini(fmd_hdl_t *hdl); + +int etm_filter_find_ldom_id(fmd_hdl_t *hdl, nvlist_t *erpt, char *name, + int name_size, uint64_t *did); +int etm_filter_find_ldom_name(fmd_hdl_t *hdl, uint64_t did, char *name, + int name_size); +void etm_filter_handle_ldom_event(fmd_hdl_t *hdl, etm_async_event_type_t event, + char *name); + +#ifdef __cplusplus +} +#endif + +#endif /* _ETM_FILTER_H */ diff --git a/usr/src/cmd/fm/modules/sun4v/etm/etm_impl.h b/usr/src/cmd/fm/modules/sun4v/etm/etm_impl.h index bb6df38b45..9647b8e19d 100644 --- a/usr/src/cmd/fm/modules/sun4v/etm/etm_impl.h +++ b/usr/src/cmd/fm/modules/sun4v/etm/etm_impl.h @@ -35,8 +35,6 @@ #ifndef _ETM_IMPL_H #define _ETM_IMPL_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif @@ -93,6 +91,7 @@ extern "C" { #define ETM_PROP_NM_FACILITY "etm_alert_facility" #define ETM_PROP_NM_MAX_RESP_Q_LEN "etm_resp_q_max_len" +#define ETM_PROP_NM_FMA_RESP_WAIT_TIME "etm_fma_resp_wait_time" #define ETM_PROP_NM_BAD_ACC_TO_SEC "etm_bad_acc_to_sec" diff --git a/usr/src/cmd/fm/modules/sun4v/etm/etm_iosvc.h b/usr/src/cmd/fm/modules/sun4v/etm/etm_iosvc.h new file mode 100644 index 0000000000..bf862e7fc5 --- /dev/null +++ b/usr/src/cmd/fm/modules/sun4v/etm/etm_iosvc.h @@ -0,0 +1,155 @@ +/* + * 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 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * etm_iosvc.h + * + * Header file of the support for io service ldom + * + */ + +#ifndef _ETM_IO_SVC_H +#define _ETM_IO_SVC_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ------------------------------ includes ----------------------------------- + */ + +#include <sys/fm/protocol.h> +#include <sys/libds.h> +#include <sys/fm/ldom.h> +#include <fm/fmd_api.h> +#include "etm_xport_api.h" +#include "etm_etm_proto.h" + +#include <libnvpair.h> + +#include <pthread.h> + +#define FORWARDING_FAULTS_TO_CONTROL 0 /* not to forward faults to control */ +#define ASYNC_EVENT_Q_SIZE 100 /* size of the async event q */ +#define NUM_OF_ROOT_DOMAINS 8 /* size of iosvc_list structure array */ +#define MAXLEN 0x6000 /* max size of an FMA event */ +#define FMD_EVN_TTL "__ttl" /* name-value pair for ev_ttl */ + +typedef enum { + + ETM_ASYNC_EVENT_TOO_LOW = 0, /* range check place holder */ + ETM_ASYNC_EVENT_LDOM_BIND, /* async event type: ldom event */ + ETM_ASYNC_EVENT_LDOM_UNBIND, /* async event type: ldom event */ + ETM_ASYNC_EVENT_LDOM_ADD, /* async event type: ldom event */ + ETM_ASYNC_EVENT_LDOM_REMOVE, /* async event type: ldom event */ + ETM_ASYNC_EVENT_DS_REG_CB, /* async event type: DS reg callback */ + ETM_ASYNC_EVENT_DS_UNREG_CB, /* async event type: DS unreg cllback */ + ETM_ASYNC_EVENT_TOO_BIG /* range check place holder */ + +} etm_async_event_type_t; /* async etm event type */ + + +typedef enum { + + SP_MSG = 0, /* msg for ereports from SP */ + FMD_XPRT_OTHER_MSG, /* fmd all other xprt msg */ + FMD_XPRT_RUN_MSG /* fmd xprt run msg */ + +} etm_pack_msg_type_t; /* msg type for etm_pack_ds_msg() */ + +typedef struct etm_iosvc_q_ele { + + char *msg; /* ptr to ETM io svc msg */ + size_t msg_size; /* sizeof ETM io svc msg */ + uint_t ckpt_flag; /* checkpoint flags */ + + struct etm_iosvc_q_ele *msg_nextp; /* PRIVATE - next ele ptr */ + +} etm_iosvc_q_ele_t; /* out-going etm msg queue element */ + + + +typedef struct etm_iosvc { + char ldom_name[MAX_LDOM_NAME]; /* ldom_name */ + pthread_cond_t msg_q_cv; /* nudges send msg func more to send */ + pthread_mutex_t msg_q_lock; /* protects iosvc msg Q */ + etm_iosvc_q_ele_t + *msg_q_head; + /* ptr to cur head of the msg Q */ + etm_iosvc_q_ele_t + *msg_q_tail; + /* ptr to cur tail of the msg Q */ + uint32_t msg_q_cur_len; + /* cur len of the msg Q */ + uint32_t msg_q_max_len; + /* max len of the msg Q */ + uint32_t cur_send_xid; /* current trnsaction id for io svc q */ + uint32_t xid_posted_ev; /* xid of last event posted ok to fmd */ + ds_hdl_t ds_hdl; /* the ds hdl for this io svc ldom */ + fmd_xprt_t *fmd_xprt; /* fmd transport layer handle */ + pthread_t send_tid; /* tid of sending msgs 2 remote iosvc */ + pthread_t recv_tid; /* tid of recving msgs frm rmte iosvc */ + pthread_cond_t msg_ack_cv; /* ready 2 send nxt or resend cur one */ + pthread_mutex_t msg_ack_lock; /* protects msg_ack_cv */ + int thr_is_dying; /* flag to exit the thread */ + uint32_t start_sending_Q; /* flag to strt sending msg Q */ + uint32_t ack_ok; /* indicate if the ACK has come */ +} etm_iosvc_t; /* structure to support io service ldom */ + + +typedef struct etm_async_event_ele { + + etm_async_event_type_t event_type; /* async event type */ + ds_hdl_t ds_hdl; /* ds handle */ + char ldom_name[MAX_LDOM_NAME]; /* ldom name */ + ds_domain_hdl_t dhdl; /* ldom handle */ + + struct etm_async_event_ele *async_event_nextp; + /* next ele ptr */ + +} etm_async_event_ele_t; /* etm async event queue element */ + + +/* + * This function + */ +extern etm_iosvc_t *etm_iosvc_lookup(fmd_hdl_t *fmd_hdl, char *ldom_name, + ds_hdl_t ds_hdl, boolean_t iosvc_create); + + +/* + * extern etm_iosvc_t *etm_lookup_iosvc(char *ldom_name); + */ +extern int etm_pack_ds_msg(fmd_hdl_t *fmd_hdl, etm_iosvc_t *iosvc, + etm_proto_v1_ev_hdr_t *ev_hdrp, size_t hdr_sz, nvlist_t *evp, + etm_pack_msg_type_t msg_type, uint_t ckpt_opt); + +#ifdef __cplusplus +} +#endif + +#endif /* _ETM_IO_SVC_H */ diff --git a/usr/src/lib/fm/libldom/Makefile b/usr/src/lib/fm/libldom/Makefile index a5de1baa50..970b336f1f 100644 --- a/usr/src/lib/fm/libldom/Makefile +++ b/usr/src/lib/fm/libldom/Makefile @@ -19,11 +19,9 @@ # CDDL HEADER END # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" -# include ../../Makefile.lib include ../Makefile.lib @@ -31,6 +29,8 @@ include ../Makefile.lib FMHDRS = ldom.h HDRDIR = sparc +ROOTFMHDRDIR = $(ROOT)/usr/platform/sun4v/include/sys/fm + SUBDIRS = $(MACH) $(BUILD64)SUBDIRS += $(MACH64) @@ -38,15 +38,21 @@ all := TARGET = all clean := TARGET = clean clobber := TARGET = clobber install := TARGET = install -install_h := TARGET = install_h lint := TARGET = lint .KEEP_STATE: -all clean clobber install_h lint: $(SUBDIRS) +all clean clobber lint: $(SUBDIRS) install: install_h .WAIT $(SUBDIRS) +install_h: $(ROOTFMHDRS) + +$(ROOTFMHDRS): $(ROOTFMHDRDIR) + +$(ROOTFMHDRDIR)/%: $(HDRDIR)/% + $(INS.file) + check: $(CHECKHDRS) $(SUBDIRS): FRC diff --git a/usr/src/lib/fm/libldom/Makefile.com b/usr/src/lib/fm/libldom/Makefile.com index daf82120d6..0c8cba5a2b 100644 --- a/usr/src/lib/fm/libldom/Makefile.com +++ b/usr/src/lib/fm/libldom/Makefile.com @@ -26,7 +26,7 @@ LIBRARY = libldom.a VERS = .1 -LIBSRCS = ldom.c ldmsvcs_utils.c +LIBSRCS = ldom.c ldom_alloc.c ldmsvcs_utils.c ldom_xmpp_client.c OBJECTS = $(LIBSRCS:%.c=%.o) include ../../../Makefile.lib @@ -37,12 +37,13 @@ SRCDIR = ../sparc LIBS = $(DYNLIB) $(LINTLIB) -CPPFLAGS += -I. -I$(SRC)/uts/sun4v -I$(ROOT)/usr/platform/sun4v/include +CPPFLAGS += -I. -I$(SRC)/uts/sun4v -I$(ROOT)/usr/platform/sun4v/include \ + -I/usr/include/libxml2 -I/usr/sfw/include CFLAGS += $(CCVERBOSE) $(C_BIGPICFLAGS) CFLAGS64 += $(CCVERBOSE) $(C_BIGPICFLAGS) LDLIBS += $(MACH_LDLIBS) -LDLIBS += -lfmd_agent -lnvpair -lscf -lmdesc -lc +LDLIBS += -lfmd_agent -lnvpair -lscf -lmdesc -lc -lxml2 -lsocket -lumem LINTFLAGS = -msux LINTFLAGS64 = -msux -m64 diff --git a/usr/src/lib/fm/libldom/sparc/Makefile b/usr/src/lib/fm/libldom/sparc/Makefile index 261bf4c60f..1f0c37cf36 100644 --- a/usr/src/lib/fm/libldom/sparc/Makefile +++ b/usr/src/lib/fm/libldom/sparc/Makefile @@ -19,11 +19,9 @@ # CDDL HEADER END # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" -# MACH_LDLIBS = -L$(ROOT)/usr/lib/fm @@ -31,19 +29,4 @@ include ../Makefile.com DYNFLAGS += -R/usr/lib/fm -FMHDRS = ldom.h -HDRDIR = . - -ROOTFMHDRDIR = $(ROOT)/usr/platform/sun4v/include/sys/fm - install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) - -install_h: $(ROOTFMHDRS) - -$(ROOTFMHDRS): $(ROOTFMHDRDIR) - -$(ROOTFMHDRDIR): - $(INS.dir) - -$(ROOTFMHDRDIR)/%: $(HDRDIR)/% - $(INS.file) diff --git a/usr/src/lib/fm/libldom/sparc/ldmsvcs_utils.c b/usr/src/lib/fm/libldom/sparc/ldmsvcs_utils.c index 8093406a2f..32e47aa27b 100644 --- a/usr/src/lib/fm/libldom/sparc/ldmsvcs_utils.c +++ b/usr/src/lib/fm/libldom/sparc/ldmsvcs_utils.c @@ -25,6 +25,7 @@ #include <stdlib.h> #include <stdio.h> + #include <strings.h> #include <sys/types.h> #include <sys/stat.h> @@ -41,6 +42,8 @@ #include <values.h> #include <libscf.h> +#include <ctype.h> + #include "ldmsvcs_utils.h" #define ASSERT(cnd) \ @@ -121,7 +124,6 @@ static struct ldmsvcs_info *channel_init(struct ldom_hdl *lhp); static int channel_openreset(struct ldmsvcs_info *lsp); static int read_msg(struct ldmsvcs_info *lsp); - static int get_smf_int_val(char *prop_nm, int min, int max, int default_val) { @@ -930,8 +932,8 @@ static void fds_svc_alloc(struct ldom_hdl *lhp, struct ldmsvcs_info *lsp) { int i; - char *name[] = { "fma-phys-cpu-service", "fma-phys-mem-service", - "fma-pri-service", NULL }; + static char *name[] = { LDM_DS_NAME_CPU, LDM_DS_NAME_MEM, + LDM_DS_NAME_PRI, LDM_DS_NAME_IOD, NULL }; (void) pthread_mutex_init(&lsp->fmas_svcs.mt, NULL); (void) pthread_cond_init(&lsp->fmas_svcs.cv, NULL); @@ -972,14 +974,28 @@ fds_svc_lookup(struct ldmsvcs_info *lsp, char *name) ASSERT(svc != NULL); - ier = 0; - twait.tv_sec = time(NULL) + lsp->cv_twait; - twait.tv_nsec = 0; + if (svc->state == DS_SVC_INACTIVE) { + /* service is not registered */ + ier = ETIMEDOUT; + } else { + ier = 0; + twait.tv_sec = time(NULL) + lsp->cv_twait; + twait.tv_nsec = 0; - while (svc->state != DS_SVC_ACTIVE && ier == 0 && - lsp->fds_chan.state != CHANNEL_UNUSABLE) - ier = pthread_cond_timedwait(&lsp->fmas_svcs.cv, - &lsp->fmas_svcs.mt, &twait); + while (svc->state != DS_SVC_ACTIVE && ier == 0 && + lsp->fds_chan.state != CHANNEL_UNUSABLE) + ier = pthread_cond_timedwait(&lsp->fmas_svcs.cv, + &lsp->fmas_svcs.mt, &twait); + + /* + * By now, the ds service should have registered already. + * If it does not, ldmd probably does not support this service. + * Then mark the service state as inactive. + */ + if (ier == ETIMEDOUT) { + svc->state = DS_SVC_INACTIVE; + } + } (void) pthread_mutex_unlock(&lsp->fmas_svcs.mt); @@ -1274,7 +1290,7 @@ cpu_request(struct ldom_hdl *lhp, uint32_t msg_type, uint32_t cpuid) ds_data_handle_t *D; fma_cpu_service_req_t *R; - char *svcname = "fma-phys-cpu-service"; + char *svcname = LDM_DS_NAME_CPU; fma_cpu_resp_t *respmsg; void *resp; size_t resplen, reqmsglen; @@ -1358,7 +1374,7 @@ mem_request(struct ldom_hdl *lhp, uint32_t msg_type, uint64_t pa, ds_data_handle_t *D; fma_mem_service_req_t *R; - char *svcname = "fma-phys-mem-service"; + char *svcname = LDM_DS_NAME_MEM; fma_mem_resp_t *respmsg; void *resp; size_t resplen, reqmsglen; @@ -1488,7 +1504,7 @@ ldmsvcs_get_core_md(struct ldom_hdl *lhp, uint64_t **buf) ds_data_handle_t *D; fma_req_pri_t *R; - char *svcname = "fma-pri-service"; + char *svcname = LDM_DS_NAME_PRI; void *resp; size_t resplen, reqmsglen; ssize_t buflen; @@ -1593,4 +1609,79 @@ ldmsvcs_mem_req_unretire(struct ldom_hdl *lhp, uint64_t pa) return (mem_request(lhp, FMA_MEM_REQ_RESURRECT, pa, getpagesize())); } +int +ldmsvcs_io_req_id(struct ldom_hdl *lhp, uint64_t addr, uint_t type, + uint64_t *virt_addr, char *name, int name_len, uint64_t *did) +{ + + ds_hdr_t *H; + ds_data_handle_t *D; + fma_io_req_t *R; + + char *svcname = LDM_DS_NAME_IOD; + void *resp; + fma_io_resp_t *iop; + size_t resplen, reqmsglen; + int offset; + int rc; + + if (lhp->lsinfo == NULL) + return (-1); + + reqmsglen = sizeof (ds_hdr_t) + sizeof (ds_data_handle_t) + + sizeof (fma_io_req_t); + + H = lhp->allocp(reqmsglen); + D = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t)); + R = (void *)((ptrdiff_t)D + sizeof (ds_data_handle_t)); + + H->msg_type = DS_DATA; + H->payload_len = sizeof (ds_data_handle_t) + sizeof (fma_io_req_t); + + R->req_num = fds_svc_req_num(); + R->msg_type = type; + R->rsrc_address = addr; + + rc = ENOMSG; + if ((rc = sendrecv(lhp, R->req_num, H, reqmsglen, + &D->svc_handle, svcname, &resp, &resplen)) != 0) { + lhp->freep(H, reqmsglen); + return (rc); + } + lhp->freep(H, reqmsglen); + + /* + * resp should contain the req_num, status, virtual addr, domain id + * and the domain name. The domain name may or may not be present. + */ + offset = sizeof (fma_io_resp_t); + if (resplen < offset) { + lhp->freep(resp, resplen); + return (-1); + } + + iop = (fma_io_resp_t *)resp; + switch (iop->result) { + case FMA_IO_RESP_OK: + /* success */ + rc = 0; + *virt_addr = iop->virt_rsrc_address; + *did = iop->domain_id; + if (name == NULL || name_len <= 0) + break; + *name = '\0'; + if (resplen > offset) { + (void) strncpy(name, (char *)((ptrdiff_t)resp + offset), + name_len); + } + break; + default: + rc = -1; + break; + } + + lhp->freep(resp, resplen); + return (rc); +} + /* end file */ diff --git a/usr/src/lib/fm/libldom/sparc/ldmsvcs_utils.h b/usr/src/lib/fm/libldom/sparc/ldmsvcs_utils.h index eaa5aa1087..135109152f 100644 --- a/usr/src/lib/fm/libldom/sparc/ldmsvcs_utils.h +++ b/usr/src/lib/fm/libldom/sparc/ldmsvcs_utils.h @@ -26,8 +26,6 @@ #ifndef _LDMSVCS_UTILS_H #define _LDMSVCS_UTILS_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include <stdlib.h> #include <sys/types.h> #include <sys/ldc.h> @@ -79,6 +77,12 @@ typedef struct fds_channel { /* * FMA services */ + +#define LDM_DS_NAME_CPU "fma-phys-cpu-service" +#define LDM_DS_NAME_MEM "fma-phys-mem-service" +#define LDM_DS_NAME_PRI "fma-pri-service" +#define LDM_DS_NAME_IOD "fma-io-domain-service" + typedef struct { uint64_t req_num; } fma_req_pri_t; @@ -139,9 +143,28 @@ typedef struct { } fma_mem_resp_t; +#define FMA_IO_RESP_OK 0 +#define FMA_IO_RESP_FAILURE 1 +#define FMA_IO_RESP_ILLEGAL 2 +#define FMA_IO_RESP_UNASSIGNED 3 + +typedef struct { + uint64_t req_num; + uint32_t msg_type; + uint32_t reserved; + uint64_t rsrc_address; +} fma_io_req_t; + +typedef struct { + uint64_t req_num; + uint32_t result; + uint32_t reserved; + uint64_t virt_rsrc_address; + uint64_t domain_id; +} fma_io_resp_t; + + struct ldom_hdl { - int major_version; - int service_ldom; void *(*allocp)(size_t size); void (*freep)(void *addr, size_t size); struct ldmsvcs_info *lsinfo; @@ -187,6 +210,9 @@ extern int ldmsvcs_cpu_req_online(struct ldom_hdl *lhp, uint32_t cpuid); extern int ldmsvcs_mem_req_unretire(struct ldom_hdl *lhp, uint64_t pa); +extern int ldmsvcs_io_req_id(struct ldom_hdl *lhp, uint64_t addr, uint_t type, + uint64_t *virt_addr, char *name, int name_len, uint64_t *did); + #ifdef __cplusplus } #endif diff --git a/usr/src/lib/fm/libldom/sparc/ldom.c b/usr/src/lib/fm/libldom/sparc/ldom.c index ce25369490..b44a038b1e 100644 --- a/usr/src/lib/fm/libldom/sparc/ldom.c +++ b/usr/src/lib/fm/libldom/sparc/ldom.c @@ -27,6 +27,7 @@ #include <unistd.h> #include <stdlib.h> #include <string.h> +#include <strings.h> #include <fcntl.h> #include <pthread.h> #include <errno.h> @@ -35,6 +36,7 @@ #include <link.h> #include <assert.h> +#include <fm/libtopo.h> #include <sys/processor.h> #include <sys/stat.h> #include <sys/mdesc.h> @@ -48,10 +50,19 @@ #include <sys/pri.h> #include "ldom.h" +#include "ldom_alloc.h" #include "ldmsvcs_utils.h" +#include "ldom_xmpp_client.h" #define MD_STR_PLATFORM "platform" #define MD_STR_DOM_CAPABLE "domaining-enabled" +#define MD_STR_IODEVICE "iodevice" +#define MD_STR_NAME "name" +#define MD_STR_DEVICE_TYPE "device-type" +#define MD_STR_CFGHDL "cfg-handle" +#define MD_STR_PCIEX "pciex" +#define MD_STR_PCI "pci" +#define MD_STR_NIU "niu" static int ldom_ldmd_is_up = 0; /* assume stays up if ever seen up */ @@ -222,83 +233,6 @@ get_local_md_prop_value(ldom_hdl_t *lhp, char *node, char *prop, uint64_t *val) return (rc); } -static int -ldom_getinfo(struct ldom_hdl *lhp) -{ - static pthread_mutex_t mt = PTHREAD_MUTEX_INITIALIZER; - static pthread_cond_t cv = PTHREAD_COND_INITIALIZER; - static int major_version = -1; - static int service_ldom = -1; - static int busy_init = 0; - - int ier, rc = 0; - uint64_t domain_capable; - - (void) pthread_mutex_lock(&mt); - - while (busy_init == 1) - (void) pthread_cond_wait(&cv, &mt); - - if (major_version != -1 && service_ldom != -1) { - lhp->major_version = major_version; - lhp->service_ldom = service_ldom; - (void) pthread_mutex_unlock(&mt); - return (0); - } - - /* - * get to this point if major_version and service_ldom have not yet - * been determined - */ - busy_init = 1; - (void) pthread_mutex_unlock(&mt); - - /* - * set defaults which correspond to the case of "LDOMS not - * available". note that these can (and will) also apply to - * non-sun4v machines. - */ - major_version = 0; - service_ldom = 0; - - if (get_local_md_prop_value(lhp, MD_STR_PLATFORM, MD_STR_DOM_CAPABLE, - &domain_capable) == 0) { - - /* - * LDOMS capable FW is installed; it should be ok to - * try to communicate with ldmd and if that fails/timesout - * then use libpri - */ - major_version = 1; - - if ((ier = ldmsvcs_check_channel()) == 0) { - /* - * control ldom - * ldmfma channel between FMA and ldmd only exists - * on the control domain. - */ - service_ldom = 1; - } else if (ier == 1) { - /* - * guest ldom - * non-control ldom such as guest and io service ldom - */ - service_ldom = 0; - } - } - - (void) pthread_mutex_lock(&mt); - lhp->major_version = major_version; - lhp->service_ldom = service_ldom; - busy_init = 0; - (void) pthread_mutex_unlock(&mt); - - (void) pthread_cond_broadcast(&cv); - - return (rc); -} - - /* * search the machine description for a "pid" entry (physical cpuid) and * return the corresponding "id" entry (virtual cpuid). @@ -363,6 +297,124 @@ cpu_phys2virt(ldom_hdl_t *lhp, uint32_t cpuid) return (vid); } +static int +get_type(ldom_hdl_t *lhp, uint32_t *type) +{ + int num_nodes, cnt, i, rc; + char *p; + mde_cookie_t *listp; + md_t *mdp; + uint64_t domain_capable; + uint64_t *bufp; + ssize_t bufsize; + + *type = 0; + + /* legacy system */ + if (get_local_md_prop_value(lhp, MD_STR_PLATFORM, MD_STR_DOM_CAPABLE, + &domain_capable) != 0) { + *type = LDOM_TYPE_LEGACY; + return (0); + } + + /* + * LDOMS capable FW is installed; it should be ok to + * try to communicate with ldmd + */ + if ((rc = ldmsvcs_check_channel()) == 0) { + /* + * control ldom + * ldmfma channel between FMA and ldmd only exists + * on the control domain. + */ + *type |= LDOM_TYPE_CONTROL; + } else if (rc == -1) { + return (rc); + } + + /* + * root domain and io domain + */ + if ((bufsize = get_local_core_md(lhp, &bufp)) < 1) + return (-1); + if ((mdp = md_init_intern(bufp, lhp->allocp, lhp->freep)) == NULL) { + lhp->freep(bufp, bufsize); + return (-1); + } + if ((num_nodes = md_node_count(mdp)) < 1) { + lhp->freep(bufp, bufsize); + (void) md_fini(mdp); + return (-1); + } + + /* Search for the root complex and niu nodes */ + listp = lhp->allocp(sizeof (mde_cookie_t) * num_nodes); + cnt = md_scan_dag(mdp, MDE_INVAL_ELEM_COOKIE, + md_find_name(mdp, MD_STR_IODEVICE), md_find_name(mdp, "fwd"), + listp); + for (i = 0, p = NULL; i < cnt; i++) { + if ((md_get_prop_str(mdp, listp[i], MD_STR_DEVICE_TYPE, &p) + == 0) && + (p != NULL) && (strcmp(p, MD_STR_PCIEX) == 0)) { + *type |= LDOM_TYPE_ROOT; + break; + } + } + for (i = 0, p = NULL; i < cnt; i++) { + if ((md_get_prop_str(mdp, listp[i], MD_STR_NAME, &p) == 0) && + (p != NULL) && (strcmp(p, MD_STR_NIU) == 0)) { + *type |= LDOM_TYPE_IO; + break; + } + } + lhp->freep(listp, sizeof (mde_cookie_t) * num_nodes); + (void) md_fini(mdp); + lhp->freep(bufp, bufsize); + + return (0); +} + +int +ldom_get_type(ldom_hdl_t *lhp, uint32_t *type) +{ + static pthread_mutex_t mt = PTHREAD_MUTEX_INITIALIZER; + static pthread_cond_t cv = PTHREAD_COND_INITIALIZER; + static uint32_t ltype = 0; + static int busy_init = 0; + + int rc = 0; + + (void) pthread_mutex_lock(&mt); + + while (busy_init == 1) + (void) pthread_cond_wait(&cv, &mt); + + if (VALID_LDOM_TYPE(ltype) != 0) { + *type = ltype; + (void) pthread_mutex_unlock(&mt); + return (0); + } + + /* + * get to this point if the ldom_type has not yet been determined + */ + busy_init = 1; + (void) pthread_mutex_unlock(&mt); + + rc = get_type(lhp, <ype); + if (rc == 0) { + *type = ltype; + } + + (void) pthread_mutex_lock(&mt); + busy_init = 0; + (void) pthread_mutex_unlock(&mt); + + (void) pthread_cond_broadcast(&cv); + + return (rc); +} + int ldom_fmri_status(ldom_hdl_t *lhp, nvlist_t *nvl) { @@ -373,11 +425,8 @@ ldom_fmri_status(ldom_hdl_t *lhp, nvlist_t *nvl) return (EINVAL); /* - * ldom_ldmd_is_up can only be true if ldom_major_version() - * returned 1 earlier; the major version is constant for the - * life of the client process + * ldom_ldmd_is_up can only be true if a pri can be obtained from ldmd. */ - if (!ldom_ldmd_is_up) { /* Zeus is unavail; use local routines for status/retire */ @@ -438,11 +487,8 @@ ldom_fmri_retire(ldom_hdl_t *lhp, nvlist_t *nvl) return (EINVAL); /* - * ldom_ldmd_is_up can only be true if ldom_major_version() - * returned 1 earlier; the major version is constant for the - * life of the client process + * ldom_ldmd_is_up can only be true if a pri can be obtained from ldmd. */ - if (!ldom_ldmd_is_up) { /* Zeus is unavail; use local routines for status/retire */ @@ -502,11 +548,8 @@ ldom_fmri_unretire(ldom_hdl_t *lhp, nvlist_t *nvl) return (EINVAL); /* - * ldom_ldmd_is_up can only be true if ldom_major_version() - * returned 1 earlier; the major version is constant for the - * life of the client process + * ldom_ldmd_is_up can only be true if a pri can be obtained from ldmd. */ - if (!ldom_ldmd_is_up) { /* Zeus is unavail; use local routines for status/retire */ @@ -560,8 +603,10 @@ static int fmri_blacklist(ldom_hdl_t *lhp, nvlist_t *nvl, int cmd) { char *name; + uint32_t type = 0; - if (ldom_major_version(lhp) != 0) + if ((ldom_get_type(lhp, &type) != 0) || + ((type & LDOM_TYPE_LEGACY) == 0)) return (0); if (nvlist_lookup_string(nvl, FM_FMRI_SCHEME, &name) != 0) @@ -624,77 +669,108 @@ ldom_fmri_unblacklist(ldom_hdl_t *lhp, nvlist_t *nvl) ssize_t +ldom_get_local_md(ldom_hdl_t *lhp, uint64_t **buf) +{ + return (get_local_core_md(lhp, buf)); +} + +ssize_t ldom_get_core_md(ldom_hdl_t *lhp, uint64_t **buf) { ssize_t rv; /* return value */ uint64_t tok; /* opaque PRI token */ + uint32_t type = 0; - switch (ldom_major_version(lhp)) { - case 0: - /* pre LDOMS */ - rv = get_local_core_md(lhp, buf); - break; - case 1: - /* LDOMS 1.0 - Zeus and libpri usable only on service dom */ - if (ldom_on_service(lhp) == 1) { - if ((rv = ldmsvcs_get_core_md(lhp, buf)) < 1) { - (void) pthread_mutex_lock(&ldom_pri_lock); - rv = ldom_pri_get(PRI_GET, &tok, - buf, lhp->allocp, lhp->freep); - (void) pthread_mutex_unlock(&ldom_pri_lock); - } else { - ldom_ldmd_is_up = 1; - } + (void) ldom_get_type(lhp, &type); + if (VALID_LDOM_TYPE(type) == 0) { + return (-1); + } + if ((type & LDOM_TYPE_CONTROL) != 0) { + /* Get the pri from Zeus first. If failed, get it from libpri */ + if ((rv = ldmsvcs_get_core_md(lhp, buf)) < 1) { + (void) pthread_mutex_lock(&ldom_pri_lock); + rv = ldom_pri_get(PRI_GET, &tok, + buf, lhp->allocp, lhp->freep); + (void) pthread_mutex_unlock(&ldom_pri_lock); } else { - rv = get_local_core_md(lhp, buf); + ldom_ldmd_is_up = 1; + xmpp_start(); } - break; - default: - rv = -1; - break; + } else { + /* get the local MD */ + rv = get_local_core_md(lhp, buf); } return (rv); } -/* - * version 0 means no LDOMS - */ int -ldom_major_version(ldom_hdl_t *lhp) +ldom_find_id(ldom_hdl_t *lhp, uint64_t addr, ldom_rsrc_t rsrc, + uint64_t *virt_addr, char *name, int name_size, uint64_t *did) { - if (lhp == NULL) - return (-1); + uint32_t type = 0; - if (ldom_getinfo(lhp) == 0) - return (lhp->major_version); - else - return (0); + (void) ldom_get_type(lhp, &type); + if ((type & LDOM_TYPE_CONTROL) == 0) { + return (ENOTSUP); + } + if (!ldom_ldmd_is_up) { + return (EAGAIN); + } + return (ldmsvcs_io_req_id(lhp, addr, rsrc, virt_addr, + name, name_size, did)); } -/* - * in the absence of ldoms we are on a single OS instance which is the - * equivalent of the service ldom - */ int -ldom_on_service(ldom_hdl_t *lhp) +ldom_register_event(ldom_hdl_t *lhp, ldom_reg_cb_t cb, ldom_cb_arg_t data) { - if (lhp == NULL) - return (-1); + uint32_t type = 0; - if (ldom_getinfo(lhp) == 0) - return (lhp->service_ldom); - else - return (1); + (void) ldom_get_type(lhp, &type); + if ((type & LDOM_TYPE_CONTROL) == 0) { + return (ENOTSUP); + } + + return (xmpp_add_client(lhp, cb, data)); } +int +ldom_unregister_event(ldom_hdl_t *lhp) +{ + uint32_t type = 0; + (void) ldom_get_type(lhp, &type); + if ((type & LDOM_TYPE_CONTROL) == 0) { + return (ENOTSUP); + } + + return (xmpp_remove_client(lhp)); +} + +/* + * ldom_init() + * Description: + * Return a libldom handle to the caller for uniquely identify the session + * betweem the caller and the libldom.so. The handle is used in + * subsequent calls into the libldom.so + * + * If the caller does not provide a alloc()/free(), the libldom uses its + * own functions. + */ ldom_hdl_t * ldom_init(void *(*allocp)(size_t size), void (*freep)(void *addr, size_t size)) { struct ldom_hdl *lhp; + if (allocp == NULL && freep == NULL) { + allocp = ldom_alloc; + freep = ldom_free; + } else if (allocp == NULL || freep == NULL) { + /* missing alloc or free functions */ + return (NULL); + } + (void) pthread_mutex_lock(&ldom_pri_lock); if (ldom_pri_init() < 0) { @@ -710,7 +786,6 @@ ldom_init(void *(*allocp)(size_t size), (void) pthread_mutex_unlock(&ldom_pri_lock); - lhp->major_version = -1; /* version not yet determined */ lhp->allocp = allocp; lhp->freep = freep; @@ -726,6 +801,7 @@ ldom_fini(ldom_hdl_t *lhp) if (lhp == NULL) return; + (void) xmpp_remove_client(lhp); ldmsvcs_fini(lhp); lhp->freep(lhp, sizeof (struct ldom_hdl)); diff --git a/usr/src/lib/fm/libldom/sparc/ldom.h b/usr/src/lib/fm/libldom/sparc/ldom.h index 7b8079b819..54b57ff698 100644 --- a/usr/src/lib/fm/libldom/sparc/ldom.h +++ b/usr/src/lib/fm/libldom/sparc/ldom.h @@ -26,11 +26,10 @@ #ifndef _LDOM_H #define _LDOM_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include <stdlib.h> #include <libnvpair.h> #include <sys/types.h> +#include <umem.h> #ifdef __cplusplus extern "C" { @@ -49,9 +48,59 @@ extern int ldom_fmri_blacklist(ldom_hdl_t *lhp, nvlist_t *nvl_fmri); extern int ldom_fmri_unblacklist(ldom_hdl_t *lhp, nvlist_t *nvl_fmri); extern ssize_t ldom_get_core_md(ldom_hdl_t *lhp, uint64_t **buf); +extern ssize_t ldom_get_local_md(ldom_hdl_t *lhp, uint64_t **buf); + +/* + * domain type + */ +#define LDOM_TYPE_LEGACY 0x1 +#define LDOM_TYPE_CONTROL 0x2 +#define LDOM_TYPE_ROOT 0x4 +#define LDOM_TYPE_IO 0x8 +#define LDOM_TYPE_ALL \ + (LDOM_TYPE_LEGACY | LDOM_TYPE_CONTROL | LDOM_TYPE_ROOT | LDOM_TYPE_IO) +#define VALID_LDOM_TYPE(t) ((t) & LDOM_TYPE_ALL) + +extern int ldom_get_type(ldom_hdl_t *lhp, uint32_t *type_mask); + +/* + * Resource map + */ +typedef enum ldom_rsrc { + LDOM_RSRC_PCI, + LDOM_RSRC_NIU, + LDOM_RSRC_MAX +} ldom_rsrc_t; + +extern int +ldom_find_id(ldom_hdl_t *lhp, uint64_t addr, ldom_rsrc_t type, + uint64_t *virt_addr, char *name, int name_size, uint64_t *id); + +/* + * event notification + */ +typedef enum ldom_event { + LDOM_EVENT_UNKNOWN, + LDOM_EVENT_ADD, + LDOM_EVENT_REMOVE, + LDOM_EVENT_BIND, + LDOM_EVENT_UNBIND, + LDOM_EVENT_START, + LDOM_EVENT_STOP, + LDOM_EVENT_RESET, + LDOM_EVENT_PANIC, + LDOM_EVENT_MAX +} ldom_event_t; +#define VALID_LDOM_EVENT(e) ((e) > LDOM_EVENT_UNKNOWN && \ + (e) < LDOM_EVENT_MAX) +#define MAX_LDOM_NAME 256 -extern int ldom_major_version(ldom_hdl_t *lhp); -extern int ldom_on_service(ldom_hdl_t *lhp); +typedef void *ldom_cb_arg_t; +typedef void (*ldom_reg_cb_t)(char *ldom_name, ldom_event_t event, + ldom_cb_arg_t data); +extern int ldom_register_event(ldom_hdl_t *lhp, ldom_reg_cb_t cb, + ldom_cb_arg_t data); +extern int ldom_unregister_event(ldom_hdl_t *lhp); #ifdef __cplusplus } diff --git a/usr/src/lib/fm/libldom/sparc/ldom_alloc.c b/usr/src/lib/fm/libldom/sparc/ldom_alloc.c new file mode 100644 index 0000000000..c5c6903111 --- /dev/null +++ b/usr/src/lib/fm/libldom/sparc/ldom_alloc.c @@ -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 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <umem.h> + +void * +ldom_alloc(size_t size) +{ + return (umem_alloc(size, UMEM_DEFAULT)); +} + +void +ldom_free(void *data, size_t size) +{ + umem_free(data, size); +} diff --git a/usr/src/lib/fm/libldom/sparc/ldom_alloc.h b/usr/src/lib/fm/libldom/sparc/ldom_alloc.h new file mode 100644 index 0000000000..c73b47dc72 --- /dev/null +++ b/usr/src/lib/fm/libldom/sparc/ldom_alloc.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 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _LDOM_ALLOC_H +#define _LDOM_ALLOC_H + +#include <sys/types.h> +#include <umem.h> + +#ifdef __cplusplus +extern "C" { +#endif + +extern void *ldom_alloc(size_t size); +extern void ldom_free(void *data, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* _LDOM_ALLOC_H */ diff --git a/usr/src/lib/fm/libldom/sparc/ldom_xmpp_client.c b/usr/src/lib/fm/libldom/sparc/ldom_xmpp_client.c new file mode 100644 index 0000000000..2066ff20cf --- /dev/null +++ b/usr/src/lib/fm/libldom/sparc/ldom_xmpp_client.c @@ -0,0 +1,790 @@ +/* + * 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 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * ldom_xmpp_client.c Extensible Messaging and Presence Protocol + * + * Implement an xmpp client to subscribe for domain events from the ldmd. + * Notify fmd module clients upon receiving the events. + * + */ + +#include "ldom_xmpp_client.h" +#include "ldom_alloc.h" + +#include <stdio.h> +#include <signal.h> +#include <strings.h> +#include <unistd.h> +#include <errno.h> + +#include <netdb.h> +#include <dlfcn.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <libxml/parser.h> +#include <openssl/ssl.h> + +typedef enum conn_state { + CONN_STATE_UNKNOWN, + CONN_STATE_TLS, + CONN_STATE_FEATURE, + CONN_STATE_LDM_INTERFACE, + CONN_STATE_LDM_EVENT, + CONN_STATE_DONE, + CONN_STATE_FAILURE, + CONN_STATE_MAX +} conn_state_t; + +typedef struct xmpp_conn { + int fd; + int state; + boolean_t tls_started; + SSL *ssl; + xmlParserCtxtPtr parser; +} xmpp_conn_t; + +/* Forward declaration */ +static int iowrite(xmpp_conn_t *conn, char *buf, int size); +static void start_element(void *state, const xmlChar *name, + const xmlChar **attrs); +static void end_element(void *state, const xmlChar *name); +static void error_func(void *state, const char *msg, ...); +static void xmpp_close(xmpp_conn_t *conn); +static int start_tls(xmpp_conn_t *conn); +static void handle_ldm_resp(xmpp_conn_t *conn, char *buf, size_t buf_size); +static void handle_ldm_event(xmpp_conn_t *conn, char *buf, size_t buf_size); + +static int xmpp_enable = 0; +static pthread_t xmpp_tid = 0; +static pthread_mutex_t xmpp_tid_lock = PTHREAD_MUTEX_INITIALIZER; + +static client_list_t clt_list = { NULL, NULL, PTHREAD_MUTEX_INITIALIZER }; + + +#define FUNCTION_ADD(_function, _pointer, _lib, _func_name, _ret) \ + _function = (_pointer)dlsym(_lib, _func_name); \ + if (_function == NULL) { \ + _ret += -1; \ + } + +/* + * Prototypes and pointers to functions needed from libssl. + */ +typedef void (*SSL_load_error_strings_pt)(void); +typedef int (*SSL_library_init_pt)(void); +typedef SSL_CTX *(*SSL_CTX_new_pt)(const SSL_METHOD *method); +typedef SSL_METHOD *(*SSLv23_client_method_pt)(void); +typedef int (*SSL_write_pt)(SSL *ssl, const void *buf, int num); +typedef int (*SSL_CTX_use_PrivateKey_file_pt)(SSL_CTX *ctx, const char *file, + int type); +typedef void (*RAND_seed_pt)(const void *buf, int num); +typedef int (*SSL_get_error_pt)(const SSL *ssl, int ret); +typedef long (*ERR_get_error_pt)(void); +typedef char *(*ERR_error_string_pt)(unsigned long e, char *buf); +typedef int (*SSL_connect_pt)(SSL *ssl); +typedef int (*SSL_CTX_use_certificate_chain_file_pt)(SSL_CTX *ctx, + const char *file); +typedef int (*SSL_set_fd_pt)(SSL *ssl, int fd); +typedef void (*SSL_free_pt)(SSL *ssl); +typedef int (*SSL_read_pt)(SSL *ssl, void *buf, int num); +typedef SSL *(*SSL_new_pt)(SSL_CTX *ctx); +typedef SSL_CTX *(*SSL_get_SSL_CTX_pt)(const SSL *ssl); +typedef void (*SSL_CTX_free_pt)(SSL_CTX *ctx); + +static SSL_load_error_strings_pt SSL_load_error_strings_f = NULL; +static SSL_library_init_pt SSL_library_init_f = NULL; +static SSL_CTX_new_pt SSL_CTX_new_f = NULL; +static SSLv23_client_method_pt SSLv23_client_method_f = NULL; +static SSL_write_pt SSL_write_f = NULL; +static SSL_CTX_use_PrivateKey_file_pt SSL_CTX_use_PrivateKey_file_f = NULL; +static RAND_seed_pt RAND_seed_f = NULL; +static SSL_get_error_pt SSL_get_error_f = NULL; +static ERR_get_error_pt ERR_get_error_f = NULL; +static ERR_error_string_pt ERR_error_string_f = NULL; +static SSL_connect_pt SSL_connect_f = NULL; +static SSL_CTX_use_certificate_chain_file_pt +SSL_CTX_use_certificate_chain_file_f = NULL; +static SSL_set_fd_pt SSL_set_fd_f = NULL; +static SSL_free_pt SSL_free_f = NULL; +static SSL_read_pt SSL_read_f = NULL; +static SSL_new_pt SSL_new_f = NULL; +static SSL_get_SSL_CTX_pt SSL_get_SSL_CTX_f = NULL; +static SSL_CTX_free_pt SSL_CTX_free_f = NULL; + +static void *xmpp_dl = NULL; + +static ldom_event_info_t event_table[] = { + { LDOM_EVENT_UNKNOWN, "unknown" }, + { LDOM_EVENT_ADD, "add-domain" }, + { LDOM_EVENT_REMOVE, "remove-domain" }, + { LDOM_EVENT_BIND, "bind-domain" }, + { LDOM_EVENT_UNBIND, "unbind-domain" }, + { LDOM_EVENT_START, "start-domain" }, + { LDOM_EVENT_STOP, "stop-domain" }, + { LDOM_EVENT_RESET, "domain-reset" }, + { LDOM_EVENT_PANIC, "panic-domain" }, + { LDOM_EVENT_MAX, NULL } +}; +static int event_table_size = \ + sizeof (event_table) / sizeof (ldom_event_info_t); + +static xmlSAXHandler xml_handler = { + NULL, /* internalSubsetSAXFunc */ + NULL, /* isStandaloneSAXFunc */ + NULL, /* hasInternalSubsetSAXFunc */ + NULL, /* hasExternalSubsetSAXFunc */ + NULL, /* resolveEntitySAXFunc */ + NULL, /* getEntitySAXFunc */ + NULL, /* entityDeclSAXFunc */ + NULL, /* notationDeclSAXFunc */ + NULL, /* attributeDeclSAXFunc */ + NULL, /* elementDeclSAXFunc */ + NULL, /* unparsedEntityDeclSAXFunc */ + NULL, /* setDocumentLocatorSAXFunc */ + NULL, /* startDocumentSAXFunc */ + NULL, /* endDocumentSAXFunc */ + start_element, /* startElementSAXFunc */ + end_element, /* endElementSAXFunc */ + NULL, /* referenceSAXFunc */ + NULL, /* charactersSAXFunc */ + NULL, /* ignorableWhitespaceSAXFunc */ + NULL, /* processingInstructionSAXFunc */ + NULL, /* commentSAXFunc */ + NULL, /* warningSAXFunc */ + error_func, /* errorSAXFunc */ + NULL, /* fatalErrorSAXFunc */ + NULL, /* getParameterEntitySAXFunc */ + NULL, /* cdataBlockSAXFunc */ + NULL, /* externalSubsetSAXFunc */ + 0, /* unsigned int */ + NULL, /* void * _private */ + NULL, /* startElementNsSAX2Func */ + NULL, /* endElementNsSAX2Func */ + NULL /* xmlStructuredErrorFunc */ +}; + +static void +end_element(void *state, const xmlChar *name) +{ + xmpp_conn_t *conn = (xmpp_conn_t *)state; + + if (xmlStrcmp(name, STREAM_NODE) == 0) { + conn->state = CONN_STATE_DONE; + } else if (xmlStrcmp(name, STARTTLS_NODE) == 0) { + (void) iowrite(conn, START_TLS, strlen(START_TLS)); + } else if (xmlStrcmp(name, PROCEED_NODE) == 0) { + if (start_tls(conn)) { + conn->state = CONN_STATE_FAILURE; + } + } else if (xmlStrcmp(name, FEATURE_NODE) == 0) { + if (conn->state == CONN_STATE_TLS) { + conn->state = CONN_STATE_FEATURE; + (void) iowrite(conn, (char *)LDM_REG_DOMAIN_EVENTS, + strlen((char *)LDM_REG_DOMAIN_EVENTS)); + } + } else if (xmlStrcmp(name, XML_LDM_INTERFACE) == 0) { + conn->state = CONN_STATE_LDM_INTERFACE; + } else if (xmlStrcmp(name, XML_LDM_EVENT) == 0) { + conn->state = CONN_STATE_LDM_EVENT; + } else if (xmlStrcmp(name, XML_FAILURE) == 0) { + conn->state = CONN_STATE_FAILURE; + } +} + +/*ARGSUSED*/ +static void +start_element(void *state, const xmlChar *name, const xmlChar **attrs) +{ +} + +/*ARGSUSED*/ +static void +error_func(void *state, const char *msg, ...) +{ +} + +static int +xmpp_connect(xmpp_conn_t *conn) +{ + int sock; + struct sockaddr_in serveraddr; + + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { + return (-1); + } + + serveraddr.sin_family = AF_INET; + serveraddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + serveraddr.sin_port = htons(XMPP_DEFAULT_PORT); + if (connect(sock, (struct sockaddr *)(&serveraddr), + sizeof (struct sockaddr_in)) < 0) { + return (-1); + } + + (void) bzero(conn, sizeof (xmpp_conn_t)); + conn->fd = sock; + conn->tls_started = B_FALSE; + + conn->parser = xmlCreatePushParserCtxt(&xml_handler, (void *) conn, + NULL, NULL, NULL); + if (conn->parser == NULL) { + return (-1); + } + + return (0); +} + +static void +xmpp_close(xmpp_conn_t *conn) +{ + (void) close(conn->fd); + conn->fd = -1; + conn->state = CONN_STATE_UNKNOWN; + if (conn->parser != NULL) { + xmlFreeParserCtxt(conn->parser); + conn->parser = NULL; + } + if (conn->tls_started) { + SSL_free_f(conn->ssl); + conn->ssl = NULL; + } + conn->tls_started = B_FALSE; +} + +static int +ioread(xmpp_conn_t *conn, char *buf, int size) +{ + int count; + if (conn->tls_started) { + count = SSL_read_f(conn->ssl, buf, size); + } else { + count = read(conn->fd, buf, size); + } + if (count <= 0) { + conn->state = CONN_STATE_FAILURE; + } + + return (count); +} + +static int +iowrite(xmpp_conn_t *conn, char *buf, int size) +{ + int count; + + if (conn->tls_started) { + count = SSL_write_f(conn->ssl, buf, size); + } else { + count = send(conn->fd, buf, size, 0); + } + if (count <= 0) { + conn->state = CONN_STATE_FAILURE; + } + + return (count); +} + +/* + * notify_event() + * Description: + * Notify all clients an event by going through the client list and invoke + * the callback functions. + */ +static void +notify_event(ldom_event_t event, char *ldom_name) +{ + client_info_t *p; + + (void) pthread_mutex_lock(&clt_list.lock); + + for (p = clt_list.head; p != NULL; p = p->next) { + p->cb(ldom_name, event, p->data); + } + + (void) pthread_mutex_unlock(&clt_list.lock); +} + +/* + * xmpp_client_thr() + * Description: + * The main entry fo the xmpp client thread. + */ +/*ARGSUSED*/ +static void * +xmpp_client_thr(void *data) +{ + int rc = 0; + int cnt; + char buf[XMPP_BUF_SIZE]; + xmpp_conn_t conn; + + /* initialize the conn struct */ + bzero(&conn, sizeof (xmpp_conn_t)); + + while (xmpp_enable) { + + /* keep making a connection until successfully */ + do { + if (rc = xmpp_connect(&conn)) + (void) sleep(XMPP_SLEEP); + } while (rc != 0 && xmpp_enable); + + /* write the stream node */ + cnt = iowrite(&conn, (char *)STREAM_START, + strlen((char *)STREAM_START)); + + /* process input */ + while ((conn.state != CONN_STATE_FAILURE) && + (conn.state != CONN_STATE_DONE) && xmpp_enable) { + + /* + * Assume the document size of a ldmd response is + * less than 1KB. This assumption is valid with the + * current ldmd implementation. + * Should the document size exceeds 1KB, the buffer + * size should be revisited accordingly. + */ + (void) memset(buf, 0, XMPP_BUF_SIZE); + cnt = ioread(&conn, buf, XMPP_BUF_SIZE); + if (cnt <= 0) + break; + if (rc = xmlParseChunk(conn.parser, buf, cnt, 0)) { + conn.state = CONN_STATE_FAILURE; + } + + switch (conn.state) { + case CONN_STATE_LDM_INTERFACE: + handle_ldm_resp(&conn, buf, cnt); + break; + case CONN_STATE_LDM_EVENT: + handle_ldm_event(&conn, buf, cnt); + break; + default: + break; + } + + /* + * For now, the parser is reset after every read. + * It should only be reset once after the ssl is opened + * in the start_tls(). + */ + (void) xmlCtxtResetPush(conn.parser, NULL, NULL, NULL, + NULL); + } + xmpp_close(&conn); + (void) sleep(XMPP_SLEEP); + } + return (NULL); +} + +/* + * find_client() + * Description: + * Walk to the list to find a libldom client + */ +static client_info_t * +find_client(ldom_hdl_t *lhp) +{ + client_info_t *p; + + for (p = clt_list.head; p != NULL; p = p->next) { + if (p->lhp == lhp) + return (p); + } + + return (NULL); +} + +/* + * xmpp_add_client() + * Description: + * Add a libldom client from the client list. + */ +int +xmpp_add_client(ldom_hdl_t *lhp, ldom_reg_cb_t cb, ldom_cb_arg_t data) +{ + client_info_t *clt; + + (void) pthread_mutex_lock(&clt_list.lock); + if (find_client(lhp)) { + /* already exists */ + (void) pthread_mutex_unlock(&clt_list.lock); + return (-1); + } + + /* new client */ + clt = (client_info_t *)ldom_alloc(sizeof (client_info_t)); + clt->lhp = lhp; + clt->cb = cb; + clt->data = data; + clt->next = NULL; + clt->prev = NULL; + + if (clt_list.head == NULL && clt_list.tail == NULL) { + clt_list.head = clt; + clt_list.tail = clt; + } else { + /* append to the list */ + clt->prev = clt_list.tail; + clt_list.tail->next = clt; + clt_list.tail = clt; + } + + (void) pthread_mutex_unlock(&clt_list.lock); + return (0); +} + +/* + * xmpp_remove_client() + * Description: + * Remove a libldom client from the client list. + */ +int +xmpp_remove_client(ldom_hdl_t *lhp) +{ + client_info_t *p; + + (void) pthread_mutex_lock(&clt_list.lock); + if ((p = find_client(lhp)) == NULL) { + /* not present */ + (void) pthread_mutex_unlock(&clt_list.lock); + return (-1); + } + + if (clt_list.head == p && clt_list.tail == p) { + /* single item list */ + clt_list.head = NULL; + clt_list.tail = NULL; + } else if (clt_list.head == p) { + /* delete the head */ + clt_list.head = p->next; + clt_list.head->prev = NULL; + } else if (clt_list.tail == p) { + /* delete the tail */ + clt_list.tail = p->prev; + clt_list.tail->next = NULL; + } else { + /* delete a middle node */ + p->next->prev = p->prev; + p->prev->next = p->next; + } + ldom_free(p, sizeof (client_info_t)); + + (void) pthread_mutex_unlock(&clt_list.lock); + return (0); +} + +/* + * xmpp_stop() + * Description: + * Stop the xmpp client thread + */ +/*ARGSUSED*/ +void +xmpp_stop(void) +{ + (void) pthread_mutex_lock(&xmpp_tid_lock); + xmpp_enable = 0; + if (xmpp_tid) { + (void) pthread_kill(xmpp_tid, SIGTERM); + (void) pthread_join(xmpp_tid, NULL); + xmpp_tid = 0; + } + (void) pthread_mutex_unlock(&xmpp_tid_lock); +} + +/* + * xmpp_start() + * Description: + * Start the xmpp client thread if have not done so. + */ +void +xmpp_start(void) +{ + xmpp_conn_t conn; + + /* Check if the xmmp thread has already started */ + (void) pthread_mutex_lock(&xmpp_tid_lock); + if (xmpp_tid != 0) { + (void) pthread_mutex_unlock(&xmpp_tid_lock); + return; + } + + /* Check if the ldmd supports xmpp by opening a connection */ + if (xmpp_connect(&conn)) { + (void) pthread_mutex_unlock(&xmpp_tid_lock); + return; + } + xmpp_close(&conn); + xmpp_enable = 1; + + /* + * create xmpp client thread for receiving domain events + */ + (void) pthread_create(&xmpp_tid, NULL, xmpp_client_thr, NULL); + + (void) pthread_mutex_unlock(&xmpp_tid_lock); + + /* + * Register a function to stop the above thread upon a termination + */ + (void) atexit(xmpp_stop); +} + +/* + * This routine will run through the first time we get a remote XMPP + * connection. After that we will not need to do this again. It cannot be run + * from main thread at start as we need to alert remote users if the TLS + * handshake failed. + */ +static int +load_SSL_lib() +{ + int ret = 0; + + /* If we have already opened the library no need to do it again. */ + if (xmpp_dl != NULL) + return (0); + + if ((xmpp_dl = dlopen("/usr/sfw/lib/libssl.so", RTLD_NOW)) == NULL) + return (-1); + + FUNCTION_ADD(SSL_load_error_strings_f, SSL_load_error_strings_pt, + xmpp_dl, "SSL_load_error_strings", ret); + FUNCTION_ADD(SSL_library_init_f, SSL_library_init_pt, xmpp_dl, + "SSL_library_init", ret); + FUNCTION_ADD(SSL_CTX_new_f, SSL_CTX_new_pt, xmpp_dl, + "SSL_CTX_new", ret); + FUNCTION_ADD(SSLv23_client_method_f, SSLv23_client_method_pt, xmpp_dl, + "SSLv23_client_method", ret); + FUNCTION_ADD(SSL_write_f, SSL_write_pt, xmpp_dl, "SSL_write", ret); + FUNCTION_ADD(SSL_CTX_use_PrivateKey_file_f, + SSL_CTX_use_PrivateKey_file_pt, xmpp_dl, + "SSL_CTX_use_PrivateKey_file", ret); + FUNCTION_ADD(RAND_seed_f, RAND_seed_pt, xmpp_dl, "RAND_seed", ret); + FUNCTION_ADD(SSL_get_error_f, SSL_get_error_pt, xmpp_dl, + "SSL_get_error", ret); + FUNCTION_ADD(ERR_get_error_f, ERR_get_error_pt, xmpp_dl, + "ERR_get_error", ret); + FUNCTION_ADD(ERR_error_string_f, ERR_error_string_pt, xmpp_dl, + "ERR_error_string", ret); + FUNCTION_ADD(SSL_connect_f, SSL_connect_pt, xmpp_dl, "SSL_connect", + ret); + FUNCTION_ADD(SSL_CTX_use_certificate_chain_file_f, + SSL_CTX_use_certificate_chain_file_pt, xmpp_dl, + "SSL_CTX_use_certificate_chain_file", ret); + FUNCTION_ADD(SSL_set_fd_f, SSL_set_fd_pt, xmpp_dl, "SSL_set_fd", ret); + FUNCTION_ADD(SSL_free_f, SSL_free_pt, xmpp_dl, "SSL_free", ret); + FUNCTION_ADD(SSL_read_f, SSL_read_pt, xmpp_dl, "SSL_read", ret); + FUNCTION_ADD(SSL_new_f, SSL_new_pt, xmpp_dl, "SSL_new", ret); + FUNCTION_ADD(SSL_get_SSL_CTX_f, SSL_get_SSL_CTX_pt, xmpp_dl, + "SSL_get_SSL_CTX", ret); + FUNCTION_ADD(SSL_CTX_free_f, SSL_CTX_free_pt, xmpp_dl, + "SSL_CTX_free", ret); + + if (ret < 0) + return (-1); + else + return (0); +} + +/* + * start_tls() + * Description: + * Load the libssl.so if has not done so and open a ssl connection. + * It is assumed that there is one xmpp thread to use the ssl connection. + * If multi-thread xmpp clients use the ssl connection, addtional work is + * needed to ensure the usage of the ssl be thread-safe. + */ +static int +start_tls(xmpp_conn_t *conn) +{ + int rv, urand_fd; + SSL_CTX *ssl_ctx; + char rand_buf[RAND_BUF_SIZE]; + + rv = load_SSL_lib(); + if (rv == -1) { + return (rv); + } + + urand_fd = open("/dev/random", O_RDONLY); + if (urand_fd == -1) { + return (-1); + } + (void) read(urand_fd, rand_buf, RAND_BUF_SIZE); + + SSL_library_init_f(); + RAND_seed_f(rand_buf, RAND_BUF_SIZE); + + ssl_ctx = SSL_CTX_new_f(SSLv23_client_method_f()); + if (ssl_ctx == NULL) { + return (-1); + } + conn->ssl = SSL_new_f(ssl_ctx); + rv = SSL_set_fd_f(conn->ssl, conn->fd); + if (rv == 0) { + return (-1); + } + rv = SSL_connect_f(conn->ssl); + if (rv != 1) { + return (-1); + } + conn->tls_started = B_TRUE; + conn->state = CONN_STATE_TLS; + + (void) iowrite(conn, STREAM_START, strlen(STREAM_START)); + + return (0); +} + +/* + * Find and return the first-level subnode (if any) of 'node' which has name + * 'name'. + */ +xmlNodePtr +xml_find_subnode(xmlNodePtr node, const xmlChar *name) +{ + xmlNodePtr subnode; + + if (node == NULL) + return (NULL); + + subnode = node->xmlChildrenNode; + while (subnode != NULL) { + if (((char *)subnode->name != NULL) && + (xmlStrcmp(subnode->name, name) == 0)) + break; + subnode = subnode->next; + } + + return (subnode); +} + +/* + * handle_ldm_resp() + * Description: + * Parse the ldmd response of the domain event registration for the failure + * status. If found, set the connection to failure so that it will be + * closed and a new xmpp connection is established. + */ +void +handle_ldm_resp(xmpp_conn_t *conn, char *buf, size_t buf_size) +{ + xmlDocPtr xml_output; + xmlNodePtr root, resp, status, cmd, action; + char *status_str, *action_str; + + if ((xml_output = xmlParseMemory((const char *)buf, buf_size)) == NULL) + return; + if ((root = xmlDocGetRootElement(xml_output)) == NULL) + return; + + /* get the cmd node */ + if ((cmd = xml_find_subnode(root, XML_CMD)) == NULL) + return; + if (strcmp((char *)cmd->name, (char *)XML_CMD) != 0) + return; + + /* get the action node and make sure it is the reg-domain-events */ + if ((action = xml_find_subnode(cmd, XML_ACTION)) == NULL) { + return; + } + if ((action_str = (char *)xmlNodeGetContent(action)) == NULL) + return; + if (strcmp(action_str, XML_REGISTER_ACTION) != 0) { + xmlFree(action_str); + return; + } + xmlFree(action_str); + + /* check the status of the response */ + if ((resp = xml_find_subnode(cmd, XML_RESPONSE)) == NULL) + return; + if ((status = xml_find_subnode(resp, XML_STATUS)) == NULL) + return; + if ((status_str = (char *)xmlNodeGetContent(status)) == NULL) + return; + if (strcmp(status_str, (char *)XML_FAILURE) == 0) { + conn->state = CONN_STATE_FAILURE; + } + xmlFree(status_str); +} + +/* + * handle_ldm_event() + * Description: + * Parse the LDM_event for the ldom name and domain action. Then invokes + * the clients's callback to notify them the event. + */ +/*ARGSUSED*/ +void +handle_ldm_event(xmpp_conn_t *conn, char *buf, size_t buf_size) +{ + int i; + xmlDocPtr xml_output; + xmlNodePtr root, cmd, action, data, envelope, content; + char *action_str, *ldom_name; + ldom_event_t event = LDOM_EVENT_UNKNOWN; + + if ((xml_output = xmlParseMemory((const char *)buf, buf_size)) == NULL) + return; + if ((root = xmlDocGetRootElement(xml_output)) == NULL) + return; + + /* get the action such as bind-domain, unbind-domain, etc. */ + if ((cmd = xml_find_subnode(root, XML_CMD)) == NULL) + return; + if ((action = xml_find_subnode(cmd, XML_ACTION)) == NULL) { + return; + } + if ((action_str = (char *)xmlNodeGetContent(action)) == NULL) + return; + for (i = 0; i < event_table_size; i++) { + if (event_table[i].name != NULL && + strcasecmp(event_table[i].name, action_str) == 0) { + event = event_table[i].id; + break; + } + } + xmlFree(action_str); + + /* get the ldom name */ + data = xml_find_subnode(cmd, XML_DATA); + envelope = xml_find_subnode(data, XML_ENVELOPE); + content = xml_find_subnode(envelope, XML_CONTENT); + if ((ldom_name = (char *)xmlGetProp(content, XML_ATTR_ID)) == NULL) + return; + + /* Notifies all the clients the event */ + if (VALID_LDOM_EVENT(event)) { + notify_event(event, ldom_name); + } + + xmlFree(ldom_name); +} diff --git a/usr/src/lib/fm/libldom/sparc/ldom_xmpp_client.h b/usr/src/lib/fm/libldom/sparc/ldom_xmpp_client.h new file mode 100644 index 0000000000..aa19f41cd4 --- /dev/null +++ b/usr/src/lib/fm/libldom/sparc/ldom_xmpp_client.h @@ -0,0 +1,114 @@ +/* + * 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 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * ldom_xmpp_client.h Extensible Messaging and Presence Protocol + */ + +#ifndef _LDOM_XMPP_CLIENT_H +#define _LDOM_XMPP_CLIENT_H + +#include <sys/fm/ldom.h> + +#include <pthread.h> +#include <libxml/xmlstring.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define XMPP_DEFAULT_PORT 6482 +#define XMPP_BUF_SIZE 1024 +#define RAND_BUF_SIZE 1024 +#define XMPP_SLEEP 3 + +#define STREAM_NODE (xmlChar *)"stream:stream" +#define FEATURE_NODE (xmlChar *)"stream:features" +#define STARTTLS_NODE (xmlChar *)"starttls" +#define PROCEED_NODE (xmlChar *)"proceed" +#define XML_LDM_INTERFACE ((xmlChar *)"LDM_interface") +#define XML_LDM_EVENT ((xmlChar *)"LDM_event") + +#define XML_SUCCESS ((xmlChar *)"success") +#define XML_FAILURE ((xmlChar *)"failure") + +#define XML_CMD ((xmlChar *)"cmd") +#define XML_ACTION ((xmlChar *)"action") +#define XML_RESPONSE ((xmlChar *)"response") +#define XML_STATUS ((xmlChar *)"status") +#define XML_DATA ((xmlChar *)"data") +#define XML_ENVELOPE ((xmlChar *)"Envelope") +#define XML_CONTENT ((xmlChar *)"Content") + +#define XML_ATTR_ID ((xmlChar *)"id") + +#define XML_REGISTER_ACTION "reg-domain-events" + +#define STREAM_START "<?xml version='1.0'?><stream:stream " \ + "xml:lang=\"en\" version=\"1.0\" id=\"xmpp\"" \ + ">" +#define START_TLS "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>" + +#define LDM_REG_DOMAIN_EVENTS \ + "<LDM_interface version=\"1.1\">" \ + " <cmd>" \ + " <action>reg-domain-events</action>" \ + " <data version=\"3.0\"> </data>" \ + " </cmd>" \ + "</LDM_interface>" + + +typedef struct ldom_event_info { + ldom_event_t id; + char *name; +} ldom_event_info_t; + + +typedef struct client_info { + ldom_hdl_t *lhp; + ldom_reg_cb_t cb; + ldom_cb_arg_t data; + struct client_info *next; + struct client_info *prev; +} client_info_t; + +typedef struct client_list { + client_info_t *head; + client_info_t *tail; + pthread_mutex_t lock; +} client_list_t; + + +extern int xmpp_add_client(ldom_hdl_t *lhp, ldom_reg_cb_t cb, + ldom_cb_arg_t data); +extern int xmpp_remove_client(ldom_hdl_t *lhp); +extern void xmpp_start(void); +extern void xmpp_stop(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _LDOM_XMPP_CLIENT_H */ diff --git a/usr/src/lib/fm/libldom/sparc/mapfile-vers b/usr/src/lib/fm/libldom/sparc/mapfile-vers index fee411ff45..359dd5f6a1 100644 --- a/usr/src/lib/fm/libldom/sparc/mapfile-vers +++ b/usr/src/lib/fm/libldom/sparc/mapfile-vers @@ -22,21 +22,22 @@ # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" -# SUNWprivate { global: ldom_fini; + ldom_find_id; ldom_fmri_blacklist; ldom_fmri_unblacklist; ldom_fmri_retire; ldom_fmri_unretire; ldom_fmri_status; ldom_get_core_md; + ldom_get_local_md; + ldom_get_type; ldom_init; - ldom_major_version; - ldom_on_service; + ldom_register_event; + ldom_unregister_event; local: *; }; diff --git a/usr/src/lib/fm/libldom/sparcv9/Makefile b/usr/src/lib/fm/libldom/sparcv9/Makefile index 4502ccb407..1c53dcd407 100644 --- a/usr/src/lib/fm/libldom/sparcv9/Makefile +++ b/usr/src/lib/fm/libldom/sparcv9/Makefile @@ -19,11 +19,9 @@ # CDDL HEADER END # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" -# MACH_LDLIBS = -L$(ROOT)/usr/lib/fm/$(MACH64) @@ -33,5 +31,3 @@ include ../../../Makefile.lib.64 DYNFLAGS += -R/usr/lib/fm/$(MACH64) install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) - -install_h: diff --git a/usr/src/lib/fm/topo/modules/sun4v/cpuboard/cpuboard.c b/usr/src/lib/fm/topo/modules/sun4v/cpuboard/cpuboard.c index aaee54ede5..0fc5ade004 100644 --- a/usr/src/lib/fm/topo/modules/sun4v/cpuboard/cpuboard.c +++ b/usr/src/lib/fm/topo/modules/sun4v/cpuboard/cpuboard.c @@ -24,8 +24,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <stdlib.h> #include <string.h> #include <strings.h> @@ -101,6 +99,7 @@ cpuboard_get_pri_info(topo_mod_t *mod, cpuboard_contents_t cpubs[]) char *pstr = NULL; char *sn = NULL, *pn = NULL; char *dn = NULL; + uint32_t type = 0; ldom_hdl_t *lhp; uint64_t id; int cpuboards_found = 0; @@ -118,13 +117,19 @@ cpuboard_get_pri_info(topo_mod_t *mod, cpuboard_contents_t cpubs[]) return (0); } - if ((bufsize = ldom_get_core_md(lhp, &bufp)) < 1) { - topo_mod_dprintf(mod, "ldom_get_core_md error, bufsize=%d\n", + (void) ldom_get_type(lhp, &type); + if ((type & LDOM_TYPE_CONTROL) != 0) { + bufsize = ldom_get_core_md(lhp, &bufp); + } else { + bufsize = ldom_get_local_md(lhp, &bufp); + } + if (bufsize < 1) { + topo_mod_dprintf(mod, "Failed to get pri/md, bufsize=%d\n", bufsize); ldom_fini(lhp); return (0); } - topo_mod_dprintf(mod, "pri bufsize=%d\n", bufsize); + topo_mod_dprintf(mod, "pri/md bufsize=%d\n", bufsize); if ((mdp = md_init_intern(bufp, cpuboard_topo_alloc, cpuboard_topo_free)) == NULL || @@ -516,7 +521,7 @@ cpuboard_enum(topo_mod_t *mod, tnode_t *parent, const char *name, topo_strerror(topo_mod_errno(mod))); return (-1); } - if (cpuboard_hb_enum(mod, cpuboard_findrc(mod, i), + if (cpuboard_hb_enum(mod, cpuboard_findrc(mod, i), cpub_rcs[i], cpuboardn, i) < 0) { topo_node_unbind(cpuboardn); topo_mod_dprintf(mod, "cpuboard_hb_enum: " diff --git a/usr/src/lib/fm/topo/modules/sun4v/cpuboard/cpuboard_hostbridge.c b/usr/src/lib/fm/topo/modules/sun4v/cpuboard/cpuboard_hostbridge.c index 474aa94fda..a07eff31b1 100644 --- a/usr/src/lib/fm/topo/modules/sun4v/cpuboard/cpuboard_hostbridge.c +++ b/usr/src/lib/fm/topo/modules/sun4v/cpuboard/cpuboard_hostbridge.c @@ -24,8 +24,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <string.h> #include <strings.h> #include <libdevinfo.h> @@ -79,11 +77,19 @@ cpuboard_node_create(topo_mod_t *mp, tnode_t *parent, const char *name, } /* - * Create a root complex node. + * cpuboard_rc_node_create() + * Description: + * Create a root complex node pciexrc + * Parameters: + * mp: topo module pointer + * parent: topo parent node of the newly created pciexrc node + * dnode: Solaris device node of the root complex + * rcpath: Used to populated the dev property of the topo pciexrc node if + * the local host does not own the root complex. */ static tnode_t * cpuboard_rc_node_create(topo_mod_t *mp, tnode_t *parent, di_node_t dnode, - int inst) + char *rcpath, int inst) { int err; tnode_t *rcn; @@ -107,6 +113,12 @@ cpuboard_rc_node_create(topo_mod_t *mp, tnode_t *parent, di_node_t dnode, if ((dnpath = di_devfs_path(dnode)) != NULL) { nvlist_t *fmri; + /* + * The local host owns the root complex, so use the dev path + * from the di_devfs_path(), instead of the passed in rcpath, + * to populate the dev property. + */ + rcpath = dnpath; fmri = topo_mod_devfmri(mp, FM_DEV_SCHEME_VERSION, dnpath, NULL); if (fmri == NULL) { @@ -147,12 +159,14 @@ cpuboard_rc_node_create(topo_mod_t *mp, tnode_t *parent, di_node_t dnode, return (NULL); } /* Add the devfs path property */ - if (dnpath) { + if (rcpath) { if (topo_prop_set_string(rcn, TOPO_PGROUP_IO, TOPO_IO_DEV, - TOPO_PROP_IMMUTABLE, dnpath, &err) != 0) { + TOPO_PROP_IMMUTABLE, rcpath, &err) != 0) { topo_mod_dprintf(mp, "Failed to set DEV property\n"); topo_mod_seterrno(mp, err); } + } + if (dnpath) { di_devfs_path_free(dnpath); } /* T5440 device type is always "pciex" */ @@ -225,7 +239,8 @@ cpuboard_hb_node_create(topo_mod_t *mp, tnode_t *parent, int inst) * match the cpuboard instance. */ int -cpuboard_hb_enum(topo_mod_t *mp, di_node_t dnode, tnode_t *cpubn, int brd) +cpuboard_hb_enum(topo_mod_t *mp, di_node_t dnode, char *rcpath, + tnode_t *cpubn, int brd) { int hb; int rc; @@ -260,7 +275,7 @@ cpuboard_hb_enum(topo_mod_t *mp, di_node_t dnode, tnode_t *cpubn, int brd) return (-1); } /* Create the root complex node */ - rcnode = cpuboard_rc_node_create(mp, hbnode, dnode, rc); + rcnode = cpuboard_rc_node_create(mp, hbnode, dnode, rcpath, rc); if (rcnode == NULL) { topo_mod_dprintf(mp, "unable to create rcnode: %s\n", diff --git a/usr/src/lib/fm/topo/modules/sun4v/cpuboard/cpuboard_topo.h b/usr/src/lib/fm/topo/modules/sun4v/cpuboard/cpuboard_topo.h index 9eb5f19725..7d12291f03 100644 --- a/usr/src/lib/fm/topo/modules/sun4v/cpuboard/cpuboard_topo.h +++ b/usr/src/lib/fm/topo/modules/sun4v/cpuboard/cpuboard_topo.h @@ -20,15 +20,13 @@ */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _CPUBOARD_TOPO_H #define _CPUBOARD_TOPO_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include <fm/topo_hc.h> #include <fm/topo_mod.h> @@ -55,7 +53,8 @@ typedef struct { } cpuboard_contents_t; /* Shared device tree root node */ -int cpuboard_hb_enum(topo_mod_t *mp, di_node_t dnode, tnode_t *cpubn, int brd); +int cpuboard_hb_enum(topo_mod_t *mp, di_node_t dnode, char *rcpath, + tnode_t *cpubn, int brd); /* Until future PRI changes, make connection between cpuboard id and RC */ #define CPUBOARD0_RC "/pci@400" diff --git a/usr/src/lib/fm/topo/modules/sun4v/hostbridge/hb_mdesc.c b/usr/src/lib/fm/topo/modules/sun4v/hostbridge/hb_mdesc.c index 884e29285a..6c81e62107 100644 --- a/usr/src/lib/fm/topo/modules/sun4v/hostbridge/hb_mdesc.c +++ b/usr/src/lib/fm/topo/modules/sun4v/hostbridge/hb_mdesc.c @@ -24,8 +24,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <string.h> #include <strings.h> #include <umem.h> @@ -172,6 +170,7 @@ hb_mdesc_init(topo_mod_t *mod, md_info_t *phbmd) md_t *mdp; ssize_t bufsiz = 0; uint64_t *bufp; + uint32_t type = 0; ldom_hdl_t *lhp; /* get the PRI/MD */ @@ -179,7 +178,14 @@ hb_mdesc_init(topo_mod_t *mod, md_info_t *phbmd) topo_mod_dprintf(mod, "ldom_init() failed\n"); return (topo_mod_seterrno(mod, EMOD_NOMEM)); } - if ((bufsiz = ldom_get_core_md(lhp, &bufp)) <= 0) { + + (void) ldom_get_type(lhp, &type); + if ((type & LDOM_TYPE_CONTROL) != 0) { + bufsiz = ldom_get_core_md(lhp, &bufp); + } else { + bufsiz = ldom_get_local_md(lhp, &bufp); + } + if (bufsiz <= 0) { topo_mod_dprintf(mod, "failed to get the PRI/MD\n"); ldom_fini(lhp); return (-1); diff --git a/usr/src/lib/fm/topo/modules/sun4v/motherboard/motherboard.c b/usr/src/lib/fm/topo/modules/sun4v/motherboard/motherboard.c index c42c1f852a..d2c0cd5a35 100644 --- a/usr/src/lib/fm/topo/modules/sun4v/motherboard/motherboard.c +++ b/usr/src/lib/fm/topo/modules/sun4v/motherboard/motherboard.c @@ -20,12 +20,10 @@ */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <stdlib.h> #include <string.h> #include <strings.h> @@ -129,6 +127,7 @@ mb_get_pri_info(topo_mod_t *mod, char **serialp, char **partp, char **csnp) int nfrus, num_nodes, i; char *pstr = NULL; char *sn, *pn, *dn, *csn; + uint32_t type = 0; ldom_hdl_t *lhp; lhp = ldom_init(mb_topo_alloc, mb_topo_free); @@ -145,13 +144,19 @@ mb_get_pri_info(topo_mod_t *mod, char **serialp, char **partp, char **csnp) return (-1); } - if ((bufsize = ldom_get_core_md(lhp, &bufp)) < 1) { - topo_mod_dprintf(mod, "ldom_get_core_md error, bufsize=%d\n", + (void) ldom_get_type(lhp, &type); + if ((type & LDOM_TYPE_CONTROL) != 0) { + bufsize = ldom_get_core_md(lhp, &bufp); + } else { + bufsize = ldom_get_local_md(lhp, &bufp); + } + if (bufsize < 1) { + topo_mod_dprintf(mod, "Failed to get the pri/md (bufsize=%d)\n", bufsize); ldom_fini(lhp); return (-1); } - topo_mod_dprintf(mod, "pri bufsize=%d\n", bufsize); + topo_mod_dprintf(mod, "pri/md bufsize=%d\n", bufsize); if ((mdp = md_init_intern(bufp, mb_topo_alloc, mb_topo_free)) == NULL || (num_nodes = md_node_count(mdp)) < 1) { diff --git a/usr/src/lib/fm/topo/modules/sun4v/platform-cpu/cpu.c b/usr/src/lib/fm/topo/modules/sun4v/platform-cpu/cpu.c index 5f028e1841..6555a4a2bf 100644 --- a/usr/src/lib/fm/topo/modules/sun4v/platform-cpu/cpu.c +++ b/usr/src/lib/fm/topo/modules/sun4v/platform-cpu/cpu.c @@ -24,8 +24,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <strings.h> #include <umem.h> #include <fm/topo_mod.h> @@ -316,6 +314,7 @@ cpu_unusable(topo_mod_t *mod, tnode_t *node, topo_version_t vers, uint32_t present = 0; md_cpumap_t *mcmp; md_info_t *chip = (md_info_t *)topo_mod_getspecific(mod); + uint32_t type = 0; if (nvlist_lookup_uint8(in, FM_VERSION, &version) != 0 || version > FM_CPU_SCHEME_VERSION || @@ -341,9 +340,10 @@ cpu_unusable(topo_mod_t *mod, tnode_t *node, topo_version_t vers, if (lhp == NULL) { return (topo_mod_seterrno(mod, EMOD_NOMEM)); } + (void) ldom_get_type(lhp, &type); status = ldom_fmri_status(lhp, in); rc = (status == P_FAULTED || - (status == P_OFFLINE && ldom_major_version(lhp) == 1)); + (status == P_OFFLINE && ((type & LDOM_TYPE_CONTROL) != 0))); ldom_fini(lhp); /* return the unusable status */ diff --git a/usr/src/lib/fm/topo/modules/sun4v/platform-cpu/cpu_mdesc.c b/usr/src/lib/fm/topo/modules/sun4v/platform-cpu/cpu_mdesc.c index b754e278df..6c8a6635e6 100644 --- a/usr/src/lib/fm/topo/modules/sun4v/platform-cpu/cpu_mdesc.c +++ b/usr/src/lib/fm/topo/modules/sun4v/platform-cpu/cpu_mdesc.c @@ -20,12 +20,10 @@ */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <string.h> #include <umem.h> #include <sys/mdesc.h> @@ -337,13 +335,21 @@ cpu_mdesc_init(topo_mod_t *mod, md_info_t *chip) ssize_t bufsiz = 0; uint64_t *bufp; ldom_hdl_t *lhp; + uint32_t type = 0; /* get the PRI/MD */ if ((lhp = ldom_init(cpu_alloc, cpu_free)) == NULL) { topo_mod_dprintf(mod, "ldom_init() failed\n"); return (topo_mod_seterrno(mod, EMOD_NOMEM)); } - if ((bufsiz = ldom_get_core_md(lhp, &bufp)) <= 0) { + + (void) ldom_get_type(lhp, &type); + if ((type & LDOM_TYPE_CONTROL) != 0) { + bufsiz = ldom_get_core_md(lhp, &bufp); + } else { + bufsiz = ldom_get_local_md(lhp, &bufp); + } + if (bufsiz <= 0) { topo_mod_dprintf(mod, "failed to get the PRI/MD\n"); ldom_fini(lhp); return (-1); diff --git a/usr/src/lib/fm/topo/modules/sun4v/platform-mem/mem_mdesc.c b/usr/src/lib/fm/topo/modules/sun4v/platform-mem/mem_mdesc.c index a3a617e10e..4639fa44d9 100644 --- a/usr/src/lib/fm/topo/modules/sun4v/platform-mem/mem_mdesc.c +++ b/usr/src/lib/fm/topo/modules/sun4v/platform-mem/mem_mdesc.c @@ -24,8 +24,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <string.h> #include <umem.h> #include <sys/mdesc.h> @@ -420,12 +418,19 @@ mem_mdesc_init(topo_mod_t *mod, md_mem_info_t *mem) mde_cookie_t *listp; int num_nodes; int num_comps = 0; + uint32_t type = 0; /* get the PRI/MD */ if ((lhp = ldom_init(mem_alloc, mem_free)) == NULL) { return (topo_mod_seterrno(mod, EMOD_NOMEM)); } - if ((bufsiz = ldom_get_core_md(lhp, &bufp)) <= 0) { + (void) ldom_get_type(lhp, &type); + if ((type & LDOM_TYPE_CONTROL) != 0) { + bufsiz = ldom_get_core_md(lhp, &bufp); + } else { + bufsiz = ldom_get_local_md(lhp, &bufp); + } + if (bufsiz <= 0) { topo_mod_dprintf(mod, "failed to get the PRI/MD\n"); ldom_fini(lhp); return (-1); |
