xref: /titanic_51/usr/src/cmd/fm/fmd/common/fmd_xprt.c (revision aab83bb83be7342f6cfccaed8d5fe0b2f404855d)
1d9638e54Smws /*
2d9638e54Smws  * CDDL HEADER START
3d9638e54Smws  *
4d9638e54Smws  * The contents of this file are subject to the terms of the
5724365f7Ssethg  * Common Development and Distribution License (the "License").
6724365f7Ssethg  * You may not use this file except in compliance with the License.
7d9638e54Smws  *
8d9638e54Smws  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9d9638e54Smws  * or http://www.opensolaris.org/os/licensing.
10d9638e54Smws  * See the License for the specific language governing permissions
11d9638e54Smws  * and limitations under the License.
12d9638e54Smws  *
13d9638e54Smws  * When distributing Covered Code, include this CDDL HEADER in each
14d9638e54Smws  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15d9638e54Smws  * If applicable, add the following below this CDDL HEADER, with the
16d9638e54Smws  * fields enclosed by brackets "[]" replaced with your own identifying
17d9638e54Smws  * information: Portions Copyright [yyyy] [name of copyright owner]
18d9638e54Smws  *
19d9638e54Smws  * CDDL HEADER END
20d9638e54Smws  */
21d9638e54Smws 
22d9638e54Smws /*
23084b846fSStephen Hanson  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24d9638e54Smws  */
25d9638e54Smws 
26d9638e54Smws /*
27d9638e54Smws  * FMD Transport Subsystem
28d9638e54Smws  *
29d9638e54Smws  * A transport module uses some underlying mechanism to transport events.
30d9638e54Smws  * This mechanism may use any underlying link-layer protocol and may support
31d9638e54Smws  * additional link-layer packets unrelated to FMA.  Some appropriate link-
32d9638e54Smws  * layer mechanism to create the underlying connection is expected to be
33d9638e54Smws  * called prior to calling fmd_xprt_open() itself.  Alternatively, a transport
34d9638e54Smws  * may be created in the suspended state by specifying the FMD_XPRT_SUSPENDED
35d9638e54Smws  * flag as part of the call to fmd_xprt_open(), and then may be resumed later.
36d9638e54Smws  * The underlying transport mechanism is *required* to provide ordering: that
37d9638e54Smws  * is, the sequences of bytes written across the transport must be read by
38d9638e54Smws  * the remote peer in the order that they are written, even across separate
39d9638e54Smws  * calls to fmdo_send().  As an example, the Internet TCP protocol would be
40d9638e54Smws  * a valid transport as it guarantees ordering, whereas the Internet UDP
41d9638e54Smws  * protocol would not because UDP datagrams may be delivered in any order
42d9638e54Smws  * as a result of delays introduced when datagrams pass through routers.
43d9638e54Smws  *
44d9638e54Smws  * Similar to sending events, a transport module receives events that are from
45d9638e54Smws  * its peer remote endpoint using some transport-specific mechanism that is
46d9638e54Smws  * unknown to FMD.  As each event is received, the transport module is
47d9638e54Smws  * responsible for constructing a valid nvlist_t object from the data and then
48d9638e54Smws  * calling fmd_xprt_post() to post the event to the containing FMD's dispatch
49d9638e54Smws  * queue, making it available to all local modules that are not transport
50d9638e54Smws  * modules that have subscribed to the event.
51d9638e54Smws  *
52d9638e54Smws  * The following state machine is used for each transport.  The initial state
53d9638e54Smws  * is either SYN, ACK, or RUN, depending on the flags specified to xprt_create.
54d9638e54Smws  *
55d9638e54Smws  *       FMD_XPRT_ACCEPT   !FMD_XPRT_ACCEPT
56d9638e54Smws  *             |                 |
57d9638e54Smws  * waiting  +--v--+           +--v--+  waiting
58d9638e54Smws  * for syn  | SYN |--+     --+| ACK |  for ack
59d9638e54Smws  * event    +-----+   \   /   +-----+  event
60d9638e54Smws  *             |       \ /       |
61d9638e54Smws  * drop all +--v--+     X     +--v--+  send subscriptions,
62d9638e54Smws  * events   | ERR |<---+ +--->| SUB |  recv subscriptions,
63d9638e54Smws  *          +-----+           +-----+  wait for run event
64d9638e54Smws  *             ^                 |
65d9638e54Smws  *             |     +-----+     |
66d9638e54Smws  *             +-----| RUN |<----+
67d9638e54Smws  *                   +--^--+
68d9638e54Smws  *                      |
69d9638e54Smws  *               FMD_XPRT_RDONLY
70d9638e54Smws  *
71d9638e54Smws  * When fmd_xprt_open() is called without FMD_XPRT_ACCEPT, the Common Transport
72d9638e54Smws  * Layer enqueues a "syn" event for the module in its event queue and sets the
73d9638e54Smws  * state to ACK.  In state ACK, we are waiting for the transport to get an
74d9638e54Smws  * "ack" event and call fmd_xprt_post() on this event.  Other events will be
75d9638e54Smws  * discarded.  If an "ack" is received, we transition to state SUB.  If a
76d9638e54Smws  * configurable timeout occurs or if the "ack" is invalid (e.g. invalid version
77d9638e54Smws  * exchange), we transition to state ERR.  Once in state ERR, no further
78d9638e54Smws  * operations are valid except fmd_xprt_close() and fmd_xprt_error() will
79d9638e54Smws  * return a non-zero value to the caller indicating the transport has failed.
80d9638e54Smws  *
81d9638e54Smws  * When fmd_xprt_open() is called with FMD_XPRT_ACCEPT, the Common Transport
82d9638e54Smws  * Layer assumes this transport is being used to accept a virtual connection
83d9638e54Smws  * from a remote peer that is sending a "syn", and sets the initial state to
84d9638e54Smws  * SYN.  In this state, the transport waits for a "syn" event, validates it,
85d9638e54Smws  * and then transitions to state SUB if it is valid or state ERR if it is not.
86d9638e54Smws  *
87d9638e54Smws  * Once in state SUB, the transport module is expected to receive a sequence of
88d9638e54Smws  * zero or more "subscribe" events from the remote peer, followed by a "run"
89d9638e54Smws  * event.  Once in state RUN, the transport is active and any events can be
90d9638e54Smws  * sent or received.  The transport module is free to call fmd_xprt_close()
91d9638e54Smws  * from any state.  The fmd_xprt_error() function will return zero if the
92d9638e54Smws  * transport is not in the ERR state, or non-zero if it is in the ERR state.
93d9638e54Smws  *
94d9638e54Smws  * Once the state machine reaches RUN, other FMA protocol events can be sent
95d9638e54Smws  * and received across the transport in addition to the various control events.
96d9638e54Smws  *
97d9638e54Smws  * Table of Common Transport Layer Control Events
98d9638e54Smws  * ==============================================
99d9638e54Smws  *
100d9638e54Smws  * FMA Class                     Payload
101d9638e54Smws  * ---------                     -------
102d9638e54Smws  * resource.fm.xprt.uuclose      string (uuid of case)
103cbf75e67SStephen Hanson  * resource.fm.xprt.uuresolved   string (uuid of case)
104cbf75e67SStephen Hanson  * resource.fm.xprt.updated      string (uuid of case)
105d9638e54Smws  * resource.fm.xprt.subscribe    string (class pattern)
106d9638e54Smws  * resource.fm.xprt.unsubscribe  string (class pattern)
107d9638e54Smws  * resource.fm.xprt.unsuback     string (class pattern)
108d9638e54Smws  * resource.fm.xprt.syn          version information
109d9638e54Smws  * resource.fm.xprt.ack          version information
110d9638e54Smws  * resource.fm.xprt.run          version information
111d9638e54Smws  *
112d9638e54Smws  * Control events are used to add and delete proxy subscriptions on the remote
113d9638e54Smws  * transport peer module, and to set up connections.  When a "syn" event is
114d9638e54Smws  * sent, FMD will include in the payload the highest version of the FMA event
115d9638e54Smws  * protocol that is supported by the sender.  When a "syn" event is received,
116d9638e54Smws  * the receiving FMD will use the minimum of this version and its version of
117d9638e54Smws  * the protocol, and reply with this new minimum version in the "ack" event.
118d9638e54Smws  * The receiver will then use this new minimum for subsequent event semantics.
119d9638e54Smws  */
120d9638e54Smws 
121d9638e54Smws #include <sys/fm/protocol.h>
122d9638e54Smws #include <strings.h>
123d9638e54Smws #include <limits.h>
124d9638e54Smws 
125d9638e54Smws #include <fmd_alloc.h>
126d9638e54Smws #include <fmd_error.h>
127d9638e54Smws #include <fmd_conf.h>
128d9638e54Smws #include <fmd_subr.h>
129d9638e54Smws #include <fmd_string.h>
130d9638e54Smws #include <fmd_protocol.h>
131d9638e54Smws #include <fmd_thread.h>
132d9638e54Smws #include <fmd_eventq.h>
133d9638e54Smws #include <fmd_dispq.h>
134d9638e54Smws #include <fmd_ctl.h>
135d9638e54Smws #include <fmd_log.h>
136d9638e54Smws #include <fmd_ustat.h>
137d9638e54Smws #include <fmd_case.h>
138d9638e54Smws #include <fmd_api.h>
139d9638e54Smws #include <fmd_fmri.h>
140d9638e54Smws #include <fmd_asru.h>
141d9638e54Smws #include <fmd_xprt.h>
142d9638e54Smws 
143d9638e54Smws #include <fmd.h>
144d9638e54Smws 
145d9638e54Smws /*
146d9638e54Smws  * The states shown above in the transport state machine diagram are encoded
147d9638e54Smws  * using arrays of class patterns and a corresponding action function.  These
148d9638e54Smws  * arrays are then passed to fmd_xprt_transition() to change transport states.
149d9638e54Smws  */
150d9638e54Smws 
151d9638e54Smws const fmd_xprt_rule_t _fmd_xprt_state_syn[] = {
152d9638e54Smws { "resource.fm.xprt.syn", fmd_xprt_event_syn },
153d9638e54Smws { "*", fmd_xprt_event_error },
154d9638e54Smws { NULL, NULL }
155d9638e54Smws };
156d9638e54Smws 
157d9638e54Smws const fmd_xprt_rule_t _fmd_xprt_state_ack[] = {
158d9638e54Smws { "resource.fm.xprt.ack", fmd_xprt_event_ack },
159d9638e54Smws { "*", fmd_xprt_event_error },
160d9638e54Smws };
161d9638e54Smws 
162d9638e54Smws const fmd_xprt_rule_t _fmd_xprt_state_err[] = {
163d9638e54Smws { "*", fmd_xprt_event_drop },
164d9638e54Smws { NULL, NULL }
165d9638e54Smws };
166d9638e54Smws 
167d9638e54Smws const fmd_xprt_rule_t _fmd_xprt_state_sub[] = {
168d9638e54Smws { "resource.fm.xprt.subscribe", fmd_xprt_event_sub },
169d9638e54Smws { "resource.fm.xprt.run", fmd_xprt_event_run },
170d9638e54Smws { "resource.fm.xprt.*", fmd_xprt_event_error },
171d9638e54Smws { "*", fmd_xprt_event_drop },
172d9638e54Smws { NULL, NULL }
173d9638e54Smws };
174d9638e54Smws 
175d9638e54Smws const fmd_xprt_rule_t _fmd_xprt_state_run[] = {
176d9638e54Smws { "resource.fm.xprt.subscribe", fmd_xprt_event_sub },
177d9638e54Smws { "resource.fm.xprt.unsubscribe", fmd_xprt_event_unsub },
178d9638e54Smws { "resource.fm.xprt.unsuback", fmd_xprt_event_unsuback },
179d9638e54Smws { "resource.fm.xprt.uuclose", fmd_xprt_event_uuclose },
180cbf75e67SStephen Hanson { "resource.fm.xprt.uuresolved", fmd_xprt_event_uuresolved },
181cbf75e67SStephen Hanson { "resource.fm.xprt.updated", fmd_xprt_event_updated },
182d9638e54Smws { "resource.fm.xprt.*", fmd_xprt_event_error },
183d9638e54Smws { NULL, NULL }
184d9638e54Smws };
185d9638e54Smws 
186d9638e54Smws /*
187d9638e54Smws  * Template for per-transport statistics installed by fmd on behalf of each
188d9638e54Smws  * transport.  These are used to initialize the per-transport xi_stats.  For
189d9638e54Smws  * each statistic, the name is prepended with "fmd.xprt.%u", where %u is the
190d9638e54Smws  * transport ID (xi_id) and then are inserted into the per-module stats hash.
191d9638e54Smws  * The values in this array must match fmd_xprt_stat_t from <fmd_xprt.h>.
192d9638e54Smws  */
193d9638e54Smws static const fmd_xprt_stat_t _fmd_xprt_stat_tmpl = {
194d9638e54Smws {
195d9638e54Smws { "dispatched", FMD_TYPE_UINT64, "total events dispatched to transport" },
196d9638e54Smws { "dequeued", FMD_TYPE_UINT64, "total events dequeued by transport" },
197d9638e54Smws { "prdequeued", FMD_TYPE_UINT64, "protocol events dequeued by transport" },
198d9638e54Smws { "dropped", FMD_TYPE_UINT64, "total events dropped on queue overflow" },
199d9638e54Smws { "wcnt", FMD_TYPE_UINT32, "count of events waiting on queue" },
200d9638e54Smws { "wtime", FMD_TYPE_TIME, "total wait time on queue" },
201d9638e54Smws { "wlentime", FMD_TYPE_TIME, "total wait length * time product" },
202d9638e54Smws { "wlastupdate", FMD_TYPE_TIME, "hrtime of last wait queue update" },
203d9638e54Smws { "dtime", FMD_TYPE_TIME, "total processing time after dequeue" },
204d9638e54Smws { "dlastupdate", FMD_TYPE_TIME, "hrtime of last event dequeue completion" },
205d9638e54Smws },
206d9638e54Smws { "module", FMD_TYPE_STRING, "module that owns this transport" },
207d9638e54Smws { "authority", FMD_TYPE_STRING, "authority associated with this transport" },
208d9638e54Smws { "state", FMD_TYPE_STRING, "current transport state" },
209d9638e54Smws { "received", FMD_TYPE_UINT64, "events received by transport" },
210d9638e54Smws { "discarded", FMD_TYPE_UINT64, "bad events discarded by transport" },
211d9638e54Smws { "retried", FMD_TYPE_UINT64, "retries requested of transport" },
212d9638e54Smws { "replayed", FMD_TYPE_UINT64, "events replayed by transport" },
213d9638e54Smws { "lost", FMD_TYPE_UINT64, "events lost by transport" },
214d9638e54Smws { "timeouts", FMD_TYPE_UINT64, "events received by transport with ttl=0" },
215d9638e54Smws { "subscriptions", FMD_TYPE_UINT64, "subscriptions registered to transport" },
216d9638e54Smws };
217d9638e54Smws 
218d9638e54Smws static void
219d9638e54Smws fmd_xprt_class_hash_create(fmd_xprt_class_hash_t *xch, fmd_eventq_t *eq)
220d9638e54Smws {
221d9638e54Smws 	uint_t hashlen = fmd.d_str_buckets;
222d9638e54Smws 
223d9638e54Smws 	xch->xch_queue = eq;
224d9638e54Smws 	xch->xch_hashlen = hashlen;
225d9638e54Smws 	xch->xch_hash = fmd_zalloc(sizeof (void *) * hashlen, FMD_SLEEP);
226d9638e54Smws }
227d9638e54Smws 
228d9638e54Smws static void
229d9638e54Smws fmd_xprt_class_hash_destroy(fmd_xprt_class_hash_t *xch)
230d9638e54Smws {
231d9638e54Smws 	fmd_eventq_t *eq = xch->xch_queue;
232d9638e54Smws 	fmd_xprt_class_t *xcp, *ncp;
233d9638e54Smws 	uint_t i;
234d9638e54Smws 
235d9638e54Smws 	for (i = 0; i < xch->xch_hashlen; i++) {
236d9638e54Smws 		for (xcp = xch->xch_hash[i]; xcp != NULL; xcp = ncp) {
237d9638e54Smws 			ncp = xcp->xc_next;
238d9638e54Smws 
239d9638e54Smws 			if (eq != NULL)
240d9638e54Smws 				fmd_dispq_delete(fmd.d_disp, eq, xcp->xc_class);
241d9638e54Smws 
242d9638e54Smws 			fmd_strfree(xcp->xc_class);
243d9638e54Smws 			fmd_free(xcp, sizeof (fmd_xprt_class_t));
244d9638e54Smws 		}
245d9638e54Smws 	}
246d9638e54Smws 
247d9638e54Smws 	fmd_free(xch->xch_hash, sizeof (void *) * xch->xch_hashlen);
248d9638e54Smws }
249d9638e54Smws 
250d9638e54Smws /*
251d9638e54Smws  * Insert the specified class into the specified class hash, and return the
252d9638e54Smws  * reference count.  A return value of one indicates this is the first insert.
253d9638e54Smws  * If an eventq is associated with the hash, insert a dispq subscription for it.
254d9638e54Smws  */
255d9638e54Smws static uint_t
256d9638e54Smws fmd_xprt_class_hash_insert(fmd_xprt_impl_t *xip,
257d9638e54Smws     fmd_xprt_class_hash_t *xch, const char *class)
258d9638e54Smws {
259d9638e54Smws 	uint_t h = fmd_strhash(class) % xch->xch_hashlen;
260d9638e54Smws 	fmd_xprt_class_t *xcp;
261d9638e54Smws 
262d9638e54Smws 	ASSERT(MUTEX_HELD(&xip->xi_lock));
263d9638e54Smws 
264d9638e54Smws 	for (xcp = xch->xch_hash[h]; xcp != NULL; xcp = xcp->xc_next) {
265d9638e54Smws 		if (strcmp(class, xcp->xc_class) == 0)
266d9638e54Smws 			return (++xcp->xc_refs);
267d9638e54Smws 	}
268d9638e54Smws 
269d9638e54Smws 	xcp = fmd_alloc(sizeof (fmd_xprt_class_t), FMD_SLEEP);
270d9638e54Smws 	xcp->xc_class = fmd_strdup(class, FMD_SLEEP);
271d9638e54Smws 	xcp->xc_next = xch->xch_hash[h];
272d9638e54Smws 	xcp->xc_refs = 1;
273d9638e54Smws 	xch->xch_hash[h] = xcp;
274d9638e54Smws 
275d9638e54Smws 	if (xch->xch_queue != NULL)
276d9638e54Smws 		fmd_dispq_insert(fmd.d_disp, xch->xch_queue, class);
277d9638e54Smws 
278d9638e54Smws 	return (xcp->xc_refs);
279d9638e54Smws }
280d9638e54Smws 
281d9638e54Smws /*
282d9638e54Smws  * Delete the specified class from the specified class hash, and return the
283d9638e54Smws  * reference count.  A return value of zero indicates the class was deleted.
284d9638e54Smws  * If an eventq is associated with the hash, delete the dispq subscription.
285d9638e54Smws  */
286d9638e54Smws static uint_t
287d9638e54Smws fmd_xprt_class_hash_delete(fmd_xprt_impl_t *xip,
288d9638e54Smws     fmd_xprt_class_hash_t *xch, const char *class)
289d9638e54Smws {
290d9638e54Smws 	uint_t h = fmd_strhash(class) % xch->xch_hashlen;
291d9638e54Smws 	fmd_xprt_class_t *xcp, **pp;
292d9638e54Smws 
293d9638e54Smws 	ASSERT(MUTEX_HELD(&xip->xi_lock));
294d9638e54Smws 	pp = &xch->xch_hash[h];
295d9638e54Smws 
296d9638e54Smws 	for (xcp = *pp; xcp != NULL; xcp = xcp->xc_next) {
297d9638e54Smws 		if (strcmp(class, xcp->xc_class) == 0)
298d9638e54Smws 			break;
299d9638e54Smws 		else
300d9638e54Smws 			pp = &xcp->xc_next;
301d9638e54Smws 	}
302d9638e54Smws 
303d9638e54Smws 	if (xcp == NULL)
304d9638e54Smws 		return (-1U); /* explicitly permit an invalid delete */
305d9638e54Smws 
306d9638e54Smws 	if (--xcp->xc_refs != 0)
307d9638e54Smws 		return (xcp->xc_refs);
308d9638e54Smws 
309d9638e54Smws 	ASSERT(xcp->xc_refs == 0);
310d9638e54Smws 	*pp = xcp->xc_next;
311d9638e54Smws 
312d9638e54Smws 	fmd_strfree(xcp->xc_class);
313d9638e54Smws 	fmd_free(xcp, sizeof (fmd_xprt_class_t));
314d9638e54Smws 
315d9638e54Smws 	if (xch->xch_queue != NULL)
316d9638e54Smws 		fmd_dispq_delete(fmd.d_disp, xch->xch_queue, class);
317d9638e54Smws 
318d9638e54Smws 	return (0);
319d9638e54Smws }
320d9638e54Smws 
321d9638e54Smws /*
322d9638e54Smws  * Queue subscribe events for the specified transport corresponding to all of
323d9638e54Smws  * the active module subscriptions.  This is an extremely heavyweight operation
324d9638e54Smws  * that we expect to take place rarely (i.e. when loading a transport module
325d9638e54Smws  * or when it establishes a connection).  We lock all of the known modules to
326d9638e54Smws  * prevent them from adding or deleting subscriptions, then snapshot their
327d9638e54Smws  * subscriptions, and then unlock all of the modules.  We hold the modhash
328d9638e54Smws  * lock for the duration of this operation to prevent new modules from loading.
329d9638e54Smws  */
330d9638e54Smws static void
331d9638e54Smws fmd_xprt_subscribe_modhash(fmd_xprt_impl_t *xip, fmd_modhash_t *mhp)
332d9638e54Smws {
333d9638e54Smws 	fmd_xprt_t *xp = (fmd_xprt_t *)xip;
334d9638e54Smws 	const fmd_conf_path_t *pap;
335d9638e54Smws 	fmd_module_t *mp;
336d9638e54Smws 	uint_t i, j;
337d9638e54Smws 
338d9638e54Smws 	(void) pthread_rwlock_rdlock(&mhp->mh_lock);
339d9638e54Smws 
340d9638e54Smws 	for (i = 0; i < mhp->mh_hashlen; i++) {
341d9638e54Smws 		for (mp = mhp->mh_hash[i]; mp != NULL; mp = mp->mod_next)
342d9638e54Smws 			fmd_module_lock(mp);
343d9638e54Smws 	}
344d9638e54Smws 
345d9638e54Smws 	(void) pthread_mutex_lock(&xip->xi_lock);
346d9638e54Smws 	ASSERT(!(xip->xi_flags & FMD_XPRT_SUBSCRIBER));
347d9638e54Smws 	xip->xi_flags |= FMD_XPRT_SUBSCRIBER;
348d9638e54Smws 	(void) pthread_mutex_unlock(&xip->xi_lock);
349d9638e54Smws 
350d9638e54Smws 	for (i = 0; i < mhp->mh_hashlen; i++) {
351d9638e54Smws 		for (mp = mhp->mh_hash[i]; mp != NULL; mp = mp->mod_next) {
352d9638e54Smws 			(void) fmd_conf_getprop(mp->mod_conf,
353d9638e54Smws 			    FMD_PROP_SUBSCRIPTIONS, &pap);
354d9638e54Smws 			for (j = 0; j < pap->cpa_argc; j++)
355d9638e54Smws 				fmd_xprt_subscribe(xp, pap->cpa_argv[j]);
356d9638e54Smws 		}
357d9638e54Smws 	}
358d9638e54Smws 
359d9638e54Smws 	for (i = 0; i < mhp->mh_hashlen; i++) {
360d9638e54Smws 		for (mp = mhp->mh_hash[i]; mp != NULL; mp = mp->mod_next)
361d9638e54Smws 			fmd_module_unlock(mp);
362d9638e54Smws 	}
363d9638e54Smws 
364d9638e54Smws 	(void) pthread_rwlock_unlock(&mhp->mh_lock);
365d9638e54Smws }
366d9638e54Smws 
367d9638e54Smws static void
368d9638e54Smws fmd_xprt_transition(fmd_xprt_impl_t *xip,
369d9638e54Smws     const fmd_xprt_rule_t *state, const char *tag)
370d9638e54Smws {
371b6955755SRobert Johnston 	fmd_xprt_t *xp = (fmd_xprt_t *)xip;
372d9638e54Smws 	fmd_event_t *e;
373d9638e54Smws 	nvlist_t *nvl;
374d9638e54Smws 	char *s;
375d9638e54Smws 
376d9638e54Smws 	TRACE((FMD_DBG_XPRT, "xprt %u -> %s\n", xip->xi_id, tag));
377d9638e54Smws 
378d9638e54Smws 	xip->xi_state = state;
379d9638e54Smws 	s = fmd_strdup(tag, FMD_SLEEP);
380d9638e54Smws 
381d9638e54Smws 	(void) pthread_mutex_lock(&xip->xi_stats_lock);
382d9638e54Smws 	fmd_strfree(xip->xi_stats->xs_state.fmds_value.str);
383d9638e54Smws 	xip->xi_stats->xs_state.fmds_value.str = s;
384d9638e54Smws 	(void) pthread_mutex_unlock(&xip->xi_stats_lock);
385d9638e54Smws 
386d9638e54Smws 	/*
387d9638e54Smws 	 * If we've reached the SUB state, take out the big hammer and snapshot
388d9638e54Smws 	 * all of the subscriptions of all of the loaded modules.  Then queue a
389d9638e54Smws 	 * run event for our remote peer indicating that it can enter RUN.
390d9638e54Smws 	 */
391d9638e54Smws 	if (state == _fmd_xprt_state_sub) {
392d9638e54Smws 		fmd_xprt_subscribe_modhash(xip, fmd.d_mod_hash);
393d9638e54Smws 
394b6955755SRobert Johnston 		/*
395b6955755SRobert Johnston 		 * For read-write transports, we always want to set up remote
396b6955755SRobert Johnston 		 * subscriptions to the bultin list.* events, regardless of
397b6955755SRobert Johnston 		 * whether any agents have subscribed to them.
398b6955755SRobert Johnston 		 */
399b6955755SRobert Johnston 		if (xip->xi_flags & FMD_XPRT_RDWR) {
400b6955755SRobert Johnston 			fmd_xprt_subscribe(xp, FM_LIST_SUSPECT_CLASS);
401b6955755SRobert Johnston 			fmd_xprt_subscribe(xp, FM_LIST_ISOLATED_CLASS);
402b6955755SRobert Johnston 			fmd_xprt_subscribe(xp, FM_LIST_UPDATED_CLASS);
403b6955755SRobert Johnston 			fmd_xprt_subscribe(xp, FM_LIST_RESOLVED_CLASS);
404b6955755SRobert Johnston 			fmd_xprt_subscribe(xp, FM_LIST_REPAIRED_CLASS);
405b6955755SRobert Johnston 		}
406b6955755SRobert Johnston 
407d9638e54Smws 		nvl = fmd_protocol_xprt_ctl(xip->xi_queue->eq_mod,
408d9638e54Smws 		    "resource.fm.xprt.run", xip->xi_version);
409d9638e54Smws 
410d9638e54Smws 		(void) nvlist_lookup_string(nvl, FM_CLASS, &s);
411d9638e54Smws 		e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, s);
412d9638e54Smws 		fmd_eventq_insert_at_time(xip->xi_queue, e);
413d9638e54Smws 	}
414d9638e54Smws }
415d9638e54Smws 
416d9638e54Smws static void
417d9638e54Smws fmd_xprt_authupdate(fmd_xprt_impl_t *xip)
418d9638e54Smws {
419d9638e54Smws 	char *s = fmd_fmri_auth2str(xip->xi_auth);
420d9638e54Smws 
421d9638e54Smws 	(void) pthread_mutex_lock(&xip->xi_stats_lock);
422d9638e54Smws 	fmd_strfree(xip->xi_stats->xs_authority.fmds_value.str);
423d9638e54Smws 	xip->xi_stats->xs_authority.fmds_value.str = s;
424d9638e54Smws 	(void) pthread_mutex_unlock(&xip->xi_stats_lock);
425d9638e54Smws }
426d9638e54Smws 
427d9638e54Smws static int
428d9638e54Smws fmd_xprt_vmismatch(fmd_xprt_impl_t *xip, nvlist_t *nvl, uint_t *rversionp)
429d9638e54Smws {
430d9638e54Smws 	uint8_t rversion;
431d9638e54Smws 
432d9638e54Smws 	if (nvlist_lookup_uint8(nvl, FM_VERSION, &rversion) != 0) {
433d9638e54Smws 		(void) pthread_mutex_lock(&xip->xi_stats_lock);
434d9638e54Smws 		xip->xi_stats->xs_discarded.fmds_value.ui64++;
435d9638e54Smws 		(void) pthread_mutex_unlock(&xip->xi_stats_lock);
436d9638e54Smws 
437d9638e54Smws 		fmd_xprt_transition(xip, _fmd_xprt_state_err, "ERR");
438d9638e54Smws 		return (1);
439d9638e54Smws 	}
440d9638e54Smws 
441d9638e54Smws 	if (rversion > xip->xi_version) {
442d9638e54Smws 		fmd_dprintf(FMD_DBG_XPRT, "xprt %u protocol mismatch: %u>%u\n",
443d9638e54Smws 		    xip->xi_id, rversion, xip->xi_version);
444d9638e54Smws 
445d9638e54Smws 		(void) pthread_mutex_lock(&xip->xi_stats_lock);
446d9638e54Smws 		xip->xi_stats->xs_discarded.fmds_value.ui64++;
447d9638e54Smws 		(void) pthread_mutex_unlock(&xip->xi_stats_lock);
448d9638e54Smws 
449d9638e54Smws 		fmd_xprt_transition(xip, _fmd_xprt_state_err, "ERR");
450d9638e54Smws 		return (1);
451d9638e54Smws 	}
452d9638e54Smws 
453d9638e54Smws 	if (rversionp != NULL)
454d9638e54Smws 		*rversionp = rversion;
455d9638e54Smws 
456d9638e54Smws 	return (0);
457d9638e54Smws }
458d9638e54Smws 
459d9638e54Smws void
460d9638e54Smws fmd_xprt_event_syn(fmd_xprt_impl_t *xip, nvlist_t *nvl)
461d9638e54Smws {
462d9638e54Smws 	fmd_event_t *e;
463d9638e54Smws 	uint_t vers;
464d9638e54Smws 	char *class;
465d9638e54Smws 
466d9638e54Smws 	if (fmd_xprt_vmismatch(xip, nvl, &vers))
467d9638e54Smws 		return; /* transitioned to error state */
468d9638e54Smws 
469d9638e54Smws 	/*
470d9638e54Smws 	 * If the transport module didn't specify an authority, extract the
471d9638e54Smws 	 * one that is passed along with the xprt.syn event and use that.
472d9638e54Smws 	 */
473d9638e54Smws 	if (xip->xi_auth == NULL &&
474d9638e54Smws 	    nvlist_lookup_nvlist(nvl, FM_RSRC_RESOURCE, &nvl) == 0 &&
475d9638e54Smws 	    nvlist_lookup_nvlist(nvl, FM_FMRI_AUTHORITY, &nvl) == 0) {
476d9638e54Smws 		(void) nvlist_xdup(nvl, &xip->xi_auth, &fmd.d_nva);
477d9638e54Smws 		fmd_xprt_authupdate(xip);
478d9638e54Smws 	}
479d9638e54Smws 
480d9638e54Smws 	nvl = fmd_protocol_xprt_ctl(xip->xi_queue->eq_mod,
481d9638e54Smws 	    "resource.fm.xprt.ack", xip->xi_version);
482d9638e54Smws 
483d9638e54Smws 	(void) nvlist_lookup_string(nvl, FM_CLASS, &class);
484d9638e54Smws 	e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, class);
485d9638e54Smws 	fmd_eventq_insert_at_time(xip->xi_queue, e);
486d9638e54Smws 
487d9638e54Smws 	xip->xi_version = MIN(FM_RSRC_XPRT_VERSION, vers);
488d9638e54Smws 	fmd_xprt_transition(xip, _fmd_xprt_state_sub, "SUB");
489d9638e54Smws }
490d9638e54Smws 
491d9638e54Smws void
492d9638e54Smws fmd_xprt_event_ack(fmd_xprt_impl_t *xip, nvlist_t *nvl)
493d9638e54Smws {
494d9638e54Smws 	uint_t vers;
495d9638e54Smws 
496d9638e54Smws 	if (fmd_xprt_vmismatch(xip, nvl, &vers))
497d9638e54Smws 		return; /* transitioned to error state */
498d9638e54Smws 
499d9638e54Smws 	/*
500d9638e54Smws 	 * If the transport module didn't specify an authority, extract the
501d9638e54Smws 	 * one that is passed along with the xprt.syn event and use that.
502d9638e54Smws 	 */
503d9638e54Smws 	if (xip->xi_auth == NULL &&
504d9638e54Smws 	    nvlist_lookup_nvlist(nvl, FM_RSRC_RESOURCE, &nvl) == 0 &&
505d9638e54Smws 	    nvlist_lookup_nvlist(nvl, FM_FMRI_AUTHORITY, &nvl) == 0) {
506d9638e54Smws 		(void) nvlist_xdup(nvl, &xip->xi_auth, &fmd.d_nva);
507d9638e54Smws 		fmd_xprt_authupdate(xip);
508d9638e54Smws 	}
509d9638e54Smws 
510d9638e54Smws 	xip->xi_version = MIN(FM_RSRC_XPRT_VERSION, vers);
511d9638e54Smws 	fmd_xprt_transition(xip, _fmd_xprt_state_sub, "SUB");
512d9638e54Smws }
513d9638e54Smws 
514d9638e54Smws /*
515d9638e54Smws  * Upon transition to RUN, we take every solved case and resend a list.suspect
516d9638e54Smws  * event for it to our remote peer.  If a case transitions from solved to a
517d9638e54Smws  * future state (CLOSE_WAIT, CLOSED, or REPAIRED) while we are iterating over
518d9638e54Smws  * the case hash, we will get it as part of examining the resource cache, next.
519d9638e54Smws  */
520d9638e54Smws static void
521d9638e54Smws fmd_xprt_send_case(fmd_case_t *cp, void *arg)
522d9638e54Smws {
523d9638e54Smws 	fmd_case_impl_t *cip = (fmd_case_impl_t *)cp;
524d9638e54Smws 	fmd_xprt_impl_t *xip = arg;
525d9638e54Smws 
526d9638e54Smws 	fmd_event_t *e;
527d9638e54Smws 	nvlist_t *nvl;
528d9638e54Smws 	char *class;
529d9638e54Smws 
530*107c18c1SLauri Tirkkonen 	if (cip->ci_state != FMD_CASE_SOLVED)
531cbf75e67SStephen Hanson 		return;
532d9638e54Smws 
533d9638e54Smws 	nvl = fmd_case_mkevent(cp, FM_LIST_SUSPECT_CLASS);
534d9638e54Smws 	(void) nvlist_lookup_string(nvl, FM_CLASS, &class);
535d9638e54Smws 	e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, class);
536d9638e54Smws 
537d9638e54Smws 	fmd_dprintf(FMD_DBG_XPRT, "re-send %s for %s to transport %u\n",
538d9638e54Smws 	    FM_LIST_SUSPECT_CLASS, cip->ci_uuid, xip->xi_id);
539d9638e54Smws 
540d9638e54Smws 	fmd_dispq_dispatch_gid(fmd.d_disp, e, class, xip->xi_queue->eq_sgid);
541d9638e54Smws }
542d9638e54Smws 
543084b846fSStephen Hanson /*
544084b846fSStephen Hanson  * Similar to the above function, but for use with readonly transport. Puts
545084b846fSStephen Hanson  * the event on the module's queue such that it's fmdo_recv function can pick
546084b846fSStephen Hanson  * it up and send it if appropriate.
547084b846fSStephen Hanson  */
548084b846fSStephen Hanson static void
549084b846fSStephen Hanson fmd_xprt_send_case_ro(fmd_case_t *cp, void *arg)
550084b846fSStephen Hanson {
551084b846fSStephen Hanson 	fmd_case_impl_t *cip = (fmd_case_impl_t *)cp;
552084b846fSStephen Hanson 	fmd_module_t *mp = arg;
553084b846fSStephen Hanson 
554084b846fSStephen Hanson 	fmd_event_t *e;
555084b846fSStephen Hanson 	nvlist_t *nvl;
556084b846fSStephen Hanson 	char *class;
557084b846fSStephen Hanson 
558*107c18c1SLauri Tirkkonen 	if (cip->ci_state != FMD_CASE_SOLVED)
559084b846fSStephen Hanson 		return;
560084b846fSStephen Hanson 
561084b846fSStephen Hanson 	nvl = fmd_case_mkevent(cp, FM_LIST_SUSPECT_CLASS);
562084b846fSStephen Hanson 	(void) nvlist_lookup_string(nvl, FM_CLASS, &class);
563084b846fSStephen Hanson 	e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, class);
564084b846fSStephen Hanson 
565084b846fSStephen Hanson 	fmd_dprintf(FMD_DBG_XPRT, "re-send %s for %s to rdonly transport %s\n",
566084b846fSStephen Hanson 	    FM_LIST_SUSPECT_CLASS, cip->ci_uuid, mp->mod_name);
567084b846fSStephen Hanson 
568084b846fSStephen Hanson 	fmd_dispq_dispatch_gid(fmd.d_disp, e, class, mp->mod_queue->eq_sgid);
569084b846fSStephen Hanson }
570084b846fSStephen Hanson 
571d9638e54Smws void
572d9638e54Smws fmd_xprt_event_run(fmd_xprt_impl_t *xip, nvlist_t *nvl)
573d9638e54Smws {
574d9638e54Smws 	if (!fmd_xprt_vmismatch(xip, nvl, NULL)) {
575d9638e54Smws 		fmd_xprt_transition(xip, _fmd_xprt_state_run, "RUN");
576d9638e54Smws 		fmd_case_hash_apply(fmd.d_cases, fmd_xprt_send_case, xip);
577d9638e54Smws 	}
578d9638e54Smws }
579d9638e54Smws 
580d9638e54Smws void
581d9638e54Smws fmd_xprt_event_sub(fmd_xprt_impl_t *xip, nvlist_t *nvl)
582d9638e54Smws {
583d9638e54Smws 	char *class;
584d9638e54Smws 
585d9638e54Smws 	if (fmd_xprt_vmismatch(xip, nvl, NULL))
586d9638e54Smws 		return; /* transitioned to error state */
587d9638e54Smws 
588d9638e54Smws 	if (nvlist_lookup_string(nvl, FM_RSRC_XPRT_SUBCLASS, &class) != 0)
589d9638e54Smws 		return; /* malformed protocol event */
590d9638e54Smws 
591d9638e54Smws 	(void) pthread_mutex_lock(&xip->xi_lock);
592d9638e54Smws 	(void) fmd_xprt_class_hash_insert(xip, &xip->xi_lsub, class);
593d9638e54Smws 	(void) pthread_mutex_unlock(&xip->xi_lock);
594d9638e54Smws 
595d9638e54Smws 	(void) pthread_mutex_lock(&xip->xi_stats_lock);
596d9638e54Smws 	xip->xi_stats->xs_subscriptions.fmds_value.ui64++;
597d9638e54Smws 	(void) pthread_mutex_unlock(&xip->xi_stats_lock);
598d9638e54Smws }
599d9638e54Smws 
600d9638e54Smws void
601d9638e54Smws fmd_xprt_event_unsub(fmd_xprt_impl_t *xip, nvlist_t *nvl)
602d9638e54Smws {
603d9638e54Smws 	fmd_event_t *e;
604d9638e54Smws 	char *class;
605d9638e54Smws 
606d9638e54Smws 	if (fmd_xprt_vmismatch(xip, nvl, NULL))
607d9638e54Smws 		return; /* transitioned to error state */
608d9638e54Smws 
609d9638e54Smws 	if (nvlist_lookup_string(nvl, FM_RSRC_XPRT_SUBCLASS, &class) != 0)
610d9638e54Smws 		return; /* malformed protocol event */
611d9638e54Smws 
612d9638e54Smws 	(void) pthread_mutex_lock(&xip->xi_lock);
613d9638e54Smws 	(void) fmd_xprt_class_hash_delete(xip, &xip->xi_lsub, class);
614d9638e54Smws 	(void) pthread_mutex_unlock(&xip->xi_lock);
615d9638e54Smws 
616d9638e54Smws 	(void) pthread_mutex_lock(&xip->xi_stats_lock);
617d9638e54Smws 	xip->xi_stats->xs_subscriptions.fmds_value.ui64--;
618d9638e54Smws 	(void) pthread_mutex_unlock(&xip->xi_stats_lock);
619d9638e54Smws 
620d9638e54Smws 	nvl = fmd_protocol_xprt_sub(xip->xi_queue->eq_mod,
621d9638e54Smws 	    "resource.fm.xprt.unsuback", xip->xi_version, class);
622d9638e54Smws 
623d9638e54Smws 	(void) nvlist_lookup_string(nvl, FM_CLASS, &class);
624d9638e54Smws 	e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, class);
625d9638e54Smws 	fmd_eventq_insert_at_time(xip->xi_queue, e);
626d9638e54Smws }
627d9638e54Smws 
628d9638e54Smws void
629d9638e54Smws fmd_xprt_event_unsuback(fmd_xprt_impl_t *xip, nvlist_t *nvl)
630d9638e54Smws {
631d9638e54Smws 	char *class;
632d9638e54Smws 
633d9638e54Smws 	if (fmd_xprt_vmismatch(xip, nvl, NULL))
634d9638e54Smws 		return; /* transitioned to error state */
635d9638e54Smws 
636d9638e54Smws 	if (nvlist_lookup_string(nvl, FM_RSRC_XPRT_SUBCLASS, &class) != 0)
637d9638e54Smws 		return; /* malformed protocol event */
638d9638e54Smws 
639d9638e54Smws 	(void) pthread_mutex_lock(&xip->xi_lock);
640d9638e54Smws 	(void) fmd_xprt_class_hash_delete(xip, &xip->xi_usub, class);
641d9638e54Smws 	(void) pthread_mutex_unlock(&xip->xi_lock);
642d9638e54Smws }
643d9638e54Smws 
644cbf75e67SStephen Hanson /*
645cbf75e67SStephen Hanson  * on diagnosing side, receive a uuclose from the proxy.
646cbf75e67SStephen Hanson  */
647d9638e54Smws void
648d9638e54Smws fmd_xprt_event_uuclose(fmd_xprt_impl_t *xip, nvlist_t *nvl)
649d9638e54Smws {
650d9638e54Smws 	fmd_case_t *cp;
651d9638e54Smws 	char *uuid;
652d9638e54Smws 
653d9638e54Smws 	if (fmd_xprt_vmismatch(xip, nvl, NULL))
654d9638e54Smws 		return; /* transitioned to error state */
655d9638e54Smws 
656d9638e54Smws 	if (nvlist_lookup_string(nvl, FM_RSRC_XPRT_UUID, &uuid) == 0 &&
657d9638e54Smws 	    (cp = fmd_case_hash_lookup(fmd.d_cases, uuid)) != NULL) {
658cbf75e67SStephen Hanson 		/*
659cbf75e67SStephen Hanson 		 * update resource cache status and transition case
660cbf75e67SStephen Hanson 		 */
661cbf75e67SStephen Hanson 		fmd_case_close_status(cp);
662d9638e54Smws 		fmd_case_transition(cp, FMD_CASE_CLOSE_WAIT, FMD_CF_ISOLATED);
663d9638e54Smws 		fmd_case_rele(cp);
664d9638e54Smws 	}
665d9638e54Smws }
666d9638e54Smws 
667cbf75e67SStephen Hanson /*
668cbf75e67SStephen Hanson  * on diagnosing side, receive a uuresolved from the proxy.
669cbf75e67SStephen Hanson  */
670cbf75e67SStephen Hanson void
671cbf75e67SStephen Hanson fmd_xprt_event_uuresolved(fmd_xprt_impl_t *xip, nvlist_t *nvl)
672cbf75e67SStephen Hanson {
673cbf75e67SStephen Hanson 	fmd_case_t *cp;
674cbf75e67SStephen Hanson 	char *uuid;
675cbf75e67SStephen Hanson 
676cbf75e67SStephen Hanson 	if (fmd_xprt_vmismatch(xip, nvl, NULL))
677cbf75e67SStephen Hanson 		return; /* transitioned to error state */
678cbf75e67SStephen Hanson 
679cbf75e67SStephen Hanson 	if (nvlist_lookup_string(nvl, FM_RSRC_XPRT_UUID, &uuid) == 0 &&
680cbf75e67SStephen Hanson 	    (cp = fmd_case_hash_lookup(fmd.d_cases, uuid)) != NULL) {
681cbf75e67SStephen Hanson 		fmd_case_impl_t *cip = (fmd_case_impl_t *)cp;
682cbf75e67SStephen Hanson 
683cbf75e67SStephen Hanson 		fmd_case_transition(cp, (cip->ci_state == FMD_CASE_REPAIRED) ?
684cbf75e67SStephen Hanson 		    FMD_CASE_RESOLVED : (cip->ci_state == FMD_CASE_CLOSED) ?
685cbf75e67SStephen Hanson 		    FMD_CASE_REPAIRED : FMD_CASE_CLOSE_WAIT, FMD_CF_RESOLVED);
686cbf75e67SStephen Hanson 		fmd_case_rele(cp);
687cbf75e67SStephen Hanson 	}
688cbf75e67SStephen Hanson }
689cbf75e67SStephen Hanson 
690cbf75e67SStephen Hanson /*
691cbf75e67SStephen Hanson  * on diagnosing side, receive a repair/acquit from the proxy.
692cbf75e67SStephen Hanson  */
693cbf75e67SStephen Hanson void
694cbf75e67SStephen Hanson fmd_xprt_event_updated(fmd_xprt_impl_t *xip, nvlist_t *nvl)
695cbf75e67SStephen Hanson {
696cbf75e67SStephen Hanson 	fmd_case_t *cp;
697cbf75e67SStephen Hanson 	char *uuid;
698cbf75e67SStephen Hanson 
699cbf75e67SStephen Hanson 	if (fmd_xprt_vmismatch(xip, nvl, NULL))
700cbf75e67SStephen Hanson 		return; /* transitioned to error state */
701cbf75e67SStephen Hanson 
702cbf75e67SStephen Hanson 	if (nvlist_lookup_string(nvl, FM_RSRC_XPRT_UUID, &uuid) == 0 &&
703cbf75e67SStephen Hanson 	    (cp = fmd_case_hash_lookup(fmd.d_cases, uuid)) != NULL) {
704cbf75e67SStephen Hanson 		uint8_t *statusp, *proxy_asrup = NULL;
705cbf75e67SStephen Hanson 		uint_t nelem = 0;
706cbf75e67SStephen Hanson 
707cbf75e67SStephen Hanson 		/*
708cbf75e67SStephen Hanson 		 * Only update status with new repairs if "no remote repair"
709cbf75e67SStephen Hanson 		 * is not set. Do the case_update anyway though (as this will
710cbf75e67SStephen Hanson 		 * refresh the status on the proxy side).
711cbf75e67SStephen Hanson 		 */
712cbf75e67SStephen Hanson 		if (!(xip->xi_flags & FMD_XPRT_NO_REMOTE_REPAIR)) {
713cbf75e67SStephen Hanson 			if (nvlist_lookup_uint8_array(nvl,
714cbf75e67SStephen Hanson 			    FM_RSRC_XPRT_FAULT_STATUS, &statusp, &nelem) == 0 &&
715cbf75e67SStephen Hanson 			    nelem != 0) {
716cbf75e67SStephen Hanson 				(void) nvlist_lookup_uint8_array(nvl,
717cbf75e67SStephen Hanson 				    FM_RSRC_XPRT_FAULT_HAS_ASRU, &proxy_asrup,
718cbf75e67SStephen Hanson 				    &nelem);
719cbf75e67SStephen Hanson 				fmd_case_update_status(cp, statusp,
720cbf75e67SStephen Hanson 				    proxy_asrup, NULL);
721cbf75e67SStephen Hanson 			}
722cbf75e67SStephen Hanson 			fmd_case_update_containees(cp);
723cbf75e67SStephen Hanson 		}
724cbf75e67SStephen Hanson 		fmd_case_update(cp);
725cbf75e67SStephen Hanson 		fmd_case_rele(cp);
726cbf75e67SStephen Hanson 	}
727cbf75e67SStephen Hanson }
728cbf75e67SStephen Hanson 
729d9638e54Smws void
730d9638e54Smws fmd_xprt_event_error(fmd_xprt_impl_t *xip, nvlist_t *nvl)
731d9638e54Smws {
732d9638e54Smws 	char *class = "<unknown>";
733d9638e54Smws 
734d9638e54Smws 	(void) pthread_mutex_lock(&xip->xi_stats_lock);
735d9638e54Smws 	xip->xi_stats->xs_discarded.fmds_value.ui64++;
736d9638e54Smws 	(void) pthread_mutex_unlock(&xip->xi_stats_lock);
737d9638e54Smws 
738d9638e54Smws 	(void) nvlist_lookup_string(nvl, FM_CLASS, &class);
739d9638e54Smws 	TRACE((FMD_DBG_XPRT, "xprt %u bad event %s\n", xip->xi_id, class));
740d9638e54Smws 
741d9638e54Smws 	fmd_xprt_transition(xip, _fmd_xprt_state_err, "ERR");
742d9638e54Smws }
743d9638e54Smws 
744d9638e54Smws void
745d9638e54Smws fmd_xprt_event_drop(fmd_xprt_impl_t *xip, nvlist_t *nvl)
746d9638e54Smws {
747d9638e54Smws 	char *class = "<unknown>";
748d9638e54Smws 
749d9638e54Smws 	(void) pthread_mutex_lock(&xip->xi_stats_lock);
750d9638e54Smws 	xip->xi_stats->xs_discarded.fmds_value.ui64++;
751d9638e54Smws 	(void) pthread_mutex_unlock(&xip->xi_stats_lock);
752d9638e54Smws 
753d9638e54Smws 	(void) nvlist_lookup_string(nvl, FM_CLASS, &class);
754d9638e54Smws 	TRACE((FMD_DBG_XPRT, "xprt %u drop event %s\n", xip->xi_id, class));
755d9638e54Smws 
756d9638e54Smws }
757d9638e54Smws 
758d9638e54Smws fmd_xprt_t *
759d9638e54Smws fmd_xprt_create(fmd_module_t *mp, uint_t flags, nvlist_t *auth, void *data)
760d9638e54Smws {
761d9638e54Smws 	fmd_xprt_impl_t *xip = fmd_zalloc(sizeof (fmd_xprt_impl_t), FMD_SLEEP);
762d9638e54Smws 	fmd_stat_t *statv;
763d9638e54Smws 	uint_t i, statc;
764d9638e54Smws 
765d9638e54Smws 	char buf[PATH_MAX];
766d9638e54Smws 	fmd_event_t *e;
767d9638e54Smws 	nvlist_t *nvl;
768d9638e54Smws 	char *s;
769d9638e54Smws 
770d9638e54Smws 	(void) pthread_mutex_init(&xip->xi_lock, NULL);
771d9638e54Smws 	(void) pthread_cond_init(&xip->xi_cv, NULL);
772d9638e54Smws 	(void) pthread_mutex_init(&xip->xi_stats_lock, NULL);
773d9638e54Smws 
774d9638e54Smws 	xip->xi_auth = auth;
775d9638e54Smws 	xip->xi_data = data;
776d9638e54Smws 	xip->xi_version = FM_RSRC_XPRT_VERSION;
777d9638e54Smws 	xip->xi_flags = flags;
778d9638e54Smws 
779d9638e54Smws 	/*
780d9638e54Smws 	 * Grab fmd.d_xprt_lock to block fmd_xprt_suspend_all() and then create
781d9638e54Smws 	 * a transport ID and make it visible in fmd.d_xprt_ids.  If transports
782d9638e54Smws 	 * were previously suspended, set the FMD_XPRT_DSUSPENDED flag on us to
783d9638e54Smws 	 * ensure that this transport will not run until fmd_xprt_resume_all().
784d9638e54Smws 	 */
785d9638e54Smws 	(void) pthread_mutex_lock(&fmd.d_xprt_lock);
786d9638e54Smws 	xip->xi_id = fmd_idspace_alloc(fmd.d_xprt_ids, xip);
787d9638e54Smws 
788d9638e54Smws 	if (fmd.d_xprt_suspend != 0)
789d9638e54Smws 		xip->xi_flags |= FMD_XPRT_DSUSPENDED;
790d9638e54Smws 
791d9638e54Smws 	(void) pthread_mutex_unlock(&fmd.d_xprt_lock);
792d9638e54Smws 
793d9638e54Smws 	/*
794d9638e54Smws 	 * If the module has not yet finished _fmd_init(), set the ISUSPENDED
795d9638e54Smws 	 * bit so that fmdo_send() is not called until _fmd_init() completes.
796d9638e54Smws 	 */
797d9638e54Smws 	if (!(mp->mod_flags & FMD_MOD_INIT))
798d9638e54Smws 		xip->xi_flags |= FMD_XPRT_ISUSPENDED;
799d9638e54Smws 
800d9638e54Smws 	/*
801d9638e54Smws 	 * Initialize the transport statistics that we keep on behalf of fmd.
802d9638e54Smws 	 * These are set up using a template defined at the top of this file.
803d9638e54Smws 	 * We rename each statistic with a prefix ensuring its uniqueness.
804d9638e54Smws 	 */
805d9638e54Smws 	statc = sizeof (_fmd_xprt_stat_tmpl) / sizeof (fmd_stat_t);
806d9638e54Smws 	statv = fmd_alloc(sizeof (_fmd_xprt_stat_tmpl), FMD_SLEEP);
807d9638e54Smws 	bcopy(&_fmd_xprt_stat_tmpl, statv, sizeof (_fmd_xprt_stat_tmpl));
808d9638e54Smws 
809d9638e54Smws 	for (i = 0; i < statc; i++) {
810d9638e54Smws 		(void) snprintf(statv[i].fmds_name,
811d9638e54Smws 		    sizeof (statv[i].fmds_name), "fmd.xprt.%u.%s", xip->xi_id,
812d9638e54Smws 		    ((fmd_stat_t *)&_fmd_xprt_stat_tmpl + i)->fmds_name);
813d9638e54Smws 	}
814d9638e54Smws 
815d9638e54Smws 	xip->xi_stats = (fmd_xprt_stat_t *)fmd_ustat_insert(
816d9638e54Smws 	    mp->mod_ustat, FMD_USTAT_NOALLOC, statc, statv, NULL);
817d9638e54Smws 
818d9638e54Smws 	if (xip->xi_stats == NULL)
819d9638e54Smws 		fmd_panic("failed to create xi_stats (%p)\n", (void *)statv);
820d9638e54Smws 
821d9638e54Smws 	xip->xi_stats->xs_module.fmds_value.str =
822d9638e54Smws 	    fmd_strdup(mp->mod_name, FMD_SLEEP);
823d9638e54Smws 
824d9638e54Smws 	if (xip->xi_auth != NULL)
825d9638e54Smws 		fmd_xprt_authupdate(xip);
826d9638e54Smws 
827d9638e54Smws 	/*
828d9638e54Smws 	 * Create the outbound eventq for this transport and link to its stats.
829d9638e54Smws 	 * If any suspend bits were set above, suspend the eventq immediately.
830d9638e54Smws 	 */
831d9638e54Smws 	xip->xi_queue = fmd_eventq_create(mp, &xip->xi_stats->xs_evqstat,
832d9638e54Smws 	    &xip->xi_stats_lock, mp->mod_stats->ms_xprtqlimit.fmds_value.ui32);
833d9638e54Smws 
834d9638e54Smws 	if (xip->xi_flags & FMD_XPRT_SMASK)
835d9638e54Smws 		fmd_eventq_suspend(xip->xi_queue);
836d9638e54Smws 
837d9638e54Smws 	/*
838d9638e54Smws 	 * Create our subscription hashes: local subscriptions go to xi_queue,
839d9638e54Smws 	 * remote subscriptions are tracked only for protocol requests, and
840d9638e54Smws 	 * pending unsubscriptions are associated with the /dev/null eventq.
841d9638e54Smws 	 */
842d9638e54Smws 	fmd_xprt_class_hash_create(&xip->xi_lsub, xip->xi_queue);
843d9638e54Smws 	fmd_xprt_class_hash_create(&xip->xi_rsub, NULL);
844d9638e54Smws 	fmd_xprt_class_hash_create(&xip->xi_usub, fmd.d_rmod->mod_queue);
845d9638e54Smws 
846d9638e54Smws 	/*
847d9638e54Smws 	 * Determine our initial state based upon the creation flags.  If we're
848d9638e54Smws 	 * read-only, go directly to RUN.  If we're accepting a new connection,
849d9638e54Smws 	 * wait for a SYN.  Otherwise send a SYN and wait for an ACK.
850d9638e54Smws 	 */
851084b846fSStephen Hanson 	if ((flags & FMD_XPRT_RDWR) == FMD_XPRT_RDONLY) {
852084b846fSStephen Hanson 		/*
853084b846fSStephen Hanson 		 * Send the list.suspects across here for readonly transports.
854084b846fSStephen Hanson 		 * For read-write transport they will be sent on transition to
855084b846fSStephen Hanson 		 * RUN state in fmd_xprt_event_run().
856084b846fSStephen Hanson 		 */
857084b846fSStephen Hanson 		fmd_case_hash_apply(fmd.d_cases, fmd_xprt_send_case_ro, mp);
858d9638e54Smws 		fmd_xprt_transition(xip, _fmd_xprt_state_run, "RUN");
859084b846fSStephen Hanson 	} else if (flags & FMD_XPRT_ACCEPT)
860d9638e54Smws 		fmd_xprt_transition(xip, _fmd_xprt_state_syn, "SYN");
861d9638e54Smws 	else
862d9638e54Smws 		fmd_xprt_transition(xip, _fmd_xprt_state_ack, "ACK");
863d9638e54Smws 
864d9638e54Smws 	/*
865d9638e54Smws 	 * If client.xprtlog is set to TRUE, create a debugging log for the
866d9638e54Smws 	 * events received by the transport in var/fm/fmd/xprt/.
867d9638e54Smws 	 */
868d9638e54Smws 	(void) fmd_conf_getprop(fmd.d_conf, "client.xprtlog", &i);
869d9638e54Smws 	(void) fmd_conf_getprop(fmd.d_conf, "log.xprt", &s);
870d9638e54Smws 
871d9638e54Smws 	if (i) {
872d9638e54Smws 		(void) snprintf(buf, sizeof (buf), "%s/%u.log", s, xip->xi_id);
873d9638e54Smws 		xip->xi_log = fmd_log_open(fmd.d_rootdir, buf, FMD_LOG_XPRT);
874d9638e54Smws 	}
875d9638e54Smws 
876d9638e54Smws 	ASSERT(fmd_module_locked(mp));
877d9638e54Smws 	fmd_list_append(&mp->mod_transports, xip);
878d9638e54Smws 
879d9638e54Smws 	(void) pthread_mutex_lock(&mp->mod_stats_lock);
880d9638e54Smws 	mp->mod_stats->ms_xprtopen.fmds_value.ui32++;
881d9638e54Smws 	(void) pthread_mutex_unlock(&mp->mod_stats_lock);
882d9638e54Smws 
883d9638e54Smws 	/*
884d9638e54Smws 	 * If this is a read-only transport, return without creating a send
885d9638e54Smws 	 * queue thread and setting up any connection events in our queue.
886d9638e54Smws 	 */
887d9638e54Smws 	if ((flags & FMD_XPRT_RDWR) == FMD_XPRT_RDONLY)
888d9638e54Smws 		goto out;
889d9638e54Smws 
890d9638e54Smws 	/*
891d9638e54Smws 	 * Once the transport is fully initialized, create a send queue thread
892d9638e54Smws 	 * and start any connect events flowing to complete our initialization.
893d9638e54Smws 	 */
894d9638e54Smws 	if ((xip->xi_thread = fmd_thread_create(mp,
895d9638e54Smws 	    (fmd_thread_f *)fmd_xprt_send, xip)) == NULL) {
896d9638e54Smws 
897d9638e54Smws 		fmd_error(EFMD_XPRT_THR,
898d9638e54Smws 		    "failed to create thread for transport %u", xip->xi_id);
899d9638e54Smws 
900d9638e54Smws 		fmd_xprt_destroy((fmd_xprt_t *)xip);
901d9638e54Smws 		(void) fmd_set_errno(EFMD_XPRT_THR);
902d9638e54Smws 		return (NULL);
903d9638e54Smws 	}
904d9638e54Smws 
905d9638e54Smws 	/*
906d9638e54Smws 	 * If the transport is not being opened to accept an inbound connect,
907d9638e54Smws 	 * start an outbound connection by enqueuing a SYN event for our peer.
908d9638e54Smws 	 */
909d9638e54Smws 	if (!(flags & FMD_XPRT_ACCEPT)) {
910d9638e54Smws 		nvl = fmd_protocol_xprt_ctl(mp,
911d9638e54Smws 		    "resource.fm.xprt.syn", FM_RSRC_XPRT_VERSION);
912d9638e54Smws 
913d9638e54Smws 		(void) nvlist_lookup_string(nvl, FM_CLASS, &s);
914d9638e54Smws 		e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, s);
915d9638e54Smws 		fmd_eventq_insert_at_time(xip->xi_queue, e);
916d9638e54Smws 	}
917d9638e54Smws out:
918d9638e54Smws 	fmd_dprintf(FMD_DBG_XPRT, "opened transport %u\n", xip->xi_id);
919d9638e54Smws 	return ((fmd_xprt_t *)xip);
920d9638e54Smws }
921d9638e54Smws 
922d9638e54Smws void
923d9638e54Smws fmd_xprt_destroy(fmd_xprt_t *xp)
924d9638e54Smws {
925d9638e54Smws 	fmd_xprt_impl_t *xip = (fmd_xprt_impl_t *)xp;
926d9638e54Smws 	fmd_module_t *mp = xip->xi_queue->eq_mod;
927d9638e54Smws 	uint_t id = xip->xi_id;
928d9638e54Smws 
929d9638e54Smws 	fmd_case_impl_t *cip, *nip;
930d9638e54Smws 	fmd_stat_t *sp;
931d9638e54Smws 	uint_t i, n;
932d9638e54Smws 
933d9638e54Smws 	ASSERT(fmd_module_locked(mp));
934d9638e54Smws 	fmd_list_delete(&mp->mod_transports, xip);
935d9638e54Smws 
936d9638e54Smws 	(void) pthread_mutex_lock(&mp->mod_stats_lock);
937d9638e54Smws 	mp->mod_stats->ms_xprtopen.fmds_value.ui32--;
938d9638e54Smws 	(void) pthread_mutex_unlock(&mp->mod_stats_lock);
939d9638e54Smws 
940d9638e54Smws 	(void) pthread_mutex_lock(&xip->xi_lock);
941d9638e54Smws 
942d9638e54Smws 	while (xip->xi_busy != 0)
943d9638e54Smws 		(void) pthread_cond_wait(&xip->xi_cv, &xip->xi_lock);
944d9638e54Smws 
945d9638e54Smws 	/*
946d9638e54Smws 	 * Remove the transport from global visibility, cancel its send-side
947d9638e54Smws 	 * thread, join with it, and then remove the transport from module
948d9638e54Smws 	 * visibility.  Once all this is done, destroy and free the transport.
949d9638e54Smws 	 */
950d9638e54Smws 	(void) fmd_idspace_free(fmd.d_xprt_ids, xip->xi_id);
951d9638e54Smws 
952d9638e54Smws 	if (xip->xi_thread != NULL) {
953d9638e54Smws 		fmd_eventq_abort(xip->xi_queue);
9548e7248e5SStephen Hanson 		fmd_module_unlock(mp);
955d9638e54Smws 		fmd_thread_destroy(xip->xi_thread, FMD_THREAD_JOIN);
9568e7248e5SStephen Hanson 		fmd_module_lock(mp);
957d9638e54Smws 	}
958d9638e54Smws 
959d9638e54Smws 	if (xip->xi_log != NULL)
960d9638e54Smws 		fmd_log_rele(xip->xi_log);
961d9638e54Smws 
962d9638e54Smws 	/*
963d9638e54Smws 	 * Release every case handle in the module that was cached by this
964d9638e54Smws 	 * transport.  This will result in these cases disappearing from the
965cbf75e67SStephen Hanson 	 * local case hash so that fmd_case_uuclose() and fmd_case_repaired()
966cbf75e67SStephen Hanson 	 * etc can no longer be used.
967d9638e54Smws 	 */
968d9638e54Smws 	for (cip = fmd_list_next(&mp->mod_cases); cip != NULL; cip = nip) {
969d9638e54Smws 		nip = fmd_list_next(cip);
970d9638e54Smws 		if (cip->ci_xprt == xp)
971cbf75e67SStephen Hanson 			fmd_case_discard((fmd_case_t *)cip, B_TRUE);
972d9638e54Smws 	}
973d9638e54Smws 
974d9638e54Smws 	/*
975d9638e54Smws 	 * Destroy every class in the various subscription hashes and remove
976d9638e54Smws 	 * any corresponding subscriptions from the event dispatch queue.
977d9638e54Smws 	 */
978d9638e54Smws 	fmd_xprt_class_hash_destroy(&xip->xi_lsub);
979d9638e54Smws 	fmd_xprt_class_hash_destroy(&xip->xi_rsub);
980d9638e54Smws 	fmd_xprt_class_hash_destroy(&xip->xi_usub);
981d9638e54Smws 
982d9638e54Smws 	/*
9838f4d6bb2Sstephh 	 * Uniquify the stat names exactly as was done in fmd_xprt_create()
9848f4d6bb2Sstephh 	 * before calling fmd_ustat_insert(), otherwise fmd_ustat_delete()
9858f4d6bb2Sstephh 	 * won't find the entries in the hash table.
986d9638e54Smws 	 */
9878f4d6bb2Sstephh 	n = sizeof (_fmd_xprt_stat_tmpl) / sizeof (fmd_stat_t);
9888f4d6bb2Sstephh 	sp = fmd_alloc(sizeof (_fmd_xprt_stat_tmpl), FMD_SLEEP);
9898f4d6bb2Sstephh 	bcopy(&_fmd_xprt_stat_tmpl, sp, sizeof (_fmd_xprt_stat_tmpl));
9908f4d6bb2Sstephh 	for (i = 0; i < n; i++) {
9918f4d6bb2Sstephh 		(void) snprintf(sp[i].fmds_name,
9928f4d6bb2Sstephh 		    sizeof (sp[i].fmds_name), "fmd.xprt.%u.%s", xip->xi_id,
9938f4d6bb2Sstephh 		    ((fmd_stat_t *)&_fmd_xprt_stat_tmpl + i)->fmds_name);
994d9638e54Smws 	}
9958f4d6bb2Sstephh 	fmd_ustat_delete(mp->mod_ustat, n, sp);
9968f4d6bb2Sstephh 	fmd_free(sp, sizeof (_fmd_xprt_stat_tmpl));
997d9638e54Smws 
998d9638e54Smws 	fmd_free(xip->xi_stats, sizeof (fmd_xprt_stat_t));
999d9638e54Smws 	fmd_eventq_destroy(xip->xi_queue);
1000d9638e54Smws 	nvlist_free(xip->xi_auth);
1001d9638e54Smws 	fmd_free(xip, sizeof (fmd_xprt_impl_t));
1002d9638e54Smws 
1003d9638e54Smws 	fmd_dprintf(FMD_DBG_XPRT, "closed transport %u\n", id);
1004d9638e54Smws }
1005d9638e54Smws 
1006d9638e54Smws void
1007d9638e54Smws fmd_xprt_xsuspend(fmd_xprt_t *xp, uint_t flags)
1008d9638e54Smws {
1009d9638e54Smws 	fmd_xprt_impl_t *xip = (fmd_xprt_impl_t *)xp;
1010d9638e54Smws 	uint_t oflags;
1011d9638e54Smws 
1012d9638e54Smws 	ASSERT((flags & ~FMD_XPRT_SMASK) == 0);
1013d9638e54Smws 	(void) pthread_mutex_lock(&xip->xi_lock);
1014d9638e54Smws 
1015d9638e54Smws 	oflags = xip->xi_flags;
1016d9638e54Smws 	xip->xi_flags |= flags;
1017d9638e54Smws 
1018d9638e54Smws 	if (!(oflags & FMD_XPRT_SMASK) && (xip->xi_flags & FMD_XPRT_SMASK) != 0)
1019d9638e54Smws 		fmd_eventq_suspend(xip->xi_queue);
1020d9638e54Smws 
1021d9638e54Smws 	(void) pthread_cond_broadcast(&xip->xi_cv);
1022d9638e54Smws 
1023d9638e54Smws 	while (xip->xi_busy != 0)
1024d9638e54Smws 		(void) pthread_cond_wait(&xip->xi_cv, &xip->xi_lock);
1025d9638e54Smws 
1026d9638e54Smws 	(void) pthread_mutex_unlock(&xip->xi_lock);
1027d9638e54Smws }
1028d9638e54Smws 
1029d9638e54Smws void
1030d9638e54Smws fmd_xprt_xresume(fmd_xprt_t *xp, uint_t flags)
1031d9638e54Smws {
1032d9638e54Smws 	fmd_xprt_impl_t *xip = (fmd_xprt_impl_t *)xp;
1033d9638e54Smws 	uint_t oflags;
1034d9638e54Smws 
1035d9638e54Smws 	ASSERT((flags & ~FMD_XPRT_SMASK) == 0);
1036d9638e54Smws 	(void) pthread_mutex_lock(&xip->xi_lock);
1037d9638e54Smws 
1038d9638e54Smws 	oflags = xip->xi_flags;
1039d9638e54Smws 	xip->xi_flags &= ~flags;
1040d9638e54Smws 
1041d9638e54Smws 	if ((oflags & FMD_XPRT_SMASK) != 0 && !(xip->xi_flags & FMD_XPRT_SMASK))
1042d9638e54Smws 		fmd_eventq_resume(xip->xi_queue);
1043d9638e54Smws 
1044d9638e54Smws 	(void) pthread_cond_broadcast(&xip->xi_cv);
1045d9638e54Smws 	(void) pthread_mutex_unlock(&xip->xi_lock);
1046d9638e54Smws }
1047d9638e54Smws 
1048d9638e54Smws void
1049d9638e54Smws fmd_xprt_send(fmd_xprt_t *xp)
1050d9638e54Smws {
1051d9638e54Smws 	fmd_xprt_impl_t *xip = (fmd_xprt_impl_t *)xp;
1052d9638e54Smws 	fmd_module_t *mp = xip->xi_queue->eq_mod;
1053d9638e54Smws 	fmd_event_t *ep;
1054d9638e54Smws 	int err;
1055d9638e54Smws 
1056d9638e54Smws 	while ((ep = fmd_eventq_delete(xip->xi_queue)) != NULL) {
1057d9638e54Smws 		if (FMD_EVENT_TTL(ep) == 0) {
1058d9638e54Smws 			fmd_event_rele(ep);
1059d9638e54Smws 			continue;
1060d9638e54Smws 		}
1061d9638e54Smws 
1062d9638e54Smws 		fmd_dprintf(FMD_DBG_XPRT, "xprt %u sending %s\n",
1063d9638e54Smws 		    xip->xi_id, (char *)FMD_EVENT_DATA(ep));
1064d9638e54Smws 
1065d9638e54Smws 		err = mp->mod_ops->mop_transport(mp, xp, ep);
1066d9638e54Smws 		fmd_eventq_done(xip->xi_queue);
1067d9638e54Smws 
1068d9638e54Smws 		if (err == FMD_SEND_RETRY) {
1069d9638e54Smws 			fmd_eventq_insert_at_time(xip->xi_queue, ep);
1070d9638e54Smws 			(void) pthread_mutex_lock(&xip->xi_stats_lock);
1071d9638e54Smws 			xip->xi_stats->xs_retried.fmds_value.ui64++;
1072d9638e54Smws 			(void) pthread_mutex_unlock(&xip->xi_stats_lock);
1073d9638e54Smws 		}
1074d9638e54Smws 
1075d9638e54Smws 		if (err != FMD_SEND_SUCCESS && err != FMD_SEND_RETRY) {
1076d9638e54Smws 			(void) pthread_mutex_lock(&xip->xi_stats_lock);
1077d9638e54Smws 			xip->xi_stats->xs_lost.fmds_value.ui64++;
1078d9638e54Smws 			(void) pthread_mutex_unlock(&xip->xi_stats_lock);
1079d9638e54Smws 		}
1080d9638e54Smws 
1081d9638e54Smws 		fmd_event_rele(ep);
1082d9638e54Smws 	}
1083d9638e54Smws }
1084d9638e54Smws 
1085cbf75e67SStephen Hanson /*
1086cbf75e67SStephen Hanson  * This function creates a local suspect list. This is used when a suspect list
1087cbf75e67SStephen Hanson  * is created directly by an external source like fminject.
1088cbf75e67SStephen Hanson  */
1089cbf75e67SStephen Hanson static void
1090cbf75e67SStephen Hanson fmd_xprt_list_suspect_local(fmd_xprt_t *xp, nvlist_t *nvl)
1091cbf75e67SStephen Hanson {
1092cbf75e67SStephen Hanson 	nvlist_t **nvlp;
1093cbf75e67SStephen Hanson 	nvlist_t *de_fmri, *de_fmri_dup = NULL;
1094cbf75e67SStephen Hanson 	int64_t *diag_time;
1095cbf75e67SStephen Hanson 	char *code = NULL;
1096cbf75e67SStephen Hanson 	fmd_xprt_impl_t *xip = (fmd_xprt_impl_t *)xp;
1097cbf75e67SStephen Hanson 	fmd_case_t *cp;
1098cbf75e67SStephen Hanson 	uint_t nelem = 0, nelem2 = 0, i;
1099540db9a9SStephen Hanson 	boolean_t injected;
1100cbf75e67SStephen Hanson 
1101cbf75e67SStephen Hanson 	fmd_module_lock(xip->xi_queue->eq_mod);
1102f6e214c7SGavin Maltby 	cp = fmd_case_create(xip->xi_queue->eq_mod, NULL, NULL);
1103cbf75e67SStephen Hanson 	if (cp == NULL) {
1104cbf75e67SStephen Hanson 		fmd_module_unlock(xip->xi_queue->eq_mod);
1105cbf75e67SStephen Hanson 		return;
1106cbf75e67SStephen Hanson 	}
1107cbf75e67SStephen Hanson 
1108cbf75e67SStephen Hanson 	/*
1109cbf75e67SStephen Hanson 	 * copy diag_code if present
1110cbf75e67SStephen Hanson 	 */
1111cbf75e67SStephen Hanson 	(void) nvlist_lookup_string(nvl, FM_SUSPECT_DIAG_CODE, &code);
1112cbf75e67SStephen Hanson 	if (code != NULL) {
1113cbf75e67SStephen Hanson 		fmd_case_impl_t *cip = (fmd_case_impl_t *)cp;
1114cbf75e67SStephen Hanson 
1115cbf75e67SStephen Hanson 		cip->ci_precanned = 1;
1116cbf75e67SStephen Hanson 		fmd_case_setcode(cp, code);
1117cbf75e67SStephen Hanson 	}
1118cbf75e67SStephen Hanson 
1119cbf75e67SStephen Hanson 	/*
1120cbf75e67SStephen Hanson 	 * copy suspects
1121cbf75e67SStephen Hanson 	 */
1122cbf75e67SStephen Hanson 	(void) nvlist_lookup_nvlist_array(nvl, FM_SUSPECT_FAULT_LIST, &nvlp,
1123cbf75e67SStephen Hanson 	    &nelem);
1124cbf75e67SStephen Hanson 	for (i = 0; i < nelem; i++) {
1125cbf75e67SStephen Hanson 		nvlist_t *flt_copy, *asru = NULL, *fru = NULL, *rsrc = NULL;
1126cbf75e67SStephen Hanson 		topo_hdl_t *thp;
1127cbf75e67SStephen Hanson 		char *loc = NULL;
1128cbf75e67SStephen Hanson 		int err;
1129cbf75e67SStephen Hanson 
1130cbf75e67SStephen Hanson 		thp = fmd_fmri_topo_hold(TOPO_VERSION);
1131cbf75e67SStephen Hanson 		(void) nvlist_xdup(nvlp[i], &flt_copy, &fmd.d_nva);
1132cbf75e67SStephen Hanson 		(void) nvlist_lookup_nvlist(nvlp[i], FM_FAULT_RESOURCE, &rsrc);
1133cbf75e67SStephen Hanson 
1134cbf75e67SStephen Hanson 		/*
1135cbf75e67SStephen Hanson 		 * If no fru specified, get it from topo
1136cbf75e67SStephen Hanson 		 */
1137cbf75e67SStephen Hanson 		if (nvlist_lookup_nvlist(nvlp[i], FM_FAULT_FRU, &fru) != 0 &&
1138cbf75e67SStephen Hanson 		    rsrc && topo_fmri_fru(thp, rsrc, &fru, &err) == 0)
1139cbf75e67SStephen Hanson 			(void) nvlist_add_nvlist(flt_copy, FM_FAULT_FRU, fru);
1140cbf75e67SStephen Hanson 		/*
1141cbf75e67SStephen Hanson 		 * If no asru specified, get it from topo
1142cbf75e67SStephen Hanson 		 */
1143cbf75e67SStephen Hanson 		if (nvlist_lookup_nvlist(nvlp[i], FM_FAULT_ASRU, &asru) != 0 &&
1144cbf75e67SStephen Hanson 		    rsrc && topo_fmri_asru(thp, rsrc, &asru, &err) == 0)
1145cbf75e67SStephen Hanson 			(void) nvlist_add_nvlist(flt_copy, FM_FAULT_ASRU, asru);
1146cbf75e67SStephen Hanson 		/*
1147cbf75e67SStephen Hanson 		 * If no location specified, get it from topo
1148cbf75e67SStephen Hanson 		 */
1149cbf75e67SStephen Hanson 		if (nvlist_lookup_string(nvlp[i], FM_FAULT_LOCATION,
1150cbf75e67SStephen Hanson 		    &loc) != 0) {
1151cbf75e67SStephen Hanson 			if (fru && topo_fmri_label(thp, fru, &loc, &err) == 0)
1152cbf75e67SStephen Hanson 				(void) nvlist_add_string(flt_copy,
1153cbf75e67SStephen Hanson 				    FM_FAULT_LOCATION, loc);
1154cbf75e67SStephen Hanson 			else if (rsrc && topo_fmri_label(thp, rsrc, &loc,
1155cbf75e67SStephen Hanson 			    &err) == 0)
1156cbf75e67SStephen Hanson 				(void) nvlist_add_string(flt_copy,
1157cbf75e67SStephen Hanson 				    FM_FAULT_LOCATION, loc);
1158cbf75e67SStephen Hanson 			if (loc)
1159cbf75e67SStephen Hanson 				topo_hdl_strfree(thp, loc);
1160cbf75e67SStephen Hanson 		}
1161cbf75e67SStephen Hanson 		nvlist_free(fru);
1162cbf75e67SStephen Hanson 		nvlist_free(asru);
1163cbf75e67SStephen Hanson 		nvlist_free(rsrc);
1164cbf75e67SStephen Hanson 		fmd_fmri_topo_rele(thp);
1165cbf75e67SStephen Hanson 		fmd_case_insert_suspect(cp, flt_copy);
1166cbf75e67SStephen Hanson 	}
1167cbf75e67SStephen Hanson 
1168cbf75e67SStephen Hanson 	/*
1169cbf75e67SStephen Hanson 	 * copy diag_time if present
1170cbf75e67SStephen Hanson 	 */
1171cbf75e67SStephen Hanson 	if (nvlist_lookup_int64_array(nvl, FM_SUSPECT_DIAG_TIME, &diag_time,
1172cbf75e67SStephen Hanson 	    &nelem2) == 0 && nelem2 >= 2)
1173cbf75e67SStephen Hanson 		fmd_case_settime(cp, diag_time[0], diag_time[1]);
1174cbf75e67SStephen Hanson 
1175cbf75e67SStephen Hanson 	/*
1176cbf75e67SStephen Hanson 	 * copy DE fmri if present
1177cbf75e67SStephen Hanson 	 */
1178cbf75e67SStephen Hanson 	if (nvlist_lookup_nvlist(nvl, FM_SUSPECT_DE, &de_fmri) == 0) {
1179cbf75e67SStephen Hanson 		(void) nvlist_xdup(de_fmri, &de_fmri_dup, &fmd.d_nva);
1180cbf75e67SStephen Hanson 		fmd_case_set_de_fmri(cp, de_fmri_dup);
1181cbf75e67SStephen Hanson 	}
1182cbf75e67SStephen Hanson 
1183540db9a9SStephen Hanson 	/*
1184540db9a9SStephen Hanson 	 * copy injected if present
1185540db9a9SStephen Hanson 	 */
1186540db9a9SStephen Hanson 	if (nvlist_lookup_boolean_value(nvl, FM_SUSPECT_INJECTED,
1187540db9a9SStephen Hanson 	    &injected) == 0 && injected)
1188540db9a9SStephen Hanson 		fmd_case_set_injected(cp);
1189540db9a9SStephen Hanson 
1190cbf75e67SStephen Hanson 	fmd_case_transition(cp, FMD_CASE_SOLVED, FMD_CF_SOLVED);
1191cbf75e67SStephen Hanson 	fmd_module_unlock(xip->xi_queue->eq_mod);
1192cbf75e67SStephen Hanson }
1193cbf75e67SStephen Hanson 
1194cbf75e67SStephen Hanson /*
1195cbf75e67SStephen Hanson  * This function is called to create a proxy case on receipt of a list.suspect
1196cbf75e67SStephen Hanson  * from the diagnosing side of the transport.
1197cbf75e67SStephen Hanson  */
1198cbf75e67SStephen Hanson static void
1199cbf75e67SStephen Hanson fmd_xprt_list_suspect(fmd_xprt_t *xp, nvlist_t *nvl)
1200cbf75e67SStephen Hanson {
1201cbf75e67SStephen Hanson 	fmd_xprt_impl_t *xip = (fmd_xprt_impl_t *)xp;
1202cbf75e67SStephen Hanson 	nvlist_t **nvlp;
1203cbf75e67SStephen Hanson 	uint_t nelem = 0, nelem2 = 0, i;
1204cbf75e67SStephen Hanson 	int64_t *diag_time;
1205cbf75e67SStephen Hanson 	topo_hdl_t *thp;
1206cbf75e67SStephen Hanson 	char *class;
1207cbf75e67SStephen Hanson 	nvlist_t *rsrc, *asru, *de_fmri, *de_fmri_dup = NULL;
1208cbf75e67SStephen Hanson 	nvlist_t *flt_copy;
1209cbf75e67SStephen Hanson 	int err;
1210cbf75e67SStephen Hanson 	nvlist_t **asrua;
1211cbf75e67SStephen Hanson 	uint8_t *proxy_asru = NULL;
1212cbf75e67SStephen Hanson 	int got_proxy_asru = 0;
1213cbf75e67SStephen Hanson 	int got_hc_rsrc = 0;
1214ac83d604SStephen Hanson 	int got_hc_asru = 0;
1215cbf75e67SStephen Hanson 	int got_present_rsrc = 0;
1216cbf75e67SStephen Hanson 	uint8_t *diag_asru = NULL;
1217cbf75e67SStephen Hanson 	char *scheme;
1218cbf75e67SStephen Hanson 	uint8_t *statusp;
1219cbf75e67SStephen Hanson 	char *uuid, *code;
1220cbf75e67SStephen Hanson 	fmd_case_t *cp;
1221cbf75e67SStephen Hanson 	fmd_case_impl_t *cip;
1222cbf75e67SStephen Hanson 	int need_update = 0;
1223540db9a9SStephen Hanson 	boolean_t injected;
1224cbf75e67SStephen Hanson 
1225cbf75e67SStephen Hanson 	if (nvlist_lookup_string(nvl, FM_SUSPECT_UUID, &uuid) != 0)
1226cbf75e67SStephen Hanson 		return;
1227cbf75e67SStephen Hanson 	if (nvlist_lookup_string(nvl, FM_SUSPECT_DIAG_CODE, &code) != 0)
1228cbf75e67SStephen Hanson 		return;
1229cbf75e67SStephen Hanson 	(void) nvlist_lookup_nvlist_array(nvl, FM_SUSPECT_FAULT_LIST, &nvlp,
1230cbf75e67SStephen Hanson 	    &nelem);
1231cbf75e67SStephen Hanson 
1232cbf75e67SStephen Hanson 	/*
1233cbf75e67SStephen Hanson 	 * In order to implement FMD_XPRT_HCONLY and FMD_XPRT_HC_PRESENT_ONLY
1234cbf75e67SStephen Hanson 	 * etc we first scan the suspects to see if
1235cbf75e67SStephen Hanson 	 * - there was an asru in the received fault
1236cbf75e67SStephen Hanson 	 * - there was an hc-scheme resource in the received fault
1237cbf75e67SStephen Hanson 	 * - any hc-scheme resource in the received fault is present in the
1238cbf75e67SStephen Hanson 	 *   local topology
1239cbf75e67SStephen Hanson 	 * - any hc-scheme resource in the received fault has an asru in the
1240cbf75e67SStephen Hanson 	 *   local topology
1241cbf75e67SStephen Hanson 	 */
1242cbf75e67SStephen Hanson 	if (nelem > 0) {
1243cbf75e67SStephen Hanson 		asrua = fmd_zalloc(sizeof (nvlist_t *) * nelem, FMD_SLEEP);
1244cbf75e67SStephen Hanson 		proxy_asru = fmd_zalloc(sizeof (uint8_t) * nelem, FMD_SLEEP);
1245cbf75e67SStephen Hanson 		diag_asru = fmd_zalloc(sizeof (uint8_t) * nelem, FMD_SLEEP);
1246cbf75e67SStephen Hanson 		thp = fmd_fmri_topo_hold(TOPO_VERSION);
1247cbf75e67SStephen Hanson 		for (i = 0; i < nelem; i++) {
1248cbf75e67SStephen Hanson 			if (nvlist_lookup_nvlist(nvlp[i], FM_FAULT_ASRU,
1249cbf75e67SStephen Hanson 			    &asru) == 0 && asru != NULL)
1250cbf75e67SStephen Hanson 				diag_asru[i] = 1;
1251cbf75e67SStephen Hanson 			if (nvlist_lookup_string(nvlp[i], FM_CLASS,
1252cbf75e67SStephen Hanson 			    &class) != 0 || strncmp(class, "fault", 5) != 0)
1253cbf75e67SStephen Hanson 				continue;
1254cbf75e67SStephen Hanson 			/*
1255cbf75e67SStephen Hanson 			 * If there is an hc-scheme asru, use that to find the
1256cbf75e67SStephen Hanson 			 * real asru. Otherwise if there is an hc-scheme
1257cbf75e67SStephen Hanson 			 * resource, work out the old asru from that.
1258cbf75e67SStephen Hanson 			 * This order is to allow a two stage evaluation
1259cbf75e67SStephen Hanson 			 * of the asru where a fault in the diagnosing side
1260cbf75e67SStephen Hanson 			 * is in a component not visible to the proxy side,
1261cbf75e67SStephen Hanson 			 * but prevents a component that is visible from
1262cbf75e67SStephen Hanson 			 * working. So the diagnosing side sets the asru to
1263cbf75e67SStephen Hanson 			 * the latter component (in hc-scheme as the diagnosing
1264cbf75e67SStephen Hanson 			 * side doesn't know about the proxy side's virtual
1265cbf75e67SStephen Hanson 			 * schemes), and then the proxy side can convert that
1266cbf75e67SStephen Hanson 			 * to a suitable virtual scheme asru.
1267cbf75e67SStephen Hanson 			 */
1268cbf75e67SStephen Hanson 			if (nvlist_lookup_nvlist(nvlp[i], FM_FAULT_ASRU,
1269cbf75e67SStephen Hanson 			    &asru) == 0 && asru != NULL &&
1270cbf75e67SStephen Hanson 			    nvlist_lookup_string(asru, FM_FMRI_SCHEME,
1271cbf75e67SStephen Hanson 			    &scheme) == 0 &&
1272cbf75e67SStephen Hanson 			    strcmp(scheme, FM_FMRI_SCHEME_HC) == 0) {
1273ac83d604SStephen Hanson 				got_hc_asru = 1;
1274cbf75e67SStephen Hanson 				if (xip->xi_flags & FMD_XPRT_EXTERNAL)
1275cbf75e67SStephen Hanson 					continue;
12766e1fa242SStephen Hanson 				if (topo_fmri_present(thp, asru, &err) != 0)
1277cbf75e67SStephen Hanson 					got_present_rsrc = 1;
1278cbf75e67SStephen Hanson 				if (topo_fmri_asru(thp, asru, &asrua[i],
1279cbf75e67SStephen Hanson 				    &err) == 0) {
1280cbf75e67SStephen Hanson 					proxy_asru[i] =
1281cbf75e67SStephen Hanson 					    FMD_PROXY_ASRU_FROM_ASRU;
1282cbf75e67SStephen Hanson 					got_proxy_asru = 1;
1283cbf75e67SStephen Hanson 				}
1284cbf75e67SStephen Hanson 			} else if (nvlist_lookup_nvlist(nvlp[i],
1285cbf75e67SStephen Hanson 			    FM_FAULT_RESOURCE, &rsrc) == 0 && rsrc != NULL &&
1286cbf75e67SStephen Hanson 			    nvlist_lookup_string(rsrc, FM_FMRI_SCHEME,
1287cbf75e67SStephen Hanson 			    &scheme) == 0 &&
1288cbf75e67SStephen Hanson 			    strcmp(scheme, FM_FMRI_SCHEME_HC) == 0) {
1289cbf75e67SStephen Hanson 				got_hc_rsrc = 1;
1290cbf75e67SStephen Hanson 				if (xip->xi_flags & FMD_XPRT_EXTERNAL)
1291cbf75e67SStephen Hanson 					continue;
12926e1fa242SStephen Hanson 				if (topo_fmri_present(thp, rsrc, &err) != 0)
1293cbf75e67SStephen Hanson 					got_present_rsrc = 1;
1294cbf75e67SStephen Hanson 				if (topo_fmri_asru(thp, rsrc, &asrua[i],
1295cbf75e67SStephen Hanson 				    &err) == 0) {
1296cbf75e67SStephen Hanson 					proxy_asru[i] =
1297cbf75e67SStephen Hanson 					    FMD_PROXY_ASRU_FROM_RSRC;
1298cbf75e67SStephen Hanson 					got_proxy_asru = 1;
1299cbf75e67SStephen Hanson 				}
1300cbf75e67SStephen Hanson 			}
1301cbf75e67SStephen Hanson 		}
1302cbf75e67SStephen Hanson 		fmd_fmri_topo_rele(thp);
1303cbf75e67SStephen Hanson 	}
1304cbf75e67SStephen Hanson 
1305cbf75e67SStephen Hanson 	/*
1306cbf75e67SStephen Hanson 	 * If we're set up only to report hc-scheme faults, and
1307cbf75e67SStephen Hanson 	 * there aren't any, then just drop the event.
1308cbf75e67SStephen Hanson 	 */
1309ac83d604SStephen Hanson 	if (got_hc_rsrc == 0 && got_hc_asru == 0 &&
1310ac83d604SStephen Hanson 	    (xip->xi_flags & FMD_XPRT_HCONLY)) {
1311cbf75e67SStephen Hanson 		if (nelem > 0) {
1312cbf75e67SStephen Hanson 			fmd_free(proxy_asru, sizeof (uint8_t) * nelem);
1313cbf75e67SStephen Hanson 			fmd_free(diag_asru, sizeof (uint8_t) * nelem);
1314cbf75e67SStephen Hanson 			fmd_free(asrua, sizeof (nvlist_t *) * nelem);
1315cbf75e67SStephen Hanson 		}
1316cbf75e67SStephen Hanson 		return;
1317cbf75e67SStephen Hanson 	}
1318cbf75e67SStephen Hanson 
1319cbf75e67SStephen Hanson 	/*
1320cbf75e67SStephen Hanson 	 * If we're set up only to report locally present hc-scheme
1321cbf75e67SStephen Hanson 	 * faults, and there aren't any, then just drop the event.
1322cbf75e67SStephen Hanson 	 */
1323cbf75e67SStephen Hanson 	if (got_present_rsrc == 0 &&
1324cbf75e67SStephen Hanson 	    (xip->xi_flags & FMD_XPRT_HC_PRESENT_ONLY)) {
1325cbf75e67SStephen Hanson 		if (nelem > 0) {
1326cbf75e67SStephen Hanson 			for (i = 0; i < nelem; i++)
1327cbf75e67SStephen Hanson 				nvlist_free(asrua[i]);
1328cbf75e67SStephen Hanson 			fmd_free(proxy_asru, sizeof (uint8_t) * nelem);
1329cbf75e67SStephen Hanson 			fmd_free(diag_asru, sizeof (uint8_t) * nelem);
1330cbf75e67SStephen Hanson 			fmd_free(asrua, sizeof (nvlist_t *) * nelem);
1331cbf75e67SStephen Hanson 		}
1332cbf75e67SStephen Hanson 		return;
1333cbf75e67SStephen Hanson 	}
1334cbf75e67SStephen Hanson 
1335cbf75e67SStephen Hanson 	/*
1336cbf75e67SStephen Hanson 	 * If fmd_case_recreate() returns NULL, UUID is already known.
1337cbf75e67SStephen Hanson 	 */
1338cbf75e67SStephen Hanson 	fmd_module_lock(xip->xi_queue->eq_mod);
1339cbf75e67SStephen Hanson 	if ((cp = fmd_case_recreate(xip->xi_queue->eq_mod, xp,
1340cbf75e67SStephen Hanson 	    FMD_CASE_UNSOLVED, uuid, code)) == NULL) {
1341cbf75e67SStephen Hanson 		if (nelem > 0) {
1342cbf75e67SStephen Hanson 			for (i = 0; i < nelem; i++)
1343cbf75e67SStephen Hanson 				nvlist_free(asrua[i]);
1344cbf75e67SStephen Hanson 			fmd_free(proxy_asru, sizeof (uint8_t) * nelem);
1345cbf75e67SStephen Hanson 			fmd_free(diag_asru, sizeof (uint8_t) * nelem);
1346cbf75e67SStephen Hanson 			fmd_free(asrua, sizeof (nvlist_t *) * nelem);
1347cbf75e67SStephen Hanson 		}
1348cbf75e67SStephen Hanson 		fmd_module_unlock(xip->xi_queue->eq_mod);
1349cbf75e67SStephen Hanson 		return;
1350cbf75e67SStephen Hanson 	}
1351cbf75e67SStephen Hanson 
1352cbf75e67SStephen Hanson 	cip = (fmd_case_impl_t *)cp;
1353cbf75e67SStephen Hanson 	cip->ci_diag_asru = diag_asru;
1354cbf75e67SStephen Hanson 	cip->ci_proxy_asru = proxy_asru;
1355cbf75e67SStephen Hanson 	for (i = 0; i < nelem; i++) {
1356cbf75e67SStephen Hanson 		(void) nvlist_xdup(nvlp[i], &flt_copy, &fmd.d_nva);
1357cbf75e67SStephen Hanson 		if (proxy_asru[i] != FMD_PROXY_ASRU_NOT_NEEDED) {
1358cbf75e67SStephen Hanson 			/*
1359cbf75e67SStephen Hanson 			 * Copy suspects, but remove/replace asru first. Also if
1360cbf75e67SStephen Hanson 			 * the original asru was hc-scheme use that as resource.
1361cbf75e67SStephen Hanson 			 */
1362cbf75e67SStephen Hanson 			if (proxy_asru[i] == FMD_PROXY_ASRU_FROM_ASRU) {
1363cbf75e67SStephen Hanson 				(void) nvlist_remove(flt_copy,
1364cbf75e67SStephen Hanson 				    FM_FAULT_RESOURCE, DATA_TYPE_NVLIST);
1365cbf75e67SStephen Hanson 				(void) nvlist_lookup_nvlist(flt_copy,
1366cbf75e67SStephen Hanson 				    FM_FAULT_ASRU, &asru);
1367cbf75e67SStephen Hanson 				(void) nvlist_add_nvlist(flt_copy,
1368cbf75e67SStephen Hanson 				    FM_FAULT_RESOURCE, asru);
1369cbf75e67SStephen Hanson 			}
1370cbf75e67SStephen Hanson 			(void) nvlist_remove(flt_copy, FM_FAULT_ASRU,
1371cbf75e67SStephen Hanson 			    DATA_TYPE_NVLIST);
1372cbf75e67SStephen Hanson 			(void) nvlist_add_nvlist(flt_copy, FM_FAULT_ASRU,
1373cbf75e67SStephen Hanson 			    asrua[i]);
1374cbf75e67SStephen Hanson 			nvlist_free(asrua[i]);
1375ac83d604SStephen Hanson 		} else if (got_hc_asru == 0 &&
1376ac83d604SStephen Hanson 		    nvlist_lookup_nvlist(flt_copy, FM_FAULT_ASRU,
1377cbf75e67SStephen Hanson 		    &asru) == 0 && asru != NULL) {
1378cbf75e67SStephen Hanson 			/*
1379ac83d604SStephen Hanson 			 * If we have an asru from diag side, but it's not
1380ac83d604SStephen Hanson 			 * in hc scheme, then we can't be sure what it
1381ac83d604SStephen Hanson 			 * represents, so mark as no retire.
1382cbf75e67SStephen Hanson 			 */
1383cbf75e67SStephen Hanson 			(void) nvlist_add_boolean_value(flt_copy,
1384cbf75e67SStephen Hanson 			    FM_SUSPECT_RETIRE, B_FALSE);
1385cbf75e67SStephen Hanson 		}
1386cbf75e67SStephen Hanson 		fmd_case_insert_suspect(cp, flt_copy);
1387cbf75e67SStephen Hanson 	}
1388cbf75e67SStephen Hanson 	/*
1389cbf75e67SStephen Hanson 	 * copy diag_time
1390cbf75e67SStephen Hanson 	 */
1391cbf75e67SStephen Hanson 	if (nvlist_lookup_int64_array(nvl, FM_SUSPECT_DIAG_TIME, &diag_time,
1392cbf75e67SStephen Hanson 	    &nelem2) == 0 && nelem2 >= 2)
1393cbf75e67SStephen Hanson 		fmd_case_settime(cp, diag_time[0], diag_time[1]);
1394cbf75e67SStephen Hanson 	/*
1395cbf75e67SStephen Hanson 	 * copy DE fmri
1396cbf75e67SStephen Hanson 	 */
1397cbf75e67SStephen Hanson 	if (nvlist_lookup_nvlist(nvl, FM_SUSPECT_DE, &de_fmri) == 0) {
1398cbf75e67SStephen Hanson 		(void) nvlist_xdup(de_fmri, &de_fmri_dup, &fmd.d_nva);
1399cbf75e67SStephen Hanson 		fmd_case_set_de_fmri(cp, de_fmri_dup);
1400cbf75e67SStephen Hanson 	}
1401cbf75e67SStephen Hanson 
1402cbf75e67SStephen Hanson 	/*
1403540db9a9SStephen Hanson 	 * copy injected if present
1404540db9a9SStephen Hanson 	 */
1405540db9a9SStephen Hanson 	if (nvlist_lookup_boolean_value(nvl, FM_SUSPECT_INJECTED,
1406540db9a9SStephen Hanson 	    &injected) == 0 && injected)
1407540db9a9SStephen Hanson 		fmd_case_set_injected(cp);
1408540db9a9SStephen Hanson 
1409540db9a9SStephen Hanson 	/*
1410cbf75e67SStephen Hanson 	 * Transition to solved. This will log the suspect list and create
1411cbf75e67SStephen Hanson 	 * the resource cache entries.
1412cbf75e67SStephen Hanson 	 */
1413cbf75e67SStephen Hanson 	fmd_case_transition(cp, FMD_CASE_SOLVED, FMD_CF_SOLVED);
1414cbf75e67SStephen Hanson 
1415cbf75e67SStephen Hanson 	/*
1416cbf75e67SStephen Hanson 	 * Update status if it is not simply "all faulty" (can happen if
1417cbf75e67SStephen Hanson 	 * list.suspects are being re-sent when the transport has reconnected).
1418cbf75e67SStephen Hanson 	 */
1419cbf75e67SStephen Hanson 	(void) nvlist_lookup_uint8_array(nvl, FM_SUSPECT_FAULT_STATUS, &statusp,
1420cbf75e67SStephen Hanson 	    &nelem);
1421cbf75e67SStephen Hanson 	for (i = 0; i < nelem; i++) {
1422cbf75e67SStephen Hanson 		if ((statusp[i] & (FM_SUSPECT_FAULTY | FM_SUSPECT_UNUSABLE |
1423cbf75e67SStephen Hanson 		    FM_SUSPECT_NOT_PRESENT | FM_SUSPECT_DEGRADED)) !=
1424cbf75e67SStephen Hanson 		    FM_SUSPECT_FAULTY)
1425cbf75e67SStephen Hanson 			need_update = 1;
1426cbf75e67SStephen Hanson 	}
1427cbf75e67SStephen Hanson 	if (need_update) {
1428cbf75e67SStephen Hanson 		fmd_case_update_status(cp, statusp, cip->ci_proxy_asru,
1429cbf75e67SStephen Hanson 		    cip->ci_diag_asru);
1430cbf75e67SStephen Hanson 		fmd_case_update_containees(cp);
1431cbf75e67SStephen Hanson 		fmd_case_update(cp);
1432cbf75e67SStephen Hanson 	}
1433cbf75e67SStephen Hanson 
1434cbf75e67SStephen Hanson 	/*
1435cbf75e67SStephen Hanson 	 * if asru on proxy side, send an update back to the diagnosing side to
1436cbf75e67SStephen Hanson 	 * update UNUSABLE/DEGRADED.
1437cbf75e67SStephen Hanson 	 */
1438cbf75e67SStephen Hanson 	if (got_proxy_asru)
1439cbf75e67SStephen Hanson 		fmd_case_xprt_updated(cp);
1440cbf75e67SStephen Hanson 
1441cbf75e67SStephen Hanson 	if (nelem > 0)
1442cbf75e67SStephen Hanson 		fmd_free(asrua, sizeof (nvlist_t *) * nelem);
1443cbf75e67SStephen Hanson 	fmd_module_unlock(xip->xi_queue->eq_mod);
1444cbf75e67SStephen Hanson }
1445cbf75e67SStephen Hanson 
1446d9638e54Smws void
144725351652SVuong Nguyen fmd_xprt_recv(fmd_xprt_t *xp, nvlist_t *nvl, hrtime_t hrt, boolean_t logonly)
1448d9638e54Smws {
1449d9638e54Smws 	fmd_xprt_impl_t *xip = (fmd_xprt_impl_t *)xp;
1450d9638e54Smws 	const fmd_xprt_rule_t *xrp;
1451d9638e54Smws 	fmd_t *dp = &fmd;
1452d9638e54Smws 
1453d9638e54Smws 	fmd_event_t *e;
1454cbf75e67SStephen Hanson 	char *class, *uuid;
1455f6e214c7SGavin Maltby 	boolean_t isproto, isereport, isireport, ishvireport, issysevent;
1456d9638e54Smws 
1457d9638e54Smws 	uint64_t *tod;
1458d9638e54Smws 	uint8_t ttl;
1459d9638e54Smws 	uint_t n;
1460cbf75e67SStephen Hanson 	fmd_case_t *cp;
1461d9638e54Smws 
1462d9638e54Smws 	/*
1463d9638e54Smws 	 * Grab the transport lock and set the busy flag to indicate we are
1464d9638e54Smws 	 * busy receiving an event.  If [DI]SUSPEND is pending, wait until fmd
1465d9638e54Smws 	 * resumes the transport before continuing on with the receive.
1466d9638e54Smws 	 */
1467d9638e54Smws 	(void) pthread_mutex_lock(&xip->xi_lock);
1468d9638e54Smws 
1469724365f7Ssethg 	while (xip->xi_flags & (FMD_XPRT_DSUSPENDED | FMD_XPRT_ISUSPENDED)) {
1470724365f7Ssethg 
1471724365f7Ssethg 		if (fmd.d_signal != 0) {
1472724365f7Ssethg 			(void) pthread_mutex_unlock(&xip->xi_lock);
1473724365f7Ssethg 			return; /* fmd_destroy() is in progress */
1474724365f7Ssethg 		}
1475724365f7Ssethg 
1476d9638e54Smws 		(void) pthread_cond_wait(&xip->xi_cv, &xip->xi_lock);
1477724365f7Ssethg 	}
1478d9638e54Smws 
1479d9638e54Smws 	xip->xi_busy++;
1480d9638e54Smws 	ASSERT(xip->xi_busy != 0);
1481d9638e54Smws 
1482d9638e54Smws 	(void) pthread_mutex_unlock(&xip->xi_lock);
1483d9638e54Smws 
1484d9638e54Smws 	(void) pthread_mutex_lock(&xip->xi_stats_lock);
1485d9638e54Smws 	xip->xi_stats->xs_received.fmds_value.ui64++;
1486d9638e54Smws 	(void) pthread_mutex_unlock(&xip->xi_stats_lock);
1487d9638e54Smws 
1488d9638e54Smws 	if (nvlist_lookup_string(nvl, FM_CLASS, &class) != 0) {
1489d9638e54Smws 		fmd_error(EFMD_XPRT_PAYLOAD, "discarding nvlist %p: missing "
1490d9638e54Smws 		    "required \"%s\" payload element", (void *)nvl, FM_CLASS);
1491d9638e54Smws 
1492d9638e54Smws 		(void) pthread_mutex_lock(&xip->xi_stats_lock);
1493d9638e54Smws 		xip->xi_stats->xs_discarded.fmds_value.ui64++;
1494d9638e54Smws 		(void) pthread_mutex_unlock(&xip->xi_stats_lock);
1495d9638e54Smws 
1496d9638e54Smws 		nvlist_free(nvl);
1497d9638e54Smws 		goto done;
1498d9638e54Smws 	}
1499d9638e54Smws 
150025351652SVuong Nguyen 	fmd_dprintf(FMD_DBG_XPRT, "xprt %u %s %s\n", xip->xi_id,
150125351652SVuong Nguyen 	    ((logonly == FMD_B_TRUE) ? "logging" : "posting"), class);
150225351652SVuong Nguyen 
1503f6e214c7SGavin Maltby 	isereport = (strncmp(class, FM_EREPORT_CLASS ".",
1504f6e214c7SGavin Maltby 	    sizeof (FM_EREPORT_CLASS)) == 0) ? FMD_B_TRUE : FMD_B_FALSE;
1505f6e214c7SGavin Maltby 
1506f6e214c7SGavin Maltby 	isireport = (strncmp(class, FM_IREPORT_CLASS ".",
1507f6e214c7SGavin Maltby 	    sizeof (FM_IREPORT_CLASS)) == 0) ?  FMD_B_TRUE : FMD_B_FALSE;
1508f6e214c7SGavin Maltby 
1509f6e214c7SGavin Maltby 	issysevent = (strncmp(class, SYSEVENT_RSRC_CLASS,
1510f6e214c7SGavin Maltby 	    sizeof (SYSEVENT_RSRC_CLASS) - 1)) == 0 ? FMD_B_TRUE : FMD_B_FALSE;
1511f6e214c7SGavin Maltby 
1512f6e214c7SGavin Maltby 	if (isireport) {
1513f6e214c7SGavin Maltby 		char *pri;
1514f6e214c7SGavin Maltby 
1515f6e214c7SGavin Maltby 		if (nvlist_lookup_string(nvl, FM_IREPORT_PRIORITY, &pri) == 0 &&
1516f6e214c7SGavin Maltby 		    strncmp(pri, "high", 5) == 0) {
1517f6e214c7SGavin Maltby 			ishvireport = 1;
1518f6e214c7SGavin Maltby 		} else {
1519f6e214c7SGavin Maltby 			ishvireport = 0;
1520f6e214c7SGavin Maltby 		}
1521f6e214c7SGavin Maltby 	}
152225351652SVuong Nguyen 
152325351652SVuong Nguyen 	/*
152425351652SVuong Nguyen 	 * The logonly flag should only be set for ereports.
152525351652SVuong Nguyen 	 */
1526f6e214c7SGavin Maltby 	if (logonly == FMD_B_TRUE && isereport == FMD_B_FALSE) {
152725351652SVuong Nguyen 		fmd_error(EFMD_XPRT_INVAL, "discarding nvlist %p: "
152825351652SVuong Nguyen 		    "logonly flag is not valid for class %s",
152925351652SVuong Nguyen 		    (void *)nvl, class);
153025351652SVuong Nguyen 
153125351652SVuong Nguyen 		(void) pthread_mutex_lock(&xip->xi_stats_lock);
153225351652SVuong Nguyen 		xip->xi_stats->xs_discarded.fmds_value.ui64++;
153325351652SVuong Nguyen 		(void) pthread_mutex_unlock(&xip->xi_stats_lock);
153425351652SVuong Nguyen 
153525351652SVuong Nguyen 		nvlist_free(nvl);
153625351652SVuong Nguyen 		goto done;
153725351652SVuong Nguyen 	}
1538d9638e54Smws 
1539d9638e54Smws 	/*
1540d9638e54Smws 	 * If a time-to-live value is present in the event and is zero, drop
1541d9638e54Smws 	 * the event and bump xs_timeouts.  Otherwise decrement the TTL value.
1542d9638e54Smws 	 */
1543d9638e54Smws 	if (nvlist_lookup_uint8(nvl, FMD_EVN_TTL, &ttl) == 0) {
1544d9638e54Smws 		if (ttl == 0) {
1545d9638e54Smws 			fmd_dprintf(FMD_DBG_XPRT, "xprt %u nvlist %p (%s) "
1546d9638e54Smws 			    "timeout: event received with ttl=0\n",
1547d9638e54Smws 			    xip->xi_id, (void *)nvl, class);
1548d9638e54Smws 
1549d9638e54Smws 			(void) pthread_mutex_lock(&xip->xi_stats_lock);
1550d9638e54Smws 			xip->xi_stats->xs_timeouts.fmds_value.ui64++;
1551d9638e54Smws 			(void) pthread_mutex_unlock(&xip->xi_stats_lock);
1552d9638e54Smws 
1553d9638e54Smws 			nvlist_free(nvl);
1554d9638e54Smws 			goto done;
1555d9638e54Smws 		}
1556d9638e54Smws 		(void) nvlist_remove(nvl, FMD_EVN_TTL, DATA_TYPE_UINT8);
1557d9638e54Smws 		(void) nvlist_add_uint8(nvl, FMD_EVN_TTL, ttl - 1);
1558d9638e54Smws 	}
1559d9638e54Smws 
1560d9638e54Smws 	/*
1561d9638e54Smws 	 * If we are using the native system clock, the underlying transport
1562d9638e54Smws 	 * code can provide a tighter event time bound by telling us when the
1563d9638e54Smws 	 * event was enqueued.  If we're using simulated clocks, this time
1564d9638e54Smws 	 * has no meaning to us, so just reset the value to use HRT_NOW.
1565d9638e54Smws 	 */
1566d9638e54Smws 	if (dp->d_clockops != &fmd_timeops_native)
1567d9638e54Smws 		hrt = FMD_HRT_NOW;
1568d9638e54Smws 
1569d9638e54Smws 	/*
1570d9638e54Smws 	 * If an event's class is in the FMD_CTL_CLASS family, then create a
1571d9638e54Smws 	 * control event.  If a FMD_EVN_TOD member is found, create a protocol
1572d9638e54Smws 	 * event using this time.  Otherwise create a protocol event using hrt.
1573d9638e54Smws 	 */
157425351652SVuong Nguyen 	isproto = (strncmp(class, FMD_CTL_CLASS, FMD_CTL_CLASS_LEN) == 0) ?
157525351652SVuong Nguyen 	    FMD_B_FALSE : FMD_B_TRUE;
157625351652SVuong Nguyen 	if (isproto == FMD_B_FALSE)
1577d9638e54Smws 		e = fmd_event_create(FMD_EVT_CTL, hrt, nvl, fmd_ctl_init(nvl));
1578d9638e54Smws 	else if (nvlist_lookup_uint64_array(nvl, FMD_EVN_TOD, &tod, &n) != 0)
1579d9638e54Smws 		e = fmd_event_create(FMD_EVT_PROTOCOL, hrt, nvl, class);
1580d9638e54Smws 	else {
1581d9638e54Smws 		e = fmd_event_recreate(FMD_EVT_PROTOCOL,
1582d9638e54Smws 		    NULL, nvl, class, NULL, 0, 0);
1583d9638e54Smws 	}
1584d9638e54Smws 
1585d9638e54Smws 	/*
1586d9638e54Smws 	 * If the debug log is enabled, create a temporary event, log it to the
1587d9638e54Smws 	 * debug log, and then reset the underlying state of the event.
1588d9638e54Smws 	 */
1589d9638e54Smws 	if (xip->xi_log != NULL) {
1590d9638e54Smws 		fmd_event_impl_t *ep = (fmd_event_impl_t *)e;
1591d9638e54Smws 
1592d9638e54Smws 		fmd_log_append(xip->xi_log, e, NULL);
1593d9638e54Smws 
1594d9638e54Smws 		ep->ev_flags |= FMD_EVF_VOLATILE;
1595d9638e54Smws 		ep->ev_off = 0;
1596d9638e54Smws 		ep->ev_len = 0;
1597d9638e54Smws 
1598d9638e54Smws 		if (ep->ev_log != NULL) {
1599d9638e54Smws 			fmd_log_rele(ep->ev_log);
1600d9638e54Smws 			ep->ev_log = NULL;
1601d9638e54Smws 		}
1602d9638e54Smws 	}
1603d9638e54Smws 
1604d9638e54Smws 	/*
1605d9638e54Smws 	 * Iterate over the rules for the current state trying to match the
1606d9638e54Smws 	 * event class to one of our special rules.  If a rule is matched, the
1607d9638e54Smws 	 * event is consumed and not dispatched to other modules.  If the rule
1608d9638e54Smws 	 * set ends without matching an event, we fall through to dispatching.
1609d9638e54Smws 	 */
1610d9638e54Smws 	for (xrp = xip->xi_state; xrp->xr_class != NULL; xrp++) {
1611d9638e54Smws 		if (fmd_event_match(e, FMD_EVT_PROTOCOL, xrp->xr_class)) {
1612d9638e54Smws 			fmd_event_hold(e);
1613d9638e54Smws 			xrp->xr_func(xip, nvl);
1614d9638e54Smws 			fmd_event_rele(e);
1615d9638e54Smws 			goto done;
1616d9638e54Smws 		}
1617d9638e54Smws 	}
1618d9638e54Smws 
1619d9638e54Smws 	/*
1620f6e214c7SGavin Maltby 	 * Record ereports and ireports in the log.  This code will
1621d9638e54Smws 	 * be replaced later with a per-transport intent log instead.
1622d9638e54Smws 	 */
1623f6e214c7SGavin Maltby 	if (isereport == FMD_B_TRUE || isireport == FMD_B_TRUE ||
1624f6e214c7SGavin Maltby 	    issysevent == B_TRUE) {
1625f6e214c7SGavin Maltby 		pthread_rwlock_t *lockp;
1626f6e214c7SGavin Maltby 		fmd_log_t *lp;
1627f6e214c7SGavin Maltby 
162825351652SVuong Nguyen 		if (isereport == FMD_B_TRUE) {
1629f6e214c7SGavin Maltby 			lp = fmd.d_errlog;
1630f6e214c7SGavin Maltby 			lockp = &fmd.d_log_lock;
1631f6e214c7SGavin Maltby 		} else {
1632f6e214c7SGavin Maltby 			if (ishvireport || issysevent) {
1633f6e214c7SGavin Maltby 				lp = fmd.d_hvilog;
1634f6e214c7SGavin Maltby 				lockp = &fmd.d_hvilog_lock;
1635f6e214c7SGavin Maltby 			} else {
1636f6e214c7SGavin Maltby 				lp = fmd.d_ilog;
1637f6e214c7SGavin Maltby 				lockp = &fmd.d_ilog_lock;
1638f6e214c7SGavin Maltby 			}
1639f6e214c7SGavin Maltby 		}
1640f6e214c7SGavin Maltby 
1641f6e214c7SGavin Maltby 		(void) pthread_rwlock_rdlock(lockp);
1642f6e214c7SGavin Maltby 		fmd_log_append(lp, e, NULL);
1643f6e214c7SGavin Maltby 		(void) pthread_rwlock_unlock(lockp);
1644d9638e54Smws 	}
1645d9638e54Smws 
1646d9638e54Smws 	/*
1647d9638e54Smws 	 * If a list.suspect event is received, create a case for the specified
1648cbf75e67SStephen Hanson 	 * UUID in the case hash, with the transport module as its owner.
1649d9638e54Smws 	 */
1650cbf75e67SStephen Hanson 	if (fmd_event_match(e, FMD_EVT_PROTOCOL, FM_LIST_SUSPECT_CLASS)) {
1651cbf75e67SStephen Hanson 		if (xip->xi_flags & FMD_XPRT_CACHE_AS_LOCAL)
1652cbf75e67SStephen Hanson 			fmd_xprt_list_suspect_local(xp, nvl);
1653cbf75e67SStephen Hanson 		else
1654cbf75e67SStephen Hanson 			fmd_xprt_list_suspect(xp, nvl);
1655cbf75e67SStephen Hanson 		fmd_event_hold(e);
1656cbf75e67SStephen Hanson 		fmd_event_rele(e);
1657cbf75e67SStephen Hanson 		goto done;
1658d9638e54Smws 	}
1659d9638e54Smws 
1660cbf75e67SStephen Hanson 	/*
1661cbf75e67SStephen Hanson 	 * If a list.updated or list.repaired event is received, update the
1662cbf75e67SStephen Hanson 	 * resource cache status and the local case.
1663cbf75e67SStephen Hanson 	 */
1664cbf75e67SStephen Hanson 	if (fmd_event_match(e, FMD_EVT_PROTOCOL, FM_LIST_REPAIRED_CLASS) ||
1665cbf75e67SStephen Hanson 	    fmd_event_match(e, FMD_EVT_PROTOCOL, FM_LIST_UPDATED_CLASS)) {
1666cbf75e67SStephen Hanson 		uint8_t *statusp;
1667cbf75e67SStephen Hanson 		uint_t nelem = 0;
1668cbf75e67SStephen Hanson 
1669cbf75e67SStephen Hanson 		(void) nvlist_lookup_uint8_array(nvl, FM_SUSPECT_FAULT_STATUS,
1670cbf75e67SStephen Hanson 		    &statusp, &nelem);
1671cbf75e67SStephen Hanson 		fmd_module_lock(xip->xi_queue->eq_mod);
1672cbf75e67SStephen Hanson 		if (nvlist_lookup_string(nvl, FM_SUSPECT_UUID, &uuid) == 0 &&
1673cbf75e67SStephen Hanson 		    (cp = fmd_case_hash_lookup(fmd.d_cases, uuid)) != NULL) {
1674cbf75e67SStephen Hanson 			fmd_case_impl_t *cip = (fmd_case_impl_t *)cp;
1675cbf75e67SStephen Hanson 			if (cip->ci_xprt != NULL) {
1676cbf75e67SStephen Hanson 				fmd_case_update_status(cp, statusp,
1677cbf75e67SStephen Hanson 				    cip->ci_proxy_asru, cip->ci_diag_asru);
1678cbf75e67SStephen Hanson 				fmd_case_update_containees(cp);
1679cbf75e67SStephen Hanson 				fmd_case_update(cp);
1680cbf75e67SStephen Hanson 			}
1681cbf75e67SStephen Hanson 			fmd_case_rele(cp);
1682cbf75e67SStephen Hanson 		}
1683cbf75e67SStephen Hanson 		fmd_module_unlock(xip->xi_queue->eq_mod);
1684cbf75e67SStephen Hanson 		fmd_event_hold(e);
1685cbf75e67SStephen Hanson 		fmd_event_rele(e);
1686cbf75e67SStephen Hanson 		goto done;
1687cbf75e67SStephen Hanson 	}
1688cbf75e67SStephen Hanson 
1689cbf75e67SStephen Hanson 	/*
1690cbf75e67SStephen Hanson 	 * If a list.isolated event is received, update resource cache status
1691cbf75e67SStephen Hanson 	 */
1692cbf75e67SStephen Hanson 	if (fmd_event_match(e, FMD_EVT_PROTOCOL, FM_LIST_ISOLATED_CLASS)) {
1693cbf75e67SStephen Hanson 		uint8_t *statusp;
1694cbf75e67SStephen Hanson 		uint_t nelem = 0;
1695cbf75e67SStephen Hanson 
1696cbf75e67SStephen Hanson 		(void) nvlist_lookup_uint8_array(nvl, FM_SUSPECT_FAULT_STATUS,
1697cbf75e67SStephen Hanson 		    &statusp, &nelem);
1698cbf75e67SStephen Hanson 		fmd_module_lock(xip->xi_queue->eq_mod);
1699cbf75e67SStephen Hanson 		if (nvlist_lookup_string(nvl, FM_SUSPECT_UUID, &uuid) == 0 &&
1700cbf75e67SStephen Hanson 		    (cp = fmd_case_hash_lookup(fmd.d_cases, uuid)) != NULL) {
1701cbf75e67SStephen Hanson 			fmd_case_impl_t *cip = (fmd_case_impl_t *)cp;
1702cbf75e67SStephen Hanson 			if (cip->ci_xprt != NULL)
1703cbf75e67SStephen Hanson 				fmd_case_update_status(cp, statusp,
1704cbf75e67SStephen Hanson 				    cip->ci_proxy_asru, cip->ci_diag_asru);
1705cbf75e67SStephen Hanson 			fmd_case_rele(cp);
1706cbf75e67SStephen Hanson 		}
1707cbf75e67SStephen Hanson 		fmd_module_unlock(xip->xi_queue->eq_mod);
1708cbf75e67SStephen Hanson 		fmd_event_hold(e);
1709cbf75e67SStephen Hanson 		fmd_event_rele(e);
1710cbf75e67SStephen Hanson 		goto done;
1711cbf75e67SStephen Hanson 	}
1712cbf75e67SStephen Hanson 
1713cbf75e67SStephen Hanson 	/*
1714cbf75e67SStephen Hanson 	 * If a list.resolved event is received, resolve the local case.
1715cbf75e67SStephen Hanson 	 */
1716cbf75e67SStephen Hanson 	if (fmd_event_match(e, FMD_EVT_PROTOCOL, FM_LIST_RESOLVED_CLASS)) {
1717cbf75e67SStephen Hanson 		fmd_module_lock(xip->xi_queue->eq_mod);
1718cbf75e67SStephen Hanson 		if (nvlist_lookup_string(nvl, FM_SUSPECT_UUID, &uuid) == 0 &&
1719cbf75e67SStephen Hanson 		    (cp = fmd_case_hash_lookup(fmd.d_cases, uuid)) != NULL) {
1720cbf75e67SStephen Hanson 			fmd_case_impl_t *cip = (fmd_case_impl_t *)cp;
1721cbf75e67SStephen Hanson 			if (cip->ci_xprt != NULL)
1722cbf75e67SStephen Hanson 				fmd_case_transition(cp, (cip->ci_state ==
1723cbf75e67SStephen Hanson 				    FMD_CASE_REPAIRED) ? FMD_CASE_RESOLVED :
1724cbf75e67SStephen Hanson 				    (cip->ci_state == FMD_CASE_CLOSED) ?
1725cbf75e67SStephen Hanson 				    FMD_CASE_REPAIRED : FMD_CASE_CLOSE_WAIT,
1726cbf75e67SStephen Hanson 				    FMD_CF_RESOLVED);
1727cbf75e67SStephen Hanson 			fmd_case_rele(cp);
1728cbf75e67SStephen Hanson 		}
1729cbf75e67SStephen Hanson 		fmd_module_unlock(xip->xi_queue->eq_mod);
1730cbf75e67SStephen Hanson 		fmd_event_hold(e);
1731cbf75e67SStephen Hanson 		fmd_event_rele(e);
1732cbf75e67SStephen Hanson 		goto done;
1733cbf75e67SStephen Hanson 	}
1734cbf75e67SStephen Hanson 
1735cbf75e67SStephen Hanson 	if (logonly == FMD_B_TRUE || (xip->xi_flags & FMD_XPRT_EXTERNAL)) {
1736cbf75e67SStephen Hanson 		/*
1737cbf75e67SStephen Hanson 		 * Don't proxy ereports on an EXTERNAL transport - we won't
1738cbf75e67SStephen Hanson 		 * know how to diagnose them with the wrong topology. Note
1739cbf75e67SStephen Hanson 		 * that here (and above) we have to hold/release the event in
1740cbf75e67SStephen Hanson 		 * order for it to be freed.
1741cbf75e67SStephen Hanson 		 */
174225351652SVuong Nguyen 		fmd_event_hold(e);
174325351652SVuong Nguyen 		fmd_event_rele(e);
174425351652SVuong Nguyen 	} else if (isproto == FMD_B_TRUE)
1745d9638e54Smws 		fmd_dispq_dispatch(dp->d_disp, e, class);
1746d9638e54Smws 	else
1747d9638e54Smws 		fmd_modhash_dispatch(dp->d_mod_hash, e);
1748d9638e54Smws done:
1749d9638e54Smws 	(void) pthread_mutex_lock(&xip->xi_lock);
1750d9638e54Smws 
1751d9638e54Smws 	ASSERT(xip->xi_busy != 0);
1752d9638e54Smws 	xip->xi_busy--;
1753d9638e54Smws 
1754d9638e54Smws 	(void) pthread_cond_broadcast(&xip->xi_cv);
1755d9638e54Smws 	(void) pthread_mutex_unlock(&xip->xi_lock);
1756d9638e54Smws }
1757d9638e54Smws 
1758d9638e54Smws void
1759d9638e54Smws fmd_xprt_uuclose(fmd_xprt_t *xp, const char *uuid)
1760d9638e54Smws {
1761d9638e54Smws 	fmd_xprt_impl_t *xip = (fmd_xprt_impl_t *)xp;
1762d9638e54Smws 
1763d9638e54Smws 	fmd_event_t *e;
1764d9638e54Smws 	nvlist_t *nvl;
1765d9638e54Smws 	char *s;
1766d9638e54Smws 
1767cbf75e67SStephen Hanson 	if ((xip->xi_flags & FMD_XPRT_RDWR) == FMD_XPRT_RDONLY)
1768cbf75e67SStephen Hanson 		return; /* read-only transports do not proxy uuclose */
1769cbf75e67SStephen Hanson 
1770cbf75e67SStephen Hanson 	TRACE((FMD_DBG_XPRT, "xprt %u closing case %s\n", xip->xi_id, uuid));
1771d9638e54Smws 
1772d9638e54Smws 	nvl = fmd_protocol_xprt_uuclose(xip->xi_queue->eq_mod,
1773d9638e54Smws 	    "resource.fm.xprt.uuclose", xip->xi_version, uuid);
1774d9638e54Smws 
1775d9638e54Smws 	(void) nvlist_lookup_string(nvl, FM_CLASS, &s);
1776d9638e54Smws 	e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, s);
1777d9638e54Smws 	fmd_eventq_insert_at_time(xip->xi_queue, e);
1778d9638e54Smws }
1779d9638e54Smws 
1780d9638e54Smws /*
1781cbf75e67SStephen Hanson  * On proxy side, send back uuresolved request to diagnosing side
1782cbf75e67SStephen Hanson  */
1783cbf75e67SStephen Hanson void
1784cbf75e67SStephen Hanson fmd_xprt_uuresolved(fmd_xprt_t *xp, const char *uuid)
1785cbf75e67SStephen Hanson {
1786cbf75e67SStephen Hanson 	fmd_xprt_impl_t *xip = (fmd_xprt_impl_t *)xp;
1787cbf75e67SStephen Hanson 
1788cbf75e67SStephen Hanson 	fmd_event_t *e;
1789cbf75e67SStephen Hanson 	nvlist_t *nvl;
1790cbf75e67SStephen Hanson 	char *s;
1791cbf75e67SStephen Hanson 
1792cbf75e67SStephen Hanson 	if ((xip->xi_flags & FMD_XPRT_RDWR) == FMD_XPRT_RDONLY)
1793cbf75e67SStephen Hanson 		return; /* read-only transports do not proxy uuresolved */
1794cbf75e67SStephen Hanson 
1795cbf75e67SStephen Hanson 	TRACE((FMD_DBG_XPRT, "xprt %u resolving case %s\n", xip->xi_id, uuid));
1796cbf75e67SStephen Hanson 
1797cbf75e67SStephen Hanson 	nvl = fmd_protocol_xprt_uuresolved(xip->xi_queue->eq_mod,
1798cbf75e67SStephen Hanson 	    "resource.fm.xprt.uuresolved", xip->xi_version, uuid);
1799cbf75e67SStephen Hanson 
1800cbf75e67SStephen Hanson 	(void) nvlist_lookup_string(nvl, FM_CLASS, &s);
1801cbf75e67SStephen Hanson 	e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, s);
1802cbf75e67SStephen Hanson 	fmd_eventq_insert_at_time(xip->xi_queue, e);
1803cbf75e67SStephen Hanson }
1804cbf75e67SStephen Hanson 
1805cbf75e67SStephen Hanson /*
1806cbf75e67SStephen Hanson  * On proxy side, send back repair/acquit/etc request to diagnosing side
1807cbf75e67SStephen Hanson  */
1808cbf75e67SStephen Hanson void
1809cbf75e67SStephen Hanson fmd_xprt_updated(fmd_xprt_t *xp, const char *uuid, uint8_t *statusp,
1810cbf75e67SStephen Hanson 	uint8_t *has_asrup, uint_t nelem)
1811cbf75e67SStephen Hanson {
1812cbf75e67SStephen Hanson 	fmd_xprt_impl_t *xip = (fmd_xprt_impl_t *)xp;
1813cbf75e67SStephen Hanson 
1814cbf75e67SStephen Hanson 	fmd_event_t *e;
1815cbf75e67SStephen Hanson 	nvlist_t *nvl;
1816cbf75e67SStephen Hanson 	char *s;
1817cbf75e67SStephen Hanson 
1818cbf75e67SStephen Hanson 	if ((xip->xi_flags & FMD_XPRT_RDWR) == FMD_XPRT_RDONLY)
1819cbf75e67SStephen Hanson 		return; /* read-only transports do not support remote repairs */
1820cbf75e67SStephen Hanson 
1821cbf75e67SStephen Hanson 	TRACE((FMD_DBG_XPRT, "xprt %u updating case %s\n", xip->xi_id, uuid));
1822cbf75e67SStephen Hanson 
1823cbf75e67SStephen Hanson 	nvl = fmd_protocol_xprt_updated(xip->xi_queue->eq_mod,
1824cbf75e67SStephen Hanson 	    "resource.fm.xprt.updated", xip->xi_version, uuid, statusp,
1825cbf75e67SStephen Hanson 	    has_asrup, nelem);
1826cbf75e67SStephen Hanson 
1827cbf75e67SStephen Hanson 	(void) nvlist_lookup_string(nvl, FM_CLASS, &s);
1828cbf75e67SStephen Hanson 	e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, s);
1829cbf75e67SStephen Hanson 	fmd_eventq_insert_at_time(xip->xi_queue, e);
1830cbf75e67SStephen Hanson }
1831cbf75e67SStephen Hanson 
1832cbf75e67SStephen Hanson /*
1833d9638e54Smws  * Insert the specified class into our remote subscription hash.  If the class
1834d9638e54Smws  * is already present, bump the reference count; otherwise add it to the hash
1835d9638e54Smws  * and then enqueue an event for our remote peer to proxy our subscription.
1836d9638e54Smws  */
1837d9638e54Smws void
1838d9638e54Smws fmd_xprt_subscribe(fmd_xprt_t *xp, const char *class)
1839d9638e54Smws {
1840d9638e54Smws 	fmd_xprt_impl_t *xip = (fmd_xprt_impl_t *)xp;
1841d9638e54Smws 
1842d9638e54Smws 	uint_t refs;
1843d9638e54Smws 	nvlist_t *nvl;
1844d9638e54Smws 	fmd_event_t *e;
1845d9638e54Smws 	char *s;
1846d9638e54Smws 
1847d9638e54Smws 	if ((xip->xi_flags & FMD_XPRT_RDWR) == FMD_XPRT_RDONLY)
1848d9638e54Smws 		return; /* read-only transports do not proxy subscriptions */
1849d9638e54Smws 
1850d9638e54Smws 	if (!(xip->xi_flags & FMD_XPRT_SUBSCRIBER))
1851d9638e54Smws 		return; /* transport is not yet an active subscriber */
1852d9638e54Smws 
1853d9638e54Smws 	(void) pthread_mutex_lock(&xip->xi_lock);
1854d9638e54Smws 	refs = fmd_xprt_class_hash_insert(xip, &xip->xi_rsub, class);
1855d9638e54Smws 	(void) pthread_mutex_unlock(&xip->xi_lock);
1856d9638e54Smws 
1857d9638e54Smws 	if (refs > 1)
1858d9638e54Smws 		return; /* we've already asked our peer for this subscription */
1859d9638e54Smws 
1860d9638e54Smws 	fmd_dprintf(FMD_DBG_XPRT,
1861d9638e54Smws 	    "xprt %u subscribing to %s\n", xip->xi_id, class);
1862d9638e54Smws 
1863d9638e54Smws 	nvl = fmd_protocol_xprt_sub(xip->xi_queue->eq_mod,
1864d9638e54Smws 	    "resource.fm.xprt.subscribe", xip->xi_version, class);
1865d9638e54Smws 
1866d9638e54Smws 	(void) nvlist_lookup_string(nvl, FM_CLASS, &s);
1867d9638e54Smws 	e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, s);
1868d9638e54Smws 	fmd_eventq_insert_at_time(xip->xi_queue, e);
1869d9638e54Smws }
1870d9638e54Smws 
1871d9638e54Smws /*
1872d9638e54Smws  * Delete the specified class from the remote subscription hash.  If the
1873d9638e54Smws  * reference count drops to zero, ask our remote peer to unsubscribe by proxy.
1874d9638e54Smws  */
1875d9638e54Smws void
1876d9638e54Smws fmd_xprt_unsubscribe(fmd_xprt_t *xp, const char *class)
1877d9638e54Smws {
1878d9638e54Smws 	fmd_xprt_impl_t *xip = (fmd_xprt_impl_t *)xp;
1879d9638e54Smws 
1880d9638e54Smws 	uint_t refs;
1881d9638e54Smws 	nvlist_t *nvl;
1882d9638e54Smws 	fmd_event_t *e;
1883d9638e54Smws 	char *s;
1884d9638e54Smws 
1885d9638e54Smws 	if ((xip->xi_flags & FMD_XPRT_RDWR) == FMD_XPRT_RDONLY)
1886d9638e54Smws 		return; /* read-only transports do not proxy subscriptions */
1887d9638e54Smws 
1888d9638e54Smws 	if (!(xip->xi_flags & FMD_XPRT_SUBSCRIBER))
1889d9638e54Smws 		return; /* transport is not yet an active subscriber */
1890d9638e54Smws 
1891d9638e54Smws 	/*
1892d9638e54Smws 	 * If the subscription reference count drops to zero in xi_rsub, insert
1893d9638e54Smws 	 * an entry into the xi_usub hash indicating we await an unsuback event.
1894d9638e54Smws 	 */
1895d9638e54Smws 	(void) pthread_mutex_lock(&xip->xi_lock);
1896d9638e54Smws 
1897d9638e54Smws 	if ((refs = fmd_xprt_class_hash_delete(xip, &xip->xi_rsub, class)) == 0)
1898d9638e54Smws 		(void) fmd_xprt_class_hash_insert(xip, &xip->xi_usub, class);
1899d9638e54Smws 
1900d9638e54Smws 	(void) pthread_mutex_unlock(&xip->xi_lock);
1901d9638e54Smws 
1902d9638e54Smws 	if (refs != 0)
1903d9638e54Smws 		return; /* other subscriptions for this class still active */
1904d9638e54Smws 
1905d9638e54Smws 	fmd_dprintf(FMD_DBG_XPRT,
1906d9638e54Smws 	    "xprt %u unsubscribing from %s\n", xip->xi_id, class);
1907d9638e54Smws 
1908d9638e54Smws 	nvl = fmd_protocol_xprt_sub(xip->xi_queue->eq_mod,
1909d9638e54Smws 	    "resource.fm.xprt.unsubscribe", xip->xi_version, class);
1910d9638e54Smws 
1911d9638e54Smws 	(void) nvlist_lookup_string(nvl, FM_CLASS, &s);
1912d9638e54Smws 	e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, s);
1913d9638e54Smws 	fmd_eventq_insert_at_time(xip->xi_queue, e);
1914d9638e54Smws }
1915d9638e54Smws 
1916d9638e54Smws static void
1917d9638e54Smws fmd_xprt_subscribe_xid(fmd_idspace_t *ids, id_t id, void *class)
1918d9638e54Smws {
1919d9638e54Smws 	fmd_xprt_t *xp;
1920d9638e54Smws 
1921d9638e54Smws 	if ((xp = fmd_idspace_hold(ids, id)) != NULL) {
1922d9638e54Smws 		fmd_xprt_subscribe(xp, class);
1923d9638e54Smws 		fmd_idspace_rele(ids, id);
1924d9638e54Smws 	}
1925d9638e54Smws }
1926d9638e54Smws 
1927d9638e54Smws void
1928d9638e54Smws fmd_xprt_subscribe_all(const char *class)
1929d9638e54Smws {
1930d9638e54Smws 	fmd_idspace_t *ids = fmd.d_xprt_ids;
1931d9638e54Smws 
1932d9638e54Smws 	if (ids->ids_count != 0)
1933d9638e54Smws 		fmd_idspace_apply(ids, fmd_xprt_subscribe_xid, (void *)class);
1934d9638e54Smws }
1935d9638e54Smws 
1936d9638e54Smws static void
1937d9638e54Smws fmd_xprt_unsubscribe_xid(fmd_idspace_t *ids, id_t id, void *class)
1938d9638e54Smws {
1939d9638e54Smws 	fmd_xprt_t *xp;
1940d9638e54Smws 
1941d9638e54Smws 	if ((xp = fmd_idspace_hold(ids, id)) != NULL) {
1942d9638e54Smws 		fmd_xprt_unsubscribe(xp, class);
1943d9638e54Smws 		fmd_idspace_rele(ids, id);
1944d9638e54Smws 	}
1945d9638e54Smws }
1946d9638e54Smws 
1947d9638e54Smws void
1948d9638e54Smws fmd_xprt_unsubscribe_all(const char *class)
1949d9638e54Smws {
1950d9638e54Smws 	fmd_idspace_t *ids = fmd.d_xprt_ids;
1951d9638e54Smws 
1952d9638e54Smws 	if (ids->ids_count != 0)
1953d9638e54Smws 		fmd_idspace_apply(ids, fmd_xprt_unsubscribe_xid, (void *)class);
1954d9638e54Smws }
1955d9638e54Smws 
1956d9638e54Smws /*ARGSUSED*/
1957d9638e54Smws static void
1958d9638e54Smws fmd_xprt_suspend_xid(fmd_idspace_t *ids, id_t id, void *arg)
1959d9638e54Smws {
1960d9638e54Smws 	fmd_xprt_t *xp;
1961d9638e54Smws 
1962d9638e54Smws 	if ((xp = fmd_idspace_hold(ids, id)) != NULL) {
1963d9638e54Smws 		fmd_xprt_xsuspend(xp, FMD_XPRT_DSUSPENDED);
1964d9638e54Smws 		fmd_idspace_rele(ids, id);
1965d9638e54Smws 	}
1966d9638e54Smws }
1967d9638e54Smws 
1968d9638e54Smws void
1969d9638e54Smws fmd_xprt_suspend_all(void)
1970d9638e54Smws {
1971d9638e54Smws 	fmd_idspace_t *ids = fmd.d_xprt_ids;
1972d9638e54Smws 
1973d9638e54Smws 	(void) pthread_mutex_lock(&fmd.d_xprt_lock);
1974d9638e54Smws 
1975d9638e54Smws 	if (fmd.d_xprt_suspend++ != 0) {
1976d9638e54Smws 		(void) pthread_mutex_unlock(&fmd.d_xprt_lock);
1977d9638e54Smws 		return; /* already suspended */
1978d9638e54Smws 	}
1979d9638e54Smws 
1980d9638e54Smws 	if (ids->ids_count != 0)
1981d9638e54Smws 		fmd_idspace_apply(ids, fmd_xprt_suspend_xid, NULL);
1982d9638e54Smws 
1983d9638e54Smws 	(void) pthread_mutex_unlock(&fmd.d_xprt_lock);
1984d9638e54Smws }
1985d9638e54Smws 
1986d9638e54Smws /*ARGSUSED*/
1987d9638e54Smws static void
1988d9638e54Smws fmd_xprt_resume_xid(fmd_idspace_t *ids, id_t id, void *arg)
1989d9638e54Smws {
1990d9638e54Smws 	fmd_xprt_t *xp;
1991d9638e54Smws 
1992d9638e54Smws 	if ((xp = fmd_idspace_hold(ids, id)) != NULL) {
1993d9638e54Smws 		fmd_xprt_xresume(xp, FMD_XPRT_DSUSPENDED);
1994d9638e54Smws 		fmd_idspace_rele(ids, id);
1995d9638e54Smws 	}
1996d9638e54Smws }
1997d9638e54Smws 
1998d9638e54Smws void
1999d9638e54Smws fmd_xprt_resume_all(void)
2000d9638e54Smws {
2001d9638e54Smws 	fmd_idspace_t *ids = fmd.d_xprt_ids;
2002d9638e54Smws 
2003d9638e54Smws 	(void) pthread_mutex_lock(&fmd.d_xprt_lock);
2004d9638e54Smws 
2005d9638e54Smws 	if (fmd.d_xprt_suspend == 0)
2006d9638e54Smws 		fmd_panic("fmd_xprt_suspend/resume_all mismatch\n");
2007d9638e54Smws 
2008d9638e54Smws 	if (--fmd.d_xprt_suspend != 0) {
2009d9638e54Smws 		(void) pthread_mutex_unlock(&fmd.d_xprt_lock);
2010d9638e54Smws 		return; /* not ready to be resumed */
2011d9638e54Smws 	}
2012d9638e54Smws 
2013d9638e54Smws 	if (ids->ids_count != 0)
2014d9638e54Smws 		fmd_idspace_apply(ids, fmd_xprt_resume_xid, NULL);
2015d9638e54Smws 
2016d9638e54Smws 	(void) pthread_mutex_unlock(&fmd.d_xprt_lock);
2017d9638e54Smws }
2018