xref: /titanic_50/usr/src/uts/sun4u/starfire/io/idn_proto.c (revision b3700b074e637f8c6991b70754c88a2cfffb246b)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * Inter-Domain Network
29  *
30  * IDN Protocol functions to support domain link/unlink/reconfig.
31  */
32 
33 #include <sys/types.h>
34 #include <sys/param.h>
35 #include <sys/machparam.h>
36 #include <sys/debug.h>
37 #include <sys/cpuvar.h>
38 #include <sys/kmem.h>
39 #include <sys/mutex.h>
40 #include <sys/rwlock.h>
41 #include <sys/systm.h>
42 #include <sys/stream.h>
43 #include <sys/strsun.h>
44 #include <sys/stropts.h>
45 #include <sys/sema_impl.h>
46 #include <sys/membar.h>
47 #include <sys/utsname.h>
48 #include <inet/common.h>
49 #include <inet/mi.h>
50 #include <netinet/ip6.h>
51 #include <inet/ip.h>
52 #include <netinet/in.h>
53 #include <sys/vm_machparam.h>
54 #include <sys/x_call.h>
55 #include <sys/ddi.h>
56 #include <sys/sunddi.h>
57 #include <sys/atomic.h>
58 #include <vm/as.h>		/* kas decl */
59 
60 #include <sys/idn.h>
61 #include <sys/idn_xf.h>
62 
63 #define	IDNBUG_CPUPERBOARD
64 
65 extern pri_t		maxclsyspri;
66 extern u_longlong_t	gettick();
67 
68 clock_t	idn_xmit_monitor_freq = 50;
69 
70 static int	idn_connect(int domid);
71 static int	idn_disconnect(int domid, idn_fin_t fintype,
72 				idn_finarg_t finarg, idn_finsync_t finsync);
73 static void	idn_deconfig(int domid);
74 static void	idn_unlink_domainset(domainset_t domset, idn_fin_t fintype,
75 				idn_finarg_t finarg, idn_finopt_t finopt,
76 				boardset_t idnset);
77 static void	idn_retry_execute(void *arg);
78 static void	idn_retry_submit(void (*func)(uint_t token, void *arg),
79 				void *arg, uint_t token, clock_t ticks);
80 static void	idn_shutdown_datapath(domainset_t domset, int force);
81 static mblk_t	*idn_fill_buffer(caddr_t bufp, int size, mblk_t *mp,
82 				uchar_t **data_rptrp);
83 static ushort_t	idn_cksum(register ushort_t *hdrp, register int count);
84 static int	idn_mark_awol(int domid, clock_t *atime);
85 
86 static void	idn_recv_proto(idn_protomsg_t *hp);
87 static void	idn_send_config(int domid, int phase);
88 static void	idn_recv_config(int domid, idn_msgtype_t *mtp,
89 				idn_xdcargs_t xargs);
90 static int	idn_send_master_config(int domid, int phase);
91 static int	idn_send_slave_config(int domid, int phase);
92 static uint_t	idn_check_master_config(int domid, uint_t *exp, uint_t *act);
93 static uint_t	idn_check_slave_config(int domid, uint_t *exp, uint_t *act);
94 static int	idn_recv_config_done(int domid);
95 static void	idn_nego_cleanup_check(int domid, int new_masterid,
96 				int new_cpuid);
97 static void	idn_recv_cmd(int domid, idn_msgtype_t *mtp,
98 				idn_xdcargs_t xargs);
99 static int	idn_recv_data(int domid, idn_msgtype_t *mtp,
100 				idn_xdcargs_t xargs);
101 static int	idn_send_data_loopback(idn_netaddr_t dst_netaddr,
102 				queue_t *wq, mblk_t *mp);
103 static void	idn_send_dataresp(int domid, idn_nack_t nacktype);
104 static int	idn_send_mboxdata(int domid, struct idn *sip, int channel,
105 				caddr_t bufp);
106 static int	idn_recv_mboxdata(int channel, caddr_t bufp);
107 static int	idn_program_hardware(int domid);
108 static int	idn_deprogram_hardware(int domid);
109 
110 static void	idn_send_cmd_nackresp(int domid, idn_msgtype_t *mtp,
111 			idn_cmd_t cmdtype, idn_nack_t nacktype);
112 static void	idn_local_cmd(idn_cmd_t cmdtype, uint_t arg1,
113 				uint_t arg2, uint_t arg3);
114 static void	idn_terminate_cmd(int domid, int serrno);
115 static void	idn_mboxarea_init(idn_mboxtbl_t *mtp, register int ntbls);
116 static void	idn_mainmbox_activate(int domid);
117 static void	idn_mainmbox_deactivate(ushort_t domset);
118 static void	idn_mainmbox_chan_register(int domid,
119 				idn_mainmbox_t *send_mmp,
120 				idn_mainmbox_t *recv_mmp, int channel);
121 static int	idn_mainmbox_chan_unregister(ushort_t domset, int channel);
122 static int	idn_mainmbox_flush(int domid, idn_mainmbox_t *mmp);
123 static void	idn_mainmbox_reset(int domid, idn_mainmbox_t *cmp);
124 static int	idn_activate_channel(idn_chanset_t chanset,
125 				idn_chanop_t chanop);
126 static void	idn_deactivate_channel(idn_chanset_t chanset,
127 				idn_chanop_t chanop);
128 static int	idn_deactivate_channel_services(int channel,
129 				idn_chanop_t chanop);
130 static int	idn_activate_channel_services(int channel);
131 static void	idn_chan_server(idn_chansvr_t **cspp);
132 #if 0
133 static void	idn_chan_flush(idn_chansvr_t *csp);
134 #endif /* 0 */
135 static void	idn_chan_action(int channel, idn_chanaction_t chanaction,
136 				int wait);
137 static void	idn_chan_addmbox(int channel, ushort_t domset);
138 static void	idn_chan_delmbox(int channel, ushort_t domset);
139 static void	idn_submit_chanactivate_job(int channel);
140 static void	idn_exec_chanactivate(void *chn);
141 
142 static void	idn_link_established(void *arg);
143 static void	idn_prealloc_slab(int nslabs);
144 static void	idn_recv_slaballoc_req(int domid, idn_msgtype_t *mtp,
145 				uint_t slab_size);
146 static void	idn_send_slaballoc_resp(int domid, idn_msgtype_t *mtp,
147 				uint_t slab_offset, uint_t slab_size,
148 				int serrno);
149 static void	idn_recv_slaballoc_resp(int domid, smr_offset_t slab_offset,
150 				uint_t slab_size, int serrno);
151 static void	idn_recv_slabreap_req(int domid, idn_msgtype_t *mtp,
152 				int nslabs);
153 static void	idn_recv_slabreap_resp(int domid, int nslabs, int serrno);
154 static void	idn_send_slabreap_resp(int domid, idn_msgtype_t *mtp,
155 				int nslabs, int serrno);
156 static void	idn_recv_slabfree_req(int domid, idn_msgtype_t *mtp,
157 				smr_offset_t slab_offset, uint_t slab_size);
158 static void	idn_recv_slabfree_resp(int domid, uint_t slab_offset,
159 				uint_t slab_size, int serrno);
160 static void	idn_send_slabfree_resp(int domid, idn_msgtype_t *mtp,
161 				uint_t slab_offset, uint_t slab_size,
162 				int serrno);
163 static void	idn_retry_nodename_req(void *arg);
164 static void	idn_send_nodename_req(int domid);
165 static void	idn_send_nodename_resp(int domid, idn_msgtype_t *mtp,
166 				uint_t bufoffset, int serrno);
167 static void	idn_recv_nodename_req(int domid, idn_msgtype_t *mtp,
168 				uint_t bufoffset);
169 static void	idn_recv_nodename_resp(int domid, uint_t bufoffset,
170 				int serrno);
171 
172 static void	idn_protocol_server(int *id);
173 static void	idn_protocol_server_killall();
174 static void	idn_protojob_free(idn_protojob_t *jp);
175 
176 static int	idn_xstate_transfunc(int domid, void *transarg);
177 static int	idn_xphase_transition(int domid, idn_msgtype_t *mtp,
178 				idn_xdcargs_t xargs);
179 static void	idn_sync_enter(int domid, idn_synccmd_t cmd,
180 				domainset_t xset, domainset_t rset,
181 				int (*transfunc)(), void *transarg);
182 static domainset_t
183 		idn_sync_register(int domid, idn_synccmd_t cmd,
184 				domainset_t ready_set, idn_syncreg_t regtype);
185 static void	idn_sync_register_awol(int domid);
186 static int	idn_verify_config_mbox(int domid);
187 static int	idn_select_master(int domid, int rmasterid, int rcpuid);
188 
189 static int	valid_mtu(uint_t mtu);
190 static int	valid_bufsize(uint_t bufsize);
191 static int	valid_slabsize(int slabsize);
192 static int	valid_nwrsize(int nwrsize);
193 
194 static int	idn_master_init();
195 static void	idn_master_deinit();
196 
197 static void	idn_send_acknack(int domid, idn_msgtype_t *mtp,
198 				idn_xdcargs_t xargs);
199 
200 static int	idn_send_nego(int domid, idn_msgtype_t *mtp,
201 				domainset_t conset);
202 static void	idn_retry_nego(uint_t token, void *arg);
203 static int	idn_check_nego(int domid, idn_msgtype_t *mtp,
204 				idn_xdcargs_t xargs);
205 static void	idn_action_nego_pend(int domid, idn_msgtype_t *mtp,
206 				idn_xdcargs_t xargs);
207 static void	idn_error_nego(int domid, idn_msgtype_t *mtp,
208 				idn_xdcargs_t xargs);
209 static void	idn_action_nego_sent(int domid, idn_msgtype_t *mtp,
210 				idn_xdcargs_t xargs);
211 static void	idn_action_nego_rcvd(int domid, idn_msgtype_t *mtp,
212 				idn_xdcargs_t xargs);
213 static void	idn_final_nego(int domid);
214 static void	idn_exit_nego(int domid, uint_t msgtype);
215 
216 static int	idn_send_con(int domid, idn_msgtype_t *mtp,
217 				idn_con_t contype, domainset_t conset);
218 static void	idn_retry_con(uint_t token, void *arg);
219 static int	idn_check_con(int domid, idn_msgtype_t *mtp,
220 				idn_xdcargs_t xargs);
221 static void	idn_action_con_pend(int domid, idn_msgtype_t *mtp,
222 				idn_xdcargs_t xargs);
223 static void	idn_error_con(int domid, idn_msgtype_t *mtp,
224 				idn_xdcargs_t xargs);
225 static void	idn_action_con_sent(int domid, idn_msgtype_t *mtp,
226 				idn_xdcargs_t xargs);
227 static void	idn_action_con_rcvd(int domid, idn_msgtype_t *mtp,
228 				idn_xdcargs_t xargs);
229 static void	idn_final_con(int domid);
230 static void	idn_exit_con(int domid, uint_t msgtype);
231 
232 static int	idn_send_fin(int domid, idn_msgtype_t *mtp, idn_fin_t fintype,
233 				idn_finarg_t finarg, idn_finopt_t finopt,
234 				domainset_t finset, uint_t finmaster);
235 static void	idn_retry_fin(uint_t token, void *arg);
236 static int	idn_check_fin_pend(int domid, idn_msgtype_t *mtp,
237 				idn_xdcargs_t xargs);
238 static void	idn_action_fin_pend(int domid, idn_msgtype_t *mtp,
239 				idn_xdcargs_t xargs);
240 static void	idn_error_fin_pend(int domid, idn_msgtype_t *mtp,
241 				idn_xdcargs_t xargs);
242 static int	idn_check_fin_sent(int domid, idn_msgtype_t *mtp,
243 				idn_xdcargs_t xargs);
244 static void	idn_action_fin_sent(int domid, idn_msgtype_t *mtp,
245 				idn_xdcargs_t xargs);
246 static void	idn_error_fin_sent(int domid, idn_msgtype_t *mtp,
247 				idn_xdcargs_t xargs);
248 static void	idn_action_fin_rcvd(int domid, idn_msgtype_t *mtp,
249 				idn_xdcargs_t xargs);
250 static void	idn_final_fin(int domid);
251 static void	idn_exit_fin(int domid, uint_t msgtype);
252 
253 /*
254  * We keep a small cache of protojob structures just
255  * in case allocation within idn_handler comes back
256  * with nothing from the land of kmem.
257  */
258 idn_protojob_t	idn_protojob_cache[IDN_DMV_PENDING_MAX];
259 idn_protojob_t	*idn_protojob_cache_list;
260 kmutex_t	idn_protojob_cache_lock;
261 
262 /*
263  *	- receive message.
264  *	- call check-function for current state.
265  *	- if (check-function == ok) then
266  *		call action-function for current state.
267  *	  else
268  *		call error-function for current state.
269  *	- transition state based on check results.
270  *	- if (next state == final state) then
271  *		call final-function.
272  */
273 static idn_xphase_t xphase_nego = {
274 	IDNP_NEGO,
275 	{
276 		{ IDNDS_NEGO_PEND,
277 			idn_check_nego,
278 			idn_action_nego_pend,
279 			idn_error_nego},
280 		{ IDNDS_NEGO_SENT,
281 			idn_check_nego,
282 			idn_action_nego_sent,
283 			idn_error_nego},
284 		{ IDNDS_NEGO_RCVD,
285 			NULL,
286 			idn_action_nego_rcvd,
287 			NULL },
288 		{ IDNDS_CONFIG, NULL, NULL, NULL },
289 	},
290 	idn_final_nego,
291 	idn_exit_nego
292 };
293 
294 static idn_xphase_t xphase_con = {
295 	IDNP_CON,
296 	{
297 		{ IDNDS_CON_PEND,
298 			idn_check_con,
299 			idn_action_con_pend,
300 			idn_error_con},
301 		{ IDNDS_CON_SENT,
302 			idn_check_con,
303 			idn_action_con_sent,
304 			idn_error_con},
305 		{ IDNDS_CON_RCVD,
306 			NULL,
307 			idn_action_con_rcvd,
308 			NULL },
309 		{ IDNDS_CON_READY, NULL, NULL, NULL },
310 	},
311 	idn_final_con,
312 	idn_exit_con
313 };
314 
315 static idn_xphase_t xphase_fin = {
316 	IDNP_FIN,
317 	{
318 		{ IDNDS_FIN_PEND,
319 			idn_check_fin_pend,
320 			idn_action_fin_pend,
321 			idn_error_fin_pend },
322 		{ IDNDS_FIN_SENT,
323 			idn_check_fin_sent,
324 			idn_action_fin_sent,
325 			idn_error_fin_sent },
326 		{ IDNDS_FIN_RCVD,
327 			NULL,
328 			idn_action_fin_rcvd,
329 			NULL },
330 		{ IDNDS_DMAP, NULL, NULL, NULL },
331 	},
332 	idn_final_fin,
333 	idn_exit_fin
334 };
335 
336 static int idnxs_state_table[4][5][2] = {
337 	{			/* IDNXS_PEND */
338 		{ IDNXS_SENT,	IDNXS_PEND },	/* 0 */
339 		{ IDNXS_RCVD,	IDNXS_PEND },	/* msg */
340 		{ IDNXS_NIL,	IDNXS_PEND },	/* msg+ack */
341 		{ IDNXS_PEND,	IDNXS_NIL },	/* ack */
342 		{ IDNXS_PEND,	IDNXS_NIL },	/* nack */
343 	},
344 	{			/* IDNXS_SENT */
345 		{ IDNXS_NIL,	IDNXS_NIL },	/* 0 */
346 		{ IDNXS_RCVD,	IDNXS_PEND },	/* msg */
347 		{ IDNXS_FINAL,	IDNXS_PEND },	/* msg+ack */
348 		{ IDNXS_NIL,	IDNXS_NIL },	/* ack */
349 		{ IDNXS_PEND,	IDNXS_NIL },	/* nack */
350 	},
351 	{			/* IDNXS_RCVD */
352 		{ IDNXS_NIL,	IDNXS_NIL },	/* 0 */
353 		{ IDNXS_NIL,	IDNXS_NIL },	/* msg */
354 		{ IDNXS_FINAL,	IDNXS_NIL },	/* msg+ack */
355 		{ IDNXS_FINAL,	IDNXS_NIL },	/* ack */
356 		{ IDNXS_PEND,	IDNXS_NIL },	/* nack */
357 	},
358 	{			/* IDNXS_FINAL */
359 		{ IDNXS_NIL,	IDNXS_NIL },	/* 0 */
360 		{ IDNXS_NIL,	IDNXS_NIL },	/* msg */
361 		{ IDNXS_NIL,	IDNXS_NIL },	/* msg+ack */
362 		{ IDNXS_NIL,	IDNXS_NIL },	/* ack */
363 		{ IDNXS_NIL,	IDNXS_NIL },	/* nack */
364 	}
365 };
366 
367 /*
368  * NONE		Respective domain does not have a master.
369  * OTHER	Respective domain has a master different
370  *		than either local or remote.
371  * LOCAL	Respective domain has chosen local as master.
372  * REMOTE	Respective domain has chosen remote as master.
373  *
374  * Actions:
375  *	VOTE		Compare votes and select one.
376  *	VOTE_RCFG	Compare votes and Reconfigure
377  *			if necessary, i.e. remote won.
378  *	CONNECT		Connect to remote's OTHER if different
379  *			than our local master.
380  *	LOCAL		Local domain is winner.
381  *	REMOTE		Remote domain is winner.
382  *	WAIT		Wait for remote to connect to our
383  *			master if his is different.
384  *	ERROR		An impossible condition.
385  *
386  * Index:
387  *	0 = Local
388  *	1 = Remote
389  */
390 static idn_master_select_t master_select_table[4][4] = {
391 	{				/* local	remote	*/
392 		MASTER_SELECT_VOTE,	/* NONE		NONE	*/
393 		MASTER_SELECT_CONNECT,	/* NONE		OTHER	*/
394 		MASTER_SELECT_LOCAL,	/* NONE		LOCAL	*/
395 		MASTER_SELECT_REMOTE	/* NONE		REMOTE	*/
396 	},
397 	{
398 		MASTER_SELECT_WAIT,	/* OTHER	NONE	*/
399 		MASTER_SELECT_CONNECT,	/* OTHER	OTHER	*/
400 		MASTER_SELECT_WAIT,	/* OTHER	LOCAL	*/
401 		MASTER_SELECT_WAIT	/* OTHER	REMOTE	*/
402 	},
403 	{
404 		MASTER_SELECT_LOCAL,	/* LOCAL	NONE	*/
405 		MASTER_SELECT_CONNECT,	/* LOCAL	OTHER	*/
406 		MASTER_SELECT_LOCAL,	/* LOCAL	LOCAL	*/
407 		MASTER_SELECT_VOTE_RCFG	/* LOCAL	REMOTE	*/
408 	},
409 	{
410 		MASTER_SELECT_REMOTE,	/* REMOTE	NONE	*/
411 		MASTER_SELECT_CONNECT,	/* REMOTE	OTHER	*/
412 		MASTER_SELECT_ERROR,	/* REMOTE	LOCAL	*/
413 		MASTER_SELECT_REMOTE	/* REMOTE	REMOTE	*/
414 	}
415 };
416 
417 void
418 idn_assign_cookie(int domid)
419 {
420 	static ushort_t	num = 0;
421 	ushort_t	cookie;
422 	procname_t	proc = "idn_assign_cookie";
423 
424 	if ((cookie = idn_domain[domid].dcookie_recv) != 0)
425 		return;
426 
427 	cookie = (ushort_t)(((uint64_t)&idn_domain[domid] >> 8) & 0xff);
428 	while ((cookie ^= num++ & 0xff) == 0)
429 		;
430 
431 	PR_PROTO("%s:%d: assigned RECV cookie 0x%x\n", proc, domid, cookie);
432 
433 	idn_domain[domid].dcookie_recv = cookie;
434 }
435 
436 void
437 idn_update_priority(int domid, int pri)
438 {
439 	idn_domain_t	*dp;
440 	procname_t	proc = "idn_update_priority";
441 
442 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
443 
444 	dp = &idn_domain[domid];
445 
446 	if (pri >= IDNVOTE_MINPRI) {
447 		dp->dvote.v.priority = pri & IDNVOTE_PRI_MASK;
448 
449 		PR_PROTO("%s:%d: SETTING PRIORITY to req(%d) "
450 		    "(localpri = 0x%x)\n",
451 		    proc, domid, pri, IDNVOTE_PRIVALUE(dp->dvote));
452 	} else {
453 		PR_PROTO("%s:%d: PRIORITIES UNCHANGED (pri = 0x%x)\n",
454 		    proc, domid, IDNVOTE_PRIVALUE(dp->dvote));
455 	}
456 }
457 
458 /*
459  * Initiate a link between the local domain and the remote domain
460  * containing the given cpuid.
461  */
462 int
463 idn_link(int domid, int cpuid, int pri, int waittime, idnsb_error_t *sep)
464 {
465 	int		rv;
466 	idn_domain_t	*dp;
467 	void		*opcookie;
468 	procname_t	proc = "idn_link";
469 
470 	if ((cpuid < 0) || (cpuid >= NCPU)) {
471 		cmn_err(CE_WARN,
472 		    "IDN: 201: (LINK) invalid CPU ID (%d)", cpuid);
473 		return (EINVAL);
474 	}
475 	if (waittime < 0) {
476 		cmn_err(CE_WARN,
477 		    "IDN: 202: (LINK) invalid time-out value (%d)",
478 		    waittime);
479 		return (EINVAL);
480 	}
481 	if (!VALID_DOMAINID(domid)) {
482 		cmn_err(CE_WARN,
483 		    "IDN: 203: (LINK) invalid domain ID (%d)",
484 		    domid);
485 		return (EINVAL);
486 	}
487 	if (domid == idn.localid)
488 		return (0);
489 
490 	IDN_SYNC_LOCK();
491 	IDN_DLOCK_EXCL(domid);
492 
493 	dp = &idn_domain[domid];
494 
495 	switch (dp->dstate) {
496 	case IDNDS_CLOSED:
497 		break;
498 
499 	case IDNDS_CONNECTED:
500 #ifdef DEBUG
501 		cmn_err(CE_NOTE,
502 		    "!IDN: domain %d (CPU ID %d) already connected",
503 		    domid, cpuid);
504 #endif /* DEBUG */
505 		IDN_DUNLOCK(domid);
506 		IDN_SYNC_UNLOCK();
507 		return (0);
508 
509 	default:
510 		cmn_err(CE_WARN,
511 		    "IDN: 204: domain %d state (%s) inappropriate",
512 		    domid, idnds_str[dp->dstate]);
513 		IDN_DUNLOCK(domid);
514 		IDN_SYNC_UNLOCK();
515 		return (EINVAL);
516 	}
517 
518 	rv = idn_open_domain(domid, cpuid, 0);
519 	if (rv != 0) {
520 		cmn_err(CE_WARN,
521 		    "IDN: 205: (%s) failed to open-domain(%d,%d)",
522 		    proc, domid, cpuid);
523 		IDN_DUNLOCK(domid);
524 		IDN_SYNC_UNLOCK();
525 		return (EIO);
526 	}
527 
528 
529 	IDN_DLOCK_EXCL(idn.localid);
530 	idn_update_priority(idn.localid, pri);
531 	IDN_DUNLOCK(idn.localid);
532 
533 	if (waittime > 0)
534 		opcookie = idn_init_op(IDNOP_CONNECTED, DOMAINSET(domid), sep);
535 
536 	(void) idn_connect(domid);
537 
538 	IDN_DUNLOCK(domid);
539 	IDN_SYNC_UNLOCK();
540 
541 	PR_PROTO("%s:%d: ALLOCATED idn_link(%d)\n", proc, domid, cpuid);
542 
543 	if (waittime > 0) {
544 		boardset_t	domset = 0;
545 		/*
546 		 * Well we've successfully allocated a domain id,
547 		 * but the link may not be fully established yet.
548 		 * Need to wait since it happens asynchronously.
549 		 */
550 		PR_PROTO("%s:%d: WAITING for op(%s) for (domset 0%x)...\n",
551 		    proc, domid, idnop_str[IDNOP_CONNECTED],
552 		    DOMAINSET(domid));
553 
554 		rv = idn_wait_op(opcookie, &domset, waittime);
555 	}
556 
557 #ifdef DEBUG
558 	if (rv == 0) {
559 		if (waittime > 0) {
560 			PR_PROTO("%s:%d: connect SUCCEEDED (cpu %d)\n",
561 			    proc, domid, cpuid);
562 		} else {
563 			PR_PROTO("%s:%d: connect KICKED OFF (cpu %d)\n",
564 			    proc, domid, cpuid);
565 		}
566 	} else {
567 		PR_PROTO("%s:%d: connect FAILED (cpu %d)\n",
568 		    proc, domid, cpuid);
569 	}
570 #endif /* DEBUG */
571 
572 	return (rv);
573 }
574 
575 /*
576  * Unlink the given domain from any domain cluster of
577  * which it might be a member.  Force indicates that domain
578  * should not go AWOL and if it's currently AWOL to close
579  * and remove it.
580  * IMPORTANT: If the (hard) force flag is set, the caller is
581  *	      assumed to GUARANTEE that the given domain will
582  *	      not attempt to communicate with the local domain
583  *	      in any manner.
584  */
585 int
586 idn_unlink(int domid, boardset_t idnset, idn_fin_t fintype,
587 		idn_finopt_t finopt, int waittime, idnsb_error_t *sep)
588 {
589 	int		rv = 0;
590 	domainset_t	domset;
591 	void		*opcookie;
592 	procname_t	proc = "idn_unlink";
593 
594 
595 	if (waittime < 0) {
596 		cmn_err(CE_WARN,
597 		    "IDN: 202: (UNLINK) invalid time-out value (%d)",
598 		    waittime);
599 		SET_IDNKERR_IDNERR(sep, IDNKERR_INVALID_WTIME);
600 		SET_IDNKERR_PARAM0(sep, waittime);
601 		return (EINVAL);
602 	}
603 	if (!VALID_DOMAINID(domid)) {
604 		cmn_err(CE_WARN,
605 		    "IDN: 203: (UNLINK) invalid domain ID (%d)",
606 		    domid);
607 		SET_IDNKERR_IDNERR(sep, IDNKERR_INVALID_DOMAIN);
608 		SET_IDNKERR_PARAM0(sep, domid);
609 		SET_IDNKERR_PARAM1(sep, -1);
610 		return (EINVAL);
611 	}
612 	if (idn.localid == IDN_NIL_DOMID) {
613 #ifdef DEBUG
614 		cmn_err(CE_NOTE,
615 		    "!IDN: %s: local domain not connected to an IDNnet",
616 		    proc);
617 #endif /* DEBUG */
618 		return (0);
619 	}
620 
621 	/*
622 	 * Lock ordering protocols requires that we grab the
623 	 * global lock _before_ the local domain's lock.
624 	 * However, non-local domains must have their lock
625 	 * grabbed _before_ the global lock.
626 	 */
627 	IDN_SYNC_LOCK();
628 	IDN_GLOCK_EXCL();
629 	domset = idn.domset.ds_trans_on | idn.domset.ds_trans_off;
630 	if ((idn.state == IDNGS_OFFLINE) && !domset) {
631 #ifdef DEBUG
632 		cmn_err(CE_WARN,
633 		    "!IDN: %s: local domain not connected to an IDNnet",
634 		    proc);
635 #endif /* DEBUG */
636 		IDN_GUNLOCK();
637 		IDN_SYNC_UNLOCK();
638 		return (0);
639 	}
640 
641 	if ((domid == IDN_NIL_DOMID) || (domid == idn.localid)) {
642 		domid = idn.localid;
643 		IDN_GSTATE_TRANSITION(IDNGS_DISCONNECT);
644 		IDN_SET_NEW_MASTERID(IDN_NIL_DOMID);
645 		domset = DOMAINSET_ALL;
646 		DOMAINSET_DEL(domset, idn.localid);
647 	} else {
648 		domset = DOMAINSET(domid);
649 	}
650 	IDN_GUNLOCK();
651 
652 	if (waittime > 0)
653 		opcookie = idn_init_op(IDNOP_DISCONNECTED, domset, sep);
654 
655 	idn_unlink_domainset(domset, fintype, IDNFIN_ARG_NONE, finopt, idnset);
656 
657 	IDN_SYNC_UNLOCK();
658 
659 	if (waittime > 0) {
660 		/*
661 		 * Well the unlink has successfully kicked off.
662 		 * Since process is asynchronous we need to wait
663 		 * for it to complete.
664 		 */
665 		PR_PROTO("%s:%d: WAITING for op(%s) for (domset 0%x)...\n",
666 		    proc, domid, idnop_str[IDNOP_DISCONNECTED],
667 		    domset);
668 
669 		rv = idn_wait_op(opcookie, &domset, waittime);
670 	}
671 
672 	if (rv == 0) {
673 		if (waittime > 0) {
674 			PR_PROTO("%s:%d: disconnect SUCCEEDED\n",
675 			    proc, domid);
676 		} else {
677 			PR_PROTO("%s:%d: disconnect KICKED OFF\n",
678 			    proc, domid);
679 		}
680 	} else {
681 		PR_PROTO("%s:%d: disconnect FAILED\n", proc, domid);
682 	}
683 
684 	return (rv);
685 }
686 
687 static void
688 idn_unlink_domainset(domainset_t domset, idn_fin_t fintype,
689 			idn_finarg_t finarg, idn_finopt_t finopt,
690 			boardset_t idnset)
691 {
692 	int		d;
693 	domainset_t	offset;
694 	procname_t	proc = "idn_unlink_domainset";
695 
696 	ASSERT(IDN_SYNC_IS_LOCKED());
697 
698 	/*
699 	 * Determine subset for which we have
700 	 * no active connections.
701 	 */
702 	offset = domset & ~(idn.domset.ds_trans_on |
703 	    idn.domset.ds_connected |
704 	    idn.domset.ds_trans_off |
705 	    idn.domset.ds_relink);
706 	/*
707 	 * Determine subset that are really candidates.
708 	 * Note that we include those already down the path
709 	 * since it's possible a request came in to upgrade
710 	 * their fintype (e.g. NORMAL->FORCE_SOFT).
711 	 */
712 	domset &= ~offset;
713 
714 	if (offset)
715 		idn_update_op(IDNOP_DISCONNECTED, offset, NULL);
716 
717 	IDN_GLOCK_EXCL();
718 	if ((finopt == IDNFIN_OPT_RELINK) && (idn.state != IDNGS_DISCONNECT)) {
719 		/*
720 		 * Don't add domains already transitioning off.
721 		 * If they caught on an earlier Reconfig wave then
722 		 * they'll already be in ds_relink anyway.  Otherwise,
723 		 * once a domain is transition off we can't upgrade
724 		 * him to a RELINK.
725 		 */
726 #ifdef DEBUG
727 		if (idn.domset.ds_hitlist & domset) {
728 			PR_HITLIST("%s: domset=%x, hitlist=%x, trans_off=%x "
729 			    "-> relink = %x -> %x\n",
730 			    proc, domset, idn.domset.ds_hitlist,
731 			    idn.domset.ds_relink, idn.domset.ds_trans_off,
732 			    idn.domset.ds_relink |
733 			    (domset & ~idn.domset.ds_trans_off));
734 		}
735 #endif /* DEBUG */
736 
737 		domset &= ~idn.domset.ds_trans_off;
738 		idn.domset.ds_relink |= domset;
739 	} else {
740 		idn.domset.ds_relink &= ~domset;
741 	}
742 	/*
743 	 * Update the ds_trans_on/off so we don't waste
744 	 * time talking to these folks.
745 	 */
746 	idn.domset.ds_trans_on  &= ~domset;
747 	idn.domset.ds_trans_off |= domset;
748 
749 	if (domset == 0) {
750 		if ((idn.domset.ds_trans_on |
751 		    idn.domset.ds_connected |
752 		    idn.domset.ds_trans_off |
753 		    idn.domset.ds_relink) == 0) {
754 			PR_HITLIST("%s:%x: HITLIST %x -> 0\n",
755 			    proc, domset, idn.domset.ds_hitlist);
756 			idn.domset.ds_hitlist = 0;
757 			IDN_GSTATE_TRANSITION(IDNGS_OFFLINE);
758 		}
759 		IDN_GUNLOCK();
760 		return;
761 	}
762 	IDN_GUNLOCK();
763 
764 	for (d = 0; d < MAX_DOMAINS; d++) {
765 		idn_domain_t	*dp;
766 		idn_fin_t	ftype;
767 
768 		if (!DOMAIN_IN_SET(domset, d))
769 			continue;
770 
771 		dp = &idn_domain[d];
772 		IDN_DLOCK_EXCL(d);
773 		IDN_HISTORY_LOG(IDNH_RELINK, d, dp->dstate,
774 		    idn.domset.ds_relink);
775 		ftype = fintype;
776 		if ((dp->dcpu != IDN_NIL_DCPU) && dp->dhw.dh_boardset) {
777 			/*
778 			 * If domain is not in the IDNSET passed
779 			 * down then we need to upgrade this to
780 			 * hard-force in order to prevent possible
781 			 * system failures (arbstop).  This is simply
782 			 * extra protection beyond that checked by
783 			 * the SSP.  IDNSET contains the set of boards
784 			 * that have a "link" to the local domain,
785 			 * including the SMD regs.
786 			 */
787 			if ((idnset & dp->dhw.dh_boardset) == 0) {
788 				PR_PROTO("%s:%d: boardset 0x%x "
789 				    "NOT in IDNSET 0x%x\n",
790 				    proc, d, dp->dhw.dh_boardset,
791 				    idnset);
792 				if (ftype != IDNFIN_FORCE_HARD)
793 					cmn_err(CE_NOTE,
794 					    "!IDN: 222: no IDN linkage "
795 					    "found (b=0x%x, i=0x%x) "
796 					    "upgrading unlink %s to %s",
797 					    dp->dhw.dh_boardset,
798 					    idnset, idnfin_str[ftype],
799 					    idnfin_str[IDNFIN_FORCE_HARD]);
800 
801 				ftype = IDNFIN_FORCE_HARD;
802 			} else {
803 				PR_PROTO("%s:%d: boardset 0x%x "
804 				    "FOUND in IDNSET 0x%x\n",
805 				    proc, d, dp->dhw.dh_boardset,
806 				    idnset);
807 			}
808 		}
809 		(void) idn_disconnect(d, ftype, finarg, IDNDS_SYNC_TYPE(dp));
810 		IDN_DUNLOCK(d);
811 	}
812 }
813 
814 /*
815  * Return w/locks held.
816  */
817 static int
818 idn_connect(int domid)
819 {
820 	idn_xdcargs_t	xargs;
821 	idn_domain_t	*dp = &idn_domain[domid];
822 	procname_t	proc = "idn_connect";
823 
824 	ASSERT(IDN_SYNC_IS_LOCKED());
825 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
826 
827 	ASSERT(dp->dcpu != IDN_NIL_DCPU);
828 
829 	if (dp->dstate != IDNDS_CLOSED) {
830 		if (DOMAIN_IN_SET(idn.domset.ds_trans_on |
831 		    idn.domset.ds_connected, domid)) {
832 			PR_PROTO("%s:%d: already connected or "
833 			    "in-progress\n", proc, domid);
834 		} else {
835 			PR_PROTO("%s:%d: current state (%s) != "
836 			    "CLOSED\n", proc, domid,
837 			    idnds_str[dp->dstate]);
838 		}
839 		return (-1);
840 	}
841 
842 	ASSERT(!DOMAIN_IN_SET(idn.domset.ds_connected, domid));
843 	ASSERT(!DOMAIN_IN_SET(idn.domset.ds_trans_off, domid));
844 
845 	dp->dxp = &xphase_nego;
846 	IDN_XSTATE_TRANSITION(dp, IDNXS_PEND);
847 
848 	(void) idn_xphase_transition(domid, NULL, xargs);
849 
850 	return (0);
851 }
852 
853 /*
854  * Return w/locks held.
855  */
856 static int
857 idn_disconnect(int domid, idn_fin_t fintype, idn_finarg_t finarg,
858     idn_finsync_t finsync)
859 {
860 	int		new_masterid, new_cpuid = IDN_NIL_DCPU;
861 	uint_t		token;
862 	uint_t		finmaster;
863 	idn_xdcargs_t	xargs;
864 	idn_finopt_t	finopt;
865 	idn_domain_t	*dp = &idn_domain[domid];
866 	procname_t	proc = "idn_disconnect";
867 
868 	ASSERT(IDN_SYNC_IS_LOCKED());
869 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
870 
871 	if (dp->dstate == IDNDS_CLOSED) {
872 		PR_PROTO("%s:%d: already CLOSED\n", proc, domid);
873 		idn_update_op(IDNOP_DISCONNECTED, DOMAINSET(domid), NULL);
874 		return (-1);
875 	}
876 
877 	/*
878 	 * Terminate any outstanding commands that were
879 	 * targeted towards this domain.
880 	 */
881 	idn_terminate_cmd(domid, ECANCELED);
882 
883 	/*
884 	 * Terminate any and all retries that may have
885 	 * outstanding for this domain.
886 	 */
887 	token = IDN_RETRY_TOKEN(domid, IDN_RETRY_TYPEALL);
888 	(void) idn_retry_terminate(token);
889 
890 	/*
891 	 * Stop all outstanding message timers for
892 	 * this guy.
893 	 */
894 	IDN_MSGTIMER_STOP(domid, 0, 0);
895 
896 	dp->dxp = &xphase_fin;
897 	IDN_XSTATE_TRANSITION(dp, IDNXS_PEND);
898 	if ((int)dp->dfin < (int)fintype) {
899 		/*
900 		 * You can only upgrade a fin type.
901 		 * We don't allow it to be downgraded
902 		 * as it's too dangerous since some
903 		 * state may have been blown away while
904 		 * we were fin'ing at a higher level.
905 		 */
906 		IDN_FSTATE_TRANSITION(dp, fintype);
907 	}
908 
909 	dp->dfin_sync = finsync;
910 	PR_PROTO("%s:%d: disconnect synchronously = %s\n",
911 	    proc, domid, (finsync == IDNFIN_SYNC_OFF) ? "OFF" :
912 	    (finsync == IDNFIN_SYNC_NO) ? "NO" : "YES");
913 
914 	IDN_GLOCK_SHARED();
915 	if (DOMAIN_IN_SET(idn.domset.ds_relink, domid) &&
916 	    (idn.state != IDNGS_DISCONNECT)) {
917 		finopt = IDNFIN_OPT_RELINK;
918 	} else {
919 		finopt = IDNFIN_OPT_UNLINK;
920 		PR_HITLIST("%s:%d: HITLIST %x -> %x\n",
921 		    proc, domid, idn.domset.ds_hitlist,
922 		    idn.domset.ds_hitlist | DOMAINSET(domid));
923 		DOMAINSET_ADD(idn.domset.ds_hitlist, domid);
924 	}
925 
926 	CLR_XARGS(xargs);
927 	SET_XARGS_FIN_TYPE(xargs, dp->dfin);
928 	SET_XARGS_FIN_ARG(xargs, finarg);
929 	SET_XARGS_FIN_OPT(xargs, finopt);
930 	SET_XARGS_FIN_DOMSET(xargs, 0);		/* unused when msg = 0 */
931 	new_masterid = IDN_GET_NEW_MASTERID();
932 	IDN_GUNLOCK();
933 	if (new_masterid != IDN_NIL_DOMID)
934 		new_cpuid = idn_domain[new_masterid].dcpu;
935 	finmaster = MAKE_FIN_MASTER(new_masterid, new_cpuid);
936 	SET_XARGS_FIN_MASTER(xargs, finmaster);
937 
938 	(void) idn_xphase_transition(domid, NULL, xargs);
939 
940 	return (0);
941 }
942 
943 static int
944 idn_next_xstate(idn_xstate_t o_xstate, int err, uint_t msg)
945 {
946 	int		index;
947 	procname_t	proc = "idn_next_xstate";
948 
949 	ASSERT(((int)o_xstate >= 0) && ((int)o_xstate <= 4));
950 
951 	if (!msg)
952 		index = 0;
953 	else if ((msg & IDNP_MSGTYPE_MASK) == 0)
954 		index = (msg & IDNP_ACK) ? 3 : (msg & IDNP_NACK) ? 4 : -1;
955 	else
956 		index = (msg & IDNP_ACK) ? 2 :
957 		    !(msg & IDNP_ACKNACK_MASK) ? 1 : -1;
958 
959 	if (index == -1) {
960 		STRING(str);
961 
962 		INUM2STR(msg, str);
963 		PR_PROTO("%s: (msg = 0x%x(%s))\n", proc, msg, str);
964 		return (IDNXS_NIL);
965 	}
966 
967 	if (err == -1) {
968 		int	n_xstate;
969 		/*
970 		 * Caller is just interested in querying is this
971 		 * is a valid message to receive in the current
972 		 * xstate.  A return value of IDNXS_NIL indicates
973 		 * that it's not.  A return value of non-IDNXS_NIL
974 		 * indicates it's cool.  An invalid message is
975 		 * determined by both err & !err states being IDNXS_NIL.
976 		 */
977 		n_xstate = idnxs_state_table[(int)o_xstate][index][0];
978 		if (n_xstate != IDNXS_NIL)
979 			return (n_xstate);
980 		else
981 			return (idnxs_state_table[(int)o_xstate][index][1]);
982 	} else {
983 		return (idnxs_state_table[(int)o_xstate][index][err ? 1 : 0]);
984 	}
985 }
986 
987 static int
988 idn_select_candidate(domainset_t master_set)
989 {
990 	int		d, best_id = IDN_NIL_DOMID;
991 	uint_t		best_vote = 0;
992 	idn_domain_t	*dp;
993 	procname_t	proc = "idn_select_candidate";
994 
995 	ASSERT(IDN_SYNC_IS_LOCKED());
996 
997 	if (master_set == 0) {
998 		PR_PROTO("%s: %x -> %d\n", proc, master_set, IDN_NIL_DOMID);
999 		return (IDN_NIL_DOMID);
1000 	}
1001 
1002 	for (d = 0; d < MAX_DOMAINS; d++) {
1003 		uint_t		vote;
1004 		idn_vote_t	v;
1005 
1006 		if (!DOMAIN_IN_SET(master_set, d))
1007 			continue;
1008 
1009 		dp = &idn_domain[d];
1010 
1011 		if ((dp->domid == IDN_NIL_DOMID) ||
1012 		    (dp->dcpu == IDN_NIL_DCPU) ||
1013 		    ((v.ticket = dp->dvote.ticket) == 0))
1014 			continue;
1015 
1016 		vote = IDNVOTE_ELECT(v);
1017 
1018 		if (vote > best_vote) {
1019 			best_vote = vote;
1020 			best_id = d;
1021 		}
1022 	}
1023 
1024 	PR_PROTO("%s: %x -> %d\n", proc, master_set, best_id);
1025 
1026 	return (best_id);
1027 }
1028 
1029 /*
1030  * If a non-zero value is returned then GLOCK will have been dropped.
1031  * Otherwise, routine returns with all incoming locks still held.
1032  */
1033 static int
1034 idn_select_master(int domid, int rmasterid, int rcpuid)
1035 {
1036 	char		*sel;
1037 	int		lmasterid, masterid;
1038 	int		do_reconfig = 0;
1039 	int		lindex, rindex;
1040 	idn_domain_t	*ldp, *rdp;
1041 	uint_t		rvote, lvote;
1042 	idn_master_select_t	select;
1043 	procname_t	proc = "idn_select_master";
1044 
1045 	ASSERT(IDN_SYNC_IS_LOCKED());
1046 	ASSERT(IDN_GLOCK_IS_EXCL());
1047 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
1048 
1049 	PR_PROTO("%s:%d: lmasterid = %d, rmasterid = %d, rcpuid = %d\n",
1050 	    proc, domid, IDN_GET_MASTERID(), rmasterid, rcpuid);
1051 
1052 	IDN_DLOCK_EXCL(idn.localid);
1053 
1054 	ldp = &idn_domain[idn.localid];
1055 	rdp = &idn_domain[domid];
1056 
1057 	/*
1058 	 * Clear master bits since mastership is derived from
1059 	 * other information (local/remote idn.masterid/idn.new_masterid)
1060 	 * and we don't want the vote master bit to confuse matters.
1061 	 */
1062 	lvote = IDNVOTE_ELECT(ldp->dvote);
1063 	rvote = IDNVOTE_ELECT(rdp->dvote);
1064 
1065 	lmasterid = IDN_GET_MASTERID();
1066 
1067 	lindex = (lmasterid == IDN_NIL_DOMID) ? MASTER_IS_NONE :
1068 	    (lmasterid == idn.localid) ? MASTER_IS_LOCAL :
1069 	    (lmasterid == domid) ? MASTER_IS_REMOTE :
1070 	    MASTER_IS_OTHER;
1071 
1072 	rindex = (rmasterid == IDN_NIL_DOMID) ? MASTER_IS_NONE :
1073 	    (rmasterid == domid) ? MASTER_IS_REMOTE :
1074 	    (rmasterid == idn.localid) ? MASTER_IS_LOCAL :
1075 	    MASTER_IS_OTHER;
1076 
1077 	select = master_select_table[lindex][rindex];
1078 
1079 	masterid = IDN_NIL_DOMID;
1080 
1081 	/*
1082 	 * Each case is responsible for dropping DLOCK(localid)
1083 	 * and GLOCK if it doesn't select a master, unless a
1084 	 * reconfig is necessary.
1085 	 */
1086 	switch (select) {
1087 	case MASTER_SELECT_VOTE_RCFG:
1088 		sel = "VOTE_RECONFIG";
1089 		if (lvote > rvote) {
1090 			/*
1091 			 * If the local domain is the winner then remote
1092 			 * domain will have to Reconfig.  We'll continue
1093 			 * through the connection process anyway.  The
1094 			 * remote domains will tell us to back-off while
1095 			 * Reconfigs, but that's okay as we'll keep retrying.
1096 			 */
1097 			masterid = idn.localid;
1098 		} else if (lvote < rvote) {
1099 			do_reconfig = 1;
1100 			/*
1101 			 * GLOCK will get dropped once reconfig
1102 			 * is kicked off.
1103 			 */
1104 		} else {
1105 			cmn_err(CE_WARN,
1106 			    "IDN: 206: cannot link domains "
1107 			    "with equal votes (L(%d),R(%d),0x%x)",
1108 			    idn.localid, domid, rvote);
1109 			IDN_GUNLOCK();
1110 		}
1111 		IDN_DUNLOCK(idn.localid);
1112 		break;
1113 
1114 	case MASTER_SELECT_VOTE:
1115 		sel = "VOTE";
1116 		if (lvote > rvote) {
1117 			masterid = idn.localid;
1118 			ldp->dvote.v.master = 1;
1119 			rdp->dvote.v.master = 0;
1120 		} else if (lvote < rvote) {
1121 			masterid = domid;
1122 			ldp->dvote.v.master = 0;
1123 			rdp->dvote.v.master = 1;
1124 		} else {
1125 			cmn_err(CE_WARN,
1126 			    "IDN: 206: cannot link domains "
1127 			    "with equal votes (L(%d),R(%d),0x%x)",
1128 			    idn.localid, domid, rvote);
1129 		}
1130 		ASSERT(IDN_GET_MASTERID() == IDN_NIL_DOMID);
1131 		if (masterid != IDN_NIL_DOMID) {
1132 			IDN_SET_MASTERID(masterid);
1133 			IDN_SET_NEW_MASTERID(IDN_NIL_DOMID);
1134 		} else {
1135 			IDN_GUNLOCK();
1136 		}
1137 		IDN_DUNLOCK(idn.localid);
1138 		break;
1139 
1140 	case MASTER_SELECT_REMOTE:
1141 		sel = "REMOTE";
1142 		masterid = domid;
1143 		if (IDN_GET_MASTERID() == IDN_NIL_DOMID) {
1144 			IDN_SET_MASTERID(masterid);
1145 			IDN_SET_NEW_MASTERID(IDN_NIL_DOMID);
1146 			ldp->dvote.v.master = 0;
1147 			rdp->dvote.v.master = 1;
1148 		}
1149 		ASSERT(IDN_GET_MASTERID() == domid);
1150 		IDN_DUNLOCK(idn.localid);
1151 		break;
1152 
1153 	case MASTER_SELECT_LOCAL:
1154 		sel = "LOCAL";
1155 		masterid = idn.localid;
1156 		if (IDN_GET_MASTERID() == IDN_NIL_DOMID) {
1157 			IDN_SET_MASTERID(masterid);
1158 			IDN_SET_NEW_MASTERID(IDN_NIL_DOMID);
1159 			ldp->dvote.v.master = 1;
1160 			rdp->dvote.v.master = 0;
1161 		}
1162 		ASSERT(IDN_GET_MASTERID() == idn.localid);
1163 		IDN_DUNLOCK(idn.localid);
1164 		break;
1165 
1166 	case MASTER_SELECT_CONNECT:
1167 		sel = "CONNECT";
1168 		if (rmasterid == lmasterid) {
1169 			/*
1170 			 * Local and remote have same master,
1171 			 * let him come onboard.
1172 			 */
1173 			masterid = lmasterid;
1174 			IDN_DUNLOCK(idn.localid);
1175 
1176 		} else {
1177 			int	rv;
1178 
1179 			IDN_DUNLOCK(idn.localid);
1180 			IDN_GUNLOCK();
1181 			IDN_DLOCK_EXCL(rmasterid);
1182 			PR_PROTO("%s:%d: attempting connect w/remote "
1183 			    "master %d\n",
1184 			    proc, domid, rmasterid);
1185 			rv = idn_open_domain(rmasterid, rcpuid, 0);
1186 			if (rv == 0) {
1187 				(void) idn_connect(rmasterid);
1188 			} else if (rv < 0) {
1189 				cmn_err(CE_WARN,
1190 				    "IDN: 205: (%s) failed to "
1191 				    "open-domain(%d,%d)",
1192 				    proc, rmasterid, rcpuid);
1193 			} else {
1194 				/*
1195 				 * Must already have a connection going.
1196 				 */
1197 				PR_PROTO("%s:%d: failed "
1198 				    "idn_open_domain(%d,%d,0) "
1199 				    "(rv = %d)\n",
1200 				    proc, domid, rmasterid,
1201 				    rcpuid, rv);
1202 			}
1203 			IDN_DUNLOCK(rmasterid);
1204 		}
1205 		break;
1206 
1207 	case MASTER_SELECT_WAIT:
1208 		sel = "WAIT";
1209 		/*
1210 		 * If the remote domain has the same master as the local
1211 		 * domain then there's no need to wait.
1212 		 */
1213 		if (rmasterid == lmasterid) {
1214 			masterid = lmasterid;
1215 		} else {
1216 			IDN_GUNLOCK();
1217 		}
1218 		IDN_DUNLOCK(idn.localid);
1219 		break;
1220 
1221 	case MASTER_SELECT_ERROR:
1222 		sel = "ERROR";
1223 		/*
1224 		 * Hit impossible condition.
1225 		 */
1226 		cmn_err(CE_WARN,
1227 		    "IDN: 207: local/remote master-id conflict "
1228 		    "(%d.lmasterid = %d, %d.rmasterid = %d)",
1229 		    idn.localid, lmasterid, domid, rmasterid);
1230 		IDN_GUNLOCK();
1231 		IDN_DUNLOCK(idn.localid);
1232 		break;
1233 
1234 	default:
1235 		cmn_err(CE_WARN,
1236 		    "IDN: 208: %s: unknown case (%d)",
1237 		    proc, (int)select);
1238 		IDN_GUNLOCK();
1239 		IDN_DUNLOCK(idn.localid);
1240 		ASSERT(0);
1241 		break;
1242 	}
1243 
1244 	if (masterid == IDN_NIL_DOMID) {
1245 		PR_PROTO("%s:%d: NO MASTER SELECTED (rmstr=%d) sel=%s\n",
1246 		    proc, domid, rmasterid, sel);
1247 	} else {
1248 		PR_PROTO("%s:%d: MASTER SELECTED = %d (%s)\n",
1249 		    proc, domid, masterid,
1250 		    (masterid == idn.localid) ? "LOCAL" :
1251 		    (masterid == domid) ? "REMOTE" : "OTHER");
1252 	}
1253 
1254 	if (do_reconfig) {
1255 		domainset_t	dis_set;
1256 
1257 		/*
1258 		 * Local domain already has a master.
1259 		 * Need to dismantle all connections
1260 		 * and reestablish one with new master.
1261 		 */
1262 		IDN_GKSTAT_GLOBAL_EVENT(gk_reconfigs, gk_reconfig_last);
1263 
1264 		PR_PROTO("%s:%d: RECONFIG new masterid = %d\n",
1265 		    proc, domid, domid);
1266 
1267 		IDN_GSTATE_TRANSITION(IDNGS_RECONFIG);
1268 		IDN_SET_NEW_MASTERID(domid);
1269 		IDN_GUNLOCK();
1270 
1271 		dis_set = idn.domset.ds_trans_on | idn.domset.ds_connected;
1272 		DOMAINSET_DEL(dis_set, domid);
1273 
1274 		idn_unlink_domainset(dis_set, IDNFIN_NORMAL, IDNFIN_ARG_NONE,
1275 		    IDNFIN_OPT_RELINK, BOARDSET_ALL);
1276 	}
1277 
1278 	return ((masterid == IDN_NIL_DOMID) ? -1 : 0);
1279 }
1280 
1281 /*ARGSUSED1*/
1282 static void
1283 idn_retry_query(uint_t token, void *arg)
1284 {
1285 	idn_retry_t	rtype = IDN_RETRY_TOKEN2TYPE(token);
1286 	int		d, domid = IDN_RETRY_TOKEN2DOMID(token);
1287 	idn_domain_t	*dp = &idn_domain[domid];
1288 	idn_synccmd_t	sync_cmd;
1289 	domainset_t	query_set, my_ready_set;
1290 	procname_t	proc = "idn_retry_query";
1291 
1292 	IDN_SYNC_LOCK();
1293 	IDN_DLOCK_EXCL(domid);
1294 
1295 	switch (rtype) {
1296 	case IDNRETRY_CONQ:
1297 		sync_cmd = IDNSYNC_CONNECT;
1298 		my_ready_set = idn.domset.ds_ready_on | idn.domset.ds_connected;
1299 		my_ready_set &= ~idn.domset.ds_trans_off;
1300 		DOMAINSET_ADD(my_ready_set, idn.localid);
1301 		break;
1302 
1303 	case IDNRETRY_FINQ:
1304 		sync_cmd = IDNSYNC_DISCONNECT;
1305 		my_ready_set = idn.domset.ds_ready_off |
1306 		    ~idn.domset.ds_connected;
1307 		break;
1308 
1309 	default:
1310 		IDN_DUNLOCK(domid);
1311 		IDN_SYNC_UNLOCK();
1312 		return;
1313 	}
1314 
1315 	if (dp->dsync.s_cmd == sync_cmd)
1316 		my_ready_set |= dp->dsync.s_set_rdy;
1317 
1318 	query_set = idn_sync_register(domid, sync_cmd, 0, IDNSYNC_REG_QUERY);
1319 
1320 	PR_PROTO("%s:%d: query_set = 0x%x\n", proc, domid, query_set);
1321 
1322 	if (query_set == 0) {
1323 		IDN_DUNLOCK(domid);
1324 		IDN_SYNC_UNLOCK();
1325 		return;
1326 	}
1327 
1328 	for (d = 0; d < MAX_DOMAINS; d++) {
1329 		if (!DOMAIN_IN_SET(query_set, d))
1330 			continue;
1331 
1332 		dp = &idn_domain[d];
1333 		if (d != domid)
1334 			IDN_DLOCK_EXCL(d);
1335 
1336 		if ((dp->dsync.s_cmd == sync_cmd) ||
1337 		    (!dp->dcookie_send &&
1338 		    (rtype == IDNRETRY_CONQ))) {
1339 			if (d != domid)
1340 				IDN_DUNLOCK(d);
1341 			continue;
1342 		}
1343 
1344 		IDN_SYNC_QUERY_UPDATE(domid, d);
1345 
1346 		if (rtype == IDNRETRY_CONQ)
1347 			(void) idn_send_con(d, NULL, IDNCON_QUERY,
1348 			    my_ready_set);
1349 		else
1350 			(void) idn_send_fin(d, NULL, IDNFIN_QUERY,
1351 			    IDNFIN_ARG_NONE, IDNFIN_OPT_NONE, my_ready_set,
1352 			    NIL_FIN_MASTER);
1353 		if (d != domid)
1354 			IDN_DUNLOCK(d);
1355 	}
1356 
1357 	IDN_DUNLOCK(domid);
1358 	IDN_SYNC_UNLOCK();
1359 }
1360 
1361 static int
1362 idn_send_nego(int domid, idn_msgtype_t *mtp, domainset_t conset)
1363 {
1364 	idn_domain_t	*ldp, *dp;
1365 	int		d, masterid;
1366 	uint_t		dmask;
1367 	uint_t		acknack;
1368 	uint_t		ticket;
1369 	idnneg_dset_t	dset;
1370 	idn_msgtype_t	mt;
1371 	procname_t	proc = "idn_send_nego";
1372 
1373 	ASSERT(IDN_SYNC_IS_LOCKED());
1374 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
1375 
1376 	if (mtp) {
1377 		acknack = mtp->mt_mtype & IDNP_ACKNACK_MASK;
1378 		mt.mt_mtype = mtp->mt_mtype;
1379 		mt.mt_atype = mtp->mt_atype;
1380 		mt.mt_cookie = mtp->mt_cookie;
1381 	} else {
1382 		acknack = 0;
1383 		mt.mt_mtype = IDNP_NEGO;
1384 		mt.mt_atype = 0;
1385 		mt.mt_cookie = IDN_TIMER_PUBLIC_COOKIE;
1386 	}
1387 
1388 	IDN_GLOCK_SHARED();
1389 
1390 	dp = &idn_domain[domid];
1391 	ldp = &idn_domain[idn.localid];
1392 
1393 	if ((idn.state == IDNGS_RECONFIG) ||
1394 	    ((masterid = IDN_GET_MASTERID()) == IDN_NIL_DOMID)) {
1395 		masterid = IDN_GET_NEW_MASTERID();
1396 		if ((masterid == idn.localid) || (masterid == domid)) {
1397 			/*
1398 			 * We only send the new-master "hint" to
1399 			 * "other" domains.  If the new-master is
1400 			 * ourself or we're talking to the new-master
1401 			 * then we need to be accurate about our
1402 			 * real master so that the correct master
1403 			 * is selected.
1404 			 */
1405 			masterid = IDN_NIL_DOMID;
1406 		}
1407 	}
1408 
1409 	DOMAINSET_DEL(conset, idn.localid);
1410 	DOMAINSET_DEL(conset, domid);
1411 	/*
1412 	 * Exclude domains from conset that are on
1413 	 * remote domain's hitlist.  He's not interested
1414 	 * in hearing about them.  SSP is probably requesting
1415 	 * such domains be unlinked - will eventually get to
1416 	 * local domain.
1417 	 */
1418 	conset &= ~idn.domset.ds_hitlist;
1419 	if ((masterid != IDN_NIL_DOMID) &&
1420 	    DOMAIN_IN_SET(idn.domset.ds_hitlist, masterid)) {
1421 		PR_PROTO("%s:%d: masterid(%d) on hitlist(0x%x) -> -1\n",
1422 		    proc, domid, masterid, idn.domset.ds_hitlist);
1423 		/*
1424 		 * Yikes, our chosen master is on the hitlist!
1425 		 */
1426 		masterid = IDN_NIL_DOMID;
1427 	}
1428 
1429 	dmask = IDNNEG_DSET_MYMASK();
1430 	IDNNEG_DSET_INIT(dset, dmask);
1431 	for (d = 0; d < MAX_DOMAINS; d++) {
1432 		int	cpuid;
1433 
1434 		if (!DOMAIN_IN_SET(conset, d))
1435 			continue;
1436 
1437 		if ((cpuid = idn_domain[d].dcpu) == IDN_NIL_DCPU) {
1438 			ASSERT(d != masterid);
1439 			continue;
1440 		}
1441 
1442 		IDNNEG_DSET_SET(dset, d, cpuid, dmask);
1443 	}
1444 	IDNNEG_DSET_SET_MASTER(dset, domid, masterid);
1445 	ASSERT((masterid != IDN_NIL_DOMID) ?
1446 	    (idn_domain[masterid].dcpu != IDN_NIL_DCPU) : 1);
1447 	IDN_GUNLOCK();
1448 
1449 	IDN_DLOCK_SHARED(idn.localid);
1450 	ticket = IDNVOTE_BASICS(ldp->dvote);
1451 	/*
1452 	 * We just want to send basic vote components without an
1453 	 * indication of mastership (master bit) since that's primarily
1454 	 * for local domain's usage.  There is more correct master
1455 	 * indications in the DSET.  Recall that if we were in a
1456 	 * Reconfig we would have transmitted the "new_masterid"
1457 	 * which might conflict with the local domain's vote.v.master
1458 	 * bit if he was originally the master prior to the Reconfig.
1459 	 */
1460 
1461 	PR_PROTO("%s:%d: sending nego%sto (cpu %d) "
1462 	    "[v=0x%x, cs=0x%x, mstr=%d]\n",
1463 	    proc, domid,
1464 	    (acknack & IDNP_ACK) ? "+ack " :
1465 	    (acknack & IDNP_NACK) ? "+nack " : " ",
1466 	    dp->dcpu, ticket, conset, masterid);
1467 
1468 	IDN_MSGTIMER_START(domid, IDNP_NEGO, 0,
1469 	    idn_msg_waittime[IDNP_NEGO], &mt.mt_cookie);
1470 
1471 	IDNXDC(domid, &mt, ticket, dset[0], dset[1], dset[2]);
1472 
1473 	IDN_DUNLOCK(idn.localid);
1474 
1475 	return (0);
1476 }
1477 
1478 static int
1479 idn_recv_nego(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs,
1480     ushort_t dcookie)
1481 {
1482 	uint_t		msg = mtp->mt_mtype;
1483 	idn_msgtype_t	mt;
1484 	idn_domain_t	*dp = &idn_domain[domid];
1485 	idn_xdcargs_t	nargs;
1486 	procname_t	proc = "idn_recv_nego";
1487 
1488 	ASSERT(IDN_SYNC_IS_LOCKED());
1489 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
1490 
1491 	mt.mt_cookie = mtp->mt_cookie;
1492 
1493 #ifdef DEBUG
1494 	if (DOMAIN_IN_SET(idn.domset.ds_hitlist, domid)) {
1495 		PR_HITLIST("%s:%d: dcpu=%d, dstate=%s, msg=%x, "
1496 		    "hitlist=%x\n",
1497 		    proc, domid, dp->dcpu, idnds_str[dp->dstate],
1498 		    msg, idn.domset.ds_hitlist);
1499 	}
1500 #endif /* DEBUG */
1501 
1502 	if (dp->dcpu == IDN_NIL_DCPU) {
1503 		int		cpuid;
1504 		uint_t		ticket;
1505 		/*
1506 		 * Brandnew link.  Need to open a new domain entry.
1507 		 */
1508 		ticket = GET_XARGS_NEGO_TICKET(xargs);
1509 		cpuid = dp->dcpu_last;
1510 		ASSERT(VALID_CPUID(cpuid));
1511 
1512 		if (idn_open_domain(domid, cpuid, ticket) != 0) {
1513 			PR_PROTO("%s:%d: FAILED to open doamin "
1514 			    "(ticket = 0x%x)\n",
1515 			    proc, domid, ticket);
1516 			return (-1);
1517 		}
1518 	}
1519 
1520 	if ((msg & IDNP_MSGTYPE_MASK) == IDNP_NEGO) {
1521 		PR_PROTO("%s:%d: assigned SEND cookie 0x%x\n",
1522 		    proc, domid, dcookie);
1523 		dp->dcookie_send = dcookie;
1524 	}
1525 
1526 	if ((dp->dxp == NULL) && IDNDS_IS_CLOSED(dp)) {
1527 		dp->dxp = &xphase_nego;
1528 		IDN_XSTATE_TRANSITION(dp, IDNXS_PEND);
1529 	} else if (dp->dxp != &xphase_nego) {
1530 		if (msg & IDNP_MSGTYPE_MASK) {
1531 			/*
1532 			 * If we already have a connection to somebody
1533 			 * trying to initiate a connection to us, then
1534 			 * possibly we've awaken from a coma or he did.
1535 			 * In any case, dismantle current connection
1536 			 * and attempt to establish a new one.
1537 			 */
1538 			if (dp->dstate == IDNDS_CONNECTED) {
1539 				DOMAINSET_ADD(idn.domset.ds_relink, domid);
1540 				IDN_HISTORY_LOG(IDNH_RELINK, domid,
1541 				    dp->dstate, idn.domset.ds_relink);
1542 				(void) idn_disconnect(domid, IDNFIN_NORMAL,
1543 				    IDNFIN_ARG_NONE, IDNFIN_SYNC_YES);
1544 			} else {
1545 				mt.mt_mtype = IDNP_NACK;
1546 				mt.mt_atype = msg;
1547 
1548 				CLR_XARGS(nargs);
1549 
1550 				if (DOMAIN_IN_SET(idn.domset.ds_hitlist,
1551 				    domid)) {
1552 					SET_XARGS_NACK_TYPE(nargs,
1553 					    IDNNACK_EXIT);
1554 				} else {
1555 					int	new_masterid;
1556 					int	new_cpuid = IDN_NIL_DCPU;
1557 
1558 					SET_XARGS_NACK_TYPE(nargs,
1559 					    IDNNACK_RETRY);
1560 					IDN_GLOCK_SHARED();
1561 					new_masterid = IDN_GET_NEW_MASTERID();
1562 					if (new_masterid == IDN_NIL_DOMID)
1563 						new_masterid =
1564 						    IDN_GET_MASTERID();
1565 					if (new_masterid != IDN_NIL_DOMID) {
1566 						idn_domain_t	*mdp;
1567 
1568 						mdp = &idn_domain[new_masterid];
1569 						new_cpuid = mdp->dcpu;
1570 					}
1571 					SET_XARGS_NACK_ARG1(nargs,
1572 					    new_masterid);
1573 					SET_XARGS_NACK_ARG2(nargs, new_cpuid);
1574 					IDN_GUNLOCK();
1575 				}
1576 				idn_send_acknack(domid, &mt, nargs);
1577 			}
1578 		}
1579 		return (0);
1580 	}
1581 
1582 	(void) idn_xphase_transition(domid, mtp, xargs);
1583 
1584 	return (0);
1585 }
1586 
1587 /*ARGSUSED1*/
1588 static void
1589 idn_retry_nego(uint_t token, void *arg)
1590 {
1591 	int		domid = IDN_RETRY_TOKEN2DOMID(token);
1592 	int		new_masterid;
1593 	idn_domain_t	*dp = &idn_domain[domid];
1594 	idn_xdcargs_t	xargs;
1595 	procname_t	proc = "idn_retry_nego";
1596 
1597 	ASSERT(IDN_RETRY_TOKEN2TYPE(token) == IDNRETRY_NEGO);
1598 
1599 	IDN_SYNC_LOCK();
1600 	IDN_DLOCK_EXCL(domid);
1601 
1602 	if (dp->dxp != &xphase_nego) {
1603 		STRING(str);
1604 
1605 #ifdef DEBUG
1606 		if (dp->dxp) {
1607 			INUM2STR(dp->dxp->xt_msgtype, str);
1608 		}
1609 #endif /* DEBUG */
1610 
1611 		PR_PROTO("%s:%d: dxp(%s) != NEGO...bailing...\n",
1612 		    proc, domid, dp->dxp ? str : "NULL");
1613 		IDN_DUNLOCK(domid);
1614 		IDN_SYNC_UNLOCK();
1615 		return;
1616 	}
1617 
1618 	if (dp->dxstate != IDNXS_PEND) {
1619 		PR_PROTO("%s:%d: xstate(%s) != %s...bailing\n",
1620 		    proc, domid, idnxs_str[dp->dxstate],
1621 		    idnxs_str[IDNXS_PEND]);
1622 		IDN_DUNLOCK(domid);
1623 		IDN_SYNC_UNLOCK();
1624 		return;
1625 	}
1626 
1627 	IDN_GLOCK_SHARED();
1628 	if (idn.state == IDNGS_RECONFIG) {
1629 		/*
1630 		 * Have to try again later after
1631 		 * reconfig has completed.
1632 		 */
1633 		PR_PROTO("%s:%d: reconfig in-progress...try later\n",
1634 		    proc, domid);
1635 		idn_retry_submit(idn_retry_nego, NULL, token,
1636 		    idn_msg_retrytime[IDNP_NEGO]);
1637 		IDN_GUNLOCK();
1638 		IDN_DUNLOCK(domid);
1639 		IDN_SYNC_UNLOCK();
1640 		return;
1641 	}
1642 	new_masterid = IDN_GET_NEW_MASTERID();
1643 	if ((idn.state == IDNGS_CONNECT) &&
1644 	    (new_masterid != IDN_NIL_DOMID) &&
1645 	    (domid != new_masterid) &&
1646 	    (idn.localid != new_masterid)) {
1647 		/*
1648 		 * We have a new master pending and this
1649 		 * guy isn't it.  Wait until the local domain
1650 		 * has a chance to connect with the new
1651 		 * master before going forward with this
1652 		 * guy.
1653 		 */
1654 		PR_PROTO("%s:%d: waiting for connect to new master %d\n",
1655 		    proc, domid, IDN_GET_NEW_MASTERID());
1656 		idn_retry_submit(idn_retry_nego, NULL, token,
1657 		    idn_msg_retrytime[IDNP_NEGO]);
1658 		IDN_GUNLOCK();
1659 		IDN_DUNLOCK(domid);
1660 		IDN_SYNC_UNLOCK();
1661 		return;
1662 	}
1663 	IDN_GUNLOCK();
1664 
1665 	(void) idn_xphase_transition(domid, NULL, xargs);
1666 
1667 	IDN_DUNLOCK(domid);
1668 	IDN_SYNC_UNLOCK();
1669 }
1670 
1671 static int
1672 idn_check_nego(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs)
1673 {
1674 	int		d, new_masterid, masterid;
1675 	int		cpuid, m_cpuid = -1;
1676 	uint_t		dmask;
1677 	uint_t		msg = mtp ? mtp->mt_mtype : 0;
1678 	idn_domain_t	*dp, *ldp;
1679 	domainset_t	con_set, pending_set;
1680 	idnneg_dset_t	dset;
1681 	procname_t	proc = "idn_check_nego";
1682 
1683 	ASSERT(IDN_SYNC_IS_LOCKED());
1684 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
1685 
1686 	dp = &idn_domain[domid];
1687 	ldp = &idn_domain[idn.localid];
1688 
1689 	if (msg & IDNP_NACK) {
1690 		if (GET_XARGS_NACK_TYPE(xargs) == IDNNACK_EXIT) {
1691 			PR_HITLIST("%s:%d(%s): (msg=%x) EXIT received, "
1692 			    "adding to hitlist %x -> %x\n",
1693 			    proc, domid, idnds_str[dp->dstate], msg,
1694 			    idn.domset.ds_hitlist,
1695 			    idn.domset.ds_hitlist | DOMAINSET(domid));
1696 
1697 			DOMAINSET_ADD(idn.domset.ds_hitlist, domid);
1698 			return (-1);
1699 		} else {
1700 			return (0);
1701 		}
1702 	}
1703 
1704 	if (DOMAIN_IN_SET(idn.domset.ds_hitlist, domid)) {
1705 		PR_HITLIST("%s:%d(%s): (msg=%x) domain in hitlist (%x) - "
1706 		    "exiting phase\n",
1707 		    proc, domid, idnds_str[dp->dstate], msg,
1708 		    idn.domset.ds_hitlist);
1709 		return (-1);
1710 	}
1711 
1712 	if ((dp->dstate == IDNDS_NEGO_PEND) && (msg & IDNP_MSGTYPE_MASK) &&
1713 	    (msg & IDNP_ACK))		/* nego+ack */
1714 		return (1);
1715 
1716 	dmask = (uint_t)-1;
1717 
1718 	IDN_GLOCK_EXCL();
1719 	if (idn.state == IDNGS_DISCONNECT) {
1720 		PR_PROTO("%s:%d: DISCONNECT in-progress >>> EXIT\n",
1721 		    proc, domid);
1722 		IDN_GUNLOCK();
1723 		return (-1);
1724 	} else if (idn.state == IDNGS_OFFLINE) {
1725 		IDN_GSTATE_TRANSITION(IDNGS_CONNECT);
1726 		IDN_PREP_HWINIT();
1727 		IDN_DLOCK_EXCL(idn.localid);
1728 		ldp->dvote.v.connected = 0;
1729 		IDN_DUNLOCK(idn.localid);
1730 	}
1731 
1732 	if (!DOMAIN_IN_SET(idn.domset.ds_trans_on, domid)) {
1733 		DOMAINSET_ADD(idn.domset.ds_trans_on, domid);
1734 		IDN_HISTORY_LOG(IDNH_NEGO, domid,
1735 		    idn.domset.ds_trans_on,
1736 		    idn.domset.ds_connected);
1737 	}
1738 
1739 	switch (idn.state) {
1740 	case IDNGS_RECONFIG:
1741 		PR_PROTO("%s:%d: RECONFIG in-progress >>> RETRY\n",
1742 		    proc, domid);
1743 		IDN_GUNLOCK();
1744 		return (1);
1745 
1746 	case IDNGS_CONNECT:
1747 		new_masterid = IDN_GET_NEW_MASTERID();
1748 		if ((new_masterid != IDN_NIL_DOMID) &&
1749 		    (domid != new_masterid) &&
1750 		    (idn.localid != new_masterid)) {
1751 			PR_PROTO("%s:%d: waiting for connect to "
1752 			    "new master %d\n",
1753 			    proc, domid, IDN_GET_NEW_MASTERID());
1754 			IDN_GUNLOCK();
1755 			return (1);
1756 		}
1757 		break;
1758 
1759 	default:
1760 		break;
1761 	}
1762 
1763 	ASSERT((idn.state == IDNGS_CONNECT) || (idn.state == IDNGS_ONLINE));
1764 
1765 	con_set = 0;
1766 
1767 	if (msg) {
1768 		idn_domain_t	*mdp;
1769 		idn_vote_t	vote;
1770 
1771 		vote.ticket = GET_XARGS_NEGO_TICKET(xargs);
1772 		/*
1773 		 * Sender should note have set master bit,
1774 		 * but just in case clear it so local domain
1775 		 * doesn't get confused.
1776 		 */
1777 		vote.v.master = 0;
1778 		dp->dvote.ticket = vote.ticket;
1779 		GET_XARGS_NEGO_DSET(xargs, dset);
1780 		/*LINTED*/
1781 		IDNNEG_DSET_GET_MASK(dset, domid, dmask);
1782 		IDNNEG_DSET_GET_MASTER(dset, new_masterid);
1783 		if (new_masterid == IDNNEG_NO_MASTER) {
1784 			new_masterid = IDN_NIL_DOMID;
1785 		} else {
1786 			/*
1787 			 * Remote domain has a master.  Find
1788 			 * his cpuid in the dset.  We may need
1789 			 * it to initiate a connection.
1790 			 */
1791 			if (new_masterid == domid) {
1792 				m_cpuid = dp->dcpu;
1793 			} else {
1794 				IDNNEG_DSET_GET(dset, new_masterid, m_cpuid,
1795 				    dmask);
1796 				if (m_cpuid == -1) {
1797 					/*
1798 					 * Something is bogus if remote domain
1799 					 * is reporting a valid masterid, but
1800 					 * doesn't have the cpuid for it.
1801 					 */
1802 					cmn_err(CE_WARN,
1803 					    "IDN: 209: remote domain (ID "
1804 					    "%d, CPU %d) reporting master "
1805 					    "(ID %d) without CPU ID",
1806 					    domid, dp->dcpu, new_masterid);
1807 					DOMAINSET_ADD(idn.domset.ds_hitlist,
1808 					    domid);
1809 					IDN_GUNLOCK();
1810 					return (-1);
1811 				}
1812 			}
1813 		}
1814 
1815 		for (d = 0; d < MAX_DOMAINS; d++) {
1816 			if ((d == idn.localid) || (d == domid))
1817 				continue;
1818 			IDNNEG_DSET_GET(dset, d, cpuid, dmask);
1819 			if (cpuid != -1) {
1820 				DOMAINSET_ADD(con_set, d);
1821 			}
1822 		}
1823 
1824 #ifdef DEBUG
1825 		if (idn.domset.ds_hitlist) {
1826 			PR_HITLIST("%s:%d: con_set %x -> %x (hitlist = %x)\n",
1827 			    proc, domid, con_set,
1828 			    con_set & ~idn.domset.ds_hitlist,
1829 			    idn.domset.ds_hitlist);
1830 		}
1831 #endif /* DEBUG */
1832 
1833 		con_set &= ~idn.domset.ds_hitlist;
1834 
1835 		ASSERT(!DOMAIN_IN_SET(con_set, idn.localid));
1836 		ASSERT(!DOMAIN_IN_SET(con_set, domid));
1837 
1838 		if ((new_masterid != IDN_NIL_DOMID) &&
1839 		    DOMAIN_IN_SET(idn.domset.ds_hitlist, new_masterid)) {
1840 			PR_HITLIST("%s:%d: new_mstr %d -> -1 (hitlist = %x)\n",
1841 			    proc, domid, new_masterid,
1842 			    idn.domset.ds_hitlist);
1843 			IDN_GUNLOCK();
1844 			return (1);
1845 		}
1846 
1847 		if (idn_select_master(domid, new_masterid, m_cpuid) < 0) {
1848 			/*
1849 			 * Returns w/GLOCK dropped if error.
1850 			 */
1851 			return (1);
1852 		}
1853 
1854 		masterid = IDN_GET_MASTERID();
1855 		ASSERT(masterid != IDN_NIL_DOMID);
1856 
1857 		if (idn.state == IDNGS_CONNECT) {
1858 			/*
1859 			 * This is the initial connection for
1860 			 * the local domain.
1861 			 */
1862 			IDN_DLOCK_EXCL(idn.localid);
1863 
1864 			if (masterid == idn.localid) {
1865 				if (idn_master_init() < 0) {
1866 					cmn_err(CE_WARN,
1867 					    "IDN: 210: failed to init "
1868 					    "MASTER context");
1869 					ldp->dvote.v.master = 0;
1870 					IDN_DUNLOCK(idn.localid);
1871 					IDN_GSTATE_TRANSITION(IDNGS_DISCONNECT);
1872 					IDN_SET_MASTERID(IDN_NIL_DOMID);
1873 					IDN_GUNLOCK();
1874 					return (-1);
1875 				}
1876 				DSLAB_LOCK_EXCL(idn.localid);
1877 				ldp->dslab_state = DSLAB_STATE_LOCAL;
1878 				DSLAB_UNLOCK(idn.localid);
1879 				ldp->dvote.v.connected = 1;
1880 			} else {
1881 				/*
1882 				 * Either the remote domain is the
1883 				 * master or its a new slave trying
1884 				 * to connect to us.  We can't allow
1885 				 * further progress until we've
1886 				 * sync'd up with the master.
1887 				 */
1888 				if (masterid != domid) {
1889 					IDN_DUNLOCK(idn.localid);
1890 					IDN_GUNLOCK();
1891 					return (1);
1892 				}
1893 				DSLAB_LOCK_EXCL(idn.localid);
1894 				ldp->dslab_state = DSLAB_STATE_REMOTE;
1895 				DSLAB_UNLOCK(idn.localid);
1896 			}
1897 			IDN_DUNLOCK(idn.localid);
1898 			/*
1899 			 * We've sync'd up with the new master.
1900 			 */
1901 			IDN_GSTATE_TRANSITION(IDNGS_ONLINE);
1902 		}
1903 
1904 		mdp = &idn_domain[masterid];
1905 
1906 		if ((masterid != domid) && !IDNDS_CONFIG_DONE(mdp)) {
1907 			/*
1908 			 * We can't progress any further with
1909 			 * other domains until we've exchanged all
1910 			 * the necessary CFG info with the master,
1911 			 * i.e. until we have a mailbox area from
1912 			 * which we can allocate mailboxes to
1913 			 * other domains.
1914 			 */
1915 			PR_PROTO("%s:%d: still exchanging CFG "
1916 			    "w/master(%d)\n", proc, domid, masterid);
1917 			IDN_GUNLOCK();
1918 			return (1);
1919 		}
1920 
1921 		DSLAB_LOCK_EXCL(domid);
1922 		dp->dslab_state = ldp->dslab_state;
1923 		DSLAB_UNLOCK(domid);
1924 		if (idn.state != IDNGS_ONLINE) {
1925 			IDN_GSTATE_TRANSITION(IDNGS_ONLINE);
1926 		}
1927 	}
1928 
1929 	IDN_GUNLOCK();
1930 
1931 	pending_set = con_set;
1932 	pending_set &= ~(idn.domset.ds_trans_on | idn.domset.ds_connected);
1933 	idn.domset.ds_trans_on |= pending_set;
1934 
1935 	con_set |= idn.domset.ds_trans_on | idn.domset.ds_connected;
1936 	con_set &= ~idn.domset.ds_trans_off;
1937 	DOMAINSET_ADD(con_set, idn.localid);
1938 
1939 	if (dp->dsync.s_cmd != IDNSYNC_CONNECT) {
1940 		idn_sync_exit(domid, IDNSYNC_DISCONNECT);
1941 		idn_sync_enter(domid, IDNSYNC_CONNECT,
1942 		    con_set, DOMAINSET(idn.localid), idn_xstate_transfunc,
1943 		    (void *)IDNP_CON);
1944 	}
1945 
1946 	/*
1947 	 * Get this domain registered as an expected domain on
1948 	 * the remaining domains in the CONNECT synchronization.
1949 	 */
1950 	(void) idn_sync_register(domid, IDNSYNC_CONNECT, 0, IDNSYNC_REG_NEW);
1951 
1952 	/*
1953 	 * Note that if (msg == 0), i.e. then there will be
1954 	 * no dset and also pending_set will be 0.
1955 	 * So, the following loop will never attempt to
1956 	 * look at the dset unless (msg != 0), implying
1957 	 * that we've been through the initial code above
1958 	 * and have initialized dmask.
1959 	 */
1960 	ASSERT(pending_set ? (dmask != (uint_t)-1) : 1);
1961 
1962 	for (d = 0; d < MAX_DOMAINS; d++) {
1963 		int	rv;
1964 
1965 		if (!DOMAIN_IN_SET(pending_set, d))
1966 			continue;
1967 
1968 		ASSERT((d != idn.localid) && (d != domid));
1969 
1970 		dp = &idn_domain[d];
1971 
1972 		IDNNEG_DSET_GET(dset, d, cpuid, dmask);
1973 		if (cpuid == -1) {
1974 			PR_PROTO("%s:%d: failed to get cpuid from dset "
1975 			    "for domain %d (pset = 0x%x)\n",
1976 			    proc, domid, d, pending_set);
1977 			DOMAINSET_DEL(idn.domset.ds_trans_on, d);
1978 			continue;
1979 		}
1980 
1981 		IDN_DLOCK_EXCL(d);
1982 		if ((rv = idn_open_domain(d, cpuid, 0)) != 0) {
1983 			PR_PROTO("%s:%d: failed "
1984 			    "idn_open_domain(%d,%d,0) (rv = %d)\n",
1985 			    proc, domid, d, cpuid, rv);
1986 			if (rv < 0) {
1987 				cmn_err(CE_WARN,
1988 				    "IDN: 205: (%s) failed to "
1989 				    "open-domain(%d,%d)",
1990 				    proc, d, cpuid);
1991 				DOMAINSET_DEL(idn.domset.ds_trans_on, d);
1992 			} else if (DOMAIN_IN_SET(idn.domset.ds_trans_off, d)) {
1993 				/*
1994 				 * We've requested to connect to a domain
1995 				 * from which we're disconnecting.  We
1996 				 * better mark this guy for relinking.
1997 				 */
1998 				DOMAINSET_ADD(idn.domset.ds_relink, d);
1999 				IDN_HISTORY_LOG(IDNH_RELINK, d, dp->dstate,
2000 				    idn.domset.ds_relink);
2001 			}
2002 			IDN_DUNLOCK(d);
2003 			continue;
2004 		}
2005 
2006 		(void) idn_connect(d);
2007 
2008 		IDN_DUNLOCK(d);
2009 	}
2010 
2011 	return (0);
2012 }
2013 
2014 /*ARGSUSED*/
2015 static void
2016 idn_action_nego_pend(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs)
2017 {
2018 	uint_t		msg = mtp ? mtp->mt_mtype : 0;
2019 	idn_msgtype_t	mt;
2020 	domainset_t	con_set;
2021 
2022 	ASSERT(IDN_SYNC_IS_LOCKED());
2023 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
2024 
2025 	con_set = idn.domset.ds_trans_on | idn.domset.ds_connected;
2026 	con_set &= ~idn.domset.ds_trans_off;
2027 
2028 	if (!msg) {
2029 		(void) idn_send_nego(domid, NULL, con_set);
2030 	} else {
2031 		mt.mt_mtype = IDNP_NEGO | IDNP_ACK;
2032 		mt.mt_atype = 0;
2033 		mt.mt_cookie = mtp->mt_cookie;
2034 		(void) idn_send_nego(domid, &mt, con_set);
2035 	}
2036 }
2037 
2038 /*ARGSUSED*/
2039 static void
2040 idn_error_nego(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs)
2041 {
2042 	int	new_masterid, new_cpuid;
2043 	int	retry = 1;
2044 	uint_t	msg = mtp ? mtp->mt_mtype : 0;
2045 	uint_t	token;
2046 
2047 	ASSERT(IDN_SYNC_IS_LOCKED());
2048 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
2049 
2050 	if (msg & IDNP_NACK) {
2051 		idn_nack_t	nack;
2052 
2053 		nack = GET_XARGS_NACK_TYPE(xargs);
2054 		switch (nack) {
2055 		case IDNNACK_RETRY:
2056 			new_masterid = (int)GET_XARGS_NACK_ARG1(xargs);
2057 			new_cpuid    = (int)GET_XARGS_NACK_ARG2(xargs);
2058 			break;
2059 
2060 		case IDNNACK_EXIT:
2061 			retry = 0;
2062 			/*FALLTHROUGH*/
2063 
2064 		default:
2065 			new_masterid = IDN_NIL_DOMID;
2066 			new_cpuid    = IDN_NIL_DCPU;
2067 			break;
2068 		}
2069 		idn_nego_cleanup_check(domid, new_masterid, new_cpuid);
2070 	}
2071 
2072 	if (msg & IDNP_MSGTYPE_MASK) {
2073 		idn_msgtype_t	mt;
2074 		idn_xdcargs_t	nargs;
2075 
2076 		mt.mt_mtype = IDNP_NACK;
2077 		mt.mt_atype = msg;
2078 		mt.mt_cookie = mtp->mt_cookie;
2079 		CLR_XARGS(nargs);
2080 		SET_XARGS_NACK_TYPE(nargs, IDNNACK_RETRY);
2081 		IDN_GLOCK_SHARED();
2082 		new_masterid = IDN_GET_NEW_MASTERID();
2083 		if (new_masterid == IDN_NIL_DOMID)
2084 			new_masterid = IDN_GET_MASTERID();
2085 		if (new_masterid != IDN_NIL_DOMID)
2086 			new_cpuid = idn_domain[new_masterid].dcpu;
2087 		else
2088 			new_cpuid = IDN_NIL_DCPU;
2089 		SET_XARGS_NACK_ARG1(nargs, new_masterid);
2090 		SET_XARGS_NACK_ARG2(nargs, new_cpuid);
2091 		IDN_GUNLOCK();
2092 		idn_send_acknack(domid, &mt, nargs);
2093 	}
2094 
2095 	if (retry) {
2096 		token = IDN_RETRY_TOKEN(domid, IDNRETRY_NEGO);
2097 		idn_retry_submit(idn_retry_nego, NULL, token,
2098 		    idn_msg_retrytime[(int)IDNRETRY_NEGO]);
2099 	} else {
2100 		DOMAINSET_DEL(idn.domset.ds_relink, domid);
2101 		IDN_RESET_COOKIES(domid);
2102 		(void) idn_disconnect(domid, IDNFIN_NORMAL, IDNFIN_ARG_NONE,
2103 		    IDNDS_SYNC_TYPE(&idn_domain[domid]));
2104 	}
2105 }
2106 
2107 /*ARGSUSED*/
2108 static void
2109 idn_action_nego_sent(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs)
2110 {
2111 	uint_t		msg = mtp ? mtp->mt_mtype : 0;
2112 	domainset_t	conset;
2113 	idn_msgtype_t	mt;
2114 
2115 	ASSERT(IDN_SYNC_IS_LOCKED());
2116 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
2117 
2118 	mt.mt_cookie = mtp ? mtp->mt_cookie : 0;
2119 
2120 	conset = idn.domset.ds_trans_on | idn.domset.ds_connected;
2121 	conset &= ~idn.domset.ds_trans_off;
2122 
2123 	if ((msg & IDNP_ACKNACK_MASK) == 0) {
2124 		/*
2125 		 * nego
2126 		 */
2127 		mt.mt_mtype = IDNP_NEGO | IDNP_ACK;
2128 		mt.mt_atype = 0;
2129 		(void) idn_send_nego(domid, &mt, conset);
2130 	} else if (msg & IDNP_MSGTYPE_MASK) {
2131 		int		d;
2132 		idn_xdcargs_t	nargs;
2133 		idnneg_dset_t	dset;
2134 		uint_t		dmask;
2135 		idn_vote_t	vote;
2136 
2137 		mt.mt_mtype = IDNP_ACK;
2138 		mt.mt_atype = msg;
2139 		DOMAINSET_DEL(conset, idn.localid);
2140 		DOMAINSET_DEL(conset, domid);
2141 
2142 		dmask = IDNNEG_DSET_MYMASK();
2143 		IDNNEG_DSET_INIT(dset, dmask);
2144 		for (d = 0; d < MAX_DOMAINS; d++) {
2145 			int	cpuid;
2146 
2147 			if (!DOMAIN_IN_SET(conset, d))
2148 				continue;
2149 
2150 			if ((cpuid = idn_domain[d].dcpu) == IDN_NIL_DCPU)
2151 				continue;
2152 
2153 			IDNNEG_DSET_SET(dset, d, cpuid, dmask);
2154 		}
2155 		IDNNEG_DSET_SET_MASTER(dset, domid, IDN_GET_MASTERID());
2156 		ASSERT((IDN_GET_MASTERID() != IDN_NIL_DOMID) ?
2157 		    (idn_domain[IDN_GET_MASTERID()].dcpu != IDN_NIL_DCPU) : 1);
2158 		vote.ticket = idn_domain[idn.localid].dvote.ticket;
2159 		vote.v.master = 0;
2160 		CLR_XARGS(nargs);
2161 		SET_XARGS_NEGO_TICKET(nargs, vote.ticket);
2162 		SET_XARGS_NEGO_DSET(nargs, dset);
2163 		/*
2164 		 * nego+ack
2165 		 */
2166 		idn_send_acknack(domid, &mt, nargs);
2167 	} else {
2168 		uint_t		token;
2169 		int		new_masterid, new_cpuid;
2170 		int		retry = 1;
2171 		idn_nack_t	nack;
2172 		/*
2173 		 * nack - retry
2174 		 *
2175 		 * It's possible if we've made it this far that
2176 		 * we may have already chosen a master and this
2177 		 * dude might be it!  If it is we need to clean up.
2178 		 */
2179 		nack = GET_XARGS_NACK_TYPE(xargs);
2180 		switch (nack) {
2181 		case IDNNACK_RETRY:
2182 			new_masterid = (int)GET_XARGS_NACK_ARG1(xargs);
2183 			new_cpuid = (int)GET_XARGS_NACK_ARG2(xargs);
2184 			break;
2185 
2186 		case IDNNACK_EXIT:
2187 			retry = 0;
2188 			/*FALLTHROUGH*/
2189 
2190 		default:
2191 			new_masterid = IDN_NIL_DOMID;
2192 			new_cpuid = IDN_NIL_DCPU;
2193 			break;
2194 		}
2195 
2196 		idn_nego_cleanup_check(domid, new_masterid, new_cpuid);
2197 
2198 		if (retry) {
2199 			token = IDN_RETRY_TOKEN(domid, IDNRETRY_NEGO);
2200 			idn_retry_submit(idn_retry_nego, NULL, token,
2201 			    idn_msg_retrytime[(int)IDNRETRY_NEGO]);
2202 		} else {
2203 			DOMAINSET_DEL(idn.domset.ds_relink, domid);
2204 			IDN_RESET_COOKIES(domid);
2205 			(void) idn_disconnect(domid, IDNFIN_NORMAL,
2206 			    IDNFIN_ARG_NONE,
2207 			    IDNDS_SYNC_TYPE(&idn_domain[domid]));
2208 		}
2209 	}
2210 }
2211 
2212 /*ARGSUSED*/
2213 static void
2214 idn_action_nego_rcvd(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs)
2215 {
2216 	uint_t	msg = mtp ? mtp->mt_mtype : 0;
2217 
2218 	ASSERT(IDN_SYNC_IS_LOCKED());
2219 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
2220 
2221 	if (msg & IDNP_NACK) {
2222 		uint_t		token;
2223 		int		new_masterid, new_cpuid;
2224 		int		retry = 1;
2225 		idn_nack_t	nack;
2226 		/*
2227 		 * nack - retry.
2228 		 *
2229 		 * At this stage of receiving a nack we need to
2230 		 * check whether we need to start over again with
2231 		 * selecting a new master.
2232 		 */
2233 		nack = GET_XARGS_NACK_TYPE(xargs);
2234 		switch (nack) {
2235 		case IDNNACK_RETRY:
2236 			new_masterid = (int)GET_XARGS_NACK_ARG1(xargs);
2237 			new_cpuid = (int)GET_XARGS_NACK_ARG2(xargs);
2238 			break;
2239 
2240 		case IDNNACK_EXIT:
2241 			retry = 0;
2242 			/*FALLTHROUGH*/
2243 
2244 		default:
2245 			new_masterid = IDN_NIL_DOMID;
2246 			new_cpuid = IDN_NIL_DCPU;
2247 			break;
2248 		}
2249 
2250 		idn_nego_cleanup_check(domid, new_masterid, new_cpuid);
2251 
2252 		if (retry) {
2253 			token = IDN_RETRY_TOKEN(domid, IDNRETRY_NEGO);
2254 			idn_retry_submit(idn_retry_nego, NULL, token,
2255 			    idn_msg_retrytime[(int)IDNRETRY_NEGO]);
2256 		} else {
2257 			DOMAINSET_DEL(idn.domset.ds_relink, domid);
2258 			IDN_RESET_COOKIES(domid);
2259 			(void) idn_disconnect(domid, IDNFIN_NORMAL,
2260 			    IDNFIN_ARG_NONE,
2261 			    IDNDS_SYNC_TYPE(&idn_domain[domid]));
2262 		}
2263 	}
2264 }
2265 
2266 static void
2267 idn_final_nego(int domid)
2268 {
2269 	idn_domain_t	*dp = &idn_domain[domid];
2270 
2271 	ASSERT(IDN_SYNC_IS_LOCKED());
2272 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
2273 
2274 	(void) idn_retry_terminate(IDN_RETRY_TOKEN(domid, IDNRETRY_NEGO));
2275 
2276 	ASSERT(dp->dstate == IDNDS_CONFIG);
2277 
2278 	dp->dxp = NULL;
2279 	IDN_XSTATE_TRANSITION(dp, IDNXS_NIL);
2280 
2281 	idn_send_config(domid, 1);
2282 }
2283 
2284 /*
2285  */
2286 /*ARGSUSED1*/
2287 static void
2288 idn_exit_nego(int domid, uint_t msgtype)
2289 {
2290 	idn_domain_t	*dp;
2291 	idn_fin_t	fintype;
2292 
2293 	ASSERT(IDN_SYNC_IS_LOCKED());
2294 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
2295 
2296 	dp = &idn_domain[domid];
2297 
2298 	fintype = msgtype ? IDNFIN_NORMAL : IDNFIN_FORCE_HARD;
2299 
2300 	(void) idn_retry_terminate(IDN_RETRY_TOKEN(domid, IDNRETRY_NEGO));
2301 
2302 	ASSERT(!DOMAIN_IN_SET(idn.domset.ds_connected, domid));
2303 	ASSERT(!DOMAIN_IN_SET(idn.domset.ds_ready_on, domid));
2304 	ASSERT(dp->dxp == &xphase_nego);
2305 
2306 	idn_nego_cleanup_check(domid, IDN_NIL_DOMID, IDN_NIL_DCPU);
2307 
2308 	IDN_GLOCK_SHARED();
2309 	if ((idn.state != IDNGS_DISCONNECT) &&
2310 	    !DOMAIN_IN_SET(idn.domset.ds_hitlist, domid)) {
2311 		DOMAINSET_ADD(idn.domset.ds_relink, domid);
2312 		IDN_HISTORY_LOG(IDNH_RELINK, domid, dp->dstate,
2313 		    idn.domset.ds_relink);
2314 	} else {
2315 		idn_update_op(IDNOP_ERROR, DOMAINSET(domid), NULL);
2316 		DOMAINSET_DEL(idn.domset.ds_relink, domid);
2317 	}
2318 	IDN_GUNLOCK();
2319 	/*
2320 	 * Reset send cookie to 0 so that receiver does not validate
2321 	 * cookie.  This is necessary since at this early stage it's
2322 	 * possible we may not have exchanged appropriate cookies.
2323 	 */
2324 	IDN_RESET_COOKIES(domid);
2325 	(void) idn_disconnect(domid, fintype, IDNFIN_ARG_NONE,
2326 	    IDNDS_SYNC_TYPE(dp));
2327 }
2328 
2329 static void
2330 idn_nego_cleanup_check(int domid, int new_masterid, int new_cpuid)
2331 {
2332 	idn_domain_t	*ldp, *dp;
2333 	procname_t	proc = "idn_nego_cleanup_check";
2334 
2335 	ASSERT(IDN_SYNC_IS_LOCKED());
2336 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
2337 
2338 	dp = &idn_domain[domid];
2339 	ldp = &idn_domain[idn.localid];
2340 
2341 	IDN_GLOCK_EXCL();
2342 
2343 	if (((idn.state == IDNGS_ONLINE) && !idn.domset.ds_connected) ||
2344 	    (idn.state == IDNGS_CONNECT)) {
2345 		domainset_t	trans_on;
2346 		int		masterid;
2347 		int		retry_domid = IDN_NIL_DOMID;
2348 		int		rv;
2349 
2350 		IDN_DLOCK_EXCL(idn.localid);
2351 		masterid = (idn.state == IDNGS_ONLINE) ?
2352 		    IDN_GET_MASTERID() : IDN_GET_NEW_MASTERID();
2353 		trans_on = idn.domset.ds_trans_on;
2354 		DOMAINSET_DEL(trans_on, domid);
2355 		if (trans_on == 0) {
2356 			int		d;
2357 			domainset_t	relink = idn.domset.ds_relink;
2358 			/*
2359 			 * This was the only guy we were trying
2360 			 * to connect with.
2361 			 */
2362 			ASSERT((idn.state == IDNGS_ONLINE) ?
2363 			    ((idn.localid == masterid) ||
2364 			    (domid == masterid)) : 1);
2365 			if (idn.localid == masterid)
2366 				idn_master_deinit();
2367 			ldp->dvote.v.connected = 0;
2368 			ldp->dvote.v.master = 0;
2369 			dp->dvote.v.master = 0;
2370 			IDN_SET_MASTERID(IDN_NIL_DOMID);
2371 			IDN_SET_NEW_MASTERID(new_masterid);
2372 			IDN_GSTATE_TRANSITION(IDNGS_CONNECT);
2373 			IDN_PREP_HWINIT();
2374 			IDN_DUNLOCK(idn.localid);
2375 			IDN_GUNLOCK();
2376 			/*
2377 			 * If there's a new master available then
2378 			 * just try and relink with him unless
2379 			 * it's ourself.
2380 			 */
2381 			if ((new_masterid != IDN_NIL_DOMID) &&
2382 			    (new_masterid != idn.localid) &&
2383 			    (new_masterid != domid)) {
2384 				IDN_DLOCK_EXCL(new_masterid);
2385 				rv = idn_open_domain(new_masterid,
2386 				    new_cpuid, 0);
2387 				if (rv < 0) {
2388 					cmn_err(CE_WARN,
2389 					    "IDN: 205: (%s) failed to "
2390 					    "open-domain(%d,%d)",
2391 					    proc, new_masterid, new_cpuid);
2392 					IDN_GLOCK_EXCL();
2393 					IDN_SET_NEW_MASTERID(IDN_NIL_DOMID);
2394 					IDN_GUNLOCK();
2395 				} else {
2396 					relink = DOMAINSET(new_masterid);
2397 				}
2398 				IDN_DUNLOCK(new_masterid);
2399 			}
2400 			DOMAINSET_DEL(relink, domid);
2401 			if (relink)
2402 				for (d = 0; d < MAX_DOMAINS; d++) {
2403 					if (!DOMAIN_IN_SET(relink, d))
2404 						continue;
2405 					retry_domid = d;
2406 					break;
2407 				}
2408 		} else if (domid == masterid) {
2409 			/*
2410 			 * There are other domains we were trying
2411 			 * to connect to.  As long as the chosen
2412 			 * master was somebody other then this
2413 			 * domain that nack'd us, life is cool, but
2414 			 * if it was this remote domain we'll need
2415 			 * to start over.
2416 			 */
2417 			IDN_DUNLOCK(idn.localid);
2418 			dp->dvote.v.master = 0;
2419 			IDN_SET_MASTERID(IDN_NIL_DOMID);
2420 			IDN_SET_NEW_MASTERID(new_masterid);
2421 
2422 			if (idn.state == IDNGS_ONLINE) {
2423 				IDN_GKSTAT_GLOBAL_EVENT(gk_reconfigs,
2424 				    gk_reconfig_last);
2425 				IDN_GSTATE_TRANSITION(IDNGS_RECONFIG);
2426 				IDN_GUNLOCK();
2427 				idn_unlink_domainset(trans_on, IDNFIN_NORMAL,
2428 				    IDNFIN_ARG_NONE,
2429 				    IDNFIN_OPT_RELINK,
2430 				    BOARDSET_ALL);
2431 			} else if ((new_masterid != IDN_NIL_DOMID) &&
2432 			    (new_masterid != idn.localid) &&
2433 			    (new_masterid != domid) &&
2434 			    !DOMAIN_IN_SET(trans_on, new_masterid)) {
2435 				IDN_GUNLOCK();
2436 				IDN_DLOCK_EXCL(new_masterid);
2437 				rv = idn_open_domain(new_masterid,
2438 				    new_cpuid, 0);
2439 				IDN_GLOCK_EXCL();
2440 				IDN_DUNLOCK(new_masterid);
2441 				if (rv < 0) {
2442 					cmn_err(CE_WARN,
2443 					    "IDN: 205: (%s) failed to "
2444 					    "open-domain(%d,%d)",
2445 					    proc, new_masterid,
2446 					    new_cpuid);
2447 					IDN_SET_NEW_MASTERID(IDN_NIL_DOMID);
2448 					new_masterid = IDN_NIL_DOMID;
2449 				} else {
2450 					retry_domid = new_masterid;
2451 				}
2452 				IDN_GUNLOCK();
2453 			} else {
2454 				IDN_GUNLOCK();
2455 			}
2456 		} else {
2457 			IDN_DUNLOCK(idn.localid);
2458 			IDN_GUNLOCK();
2459 		}
2460 		if (retry_domid != IDN_NIL_DOMID) {
2461 			uint_t		token;
2462 			idn_domain_t	*rdp = &idn_domain[retry_domid];
2463 
2464 			IDN_DLOCK_EXCL(retry_domid);
2465 			rdp->dxp = &xphase_nego;
2466 			IDN_XSTATE_TRANSITION(rdp, IDNXS_PEND);
2467 			IDN_DUNLOCK(retry_domid);
2468 			token = IDN_RETRY_TOKEN(retry_domid, IDNRETRY_NEGO);
2469 			idn_retry_submit(idn_retry_nego, NULL, token,
2470 			    idn_msg_retrytime[(int)IDNRETRY_NEGO]);
2471 		}
2472 	} else {
2473 		IDN_GUNLOCK();
2474 	}
2475 }
2476 
2477 static int
2478 idn_send_con(int domid, idn_msgtype_t *mtp, idn_con_t contype, domainset_t
2479     conset)
2480 {
2481 	idn_msgtype_t	mt;
2482 	uint_t		acknack;
2483 	procname_t	proc = "idn_send_con";
2484 
2485 	ASSERT(IDN_SYNC_IS_LOCKED());
2486 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
2487 
2488 	if (mtp) {
2489 		acknack = mtp->mt_mtype & IDNP_ACKNACK_MASK;
2490 		mt.mt_mtype = mtp->mt_mtype;
2491 		mt.mt_atype = mtp->mt_atype;
2492 		mt.mt_cookie = mtp->mt_cookie;
2493 	} else {
2494 		acknack = 0;
2495 		mt.mt_mtype = IDNP_CON;
2496 		mt.mt_atype = 0;
2497 		/*
2498 		 * For simple CON queries we want a unique
2499 		 * timer assigned.  For others, they
2500 		 * effectively share one.
2501 		 */
2502 		if (contype == IDNCON_QUERY)
2503 			mt.mt_cookie = 0;
2504 		else
2505 			mt.mt_cookie = IDN_TIMER_PUBLIC_COOKIE;
2506 	}
2507 
2508 	ASSERT((contype == IDNCON_QUERY) ? idn_domain[domid].dcookie_send : 1);
2509 
2510 	PR_PROTO("%s:%d: sending con%sto (cpu %d) [ct=%s, cs=0x%x]\n",
2511 	    proc, domid,
2512 	    (acknack & IDNP_ACK) ? "+ack " :
2513 	    (acknack & IDNP_NACK) ? "+nack " : " ",
2514 	    idn_domain[domid].dcpu,
2515 	    idncon_str[contype], conset);
2516 
2517 	IDN_MSGTIMER_START(domid, IDNP_CON, (ushort_t)contype,
2518 	    idn_msg_waittime[IDNP_CON], &mt.mt_cookie);
2519 
2520 	IDNXDC(domid, &mt, (uint_t)contype, (uint_t)conset, 0, 0);
2521 
2522 	return (0);
2523 }
2524 
2525 /*
2526  * Must leave w/DLOCK dropped and SYNC_LOCK held.
2527  */
2528 static int
2529 idn_recv_con(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs)
2530 {
2531 	uint_t		msg = mtp ? mtp->mt_mtype : 0;
2532 	uint_t		msgarg = mtp ? mtp->mt_atype : 0;
2533 	idn_con_t	contype;
2534 	domainset_t	my_ready_set, ready_set;
2535 	idn_msgtype_t	mt;
2536 	idn_domain_t	*dp = &idn_domain[domid];
2537 	idn_xdcargs_t	aargs;
2538 	procname_t	proc = "idn_recv_con";
2539 
2540 	ASSERT(IDN_SYNC_IS_LOCKED());
2541 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
2542 
2543 	mt.mt_cookie = mtp ? mtp->mt_cookie : 0;
2544 
2545 	contype   = GET_XARGS_CON_TYPE(xargs);
2546 	ready_set = GET_XARGS_CON_DOMSET(xargs);
2547 
2548 	CLR_XARGS(aargs);
2549 
2550 	if (!(msg & IDNP_NACK) && (contype == IDNCON_QUERY)) {
2551 		domainset_t	query_set;
2552 
2553 		query_set = idn_sync_register(domid, IDNSYNC_CONNECT,
2554 		    ready_set, IDNSYNC_REG_REG);
2555 
2556 		my_ready_set = idn.domset.ds_connected | idn.domset.ds_ready_on;
2557 		my_ready_set &= ~idn.domset.ds_trans_off;
2558 		DOMAINSET_ADD(my_ready_set, idn.localid);
2559 
2560 		if (msg & IDNP_MSGTYPE_MASK) {
2561 			mt.mt_mtype = IDNP_ACK;
2562 			mt.mt_atype = IDNP_CON;
2563 			SET_XARGS_CON_TYPE(aargs, contype);
2564 			SET_XARGS_CON_DOMSET(aargs, my_ready_set);
2565 			idn_send_acknack(domid, &mt, aargs);
2566 		}
2567 
2568 		if (query_set) {
2569 			uint_t	token;
2570 
2571 			token = IDN_RETRY_TOKEN(domid, IDNRETRY_CONQ);
2572 			idn_retry_submit(idn_retry_query, NULL, token,
2573 			    idn_msg_retrytime[(int)IDNRETRY_CONQ]);
2574 		}
2575 
2576 		return (0);
2577 	}
2578 
2579 	if (dp->dxp == NULL) {
2580 		STRING(mstr);
2581 		STRING(lstr);
2582 		/*
2583 		 * Must have received an inappropriate error
2584 		 * message as we should already be registered
2585 		 * by the time we reach here.
2586 		 */
2587 		INUM2STR(msg, mstr);
2588 		INUM2STR(msgarg, lstr);
2589 
2590 		PR_PROTO("%s:%d: ERROR: NOT YET REGISTERED (%s/%s)\n",
2591 		    proc, domid, mstr, lstr);
2592 
2593 		if (msg & IDNP_MSGTYPE_MASK) {
2594 			mt.mt_mtype = IDNP_NACK;
2595 			mt.mt_atype = msg;
2596 			SET_XARGS_NACK_TYPE(aargs, IDNNACK_RETRY);
2597 			idn_send_acknack(domid, &mt, aargs);
2598 		}
2599 
2600 		return (-1);
2601 	}
2602 
2603 	(void) idn_xphase_transition(domid, mtp, xargs);
2604 
2605 	return (0);
2606 }
2607 
2608 /*ARGSUSED1*/
2609 static void
2610 idn_retry_con(uint_t token, void *arg)
2611 {
2612 	int		domid = IDN_RETRY_TOKEN2DOMID(token);
2613 	idn_domain_t	*dp = &idn_domain[domid];
2614 	idn_xdcargs_t	xargs;
2615 	procname_t	proc = "idn_retry_con";
2616 
2617 	ASSERT(IDN_RETRY_TOKEN2TYPE(token) == IDNRETRY_CON);
2618 
2619 	IDN_SYNC_LOCK();
2620 	IDN_DLOCK_EXCL(domid);
2621 
2622 	if (dp->dxp != &xphase_con) {
2623 		STRING(str);
2624 
2625 #ifdef DEBUG
2626 		if (dp->dxp) {
2627 			INUM2STR(dp->dxp->xt_msgtype, str);
2628 		}
2629 #endif /* DEBUG */
2630 
2631 		PR_PROTO("%s:%d: dxp(%s) != CON...bailing...\n",
2632 		    proc, domid, dp->dxp ? str : "NULL");
2633 		IDN_DUNLOCK(domid);
2634 		IDN_SYNC_UNLOCK();
2635 		return;
2636 	}
2637 
2638 	if ((dp->dsync.s_cmd != IDNSYNC_CONNECT) ||
2639 	    (dp->dxstate != IDNXS_PEND)) {
2640 		PR_PROTO("%s:%d: cmd (%s) and/or xstate (%s) not "
2641 		    "expected (%s/%s)\n",
2642 		    proc, domid, idnsync_str[dp->dsync.s_cmd],
2643 		    idnxs_str[dp->dxstate], idnsync_str[IDNSYNC_CONNECT],
2644 		    idnxs_str[IDNXS_PEND]);
2645 		IDN_DUNLOCK(domid);
2646 		IDN_SYNC_UNLOCK();
2647 		return;
2648 	}
2649 
2650 	(void) idn_xphase_transition(domid, NULL, xargs);
2651 
2652 	IDN_DUNLOCK(domid);
2653 	IDN_SYNC_UNLOCK();
2654 }
2655 
2656 static int
2657 idn_check_con(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs)
2658 {
2659 	int		ready;
2660 	uint_t		msg = mtp ? mtp->mt_mtype : 0;
2661 	idn_domain_t	*dp = &idn_domain[domid];
2662 	domainset_t	ready_set, my_ready_set, query_set;
2663 
2664 	ASSERT(IDN_SYNC_IS_LOCKED());
2665 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
2666 
2667 	if (msg & IDNP_NACK)
2668 		return (0);
2669 
2670 	if ((dp->dstate == IDNDS_CON_PEND) &&
2671 	    (msg & IDNP_MSGTYPE_MASK) && (msg & IDNP_ACK))	/* con+ack */
2672 		return (1);
2673 
2674 	if (msg == 0) {
2675 		ready_set = idn.domset.ds_connected &
2676 		    ~idn.domset.ds_trans_off;
2677 	} else {
2678 		ready_set = GET_XARGS_CON_DOMSET(xargs);
2679 		DOMAINSET_ADD(idn.domset.ds_ready_on, domid);
2680 	}
2681 
2682 	DOMAINSET_ADD(ready_set, idn.localid);
2683 
2684 	query_set = idn_sync_register(domid, IDNSYNC_CONNECT,
2685 	    ready_set, IDNSYNC_REG_REG);
2686 	/*
2687 	 * No need to query this domain as he's already
2688 	 * in the CON sequence.
2689 	 */
2690 	DOMAINSET_DEL(query_set, domid);
2691 
2692 	ready = (dp->dsync.s_set_exp == dp->dsync.s_set_rdy) ? 1 : 0;
2693 	if (ready) {
2694 		DOMAINSET_DEL(idn.domset.ds_ready_on, domid);
2695 		DOMAINSET_ADD(idn.domset.ds_connected, domid);
2696 	}
2697 
2698 	if (query_set) {
2699 		int	d;
2700 
2701 		my_ready_set = idn.domset.ds_ready_on |
2702 		    idn.domset.ds_connected;
2703 		my_ready_set &= ~idn.domset.ds_trans_off;
2704 		DOMAINSET_ADD(my_ready_set, idn.localid);
2705 
2706 		for (d = 0; d < MAX_DOMAINS; d++) {
2707 			if (!DOMAIN_IN_SET(query_set, d))
2708 				continue;
2709 
2710 			dp = &idn_domain[d];
2711 
2712 			IDN_DLOCK_EXCL(d);
2713 			if ((dp->dsync.s_cmd == IDNSYNC_CONNECT) ||
2714 			    !dp->dcookie_send) {
2715 				IDN_DUNLOCK(d);
2716 				continue;
2717 			}
2718 
2719 			IDN_SYNC_QUERY_UPDATE(domid, d);
2720 
2721 			(void) idn_send_con(d, NULL, IDNCON_QUERY,
2722 			    my_ready_set);
2723 			IDN_DUNLOCK(d);
2724 		}
2725 	}
2726 
2727 	return (!msg ? 0 : (ready ? 0 : 1));
2728 }
2729 
2730 /*ARGSUSED2*/
2731 static void
2732 idn_error_con(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs)
2733 {
2734 	uint_t	token;
2735 	uint_t	msg = mtp ? mtp->mt_mtype : 0;
2736 
2737 	ASSERT(IDN_SYNC_IS_LOCKED());
2738 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
2739 
2740 	if (msg & IDNP_MSGTYPE_MASK) {
2741 		idn_msgtype_t	mt;
2742 		idn_xdcargs_t	nargs;
2743 
2744 		mt.mt_mtype = IDNP_NACK;
2745 		mt.mt_atype = msg;
2746 		mt.mt_cookie = mtp->mt_cookie;
2747 		CLR_XARGS(nargs);
2748 		SET_XARGS_NACK_TYPE(nargs, IDNNACK_RETRY);
2749 		idn_send_acknack(domid, &mt, nargs);
2750 	}
2751 
2752 	token = IDN_RETRY_TOKEN(domid, IDNRETRY_CON);
2753 	idn_retry_submit(idn_retry_con, NULL, token,
2754 	    idn_msg_retrytime[(int)IDNRETRY_CON]);
2755 }
2756 
2757 /*ARGSUSED*/
2758 static void
2759 idn_action_con_pend(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs)
2760 {
2761 	uint_t		msg = mtp ? mtp->mt_mtype : 0;
2762 	idn_domain_t	*dp = &idn_domain[domid];
2763 	idn_msgtype_t	mt;
2764 	domainset_t	my_ready_set;
2765 
2766 	ASSERT(IDN_SYNC_IS_LOCKED());
2767 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
2768 
2769 	my_ready_set = dp->dsync.s_set_rdy | idn.domset.ds_ready_on |
2770 	    idn.domset.ds_connected;
2771 	my_ready_set &= ~idn.domset.ds_trans_off;
2772 	DOMAINSET_ADD(my_ready_set, idn.localid);
2773 
2774 	if (!msg) {
2775 		(void) idn_send_con(domid, NULL, IDNCON_NORMAL, my_ready_set);
2776 	} else {
2777 		mt.mt_mtype = IDNP_CON | IDNP_ACK;
2778 		mt.mt_atype = 0;
2779 		mt.mt_cookie = mtp->mt_cookie;
2780 		(void) idn_send_con(domid, &mt, IDNCON_NORMAL, my_ready_set);
2781 	}
2782 }
2783 
2784 static void
2785 idn_action_con_sent(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs)
2786 {
2787 	uint_t		msg = mtp ? mtp->mt_mtype : 0;
2788 	idn_domain_t	*dp = &idn_domain[domid];
2789 	idn_con_t	contype;
2790 	domainset_t	my_ready_set;
2791 	idn_msgtype_t	mt;
2792 
2793 	ASSERT(IDN_SYNC_IS_LOCKED());
2794 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
2795 
2796 	mt.mt_cookie = mtp ? mtp->mt_cookie : 0;
2797 
2798 	my_ready_set = dp->dsync.s_set_rdy | idn.domset.ds_ready_on |
2799 	    idn.domset.ds_connected;
2800 	my_ready_set &= ~idn.domset.ds_trans_off;
2801 	DOMAINSET_ADD(my_ready_set, idn.localid);
2802 
2803 	contype = GET_XARGS_CON_TYPE(xargs);
2804 
2805 	if ((msg & IDNP_ACKNACK_MASK) == 0) {
2806 		/*
2807 		 * con
2808 		 */
2809 		mt.mt_mtype = IDNP_CON | IDNP_ACK;
2810 		mt.mt_atype = 0;
2811 		(void) idn_send_con(domid, &mt, contype, my_ready_set);
2812 	} else if (msg & IDNP_MSGTYPE_MASK) {
2813 		idn_xdcargs_t	cargs;
2814 
2815 		mt.mt_mtype = IDNP_ACK;
2816 		mt.mt_atype = msg;
2817 		CLR_XARGS(cargs);
2818 		SET_XARGS_CON_TYPE(cargs, contype);
2819 		SET_XARGS_CON_DOMSET(cargs, my_ready_set);
2820 		/*
2821 		 * con+ack
2822 		 */
2823 		idn_send_acknack(domid, &mt, cargs);
2824 	} else {
2825 		uint_t	token;
2826 		/*
2827 		 * nack - retry
2828 		 */
2829 		token = IDN_RETRY_TOKEN(domid, IDNRETRY_CON);
2830 		idn_retry_submit(idn_retry_con, NULL, token,
2831 		    idn_msg_retrytime[(int)IDNRETRY_CON]);
2832 	}
2833 }
2834 
2835 /*ARGSUSED*/
2836 static void
2837 idn_action_con_rcvd(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs)
2838 {
2839 	uint_t	msg = mtp ? mtp->mt_mtype : 0;
2840 
2841 	ASSERT(IDN_SYNC_IS_LOCKED());
2842 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
2843 
2844 	if (msg & IDNP_NACK) {
2845 		uint_t	token;
2846 		/*
2847 		 * nack - retry
2848 		 */
2849 		token = IDN_RETRY_TOKEN(domid, IDNRETRY_CON);
2850 		idn_retry_submit(idn_retry_con, NULL, token,
2851 		    idn_msg_retrytime[(int)IDNRETRY_CON]);
2852 	}
2853 }
2854 
2855 static void
2856 idn_final_con(int domid)
2857 {
2858 	uint_t		targ;
2859 	uint_t		token = IDN_RETRY_TOKEN(domid, IDNRETRY_CON);
2860 	idn_domain_t	*dp = &idn_domain[domid];
2861 	procname_t	proc = "idn_final_con";
2862 
2863 	ASSERT(IDN_SYNC_IS_LOCKED());
2864 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
2865 
2866 	(void) idn_retry_terminate(token);
2867 
2868 	dp->dxp = NULL;
2869 	IDN_XSTATE_TRANSITION(dp, IDNXS_NIL);
2870 
2871 	idn_sync_exit(domid, IDNSYNC_CONNECT);
2872 
2873 	CHECKPOINT_OPENED(IDNSB_CHKPT_LINK, dp->dhw.dh_boardset, 1);
2874 
2875 	DOMAINSET_DEL(idn.domset.ds_trans_on, domid);
2876 	DOMAINSET_DEL(idn.domset.ds_relink, domid);
2877 	IDN_FSTATE_TRANSITION(dp, IDNFIN_OFF);
2878 
2879 	PR_PROTO("%s:%d: CONNECTED\n", proc, domid);
2880 
2881 	if (idn.domset.ds_trans_on == 0) {
2882 		if ((idn.domset.ds_trans_off | idn.domset.ds_relink) == 0) {
2883 			PR_HITLIST("%s:%d: HITLIST %x -> 0\n",
2884 			    proc, domid, idn.domset.ds_hitlist);
2885 			idn.domset.ds_hitlist = 0;
2886 		}
2887 		PR_PROTO("%s:%d: ALL CONNECTED ************ "
2888 		    "(0x%x + 0x%x) = 0x%x\n", proc, domid,
2889 		    DOMAINSET(idn.localid), idn.domset.ds_connected,
2890 		    DOMAINSET(idn.localid) | idn.domset.ds_connected);
2891 	} else {
2892 		PR_PROTO("%s:%d: >>> ds_trans_on = 0x%x, ds_ready_on = 0x%x\n",
2893 		    proc, domid,
2894 		    idn.domset.ds_trans_on, idn.domset.ds_ready_on);
2895 	}
2896 
2897 	if (idn_verify_config_mbox(domid)) {
2898 		idnsb_error_t	idnerr;
2899 		/*
2900 		 * Mailbox is not cool. Need to disconnect.
2901 		 */
2902 		INIT_IDNKERR(&idnerr);
2903 		SET_IDNKERR_ERRNO(&idnerr, EPROTO);
2904 		SET_IDNKERR_IDNERR(&idnerr, IDNKERR_SMR_CORRUPTED);
2905 		SET_IDNKERR_PARAM0(&idnerr, domid);
2906 		idn_update_op(IDNOP_ERROR, DOMAINSET(domid), &idnerr);
2907 		/*
2908 		 * We cannot disconnect from an individual domain
2909 		 * unless all domains are attempting to disconnect
2910 		 * from him also, especially now since we touched
2911 		 * the SMR and now we have a potential cache conflicts
2912 		 * with the other domains with respect to this
2913 		 * domain.  Disconnect attempt will effectively
2914 		 * shutdown connection with respective domain
2915 		 * which is the effect we really want anyway.
2916 		 */
2917 		(void) idn_disconnect(domid, IDNFIN_NORMAL, IDNFIN_ARG_SMRBAD,
2918 		    IDNFIN_SYNC_YES);
2919 
2920 		return;
2921 	}
2922 
2923 	if (lock_try(&idn.first_swlink)) {
2924 		/*
2925 		 * This is our first connection.  Need to
2926 		 * kick some stuff into gear.
2927 		 */
2928 		idndl_dlpi_init();
2929 		(void) idn_activate_channel(CHANSET_ALL, IDNCHAN_ONLINE);
2930 
2931 		targ = 0xf0;
2932 	} else {
2933 		targ = 0;
2934 	}
2935 
2936 	idn_mainmbox_activate(domid);
2937 
2938 	idn_update_op(IDNOP_CONNECTED, DOMAINSET(domid), NULL);
2939 
2940 	IDN_GKSTAT_GLOBAL_EVENT(gk_links, gk_link_last);
2941 
2942 	membar_stst_ldst();
2943 
2944 	IDN_DSTATE_TRANSITION(dp, IDNDS_CONNECTED);
2945 	/*
2946 	 * Need to kick off initial commands in background.
2947 	 * We do not want to do them within the context of
2948 	 * a protocol server because they may sleep and thus
2949 	 * cause the protocol server to incur a soft-deadlock,
2950 	 * i.e. he's sleeping waiting in the slab-waiting area
2951 	 * for a response that will arrive on his protojob
2952 	 * queue, but which he obviously can't process since
2953 	 * he's not waiting on his protojob queue.
2954 	 */
2955 	targ |= domid & 0x0f;
2956 	(void) timeout(idn_link_established, (void *)(uintptr_t)targ, 50);
2957 
2958 	cmn_err(CE_NOTE,
2959 	    "!IDN: 200: link (domain %d, CPU %d) connected",
2960 	    dp->domid, dp->dcpu);
2961 }
2962 
2963 static void
2964 idn_exit_con(int domid, uint_t msgtype)
2965 {
2966 	idn_domain_t	*dp = &idn_domain[domid];
2967 	idn_fin_t	fintype;
2968 	procname_t	proc = "idn_exit_con";
2969 	STRING(str);
2970 
2971 	ASSERT(IDN_SYNC_IS_LOCKED());
2972 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
2973 
2974 	INUM2STR(msgtype, str);
2975 	PR_PROTO("%s:%d: msgtype = 0x%x(%s)\n", proc, domid, msgtype, str);
2976 
2977 	fintype = msgtype ? IDNFIN_NORMAL : IDNFIN_FORCE_HARD;
2978 
2979 	IDN_GLOCK_SHARED();
2980 	if (idn.state != IDNGS_DISCONNECT) {
2981 		DOMAINSET_ADD(idn.domset.ds_relink, domid);
2982 		IDN_HISTORY_LOG(IDNH_RELINK, domid, dp->dstate,
2983 		    idn.domset.ds_relink);
2984 	} else {
2985 		DOMAINSET_DEL(idn.domset.ds_relink, domid);
2986 	}
2987 	IDN_GUNLOCK();
2988 
2989 	(void) idn_disconnect(domid, fintype, IDNFIN_ARG_NONE,
2990 	    IDNDS_SYNC_TYPE(dp));
2991 }
2992 
2993 static int
2994 idn_send_fin(int domid, idn_msgtype_t *mtp, idn_fin_t fintype, idn_finarg_t
2995     finarg, idn_finopt_t finopt, domainset_t finset, uint_t finmaster)
2996 {
2997 	int		need_timer = 1;
2998 	uint_t		acknack;
2999 	uint_t		fintypearg = 0;
3000 	idn_msgtype_t	mt;
3001 	idn_domain_t	*dp = &idn_domain[domid];
3002 	procname_t	proc = "idn_send_fin";
3003 
3004 	ASSERT(IDN_SYNC_IS_LOCKED());
3005 	ASSERT(IDN_DLOCK_IS_HELD(domid));
3006 
3007 	ASSERT((fintype != IDNFIN_QUERY) ? (finopt != IDNFIN_OPT_NONE) : 1);
3008 
3009 	if (mtp) {
3010 		acknack = mtp->mt_mtype & IDNP_ACKNACK_MASK;
3011 		mt.mt_mtype = mtp->mt_mtype;
3012 		mt.mt_atype = mtp->mt_atype;
3013 		mt.mt_cookie = mtp->mt_cookie;
3014 	} else {
3015 		acknack = 0;
3016 		mt.mt_mtype = IDNP_FIN;
3017 		mt.mt_atype = 0;
3018 		/*
3019 		 * For simple FIN queries we want a unique
3020 		 * timer assigned.  For others, they
3021 		 * effectively share one.
3022 		 */
3023 		if (fintype == IDNFIN_QUERY)
3024 			mt.mt_cookie = 0;
3025 		else
3026 			mt.mt_cookie = IDN_TIMER_PUBLIC_COOKIE;
3027 	}
3028 
3029 	PR_PROTO("%s:%d: sending fin%sto (cpu %d) "
3030 	    "[ft=%s, fa=%s, fs=0x%x, fo=%s, fm=(%d,%d)]\n",
3031 	    proc, domid,
3032 	    (acknack & IDNP_ACK) ? "+ack " :
3033 	    (acknack & IDNP_NACK) ? "+nack " : " ",
3034 	    dp->dcpu, idnfin_str[fintype], idnfinarg_str[finarg],
3035 	    (int)finset, idnfinopt_str[finopt],
3036 	    FIN_MASTER_DOMID(finmaster), FIN_MASTER_CPUID(finmaster));
3037 
3038 	if (need_timer) {
3039 		IDN_MSGTIMER_START(domid, IDNP_FIN, (ushort_t)fintype,
3040 		    idn_msg_waittime[IDNP_FIN], &mt.mt_cookie);
3041 	}
3042 
3043 	SET_FIN_TYPE(fintypearg, fintype);
3044 	SET_FIN_ARG(fintypearg, finarg);
3045 
3046 	IDNXDC(domid, &mt, fintypearg, (uint_t)finset, (uint_t)finopt,
3047 	    finmaster);
3048 
3049 	return (0);
3050 }
3051 
3052 /*
3053  * Must leave w/DLOCK dropped and SYNC_LOCK held.
3054  */
3055 static int
3056 idn_recv_fin(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs)
3057 {
3058 	uint_t		msg = mtp ? mtp->mt_mtype : 0;
3059 	idn_fin_t	fintype;
3060 	idn_finarg_t	finarg;
3061 	idn_finopt_t	finopt;
3062 	domainset_t	my_ready_set, ready_set;
3063 	idn_msgtype_t	mt;
3064 	idn_domain_t	*dp = &idn_domain[domid];
3065 	idn_xdcargs_t	aargs;
3066 	procname_t	proc = "idn_recv_fin";
3067 
3068 	ASSERT(IDN_SYNC_IS_LOCKED());
3069 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
3070 
3071 	mt.mt_cookie = mtp ? mtp->mt_cookie : 0;
3072 
3073 	fintype   = GET_XARGS_FIN_TYPE(xargs);
3074 	finarg    = GET_XARGS_FIN_ARG(xargs);
3075 	ready_set = GET_XARGS_FIN_DOMSET(xargs);
3076 	finopt    = GET_XARGS_FIN_OPT(xargs);
3077 
3078 	CLR_XARGS(aargs);
3079 
3080 	if (msg & IDNP_NACK) {
3081 		PR_PROTO("%s:%d: received NACK (type = %s)\n",
3082 		    proc, domid, idnnack_str[xargs[0]]);
3083 	} else {
3084 		PR_PROTO("%s:%d: fintype = %s, finopt = %s, "
3085 		    "finarg = %s, ready_set = 0x%x\n",
3086 		    proc, domid, idnfin_str[fintype],
3087 		    idnfinopt_str[finopt],
3088 		    idnfinarg_str[finarg], ready_set);
3089 	}
3090 
3091 	if (!(msg & IDNP_NACK) && (fintype == IDNFIN_QUERY)) {
3092 		domainset_t	query_set;
3093 
3094 		query_set = idn_sync_register(domid, IDNSYNC_DISCONNECT,
3095 		    ready_set, IDNSYNC_REG_REG);
3096 
3097 		my_ready_set = ~idn.domset.ds_connected |
3098 		    idn.domset.ds_ready_off;
3099 
3100 		if (msg & IDNP_MSGTYPE_MASK) {
3101 			mt.mt_mtype = IDNP_ACK;
3102 			mt.mt_atype = IDNP_FIN;
3103 			SET_XARGS_FIN_TYPE(aargs, fintype);
3104 			SET_XARGS_FIN_ARG(aargs, finarg);
3105 			SET_XARGS_FIN_DOMSET(aargs, my_ready_set);
3106 			SET_XARGS_FIN_OPT(aargs, IDNFIN_OPT_NONE);
3107 			SET_XARGS_FIN_MASTER(aargs, NIL_FIN_MASTER);
3108 			idn_send_acknack(domid, &mt, aargs);
3109 		}
3110 
3111 		if (query_set) {
3112 			uint_t	token;
3113 
3114 			token = IDN_RETRY_TOKEN(domid, IDNRETRY_FINQ);
3115 			idn_retry_submit(idn_retry_query, NULL, token,
3116 			    idn_msg_retrytime[(int)IDNRETRY_FINQ]);
3117 		}
3118 
3119 		return (0);
3120 	}
3121 
3122 	if (dp->dxp != &xphase_fin) {
3123 		uint_t	token;
3124 
3125 		if (IDNDS_IS_CLOSED(dp)) {
3126 			PR_PROTO("%s:%d: domain already closed (%s)\n",
3127 			    proc, domid, idnds_str[dp->dstate]);
3128 			if (msg & IDNP_MSGTYPE_MASK) {
3129 				/*
3130 				 * fin or fin+ack.
3131 				 */
3132 				mt.mt_mtype = IDNP_NACK;
3133 				mt.mt_atype = msg;
3134 				SET_XARGS_NACK_TYPE(aargs, IDNNACK_NOCONN);
3135 				idn_send_acknack(domid, &mt, aargs);
3136 			}
3137 			return (0);
3138 		}
3139 		dp->dfin_sync = IDNDS_SYNC_TYPE(dp);
3140 
3141 		/*
3142 		 * Need to do some clean-up ala idn_disconnect().
3143 		 *
3144 		 * Terminate any outstanding commands that were
3145 		 * targeted towards this domain.
3146 		 */
3147 		idn_terminate_cmd(domid, ECANCELED);
3148 
3149 		/*
3150 		 * Terminate any and all retries that may have
3151 		 * outstanding for this domain.
3152 		 */
3153 		token = IDN_RETRY_TOKEN(domid, IDN_RETRY_TYPEALL);
3154 		(void) idn_retry_terminate(token);
3155 
3156 		/*
3157 		 * Stop all outstanding message timers for
3158 		 * this guy.
3159 		 */
3160 		IDN_MSGTIMER_STOP(domid, 0, 0);
3161 
3162 		dp->dxp = &xphase_fin;
3163 		IDN_XSTATE_TRANSITION(dp, IDNXS_PEND);
3164 	}
3165 
3166 	if (msg & IDNP_NACK) {
3167 		idn_nack_t	nack;
3168 
3169 		nack = GET_XARGS_NACK_TYPE(xargs);
3170 		if (nack == IDNNACK_NOCONN) {
3171 			/*
3172 			 * We're trying to FIN with somebody we're
3173 			 * already disconnected from.  Need to
3174 			 * speed this guy through.
3175 			 */
3176 			DOMAINSET_ADD(idn.domset.ds_ready_off, domid);
3177 			(void) idn_sync_register(domid, IDNSYNC_DISCONNECT,
3178 			    DOMAINSET_ALL, IDNSYNC_REG_REG);
3179 			ready_set = (uint_t)DOMAINSET_ALL;
3180 			/*
3181 			 * Need to transform message to allow us to
3182 			 * pass this guy right through and not waste time
3183 			 * talking to him.
3184 			 */
3185 			IDN_FSTATE_TRANSITION(dp, IDNFIN_FORCE_HARD);
3186 
3187 			switch (dp->dstate) {
3188 			case IDNDS_FIN_PEND:
3189 				mtp->mt_mtype = 0;
3190 				mtp->mt_atype = 0;
3191 				break;
3192 
3193 			case IDNDS_FIN_SENT:
3194 				mtp->mt_mtype = IDNP_FIN | IDNP_ACK;
3195 				mtp->mt_atype = 0;
3196 				break;
3197 
3198 			case IDNDS_FIN_RCVD:
3199 				mtp->mt_mtype = IDNP_ACK;
3200 				mtp->mt_atype = IDNP_FIN | IDNP_ACK;
3201 				break;
3202 
3203 			default:
3204 #ifdef DEBUG
3205 				cmn_err(CE_PANIC,
3206 				    "%s:%d: UNEXPECTED state = %s",
3207 				    proc, domid,
3208 				    idnds_str[dp->dstate]);
3209 #endif /* DEBUG */
3210 				break;
3211 			}
3212 		}
3213 		fintype = (uint_t)dp->dfin;
3214 		finopt = DOMAIN_IN_SET(idn.domset.ds_relink, domid) ?
3215 		    IDNFIN_OPT_RELINK : IDNFIN_OPT_UNLINK;
3216 
3217 		CLR_XARGS(xargs);
3218 		SET_XARGS_FIN_TYPE(xargs, fintype);
3219 		SET_XARGS_FIN_ARG(xargs, finarg);
3220 		SET_XARGS_FIN_DOMSET(xargs, ready_set);
3221 		SET_XARGS_FIN_OPT(xargs, finopt);
3222 		SET_XARGS_FIN_MASTER(xargs, NIL_FIN_MASTER);
3223 	}
3224 
3225 	(void) idn_xphase_transition(domid, mtp, xargs);
3226 
3227 	return (0);
3228 }
3229 
3230 /*ARGSUSED1*/
3231 static void
3232 idn_retry_fin(uint_t token, void *arg)
3233 {
3234 	int		domid = IDN_RETRY_TOKEN2DOMID(token);
3235 	int		new_masterid, new_cpuid = IDN_NIL_DCPU;
3236 	uint_t		finmaster;
3237 	idn_domain_t	*dp = &idn_domain[domid];
3238 	idn_xdcargs_t	xargs;
3239 	idn_finopt_t	finopt;
3240 	procname_t	proc = "idn_retry_fin";
3241 
3242 	ASSERT(IDN_RETRY_TOKEN2TYPE(token) == IDNRETRY_FIN);
3243 
3244 	IDN_SYNC_LOCK();
3245 	IDN_DLOCK_EXCL(domid);
3246 
3247 	if (dp->dxp != &xphase_fin) {
3248 		PR_PROTO("%s:%d: dxp(0x%p) != xstate_fin(0x%p)...bailing\n",
3249 		    proc, domid, (void *)dp->dxp, (void *)&xphase_fin);
3250 		IDN_DUNLOCK(domid);
3251 		IDN_SYNC_UNLOCK();
3252 		return;
3253 	}
3254 
3255 	if (dp->dxstate != IDNXS_PEND) {
3256 		PR_PROTO("%s:%d: xstate(%s) != %s...bailing\n",
3257 		    proc, domid, idnxs_str[dp->dxstate],
3258 		    idnxs_str[IDNXS_PEND]);
3259 		IDN_DUNLOCK(domid);
3260 		IDN_SYNC_UNLOCK();
3261 		return;
3262 	}
3263 
3264 	finopt = DOMAIN_IN_SET(idn.domset.ds_relink, domid) ?
3265 	    IDNFIN_OPT_RELINK : IDNFIN_OPT_UNLINK;
3266 
3267 	CLR_XARGS(xargs);
3268 	SET_XARGS_FIN_TYPE(xargs, dp->dfin);
3269 	/*LINTED*/
3270 	SET_XARGS_FIN_ARG(xargs, IDNFIN_ARG_NONE);
3271 	SET_XARGS_FIN_OPT(xargs, finopt);
3272 	SET_XARGS_FIN_DOMSET(xargs, 0);		/* unused when msg == 0 */
3273 	IDN_GLOCK_SHARED();
3274 	new_masterid = IDN_GET_NEW_MASTERID();
3275 	IDN_GUNLOCK();
3276 	if (new_masterid != IDN_NIL_DOMID)
3277 		new_cpuid = idn_domain[new_masterid].dcpu;
3278 	finmaster = MAKE_FIN_MASTER(new_masterid, new_cpuid);
3279 	SET_XARGS_FIN_MASTER(xargs, finmaster);
3280 
3281 	(void) idn_xphase_transition(domid, NULL, xargs);
3282 
3283 	IDN_DUNLOCK(domid);
3284 	IDN_SYNC_UNLOCK();
3285 }
3286 
3287 static int
3288 idn_check_fin_pend(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs)
3289 {
3290 	idn_domain_t	*dp = &idn_domain[domid];
3291 	idn_fin_t	fintype;
3292 	idn_finopt_t	finopt;
3293 	idn_finarg_t	finarg;
3294 	int		ready;
3295 	int		finmasterid;
3296 	int		fincpuid;
3297 	uint_t		finmaster;
3298 	uint_t		msg = mtp ? mtp->mt_mtype : 0;
3299 	domainset_t	query_set, ready_set, conn_set;
3300 	domainset_t	my_ready_set, shutdown_set;
3301 	procname_t	proc = "idn_check_fin_pend";
3302 
3303 	ASSERT(IDN_SYNC_IS_LOCKED());
3304 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
3305 
3306 	if (msg & IDNP_NACK)
3307 		return (0);
3308 
3309 	if ((dp->dstate == IDNDS_FIN_PEND) && (msg & IDNP_MSGTYPE_MASK) &&
3310 	    (msg & IDNP_ACK))		/* fin+ack */
3311 		return (1);
3312 
3313 	query_set = 0;
3314 
3315 	if (!DOMAIN_IN_SET(idn.domset.ds_trans_off, domid)) {
3316 		/*
3317 		 * Can't remove domain from ds_connected yet,
3318 		 * since he's still officially connected until
3319 		 * we get an ACK from him.
3320 		 */
3321 		DOMAINSET_DEL(idn.domset.ds_trans_on, domid);
3322 		DOMAINSET_ADD(idn.domset.ds_trans_off, domid);
3323 	}
3324 
3325 	IDN_GLOCK_SHARED();
3326 	conn_set = (idn.domset.ds_connected | idn.domset.ds_trans_on) &
3327 	    ~idn.domset.ds_trans_off;
3328 	if ((idn.state == IDNGS_DISCONNECT) ||
3329 	    (idn.state == IDNGS_RECONFIG) ||
3330 	    (domid == IDN_GET_MASTERID()) || !conn_set) {
3331 		/*
3332 		 * If we're disconnecting, reconfiguring,
3333 		 * unlinking from the master, or unlinking
3334 		 * the last of our connections, then we need
3335 		 * to shutdown all the channels.
3336 		 */
3337 		shutdown_set = DOMAINSET_ALL;
3338 	} else {
3339 		shutdown_set = DOMAINSET(domid);
3340 	}
3341 	IDN_GUNLOCK();
3342 
3343 	idn_shutdown_datapath(shutdown_set, (dp->dfin == IDNFIN_FORCE_HARD));
3344 
3345 	IDN_GLOCK_EXCL();
3346 	/*
3347 	 * Remap the SMR back to our local space if the remote
3348 	 * domain going down is the master.  We do this now before
3349 	 * flushing caches.  This will help guarantee that any
3350 	 * accidental accesses to the SMR after the cache flush
3351 	 * will only go to local memory.
3352 	 */
3353 	if ((domid == IDN_GET_MASTERID()) && (idn.smr.rempfn != PFN_INVALID)) {
3354 		PR_PROTO("%s:%d: deconfiging CURRENT MASTER - SMR remap\n",
3355 		    proc, domid);
3356 		IDN_DLOCK_EXCL(idn.localid);
3357 		/*
3358 		 * We're going to remap the SMR,
3359 		 * so gotta blow away our local
3360 		 * pointer to the mbox table.
3361 		 */
3362 		idn_domain[idn.localid].dmbox.m_tbl = NULL;
3363 		IDN_DUNLOCK(idn.localid);
3364 
3365 		idn.smr.rempfn = PFN_INVALID;
3366 		idn.smr.rempfnlim = PFN_INVALID;
3367 
3368 		smr_remap(&kas, idn.smr.vaddr, idn.smr.locpfn, IDN_SMR_SIZE);
3369 	}
3370 	IDN_GUNLOCK();
3371 
3372 	if (DOMAIN_IN_SET(idn.domset.ds_flush, domid)) {
3373 		idnxf_flushall_ecache();
3374 		CHECKPOINT_CLOSED(IDNSB_CHKPT_CACHE, dp->dhw.dh_boardset, 2);
3375 		DOMAINSET_DEL(idn.domset.ds_flush, domid);
3376 	}
3377 
3378 	fintype   = GET_XARGS_FIN_TYPE(xargs);
3379 	finarg    = GET_XARGS_FIN_ARG(xargs);
3380 	ready_set = GET_XARGS_FIN_DOMSET(xargs);
3381 	finopt    = GET_XARGS_FIN_OPT(xargs);
3382 
3383 	ASSERT(fintype != IDNFIN_QUERY);
3384 	if (!VALID_FIN(fintype)) {
3385 		/*
3386 		 * If for some reason remote domain
3387 		 * sent us an invalid FIN type,
3388 		 * override it to a  NORMAL fin.
3389 		 */
3390 		PR_PROTO("%s:%d: WARNING invalid fintype (%d) -> %s(%d)\n",
3391 		    proc, domid, (int)fintype,
3392 		    idnfin_str[IDNFIN_NORMAL], (int)IDNFIN_NORMAL);
3393 		fintype = IDNFIN_NORMAL;
3394 	}
3395 
3396 	if (!VALID_FINOPT(finopt)) {
3397 		PR_PROTO("%s:%d: WARNING invalid finopt (%d) -> %s(%d)\n",
3398 		    proc, domid, (int)finopt,
3399 		    idnfinopt_str[IDNFIN_OPT_UNLINK],
3400 		    (int)IDNFIN_OPT_UNLINK);
3401 		finopt = IDNFIN_OPT_UNLINK;
3402 	}
3403 
3404 	finmaster = GET_XARGS_FIN_MASTER(xargs);
3405 	finmasterid = FIN_MASTER_DOMID(finmaster);
3406 	fincpuid = FIN_MASTER_CPUID(finmaster);
3407 
3408 	if ((finarg != IDNFIN_ARG_NONE) &&
3409 	    !DOMAIN_IN_SET(idn.domset.ds_hitlist, domid)) {
3410 		idnsb_error_t	idnerr;
3411 
3412 		INIT_IDNKERR(&idnerr);
3413 		SET_IDNKERR_ERRNO(&idnerr, EPROTO);
3414 		SET_IDNKERR_IDNERR(&idnerr, FINARG2IDNKERR(finarg));
3415 		SET_IDNKERR_PARAM0(&idnerr, domid);
3416 
3417 		if (IDNFIN_ARG_IS_FATAL(finarg)) {
3418 			finopt = IDNFIN_OPT_UNLINK;
3419 			DOMAINSET_DEL(idn.domset.ds_relink, domid);
3420 			DOMAINSET_ADD(idn.domset.ds_hitlist, domid);
3421 
3422 			if (idn.domset.ds_connected == 0) {
3423 				domainset_t	domset;
3424 
3425 				IDN_GLOCK_EXCL();
3426 				domset = ~idn.domset.ds_relink;
3427 				if (idn.domset.ds_relink == 0) {
3428 					IDN_GSTATE_TRANSITION(IDNGS_DISCONNECT);
3429 				}
3430 				domset &= ~idn.domset.ds_hitlist;
3431 				/*
3432 				 * The primary domain we were trying to
3433 				 * connect to fin'd us with a fatal argument.
3434 				 * Something isn't cool in our IDN environment,
3435 				 * e.g. corrupted SMR or non-compatible CONFIG
3436 				 * parameters.  In any case we need to dismantle
3437 				 * ourselves completely.
3438 				 */
3439 				IDN_SET_NEW_MASTERID(IDN_NIL_DOMID);
3440 				IDN_GUNLOCK();
3441 				IDN_DUNLOCK(domid);
3442 
3443 				DOMAINSET_DEL(domset, idn.localid);
3444 				DOMAINSET_DEL(domset, domid);
3445 
3446 				idn_update_op(IDNOP_ERROR, DOMAINSET_ALL,
3447 				    &idnerr);
3448 
3449 				PR_HITLIST("%s:%d: unlink_domainset(%x) "
3450 				    "due to CFG error (relink=%x, "
3451 				    "hitlist=%x)\n", proc, domid, domset,
3452 				    idn.domset.ds_relink,
3453 				    idn.domset.ds_hitlist);
3454 
3455 				idn_unlink_domainset(domset, IDNFIN_NORMAL,
3456 				    finarg, IDNFIN_OPT_UNLINK, BOARDSET_ALL);
3457 				IDN_DLOCK_EXCL(domid);
3458 			}
3459 			PR_HITLIST("%s:%d: CFG error, (conn=%x, relink=%x, "
3460 			    "hitlist=%x)\n",
3461 			    proc, domid, idn.domset.ds_connected,
3462 			    idn.domset.ds_relink, idn.domset.ds_hitlist);
3463 		}
3464 		idn_update_op(IDNOP_ERROR, DOMAINSET(domid), &idnerr);
3465 	}
3466 
3467 	if ((finmasterid != IDN_NIL_DOMID) && (!VALID_DOMAINID(finmasterid) ||
3468 	    DOMAIN_IN_SET(idn.domset.ds_hitlist, domid))) {
3469 		PR_HITLIST("%s:%d: finmasterid = %d -> -1, relink=%x, "
3470 		    "hitlist=%x\n",
3471 		    proc, domid, finmasterid, idn.domset.ds_relink,
3472 		    idn.domset.ds_hitlist);
3473 		PR_PROTO("%s:%d: WARNING invalid finmasterid (%d) -> -1\n",
3474 		    proc, domid, finmasterid);
3475 		finmasterid = IDN_NIL_DOMID;
3476 	}
3477 
3478 	IDN_GLOCK_EXCL();
3479 
3480 	if ((finopt == IDNFIN_OPT_RELINK) && (idn.state != IDNGS_DISCONNECT)) {
3481 		DOMAINSET_ADD(idn.domset.ds_relink, domid);
3482 		IDN_HISTORY_LOG(IDNH_RELINK, domid, dp->dstate,
3483 		    idn.domset.ds_relink);
3484 	} else {
3485 		DOMAINSET_DEL(idn.domset.ds_relink, domid);
3486 		DOMAINSET_ADD(idn.domset.ds_hitlist, domid);
3487 	}
3488 
3489 	if ((domid == IDN_GET_NEW_MASTERID()) &&
3490 	    !DOMAIN_IN_SET(idn.domset.ds_relink, domid)) {
3491 		IDN_SET_NEW_MASTERID(IDN_NIL_DOMID);
3492 	}
3493 
3494 	if ((idn.state != IDNGS_DISCONNECT) && (idn.state != IDNGS_RECONFIG) &&
3495 	    (domid == IDN_GET_MASTERID())) {
3496 		domainset_t	dis_set, master_candidates;
3497 
3498 		IDN_GKSTAT_GLOBAL_EVENT(gk_reconfigs, gk_reconfig_last);
3499 
3500 		IDN_GSTATE_TRANSITION(IDNGS_RECONFIG);
3501 		IDN_GUNLOCK();
3502 
3503 		if ((finmasterid != IDN_NIL_DOMID) &&
3504 		    (finmasterid != idn.localid)) {
3505 			if (finmasterid != domid)
3506 				IDN_DLOCK_EXCL(finmasterid);
3507 			if (idn_open_domain(finmasterid, fincpuid, 0) < 0) {
3508 				cmn_err(CE_WARN,
3509 				    "IDN: 205: (%s) failed to "
3510 				    "open-domain(%d,%d)",
3511 				    proc, finmasterid, fincpuid);
3512 				if (finmasterid != domid)
3513 					IDN_DUNLOCK(finmasterid);
3514 				finmasterid = IDN_NIL_DOMID;
3515 			}
3516 			if (finmasterid != domid)
3517 				IDN_DUNLOCK(finmasterid);
3518 		}
3519 
3520 		IDN_GLOCK_EXCL();
3521 		if (finmasterid == IDN_NIL_DOMID) {
3522 			int	m;
3523 
3524 			master_candidates = idn.domset.ds_trans_on |
3525 			    idn.domset.ds_connected |
3526 			    idn.domset.ds_relink;
3527 			master_candidates &= ~(idn.domset.ds_trans_off &
3528 			    ~idn.domset.ds_relink);
3529 			DOMAINSET_DEL(master_candidates, domid);
3530 			/*
3531 			 * Local domain gets to participate also.
3532 			 */
3533 			DOMAINSET_ADD(master_candidates, idn.localid);
3534 
3535 			m = idn_select_candidate(master_candidates);
3536 			IDN_SET_NEW_MASTERID(m);
3537 		} else {
3538 			IDN_SET_NEW_MASTERID(finmasterid);
3539 		}
3540 		IDN_GUNLOCK();
3541 
3542 		dis_set = idn.domset.ds_trans_on | idn.domset.ds_connected;
3543 		DOMAINSET_DEL(dis_set, domid);
3544 
3545 		idn_unlink_domainset(dis_set, IDNFIN_NORMAL, IDNFIN_ARG_NONE,
3546 		    IDNFIN_OPT_RELINK, BOARDSET_ALL);
3547 	} else {
3548 		IDN_GUNLOCK();
3549 	}
3550 
3551 	/*
3552 	 * My local ready-set are those domains from which I
3553 	 * have confirmed no datapaths exist.
3554 	 */
3555 	my_ready_set = ~idn.domset.ds_connected;
3556 
3557 	switch (dp->dfin) {
3558 	case IDNFIN_NORMAL:
3559 	case IDNFIN_FORCE_SOFT:
3560 	case IDNFIN_FORCE_HARD:
3561 		if (fintype < dp->dfin) {
3562 			/*
3563 			 * Remote domain has requested a
3564 			 * FIN of lower priority than what
3565 			 * we're currently running.  Just
3566 			 * leave the priority where it is.
3567 			 */
3568 			break;
3569 		}
3570 		/*FALLTHROUGH*/
3571 
3572 	default:
3573 		IDN_FSTATE_TRANSITION(dp, fintype);
3574 		break;
3575 	}
3576 
3577 	ASSERT(dp->dfin_sync != IDNFIN_SYNC_OFF);
3578 
3579 	if (msg == 0) {
3580 		/*
3581 		 * Local domain is initiating a FIN sequence
3582 		 * to remote domid.  Note that remote domain
3583 		 * remains in ds_connected even though he's
3584 		 * in thet ready-set from the local domain's
3585 		 * perspective.  We can't remove him from
3586 		 * ds_connected until we get a confirmed message
3587 		 * from him indicating he has ceased communication.
3588 		 */
3589 		ready_set = my_ready_set;
3590 	} else {
3591 		/*
3592 		 * Remote domain initiated a FIN sequence
3593 		 * to local domain.  This implies that he
3594 		 * has shutdown his datapath to us.  Since
3595 		 * we shutdown our datapath to him, we're
3596 		 * effectively now in his ready-set.
3597 		 */
3598 		DOMAINSET_ADD(ready_set, idn.localid);
3599 		/*
3600 		 * Since we know both sides of the connection
3601 		 * have ceased, this remote domain is effectively
3602 		 * considered disconnected.
3603 		 */
3604 		DOMAINSET_ADD(idn.domset.ds_ready_off, domid);
3605 	}
3606 
3607 	if (dp->dfin == IDNFIN_FORCE_HARD) {
3608 		/*
3609 		 * If we're doing a hard disconnect
3610 		 * of this domain then we want to
3611 		 * blow straight through and not
3612 		 * waste time trying to talk to the
3613 		 * remote domain nor to domains we
3614 		 * believe are AWOL.  Although we will
3615 		 * try and do it cleanly with
3616 		 * everybody else.
3617 		 */
3618 		DOMAINSET_ADD(my_ready_set, domid);
3619 		my_ready_set |= idn.domset.ds_awol;
3620 		ready_set = DOMAINSET_ALL;
3621 
3622 	} else if (dp->dfin_sync == IDNFIN_SYNC_NO) {
3623 		/*
3624 		 * If we're not fin'ing this domain
3625 		 * synchronously then the only
3626 		 * expected domain set is himself.
3627 		 */
3628 		ready_set |= ~DOMAINSET(domid);
3629 		my_ready_set |= ~DOMAINSET(domid);
3630 	}
3631 
3632 	if (dp->dsync.s_cmd != IDNSYNC_DISCONNECT) {
3633 		idn_sync_exit(domid, IDNSYNC_CONNECT);
3634 		idn_sync_enter(domid, IDNSYNC_DISCONNECT, DOMAINSET_ALL,
3635 		    my_ready_set, idn_xstate_transfunc,	(void *)IDNP_FIN);
3636 	}
3637 
3638 	query_set = idn_sync_register(domid, IDNSYNC_DISCONNECT, ready_set,
3639 	    IDNSYNC_REG_REG);
3640 
3641 	/*
3642 	 * No need to query this domain as he's already
3643 	 * in the FIN sequence.
3644 	 */
3645 	DOMAINSET_DEL(query_set, domid);
3646 
3647 	ready = (dp->dsync.s_set_exp == dp->dsync.s_set_rdy) ? 1 : 0;
3648 	if (ready) {
3649 		DOMAINSET_DEL(idn.domset.ds_ready_off, domid);
3650 		DOMAINSET_DEL(idn.domset.ds_connected, domid);
3651 	}
3652 
3653 	if (query_set) {
3654 		int	d;
3655 
3656 		my_ready_set = idn.domset.ds_ready_off |
3657 		    ~idn.domset.ds_connected;
3658 
3659 		for (d = 0; d < MAX_DOMAINS; d++) {
3660 			if (!DOMAIN_IN_SET(query_set, d))
3661 				continue;
3662 
3663 			dp = &idn_domain[d];
3664 
3665 			IDN_DLOCK_EXCL(d);
3666 
3667 			if (dp->dsync.s_cmd == IDNSYNC_DISCONNECT) {
3668 				IDN_DUNLOCK(d);
3669 				continue;
3670 			}
3671 
3672 			IDN_SYNC_QUERY_UPDATE(domid, d);
3673 
3674 			(void) idn_send_fin(d, NULL, IDNFIN_QUERY,
3675 			    IDNFIN_ARG_NONE, IDNFIN_OPT_NONE, my_ready_set,
3676 			    NIL_FIN_MASTER);
3677 			IDN_DUNLOCK(d);
3678 		}
3679 	}
3680 
3681 	return (!msg ? 0 : (ready ? 0 : 1));
3682 }
3683 
3684 /*ARGSUSED*/
3685 static void
3686 idn_error_fin_pend(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs)
3687 {
3688 	uint_t	msg = mtp ? mtp->mt_mtype : 0;
3689 	uint_t	token;
3690 
3691 	ASSERT(IDN_SYNC_IS_LOCKED());
3692 	ASSERT(IDN_DLOCK_IS_HELD(domid));
3693 
3694 	/*
3695 	 * Don't communicate with domains that
3696 	 * we're forcing a hard disconnect.
3697 	 */
3698 	if ((idn_domain[domid].dfin != IDNFIN_FORCE_HARD) &&
3699 	    (msg & IDNP_MSGTYPE_MASK)) {
3700 		idn_msgtype_t	mt;
3701 		idn_xdcargs_t	nargs;
3702 
3703 		mt.mt_mtype = IDNP_NACK;
3704 		mt.mt_atype = msg;
3705 		mt.mt_cookie = mtp->mt_cookie;
3706 		CLR_XARGS(nargs);
3707 		SET_XARGS_NACK_TYPE(nargs, IDNNACK_RETRY);
3708 		idn_send_acknack(domid, &mt, nargs);
3709 	}
3710 
3711 	token = IDN_RETRY_TOKEN(domid, IDNRETRY_FIN);
3712 	idn_retry_submit(idn_retry_fin, NULL, token,
3713 	    idn_msg_retrytime[(int)IDNRETRY_FIN]);
3714 }
3715 
3716 static void
3717 idn_action_fin_pend(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs)
3718 {
3719 	idn_domain_t	*dp = &idn_domain[domid];
3720 	domainset_t	my_ready_set;
3721 	idn_finopt_t	finopt;
3722 	idn_finarg_t	finarg;
3723 	uint_t		finmaster;
3724 	int		new_masterid, new_cpuid = IDN_NIL_DCPU;
3725 	uint_t		msg = mtp ? mtp->mt_mtype : 0;
3726 	idn_msgtype_t	mt;
3727 
3728 	ASSERT(IDN_SYNC_IS_LOCKED());
3729 	ASSERT(IDN_DLOCK_IS_HELD(domid));
3730 
3731 	my_ready_set = dp->dsync.s_set_rdy | idn.domset.ds_ready_off |
3732 	    ~idn.domset.ds_connected;
3733 
3734 	ASSERT(xargs[0] != (uint_t)IDNFIN_QUERY);
3735 
3736 	finarg = GET_XARGS_FIN_ARG(xargs);
3737 	finopt = DOMAIN_IN_SET(idn.domset.ds_relink, domid) ?
3738 	    IDNFIN_OPT_RELINK : IDNFIN_OPT_UNLINK;
3739 
3740 	mt.mt_cookie = mtp ? mtp->mt_cookie : 0;
3741 
3742 	IDN_GLOCK_SHARED();
3743 	new_masterid = IDN_GET_NEW_MASTERID();
3744 	IDN_GUNLOCK();
3745 	if (new_masterid != IDN_NIL_DOMID)
3746 		new_cpuid = idn_domain[new_masterid].dcpu;
3747 	finmaster = MAKE_FIN_MASTER(new_masterid, new_cpuid);
3748 
3749 	if (dp->dfin == IDNFIN_FORCE_HARD) {
3750 		ASSERT(IDN_DLOCK_IS_EXCL(domid));
3751 
3752 		if (!msg) {
3753 			mt.mt_mtype = IDNP_FIN | IDNP_ACK;
3754 			mt.mt_atype = 0;
3755 		} else {
3756 			mt.mt_mtype = IDNP_ACK;
3757 			mt.mt_atype = IDNP_FIN | IDNP_ACK;
3758 		}
3759 		(void) idn_xphase_transition(domid, &mt, xargs);
3760 	} else if (!msg) {
3761 		(void) idn_send_fin(domid, NULL, dp->dfin, finarg,
3762 		    finopt, my_ready_set, finmaster);
3763 	} else if ((msg & IDNP_ACKNACK_MASK) == 0) {
3764 		/*
3765 		 * fin
3766 		 */
3767 		mt.mt_mtype = IDNP_FIN | IDNP_ACK;
3768 		mt.mt_atype = 0;
3769 		(void) idn_send_fin(domid, &mt, dp->dfin, finarg,
3770 		    finopt, my_ready_set, finmaster);
3771 	} else {
3772 		uint_t	token;
3773 		/*
3774 		 * nack - retry
3775 		 */
3776 		token = IDN_RETRY_TOKEN(domid, IDNRETRY_FIN);
3777 		idn_retry_submit(idn_retry_fin, NULL, token,
3778 		    idn_msg_retrytime[(int)IDNRETRY_FIN]);
3779 	}
3780 }
3781 
3782 static int
3783 idn_check_fin_sent(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs)
3784 {
3785 	int		ready;
3786 	uint_t		msg = mtp ? mtp->mt_mtype : 0;
3787 	idn_fin_t	fintype;
3788 	idn_finopt_t	finopt;
3789 	idn_domain_t	*dp = &idn_domain[domid];
3790 	domainset_t	query_set, ready_set;
3791 
3792 	ASSERT(IDN_SYNC_IS_LOCKED());
3793 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
3794 
3795 	if (msg & IDNP_NACK)
3796 		return (0);
3797 
3798 	fintype   = GET_XARGS_FIN_TYPE(xargs);
3799 	ready_set = GET_XARGS_FIN_DOMSET(xargs);
3800 	finopt    = GET_XARGS_FIN_OPT(xargs);
3801 
3802 	ASSERT(fintype != IDNFIN_QUERY);
3803 	if (!VALID_FIN(fintype)) {
3804 		/*
3805 		 * If for some reason remote domain
3806 		 * sent us an invalid FIN type,
3807 		 * override it to a  NORMAL fin.
3808 		 */
3809 		fintype = IDNFIN_NORMAL;
3810 	}
3811 
3812 	if (!VALID_FINOPT(finopt)) {
3813 		finopt = IDNFIN_OPT_UNLINK;
3814 	}
3815 	IDN_GLOCK_SHARED();
3816 	if ((finopt == IDNFIN_OPT_RELINK) && (idn.state != IDNGS_DISCONNECT)) {
3817 		DOMAINSET_ADD(idn.domset.ds_relink, domid);
3818 		IDN_HISTORY_LOG(IDNH_RELINK, domid, dp->dstate,
3819 		    idn.domset.ds_relink);
3820 	} else {
3821 		DOMAINSET_DEL(idn.domset.ds_relink, domid);
3822 	}
3823 	IDN_GUNLOCK();
3824 
3825 	switch (dp->dfin) {
3826 	case IDNFIN_NORMAL:
3827 	case IDNFIN_FORCE_SOFT:
3828 	case IDNFIN_FORCE_HARD:
3829 		if (fintype < dp->dfin) {
3830 			/*
3831 			 * Remote domain has requested a
3832 			 * FIN of lower priority than what
3833 			 * we're current running.  Just
3834 			 * leave the priority where it is.
3835 			 */
3836 			break;
3837 		}
3838 		/*FALLTHROUGH*/
3839 
3840 	default:
3841 		IDN_FSTATE_TRANSITION(dp, fintype);
3842 		break;
3843 	}
3844 
3845 	if (dp->dfin == IDNFIN_FORCE_HARD) {
3846 		/*
3847 		 * If we're doing a hard disconnect
3848 		 * of this domain then we want to
3849 		 * blow straight through and not
3850 		 * waste time trying to talk to the
3851 		 * remote domain.  By registering him
3852 		 * as ready with respect to all
3853 		 * possible domains he'll transition
3854 		 * immediately.  Note that we'll still
3855 		 * try and do it coherently with
3856 		 * other domains to which we're connected.
3857 		 */
3858 		ready_set = DOMAINSET_ALL;
3859 	} else {
3860 		DOMAINSET_ADD(ready_set, idn.localid);
3861 	}
3862 
3863 	DOMAINSET_ADD(idn.domset.ds_ready_off, domid);
3864 
3865 	query_set = idn_sync_register(domid, IDNSYNC_DISCONNECT,
3866 	    ready_set, IDNSYNC_REG_REG);
3867 	/*
3868 	 * No need to query this domain as he's already
3869 	 * in the FIN sequence.
3870 	 */
3871 	DOMAINSET_DEL(query_set, domid);
3872 
3873 	ready = (dp->dsync.s_set_exp == dp->dsync.s_set_rdy) ? 1 : 0;
3874 	if (ready) {
3875 		DOMAINSET_DEL(idn.domset.ds_ready_off, domid);
3876 		DOMAINSET_DEL(idn.domset.ds_connected, domid);
3877 	}
3878 
3879 	if (query_set) {
3880 		int		d;
3881 		domainset_t	my_ready_set;
3882 
3883 		my_ready_set = idn.domset.ds_ready_off |
3884 		    ~idn.domset.ds_connected;
3885 
3886 		for (d = 0; d < MAX_DOMAINS; d++) {
3887 			if (!DOMAIN_IN_SET(query_set, d))
3888 				continue;
3889 
3890 			dp = &idn_domain[d];
3891 
3892 			IDN_DLOCK_EXCL(d);
3893 
3894 			if (dp->dsync.s_cmd == IDNSYNC_DISCONNECT) {
3895 				IDN_DUNLOCK(d);
3896 				continue;
3897 			}
3898 
3899 			IDN_SYNC_QUERY_UPDATE(domid, d);
3900 
3901 			(void) idn_send_fin(d, NULL, IDNFIN_QUERY,
3902 			    IDNFIN_ARG_NONE, IDNFIN_OPT_NONE, my_ready_set,
3903 			    NIL_FIN_MASTER);
3904 			IDN_DUNLOCK(d);
3905 		}
3906 	}
3907 
3908 	return ((ready > 0) ? 0 : 1);
3909 }
3910 
3911 /*ARGSUSED*/
3912 static void
3913 idn_error_fin_sent(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs)
3914 {
3915 	uint_t	msg = mtp ? mtp->mt_mtype : 0;
3916 	uint_t	token;
3917 
3918 	ASSERT(IDN_SYNC_IS_LOCKED());
3919 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
3920 
3921 	/*
3922 	 * Don't communicate with domains that
3923 	 * we're forcing a hard disconnect.
3924 	 */
3925 	if ((idn_domain[domid].dfin != IDNFIN_FORCE_HARD) &&
3926 	    (msg & IDNP_MSGTYPE_MASK)) {
3927 		idn_msgtype_t	mt;
3928 		idn_xdcargs_t	nargs;
3929 
3930 		mt.mt_mtype = IDNP_NACK;
3931 		mt.mt_atype = msg;
3932 		mt.mt_cookie = mtp->mt_cookie;
3933 		CLR_XARGS(nargs);
3934 		SET_XARGS_NACK_TYPE(nargs, IDNNACK_RETRY);
3935 		idn_send_acknack(domid, &mt, nargs);
3936 	}
3937 
3938 	token = IDN_RETRY_TOKEN(domid, IDNRETRY_FIN);
3939 	idn_retry_submit(idn_retry_fin, NULL, token,
3940 	    idn_msg_retrytime[(int)IDNRETRY_FIN]);
3941 }
3942 
3943 static void
3944 idn_action_fin_sent(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs)
3945 {
3946 	uint_t		msg = mtp ? mtp->mt_mtype : 0;
3947 	int		new_masterid, new_cpuid = IDN_NIL_DCPU;
3948 	uint_t		finmaster;
3949 	idn_msgtype_t	mt;
3950 	idn_finopt_t	finopt;
3951 	idn_finarg_t	finarg;
3952 	domainset_t	my_ready_set;
3953 	idn_domain_t	*dp = &idn_domain[domid];
3954 
3955 	ASSERT(IDN_SYNC_IS_LOCKED());
3956 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
3957 
3958 	mt.mt_cookie = mtp ? mtp->mt_cookie : 0;
3959 
3960 	finopt = DOMAIN_IN_SET(idn.domset.ds_relink, domid) ?
3961 	    IDNFIN_OPT_RELINK : IDNFIN_OPT_UNLINK;
3962 
3963 	finarg = GET_XARGS_FIN_ARG(xargs);
3964 
3965 	my_ready_set = dp->dsync.s_set_rdy | idn.domset.ds_ready_off |
3966 	    ~idn.domset.ds_connected;
3967 
3968 	IDN_GLOCK_SHARED();
3969 	new_masterid = IDN_GET_NEW_MASTERID();
3970 	IDN_GUNLOCK();
3971 	if (new_masterid != IDN_NIL_DOMID)
3972 		new_cpuid = idn_domain[new_masterid].dcpu;
3973 	finmaster = MAKE_FIN_MASTER(new_masterid, new_cpuid);
3974 
3975 	if ((msg & IDNP_ACKNACK_MASK) == 0) {
3976 		/*
3977 		 * fin
3978 		 */
3979 		if (dp->dfin == IDNFIN_FORCE_HARD) {
3980 			mt.mt_mtype = IDNP_ACK;
3981 			mt.mt_atype = IDNP_FIN | IDNP_ACK;
3982 			(void) idn_xphase_transition(domid, &mt, xargs);
3983 		} else {
3984 			mt.mt_mtype = IDNP_FIN | IDNP_ACK;
3985 			mt.mt_atype = 0;
3986 			(void) idn_send_fin(domid, &mt, dp->dfin, finarg,
3987 			    finopt, my_ready_set, finmaster);
3988 		}
3989 	} else if (msg & IDNP_MSGTYPE_MASK) {
3990 		/*
3991 		 * fin+ack
3992 		 */
3993 		if (dp->dfin != IDNFIN_FORCE_HARD) {
3994 			idn_xdcargs_t	fargs;
3995 
3996 			mt.mt_mtype = IDNP_ACK;
3997 			mt.mt_atype = msg;
3998 			CLR_XARGS(fargs);
3999 			SET_XARGS_FIN_TYPE(fargs, dp->dfin);
4000 			SET_XARGS_FIN_ARG(fargs, finarg);
4001 			SET_XARGS_FIN_DOMSET(fargs, my_ready_set);
4002 			SET_XARGS_FIN_OPT(fargs, finopt);
4003 			SET_XARGS_FIN_MASTER(fargs, finmaster);
4004 			idn_send_acknack(domid, &mt, fargs);
4005 		}
4006 	} else {
4007 		uint_t	token;
4008 		/*
4009 		 * nack - retry
4010 		 */
4011 		token = IDN_RETRY_TOKEN(domid, IDNRETRY_FIN);
4012 		idn_retry_submit(idn_retry_fin, NULL, token,
4013 		    idn_msg_retrytime[(int)IDNRETRY_FIN]);
4014 	}
4015 }
4016 
4017 /*ARGSUSED*/
4018 static void
4019 idn_action_fin_rcvd(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs)
4020 {
4021 	uint_t	msg = mtp ? mtp->mt_mtype : 0;
4022 
4023 	ASSERT(IDN_SYNC_IS_LOCKED());
4024 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
4025 
4026 	if (msg & IDNP_NACK) {
4027 		uint_t	token;
4028 		/*
4029 		 * nack - retry.
4030 		 */
4031 		token = IDN_RETRY_TOKEN(domid, IDNRETRY_FIN);
4032 		idn_retry_submit(idn_retry_fin, NULL, token,
4033 		    idn_msg_retrytime[(int)IDNRETRY_FIN]);
4034 	}
4035 }
4036 
4037 static void
4038 idn_final_fin(int domid)
4039 {
4040 	int		do_relink;
4041 	int		rv, d, new_masterid = IDN_NIL_DOMID;
4042 	idn_gstate_t	next_gstate;
4043 	domainset_t	relinkset;
4044 	uint_t		token = IDN_RETRY_TOKEN(domid, IDNRETRY_FIN);
4045 	idn_domain_t	*ldp, *dp = &idn_domain[domid];
4046 	procname_t	proc = "idn_final_fin";
4047 
4048 	ASSERT(IDN_SYNC_IS_LOCKED());
4049 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
4050 	ASSERT(dp->dstate == IDNDS_DMAP);
4051 
4052 	(void) idn_retry_terminate(token);
4053 
4054 	dp->dxp = NULL;
4055 	IDN_XSTATE_TRANSITION(dp, IDNXS_NIL);
4056 
4057 	idn_sync_exit(domid, IDNSYNC_DISCONNECT);
4058 
4059 	DOMAINSET_DEL(idn.domset.ds_trans_off, domid);
4060 
4061 	do_relink = DOMAIN_IN_SET(idn.domset.ds_relink, domid) ? 1 : 0;
4062 
4063 	/*
4064 	 * idn_deconfig will idn_close_domain.
4065 	 */
4066 	idn_deconfig(domid);
4067 
4068 	PR_PROTO("%s:%d: DISCONNECTED\n", proc, domid);
4069 
4070 	IDN_GLOCK_EXCL();
4071 	/*
4072 	 * It's important that this update-op occur within
4073 	 * the context of holding the glock(EXCL).  There is
4074 	 * still some additional state stuff to cleanup which
4075 	 * will be completed once the glock is dropped in
4076 	 * this flow.  Which means anybody that's doing a
4077 	 * SSI_INFO and waiting on glock will not actually
4078 	 * run until the clean-up is completed, which is what
4079 	 * we want.  Recall that a separate thread processes
4080 	 * the SSI_LINK/UNLINK calls and when they complete
4081 	 * (i.e. are awakened) they will immediately SSI_INFO
4082 	 * and we don't want them to prematurely pick up stale
4083 	 * information.
4084 	 */
4085 	idn_update_op(IDNOP_DISCONNECTED, DOMAINSET(domid), NULL);
4086 
4087 	ASSERT(idn.state != IDNGS_OFFLINE);
4088 	ASSERT(!DOMAIN_IN_SET(idn.domset.ds_trans_on, domid));
4089 
4090 	if (domid == IDN_GET_MASTERID()) {
4091 		IDN_SET_MASTERID(IDN_NIL_DOMID);
4092 		dp->dvote.v.master = 0;
4093 	}
4094 
4095 	if ((domid == IDN_GET_NEW_MASTERID()) && !do_relink) {
4096 		IDN_SET_NEW_MASTERID(IDN_NIL_DOMID);
4097 	}
4098 
4099 	if (idn.state == IDNGS_RECONFIG)
4100 		new_masterid = IDN_GET_NEW_MASTERID();
4101 
4102 	if ((idn.domset.ds_trans_on | idn.domset.ds_trans_off |
4103 	    idn.domset.ds_relink) == 0) {
4104 		PR_HITLIST("%s:%d: HITLIST %x -> 0\n",
4105 		    proc, domid, idn.domset.ds_hitlist);
4106 		idn.domset.ds_hitlist = 0;
4107 	}
4108 
4109 	if (idn.domset.ds_connected || idn.domset.ds_trans_off) {
4110 		PR_PROTO("%s:%d: ds_connected = 0x%x, ds_trans_off = 0x%x\n",
4111 		    proc, domid, idn.domset.ds_connected,
4112 		    idn.domset.ds_trans_off);
4113 		IDN_GUNLOCK();
4114 		goto fin_done;
4115 	}
4116 
4117 	IDN_DLOCK_EXCL(idn.localid);
4118 	ldp = &idn_domain[idn.localid];
4119 
4120 	if (idn.domset.ds_trans_on != 0) {
4121 		ASSERT((idn.state != IDNGS_DISCONNECT) &&
4122 		    (idn.state != IDNGS_OFFLINE));
4123 
4124 		switch (idn.state) {
4125 		case IDNGS_CONNECT:
4126 			if (idn.localid == IDN_GET_MASTERID()) {
4127 				idn_master_deinit();
4128 				IDN_SET_MASTERID(IDN_NIL_DOMID);
4129 				ldp->dvote.v.master = 0;
4130 			}
4131 			/*FALLTHROUGH*/
4132 		case IDNGS_ONLINE:
4133 			next_gstate = idn.state;
4134 			break;
4135 
4136 		case IDNGS_RECONFIG:
4137 			if (idn.localid == IDN_GET_MASTERID()) {
4138 				idn_master_deinit();
4139 				IDN_SET_MASTERID(IDN_NIL_DOMID);
4140 				ldp->dvote.v.master = 0;
4141 			}
4142 			ASSERT(IDN_GET_MASTERID() == IDN_NIL_DOMID);
4143 			next_gstate = IDNGS_CONNECT;
4144 			ldp->dvote.v.connected = 0;
4145 			/*
4146 			 * Need to do HWINIT since we won't
4147 			 * be transitioning through OFFLINE
4148 			 * which would normally be caught in
4149 			 * idn_check_nego() when we
4150 			 * initially go to CONNECT.
4151 			 */
4152 			IDN_PREP_HWINIT();
4153 			break;
4154 
4155 		case IDNGS_DISCONNECT:
4156 		case IDNGS_OFFLINE:
4157 			cmn_err(CE_WARN,
4158 			    "IDN: 211: disconnect domain %d, "
4159 			    "unexpected Gstate (%s)",
4160 			    domid, idngs_str[idn.state]);
4161 			IDN_DUNLOCK(idn.localid);
4162 			IDN_GUNLOCK();
4163 			goto fin_done;
4164 
4165 		default:
4166 			/*
4167 			 * XXX
4168 			 * Go into FATAL state?
4169 			 */
4170 			cmn_err(CE_PANIC,
4171 			    "IDN: 212: disconnect domain %d, "
4172 			    "bad Gstate (%d)",
4173 			    domid, idn.state);
4174 			/* not reached */
4175 			break;
4176 		}
4177 	} else {
4178 		if (idn.localid == IDN_GET_MASTERID()) {
4179 			idn_master_deinit();
4180 			IDN_SET_MASTERID(IDN_NIL_DOMID);
4181 			ldp->dvote.v.master = 0;
4182 		}
4183 		next_gstate = IDNGS_OFFLINE;
4184 		if (idn.domset.ds_relink == 0) {
4185 			IDN_SET_NEW_MASTERID(IDN_NIL_DOMID);
4186 		}
4187 	}
4188 	IDN_DUNLOCK(idn.localid);
4189 
4190 	/*
4191 	 * If we reach here we've effectively disconnected all
4192 	 * existing links, however new ones may be pending.
4193 	 */
4194 	PR_PROTO("%s:%d: ALL DISCONNECTED *****************\n", proc, domid);
4195 
4196 	IDN_GSTATE_TRANSITION(next_gstate);
4197 
4198 	ASSERT((idn.state == IDNGS_OFFLINE) ?
4199 	    (IDN_GET_MASTERID() == IDN_NIL_DOMID) : 1);
4200 
4201 	IDN_GUNLOCK();
4202 
4203 	/*
4204 	 * If we have no new masterid and yet there are relinkers
4205 	 * out there, then force us to attempt to link with one
4206 	 * of them.
4207 	 */
4208 	if ((new_masterid == IDN_NIL_DOMID) && idn.domset.ds_relink)
4209 		new_masterid = idn.localid;
4210 
4211 	if (new_masterid != IDN_NIL_DOMID) {
4212 		/*
4213 		 * If the local domain is the selected
4214 		 * master then we'll want to initiate
4215 		 * a link with one of the other candidates.
4216 		 * If not, then we want to initiate a link
4217 		 * with the master only.
4218 		 */
4219 		relinkset = (new_masterid == idn.localid) ?
4220 		    idn.domset.ds_relink : DOMAINSET(new_masterid);
4221 
4222 		DOMAINSET_DEL(relinkset, idn.localid);
4223 
4224 		for (d = 0; d < MAX_DOMAINS; d++) {
4225 			int	lock_held;
4226 
4227 			if (!DOMAIN_IN_SET(relinkset, d))
4228 				continue;
4229 
4230 			if (d == domid) {
4231 				do_relink = 0;
4232 				lock_held = 0;
4233 			} else {
4234 				IDN_DLOCK_EXCL(d);
4235 				lock_held = 1;
4236 			}
4237 
4238 			rv = idn_open_domain(d, -1, 0);
4239 			if (rv == 0) {
4240 				rv = idn_connect(d);
4241 				if (lock_held)
4242 					IDN_DUNLOCK(d);
4243 				/*
4244 				 * If we're able to kick off at
4245 				 * least one connect then that's
4246 				 * good enough for now.  The others
4247 				 * will fall into place normally.
4248 				 */
4249 				if (rv == 0)
4250 					break;
4251 			} else if (rv < 0) {
4252 				if (lock_held)
4253 					IDN_DUNLOCK(d);
4254 				cmn_err(CE_WARN,
4255 				    "IDN: 205: (%s.1) failed to "
4256 				    "open-domain(%d,%d)",
4257 				    proc, domid, -1);
4258 				DOMAINSET_DEL(idn.domset.ds_relink, d);
4259 			} else {
4260 				if (lock_held)
4261 					IDN_DUNLOCK(d);
4262 				PR_PROTO("%s:%d: failed to "
4263 				    "re-open domain %d "
4264 				    "(cpu %d) [rv = %d]\n",
4265 				    proc, domid, d, idn_domain[d].dcpu,
4266 				    rv);
4267 			}
4268 		}
4269 	}
4270 
4271 fin_done:
4272 	if (do_relink) {
4273 		ASSERT(IDN_DLOCK_IS_EXCL(domid));
4274 
4275 		rv = idn_open_domain(domid, -1, 0);
4276 		if (rv == 0) {
4277 			(void) idn_connect(domid);
4278 		} else if (rv < 0) {
4279 			cmn_err(CE_WARN,
4280 			    "IDN: 205: (%s.2) failed to "
4281 			    "open-domain(%d,%d)",
4282 			    proc, domid, -1);
4283 			DOMAINSET_DEL(idn.domset.ds_relink, domid);
4284 		}
4285 	}
4286 }
4287 
4288 static void
4289 idn_exit_fin(int domid, uint_t msgtype)
4290 {
4291 	idn_domain_t	*dp = &idn_domain[domid];
4292 	uint_t		token;
4293 	procname_t	proc = "idn_exit_fin";
4294 	STRING(str);
4295 
4296 	ASSERT(IDN_SYNC_IS_LOCKED());
4297 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
4298 
4299 	INUM2STR(msgtype, str);
4300 	PR_PROTO("%s:%d: msgtype = 0x%x(%s)\n", proc, domid, msgtype, str);
4301 
4302 	token = IDN_RETRY_TOKEN(domid, IDNRETRY_FIN);
4303 	(void) idn_retry_terminate(token);
4304 
4305 	DOMAINSET_DEL(idn.domset.ds_ready_off, domid);
4306 
4307 	dp->dxp = &xphase_fin;
4308 	IDN_XSTATE_TRANSITION(dp, IDNXS_PEND);
4309 
4310 	idn_retry_submit(idn_retry_fin, NULL, token,
4311 	    idn_msg_retrytime[(int)IDNRETRY_FIN]);
4312 }
4313 
4314 /*
4315  * Must return w/locks held.
4316  */
4317 static int
4318 idn_xphase_transition(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs)
4319 {
4320 	uint_t		msg = mtp ? mtp->mt_mtype : 0;
4321 	uint_t		msgarg = mtp ? mtp->mt_atype : 0;
4322 	idn_xphase_t	*xp;
4323 	idn_domain_t	*dp;
4324 	int		(*cfunc)(int, idn_msgtype_t *, idn_xdcargs_t);
4325 	void		(*ffunc)(int);
4326 	void		(*afunc)(int, idn_msgtype_t *, idn_xdcargs_t);
4327 	void		(*efunc)(int, idn_msgtype_t *, idn_xdcargs_t);
4328 	void		(*xfunc)(int, uint_t);
4329 	int		err = 0;
4330 	uint_t		msgtype;
4331 	idn_xstate_t	o_xstate, n_xstate;
4332 	procname_t	proc = "idn_xphase_transition";
4333 	STRING(mstr);
4334 	STRING(astr);
4335 
4336 	ASSERT(IDN_SYNC_IS_LOCKED());
4337 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
4338 
4339 	INUM2STR(msg, mstr);
4340 	INUM2STR(msgarg, astr);
4341 
4342 	dp = &idn_domain[domid];
4343 	if ((xp = dp->dxp) == NULL) {
4344 		PR_PROTO("%s:%d: WARNING: domain xsp is NULL (msg = %s, "
4345 		    "msgarg = %s) <<<<<<<<<<<<\n",
4346 		    proc, domid, mstr, astr);
4347 		return (-1);
4348 	}
4349 	o_xstate = dp->dxstate;
4350 
4351 	xfunc = xp->xt_exit;
4352 
4353 	if ((msgtype = (msg & IDNP_MSGTYPE_MASK)) == 0)
4354 		msgtype = msgarg & IDNP_MSGTYPE_MASK;
4355 
4356 	if ((o_xstate == IDNXS_PEND) && msg &&
4357 	    ((msg & IDNP_ACKNACK_MASK) == msg)) {
4358 		PR_PROTO("%s:%d: unwanted acknack received (o_xstate = %s, "
4359 		    "msg = %s/%s - dropping message\n",
4360 		    proc, domid, idnxs_str[(int)o_xstate], mstr, astr);
4361 		return (0);
4362 	}
4363 
4364 	/*
4365 	 * Validate that message received is following
4366 	 * the expected protocol for the current state.
4367 	 */
4368 	if (idn_next_xstate(o_xstate, -1, msg) == IDNXS_NIL) {
4369 		PR_PROTO("%s:%d: WARNING: o_xstate = %s, msg = %s -> NIL "
4370 		    "<<<<<<<<<\n",
4371 		    proc, domid, idnxs_str[(int)o_xstate], mstr);
4372 		if (xfunc)
4373 			(*xfunc)(domid, msgtype);
4374 		return (-1);
4375 	}
4376 
4377 	if (msg || msgarg) {
4378 		/*
4379 		 * Verify that message type is correct for
4380 		 * the given xstate.
4381 		 */
4382 		if (msgtype != xp->xt_msgtype) {
4383 			STRING(xstr);
4384 			STRING(tstr);
4385 
4386 			INUM2STR(xp->xt_msgtype, xstr);
4387 			INUM2STR(msgtype, tstr);
4388 			PR_PROTO("%s:%d: WARNING: msg expected %s(0x%x), "
4389 			    "actual %s(0x%x) [msg=%s(0x%x), "
4390 			    "msgarg=%s(0x%x)]\n",
4391 			    proc, domid, xstr, xp->xt_msgtype,
4392 			    tstr, msgtype, mstr, msg, astr, msgarg);
4393 			if (xfunc)
4394 				(*xfunc)(domid, msgtype);
4395 			return (-1);
4396 		}
4397 	}
4398 
4399 	cfunc = xp->xt_trans[(int)o_xstate].t_check;
4400 
4401 	if (cfunc && ((err = (*cfunc)(domid, mtp, xargs)) < 0)) {
4402 		if (o_xstate != IDNXS_PEND) {
4403 			IDN_XSTATE_TRANSITION(dp, IDNXS_PEND);
4404 		}
4405 		if (xfunc)
4406 			(*xfunc)(domid, msgtype);
4407 		return (-1);
4408 	}
4409 
4410 	n_xstate = idn_next_xstate(o_xstate, err, msg);
4411 
4412 	if (n_xstate == IDNXS_NIL) {
4413 		PR_PROTO("%s:%d: WARNING: n_xstate = %s, msg = %s -> NIL "
4414 		    "<<<<<<<<<\n",
4415 		    proc, domid, idnxs_str[(int)n_xstate], mstr);
4416 		if (xfunc)
4417 			(*xfunc)(domid, msgtype);
4418 		return (-1);
4419 	}
4420 
4421 	if (n_xstate != o_xstate) {
4422 		IDN_XSTATE_TRANSITION(dp, n_xstate);
4423 	}
4424 
4425 	if (err) {
4426 		if ((efunc = xp->xt_trans[(int)o_xstate].t_error) != NULL)
4427 			(*efunc)(domid, mtp, xargs);
4428 	} else if ((afunc = xp->xt_trans[(int)o_xstate].t_action) != NULL) {
4429 		(*afunc)(domid, mtp, xargs);
4430 	}
4431 
4432 	if ((n_xstate == IDNXS_FINAL) && ((ffunc = xp->xt_final) != NULL))
4433 		(*ffunc)(domid);
4434 
4435 	return (0);
4436 }
4437 
4438 /*
4439  * Entered and returns w/DLOCK & SYNC_LOCK held.
4440  */
4441 static int
4442 idn_xstate_transfunc(int domid, void *transarg)
4443 {
4444 	uint_t		msg = (uint_t)(uintptr_t)transarg;
4445 	uint_t		token;
4446 	procname_t	proc = "idn_xstate_transfunc";
4447 
4448 	ASSERT(IDN_SYNC_IS_LOCKED());
4449 
4450 	switch (msg) {
4451 	case IDNP_CON:
4452 		DOMAINSET_ADD(idn.domset.ds_connected, domid);
4453 		break;
4454 
4455 	case IDNP_FIN:
4456 		DOMAINSET_DEL(idn.domset.ds_connected, domid);
4457 		break;
4458 
4459 	default:
4460 		PR_PROTO("%s:%d: ERROR: unknown msg (0x%x) <<<<<<<<\n",
4461 		    proc, domid, msg);
4462 		return (0);
4463 	}
4464 
4465 	token = IDN_RETRY_TOKEN(domid, (msg == IDNP_CON) ?
4466 	    IDNRETRY_CON : IDNRETRY_FIN);
4467 	if (msg == IDNP_CON)
4468 		idn_retry_submit(idn_retry_con, NULL, token,
4469 		    idn_msg_retrytime[(int)IDNRETRY_CON]);
4470 	else
4471 		idn_retry_submit(idn_retry_fin, NULL, token,
4472 		    idn_msg_retrytime[(int)IDNRETRY_FIN]);
4473 
4474 	return (1);
4475 }
4476 
4477 /*
4478  * Entered and returns w/DLOCK & SYNC_LOCK held.
4479  */
4480 static void
4481 idn_sync_enter(int domid, idn_synccmd_t cmd, domainset_t xset,
4482     domainset_t rset, int (*transfunc)(), void *transarg)
4483 {
4484 	int		z;
4485 	idn_syncop_t	*sp;
4486 	idn_synczone_t	*zp;
4487 	procname_t	proc = "idn_sync_enter";
4488 
4489 	ASSERT(IDN_SYNC_IS_LOCKED());
4490 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
4491 
4492 	z = IDN_SYNC_GETZONE(cmd);
4493 	ASSERT(z >= 0);
4494 	zp = &idn.sync.sz_zone[z];
4495 
4496 	PR_SYNC("%s:%d: cmd=%s(%d), z=%d, xs=0x%x, rx=0x%x, cnt=%d\n",
4497 	    proc, domid, idnsync_str[cmd], cmd, z, xset, rset, zp->sc_cnt);
4498 
4499 	sp = &idn_domain[domid].dsync;
4500 
4501 	sp->s_domid = domid;
4502 	sp->s_cmd = cmd;
4503 	sp->s_msg = 0;
4504 	sp->s_set_exp = xset;
4505 	sp->s_set_rdy = rset;
4506 	sp->s_transfunc = transfunc;
4507 	sp->s_transarg = transarg;
4508 	IDN_SYNC_QUERY_INIT(domid);
4509 
4510 	sp->s_next = zp->sc_op;
4511 	zp->sc_op = sp;
4512 	zp->sc_cnt++;
4513 }
4514 
4515 /*
4516  * Entered and returns w/DLOCK & SYNC_LOCK held.
4517  */
4518 void
4519 idn_sync_exit(int domid, idn_synccmd_t cmd)
4520 {
4521 	int		d, z, zone, tot_queries, tot_domains;
4522 	idn_syncop_t	*sp;
4523 	idn_synczone_t	*zp = NULL;
4524 	procname_t	proc = "idn_sync_exit";
4525 
4526 	ASSERT(IDN_SYNC_IS_LOCKED());
4527 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
4528 
4529 	sp = &idn_domain[domid].dsync;
4530 
4531 	z = IDN_SYNC_GETZONE(sp->s_cmd);
4532 
4533 	zone = IDN_SYNC_GETZONE(cmd);
4534 
4535 	PR_SYNC("%s:%d: cmd=%s(%d) (z=%d, zone=%d)\n",
4536 	    proc, domid, idnsync_str[cmd], cmd, z, zone);
4537 
4538 #ifdef DEBUG
4539 	if (z != -1) {
4540 		tot_queries = tot_domains = 0;
4541 
4542 		for (d = 0; d < MAX_DOMAINS; d++) {
4543 			int	qv;
4544 
4545 			if ((qv = sp->s_query[d]) > 0) {
4546 				tot_queries += qv;
4547 				tot_domains++;
4548 				PR_SYNC("%s:%d: query_count = %d\n",
4549 				    proc, domid, qv);
4550 			}
4551 		}
4552 		PR_SYNC("%s:%d: tot_queries = %d, tot_domaines = %d\n",
4553 		    proc, domid, tot_queries, tot_domains);
4554 	}
4555 #endif /* DEBUG */
4556 
4557 	zp = (z != -1) ? &idn.sync.sz_zone[z] : NULL;
4558 
4559 	if (zp) {
4560 		idn_syncop_t	**spp;
4561 
4562 		for (spp = &zp->sc_op; *spp; spp = &((*spp)->s_next)) {
4563 			if (*spp == sp) {
4564 				*spp = sp->s_next;
4565 				sp->s_next = NULL;
4566 				zp->sc_cnt--;
4567 				break;
4568 			}
4569 		}
4570 	}
4571 
4572 	sp->s_cmd = IDNSYNC_NIL;
4573 
4574 	for (z = 0; z < IDN_SYNC_NUMZONE; z++) {
4575 		idn_syncop_t	**spp, **nspp;
4576 
4577 		if ((zone != -1) && (z != zone))
4578 			continue;
4579 
4580 		zp = &idn.sync.sz_zone[z];
4581 
4582 		for (spp = &zp->sc_op; *spp; spp = nspp) {
4583 			sp = *spp;
4584 			nspp = &sp->s_next;
4585 
4586 			if (!DOMAIN_IN_SET(sp->s_set_exp, domid))
4587 				continue;
4588 
4589 			DOMAINSET_DEL(sp->s_set_exp, domid);
4590 			DOMAINSET_DEL(sp->s_set_rdy, domid);
4591 
4592 			if ((sp->s_set_exp == sp->s_set_rdy) &&
4593 			    sp->s_transfunc) {
4594 				int	delok;
4595 
4596 				ASSERT(sp->s_domid != domid);
4597 
4598 				PR_SYNC("%s:%d invoking transfunc "
4599 				    "for domain %d\n",
4600 				    proc, domid, sp->s_domid);
4601 				delok = (*sp->s_transfunc)(sp->s_domid,
4602 				    sp->s_transarg);
4603 				if (delok) {
4604 					*spp = sp->s_next;
4605 					sp->s_next = NULL;
4606 					zp->sc_cnt--;
4607 					nspp = spp;
4608 				}
4609 			}
4610 		}
4611 	}
4612 }
4613 
4614 /*
4615  * Entered and returns w/DLOCK & SYNC_LOCK held.
4616  */
4617 static domainset_t
4618 idn_sync_register(int domid, idn_synccmd_t cmd, domainset_t ready_set,
4619     idn_syncreg_t regtype)
4620 {
4621 	int		z;
4622 	idn_synczone_t	*zp;
4623 	idn_syncop_t	*sp, **spp, **nspp;
4624 	domainset_t	query_set = 0, trans_set;
4625 	procname_t	proc = "idn_sync_register";
4626 
4627 	ASSERT(IDN_SYNC_IS_LOCKED());
4628 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
4629 
4630 	if ((z = IDN_SYNC_GETZONE(cmd)) == -1) {
4631 		PR_SYNC("%s:%d: ERROR: unexpected sync cmd(%d)\n",
4632 		    proc, domid, cmd);
4633 		return (0);
4634 	}
4635 
4636 	/*
4637 	 * Find out what domains are in transition with respect
4638 	 * to given command.  There will be no need to query
4639 	 * these folks.
4640 	 */
4641 	trans_set = IDN_SYNC_GETTRANS(cmd);
4642 
4643 	zp = &idn.sync.sz_zone[z];
4644 
4645 	PR_SYNC("%s:%d: cmd=%s(%d), z=%d, rset=0x%x, "
4646 	    "regtype=%s(%d), sc_op=%s\n",
4647 	    proc, domid, idnsync_str[cmd], cmd, z, ready_set,
4648 	    idnreg_str[regtype], regtype,
4649 	    zp->sc_op ? idnsync_str[zp->sc_op->s_cmd] : "NULL");
4650 
4651 	for (spp = &zp->sc_op; *spp; spp = nspp) {
4652 		sp = *spp;
4653 		nspp = &sp->s_next;
4654 
4655 		if (regtype == IDNSYNC_REG_NEW) {
4656 			DOMAINSET_ADD(sp->s_set_exp, domid);
4657 			PR_SYNC("%s:%d: adding new to %d (exp=0x%x)\n",
4658 			    proc, domid, sp->s_domid, sp->s_set_exp);
4659 		} else if (regtype == IDNSYNC_REG_QUERY) {
4660 			query_set |= ~sp->s_set_rdy & sp->s_set_exp;
4661 			continue;
4662 		}
4663 
4664 		if (!DOMAIN_IN_SET(sp->s_set_exp, domid))
4665 			continue;
4666 
4667 		if (!DOMAIN_IN_SET(ready_set, sp->s_domid)) {
4668 			/*
4669 			 * Given domid doesn't have a desired
4670 			 * domain in his ready-set.  We'll need
4671 			 * to query him again.
4672 			 */
4673 			DOMAINSET_ADD(query_set, domid);
4674 			continue;
4675 		}
4676 
4677 		/*
4678 		 * If we reach here, then an expected domain
4679 		 * has marked its respective datapath to
4680 		 * sp->s_domid as down (i.e. in his ready_set).
4681 		 */
4682 		DOMAINSET_ADD(sp->s_set_rdy, domid);
4683 
4684 		PR_SYNC("%s:%d: mark READY for domain %d "
4685 		    "(r=0x%x, x=0x%x)\n",
4686 		    proc, domid, sp->s_domid,
4687 		    sp->s_set_rdy, sp->s_set_exp);
4688 
4689 		query_set |= ~sp->s_set_rdy & sp->s_set_exp;
4690 
4691 		if (sp->s_set_exp == sp->s_set_rdy) {
4692 #ifdef DEBUG
4693 			if (sp->s_msg == 0) {
4694 				sp->s_msg = 1;
4695 				PR_SYNC("%s:%d: >>>>>>>>>>> DOMAIN %d "
4696 				    "ALL CHECKED IN (0x%x)\n",
4697 				    proc, domid, sp->s_domid,
4698 				    sp->s_set_exp);
4699 			}
4700 #endif /* DEBUG */
4701 
4702 			if ((sp->s_domid != domid) && sp->s_transfunc) {
4703 				int	delok;
4704 
4705 				PR_SYNC("%s:%d invoking transfunc "
4706 				    "for domain %d\n",
4707 				    proc, domid, sp->s_domid);
4708 				delok = (*sp->s_transfunc)(sp->s_domid,
4709 				    sp->s_transarg);
4710 				if (delok) {
4711 					*spp = sp->s_next;
4712 					sp->s_next = NULL;
4713 					zp->sc_cnt--;
4714 					nspp = spp;
4715 				}
4716 			}
4717 		}
4718 	}
4719 
4720 	PR_SYNC("%s:%d: trans_set = 0x%x, query_set = 0x%x -> 0x%x\n",
4721 	    proc, domid, trans_set, query_set, query_set & ~trans_set);
4722 
4723 	query_set &= ~trans_set;
4724 
4725 	return (query_set);
4726 }
4727 
4728 static void
4729 idn_sync_register_awol(int domid)
4730 {
4731 	int		z;
4732 	idn_synccmd_t	cmd = IDNSYNC_DISCONNECT;
4733 	idn_synczone_t	*zp;
4734 	idn_syncop_t	*sp;
4735 	procname_t	proc = "idn_sync_register_awol";
4736 
4737 	ASSERT(IDN_SYNC_IS_LOCKED());
4738 
4739 	if ((z = IDN_SYNC_GETZONE(cmd)) == -1) {
4740 		PR_SYNC("%s:%d: ERROR: unexpected sync cmd(%d)\n",
4741 		    proc, domid, cmd);
4742 		return;
4743 	}
4744 
4745 	zp = &idn.sync.sz_zone[z];
4746 
4747 	PR_SYNC("%s:%d: cmd=%s(%d), z=%d (domain %d = AWOL)\n",
4748 	    proc, domid, idnsync_str[cmd], cmd, z, domid);
4749 
4750 	for (sp = zp->sc_op; sp; sp = sp->s_next) {
4751 		idn_domain_t	*dp;
4752 
4753 		dp = &idn_domain[sp->s_domid];
4754 		if (dp->dfin == IDNFIN_FORCE_HARD) {
4755 			DOMAINSET_ADD(sp->s_set_rdy, domid);
4756 			PR_SYNC("%s:%d: adding new to %d (rdy=0x%x)\n",
4757 			    proc, domid, sp->s_domid, sp->s_set_rdy);
4758 		}
4759 	}
4760 }
4761 
4762 static void
4763 idn_link_established(void *arg)
4764 {
4765 	int	first_link;
4766 	int	domid, masterid;
4767 	uint_t	info = (uint_t)(uintptr_t)arg;
4768 
4769 	first_link = (int)(info & 0xf0);
4770 	domid = (int)(info & 0x0f);
4771 
4772 	IDN_GLOCK_SHARED();
4773 	masterid = IDN_GET_MASTERID();
4774 	if ((masterid == IDN_NIL_DOMID) ||
4775 	    (idn_domain[masterid].dstate != IDNDS_CONNECTED)) {
4776 		/*
4777 		 * No point in doing this unless we're connected
4778 		 * to the master.
4779 		 */
4780 		if ((masterid != IDN_NIL_DOMID) &&
4781 		    (idn.state == IDNGS_ONLINE)) {
4782 			/*
4783 			 * As long as we're still online keep
4784 			 * trying.
4785 			 */
4786 			(void) timeout(idn_link_established, arg, 50);
4787 		}
4788 		IDN_GUNLOCK();
4789 		return;
4790 	}
4791 	IDN_GUNLOCK();
4792 
4793 	if (first_link && IDN_SLAB_PREALLOC)
4794 		idn_prealloc_slab(IDN_SLAB_PREALLOC);
4795 
4796 	/*
4797 	 * No guarantee, but it might save a little
4798 	 * time.
4799 	 */
4800 	if (idn_domain[domid].dstate == IDNDS_CONNECTED) {
4801 		/*
4802 		 * Get the remote domain's dname.
4803 		 */
4804 		idn_send_nodename_req(domid);
4805 	}
4806 
4807 	/*
4808 	 * May have had some streams backed up waiting for
4809 	 * this connection.  Prod them.
4810 	 */
4811 	rw_enter(&idn.struprwlock, RW_READER);
4812 	mutex_enter(&idn.sipwenlock);
4813 	idndl_wenable(NULL);
4814 	mutex_exit(&idn.sipwenlock);
4815 	rw_exit(&idn.struprwlock);
4816 }
4817 
4818 /*
4819  * Send the following chunk of data received from above onto
4820  * the IDN wire.  This is raw data as far as the IDN driver
4821  * is concerned.
4822  * Returns:
4823  *	IDNXMIT_LOOP	- Msg handled in loopback and thus
4824  *			  still active (i.e. don't free).
4825  *	IDNXMIT_OKAY	- Data handled (freemsg).
4826  *	IDNXMIT_DROP	- Packet should be dropped.
4827  *	IDNXMIT_RETRY	- Packet should be requeued and retried.
4828  *	IDNXMIT_REQUEUE	- Packet should be requeued, but not
4829  *			  immediatetly retried.
4830  */
4831 int
4832 idn_send_data(int dst_domid, idn_netaddr_t dst_netaddr, queue_t *wq, mblk_t *mp)
4833 {
4834 	int		pktcnt = 0;
4835 	int		msglen;
4836 	int		rv = IDNXMIT_OKAY;
4837 	int		xfersize = 0;
4838 	caddr_t		iobufp, iodatap;
4839 	uchar_t		*data_rptr;
4840 	int		cpuindex;
4841 	int		serrno;
4842 	int		channel;
4843 	int		retry_reclaim;
4844 	idn_chansvr_t	*csp = NULL;
4845 	uint_t		netports = 0;
4846 	struct idnstr	*stp;
4847 	struct idn	*sip;
4848 	idn_domain_t	*dp;
4849 	struct ether_header	*ehp;
4850 	smr_pkthdr_t	*hdrp;
4851 	idn_msgtype_t	mt;
4852 	procname_t	proc = "idn_send_data";
4853 #ifdef DEBUG
4854 	size_t		orig_msglen = msgsize(mp);
4855 #endif /* DEBUG */
4856 
4857 	ASSERT(DB_TYPE(mp) == M_DATA);
4858 
4859 	mt.mt_mtype = IDNP_DATA;
4860 	mt.mt_atype = 0;
4861 	mt.mt_cookie = 0;
4862 
4863 	channel = (int)dst_netaddr.net.chan;
4864 
4865 	msglen = msgdsize(mp);
4866 	PR_DATA("%s:%d: (netaddr 0x%x) msgsize=%ld, msgdsize=%d\n",
4867 	    proc, dst_domid, dst_netaddr.netaddr, msgsize(mp), msglen);
4868 
4869 	ASSERT(wq->q_ptr);
4870 
4871 	stp = (struct idnstr *)wq->q_ptr;
4872 	sip = stp->ss_sip;
4873 	ASSERT(sip);
4874 
4875 	if (msglen < 0) {
4876 		/*
4877 		 * No data to send.  That was easy!
4878 		 */
4879 		PR_DATA("%s:%d: BAD msg length (%d) (netaddr 0x%x)\n",
4880 		    proc, dst_domid, msglen, dst_netaddr.netaddr);
4881 		return (IDNXMIT_DROP);
4882 	}
4883 
4884 	ASSERT(RW_READ_HELD(&stp->ss_rwlock));
4885 
4886 	if (dst_domid == IDN_NIL_DOMID) {
4887 		cmn_err(CE_WARN,
4888 		    "IDN: 213: no destination specified "
4889 		    "(d=%d, c=%d, n=0x%x)",
4890 		    dst_domid, dst_netaddr.net.chan,
4891 		    dst_netaddr.net.netid);
4892 		IDN_KSTAT_INC(sip, si_nolink);
4893 		IDN_KSTAT_INC(sip, si_macxmt_errors);
4894 		rv = IDNXMIT_DROP;
4895 		goto nocando;
4896 	}
4897 
4898 	ehp = (struct ether_header *)mp->b_rptr;
4899 	PR_DATA("%s:%d: destination channel = %d\n", proc, dst_domid, channel);
4900 
4901 #ifdef DEBUG
4902 	{
4903 		uchar_t	echn;
4904 
4905 		echn = (uchar_t)
4906 		    ehp->ether_shost.ether_addr_octet[IDNETHER_CHANNEL];
4907 		ASSERT((uchar_t)channel == echn);
4908 	}
4909 #endif /* DEBUG */
4910 	ASSERT(msglen <= IDN_DATA_SIZE);
4911 
4912 	dp = &idn_domain[dst_domid];
4913 	/*
4914 	 * Get reader lock.  We hold for the duration
4915 	 * of the transfer so that our state doesn't
4916 	 * change during this activity.  Note that since
4917 	 * we grab the reader lock, we can still permit
4918 	 * simultaneous tranfers from different threads
4919 	 * to the same domain.
4920 	 * Before we waste a bunch of time gathering locks, etc.
4921 	 * do a an unprotected check to make sure things are
4922 	 * semi-copesetic.  If these values are in flux,
4923 	 * that's okay.
4924 	 */
4925 	if ((dp->dstate != IDNDS_CONNECTED) || (idn.state != IDNGS_ONLINE)) {
4926 		IDN_KSTAT_INC(sip, si_linkdown);
4927 		if (idn.state != IDNGS_ONLINE) {
4928 			rv = IDNXMIT_REQUEUE;
4929 		} else {
4930 			IDN_KSTAT_INC(sip, si_macxmt_errors);
4931 			rv = IDNXMIT_DROP;
4932 		}
4933 		goto nocando;
4934 	}
4935 
4936 	if (idn.chan_servers[channel].ch_send.c_checkin) {
4937 		/*
4938 		 * Gotta bail, somethin' s'up.
4939 		 */
4940 		rv = IDNXMIT_REQUEUE;
4941 		goto nocando;
4942 	}
4943 
4944 	csp = &idn.chan_servers[channel];
4945 	IDN_CHAN_LOCK_SEND(csp);
4946 
4947 	if (dst_netaddr.net.netid == IDN_BROADCAST_ALLNETID) {
4948 		/*
4949 		 * We're doing a broadcast.  Need to set
4950 		 * up IDN netaddr's one at a time.
4951 		 * We set the ethernet destination to the same
4952 		 * instance as the sending address.  The instance
4953 		 * numbers effectively represent subnets.
4954 		 */
4955 		dst_netaddr.net.netid = dp->dnetid;
4956 
4957 		(void) idndl_domain_etheraddr(dst_domid, channel,
4958 		    &ehp->ether_dhost);
4959 
4960 		if (dst_domid == idn.localid) {
4961 			mblk_t	*nmp;
4962 			/*
4963 			 * If this is a broadcast and going to
4964 			 * the local domain, then we need to make
4965 			 * a private copy of the message since
4966 			 * the current one will be reused when
4967 			 * transmitting to other domains.
4968 			 */
4969 			PR_DATA("%s:%d: dup broadcast msg for local domain\n",
4970 			    proc, dst_domid);
4971 			if ((nmp = copymsg(mp)) == NULL) {
4972 				/*
4973 				 * Couldn't get a duplicate copy.
4974 				 */
4975 				IDN_CHAN_UNLOCK_SEND(csp);
4976 				csp = NULL;
4977 				IDN_KSTAT_INC(sip, si_allocbfail);
4978 				IDN_KSTAT_INC(sip, si_noxmtbuf);
4979 				rv = IDNXMIT_DROP;
4980 				goto nocando;
4981 			}
4982 			mp = nmp;
4983 		}
4984 	}
4985 
4986 	if (dp->dnetid != dst_netaddr.net.netid) {
4987 		PR_DATA("%s:%d: dest netid (0x%x) != expected (0x%x)\n",
4988 		    proc, dst_domid, (uint_t)dst_netaddr.net.netid,
4989 		    (uint_t)dp->dnetid);
4990 		IDN_CHAN_UNLOCK_SEND(csp);
4991 		csp = NULL;
4992 		IDN_KSTAT_INC(sip, si_nolink);
4993 		IDN_KSTAT_INC(sip, si_macxmt_errors);
4994 		rv = IDNXMIT_DROP;
4995 		goto nocando;
4996 	}
4997 
4998 	if (dst_domid == idn.localid) {
4999 		int	lbrv;
5000 		/*
5001 		 * Sending to our local domain! Loopback.
5002 		 * Note that idn_send_data_loop returning 0
5003 		 * does not mean the message can now be freed.
5004 		 * We need to return (-1) so that caller doesn't
5005 		 * try to free mblk.
5006 		 */
5007 		IDN_CHAN_UNLOCK_SEND(csp);
5008 		rw_exit(&stp->ss_rwlock);
5009 		lbrv = idn_send_data_loopback(dst_netaddr, wq, mp);
5010 		rw_enter(&stp->ss_rwlock, RW_READER);
5011 		if (lbrv == 0) {
5012 			return (IDNXMIT_LOOP);
5013 		} else {
5014 			IDN_KSTAT_INC(sip, si_macxmt_errors);
5015 			return (IDNXMIT_DROP);
5016 		}
5017 	}
5018 
5019 	if (dp->dstate != IDNDS_CONNECTED) {
5020 		/*
5021 		 * Can't send data unless a link has already been
5022 		 * established with the target domain.  Normally,
5023 		 * a user cannot set the remote netaddr unless a
5024 		 * link has already been established, however it
5025 		 * is possible the connection may have become
5026 		 * disconnected since that time.
5027 		 */
5028 		IDN_CHAN_UNLOCK_SEND(csp);
5029 		csp = NULL;
5030 		IDN_KSTAT_INC(sip, si_linkdown);
5031 		IDN_KSTAT_INC(sip, si_macxmt_errors);
5032 		rv = IDNXMIT_DROP;
5033 		goto nocando;
5034 	}
5035 
5036 	/*
5037 	 * Need to make sure the channel is active and that the
5038 	 * domain to which we're sending is allowed to receive stuff.
5039 	 */
5040 	if (!IDN_CHANNEL_IS_SEND_ACTIVE(csp)) {
5041 		int	not_active;
5042 		/*
5043 		 * See if we can activate channel.
5044 		 */
5045 		IDN_CHAN_UNLOCK_SEND(csp);
5046 		not_active = idn_activate_channel(CHANSET(channel),
5047 		    IDNCHAN_OPEN);
5048 		if (!not_active) {
5049 			/*
5050 			 * Only grab the lock for a recheck if we were
5051 			 * able to activate the channel.
5052 			 */
5053 			IDN_CHAN_LOCK_SEND(csp);
5054 		}
5055 		/*
5056 		 * Verify channel still active now that we have the lock.
5057 		 */
5058 		if (not_active || !IDN_CHANNEL_IS_SEND_ACTIVE(csp)) {
5059 			if (!not_active) {
5060 				/*
5061 				 * Only need to drop the lock if it was
5062 				 * acquired while we thought we had
5063 				 * activated the channel.
5064 				 */
5065 				IDN_CHAN_UNLOCK_SEND(csp);
5066 			}
5067 			ASSERT(!IDN_CHAN_SEND_IS_LOCKED(csp));
5068 			/*
5069 			 * Damn!   Must have went inactive during the window
5070 			 * before we regrabbed the send lock.  Oh well, can't
5071 			 * spend all day doing this, bail out.  Set csp to
5072 			 * NULL to prevent inprogress update at bottom.
5073 			 */
5074 			csp = NULL;
5075 			/*
5076 			 * Channel is not active, should not be used.
5077 			 */
5078 			PR_DATA("%s:%d: dest channel %d NOT ACTIVE\n",
5079 			    proc, dst_domid, channel);
5080 			IDN_KSTAT_INC(sip, si_linkdown);
5081 			rv = IDNXMIT_REQUEUE;
5082 			goto nocando;
5083 		}
5084 		ASSERT(IDN_CHAN_SEND_IS_LOCKED(csp));
5085 	}
5086 	/*
5087 	 * If we made it here then the channel is active
5088 	 * Make sure the target domain is registered to receive stuff,
5089 	 * i.e. we're still linked.
5090 	 */
5091 	if (!IDN_CHAN_DOMAIN_IS_REGISTERED(csp, dst_domid)) {
5092 		/*
5093 		 * If domain is not even registered with this channel
5094 		 * then we have no business being here.  Doesn't matter
5095 		 * whether it's active or not.
5096 		 */
5097 		PR_DATA("%s:%d: domain not registered with channel %d\n",
5098 		    proc, dst_domid, channel);
5099 		/*
5100 		 * Set csp to NULL to prevent in-progress update below.
5101 		 */
5102 		IDN_CHAN_UNLOCK_SEND(csp);
5103 		csp = NULL;
5104 		IDN_KSTAT_INC(sip, si_linkdown);
5105 		IDN_KSTAT_INC(sip, si_macxmt_errors);
5106 		rv = IDNXMIT_DROP;
5107 		goto nocando;
5108 	}
5109 
5110 	IDN_CHAN_SEND_INPROGRESS(csp);
5111 	IDN_CHAN_UNLOCK_SEND(csp);
5112 
5113 	/*
5114 	 * Find a target cpu to send interrupt to if
5115 	 * it becomes necessary (i.e. remote channel
5116 	 * server is idle).
5117 	 */
5118 	cpuindex = dp->dcpuindex;
5119 
5120 	/*
5121 	 * dcpuindex is atomically incremented, but other than
5122 	 * that is not well protected and that's okay.  The
5123 	 * intention is to simply spread around the interrupts
5124 	 * at the destination domain, however we don't have to
5125 	 * anal about it.  If we hit the same cpu multiple times
5126 	 * in a row that's okay, it will only be for a very short
5127 	 * period anyway before the cpuindex is incremented
5128 	 * to the next cpu.
5129 	 */
5130 	if (cpuindex < NCPU) {
5131 		ATOMIC_INC(dp->dcpuindex);
5132 	}
5133 	if (dp->dcpuindex >= NCPU)
5134 		dp->dcpuindex = 0;
5135 
5136 	IDN_ASSIGN_DCPU(dp, cpuindex);
5137 
5138 #ifdef XXX_DLPI_UNFRIENDLY
5139 	{
5140 		ushort_t	dstport = (ushort_t)dp->dcpu;
5141 
5142 		/*
5143 		 * XXX
5144 		 * This is not DLPI friendly, but we need some way
5145 		 * of distributing our XDC interrupts to the cpus
5146 		 * on the remote domain in a relatively random fashion
5147 		 * while trying to remain constant for an individual
5148 		 * network connection.  Don't want the target network
5149 		 * appl pinging around cpus thrashing the caches.
5150 		 * So, we'll pick target cpus based on the destination
5151 		 * TCP/IP port (socket).  The (simple) alternative to
5152 		 * this is to simply send all messages destined for
5153 		 * particular domain to the same cpu (dcpu), but
5154 		 * will lower our bandwidth and introduce a lot of
5155 		 * contention on that target cpu.
5156 		 */
5157 		if (ehp->ether_type == ETHERTYPE_IP) {
5158 			ipha_t	*ipha;
5159 			uchar_t	*dstporta;
5160 			int	hdr_length;
5161 			mblk_t	*nmp = mp;
5162 			uchar_t	*rptr = mp->b_rptr +
5163 			    sizeof (struct ether_header);
5164 			if (nmp->b_wptr <= rptr) {
5165 				/*
5166 				 * Only the ethernet header was contained
5167 				 * in the first block.  Check for the
5168 				 * next packet.
5169 				 */
5170 				if ((nmp = mp->b_cont) != NULL)
5171 					rptr = nmp->b_rptr;
5172 			}
5173 			/*
5174 			 * If we still haven't found the IP header packet
5175 			 * then don't bother.  Can't search forever.
5176 			 */
5177 			if (nmp &&
5178 			    ((nmp->b_wptr - rptr) >= IP_SIMPLE_HDR_LENGTH)) {
5179 				ipha = (ipha_t *)ALIGN32(rptr);
5180 
5181 				ASSERT(DB_TYPE(mp) == M_DATA);
5182 				hdr_length = IPH_HDR_LENGTH(ipha);
5183 
5184 				switch (ipha->ipha_protocol) {
5185 				case IPPROTO_UDP:
5186 				case IPPROTO_TCP:
5187 					/*
5188 					 * TCP/UDP Protocol Header (1st word)
5189 					 * 0	    15,16	31
5190 					 * -----------------------
5191 					 * | src port | dst port |
5192 					 * -----------------------
5193 					 */
5194 					dstporta = (uchar_t *)ipha + hdr_length;
5195 					netports = *(uint_t *)dstporta;
5196 					dstporta += 2;
5197 					dstport  = *(ushort_t *)dstporta;
5198 					break;
5199 				default:
5200 					break;
5201 				}
5202 			}
5203 
5204 		}
5205 		IDN_ASSIGN_DCPU(dp, dstport);
5206 
5207 		PR_DATA("%s:%d: (dstport %d) assigned %d\n",
5208 		    proc, dst_domid, (int)dstport, dp->dcpu);
5209 	}
5210 #endif /* XXX_DLPI_UNFRIENDLY */
5211 
5212 	data_rptr = mp->b_rptr;
5213 
5214 	ASSERT(dp->dcpu != IDN_NIL_DCPU);
5215 
5216 	ASSERT(idn_domain[dst_domid].dmbox.m_send);
5217 
5218 	retry_reclaim = 1;
5219 retry:
5220 	if ((dp->dio >= IDN_RECLAIM_MIN) || dp->diowanted) {
5221 		int	reclaim_req;
5222 		/*
5223 		 * Reclaim however many outstanding buffers
5224 		 * there are up to IDN_RECLAIM_MAX if it's set.
5225 		 */
5226 		reclaim_req = dp->diowanted ? -1 : IDN_RECLAIM_MAX ?
5227 		    MIN(dp->dio, IDN_RECLAIM_MAX) : dp->dio;
5228 		(void) idn_reclaim_mboxdata(dst_domid, channel,
5229 		    reclaim_req);
5230 	}
5231 
5232 	if (dp->dio >= IDN_WINDOW_EMAX) {
5233 
5234 		if (lock_try(&dp->diocheck)) {
5235 			IDN_MSGTIMER_START(dst_domid, IDNP_DATA, 0,
5236 			    idn_msg_waittime[IDNP_DATA],
5237 			    &mt.mt_cookie);
5238 			/*
5239 			 * We have exceeded the minimum window for
5240 			 * outstanding I/O buffers to this domain.
5241 			 * Need to start the MSG timer to check for
5242 			 * possible response from remote domain.
5243 			 * The remote domain may be hung.  Send a
5244 			 * wakeup!  Specify all channels for given
5245 			 * domain since we don't know precisely which
5246 			 * is backed up (dio is global).
5247 			 */
5248 			IDNXDC(dst_domid, &mt,
5249 			    (uint_t)dst_netaddr.net.chan, 0, 0, 0);
5250 		}
5251 
5252 		/*
5253 		 * Yikes!  We have exceeded the maximum window
5254 		 * which means no more packets going to remote
5255 		 * domain until he frees some up.
5256 		 */
5257 		IDN_KSTAT_INC(sip, si_txmax);
5258 		IDN_KSTAT_INC(sip, si_macxmt_errors);
5259 		rv = IDNXMIT_DROP;
5260 		goto nocando;
5261 	}
5262 
5263 	/*
5264 	 * Allocate a SMR I/O buffer and send it.
5265 	 */
5266 	if (msglen == 0) {
5267 		/*
5268 		 * A zero length messages is effectively a signal
5269 		 * to just send an interrupt to the remote domain.
5270 		 */
5271 		IDN_MSGTIMER_START(dst_domid, IDNP_DATA, 0,
5272 		    idn_msg_waittime[IDNP_DATA],
5273 		    &mt.mt_cookie);
5274 		IDNXDC(dst_domid, &mt,
5275 		    (uint_t)dst_netaddr.net.chan, 0, 0, 0);
5276 	}
5277 	for (; (msglen > 0) && mp; msglen -= xfersize) {
5278 		int		xrv;
5279 		smr_offset_t	bufoffset;
5280 #ifdef DEBUG
5281 		int		n_xfersize;
5282 #endif /* DEBUG */
5283 
5284 		ASSERT(msglen <= IDN_DATA_SIZE);
5285 		xfersize = msglen;
5286 
5287 		serrno = smr_buf_alloc(dst_domid, xfersize, &iobufp);
5288 		if (serrno) {
5289 			PR_DATA("%s:%d: failed to alloc SMR I/O buffer "
5290 			    "(serrno = %d)\n",
5291 			    proc, dst_domid, serrno);
5292 			/*
5293 			 * Failure is either due to a timeout waiting
5294 			 * for the master to give us a slab, OR the
5295 			 * local domain exhausted its slab quota!
5296 			 * In either case we'll have to bail from
5297 			 * here and let higher layers decide what
5298 			 * to do.
5299 			 * We also could have had locking problems.
5300 			 * A negative serrno indicates we lost the lock
5301 			 * on dst_domid, so no need in dropping lock.
5302 			 */
5303 
5304 			if (lock_try(&dp->diowanted) && retry_reclaim) {
5305 				/*
5306 				 * We were the first to acquire the
5307 				 * lock indicating that it wasn't
5308 				 * set on entry to idn_send_data.
5309 				 * So, let's go back and see if we
5310 				 * can't reclaim some buffers and
5311 				 * try again.
5312 				 * It's very likely diowanted will be
5313 				 * enough to prevent us from looping
5314 				 * on retrying here, however to protect
5315 				 * against the small window where a
5316 				 * race condition might exist, we use
5317 				 * the retry_reclaim flag so that we
5318 				 * don't retry more than once.
5319 				 */
5320 				retry_reclaim = 0;
5321 				goto retry;
5322 			}
5323 
5324 			rv = (serrno > 0) ? serrno : -serrno;
5325 			IDN_KSTAT_INC(sip, si_notbufs);
5326 			IDN_KSTAT_INC(sip, si_noxmtbuf);	/* MIB II */
5327 			switch (rv) {
5328 			case ENOMEM:
5329 			case EBUSY:
5330 			case ENOLCK:
5331 			case ETIMEDOUT:
5332 			case EDQUOT:
5333 				/*
5334 				 * These are all transient conditions
5335 				 * which should be recoverable over
5336 				 * time.
5337 				 */
5338 				rv = IDNXMIT_REQUEUE;
5339 				break;
5340 
5341 			default:
5342 				rv = IDNXMIT_DROP;
5343 				break;
5344 			}
5345 			goto nocando;
5346 		}
5347 
5348 		lock_clear(&dp->diowanted);
5349 
5350 		hdrp = IDN_BUF2HDR(iobufp);
5351 		bufoffset = (smr_offset_t)IDN_ALIGNPTR(sizeof (smr_pkthdr_t),
5352 		    data_rptr);
5353 		/*
5354 		 * If the alignment of bufoffset took us pass the
5355 		 * length of a smr_pkthdr_t then we need to possibly
5356 		 * lower xfersize since it was calulated based on
5357 		 * a perfect alignment.  However, if we're in DLPI
5358 		 * mode then shouldn't be necessary since the length
5359 		 * of the incoming packet (mblk) should have already
5360 		 * taken into consideration this possible adjustment.
5361 		 */
5362 #ifdef DEBUG
5363 		if (bufoffset != sizeof (smr_pkthdr_t))
5364 			PR_DATA("%s:%d: offset ALIGNMENT (%lu -> %u) "
5365 			    "(data_rptr = %p)\n",
5366 			    proc, dst_domid, sizeof (smr_pkthdr_t),
5367 			    bufoffset, (void *)data_rptr);
5368 
5369 		n_xfersize = MIN(xfersize, (IDN_SMR_BUFSIZE - bufoffset));
5370 		if (xfersize != n_xfersize) {
5371 			PR_DATA("%s:%d: xfersize ADJUST (%d -> %d)\n",
5372 			    proc, dst_domid, xfersize, n_xfersize);
5373 			cmn_err(CE_WARN, "%s: ERROR (xfersize = %d, > "
5374 			    "bufsize(%d)-bufoffset(%d) = %d)",
5375 			    proc, xfersize, IDN_SMR_BUFSIZE,
5376 			    bufoffset,
5377 			    IDN_SMR_BUFSIZE - bufoffset);
5378 		}
5379 #endif /* DEBUG */
5380 		xfersize = MIN(xfersize, (int)(IDN_SMR_BUFSIZE - bufoffset));
5381 
5382 		iodatap = IDN_BUF2DATA(iobufp, bufoffset);
5383 		mp = idn_fill_buffer(iodatap, xfersize, mp, &data_rptr);
5384 
5385 		hdrp->b_netaddr  = dst_netaddr.netaddr;
5386 		hdrp->b_netports = netports;
5387 		hdrp->b_offset   = bufoffset;
5388 		hdrp->b_length   = xfersize;
5389 		hdrp->b_next	 = IDN_NIL_SMROFFSET;
5390 		hdrp->b_rawio	 = 0;
5391 		hdrp->b_cksum    = IDN_CKSUM_PKT(hdrp);
5392 
5393 		xrv = idn_send_mboxdata(dst_domid, sip, channel, iobufp);
5394 		if (xrv) {
5395 			/*
5396 			 * Reclaim packet.
5397 			 * Return error on this packet so it can be retried
5398 			 * (putbq).  Note that it should be safe to assume
5399 			 * that this for-loop is only executed once when in
5400 			 * DLPI mode and so no need to worry about fractured
5401 			 * mblk packet.
5402 			 */
5403 			PR_DATA("%s:%d: DATA XFER to chan %d FAILED "
5404 			    "(ret=%d)\n",
5405 			    proc, dst_domid, channel, xrv);
5406 			(void) smr_buf_free(dst_domid, iobufp, xfersize);
5407 
5408 			PR_DATA("%s:%d: (line %d) dec(dio) -> %d\n",
5409 			    proc, dst_domid, __LINE__, dp->dio);
5410 
5411 			rv = IDNXMIT_DROP;
5412 			IDN_KSTAT_INC(sip, si_macxmt_errors);
5413 			goto nocando;
5414 		} else {
5415 			pktcnt++;
5416 			/*
5417 			 * Packet will get freed on a subsequent send
5418 			 * when we reclaim buffers that the receivers
5419 			 * has finished consuming.
5420 			 */
5421 		}
5422 	}
5423 
5424 #ifdef DEBUG
5425 	if (pktcnt > 1)
5426 		cmn_err(CE_WARN,
5427 		    "%s: ERROR: sent multi-pkts (%d), len = %ld",
5428 		    proc, pktcnt, orig_msglen);
5429 #endif /* DEBUG */
5430 
5431 	PR_DATA("%s:%d: SENT %d packets (%d @ 0x%x)\n",
5432 	    proc, dst_domid, pktcnt, dst_netaddr.net.chan,
5433 	    dst_netaddr.net.netid);
5434 
5435 	IDN_CHAN_LOCK_SEND(csp);
5436 	IDN_CHAN_SEND_DONE(csp);
5437 	IDN_CHAN_UNLOCK_SEND(csp);
5438 
5439 	return (IDNXMIT_OKAY);
5440 
5441 nocando:
5442 
5443 	if (csp) {
5444 		IDN_CHAN_LOCK_SEND(csp);
5445 		IDN_CHAN_SEND_DONE(csp);
5446 		IDN_CHAN_UNLOCK_SEND(csp);
5447 	}
5448 
5449 	if (rv == IDNXMIT_REQUEUE) {
5450 		/*
5451 		 * Better kick off monitor to check when
5452 		 * it's ready to reenable the queues for
5453 		 * this channel.
5454 		 */
5455 		idn_xmit_monitor_kickoff(channel);
5456 	}
5457 
5458 	return (rv);
5459 }
5460 
5461 /*
5462  * Function to support local loopback testing of IDN driver.
5463  * Primarily geared towards measuring stream-head and IDN driver
5464  * overhead with respect to data messages.  Setting idn_strhead_only
5465  * allows routine to focus on stream-head overhead by simply putting
5466  * the message straight to the 'next' queue of the destination
5467  * read-queue.  Current implementation puts the message directly to
5468  * the read-queue thus sending the message right back to the IDN driver
5469  * as though the data came in off the wire.  No need to worry about
5470  * any IDN layers attempting to ack data as that's normally handled
5471  * by idnh_recv_data.
5472  *
5473  * dst_netaddr = destination port-n-addr on local domain.
5474  * wq          = write queue from whence message came.
5475  * mp          = the (data-only) message.
5476  *
5477  * Returns 0		Indicates data handled.
5478  *	   errno	EAGAIN indicates data can be retried.
5479  *			Other errno's indicate failure to handle.
5480  */
5481 static int
5482 idn_send_data_loopback(idn_netaddr_t dst_netaddr, queue_t *wq, mblk_t *mp)
5483 {
5484 	register struct idnstr	*stp;
5485 	struct idn	*sip;
5486 	int		rv = 0;
5487 	procname_t	proc = "idn_send_data_loopback";
5488 
5489 	if (dst_netaddr.net.netid != idn_domain[idn.localid].dnetid) {
5490 		PR_DATA("%s: dst_netaddr.net.netid 0x%x != local 0x%x\n",
5491 		    proc, dst_netaddr.net.netid,
5492 		    idn_domain[idn.localid].dnetid);
5493 		rv = EADDRNOTAVAIL;
5494 		goto done;
5495 	}
5496 	stp = (struct idnstr *)wq->q_ptr;
5497 	if (!stp || !stp->ss_rq) {
5498 		rv = EDESTADDRREQ;
5499 		goto done;
5500 	}
5501 	sip = stp->ss_sip;
5502 
5503 	idndl_read(sip, mp);
5504 	rv = 0;
5505 
5506 done:
5507 	return (rv);
5508 }
5509 
5510 /*
5511  * Fill bufp with as much data as possible from the message pointed
5512  * to by mp up to size bytes.
5513  * Save our current read pointer in the variable parameter (data_rptrp)
5514  * so we know where to start on the next go around.  Don't want to
5515  * bump the actual b_rptr in the mblk because the mblk may need to
5516  * be reused, e.g. broadcast.
5517  * Return the mblk pointer to the position we had to stop.
5518  */
5519 static mblk_t *
5520 idn_fill_buffer(caddr_t bufp, int size, mblk_t *mp, uchar_t **data_rptrp)
5521 {
5522 	int	copysize;
5523 
5524 	ASSERT(bufp && size);
5525 
5526 	if (mp == NULL)
5527 		return (NULL);
5528 
5529 	while ((size > 0) && mp) {
5530 
5531 		copysize = MIN(mp->b_wptr - (*data_rptrp), size);
5532 
5533 		if (copysize > 0) {
5534 			/*
5535 			 * If there's data to copy, do it.
5536 			 */
5537 			bcopy((*data_rptrp), bufp, copysize);
5538 			(*data_rptrp) += copysize;
5539 			bufp += copysize;
5540 			size -= copysize;
5541 		}
5542 		if (mp->b_wptr <= (*data_rptrp)) {
5543 			/*
5544 			 * If we emptied the mblk, then
5545 			 * move on to the next one.
5546 			 */
5547 			for (mp = mp->b_cont;
5548 			    mp && (mp->b_datap->db_type != M_DATA);
5549 			    mp = mp->b_cont)
5550 				;
5551 			if (mp)
5552 				*data_rptrp = mp->b_rptr;
5553 		}
5554 	}
5555 	return (mp);
5556 }
5557 
5558 /*
5559  * Messages received here do NOT arrive on a stream, but are
5560  * instead handled via the idn_protocol_servers.  This routine
5561  * is effectively the job processor for the protocol servers.
5562  */
5563 static void
5564 idn_recv_proto(idn_protomsg_t *hp)
5565 {
5566 	int		domid, cpuid;
5567 	int		sync_lock = 0;
5568 	idn_domain_t	*dp;
5569 	register uint_t	mtype;
5570 	register uint_t	msgtype, acktype;
5571 	idn_msgtype_t	mt;
5572 	ushort_t	dcookie, tcookie;
5573 	procname_t	proc = "idn_recv_proto";
5574 
5575 
5576 	if (idn.state == IDNGS_IGNORE) {
5577 		/*
5578 		 * Fault injection to simulate non-responsive domain.
5579 		 */
5580 		return;
5581 	}
5582 
5583 	domid   = hp->m_domid;
5584 	cpuid   = hp->m_cpuid;
5585 	msgtype = hp->m_msgtype;
5586 	acktype = hp->m_acktype;
5587 	dcookie = IDN_DCOOKIE(hp->m_cookie);
5588 	tcookie = IDN_TCOOKIE(hp->m_cookie);
5589 	/*
5590 	 * msgtype =	Is the type of message we received,
5591 	 *		e.g. nego, ack, nego+ack, etc.
5592 	 *
5593 	 * acktype =	If we received a pure ack or nack
5594 	 *		then this variable is set to the
5595 	 *		type of message that was ack/nack'd.
5596 	 */
5597 	if ((mtype = msgtype & IDNP_MSGTYPE_MASK) == 0) {
5598 		/*
5599 		 * Received a pure ack/nack.
5600 		 */
5601 		mtype = acktype & IDNP_MSGTYPE_MASK;
5602 	}
5603 
5604 	if (!VALID_MSGTYPE(mtype)) {
5605 		PR_PROTO("%s:%d: ERROR: invalid message type (0x%x)\n",
5606 		    proc, domid, mtype);
5607 		return;
5608 	}
5609 	if (!VALID_CPUID(cpuid)) {
5610 		PR_PROTO("%s:%d: ERROR: invalid cpuid (%d)\n",
5611 		    proc, domid, cpuid);
5612 		return;
5613 	}
5614 
5615 	/*
5616 	 * No pure data packets should reach this level.
5617 	 * Data+ack messages will reach here, but only
5618 	 * for the purpose of stopping the timer which
5619 	 * happens by default when this routine is called.
5620 	 */
5621 	ASSERT(msgtype != IDNP_DATA);
5622 
5623 	/*
5624 	 * We should never receive a request from ourself,
5625 	 * except for commands in the case of broadcasts!
5626 	 */
5627 	if ((domid == idn.localid) && (mtype != IDNP_CMD)) {
5628 		char	str[15];
5629 
5630 		inum2str(hp->m_msgtype, str);
5631 
5632 		cmn_err(CE_WARN,
5633 		    "IDN: 214: received message (%s[0x%x]) from self "
5634 		    "(domid %d)",
5635 		    str, hp->m_msgtype, domid);
5636 		return;
5637 	}
5638 
5639 	IDN_SYNC_LOCK();
5640 	/*
5641 	 * Set a flag indicating whether we really need
5642 	 * SYNC-LOCK.  We'll drop it in a little bit if
5643 	 * we really don't need it.
5644 	 */
5645 	switch (mtype) {
5646 	case IDNP_CON:
5647 	case IDNP_FIN:
5648 	case IDNP_NEGO:
5649 		sync_lock = 1;
5650 		break;
5651 
5652 	default:
5653 		break;
5654 	}
5655 
5656 	dp = &idn_domain[domid];
5657 	IDN_DLOCK_EXCL(domid);
5658 
5659 	/*
5660 	 * The only messages we do _not_ check the cookie are:
5661 	 *	nego
5662 	 *	nego+ack
5663 	 *	fin	 - if received cookie is 0.
5664 	 *	fin+ack	 - if received cookie is 0.
5665 	 *	ack/fin	 - if received cookie is 0.
5666 	 *	nack/fin - if received cookie is 0.
5667 	 */
5668 	if (((msgtype & IDNP_MSGTYPE_MASK) != IDNP_NEGO) &&
5669 	    ((mtype != IDNP_FIN) || (dcookie && dp->dcookie_recv))) {
5670 		if (dp->dcookie_recv != dcookie) {
5671 			dp->dcookie_errcnt++;
5672 			if (dp->dcookie_err == 0) {
5673 				/*
5674 				 * Set cookie error to prevent a
5675 				 * possible flood of bogus cookies
5676 				 * and thus error messages.
5677 				 */
5678 				dp->dcookie_err = 1;
5679 				cmn_err(CE_WARN,
5680 				    "IDN: 215: invalid cookie (0x%x) "
5681 				    "for message (0x%x) from domain %d",
5682 				    dcookie, hp->m_msgtype, domid);
5683 
5684 				PR_PROTO("%s:%d: received cookie (0x%x), "
5685 				    "expected (0x%x) [errcnt = %d]\n",
5686 				    proc, domid, dcookie,
5687 				    dp->dcookie_recv, dp->dcookie_errcnt);
5688 			}
5689 			IDN_DUNLOCK(domid);
5690 			IDN_SYNC_UNLOCK();
5691 			return;
5692 		}
5693 	}
5694 	dp->dcookie_err = 0;
5695 	IDN_GLOCK_EXCL();
5696 
5697 	idn_clear_awol(domid);
5698 
5699 	IDN_GUNLOCK();
5700 	if (!sync_lock)		/* really don't need SYNC-LOCK past here */
5701 		IDN_SYNC_UNLOCK();
5702 
5703 	/*
5704 	 * Stop any timers that may have been outstanding for
5705 	 * this domain, for this particular message type.
5706 	 * Note that CFG timers are directly managed by
5707 	 * config recv/send code.
5708 	 */
5709 	if ((mtype != IDNP_CFG) && (msgtype & IDNP_ACKNACK_MASK) && tcookie) {
5710 		IDN_MSGTIMER_STOP(domid, mtype, tcookie);
5711 	}
5712 
5713 	/*
5714 	 * Keep track of the last cpu to send us a message.
5715 	 * If the domain has not yet been assigned, we'll need
5716 	 * this cpuid in order to send back a respond.
5717 	 */
5718 	dp->dcpu_last = cpuid;
5719 
5720 	mt.mt_mtype = (ushort_t)msgtype;
5721 	mt.mt_atype = (ushort_t)acktype;
5722 	mt.mt_cookie = tcookie;
5723 
5724 	switch (mtype) {
5725 	case IDNP_NEGO:
5726 		(void) idn_recv_nego(domid, &mt, hp->m_xargs, dcookie);
5727 		break;
5728 
5729 	case IDNP_CFG:
5730 		idn_recv_config(domid, &mt, hp->m_xargs);
5731 		break;
5732 
5733 	case IDNP_CON:
5734 		(void) idn_recv_con(domid, &mt, hp->m_xargs);
5735 		break;
5736 
5737 	case IDNP_FIN:
5738 		(void) idn_recv_fin(domid, &mt, hp->m_xargs);
5739 		break;
5740 
5741 	case IDNP_CMD:
5742 		idn_recv_cmd(domid, &mt, hp->m_xargs);
5743 		break;
5744 
5745 	case IDNP_DATA:
5746 		ASSERT(msgtype & IDNP_ACKNACK_MASK);
5747 		/*
5748 		 * When doing the fast track we simply process
5749 		 * possible nack error conditions.  The actual
5750 		 * processing of the SMR data buffer is taken
5751 		 * care of in idnh_recv_dataack.  When NOT doing
5752 		 * the fast track, we do all the processing here
5753 		 * in the protocol server.
5754 		 */
5755 		(void) idn_recv_data(domid, &mt, hp->m_xargs);
5756 		break;
5757 
5758 	default:
5759 		/*
5760 		 * Should be receiving 0 inum and 0 acknack.
5761 		 */
5762 #ifdef DEBUG
5763 		cmn_err(CE_PANIC,
5764 #else /* DEBUG */
5765 		    cmn_err(CE_WARN,
5766 #endif /* DEBUG */
5767 			/* CSTYLED */
5768 			"IDN: 216: (0x%x)msgtype/(0x%x)acktype rcvd from "
5769 			/* CSTYLED */
5770 			"domain %d", msgtype, acktype, domid);
5771 		break;
5772 	}
5773 
5774 	IDN_DUNLOCK(domid);
5775 	/*
5776 	 * All receiving routines are responsible for dropping drwlock.
5777 	 */
5778 
5779 	if (sync_lock)
5780 		IDN_SYNC_UNLOCK();
5781 }
5782 
5783 /*
5784  * Once the CONFIG state is hit we immediately blast out all
5785  * of our config info.  This guarantees that the CONFIG state
5786  * effectively signifies that the sender has sent _all_ of
5787  * their config info.
5788  */
5789 static void
5790 idn_send_config(int domid, int phase)
5791 {
5792 	idn_domain_t	*dp;
5793 	int		rv;
5794 	clock_t		cfg_waittime = idn_msg_waittime[IDNP_CFG];
5795 	procname_t	proc = "idn_send_config";
5796 
5797 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
5798 
5799 	dp = &idn_domain[domid];
5800 
5801 	ASSERT(dp->dstate == IDNDS_CONFIG);
5802 
5803 	if (phase == 1) {
5804 		/*
5805 		 * Reset stuff in dtmp to 0:
5806 		 *	dcfgphase
5807 		 *	dcksum
5808 		 *	dncfgitems
5809 		 *	dmaxnets
5810 		 *	dmboxpernet
5811 		 */
5812 		dp->dtmp = 0;
5813 	}
5814 
5815 	if (dp->dcfgsnddone) {
5816 		if (!dp->dcfgrcvdone) {
5817 			IDN_MSGTIMER_START(domid, IDNP_CFG, 0,
5818 			    cfg_waittime, NULL);
5819 		}
5820 		return;
5821 	}
5822 
5823 	IDN_DLOCK_SHARED(idn.localid);
5824 
5825 	PR_PROTO("%s:%d: sending %s config (phase %d)\n",
5826 	    proc, domid,
5827 	    idn_domain[idn.localid].dvote.v.master ? "MASTER" : "SLAVE",
5828 	    phase);
5829 
5830 	if (idn_domain[idn.localid].dvote.v.master)
5831 		rv = idn_send_master_config(domid, phase);
5832 	else
5833 		rv = idn_send_slave_config(domid, phase);
5834 
5835 	IDN_DUNLOCK(idn.localid);
5836 
5837 	if (rv >= 0) {
5838 
5839 		if (rv == 1) {
5840 			dp->dcfgsnddone = 1;
5841 			PR_PROTO("%s:%d: SEND config DONE\n", proc, domid);
5842 			if (!dp->dcfgrcvdone) {
5843 				IDN_MSGTIMER_START(domid, IDNP_CFG, 0,
5844 				    cfg_waittime, NULL);
5845 			}
5846 		} else {
5847 			IDN_MSGTIMER_START(domid, IDNP_CFG, 0,
5848 			    cfg_waittime, NULL);
5849 		}
5850 	}
5851 }
5852 
5853 /*
5854  * Clear out the mailbox table.
5855  * NOTE: This routine touches the SMR.
5856  */
5857 static void
5858 idn_reset_mboxtbl(idn_mboxtbl_t *mtp)
5859 {
5860 	int		qi;
5861 	idn_mboxmsg_t	*mp = &mtp->mt_queue[0];
5862 
5863 	qi = 0;
5864 	do {
5865 		mp[qi].ms_bframe = 0;
5866 		mp[qi].ms_owner = 0;
5867 		mp[qi].ms_flag = 0;
5868 		IDN_MMBOXINDEX_INC(qi);
5869 	} while (qi);
5870 }
5871 
5872 static int
5873 idn_get_mbox_config(int domid, int *mindex, smr_offset_t *mtable,
5874     smr_offset_t *mdomain)
5875 {
5876 	idn_domain_t	*dp, *ldp;
5877 
5878 	dp = &idn_domain[domid];
5879 	ldp = &idn_domain[idn.localid];
5880 
5881 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
5882 	ASSERT(IDN_DLOCK_IS_SHARED(idn.localid));
5883 	ASSERT(IDN_GET_MASTERID() != IDN_NIL_DOMID);
5884 
5885 	/*
5886 	 * Get SMR offset of receive mailbox assigned
5887 	 * to respective domain.  If I'm a slave then
5888 	 * my dmbox.m_tbl will not have been assigned yet.
5889 	 * Instead of sending the actual offset I send
5890 	 * the master his assigned index.  Since the
5891 	 * master knows what offset it will assign to
5892 	 * me he can determine his assigned (recv) mailbox
5893 	 * based on the offset and given index.  The local
5894 	 * domain can also use this information once the
5895 	 * dmbox.m_tbl is received to properly assign the
5896 	 * correct mbox offset to the master.
5897 	 */
5898 	if (ldp->dmbox.m_tbl == NULL) {
5899 		/*
5900 		 * Local domain has not yet been assigned a
5901 		 * (recv) mailbox table.  This must be the
5902 		 * initial connection of this domain.
5903 		 */
5904 		ASSERT(dp->dvote.v.master && !ldp->dvote.v.master);
5905 		ASSERT(mindex);
5906 		*mindex = domid;
5907 	} else {
5908 		idn_mboxtbl_t	*mtp;
5909 
5910 		mtp = IDN_MBOXTBL_PTR(ldp->dmbox.m_tbl, domid);
5911 
5912 		ASSERT(mdomain);
5913 		*mdomain = IDN_ADDR2OFFSET(mtp);
5914 
5915 		if (ldp->dvote.v.master) {
5916 			/*
5917 			 * Need to calculate mailbox table to
5918 			 * assign to the given domain.  Since
5919 			 * I'm the master his mailbox is in
5920 			 * the (all-domains) mailbox table.
5921 			 */
5922 			mtp = IDN_MBOXAREA_BASE(idn.mboxarea, domid);
5923 			ASSERT(mtable);
5924 			*mtable = IDN_ADDR2OFFSET(mtp);
5925 
5926 			dp->dmbox.m_tbl = mtp;
5927 		}
5928 	}
5929 
5930 	return (0);
5931 }
5932 
5933 /*
5934  * RETURNS:
5935  *	1	Unexpected/unnecessary phase.
5936  *	0	Successfully handled, timer needed.
5937  */
5938 static int
5939 idn_send_master_config(int domid, int phase)
5940 {
5941 	idn_cfgsubtype_t	cfg_subtype;
5942 	int		rv = 0;
5943 	idn_domain_t	*dp, *ldp;
5944 	idn_msgtype_t	mt;
5945 	int		nmcadr;
5946 	uint_t		barpfn, larpfn;
5947 	uint_t		cpus_u32, cpus_l32;
5948 	uint_t		mcadr[3];
5949 	smr_offset_t	mbox_table, mbox_domain;
5950 	register int	b, p, m;
5951 	procname_t	proc = "idn_send_master_config";
5952 
5953 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
5954 	ASSERT(IDN_DLOCK_IS_SHARED(idn.localid));
5955 
5956 	dp = &idn_domain[domid];
5957 	ldp = &idn_domain[idn.localid];
5958 
5959 	ASSERT(dp->dstate == IDNDS_CONFIG);
5960 	ASSERT(dp->dvote.v.master == 0);
5961 	ASSERT(ldp->dvote.v.master == 1);
5962 
5963 	mt.mt_mtype = IDNP_CFG;
5964 	mt.mt_atype = 0;
5965 	mt.mt_cookie = 0;
5966 	m = 0;
5967 	mcadr[0] = mcadr[1] = mcadr[2] = 0;
5968 	cfg_subtype.val = 0;
5969 
5970 	switch (phase) {
5971 
5972 	case 1:
5973 		mbox_table = mbox_domain = IDN_NIL_SMROFFSET;
5974 		(void) idn_get_mbox_config(domid, NULL, &mbox_table,
5975 		    &mbox_domain);
5976 		/*
5977 		 * ----------------------------------------------------
5978 		 * Send: SLABSIZE, DATAMBOX.DOMAIN, DATAMBOX.TABLE
5979 		 * ----------------------------------------------------
5980 		 */
5981 		cfg_subtype.param.p[0] = IDN_CFGPARAM(IDNCFG_SIZE,
5982 		    IDNCFGARG_SIZE_SLAB);
5983 		cfg_subtype.param.p[1] = IDN_CFGPARAM(IDNCFG_DATAMBOX,
5984 		    IDNCFGARG_DATAMBOX_DOMAIN);
5985 		cfg_subtype.param.p[2] = IDN_CFGPARAM(IDNCFG_DATAMBOX,
5986 		    IDNCFGARG_DATAMBOX_TABLE);
5987 		cfg_subtype.info.num = 3;
5988 		cfg_subtype.info.phase = phase;
5989 		dp->dcfgphase = phase;
5990 
5991 		ASSERT(mbox_domain != IDN_NIL_SMROFFSET);
5992 		ASSERT(mbox_table != IDN_NIL_SMROFFSET);
5993 
5994 		PR_PROTO("%s:%d:%d: sending SLABSIZE (%d), "
5995 		    "DATAMBOX.DOMAIN (0x%x), DATAMBOX.TABLE (0x%x)\n",
5996 		    proc, domid, phase, IDN_SLAB_BUFCOUNT, mbox_domain,
5997 		    mbox_table);
5998 
5999 		IDNXDC(domid, &mt, cfg_subtype.val, IDN_SLAB_BUFCOUNT,
6000 		    mbox_domain, mbox_table);
6001 		break;
6002 
6003 	case 2:
6004 		barpfn = idn.smr.locpfn;
6005 		larpfn = barpfn + (uint_t)btop(MB2B(IDN_SMR_SIZE));
6006 		/*
6007 		 * ----------------------------------------------------
6008 		 * Send: NETID, BARLAR
6009 		 * ----------------------------------------------------
6010 		 */
6011 		cfg_subtype.param.p[0] = IDN_CFGPARAM(IDNCFG_NETID, 0);
6012 		cfg_subtype.param.p[1] = IDN_CFGPARAM(IDNCFG_BARLAR,
6013 		    IDNCFGARG_BARLAR_BAR);
6014 		cfg_subtype.param.p[2] = IDN_CFGPARAM(IDNCFG_BARLAR,
6015 		    IDNCFGARG_BARLAR_LAR);
6016 		cfg_subtype.info.num = 3;
6017 		cfg_subtype.info.phase = phase;
6018 		dp->dcfgphase = phase;
6019 
6020 		PR_PROTO("%s:%d:%d: sending NETID (%d), "
6021 		    "BARPFN/LARPFN (0x%x/0x%x)\n",
6022 		    proc, domid, phase, ldp->dnetid, barpfn, larpfn);
6023 
6024 		IDNXDC(domid, &mt, cfg_subtype.val,
6025 		    (uint_t)ldp->dnetid, barpfn, larpfn);
6026 		break;
6027 
6028 	case 3:
6029 		nmcadr = ldp->dhw.dh_nmcadr;
6030 		cpus_u32 = UPPER32_CPUMASK(ldp->dcpuset);
6031 		cpus_l32 = LOWER32_CPUMASK(ldp->dcpuset);
6032 		/*
6033 		 * ----------------------------------------------------
6034 		 * Send: CPUSET, NMCADR
6035 		 * ----------------------------------------------------
6036 		 */
6037 		cfg_subtype.param.p[0] = IDN_CFGPARAM(IDNCFG_CPUSET,
6038 		    IDNCFGARG_CPUSET_UPPER);
6039 		cfg_subtype.param.p[1] = IDN_CFGPARAM(IDNCFG_CPUSET,
6040 		    IDNCFGARG_CPUSET_LOWER);
6041 		cfg_subtype.param.p[2] = IDN_CFGPARAM(IDNCFG_NMCADR, 0);
6042 		cfg_subtype.info.num = 3;
6043 		cfg_subtype.info.phase = phase;
6044 		dp->dcfgphase = phase;
6045 
6046 		PR_PROTO("%s:%d:%d: sending CPUSET (0x%x.%x), NMCADR (%d)\n",
6047 		    proc, domid, phase, cpus_u32, cpus_l32, nmcadr);
6048 
6049 		IDNXDC(domid, &mt, cfg_subtype.val,
6050 		    cpus_u32, cpus_l32, nmcadr);
6051 		break;
6052 
6053 	case 4:
6054 		/*
6055 		 * ----------------------------------------------------
6056 		 * Send: BOARDSET, MTU, BUFSIZE
6057 		 * ----------------------------------------------------
6058 		 */
6059 		cfg_subtype.param.p[0] = IDN_CFGPARAM(IDNCFG_BOARDSET, 0);
6060 		cfg_subtype.param.p[1] = IDN_CFGPARAM(IDNCFG_SIZE,
6061 		    IDNCFGARG_SIZE_MTU);
6062 		cfg_subtype.param.p[2] = IDN_CFGPARAM(IDNCFG_SIZE,
6063 		    IDNCFGARG_SIZE_BUF);
6064 		cfg_subtype.info.num = 3;
6065 		cfg_subtype.info.phase = phase;
6066 		dp->dcfgphase = phase;
6067 
6068 		PR_PROTO("%s:%d:%d: sending BOARDSET (0x%x), MTU (0x%lx), "
6069 		    "BUFSIZE (0x%x)\n", proc, domid, phase,
6070 		    ldp->dhw.dh_boardset, IDN_MTU, IDN_SMR_BUFSIZE);
6071 
6072 		IDNXDC(domid, &mt, cfg_subtype.val,
6073 		    ldp->dhw.dh_boardset, IDN_MTU, IDN_SMR_BUFSIZE);
6074 		break;
6075 
6076 	case 5:
6077 		/*
6078 		 * ----------------------------------------------------
6079 		 * Send: MAXNETS, MBOXPERNET, CKSUM
6080 		 * ----------------------------------------------------
6081 		 */
6082 		cfg_subtype.param.p[0] = IDN_CFGPARAM(IDNCFG_DATASVR,
6083 		    IDNCFGARG_DATASVR_MAXNETS);
6084 		cfg_subtype.param.p[1] = IDN_CFGPARAM(IDNCFG_DATASVR,
6085 		    IDNCFGARG_DATASVR_MBXPERNET);
6086 		cfg_subtype.param.p[2] = IDN_CFGPARAM(IDNCFG_OPTIONS,
6087 		    IDNCFGARG_CHECKSUM);
6088 		cfg_subtype.info.num = 3;
6089 		cfg_subtype.info.phase = phase;
6090 		dp->dcfgphase = phase;
6091 
6092 		PR_PROTO("%s:%d:%d: sending MAXNETS (%d), "
6093 		    "MBOXPERNET (%d), CKSUM (%d)\n",
6094 		    proc, domid, phase,
6095 		    IDN_MAX_NETS, IDN_MBOX_PER_NET,
6096 		    IDN_CHECKSUM);
6097 
6098 		IDNXDC(domid, &mt, cfg_subtype.val,
6099 		    IDN_MAX_NETS, IDN_MBOX_PER_NET, IDN_CHECKSUM);
6100 		break;
6101 
6102 	case 6:
6103 		/*
6104 		 * ----------------------------------------------------
6105 		 * Send: NWRSIZE (piggyback on MCADRs)
6106 		 * ----------------------------------------------------
6107 		 */
6108 		cfg_subtype.param.p[0] = IDN_CFGPARAM(IDNCFG_SIZE,
6109 		    IDNCFGARG_SIZE_NWR);
6110 		mcadr[0] = IDN_NWR_SIZE;
6111 		m = 1;
6112 
6113 		/*FALLTHROUGH*/
6114 
6115 	default:	/* case 7 and above */
6116 		/*
6117 		 * ----------------------------------------------------
6118 		 * Send: MCADR's
6119 		 * ----------------------------------------------------
6120 		 * First need to figure how many we've already sent
6121 		 * based on what phase of CONFIG we're in.
6122 		 * ----------------------------------------------------
6123 		 */
6124 		if (phase > 6) {
6125 			p = ((phase - 7) * 3) + 2;
6126 			for (b = 0; (b < MAX_BOARDS) && (p > 0); b++)
6127 				if (ldp->dhw.dh_mcadr[b])
6128 					p--;
6129 		} else {
6130 			b = 0;
6131 		}
6132 
6133 		for (; (b < MAX_BOARDS) && (m < 3); b++) {
6134 			if (ldp->dhw.dh_mcadr[b] == 0)
6135 				continue;
6136 			mcadr[m] = ldp->dhw.dh_mcadr[b];
6137 			cfg_subtype.param.p[m] = IDN_CFGPARAM(IDNCFG_MCADR, b);
6138 			m++;
6139 		}
6140 		if (m > 0) {
6141 			if (phase == 6) {
6142 				PR_PROTO("%s:%d:%d: sending NWRSIZE (%d), "
6143 				    "MCADRs (0x%x, 0x%x)\n",
6144 				    proc, domid, phase,
6145 				    mcadr[0], mcadr[1], mcadr[2]);
6146 			} else {
6147 				PR_PROTO("%s:%d:%d: sending MCADRs "
6148 				    "(0x%x, 0x%x, 0x%x)\n",
6149 				    proc, domid, phase,
6150 				    mcadr[0], mcadr[1], mcadr[2]);
6151 			}
6152 			cfg_subtype.info.num = m;
6153 			cfg_subtype.info.phase = phase;
6154 			dp->dcfgphase = phase;
6155 
6156 			IDNXDC(domid, &mt, cfg_subtype.val,
6157 			    mcadr[0], mcadr[1], mcadr[2]);
6158 		} else {
6159 			rv = 1;
6160 		}
6161 		break;
6162 	}
6163 
6164 	return (rv);
6165 }
6166 
6167 /*
6168  * RETURNS:
6169  *	1	Unexpected/unnecessary phase.
6170  *	0	Successfully handled.
6171  */
6172 static int
6173 idn_send_slave_config(int domid, int phase)
6174 {
6175 	idn_cfgsubtype_t	cfg_subtype;
6176 	int		rv = 0;
6177 	idn_domain_t	*dp, *ldp;
6178 	smr_offset_t	mbox_domain;
6179 	idn_msgtype_t	mt;
6180 	int		mbox_index;
6181 	uint_t		cpus_u32, cpus_l32;
6182 	procname_t	proc = "idn_send_slave_config";
6183 
6184 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
6185 	ASSERT(IDN_DLOCK_IS_SHARED(idn.localid));
6186 
6187 	mt.mt_mtype = IDNP_CFG;
6188 	mt.mt_atype = 0;
6189 	dp = &idn_domain[domid];
6190 	ldp = &idn_domain[idn.localid];
6191 
6192 	ASSERT(dp->dstate == IDNDS_CONFIG);
6193 	ASSERT(ldp->dvote.v.master == 0);
6194 
6195 	switch (phase) {
6196 
6197 	case 1:
6198 		mbox_index = IDN_NIL_DOMID;
6199 		mbox_domain = IDN_NIL_SMROFFSET;
6200 		(void) idn_get_mbox_config(domid, &mbox_index, NULL,
6201 		    &mbox_domain);
6202 		/*
6203 		 * ----------------------------------------------------
6204 		 * Send: DATAMBOX.DOMAIN or DATAMBOX.INDEX,
6205 		 *	 DATASVR.MAXNETS, DATASVR.MBXPERNET
6206 		 * ----------------------------------------------------
6207 		 */
6208 		cfg_subtype.val = 0;
6209 		if (mbox_index == IDN_NIL_DOMID) {
6210 			ASSERT(mbox_domain != IDN_NIL_SMROFFSET);
6211 			cfg_subtype.param.p[0] = IDN_CFGPARAM(IDNCFG_DATAMBOX,
6212 			    IDNCFGARG_DATAMBOX_DOMAIN);
6213 		} else {
6214 			/*
6215 			 * Should only be sending Index to
6216 			 * the master and not another slave.
6217 			 */
6218 			ASSERT(dp->dvote.v.master);
6219 			ASSERT(mbox_domain == IDN_NIL_SMROFFSET);
6220 			cfg_subtype.param.p[0] = IDN_CFGPARAM(IDNCFG_DATAMBOX,
6221 			    IDNCFGARG_DATAMBOX_INDEX);
6222 		}
6223 		cfg_subtype.param.p[1] = IDN_CFGPARAM(IDNCFG_DATASVR,
6224 		    IDNCFGARG_DATASVR_MAXNETS);
6225 		cfg_subtype.param.p[2] = IDN_CFGPARAM(IDNCFG_DATASVR,
6226 		    IDNCFGARG_DATASVR_MBXPERNET);
6227 		cfg_subtype.info.num = 3;
6228 		cfg_subtype.info.phase = phase;
6229 		dp->dcfgphase = phase;
6230 
6231 		PR_PROTO("%s:%d:%d: sending DATAMBOX.%s (0x%x), "
6232 		    "MAXNETS (%d), MBXPERNET (%d)\n",
6233 		    proc, domid, phase,
6234 		    (IDN_CFGPARAM_ARG(cfg_subtype.param.p[0])
6235 		    == IDNCFGARG_DATAMBOX_INDEX) ? "INDEX" : "DOMAIN",
6236 		    (mbox_index == IDN_NIL_DOMID) ? mbox_domain : mbox_index,
6237 		    IDN_MAX_NETS, IDN_MBOX_PER_NET);
6238 
6239 		IDNXDC(domid, &mt, cfg_subtype.val,
6240 		    ((mbox_index == IDN_NIL_DOMID) ? mbox_domain : mbox_index),
6241 		    IDN_MAX_NETS, IDN_MBOX_PER_NET);
6242 		break;
6243 
6244 	case 2:
6245 		cpus_u32 = UPPER32_CPUMASK(ldp->dcpuset);
6246 		cpus_l32 = LOWER32_CPUMASK(ldp->dcpuset);
6247 		/*
6248 		 * ----------------------------------------------------
6249 		 * Send: NETID, CPUSET
6250 		 * ----------------------------------------------------
6251 		 */
6252 		cfg_subtype.val = 0;
6253 		cfg_subtype.param.p[0] = IDN_CFGPARAM(IDNCFG_NETID, 0);
6254 		cfg_subtype.param.p[1] = IDN_CFGPARAM(IDNCFG_CPUSET,
6255 		    IDNCFGARG_CPUSET_UPPER);
6256 		cfg_subtype.param.p[2] = IDN_CFGPARAM(IDNCFG_CPUSET,
6257 		    IDNCFGARG_CPUSET_LOWER);
6258 		cfg_subtype.info.num = 3;
6259 		cfg_subtype.info.phase = phase;
6260 		dp->dcfgphase = phase;
6261 
6262 		PR_PROTO("%s:%d:%d: sending NETID (%d), "
6263 		    "CPUSET (0x%x.%x)\n", proc, domid, phase,
6264 		    ldp->dnetid, cpus_u32, cpus_l32);
6265 
6266 		IDNXDC(domid, &mt, cfg_subtype.val,
6267 		    (uint_t)ldp->dnetid, cpus_u32, cpus_l32);
6268 		break;
6269 
6270 	case 3:
6271 		/*
6272 		 * ----------------------------------------------------
6273 		 * Send: BOARDSET, MTU, BUFSIZE
6274 		 * ----------------------------------------------------
6275 		 */
6276 		cfg_subtype.val = 0;
6277 		cfg_subtype.param.p[0] = IDN_CFGPARAM(IDNCFG_BOARDSET, 0);
6278 		cfg_subtype.param.p[1] = IDN_CFGPARAM(IDNCFG_SIZE,
6279 		    IDNCFGARG_SIZE_MTU);
6280 		cfg_subtype.param.p[2] = IDN_CFGPARAM(IDNCFG_SIZE,
6281 		    IDNCFGARG_SIZE_BUF);
6282 		cfg_subtype.info.num = 3;
6283 		cfg_subtype.info.phase = phase;
6284 		dp->dcfgphase = phase;
6285 
6286 		PR_PROTO("%s:%d:%d: sending BOARDSET (0x%x), MTU (0x%lx), "
6287 		    "BUFSIZE (0x%x)\n",
6288 		    proc, domid, phase, ldp->dhw.dh_boardset, IDN_MTU,
6289 		    IDN_SMR_BUFSIZE);
6290 
6291 		IDNXDC(domid, &mt, cfg_subtype.val,
6292 		    ldp->dhw.dh_boardset, IDN_MTU, IDN_SMR_BUFSIZE);
6293 		break;
6294 
6295 	case 4:
6296 		/*
6297 		 * ----------------------------------------------------
6298 		 * Send: SLABSIZE, OPTIONS.CHECKSUM, NWR_SIZE
6299 		 * ----------------------------------------------------
6300 		 */
6301 		cfg_subtype.val = 0;
6302 		cfg_subtype.param.p[0] = IDN_CFGPARAM(IDNCFG_SIZE,
6303 		    IDNCFGARG_SIZE_SLAB);
6304 		cfg_subtype.param.p[1] = IDN_CFGPARAM(IDNCFG_OPTIONS,
6305 		    IDNCFGARG_CHECKSUM);
6306 		cfg_subtype.param.p[2] = IDN_CFGPARAM(IDNCFG_SIZE,
6307 		    IDNCFGARG_SIZE_NWR);
6308 		cfg_subtype.info.num = 3;
6309 		cfg_subtype.info.phase = phase;
6310 		dp->dcfgphase = phase;
6311 
6312 		PR_PROTO("%s:%d:%d: sending SLABSIZE (%d), CKSUM (%d), "
6313 		    "NWRSIZE (%d)\n",
6314 		    proc, domid, phase, IDN_SLAB_BUFCOUNT,
6315 		    IDN_CHECKSUM, IDN_NWR_SIZE);
6316 
6317 		IDNXDC(domid, &mt, cfg_subtype.val,
6318 		    IDN_SLAB_BUFCOUNT, IDN_CHECKSUM, IDN_NWR_SIZE);
6319 		break;
6320 
6321 	default:
6322 		rv = 1;
6323 		break;
6324 	}
6325 
6326 	return (rv);
6327 }
6328 
6329 #define	CFG_FATAL	((uint_t)-1)	/* reset link */
6330 #define	CFG_CONTINUE	0x0000		/* looking for more */
6331 #define	CFG_DONE	0x0001		/* got everything expected */
6332 #define	CFG_ERR_MTU	0x0002
6333 #define	CFG_ERR_BUF	0x0004
6334 #define	CFG_ERR_SLAB	0x0008
6335 #define	CFG_ERR_NWR	0x0010
6336 #define	CFG_ERR_NETS	0x0020
6337 #define	CFG_ERR_MBOX	0x0040
6338 #define	CFG_ERR_NMCADR	0x0080
6339 #define	CFG_ERR_MCADR	0x0100
6340 #define	CFG_ERR_CKSUM	0x0200
6341 #define	CFG_ERR_SMR	0x0400
6342 #define	CFG_MAX_ERRORS	16
6343 
6344 #define	CFGERR2IDNKERR(ce) \
6345 	(((ce) & CFG_ERR_MTU)	? IDNKERR_CONFIG_MTU 	: \
6346 	((ce) & CFG_ERR_BUF)	? IDNKERR_CONFIG_BUF 	: \
6347 	((ce) & CFG_ERR_SLAB)	? IDNKERR_CONFIG_SLAB 	: \
6348 	((ce) & CFG_ERR_NWR)	? IDNKERR_CONFIG_NWR 	: \
6349 	((ce) & CFG_ERR_NETS)	? IDNKERR_CONFIG_NETS 	: \
6350 	((ce) & CFG_ERR_MBOX)	? IDNKERR_CONFIG_MBOX 	: \
6351 	((ce) & CFG_ERR_NMCADR)	? IDNKERR_CONFIG_NMCADR	: \
6352 	((ce) & CFG_ERR_MCADR)	? IDNKERR_CONFIG_MCADR	: \
6353 	((ce) & CFG_ERR_CKSUM)	? IDNKERR_CONFIG_CKSUM	: \
6354 	((ce) & CFG_ERR_SMR)	? IDNKERR_CONFIG_SMR	: 0)
6355 
6356 #define	CFGERR2FINARG(ce) \
6357 	(((ce) & CFG_ERR_MTU)	? IDNFIN_ARG_CFGERR_MTU    : \
6358 	((ce) & CFG_ERR_BUF)	? IDNFIN_ARG_CFGERR_BUF    : \
6359 	((ce) & CFG_ERR_SLAB)	? IDNFIN_ARG_CFGERR_SLAB   : \
6360 	((ce) & CFG_ERR_NWR)	? IDNFIN_ARG_CFGERR_NWR    : \
6361 	((ce) & CFG_ERR_NETS)	? IDNFIN_ARG_CFGERR_NETS   : \
6362 	((ce) & CFG_ERR_MBOX)	? IDNFIN_ARG_CFGERR_MBOX   : \
6363 	((ce) & CFG_ERR_NMCADR)	? IDNFIN_ARG_CFGERR_NMCADR : \
6364 	((ce) & CFG_ERR_MCADR)	? IDNFIN_ARG_CFGERR_MCADR  : \
6365 	((ce) & CFG_ERR_CKSUM)	? IDNFIN_ARG_CFGERR_CKSUM  : \
6366 	((ce) & CFG_ERR_SMR)	? IDNFIN_ARG_CFGERR_SMR	   : IDNFIN_ARG_NONE)
6367 
6368 /*
6369  * Called when some CFG messages arrive.  We use dncfgitems to count the
6370  * total number of items received so far since we'll receive multiple CFG
6371  * messages during the CONFIG phase.  Note that dncfgitems is initialized
6372  * in idn_send_config.
6373  */
6374 static void
6375 idn_recv_config(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs)
6376 {
6377 	uint_t		msg = mtp->mt_mtype;
6378 	uint_t		rv, rv_expected, rv_actual;
6379 	int		pnum;
6380 	int		phase;
6381 	register int	p;
6382 	register int	c;
6383 	idn_mainmbox_t	*mmp;
6384 	register uint_t	subtype, subtype_arg;
6385 	idn_domain_t	*dp;
6386 	int		index;
6387 	idn_domain_t	*ldp = &idn_domain[idn.localid];
6388 	idn_mboxtbl_t	*mbtp;
6389 	idn_cfgsubtype_t	cfg_subtype;
6390 	idn_xdcargs_t	cfg_arg;
6391 	idn_msgtype_t	mt;
6392 	idnsb_error_t	idnerr;
6393 	procname_t	proc = "idn_recv_config";
6394 
6395 	ASSERT(domid != idn.localid);
6396 
6397 	GET_XARGS(xargs, &cfg_subtype.val, &cfg_arg[0], &cfg_arg[1],
6398 	    &cfg_arg[2]);
6399 	cfg_arg[3] = 0;
6400 
6401 	dp = &idn_domain[domid];
6402 
6403 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
6404 
6405 	if (dp->dstate != IDNDS_CONFIG) {
6406 		/*
6407 		 * Not ready to receive config info.
6408 		 * Drop whatever he sent us.  Let the
6409 		 * timer continue and timeout if needed.
6410 		 */
6411 		PR_PROTO("%s:%d: WARNING state(%s) != CONFIG\n",
6412 		    proc, domid, idnds_str[dp->dstate]);
6413 		return;
6414 	}
6415 
6416 	if ((msg & IDNP_ACKNACK_MASK) || dp->dcfgsnddone) {
6417 		IDN_MSGTIMER_STOP(domid, IDNP_CFG, 0);
6418 	}
6419 
6420 	if (msg & IDNP_ACKNACK_MASK) {
6421 		/*
6422 		 * ack/cfg
6423 		 */
6424 		phase = GET_XARGS_CFG_PHASE(xargs);
6425 
6426 		PR_PROTO("%s:%d: received ACK for CFG phase %d\n",
6427 		    proc, domid, phase);
6428 		if (phase != (int)dp->dcfgphase) {
6429 			/*
6430 			 * Phase is not what we were
6431 			 * expecting.  Something got lost
6432 			 * in the shuffle.  Restart the
6433 			 * timer and let it timeout if necessary
6434 			 * and reestablish the connection.
6435 			 */
6436 			IDN_MSGTIMER_START(domid, IDNP_CFG, dp->dcfgphase,
6437 			    idn_msg_waittime[IDNP_CFG], NULL);
6438 		} else {
6439 			idn_send_config(domid, phase + 1);
6440 
6441 			if (dp->dcfgsnddone && dp->dcfgrcvdone) {
6442 				IDN_DUNLOCK(domid);
6443 				IDN_SYNC_LOCK();
6444 				IDN_DLOCK_EXCL(domid);
6445 				if (dp->dstate == IDNDS_CONFIG) {
6446 					dp->dxp = &xphase_con;
6447 					IDN_XSTATE_TRANSITION(dp, IDNXS_PEND);
6448 					bzero(xargs, sizeof (xargs));
6449 
6450 					(void) idn_xphase_transition(domid,
6451 					    NULL, xargs);
6452 				}
6453 				IDN_SYNC_UNLOCK();
6454 			}
6455 		}
6456 		return;
6457 	}
6458 
6459 	pnum = (int)cfg_subtype.info.num;
6460 	phase = (int)cfg_subtype.info.phase;
6461 
6462 	for (p = 0; p < pnum; p++) {
6463 		int	board;
6464 #ifdef DEBUG
6465 		uint_t	val;
6466 		char	*str;
6467 
6468 		val = 0;
6469 		str = NULL;
6470 #define	RCVCFG(s, v)	{ str = (s); val = (v); }
6471 #else
6472 #define	RCVCFG(s, v)	{}
6473 #endif /* DEBUG */
6474 
6475 		subtype	    = IDN_CFGPARAM_TYPE(cfg_subtype.param.p[p]);
6476 		subtype_arg = IDN_CFGPARAM_ARG(cfg_subtype.param.p[p]);
6477 
6478 		switch (subtype) {
6479 
6480 		case IDNCFG_BARLAR:
6481 			IDN_GLOCK_EXCL();
6482 			switch (subtype_arg) {
6483 
6484 			case IDNCFGARG_BARLAR_BAR:
6485 				if (idn.smr.rempfn == PFN_INVALID) {
6486 					idn.smr.rempfn = (pfn_t)cfg_arg[p];
6487 					dp->dncfgitems++;
6488 					RCVCFG("BARLAR_BAR", cfg_arg[p]);
6489 				}
6490 				break;
6491 
6492 			case IDNCFGARG_BARLAR_LAR:
6493 				if (idn.smr.rempfnlim == PFN_INVALID) {
6494 					idn.smr.rempfnlim = (pfn_t)cfg_arg[p];
6495 					dp->dncfgitems++;
6496 					RCVCFG("BARLAR_LAR", cfg_arg[p]);
6497 				}
6498 				break;
6499 
6500 			default:
6501 				cmn_err(CE_WARN,
6502 				    "IDN 217: unknown CFGARG type (%d) "
6503 				    "from domain %d",
6504 				    subtype_arg, domid);
6505 				break;
6506 			}
6507 			IDN_GUNLOCK();
6508 			break;
6509 
6510 		case IDNCFG_MCADR:
6511 			board = subtype_arg;
6512 			if ((board >= 0) && (board < MAX_BOARDS) &&
6513 			    (dp->dhw.dh_mcadr[board] == 0)) {
6514 				dp->dhw.dh_mcadr[board] = cfg_arg[p];
6515 				dp->dncfgitems++;
6516 				RCVCFG("MCADR", cfg_arg[p]);
6517 			}
6518 			break;
6519 
6520 		case IDNCFG_NMCADR:
6521 			if (dp->dhw.dh_nmcadr == 0) {
6522 				dp->dhw.dh_nmcadr = cfg_arg[p];
6523 				dp->dncfgitems++;
6524 				RCVCFG("NMCADR", cfg_arg[p]);
6525 			}
6526 			break;
6527 
6528 		case IDNCFG_CPUSET:
6529 			switch (subtype_arg) {
6530 
6531 			case IDNCFGARG_CPUSET_UPPER:
6532 			{
6533 				cpuset_t	tmpset;
6534 
6535 				MAKE64_CPUMASK(tmpset, cfg_arg[p], 0);
6536 				CPUSET_OR(dp->dcpuset, tmpset);
6537 				dp->dncfgitems++;
6538 				RCVCFG("CPUSET_UPPER", cfg_arg[p]);
6539 				break;
6540 			}
6541 			case IDNCFGARG_CPUSET_LOWER:
6542 			{
6543 				cpuset_t	tmpset;
6544 
6545 				MAKE64_CPUMASK(tmpset, 0, cfg_arg[p]);
6546 				CPUSET_OR(dp->dcpuset, tmpset);
6547 				dp->dncfgitems++;
6548 				RCVCFG("CPUSET_LOWER", cfg_arg[p]);
6549 				break;
6550 			}
6551 			default:
6552 				ASSERT(0);
6553 				break;
6554 			}
6555 			break;
6556 
6557 		case IDNCFG_NETID:
6558 			if (dp->dnetid == (ushort_t)-1) {
6559 				dp->dnetid = (ushort_t)cfg_arg[p];
6560 				dp->dncfgitems++;
6561 				RCVCFG("NETID", cfg_arg[p]);
6562 			}
6563 			break;
6564 
6565 		case IDNCFG_BOARDSET:
6566 			if ((dp->dhw.dh_boardset & cfg_arg[p])
6567 			    == dp->dhw.dh_boardset) {
6568 				/*
6569 				 * Boardset better include what we
6570 				 * already know about.
6571 				 */
6572 				dp->dhw.dh_boardset = cfg_arg[p];
6573 				dp->dncfgitems++;
6574 				RCVCFG("BOARDSET", cfg_arg[p]);
6575 			}
6576 			break;
6577 
6578 		case IDNCFG_SIZE:
6579 			switch (subtype_arg) {
6580 
6581 			case IDNCFGARG_SIZE_MTU:
6582 				if (dp->dmtu == 0) {
6583 					dp->dmtu = cfg_arg[p];
6584 					dp->dncfgitems++;
6585 					RCVCFG("MTU", cfg_arg[p]);
6586 				}
6587 				break;
6588 
6589 			case IDNCFGARG_SIZE_BUF:
6590 				if (dp->dbufsize == 0) {
6591 					dp->dbufsize = cfg_arg[p];
6592 					dp->dncfgitems++;
6593 					RCVCFG("BUFSIZE", cfg_arg[p]);
6594 				}
6595 				break;
6596 
6597 			case IDNCFGARG_SIZE_SLAB:
6598 				if (dp->dslabsize == 0) {
6599 					dp->dslabsize = (short)cfg_arg[p];
6600 					dp->dncfgitems++;
6601 					RCVCFG("SLABSIZE", cfg_arg[p]);
6602 				}
6603 				break;
6604 
6605 			case IDNCFGARG_SIZE_NWR:
6606 				if (dp->dnwrsize == 0) {
6607 					dp->dnwrsize = (short)cfg_arg[p];
6608 					dp->dncfgitems++;
6609 					RCVCFG("NWRSIZE", cfg_arg[p]);
6610 				}
6611 				break;
6612 
6613 			default:
6614 				ASSERT(0);
6615 				break;
6616 			}
6617 			break;
6618 
6619 		case IDNCFG_DATAMBOX:
6620 			switch (subtype_arg) {
6621 
6622 			case IDNCFGARG_DATAMBOX_TABLE:
6623 				if (ldp->dmbox.m_tbl ||
6624 				    !dp->dvote.v.master ||
6625 				    !VALID_NWROFFSET(cfg_arg[p], 4)) {
6626 					/*
6627 					 * Only a master should be
6628 					 * sending us a datambox table.
6629 					 */
6630 					break;
6631 				}
6632 				IDN_DLOCK_EXCL(idn.localid);
6633 				ldp->dmbox.m_tbl = (idn_mboxtbl_t *)
6634 				    IDN_OFFSET2ADDR(cfg_arg[p]);
6635 				IDN_DUNLOCK(idn.localid);
6636 				dp->dncfgitems++;
6637 				RCVCFG("DATAMBOX.TABLE", cfg_arg[p]);
6638 				break;
6639 
6640 			case IDNCFGARG_DATAMBOX_DOMAIN:
6641 				if (dp->dmbox.m_send->mm_smr_mboxp ||
6642 				    !VALID_NWROFFSET(cfg_arg[p], 4))
6643 					break;
6644 				mbtp = (idn_mboxtbl_t *)
6645 				    IDN_OFFSET2ADDR(cfg_arg[p]);
6646 				mmp = dp->dmbox.m_send;
6647 				for (c = 0; c < IDN_MAX_NETS; c++) {
6648 
6649 					mutex_enter(&mmp[c].mm_mutex);
6650 					mmp[c].mm_smr_mboxp = mbtp;
6651 					mutex_exit(&mmp[c].mm_mutex);
6652 
6653 					IDN_MBOXTBL_PTR_INC(mbtp);
6654 				}
6655 				if (c <= 0)
6656 					break;
6657 				dp->dncfgitems++;
6658 				RCVCFG("DATAMBOX.DOMAIN", cfg_arg[p]);
6659 				break;
6660 
6661 			case IDNCFGARG_DATAMBOX_INDEX:
6662 				if (!ldp->dvote.v.master ||
6663 				    dp->dmbox.m_send->mm_smr_mboxp) {
6664 					/*
6665 					 * If I'm not the master then
6666 					 * I can't handle processing a
6667 					 * mailbox index.
6668 					 * OR, if I already have the send
6669 					 * mailbox, I'm done with this
6670 					 * config item.
6671 					 */
6672 					break;
6673 				}
6674 				ASSERT(dp->dmbox.m_tbl);
6675 				index = (int)cfg_arg[p];
6676 				/*
6677 				 * The given index is the local domain's
6678 				 * index into the remote domain's mailbox
6679 				 * table that contains the mailbox that
6680 				 * remote domain wants the local domain to
6681 				 * use as the send mailbox for messages
6682 				 * destined for the remote domain.
6683 				 * I.e. from the remote domain's
6684 				 *	perspective, this is his receive
6685 				 *	mailbox.
6686 				 */
6687 				mbtp = IDN_MBOXTBL_PTR(dp->dmbox.m_tbl, index);
6688 				mmp = dp->dmbox.m_send;
6689 				for (c = 0; c < IDN_MAX_NETS; c++) {
6690 
6691 					mutex_enter(&mmp[c].mm_mutex);
6692 					mmp[c].mm_smr_mboxp = mbtp;
6693 					mutex_exit(&mmp[c].mm_mutex);
6694 
6695 					IDN_MBOXTBL_PTR_INC(mbtp);
6696 				}
6697 				if (c <= 0)
6698 					break;
6699 				dp->dncfgitems++;
6700 				RCVCFG("DATAMBOX.INDEX", cfg_arg[p]);
6701 				break;
6702 
6703 			default:
6704 				ASSERT(0);
6705 				break;
6706 			}
6707 			break;
6708 
6709 		case IDNCFG_DATASVR:
6710 			switch (subtype_arg) {
6711 
6712 			case IDNCFGARG_DATASVR_MAXNETS:
6713 				if (dp->dmaxnets)
6714 					break;
6715 				dp->dmaxnets = (uint_t)(cfg_arg[p] & 0x3f);
6716 				dp->dncfgitems++;
6717 				RCVCFG("DATASVR.MAXNETS", cfg_arg[p]);
6718 				break;
6719 
6720 			case IDNCFGARG_DATASVR_MBXPERNET:
6721 				if (dp->dmboxpernet)
6722 					break;
6723 				dp->dmboxpernet = (uint_t)(cfg_arg[p] & 0x1ff);
6724 				dp->dncfgitems++;
6725 				RCVCFG("DATASVR.MBXPERNET", cfg_arg[p]);
6726 				break;
6727 
6728 			default:
6729 				ASSERT(0);
6730 				break;
6731 			}
6732 			break;
6733 
6734 		case IDNCFG_OPTIONS:
6735 			switch (subtype_arg) {
6736 
6737 			case IDNCFGARG_CHECKSUM:
6738 				if (dp->dcksum)
6739 					break;
6740 				if ((cfg_arg[p] & 0xff) == 0)
6741 					dp->dcksum = 1;		/* off */
6742 				else
6743 					dp->dcksum = 2;		/* on */
6744 				dp->dncfgitems++;
6745 				RCVCFG("OPTIONS.CHECKSUM", cfg_arg[p]);
6746 				break;
6747 
6748 			default:
6749 				ASSERT(0);
6750 				break;
6751 			}
6752 
6753 		default:
6754 			break;
6755 		}
6756 #ifdef DEBUG
6757 		PR_PROTO("%s:%d: received %s (0x%x)\n",
6758 		    proc, domid, str ? str : "<empty>", val);
6759 #endif /* DEBUG */
6760 	}
6761 
6762 	mt.mt_mtype = IDNP_ACK;
6763 	mt.mt_atype = IDNP_CFG;
6764 	mt.mt_cookie = mtp->mt_cookie;
6765 	CLR_XARGS(cfg_arg);
6766 	SET_XARGS_CFG_PHASE(cfg_arg, phase);
6767 	idn_send_acknack(domid, &mt, cfg_arg);
6768 
6769 	rv_expected = rv_actual = 0;
6770 
6771 	if (dp->dvote.v.master == 0) {
6772 		/*
6773 		 * Remote domain is a slave, check if we've received
6774 		 * all that we were expecting, and if so transition to
6775 		 * the next state.
6776 		 */
6777 		rv = idn_check_slave_config(domid, &rv_expected, &rv_actual);
6778 	} else {
6779 		/*
6780 		 * Remote domain is a master, check if this slave has
6781 		 * received all that it was expecting, and if so
6782 		 * transition to the next state.
6783 		 */
6784 		rv = idn_check_master_config(domid, &rv_expected, &rv_actual);
6785 	}
6786 
6787 	switch (rv) {
6788 	case CFG_DONE:
6789 		/*
6790 		 * All config info received that was expected, wrap up.
6791 		 */
6792 		if (!idn_recv_config_done(domid) && dp->dvote.v.master) {
6793 			IDN_DLOCK_EXCL(idn.localid);
6794 			ldp->dvote.v.connected = 1;
6795 			IDN_DUNLOCK(idn.localid);
6796 		}
6797 		break;
6798 
6799 	case CFG_CONTINUE:
6800 		/*
6801 		 * If we're not done sending our own config, then
6802 		 * there's no need to set a timer since one will
6803 		 * automatically be set when we send a config
6804 		 * message waiting for an acknowledgement.
6805 		 */
6806 		if (dp->dcfgsnddone) {
6807 			/*
6808 			 * We haven't yet received all the config
6809 			 * information we were expecting.  Need to
6810 			 * restart CFG timer if we've sent everything..
6811 			 */
6812 			IDN_MSGTIMER_START(domid, IDNP_CFG, 0,
6813 			    idn_msg_waittime[IDNP_CFG], NULL);
6814 		}
6815 		break;
6816 
6817 	case CFG_FATAL:
6818 		/*
6819 		 * Fatal error occurred during config exchange.
6820 		 * We need to shutdown connection in this
6821 		 * case, so initiate a (non-relink) FIN.
6822 		 * so let's get the show on the road.
6823 		 */
6824 		IDN_DUNLOCK(domid);
6825 		IDN_SYNC_LOCK();
6826 		IDN_DLOCK_EXCL(domid);
6827 		/*
6828 		 * If the state has changed from CONFIG
6829 		 * then somebody else has taken over
6830 		 * control of this domain so we can just
6831 		 * bail out.
6832 		 */
6833 		if (dp->dstate == IDNDS_CONFIG) {
6834 			INIT_IDNKERR(&idnerr);
6835 			SET_IDNKERR_ERRNO(&idnerr, EPROTO);
6836 			SET_IDNKERR_IDNERR(&idnerr, IDNKERR_CONFIG_FATAL);
6837 			SET_IDNKERR_PARAM0(&idnerr, domid);
6838 			idn_update_op(IDNOP_ERROR, DOMAINSET(domid), &idnerr);
6839 			/*
6840 			 * Keep this guy around so we can try again.
6841 			 */
6842 			DOMAINSET_ADD(idn.domset.ds_relink, domid);
6843 			IDN_HISTORY_LOG(IDNH_RELINK, domid, dp->dstate,
6844 			    idn.domset.ds_relink);
6845 			(void) idn_disconnect(domid, IDNFIN_NORMAL,
6846 			    IDNFIN_ARG_CFGERR_FATAL,
6847 			    IDNFIN_SYNC_NO);
6848 		}
6849 		IDN_SYNC_UNLOCK();
6850 		break;
6851 
6852 	default:	/* parameter conflict */
6853 		IDN_DUNLOCK(domid);
6854 		IDN_SYNC_LOCK();
6855 		IDN_DLOCK_EXCL(domid);
6856 		if (dp->dstate != IDNDS_CONFIG) {
6857 			/*
6858 			 * Hmmm...changed in the short period
6859 			 * we had dropped the lock, oh well.
6860 			 */
6861 			IDN_SYNC_UNLOCK();
6862 			break;
6863 		}
6864 		c = 0;
6865 		for (p = 0; p < CFG_MAX_ERRORS; p++)
6866 			if (rv & (1 << p))
6867 				c++;
6868 		INIT_IDNKERR(&idnerr);
6869 		SET_IDNKERR_ERRNO(&idnerr, EINVAL);
6870 		SET_IDNKERR_PARAM0(&idnerr, domid);
6871 		if (c > 1) {
6872 			SET_IDNKERR_IDNERR(&idnerr, IDNKERR_CONFIG_MULTIPLE);
6873 			SET_IDNKERR_PARAM1(&idnerr, c);
6874 		} else {
6875 			SET_IDNKERR_IDNERR(&idnerr, CFGERR2IDNKERR(rv));
6876 			SET_IDNKERR_PARAM1(&idnerr, rv_expected);
6877 			SET_IDNKERR_PARAM2(&idnerr, rv_actual);
6878 		}
6879 		/*
6880 		 * Any parameter conflicts are grounds for dismissal.
6881 		 */
6882 		if (idn.domset.ds_connected == 0) {
6883 			domainset_t	domset;
6884 			/*
6885 			 * We have no other connections yet.
6886 			 * We must blow out of here completely
6887 			 * unless we have relinkers left from
6888 			 * a RECONFIG.
6889 			 */
6890 			IDN_GLOCK_EXCL();
6891 			domset = ~idn.domset.ds_relink;
6892 			if (idn.domset.ds_relink == 0) {
6893 				IDN_GSTATE_TRANSITION(IDNGS_DISCONNECT);
6894 			}
6895 			domset &= ~idn.domset.ds_hitlist;
6896 			IDN_SET_NEW_MASTERID(IDN_NIL_DOMID);
6897 			IDN_GUNLOCK();
6898 			IDN_DUNLOCK(domid);
6899 
6900 			DOMAINSET_DEL(domset, idn.localid);
6901 
6902 			idn_update_op(IDNOP_ERROR, DOMAINSET_ALL, &idnerr);
6903 
6904 			PR_HITLIST("%s:%d: unlink_domainset(%x) due to "
6905 			    "CFG error (relink=%x, hitlist=%x)\n",
6906 			    proc, domid, domset, idn.domset.ds_relink,
6907 			    idn.domset.ds_hitlist);
6908 
6909 			idn_unlink_domainset(domset, IDNFIN_NORMAL,
6910 			    CFGERR2FINARG(rv),
6911 			    IDNFIN_OPT_UNLINK,
6912 			    BOARDSET_ALL);
6913 			IDN_SYNC_UNLOCK();
6914 			IDN_DLOCK_EXCL(domid);
6915 		} else {
6916 			PR_HITLIST("%s:%d: idn_disconnect(%d) due to CFG "
6917 			    "error (conn=%x, relink=%x, hitlist=%x)\n",
6918 			    proc, domid, domid, idn.domset.ds_connected,
6919 			    idn.domset.ds_relink, idn.domset.ds_hitlist);
6920 			/*
6921 			 * If we have other connections then
6922 			 * we're only going to blow away this
6923 			 * single connection.
6924 			 */
6925 			idn_update_op(IDNOP_ERROR, DOMAINSET(domid), &idnerr);
6926 
6927 			DOMAINSET_DEL(idn.domset.ds_relink, domid);
6928 			(void) idn_disconnect(domid, IDNFIN_NORMAL,
6929 			    CFGERR2FINARG(rv), IDNFIN_SYNC_NO);
6930 			IDN_SYNC_UNLOCK();
6931 		}
6932 		break;
6933 	}
6934 }
6935 
6936 /*
6937  * Called by master or slave which expects exactly the following
6938  * with respect to config info received from a SLAVE:
6939  * 	IDNCFG_CPUSET
6940  *	IDNCFG_NETID
6941  *	IDNCFG_BOARDSET
6942  *	IDNCFG_SIZE (MTU, BUF, SLAB, NWR)
6943  *	IDNCFG_DATAMBOX (DOMAIN or INDEX if caller is master)
6944  *	IDNCFG_DATASVR (MAXNETS, MBXPERNET)
6945  *	IDNCFG_OPTIONS (CHECKSUM)
6946  */
6947 static uint_t
6948 idn_check_slave_config(int domid, uint_t *exp, uint_t *act)
6949 {
6950 	uint_t		rv = 0;
6951 	idn_domain_t	*ldp, *dp;
6952 	procname_t	proc = "idn_check_slave_config";
6953 
6954 	dp = &idn_domain[domid];
6955 	ldp = &idn_domain[idn.localid];
6956 
6957 	ASSERT(domid != idn.localid);
6958 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
6959 	ASSERT(dp->dstate == IDNDS_CONFIG);
6960 
6961 	PR_PROTO("%s:%d: number received %d, number expected %d\n",
6962 	    proc, domid, (int)dp->dncfgitems, IDN_SLAVE_NCFGITEMS);
6963 
6964 	if ((int)dp->dncfgitems < IDN_SLAVE_NCFGITEMS)
6965 		return (CFG_CONTINUE);
6966 
6967 	if ((dp->dnetid == (ushort_t)-1) ||
6968 	    CPUSET_ISNULL(dp->dcpuset) ||
6969 	    (dp->dhw.dh_boardset == 0) ||
6970 	    (dp->dmbox.m_send->mm_smr_mboxp == NULL) ||
6971 	    (dp->dmaxnets == 0) ||
6972 	    (dp->dmboxpernet == 0) ||
6973 	    (dp->dcksum == 0) ||
6974 	    (dp->dmtu == 0) ||
6975 	    (dp->dbufsize == 0) ||
6976 	    (dp->dslabsize == 0) ||
6977 	    (dp->dnwrsize == 0)) {
6978 		/*
6979 		 * We received our IDN_SLAVE_NCFGITEMS config items,
6980 		 * but not all what we were expecting!  Gotta nack and
6981 		 * close connection.
6982 		 */
6983 		cmn_err(CE_WARN,
6984 		    "IDN: 218: missing some required config items from "
6985 		    "domain %d", domid);
6986 
6987 		rv = CFG_FATAL;
6988 		goto done;
6989 	}
6990 
6991 	if (!valid_mtu(dp->dmtu)) {
6992 		cmn_err(CE_WARN,
6993 		    "IDN: 219: remote domain %d MTU (%d) invalid "
6994 		    "(local.mtu = %d)", dp->domid, dp->dmtu, ldp->dmtu);
6995 
6996 		*exp = (uint_t)ldp->dmtu;
6997 		*act = (uint_t)dp->dmtu;
6998 		rv |= CFG_ERR_MTU;
6999 	}
7000 	if (!valid_bufsize(dp->dbufsize)) {
7001 		cmn_err(CE_WARN,
7002 		    "IDN: 220: remote domain %d BUFSIZE (%d) invalid "
7003 		    "(local.bufsize = %d)", dp->domid, dp->dbufsize,
7004 		    ldp->dbufsize);
7005 
7006 		*exp = (uint_t)ldp->dbufsize;
7007 		*act = (uint_t)dp->dbufsize;
7008 		rv |= CFG_ERR_BUF;
7009 	}
7010 	if (!valid_slabsize((int)dp->dslabsize)) {
7011 		cmn_err(CE_WARN,
7012 		    "IDN: 221: remote domain %d SLABSIZE (%d) invalid "
7013 		    "(local.slabsize = %d)",
7014 		    dp->domid, dp->dslabsize, ldp->dslabsize);
7015 
7016 		*exp = (uint_t)ldp->dslabsize;
7017 		*act = (uint_t)dp->dslabsize;
7018 		rv |= CFG_ERR_SLAB;
7019 	}
7020 	if (!valid_nwrsize((int)dp->dnwrsize)) {
7021 		cmn_err(CE_WARN,
7022 		    "IDN: 223: remote domain %d NWRSIZE (%d) invalid "
7023 		    "(local.nwrsize = %d)",
7024 		    dp->domid, dp->dnwrsize, ldp->dnwrsize);
7025 
7026 		*exp = (uint_t)ldp->dnwrsize;
7027 		*act = (uint_t)dp->dnwrsize;
7028 		rv |= CFG_ERR_NWR;
7029 	}
7030 	if ((int)dp->dmaxnets != IDN_MAX_NETS) {
7031 		cmn_err(CE_WARN,
7032 		    "IDN: 224: remote domain %d MAX_NETS (%d) invalid "
7033 		    "(local.maxnets = %d)",
7034 		    dp->domid, (int)dp->dmaxnets, IDN_MAX_NETS);
7035 
7036 		*exp = (uint_t)IDN_MAX_NETS;
7037 		*act = (uint_t)dp->dmaxnets;
7038 		rv |= CFG_ERR_NETS;
7039 	}
7040 	if ((int)dp->dmboxpernet != IDN_MBOX_PER_NET) {
7041 		cmn_err(CE_WARN,
7042 		    "IDN: 225: remote domain %d MBOX_PER_NET (%d) "
7043 		    "invalid (local.mboxpernet = %d)",
7044 		    dp->domid, (int)dp->dmboxpernet, IDN_MBOX_PER_NET);
7045 
7046 		*exp = (uint_t)IDN_MBOX_PER_NET;
7047 		*act = (uint_t)dp->dmboxpernet;
7048 		rv |= CFG_ERR_MBOX;
7049 	}
7050 	if ((dp->dcksum - 1) != (uchar_t)IDN_CHECKSUM) {
7051 		cmn_err(CE_WARN,
7052 		    "IDN: 226: remote domain %d CHECKSUM flag (%d) "
7053 		    "mismatches local domain's (%d)",
7054 		    dp->domid, (int)dp->dcksum - 1, IDN_CHECKSUM);
7055 
7056 		*exp = (uint_t)IDN_CHECKSUM;
7057 		*act = (uint_t)(dp->dcksum - 1);
7058 		rv |= CFG_ERR_CKSUM;
7059 	}
7060 
7061 done:
7062 
7063 	return (rv ? rv : CFG_DONE);
7064 }
7065 
7066 /*
7067  * Called by slave ONLY which expects exactly the following
7068  * config info from the MASTER:
7069  *	IDNCFG_BARLAR
7070  *	IDNCFG_MCADR
7071  *	IDNCFG_NMCADR
7072  * 	IDNCFG_CPUSET
7073  *	IDNCFG_NETID
7074  *	IDNCFG_BOARDSET
7075  *	IDNCFG_SIZE (MTU, BUF, SLAB, NWR)
7076  *	IDNCFG_DATAMBOX (TABLE, DOMAIN)
7077  *	IDNCFG_DATASVR (MAXNETS, MBXPERNET)
7078  *	IDNCFG_OPTIONS (CHECKSUM)
7079  */
7080 static uint_t
7081 idn_check_master_config(int domid, uint_t *exp, uint_t *act)
7082 {
7083 	uint_t		rv = 0;
7084 	int		nmcadr;
7085 	int		total_expitems;
7086 	int		p, m, err;
7087 	idn_domain_t	*dp;
7088 	idn_domain_t	*ldp = &idn_domain[idn.localid];
7089 	procname_t	proc = "idn_check_master_config";
7090 
7091 	dp = &idn_domain[domid];
7092 
7093 	ASSERT(IDN_GET_MASTERID() != idn.localid);
7094 	ASSERT(domid != idn.localid);
7095 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
7096 	ASSERT(dp->dstate == IDNDS_CONFIG);
7097 
7098 	PR_PROTO("%s:%d: number received %d, minimum number expected %d\n",
7099 	    proc, domid, (int)dp->dncfgitems, IDN_MASTER_NCFGITEMS);
7100 
7101 	if ((int)dp->dncfgitems < IDN_MASTER_NCFGITEMS)
7102 		return (CFG_CONTINUE);
7103 
7104 	/*
7105 	 * We have at least IDN_MASTER_NCFGITEMS items which
7106 	 * means we have at least one MCADR.  Need to make sure
7107 	 * we have all that we're expecting, NMCADR.
7108 	 */
7109 	total_expitems = IDN_MASTER_NCFGITEMS + dp->dhw.dh_nmcadr - 1;
7110 	if ((dp->dhw.dh_nmcadr == 0) ||
7111 	    ((int)dp->dncfgitems < total_expitems)) {
7112 		/*
7113 		 * We have not yet received all the MCADRs
7114 		 * we're expecting.
7115 		 */
7116 		PR_PROTO("%s:%d: haven't received all MCADRs yet.\n",
7117 		    proc, domid);
7118 		return (CFG_CONTINUE);
7119 	}
7120 
7121 	nmcadr = 0;
7122 	for (p = 0; p < MAX_BOARDS; p++)
7123 		if (dp->dhw.dh_mcadr[p] != 0)
7124 			nmcadr++;
7125 
7126 	IDN_GLOCK_SHARED();
7127 	if ((idn.smr.rempfn == PFN_INVALID) ||
7128 	    (idn.smr.rempfnlim == PFN_INVALID) ||
7129 	    (dp->dnetid == (ushort_t)-1) ||
7130 	    CPUSET_ISNULL(dp->dcpuset) ||
7131 	    (dp->dhw.dh_boardset == 0) ||
7132 	    (nmcadr != dp->dhw.dh_nmcadr) ||
7133 	    (dp->dmbox.m_send->mm_smr_mboxp == NULL) ||
7134 	    (ldp->dmbox.m_tbl == NULL) ||
7135 	    (dp->dmaxnets == 0) ||
7136 	    (dp->dmboxpernet == 0) ||
7137 	    (dp->dcksum == 0) ||
7138 	    (dp->dmtu == 0) ||
7139 	    (dp->dbufsize == 0) ||
7140 	    (dp->dnwrsize == 0)) {
7141 
7142 		IDN_GUNLOCK();
7143 		/*
7144 		 * We received all of our config items, but not
7145 		 * all what we were expecting!  Gotta reset and
7146 		 * close connection.
7147 		 */
7148 		cmn_err(CE_WARN,
7149 		    "IDN: 227: missing some required config items from "
7150 		    "domain %d", domid);
7151 
7152 		rv = CFG_FATAL;
7153 		goto done;
7154 	}
7155 	if ((idn.smr.rempfnlim - idn.smr.rempfn) > btop(MB2B(IDN_SMR_SIZE))) {
7156 		/*
7157 		 * The master's SMR region is larger than
7158 		 * mine!  This means that this domain may
7159 		 * receive I/O buffers which are out of the
7160 		 * range of this local domain's SMR virtual
7161 		 * address space.  The master SMR has to be
7162 		 * no larger than the local SMR in order to
7163 		 * guarantee enough local virtual addresses
7164 		 * to see all of the SMR space.
7165 		 * XXX - Possibly add negotiating SMR size.
7166 		 *	 Try to create a new virtual mapping.
7167 		 *	 Could let domains negotiate SMR size.
7168 		 *	 Winning size would have to be smallest
7169 		 *	 in DC.  If so, how to handle incoming
7170 		 *	 domains with even smaller SMRs?
7171 		 *	 - Could either disallow connection
7172 		 *	 - Could reconfigure to use smaller SMR.
7173 		 */
7174 		cmn_err(CE_WARN,
7175 		    "IDN: 228: master's SMR (%ld) larger than "
7176 		    "local's SMR (%ld)",
7177 		    idn.smr.rempfnlim - idn.smr.rempfn,
7178 		    btop(MB2B(IDN_SMR_SIZE)));
7179 
7180 		*exp = (uint_t)IDN_SMR_SIZE;
7181 		*act = (uint_t)B2MB(ptob(idn.smr.rempfnlim - idn.smr.rempfn));
7182 		rv |= CFG_ERR_SMR;
7183 	}
7184 	IDN_GUNLOCK();
7185 
7186 	if (!valid_mtu(dp->dmtu)) {
7187 		cmn_err(CE_WARN,
7188 		    "IDN: 219: remote domain %d MTU (%d) invalid "
7189 		    "(local.mtu = %d)", dp->domid, dp->dmtu, ldp->dmtu);
7190 
7191 		*exp = (uint_t)ldp->dmtu;
7192 		*act = (uint_t)dp->dmtu;
7193 		rv |= CFG_ERR_MTU;
7194 	}
7195 	if (!valid_bufsize(dp->dbufsize)) {
7196 		cmn_err(CE_WARN,
7197 		    "IDN: 220: remote domain %d BUFSIZE (%d) invalid "
7198 		    "(local.bufsize = %d)", dp->domid, dp->dbufsize,
7199 		    ldp->dbufsize);
7200 
7201 		*exp = (uint_t)ldp->dbufsize;
7202 		*act = (uint_t)dp->dbufsize;
7203 		rv |= CFG_ERR_BUF;
7204 	}
7205 	if (!valid_nwrsize((int)dp->dnwrsize)) {
7206 		cmn_err(CE_WARN,
7207 		    "IDN: 223: remote domain %d NWRSIZE (%d) invalid "
7208 		    "(local.nwrsize = %d)",
7209 		    dp->domid, dp->dnwrsize, ldp->dnwrsize);
7210 
7211 		*exp = (uint_t)ldp->dnwrsize;
7212 		*act = (uint_t)dp->dnwrsize;
7213 		rv |= CFG_ERR_NWR;
7214 	}
7215 	if ((int)dp->dmaxnets != IDN_MAX_NETS) {
7216 		cmn_err(CE_WARN,
7217 		    "IDN: 224: remote domain %d MAX_NETS (%d) invalid "
7218 		    "(local.maxnets = %d)",
7219 		    dp->domid, (int)dp->dmaxnets, IDN_MAX_NETS);
7220 
7221 		*exp = (uint_t)IDN_MAX_NETS;
7222 		*act = (uint_t)dp->dmaxnets;
7223 		rv |= CFG_ERR_NETS;
7224 	}
7225 	if ((int)dp->dmboxpernet != IDN_MBOX_PER_NET) {
7226 		cmn_err(CE_WARN,
7227 		    "IDN: 225: remote domain %d MBOX_PER_NET (%d) "
7228 		    "invalid (local.mboxpernet = %d)",
7229 		    dp->domid, (int)dp->dmboxpernet, IDN_MBOX_PER_NET);
7230 
7231 		*exp = (uint_t)IDN_MBOX_PER_NET;
7232 		*act = (uint_t)dp->dmboxpernet;
7233 		rv |= CFG_ERR_MBOX;
7234 	}
7235 	if ((dp->dcksum - 1) != (uchar_t)IDN_CHECKSUM) {
7236 		cmn_err(CE_WARN,
7237 		    "IDN: 226: remote domain %d CHECKSUM flag (%d) "
7238 		    "mismatches local domain's (%d)",
7239 		    dp->domid, (int)dp->dcksum - 1, IDN_CHECKSUM);
7240 
7241 		*exp = (uint_t)IDN_CHECKSUM;
7242 		*act = (uint_t)(dp->dcksum - 1);
7243 		rv |= CFG_ERR_CKSUM;
7244 	}
7245 	nmcadr = 0;
7246 	err = 0;
7247 	for (m = 0; m < MAX_BOARDS; m++) {
7248 		if (!BOARD_IN_SET(dp->dhw.dh_boardset, m) &&
7249 		    dp->dhw.dh_mcadr[m]) {
7250 			cmn_err(CE_WARN,
7251 			    "IDN: 229: remote domain %d boardset (0x%x) "
7252 			    "conflicts with MCADR(board %d) [0x%x]",
7253 			    dp->domid, (uint_t)dp->dhw.dh_boardset, m,
7254 			    dp->dhw.dh_mcadr[m]);
7255 			err++;
7256 		}
7257 		if (dp->dhw.dh_mcadr[m])
7258 			nmcadr++;
7259 	}
7260 	if (err) {
7261 		*exp = 0;
7262 		*act = err;
7263 		rv |= CFG_ERR_MCADR;
7264 	} else if (nmcadr != dp->dhw.dh_nmcadr) {
7265 		cmn_err(CE_WARN,
7266 		    "IDN: 230: remote domain %d reported number of "
7267 		    "MCADRs (%d) mismatches received (%d)",
7268 		    dp->domid, dp->dhw.dh_nmcadr, nmcadr);
7269 		*exp = (uint_t)dp->dhw.dh_nmcadr;
7270 		*act = (uint_t)nmcadr;
7271 		rv |= CFG_ERR_NMCADR;
7272 	}
7273 
7274 done:
7275 
7276 	return (rv ? rv : CFG_DONE);
7277 }
7278 
7279 static int
7280 idn_recv_config_done(int domid)
7281 {
7282 	boardset_t		b_conflicts;
7283 	cpuset_t		p_conflicts;
7284 	register int		p, i;
7285 	register idn_domain_t	*dp;
7286 	idnsb_error_t		idnerr;
7287 	procname_t		proc = "idn_recv_config_done";
7288 
7289 	ASSERT(domid != IDN_NIL_DOMID);
7290 	dp = &idn_domain[domid];
7291 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
7292 
7293 	/*
7294 	 * Well, we received all that we were expecting
7295 	 * so stop any CFG timers we had going.
7296 	 */
7297 	IDN_MSGTIMER_STOP(domid, IDNP_CFG, 0);
7298 
7299 	dp->dncpus = 0;
7300 	for (p = 0; p < NCPU; p++)
7301 		if (CPU_IN_SET(dp->dcpuset, p))
7302 			dp->dncpus++;
7303 	dp->dhw.dh_nboards = 0;
7304 	for (p = 0; p < MAX_BOARDS; p++)
7305 		if (BOARD_IN_SET(dp->dhw.dh_boardset, p))
7306 			dp->dhw.dh_nboards++;
7307 
7308 	IDN_GLOCK_EXCL();
7309 	/*
7310 	 * Verify dcpuset and dhw.dh_boardset don't
7311 	 * conflict with any existing DC member.
7312 	 */
7313 	b_conflicts = idn.dc_boardset & dp->dhw.dh_boardset;
7314 	CPUSET_ZERO(p_conflicts);
7315 	CPUSET_OR(p_conflicts, idn.dc_cpuset);
7316 	CPUSET_AND(p_conflicts, dp->dcpuset);
7317 
7318 	if (b_conflicts || !CPUSET_ISNULL(p_conflicts)) {
7319 		if (b_conflicts) {
7320 			cmn_err(CE_WARN,
7321 			    "IDN: 231: domain %d boardset "
7322 			    "(0x%x) conflicts with existing "
7323 			    "IDN boardset (0x%x)",
7324 			    domid, dp->dhw.dh_boardset,
7325 			    b_conflicts);
7326 		}
7327 		if (!CPUSET_ISNULL(p_conflicts)) {
7328 			cmn_err(CE_WARN,
7329 			    "IDN: 232: domain %d cpuset "
7330 			    "(0x%x.%0x) conflicts with existing "
7331 			    "IDN cpuset (0x%x.%0x)", domid,
7332 			    UPPER32_CPUMASK(dp->dcpuset),
7333 			    LOWER32_CPUMASK(dp->dcpuset),
7334 			    UPPER32_CPUMASK(p_conflicts),
7335 			    LOWER32_CPUMASK(p_conflicts));
7336 		}
7337 		IDN_GUNLOCK();
7338 		/*
7339 		 * Need to disconnect and not retry with this guy.
7340 		 */
7341 		IDN_DUNLOCK(domid);
7342 		IDN_SYNC_LOCK();
7343 		DOMAINSET_DEL(idn.domset.ds_relink, domid);
7344 		IDN_DLOCK_EXCL(domid);
7345 
7346 		INIT_IDNKERR(&idnerr);
7347 		SET_IDNKERR_ERRNO(&idnerr, EPROTO);
7348 		SET_IDNKERR_IDNERR(&idnerr, IDNKERR_CONFIG_FATAL);
7349 		SET_IDNKERR_PARAM0(&idnerr, domid);
7350 		idn_update_op(IDNOP_ERROR, DOMAINSET(domid), &idnerr);
7351 
7352 		(void) idn_disconnect(domid, IDNFIN_FORCE_HARD,
7353 		    IDNFIN_ARG_CFGERR_FATAL, IDNFIN_SYNC_NO);
7354 		IDN_SYNC_UNLOCK();
7355 
7356 		return (-1);
7357 	}
7358 
7359 	idn_mainmbox_reset(domid, dp->dmbox.m_send);
7360 	idn_mainmbox_reset(domid, dp->dmbox.m_recv);
7361 
7362 #ifdef IDNBUG_CPUPERBOARD
7363 	/*
7364 	 * We only allow connections to domains whose (mem) boards
7365 	 * all have at least one cpu.  This is necessary so that
7366 	 * we can program the CICs of that respective board.  This
7367 	 * is primarily only a requirement if the remote domain
7368 	 * is the master _and_ has the SMR in that particular board.
7369 	 * To simplify the checking we simply restrict connections to
7370 	 * domains that have at least one cpu on all boards that
7371 	 * contain memory.
7372 	 */
7373 	if (!idn_cpu_per_board((void *)NULL, dp->dcpuset, &dp->dhw)) {
7374 		cmn_err(CE_WARN,
7375 		    "IDN: 233: domain %d missing CPU per "
7376 		    "memory boardset (0x%x), CPU boardset (0x%x)",
7377 		    domid, dp->dhw.dh_boardset,
7378 		    cpuset2boardset(dp->dcpuset));
7379 
7380 		IDN_GUNLOCK();
7381 		/*
7382 		 * Need to disconnect and not retry with this guy.
7383 		 */
7384 		IDN_DUNLOCK(domid);
7385 		IDN_SYNC_LOCK();
7386 		DOMAINSET_DEL(idn.domset.ds_relink, domid);
7387 		IDN_DLOCK_EXCL(domid);
7388 
7389 		INIT_IDNKERR(&idnerr);
7390 		SET_IDNKERR_ERRNO(&idnerr, EINVAL);
7391 		SET_IDNKERR_IDNERR(&idnerr, IDNKERR_CPU_CONFIG);
7392 		SET_IDNKERR_PARAM0(&idnerr, domid);
7393 		idn_update_op(IDNOP_ERROR, DOMAINSET(domid), &idnerr);
7394 
7395 		(void) idn_disconnect(domid, IDNFIN_FORCE_HARD,
7396 		    IDNFIN_ARG_CPUCFG, IDNFIN_SYNC_NO);
7397 		IDN_SYNC_UNLOCK();
7398 
7399 		return (-1);
7400 	}
7401 #endif /* IDNBUG_CPUPERBOARD */
7402 
7403 	CPUSET_OR(idn.dc_cpuset, dp->dcpuset);
7404 	idn.dc_boardset |= dp->dhw.dh_boardset;
7405 
7406 	IDN_GUNLOCK();
7407 
7408 	/*
7409 	 * Set up the portmap for this domain.
7410 	 */
7411 	i = -1;
7412 	for (p = 0; p < NCPU; p++) {
7413 		BUMP_INDEX(dp->dcpuset, i);
7414 		dp->dcpumap[p] = (uchar_t)i;
7415 	}
7416 
7417 	/*
7418 	 * Got everything we need from the remote
7419 	 * domain, now we can program hardware as needed.
7420 	 */
7421 	if (idn_program_hardware(domid) != 0) {
7422 		domainset_t	domset;
7423 		/*
7424 		 * Yikes!  Failed to program hardware.
7425 		 * Gotta bail.
7426 		 */
7427 		cmn_err(CE_WARN,
7428 		    "IDN: 234: failed to program hardware for domain %d "
7429 		    "(boardset = 0x%x)",
7430 		    domid, dp->dhw.dh_boardset);
7431 
7432 		IDN_DUNLOCK(domid);
7433 		/*
7434 		 * If we're having problems programming our
7435 		 * hardware we better unlink completely from
7436 		 * the IDN before things get really bad.
7437 		 */
7438 		IDN_SYNC_LOCK();
7439 		IDN_GLOCK_EXCL();
7440 		IDN_GSTATE_TRANSITION(IDNGS_DISCONNECT);
7441 		domset = DOMAINSET_ALL;
7442 		DOMAINSET_DEL(domset, idn.localid);
7443 		IDN_SET_NEW_MASTERID(IDN_NIL_DOMID);
7444 		IDN_GUNLOCK();
7445 
7446 		INIT_IDNKERR(&idnerr);
7447 		SET_IDNKERR_ERRNO(&idnerr, EINVAL);
7448 		SET_IDNKERR_IDNERR(&idnerr, IDNKERR_HW_ERROR);
7449 		SET_IDNKERR_PARAM0(&idnerr, domid);
7450 		idn_update_op(IDNOP_ERROR, DOMAINSET_ALL, &idnerr);
7451 
7452 		idn_unlink_domainset(domset, IDNFIN_NORMAL, IDNFIN_ARG_HWERR,
7453 		    IDNFIN_OPT_UNLINK, BOARDSET_ALL);
7454 
7455 		IDN_SYNC_UNLOCK();
7456 		IDN_DLOCK_EXCL(domid);
7457 
7458 		return (-1);
7459 	}
7460 
7461 	/*
7462 	 * Now that hardware has been programmed we can
7463 	 * remap the SMR into our local space, if necessary.
7464 	 */
7465 	IDN_GLOCK_EXCL();
7466 	if (domid == IDN_GET_MASTERID()) {
7467 		/*
7468 		 * No need to worry about disabling the data
7469 		 * server since at this stage there is only
7470 		 * one and he doesn't go active until his
7471 		 * mailbox (dmbox.m_recv->mm_smr_mboxp) is set up.
7472 		 */
7473 		smr_remap(&kas, idn.smr.vaddr, idn.smr.rempfn, IDN_SMR_SIZE);
7474 	}
7475 	IDN_GUNLOCK();
7476 
7477 	/*
7478 	 * There is no need to ACK the CFG messages since remote
7479 	 * domain would not progress to the next state (CON_SENT)
7480 	 * unless he has received everything.
7481 	 */
7482 
7483 	dp->dcfgrcvdone = 1;
7484 	PR_PROTO("%s:%d: RECV config DONE\n", proc, domid);
7485 
7486 	if (dp->dcfgsnddone) {
7487 		idn_xdcargs_t	xargs;
7488 		/*
7489 		 * Well, we've received all that we were expecting,
7490 		 * but we don't know if the remote domain has
7491 		 * received all that it was expecting from us,
7492 		 * although we know we transferred everything
7493 		 * so let's get the show on the road.
7494 		 */
7495 		IDN_DUNLOCK(domid);
7496 		IDN_SYNC_LOCK();
7497 		IDN_DLOCK_EXCL(domid);
7498 		/*
7499 		 * If the state has changed from CONFIG
7500 		 * then somebody else has taken over
7501 		 * control of this domain so we can just
7502 		 * bail out.
7503 		 */
7504 		if (dp->dstate == IDNDS_CONFIG) {
7505 			dp->dxp = &xphase_con;
7506 			IDN_XSTATE_TRANSITION(dp, IDNXS_PEND);
7507 			bzero(xargs, sizeof (xargs));
7508 
7509 			(void) idn_xphase_transition(domid, NULL, xargs);
7510 		}
7511 		IDN_SYNC_UNLOCK();
7512 	}
7513 
7514 	return (0);
7515 }
7516 
7517 static int
7518 idn_verify_config_mbox(int domid)
7519 {
7520 	idn_domain_t	*ldp, *dp;
7521 	idn_mainmbox_t	*mmp;
7522 	idn_mboxtbl_t	*mtp;
7523 	int		c, rv = 0;
7524 	uint_t		activeptr, readyptr;
7525 	ushort_t	mbox_csum;
7526 
7527 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
7528 
7529 	dp = &idn_domain[domid];
7530 	ldp = &idn_domain[idn.localid];
7531 
7532 	/*
7533 	 * The master will have assigned us the dmbox.m_tbl
7534 	 * from which we assign our receive mailboxes.
7535 	 * The first (0) entry contains the cookie used
7536 	 * for verification.
7537 	 */
7538 	IDN_DLOCK_SHARED(idn.localid);
7539 	/*
7540 	 * Now that we have an assigned mboxtbl from the
7541 	 * master, we can determine which receive mailbox
7542 	 * we indirectly assigned to him at the time we
7543 	 * sent him his MBOX_INDEX.  Prep it, however note
7544 	 * that the master will have not been able to
7545 	 * validate it because of the chicken 'n egg
7546 	 * problem between a master and slave.  Thus we
7547 	 * need to reset the cookie after the prep.
7548 	 */
7549 	mmp = dp->dmbox.m_recv;
7550 	mtp = IDN_MBOXTBL_PTR(ldp->dmbox.m_tbl, domid);
7551 	for (c = 0; c < IDN_MAX_NETS; c++) {
7552 		mutex_enter(&mmp[c].mm_mutex);
7553 		ASSERT(!mmp[c].mm_smr_mboxp);
7554 
7555 		mmp[c].mm_smr_mboxp = mtp;
7556 		mbox_csum = IDN_CKSUM_MBOX(&mtp->mt_header);
7557 		if (!VALID_MBOXHDR(&mtp->mt_header, c, mbox_csum)) {
7558 			cmn_err(CE_WARN,
7559 			    "IDN: 235: [recv] mailbox (domain %d, "
7560 			    "channel %d) SMR CORRUPTED - RELINK",
7561 			    domid, c);
7562 			cmn_err(CE_CONT,
7563 			    "IDN: 235: [recv] expected (cookie 0x%x, "
7564 			    "cksum 0x%x) actual (cookie 0x%x, "
7565 			    "cksum 0x%x)\n",
7566 			    IDN_GET_MBOXHDR_COOKIE(&mtp->mt_header),
7567 			    (int)mtp->mt_header.mh_cksum,
7568 			    IDN_MAKE_MBOXHDR_COOKIE(0, 0, c),
7569 			    (int)mbox_csum);
7570 			mutex_exit(&mmp[c].mm_mutex);
7571 			rv = -1;
7572 			break;
7573 		}
7574 		activeptr = mtp->mt_header.mh_svr_active_ptr;
7575 		readyptr = mtp->mt_header.mh_svr_ready_ptr;
7576 		/*
7577 		 * Verify pointers are valid.
7578 		 */
7579 		if (!activeptr || !VALID_NWROFFSET(activeptr, 2) ||
7580 		    !readyptr || !VALID_NWROFFSET(readyptr, 2)) {
7581 			cmn_err(CE_WARN,
7582 			    "IDN: 235: [recv] mailbox (domain %d, "
7583 			    "channel %d) SMR CORRUPTED - RELINK",
7584 			    domid, c);
7585 			cmn_err(CE_CONT,
7586 			    "IDN: 235: [recv] activeptr (0x%x), "
7587 			    "readyptr (0x%x)\n",
7588 			    activeptr, readyptr);
7589 			mutex_exit(&mmp[c].mm_mutex);
7590 			rv = -1;
7591 			break;
7592 		}
7593 		mmp[c].mm_smr_activep =	(ushort_t *)IDN_OFFSET2ADDR(activeptr);
7594 		mmp[c].mm_smr_readyp =	(ushort_t *)IDN_OFFSET2ADDR(readyptr);
7595 		mutex_exit(&mmp[c].mm_mutex);
7596 		IDN_MBOXTBL_PTR_INC(mtp);
7597 	}
7598 
7599 	IDN_DUNLOCK(idn.localid);
7600 
7601 	if (rv)
7602 		return (rv);
7603 
7604 	/*
7605 	 * Now we need to translate SMR offsets for send mailboxes
7606 	 * to actual virtual addresses.
7607 	 */
7608 	mmp = dp->dmbox.m_send;
7609 	for (c = 0; c < IDN_MAX_NETS; mmp++, c++) {
7610 		mutex_enter(&mmp->mm_mutex);
7611 		if ((mtp = mmp->mm_smr_mboxp) == NULL) {
7612 			mutex_exit(&mmp->mm_mutex);
7613 			rv = -1;
7614 			break;
7615 		}
7616 
7617 		mbox_csum = IDN_CKSUM_MBOX(&mtp->mt_header);
7618 
7619 		if (!VALID_MBOXHDR(&mtp->mt_header, c, mbox_csum)) {
7620 			cmn_err(CE_WARN,
7621 			    "IDN: 235: [send] mailbox (domain %d, "
7622 			    "channel %d) SMR CORRUPTED - RELINK",
7623 			    domid, c);
7624 			cmn_err(CE_CONT,
7625 			    "IDN: 235: [send] expected (cookie 0x%x, "
7626 			    "cksum 0x%x) actual (cookie 0x%x, "
7627 			    "cksum 0x%x)\n",
7628 			    IDN_GET_MBOXHDR_COOKIE(&mtp->mt_header),
7629 			    (int)mtp->mt_header.mh_cksum,
7630 			    IDN_MAKE_MBOXHDR_COOKIE(0, 0, c),
7631 			    (int)mbox_csum);
7632 			mutex_exit(&mmp->mm_mutex);
7633 			rv = -1;
7634 			break;
7635 		}
7636 		activeptr = mtp->mt_header.mh_svr_active_ptr;
7637 		readyptr = mtp->mt_header.mh_svr_ready_ptr;
7638 		/*
7639 		 * Paranoid check.
7640 		 */
7641 		if (!activeptr || !VALID_NWROFFSET(activeptr, 2) ||
7642 		    !readyptr || !VALID_NWROFFSET(readyptr, 2)) {
7643 			cmn_err(CE_WARN,
7644 			    "IDN: 235: [send] mailbox (domain %d, "
7645 			    "channel %d) SMR CORRUPTED - RELINK",
7646 			    domid, c);
7647 			cmn_err(CE_CONT,
7648 			    "IDN: 235: [send] activeptr (0x%x), "
7649 			    "readyptr (0x%x)\n",
7650 			    activeptr, readyptr);
7651 			mutex_exit(&mmp->mm_mutex);
7652 			rv = -1;
7653 			break;
7654 		}
7655 		mmp->mm_smr_activep = (ushort_t *)IDN_OFFSET2ADDR(activeptr);
7656 		mmp->mm_smr_readyp = (ushort_t *)IDN_OFFSET2ADDR(readyptr);
7657 		idn_reset_mboxtbl(mtp);
7658 		mutex_exit(&mmp->mm_mutex);
7659 		IDN_MBOXTBL_PTR_INC(mtp);
7660 	}
7661 
7662 	return (rv);
7663 }
7664 
7665 /*
7666  * The BUFSIZEs between domains have to be equal so that slave buffers
7667  * and the master's slabpool are consistent.
7668  * The MTUs between domains have to be equal so they can transfer
7669  * packets consistently without possible data truncation.
7670  *
7671  * ZZZ - Perhaps these could be negotiated?
7672  */
7673 static int
7674 valid_mtu(uint_t mtu)
7675 {
7676 	return ((mtu == idn_domain[idn.localid].dmtu) && mtu);
7677 }
7678 
7679 static int
7680 valid_bufsize(uint_t bufsize)
7681 {
7682 	return ((bufsize == idn_domain[idn.localid].dbufsize) && bufsize);
7683 }
7684 
7685 static int
7686 valid_slabsize(int slabsize)
7687 {
7688 	return ((slabsize == idn_domain[idn.localid].dslabsize) && slabsize);
7689 }
7690 
7691 static int
7692 valid_nwrsize(int nwrsize)
7693 {
7694 	return ((nwrsize == idn_domain[idn.localid].dnwrsize) && nwrsize);
7695 }
7696 
7697 static int
7698 idn_program_hardware(int domid)
7699 {
7700 	int		rv, is_master;
7701 	idn_domain_t	*dp;
7702 	uint_t		*mcadrp;
7703 	pfn_t		rem_pfn, rem_pfnlimit;
7704 	procname_t	proc = "idn_program_hardware";
7705 
7706 	PR_PROTO("%s:%d: program hw in domain %d w.r.t remote domain %d\n",
7707 	    proc, domid, idn.localid, domid);
7708 
7709 	dp = &idn_domain[domid];
7710 
7711 	ASSERT(domid != idn.localid);
7712 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
7713 	ASSERT(dp->dstate == IDNDS_CONFIG);
7714 
7715 	IDN_GLOCK_EXCL();
7716 
7717 	if (DOMAIN_IN_SET(idn.domset.ds_hwlinked, domid)) {
7718 		IDN_GUNLOCK();
7719 		return (0);
7720 	}
7721 
7722 	DOMAINSET_ADD(idn.domset.ds_flush, domid);
7723 	CHECKPOINT_OPENED(IDNSB_CHKPT_CACHE, dp->dhw.dh_boardset, 1);
7724 
7725 	if (domid != IDN_GET_MASTERID()) {
7726 		/*
7727 		 * If the remote domain is a slave, then
7728 		 * all we have to program is the CIC sm_mask.
7729 		 */
7730 		is_master = 0;
7731 		if ((idn.localid == IDN_GET_MASTERID()) &&
7732 		    lock_try(&idn.first_hwlink)) {
7733 			/*
7734 			 * This is our first HW link and I'm the
7735 			 * master, which means we need to program
7736 			 * our local bar/lar.
7737 			 */
7738 			ASSERT(idn.first_hwmasterid == (short)IDN_NIL_DOMID);
7739 			idn.first_hwmasterid = (short)idn.localid;
7740 			rem_pfn = idn.smr.locpfn;
7741 			rem_pfnlimit = idn.smr.locpfn +
7742 			    btop(MB2B(IDN_SMR_SIZE));
7743 		} else {
7744 			/*
7745 			 * Otherwise, just a slave linking to
7746 			 * another slave.  No bar/lar updating
7747 			 * necessary.
7748 			 */
7749 			rem_pfn = rem_pfnlimit = PFN_INVALID;
7750 		}
7751 		mcadrp = NULL;
7752 	} else {
7753 		/*
7754 		 * If the remote domain is a master, then
7755 		 * we need to program the CIC sm_mask/sm_bar/sm_lar,
7756 		 * and PC's.
7757 		 */
7758 		is_master = 1;
7759 		rem_pfn = idn.smr.rempfn;
7760 		rem_pfnlimit = idn.smr.rempfnlim;
7761 		mcadrp = dp->dhw.dh_mcadr;
7762 		ASSERT(idn.first_hwmasterid == (short)IDN_NIL_DOMID);
7763 		idn.first_hwmasterid = (short)domid;
7764 	}
7765 
7766 	PR_PROTO("%s:%d: ADD bset (0x%x)\n", proc, domid, dp->dhw.dh_boardset);
7767 
7768 	rv = idnxf_shmem_add(is_master, dp->dhw.dh_boardset,
7769 	    rem_pfn, rem_pfnlimit, mcadrp);
7770 
7771 	if (rv == 0) {
7772 		DOMAINSET_ADD(idn.domset.ds_hwlinked, domid);
7773 	} else {
7774 		if (rem_pfn == idn.smr.locpfn)
7775 			lock_clear(&idn.first_hwlink);
7776 
7777 		if (idn.first_hwmasterid == (short)domid)
7778 			idn.first_hwmasterid = (short)IDN_NIL_DOMID;
7779 
7780 		(void) idnxf_shmem_sub(is_master, dp->dhw.dh_boardset);
7781 	}
7782 
7783 	IDN_GUNLOCK();
7784 
7785 	return (rv);
7786 }
7787 
7788 static int
7789 idn_deprogram_hardware(int domid)
7790 {
7791 	int		rv, is_master;
7792 	idn_domain_t	*dp;
7793 	procname_t	proc = "idn_deprogram_hardware";
7794 
7795 
7796 	dp = &idn_domain[domid];
7797 
7798 	ASSERT(domid != idn.localid);
7799 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
7800 
7801 	/*
7802 	 * Need to take into consideration what boards remote
7803 	 * domain was connected to.  If we don't have a connection to
7804 	 * them ourself, then we better remove them now , otherwise
7805 	 * they'll never be removed (unless we link to them at some point).
7806 	 */
7807 #if 0
7808 	DEBUG_USECDELAY(500000);
7809 #endif /* 0 */
7810 
7811 	IDN_GLOCK_EXCL();
7812 
7813 	if (!DOMAIN_IN_SET(idn.domset.ds_hwlinked, domid)) {
7814 		IDN_GUNLOCK();
7815 		return (0);
7816 	}
7817 
7818 	PR_PROTO("%s:%d: DEprogram hw in domain %d w.r.t remote domain %d\n",
7819 	    proc, domid, idn.localid, domid);
7820 
7821 	/*
7822 	 * It's possible to come through this flow for domains that
7823 	 * have not been programmed, i.e. not in idn.hwlinked_domset,
7824 	 * so don't bother asserting that they might be in there.
7825 	 * This can occur if we lose a domain during the config/syn
7826 	 * sequence.  If this occurs we won't know whether the remote
7827 	 * domain has programmed its hardware or not.  If it has then
7828 	 * it will have to go through the DMAP sequence and thus we
7829 	 * have to go through it also.  So, if we reach at least the
7830 	 * CONFIG state, we need to go through the DMAP handshake.
7831 	 */
7832 
7833 	PR_PROTO("%s:%d: SUB bset (0x%x)\n", proc, domid, dp->dhw.dh_boardset);
7834 
7835 	if (idn.first_hwmasterid == (short)domid) {
7836 		is_master = 1;
7837 		idn.first_hwmasterid = (short)IDN_NIL_DOMID;
7838 	} else {
7839 		is_master = 0;
7840 	}
7841 	rv = idnxf_shmem_sub(is_master, dp->dhw.dh_boardset);
7842 
7843 	if (rv == 0)
7844 		DOMAINSET_DEL(idn.domset.ds_hwlinked, domid);
7845 
7846 	IDN_GUNLOCK();
7847 
7848 	return (rv);
7849 }
7850 
7851 /*
7852  * Remember can't send slabs back to master at this point.
7853  * Entered with write-drwlock held.
7854  * Returns with drwlock dropped.
7855  */
7856 static void
7857 idn_deconfig(int domid)
7858 {
7859 	idn_domain_t	*dp, *ldp;
7860 	smr_slab_t	*sp;
7861 	int		c, masterid;
7862 	procname_t	proc = "idn_deconfig";
7863 
7864 	ASSERT(IDN_SYNC_IS_LOCKED());
7865 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
7866 	ASSERT(domid != idn.localid);
7867 
7868 	ldp = &idn_domain[idn.localid];
7869 	dp = &idn_domain[domid];
7870 
7871 	ASSERT(dp->dstate == IDNDS_DMAP);
7872 
7873 	PR_PROTO("%s:%d: (dio=%d, dioerr=%d, dnslabs=%d)\n",
7874 	    proc, domid, dp->dio, dp->dioerr, dp->dnslabs);
7875 
7876 	IDN_GLOCK_EXCL();
7877 	masterid = IDN_GET_MASTERID();
7878 
7879 	idn.dc_boardset &= ~dp->dhw.dh_boardset;
7880 	for (c = 0; c < NCPU; c++) {
7881 		if (CPU_IN_SET(dp->dcpuset, c)) {
7882 			CPUSET_DEL(idn.dc_cpuset, c);
7883 		}
7884 	}
7885 
7886 	IDN_GUNLOCK();
7887 
7888 	(void) smr_buf_free_all(domid);
7889 
7890 	if (idn.localid == masterid) {
7891 		/*
7892 		 * Since I'm the master there may
7893 		 * have been slabs in this domain's
7894 		 * idn_domain[] entry.
7895 		 */
7896 		DSLAB_LOCK_EXCL(domid);
7897 		if ((sp = dp->dslab) != NULL) {
7898 			PR_PROTO("%s:%d: freeing up %d dead slabs\n",
7899 			    proc, domid, dp->dnslabs);
7900 			smr_slab_free(domid, sp);
7901 			dp->dslab = NULL;
7902 			dp->dnslabs = 0;
7903 			dp->dslab_state = DSLAB_STATE_UNKNOWN;
7904 		}
7905 		DSLAB_UNLOCK(domid);
7906 	} else if (domid == masterid) {
7907 		/*
7908 		 * We're shutting down the master!
7909 		 * We need to blow away our local slab
7910 		 * data structures.
7911 		 * Since I'm not the master, there should
7912 		 * be no slab structures in the given
7913 		 * domain's idn_domain[] entry.  They should
7914 		 * only exist in the local domain's entry.
7915 		 */
7916 		DSLAB_LOCK_EXCL(idn.localid);
7917 		ASSERT(dp->dslab == NULL);
7918 #ifdef DEBUG
7919 		{
7920 			int	nbusy = 0;
7921 			uint_t	dommask = 0;
7922 			for (sp = ldp->dslab; sp; sp = sp->sl_next) {
7923 				smr_slabbuf_t *bp;
7924 
7925 				if (!smr_slab_busy(sp))
7926 					continue;
7927 				nbusy++;
7928 				for (bp = sp->sl_inuse; bp; bp = bp->sb_next)
7929 					if (bp->sb_domid != IDN_NIL_DOMID)
7930 						DOMAINSET_ADD(dommask,
7931 						    bp->sb_domid);
7932 			}
7933 			if (nbusy)
7934 				PR_PROTO("%s:%d: found %d busy slabs "
7935 				    "(dommask = 0x%x)\n",
7936 				    proc, domid, nbusy, dommask);
7937 		}
7938 #endif /* DEBUG */
7939 		if ((sp = ldp->dslab) != NULL) {
7940 			PR_PROTO("%s:%d: freeing up %d local slab "
7941 			    "structs\n", proc, domid, ldp->dnslabs);
7942 			smr_slab_garbage_collection(sp);
7943 			ldp->dslab = NULL;
7944 			ldp->dnslabs = 0;
7945 			ldp->dslab_state = DSLAB_STATE_UNKNOWN;
7946 		}
7947 		DSLAB_UNLOCK(idn.localid);
7948 	}
7949 	if (dp->dio) {
7950 		PR_PROTO("%s:%d: reset dio (%d) to 0\n", proc, domid, dp->dio);
7951 		dp->dio = 0;
7952 	}
7953 	dp->dioerr = 0;
7954 
7955 	PR_PROTO("%s:%d: reset diocheck (%x) to 0\n",
7956 	    proc, domid, dp->diocheck);
7957 	lock_clear(&dp->diocheck);
7958 
7959 	CHECKPOINT_CLOSED(IDNSB_CHKPT_LINK, dp->dhw.dh_boardset, 2);
7960 
7961 	/*
7962 	 * Should have already flush our memory before
7963 	 * reaching this stage.  The issue is that by the
7964 	 * time we reach here the remote domains may have
7965 	 * already reprogrammed their hardware and so flushing
7966 	 * out caches now could result in a arbstop/hang
7967 	 * if we have data that needs to go back to one
7968 	 * of the remote domains that has already reprogrammed
7969 	 * its hardware.
7970 	 */
7971 	ASSERT(!DOMAIN_IN_SET(idn.domset.ds_flush, domid));
7972 
7973 	(void) idn_deprogram_hardware(domid);
7974 	/*
7975 	 * XXX - what to do if we
7976 	 *	 fail to program hardware
7977 	 *	 probably should panic since
7978 	 *	 demise of system may be near?
7979 	 *	 Sufficient to just shutdown network?
7980 	 */
7981 
7982 	IDN_DSTATE_TRANSITION(dp, IDNDS_CLOSED);
7983 
7984 	idn_close_domain(domid);
7985 }
7986 
7987 /*
7988  * If we're sending a Reset we better make sure we don't have any
7989  * references or traffic headed in the direction of this guy, since
7990  * when he receives the reset, he'll start shutting down which means
7991  * we effectively have to shutdown _before_ sending the reset.
7992  * DO NOT HOLD ANY DOMAIN RWLOCKS ON ENTRY.  Could result in deadlock
7993  * due to channel server looping back through STREAMs and attempting
7994  * to acquire domain lock, i.e. channel server will never "stop".
7995  */
7996 static void
7997 idn_shutdown_datapath(domainset_t domset, int force)
7998 {
7999 	int		do_allchan;
8000 	idn_domain_t	*dp;
8001 	register int	d;
8002 	procname_t	proc = "idn_shutdown_datapath";
8003 
8004 
8005 	PR_CHAN("%s: domset = 0x%x\n", proc, (uint_t)domset);
8006 
8007 	do_allchan = (domset == DOMAINSET_ALL) ? 1 : 0;
8008 
8009 	DOMAINSET_DEL(domset, idn.localid);
8010 
8011 	if (do_allchan) {
8012 		/*
8013 		 * Need to stop all outgoing and
8014 		 * incoming SMR references.
8015 		 */
8016 		idn_deactivate_channel(CHANSET_ALL, IDNCHAN_OFFLINE);
8017 	}
8018 
8019 	/*
8020 	 * If force is set then we don't want to reference
8021 	 * the SMR at all, so deactivate the domains from
8022 	 * channels first.  This will result in the mainmbox-flush
8023 	 * routines to just clean up without referencing the
8024 	 * SMR space.
8025 	 */
8026 	if (force)
8027 		idn_mainmbox_deactivate(domset);
8028 
8029 	/*
8030 	 * Flush out mailboxes (clear smr reference).
8031 	 */
8032 	for (d = 0; d < MAX_DOMAINS; d++) {
8033 		if (!DOMAIN_IN_SET(domset, d))
8034 			continue;
8035 
8036 		dp = &idn_domain[d];
8037 		if ((dp->dmbox.m_send == NULL) && (dp->dmbox.m_recv == NULL))
8038 			continue;
8039 
8040 		IDN_MBOX_LOCK(d);
8041 		if (dp->dmbox.m_send)
8042 			(void) idn_mainmbox_flush(d, dp->dmbox.m_send);
8043 		if (dp->dmbox.m_recv)
8044 			(void) idn_mainmbox_flush(d, dp->dmbox.m_recv);
8045 		IDN_MBOX_UNLOCK(d);
8046 	}
8047 	/*
8048 	 * Deactivate all domain references also.
8049 	 * Only necessary if it wasn't already done above.
8050 	 */
8051 	if (!force)
8052 		idn_mainmbox_deactivate(domset);
8053 }
8054 
8055 void
8056 idn_send_cmd(int domid, idn_cmd_t cmdtype, uint_t arg1, uint_t arg2, uint_t
8057     arg3)
8058 {
8059 	idn_msgtype_t	mt;
8060 	procname_t	proc = "idn_send_cmd";
8061 
8062 	mt.mt_mtype = IDNP_CMD;
8063 	mt.mt_atype = 0;
8064 	mt.mt_cookie = 0;
8065 
8066 	ASSERT(IDN_DLOCK_IS_HELD(domid));
8067 
8068 	PR_PROTO("%s:%d: sending command %s\n", proc, domid,
8069 	    VALID_IDNCMD(cmdtype) ? idncmd_str[cmdtype] : "unknown");
8070 
8071 	IDN_MSGTIMER_START(domid, IDNP_CMD, (ushort_t)cmdtype,
8072 	    idn_msg_waittime[IDNP_CMD], &mt.mt_cookie);
8073 
8074 	IDNXDC(domid, &mt, (uint_t)cmdtype, arg1, arg2, arg3);
8075 }
8076 
8077 void
8078 idn_send_cmdresp(int domid, idn_msgtype_t *mtp, idn_cmd_t cmdtype, uint_t arg1,
8079     uint_t arg2, uint_t cerrno)
8080 {
8081 	idn_msgtype_t	mt;
8082 
8083 	ASSERT(IDN_DLOCK_IS_HELD(domid));
8084 
8085 	if (domid == idn.localid) {
8086 		/*
8087 		 * It's possible local domain received a command
8088 		 * from itself.  However, we cannot send a normal
8089 		 * "ack" response (XDC) to ourself.
8090 		 */
8091 		return;
8092 	}
8093 
8094 	mt.mt_mtype = IDNP_CMD | IDNP_ACK;
8095 	mt.mt_atype = 0;
8096 	mt.mt_cookie = mtp->mt_cookie;
8097 
8098 	IDNXDC(domid, &mt, (uint_t)cmdtype, arg1, arg2, cerrno);
8099 }
8100 
8101 static void
8102 idn_send_cmd_nackresp(int domid, idn_msgtype_t *mtp, idn_cmd_t cmdtype,
8103     idn_nack_t nacktype)
8104 {
8105 	idn_msgtype_t	mt;
8106 
8107 	if (domid == idn.localid)
8108 		return;
8109 
8110 	mt.mt_mtype = IDNP_CMD | IDNP_NACK;
8111 	mt.mt_atype = 0;
8112 	mt.mt_cookie = mtp->mt_cookie;
8113 
8114 	(void) IDNXDC(domid, &mt, (uint_t)cmdtype, (uint_t)nacktype, 0, 0);
8115 }
8116 
8117 void
8118 idn_broadcast_cmd(idn_cmd_t cmdtype, uint_t arg1, uint_t arg2, uint_t arg3)
8119 {
8120 	idn_msgtype_t	mt;
8121 	domainset_t	domset;
8122 	procname_t	proc = "idn_broadcast_cmd";
8123 
8124 	IDN_GLOCK_SHARED();
8125 
8126 	domset = idn.domset.ds_connected;
8127 	DOMAINSET_DEL(domset, idn.localid);
8128 
8129 	PR_PROTO("%s: broadcasting command (%s) to domainset 0x%x\n",
8130 	    proc, VALID_IDNCMD(cmdtype) ? idncmd_str[cmdtype] : "unknown",
8131 	    domset);
8132 
8133 	mt.mt_mtype = IDNP_CMD;
8134 	mt.mt_atype = 0;
8135 	mt.mt_cookie = 0;
8136 
8137 	IDNXDC_BROADCAST(domset, &mt, (uint_t)cmdtype, arg1, arg2, arg3);
8138 
8139 	IDN_GUNLOCK();
8140 	/*
8141 	 * This is a broadcast which means local domain needs
8142 	 * to process it also.  Since we can't XDC to ourselves
8143 	 * we simply call a local function.
8144 	 */
8145 	idn_local_cmd(cmdtype, arg1, arg2, arg3);
8146 }
8147 
8148 /*
8149  * Since xargs[0] contains the cmdtype, only xargs[1], xargs[2], xargs[3]
8150  * are valid possible response arguments.
8151  */
8152 static void
8153 idn_recv_cmd(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs)
8154 {
8155 	uint_t			msg = mtp->mt_mtype;
8156 	register idn_domain_t	*dp;
8157 	idn_cmd_t		cmdtype;
8158 	uint_t			acknack;
8159 	uint_t			cmdarg1, cmdarg2, cmdarg3;
8160 	int			islocal;
8161 	int			unsup_cmd_sent, unsup_cmd_recvd;
8162 	procname_t		proc = "idn_recv_cmd";
8163 
8164 	acknack = msg & IDNP_ACKNACK_MASK;
8165 	GET_XARGS(xargs, &cmdtype, &cmdarg1, &cmdarg2, &cmdarg3);
8166 
8167 	dp = &idn_domain[domid];
8168 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
8169 
8170 	IDN_GLOCK_SHARED();
8171 
8172 	islocal = (domid == idn.localid);
8173 
8174 	ASSERT(!acknack || (acknack & IDNP_ACKNACK_MASK));
8175 
8176 	PR_PROTO("%s:%d: (local=%d) acknack=0x%x, cmdtype=%s(%d), "
8177 	    "a1=0x%x, a2=0x%x, a3=0x%x\n",
8178 	    proc, domid, islocal, acknack,
8179 	    VALID_IDNCMD(cmdtype) ? idncmd_str[cmdtype] : "unknown",
8180 	    cmdtype, cmdarg1, cmdarg2, cmdarg3);
8181 
8182 	unsup_cmd_sent = unsup_cmd_recvd = 0;
8183 
8184 	if ((IDN_GET_MASTERID() == IDN_NIL_DOMID) ||
8185 	    (dp->dstate != IDNDS_CONNECTED)) {
8186 		/*
8187 		 * Commands cannot be handled without a valid
8188 		 * master.  If this is a request then nack him.
8189 		 */
8190 		PR_PROTO("%s:%d: cannot process CMD w/o master (%d, %s)\n",
8191 		    proc, domid, IDN_GET_MASTERID(),
8192 		    idnds_str[dp->dstate]);
8193 
8194 		if (!islocal && !(acknack & IDNP_ACKNACK_MASK))
8195 			idn_send_cmd_nackresp(domid, mtp, cmdtype,
8196 			    IDNNACK_NOCONN);
8197 		IDN_GUNLOCK();
8198 		return;
8199 	}
8200 	IDN_GUNLOCK();
8201 
8202 	if (acknack & IDNP_ACKNACK_MASK) {
8203 		idn_nack_t	nack;
8204 		/*
8205 		 * Receiving a cmd+ack or cmd+nack in response to some
8206 		 * earlier command we must have issued.
8207 		 * If the response is a nack, there are two possibilites:
8208 		 *
8209 		 *	1. Remote domain failed to allocate due
8210 		 *	   to limited resources.
8211 		 *
8212 		 *	2. Remote domain does not support this
8213 		 *	   particular command.
8214 		 *
8215 		 * In the case of #2, the argument immediately after
8216 		 * the cmdtype (xargs[1]) will be (-1).
8217 		 */
8218 		nack = (idn_nack_t)cmdarg1;
8219 		if ((acknack & IDNP_NACK) && (nack == IDNNACK_BADCMD))
8220 			unsup_cmd_sent++;
8221 
8222 		if (islocal) {
8223 			/*
8224 			 * Shouldn't be receiving local commands w/acks.
8225 			 */
8226 			cmdtype = (idn_cmd_t)0;
8227 		}
8228 
8229 		switch (cmdtype) {
8230 		case IDNCMD_SLABALLOC:
8231 			idn_recv_slaballoc_resp(domid, cmdarg1, cmdarg2,
8232 			    cmdarg3);
8233 			break;
8234 
8235 		case IDNCMD_SLABFREE:
8236 			idn_recv_slabfree_resp(domid, cmdarg1, cmdarg2,
8237 			    cmdarg3);
8238 			break;
8239 
8240 		case IDNCMD_SLABREAP:
8241 			/*
8242 			 * We only care if successful.
8243 			 */
8244 			if (acknack & IDNP_ACK)
8245 				idn_recv_slabreap_resp(domid, cmdarg1, cmdarg3);
8246 			break;
8247 
8248 		case IDNCMD_NODENAME:
8249 			if ((acknack & IDNP_NACK) == 0) {
8250 				idn_recv_nodename_resp(domid, cmdarg1, cmdarg3);
8251 				break;
8252 			}
8253 			switch (nack) {
8254 			case IDNNACK_NOCONN:
8255 			case IDNNACK_RETRY:
8256 				/*
8257 				 * Remote domain was not quite
8258 				 * ready, try again.
8259 				 */
8260 				PR_PROTO("%s:%d: remote not ready "
8261 				    "for %s - retrying "
8262 				    "[dstate=%s]\n",
8263 				    proc, domid,
8264 				    idncmd_str[IDNCMD_NODENAME],
8265 				    idnds_str[dp->dstate]);
8266 
8267 				if (dp->dstate == IDNDS_CONNECTED)
8268 					(void) timeout(idn_retry_nodename_req,
8269 					    (void *)(uintptr_t)domid, hz);
8270 			default:
8271 				break;
8272 			}
8273 			break;
8274 
8275 		default:
8276 			/*
8277 			 * Unsupported command.
8278 			 */
8279 			unsup_cmd_recvd++;
8280 			break;
8281 		}
8282 		if (unsup_cmd_sent) {
8283 			PR_PROTO("%s:%d: unsupported command "
8284 			    "requested (0x%x)\n",
8285 			    proc, domid, cmdtype);
8286 		}
8287 		if (unsup_cmd_recvd) {
8288 			PR_PROTO("%s:%d: unsupported command "
8289 			    "response (0x%x)\n",
8290 			    proc, domid, cmdtype);
8291 		}
8292 	} else {
8293 		/*
8294 		 * Receiving a regular cmd from a remote domain.
8295 		 */
8296 		switch (cmdtype) {
8297 		case IDNCMD_SLABALLOC:
8298 			idn_recv_slaballoc_req(domid, mtp, cmdarg1);
8299 			break;
8300 
8301 		case IDNCMD_SLABFREE:
8302 			idn_recv_slabfree_req(domid, mtp, cmdarg1, cmdarg2);
8303 			break;
8304 
8305 		case IDNCMD_SLABREAP:
8306 			idn_recv_slabreap_req(domid, mtp, cmdarg1);
8307 			break;
8308 
8309 		case IDNCMD_NODENAME:
8310 			idn_recv_nodename_req(domid, mtp, cmdarg1);
8311 			break;
8312 
8313 		default:
8314 			/*
8315 			 * Unsupported command.
8316 			 */
8317 			unsup_cmd_recvd++;
8318 			break;
8319 		}
8320 		if (!islocal && unsup_cmd_recvd) {
8321 			/*
8322 			 * Received an unsupported IDN command.
8323 			 */
8324 			idn_send_cmd_nackresp(domid, mtp, cmdtype,
8325 			    IDNNACK_BADCMD);
8326 		}
8327 	}
8328 }
8329 
8330 /*
8331  * This is a supporting routine for idn_broadcast_cmd() to
8332  * handle processing of the requested command for the local
8333  * domain.  Currently the only support broadcast command
8334  * supported is reaping.
8335  */
8336 /*ARGSUSED2*/
8337 static void
8338 idn_local_cmd(idn_cmd_t cmdtype, uint_t arg1, uint_t arg2, uint_t arg3)
8339 {
8340 	idn_protojob_t	*jp;
8341 	idn_domain_t	*ldp = &idn_domain[idn.localid];
8342 	procname_t	proc = "idn_local_cmd";
8343 
8344 	PR_PROTO("%s: submitting local command %s on domain %d\n",
8345 	    proc, VALID_IDNCMD(cmdtype) ? idncmd_str[cmdtype] : "unknown",
8346 	    idn.localid);
8347 
8348 
8349 	jp = idn_protojob_alloc(KM_SLEEP);
8350 
8351 	jp->j_msg.m_domid    = ldp->domid;
8352 	jp->j_msg.m_msgtype  = IDNP_CMD;
8353 	jp->j_msg.m_cookie   = ldp->dcookie_recv;
8354 	SET_XARGS(jp->j_msg.m_xargs, cmdtype, arg1, arg2, arg3);
8355 
8356 	idn_protojob_submit(ldp->domid, jp);
8357 }
8358 
8359 /*
8360  * Terminate any outstanding commands that may have
8361  * been targeted for the given domain.  A command is
8362  * designated as outstanding if it has an active timer.
8363  *
8364  * serrno = ECANCELED.
8365  */
8366 static void
8367 idn_terminate_cmd(int domid, int serrno)
8368 {
8369 	idn_domain_t	*dp;
8370 	idn_timer_t	*tplist = NULL, *tp;
8371 	procname_t	proc = "idn_terminate_cmd";
8372 
8373 	dp = &idn_domain[domid];
8374 
8375 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
8376 
8377 	IDN_MSGTIMER_GET(dp, IDNP_CMD, tplist, 0);
8378 	/*
8379 	 * At this point the timers are effectively terminated
8380 	 * since when they're t_onq indication is set false.
8381 	 */
8382 	if (tplist == NULL) {
8383 		PR_PROTO("%s:%d: no outstanding cmds found\n",
8384 		    proc, domid);
8385 		/*
8386 		 * There is a window where we may have caught a
8387 		 * request just prior to issuing the actual
8388 		 * command (SLABALLOC).  We're guaranteed if there
8389 		 * was, then he will have at least registered.
8390 		 * So, if we abort the command now, he'll catch
8391 		 * it before going to sleep.
8392 		 * Drop through.
8393 		 */
8394 	}
8395 	ASSERT(tplist ? (tplist->t_back->t_forw == NULL) : 1);
8396 
8397 	for (tp = tplist; tp; tp = tp->t_forw) {
8398 		ASSERT(tp->t_type == IDNP_CMD);
8399 
8400 		PR_PROTO("%s:%d: found outstanding cmd: %s\n",
8401 		    proc, domid, idncmd_str[tp->t_subtype]);
8402 
8403 		switch (tp->t_subtype) {
8404 		case IDNCMD_SLABALLOC:
8405 			/*
8406 			 * Outstanding slaballoc request may have
8407 			 * slab waiters hanging around.  Need to
8408 			 * tell them to bail out.  The given domain
8409 			 * must be the master if we have an outstanding
8410 			 * command to him.  This also presumes that
8411 			 * if there are any waiters they're only in
8412 			 * the local domain's waiting area (i.e. we're
8413 			 * a slave).
8414 			 */
8415 #ifdef DEBUG
8416 			IDN_GLOCK_SHARED();
8417 			ASSERT(domid == IDN_GET_MASTERID());
8418 			ASSERT(idn.localid != IDN_GET_MASTERID());
8419 			IDN_GUNLOCK();
8420 #endif /* DEBUG */
8421 			(void) smr_slabwaiter_abort(idn.localid, serrno);
8422 			break;
8423 
8424 		case IDNCMD_SLABFREE:
8425 		case IDNCMD_SLABREAP:
8426 		case IDNCMD_NODENAME:
8427 			/*
8428 			 * Nothing really waiting for these operations
8429 			 * so no biggy if we just drop.
8430 			 * Note that NODENAME may have an outstanding
8431 			 * buffer, however that will be reclaimed
8432 			 * when we actually unlink from domain.
8433 			 */
8434 			break;
8435 
8436 		default:
8437 			ASSERT(0);
8438 			break;
8439 		}
8440 	}
8441 	/*
8442 	 * As mentioned before the timers are effectively no-op'd
8443 	 * once they're dequeued, however let's cleanup house and
8444 	 * get rid of the useless entries in the timeout queue.
8445 	 */
8446 	if (tplist) {
8447 		IDN_TIMER_STOPALL(tplist);
8448 	}
8449 
8450 	if (idn_domain[idn.localid].dvote.v.master) {
8451 		/*
8452 		 * I'm the master so it's possible I had
8453 		 * outstanding commands (SLABALLOC) waiting
8454 		 * to be satisfied for the given domain.
8455 		 * Since we're forcing an error it's okay
8456 		 * to continue holding onto the drwlock.
8457 		 */
8458 		PR_PROTO("%s:%d: abort slaballoc waiters\n", proc, domid);
8459 		(void) smr_slabwaiter_abort(domid, serrno);
8460 
8461 	} else if (dp->dvote.v.master) {
8462 		PR_PROTO("%s:%d: abort (local domain) slaballoc waiters\n",
8463 		    proc, domid);
8464 		(void) smr_slabwaiter_abort(idn.localid, serrno);
8465 	}
8466 }
8467 
8468 static void
8469 idn_send_acknack(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs)
8470 {
8471 	idn_domain_t	*dp = &idn_domain[domid];
8472 	procname_t	proc = "idn_send_acknack";
8473 
8474 	ASSERT(mtp ? (mtp->mt_mtype & IDNP_ACKNACK_MASK) : 1);
8475 	ASSERT(domid != IDN_NIL_DOMID);
8476 
8477 #ifdef DEBUG
8478 	{
8479 		STRING(mstr);
8480 		STRING(astr);
8481 
8482 		INUM2STR(mtp->mt_mtype, mstr);
8483 		INUM2STR(mtp->mt_atype, astr);
8484 
8485 		if (mtp->mt_mtype & IDNP_ACK) {
8486 			PR_PROTO("%s:%d: dstate=%s, msg=(%s/%s), "
8487 			    "a1=0x%x, a2=0x%x, a3=0x%x, a4 = 0x%x\n",
8488 			    proc, domid, idnds_str[dp->dstate],
8489 			    astr, mstr, xargs[0], xargs[1],
8490 			    xargs[2], xargs[3]);
8491 		} else {
8492 			idn_nack_t	nack;
8493 
8494 			nack = GET_XARGS_NACK_TYPE(xargs);
8495 			PR_PROTO("%s:%d: dstate=%s, msg=(%s/%s), "
8496 			    "nack=%s(0x%x)\n",
8497 			    proc, domid, idnds_str[dp->dstate],
8498 			    astr, mstr, idnnack_str[nack],
8499 			    (uint_t)nack);
8500 		}
8501 	}
8502 #endif /* DEBUG */
8503 
8504 	(void) IDNXDC(domid, mtp, xargs[0], xargs[1], xargs[2], xargs[3]);
8505 }
8506 
8507 /*ARGSUSED0*/
8508 static void
8509 idn_prealloc_slab(int nslabs)
8510 {
8511 	register int	s, serrno;
8512 	smr_slab_t	*sp;
8513 	idn_domain_t	*ldp = &idn_domain[idn.localid];
8514 	procname_t	proc = "idn_prealloc_slab";
8515 
8516 	IDN_GLOCK_SHARED();
8517 	DSLAB_LOCK_SHARED(idn.localid);
8518 	if ((idn.state != IDNGS_ONLINE) || (ldp->dnslabs > 0)) {
8519 		/*
8520 		 * Not in the proper state or slab already allocated.
8521 		 */
8522 		DSLAB_UNLOCK(idn.localid);
8523 		IDN_GUNLOCK();
8524 		return;
8525 	}
8526 	IDN_GUNLOCK();
8527 	ASSERT(!ldp->dslab);
8528 
8529 	serrno = 0;
8530 	for (s = 0; (s < nslabs) && ((int)ldp->dnslabs < nslabs); s++) {
8531 		/*
8532 		 * Returns with ldp->drwlock dropped.
8533 		 */
8534 		serrno = smr_slab_alloc(idn.localid, &sp);
8535 		if (serrno != 0) {
8536 			PR_PROTO("%s: FAILED to pre-alloc'd "
8537 			    "slab (serrno = %d)\n", proc, serrno);
8538 			break;
8539 		}
8540 		/*
8541 		 * State may have changed since smr_slab_alloc
8542 		 * temporarily drops drwlock.  Make sure we're
8543 		 * still connected.
8544 		 */
8545 		PR_PROTO("%s: SUCCESSFULLY pre-alloc'd slab\n", proc);
8546 
8547 		if (idn.state != IDNGS_ONLINE) {
8548 			PR_PROTO("%s: Lost connection..leaving\n", proc);
8549 			break;
8550 		}
8551 	}
8552 
8553 	DSLAB_UNLOCK(idn.localid);
8554 }
8555 
8556 /*
8557  * Received a request from a remote domain to
8558  * allocate a slab from the master SMR for him.
8559  * Allocate slab and return the response.
8560  */
8561 static void
8562 idn_recv_slaballoc_req(int domid, idn_msgtype_t *mtp, uint_t slab_size)
8563 {
8564 	register idn_domain_t	*dp;
8565 	procname_t		proc = "idn_recv_slaballoc_req";
8566 
8567 	PR_PROTO("%s: slaballoc req from domain %d (size=0x%x)\n",
8568 	    proc, domid, slab_size);
8569 
8570 	dp = &idn_domain[domid];
8571 
8572 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
8573 
8574 	IDN_GLOCK_SHARED();
8575 
8576 	if (idn.localid != IDN_GET_MASTERID()) {
8577 		IDN_GUNLOCK();
8578 		/*
8579 		 * It's a fatal error if the remote domain thinks
8580 		 * we're the master.
8581 		 */
8582 		idn_send_slaballoc_resp(domid, mtp, 0, 0, EACCES);
8583 
8584 	} else if (dp->dstate != IDNDS_CONNECTED) {
8585 
8586 		IDN_GUNLOCK();
8587 		/*
8588 		 * It's a fatal error if we don't yet have a
8589 		 * connection established with the requestor.
8590 		 */
8591 		idn_send_slaballoc_resp(domid, mtp, 0, 0, ENOLINK);
8592 	} else {
8593 		int		serrno;
8594 		smr_slab_t	*sp;
8595 		smr_offset_t	slab_offset;
8596 
8597 		IDN_GUNLOCK();
8598 		DSLAB_LOCK_SHARED(domid);
8599 		IDN_DUNLOCK(domid);
8600 		/*
8601 		 * We're connected and we're the master.
8602 		 * smr_slab_alloc() returns with dp->drwlock dropped.
8603 		 */
8604 		if ((serrno = smr_slab_alloc(domid, &sp)) == 0) {
8605 			/*
8606 			 * Successfully allocated slab for remote slave.
8607 			 */
8608 			slab_offset = IDN_ADDR2OFFSET(sp->sl_start);
8609 			slab_size   = sp->sl_end - sp->sl_start;
8610 			ASSERT((slab_offset != 0) && (slab_size != 0));
8611 		} else {
8612 			slab_offset = slab_size = 0;
8613 		}
8614 		DSLAB_UNLOCK(domid);
8615 		/*
8616 		 * The drwlock is dropped during smr_slab_alloc.
8617 		 * During that time our connection with the given
8618 		 * domain may have changed.  Better check again.
8619 		 */
8620 		IDN_DLOCK_SHARED(domid);
8621 		if ((dp->dstate != IDNDS_CONNECTED) && !serrno) {
8622 			/*
8623 			 * Connection broke.  Keep the slab here.
8624 			 */
8625 			DSLAB_LOCK_EXCL(domid);
8626 			IDN_DUNLOCK(domid);
8627 			smr_slab_free(domid, sp);
8628 			DSLAB_UNLOCK(domid);
8629 			slab_offset = slab_size = 0;
8630 			serrno = ECANCELED;
8631 			IDN_DLOCK_SHARED(domid);
8632 		}
8633 		/*
8634 		 * Send response.
8635 		 * Note that smr_slab_alloc automatically installs
8636 		 * slab into domains respective idn_domain entry
8637 		 * to be associated with that domain.
8638 		 */
8639 		idn_send_slaballoc_resp(domid, mtp, slab_offset, slab_size,
8640 		    serrno);
8641 	}
8642 }
8643 
8644 static void
8645 idn_send_slaballoc_resp(int domid, idn_msgtype_t *mtp, smr_offset_t slab_offset,
8646     uint_t slab_size, int serrno)
8647 {
8648 	procname_t	proc = "idn_send_slaballoc_resp";
8649 
8650 	PR_PROTO("%s: slaballoc resp to domain %d (off=0x%x, size=0x%x) "
8651 	    "[serrno = %d]\n",
8652 	    proc, domid, slab_offset, slab_size, serrno);
8653 
8654 	idn_send_cmdresp(domid, mtp, IDNCMD_SLABALLOC, slab_offset, slab_size,
8655 	    serrno);
8656 }
8657 
8658 /*
8659  * Received the ack or nack to a previous allocation request
8660  * made by the local domain to the master for a slab.  Need
8661  * to "put" the response into the waiting area for any
8662  * waiters.
8663  */
8664 static void
8665 idn_recv_slaballoc_resp(int domid, smr_offset_t slab_offset, uint_t slab_size,
8666     int serrno)
8667 {
8668 	smr_slab_t		*sp = NULL;
8669 	int			rv;
8670 	procname_t		proc = "idn_recv_slaballoc_resp";
8671 
8672 
8673 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
8674 
8675 	PR_PROTO("%s: slaballoc resp from domain %d (off=0x%x, size=0x%x) "
8676 	    "[serrno = %d]\n",
8677 	    proc, domid, slab_offset, slab_size, serrno);
8678 
8679 	if (!serrno) {
8680 		IDN_GLOCK_SHARED();
8681 		if (domid != IDN_GET_MASTERID()) {
8682 			/*
8683 			 * We should only be receiving responses from
8684 			 * our master.  This is either a bogus message
8685 			 * or an old response.  In either case dump it.
8686 			 */
8687 			PR_PROTO("%s: BOGUS slaballoc resp from domid %d "
8688 			    "(master = %d)\n",
8689 			    proc, domid, IDN_GET_MASTERID());
8690 			serrno = EPROTO;
8691 		}
8692 		IDN_GUNLOCK();
8693 
8694 		if (!serrno &&
8695 		    !VALID_NWROFFSET(slab_offset, IDN_SMR_BUFSIZE)) {
8696 			PR_PROTO("%s: slab offset (0x%x) out of range "
8697 			    "(0-0x%lx)\n",
8698 			    proc, slab_offset, MB2B(IDN_NWR_SIZE));
8699 			serrno = EPROTO;
8700 		} else if (!serrno) {
8701 			sp = GETSTRUCT(smr_slab_t, 1);
8702 			sp->sl_start = IDN_OFFSET2ADDR(slab_offset);
8703 			sp->sl_end   = sp->sl_start + slab_size;
8704 			smr_alloc_buflist(sp);
8705 		}
8706 	}
8707 
8708 	/*
8709 	 * Always "put" slabs back to yourself since you're a slave.
8710 	 * Note that we set the forceflag so that even if there are
8711 	 * no waiters we still install the slab for the domain.
8712 	 */
8713 	if (!serrno) {
8714 		DSLAB_LOCK_EXCL(idn.localid);
8715 	}
8716 	rv = smr_slaballoc_put(idn.localid, sp, 1, serrno);
8717 	if (!serrno) {
8718 		DSLAB_UNLOCK(idn.localid);
8719 	}
8720 
8721 	if (rv < 0) {
8722 		/*
8723 		 * Some kind of error trying to install response.
8724 		 * If there was a valid slab sent to us, we'll
8725 		 * just have to send it back.
8726 		 */
8727 		PR_PROTO("%s: failed to install response in waiting area\n",
8728 		    proc);
8729 		if (slab_size != 0) {
8730 			PR_PROTO("%s: sending slab back to domain %d "
8731 			    "(master = %d)\n",
8732 			    proc, domid, IDN_GET_MASTERID());
8733 			idn_send_cmd(domid, IDNCMD_SLABFREE, slab_offset,
8734 			    slab_size, 0);
8735 		}
8736 		if (sp) {
8737 			smr_free_buflist(sp);
8738 			FREESTRUCT(sp, smr_slab_t, 1);
8739 		}
8740 	}
8741 }
8742 
8743 /*
8744  * Note that slab reaping is effectively performed asynchronously
8745  * since the request will be received a protocol server.
8746  */
8747 static void
8748 idn_recv_slabreap_req(int domid, idn_msgtype_t *mtp, int nslabs)
8749 {
8750 	procname_t	proc = "idn_recv_slabreap_req";
8751 
8752 	PR_PROTO("%s: slab reap request (nslabs = %d)\n", proc, nslabs);
8753 
8754 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
8755 
8756 	IDN_GLOCK_SHARED();
8757 	if (domid != IDN_GET_MASTERID()) {
8758 		/*
8759 		 * Only the master can request that slabs be reaped.
8760 		 */
8761 		IDN_GUNLOCK();
8762 		PR_PROTO("%s: only master can request slab reaping\n", proc);
8763 
8764 		idn_send_cmdresp(domid, mtp, IDNCMD_SLABREAP, 0, 0, EACCES);
8765 
8766 		return;
8767 	}
8768 	IDN_GUNLOCK();
8769 
8770 	if (nslabs != 0) {
8771 		IDN_DUNLOCK(domid);
8772 		smr_slab_reap(idn.localid, &nslabs);
8773 		IDN_DLOCK_SHARED(domid);
8774 	}
8775 
8776 	PR_PROTO("%s: slab reap result (nslabs = %d)\n", proc, nslabs);
8777 
8778 	/*
8779 	 * Go ahead and send the reap response back before we start
8780 	 * free'ing off the individual slabs.
8781 	 */
8782 	idn_send_slabreap_resp(domid, mtp, nslabs, 0);
8783 }
8784 
8785 static void
8786 idn_recv_slabreap_resp(int domid, int nslabs, int serrno)
8787 {
8788 	procname_t	proc = "idn_recv_slabreap_resp";
8789 
8790 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
8791 
8792 	if ((idn.localid != IDN_GET_MASTERID()) || (idn.localid == domid)) {
8793 		PR_PROTO("%s: unexpected slabreap resp received "
8794 		    "(domid = %d)\n", proc, domid);
8795 		ASSERT(0);
8796 		return;
8797 	}
8798 	PR_PROTO("%s: recvd reap response from domain %d for %d slabs "
8799 	    "[serrno = %d]\n", proc, domid, nslabs, serrno);
8800 }
8801 
8802 /*
8803  * Not really necessary to send slabreap response.
8804  * XXX - perhaps useful to master for accounting or
8805  *	 throttling of further reaping?
8806  */
8807 static void
8808 idn_send_slabreap_resp(int domid, idn_msgtype_t *mtp, int nslabs, int serrno)
8809 {
8810 	idn_send_cmdresp(domid, mtp, IDNCMD_SLABREAP, nslabs, 0, serrno);
8811 }
8812 
8813 /*
8814  * Slave -> Master ONLY
8815  * Master never sends slabfree request to itself.
8816  */
8817 static void
8818 idn_recv_slabfree_req(int domid, idn_msgtype_t *mtp, smr_offset_t slab_offset,
8819     uint_t slab_size)
8820 {
8821 	smr_slab_t	*sp;
8822 	int		serrno;
8823 	caddr_t		s_start, s_end;
8824 	procname_t	proc = "idn_recv_slabfree_req";
8825 
8826 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
8827 
8828 	if (domid == IDN_GET_MASTERID()) {
8829 		PR_PROTO("%s: unexpected slabfree req received (domid = %d)\n",
8830 		    proc, domid);
8831 		idn_send_slabfree_resp(domid, mtp, slab_offset, slab_size,
8832 		    EACCES);
8833 		return;
8834 	}
8835 	if (slab_size > IDN_SLAB_SIZE) {
8836 		PR_PROTO("%s: unexpected slab size. exp %d, recvd %d\n",
8837 		    proc, IDN_SLAB_SIZE, slab_size);
8838 		idn_send_slabfree_resp(domid, mtp, slab_offset, slab_size,
8839 		    EINVAL);
8840 		return;
8841 	}
8842 	s_start = IDN_OFFSET2ADDR(slab_offset);
8843 	s_end   = s_start + slab_size;
8844 	/*
8845 	 * Master has received a SLABFREE request (effectively a response
8846 	 * to some earlier SLABREAP request.
8847 	 * Find the slab associated with this slab and free it up.
8848 	 */
8849 	DSLAB_LOCK_EXCL(domid);
8850 	if ((sp = smr_slaballoc_get(domid, s_start, s_end)) != NULL) {
8851 		smr_slab_free(domid, sp);
8852 		serrno = 0;
8853 	} else {
8854 		serrno = EINVAL;
8855 	}
8856 	DSLAB_UNLOCK(domid);
8857 
8858 	idn_send_slabfree_resp(domid, mtp, slab_offset, slab_size, serrno);
8859 }
8860 
8861 /*
8862  * Master -> Slave ONLY
8863  */
8864 static void
8865 idn_recv_slabfree_resp(int domid, uint_t slab_offset, uint_t slab_size, int
8866     serrno)
8867 {
8868 	procname_t	proc = "idn_recv_slabfree_resp";
8869 
8870 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
8871 
8872 	if (domid != IDN_GET_MASTERID()) {
8873 		PR_PROTO("%s: unexpected slabfree resp received (domid = %d)\n",
8874 		    proc, domid);
8875 		ASSERT(0);
8876 		return;
8877 	}
8878 	if (slab_size > IDN_SLAB_SIZE) {
8879 		PR_PROTO("%s: unexpected slab size. exp %d, recvd %d\n",
8880 		    proc, IDN_SLAB_SIZE, slab_size);
8881 		ASSERT(0);
8882 		return;
8883 	}
8884 	PR_PROTO("%s: recvd free resp from dom %d "
8885 	    "- slab (off/size) 0x%x/0x%x [serrno = %d]\n",
8886 	    proc, domid, slab_offset, slab_size, serrno);
8887 }
8888 
8889 static void
8890 idn_send_slabfree_resp(int domid, idn_msgtype_t *mtp, uint_t slab_offset,
8891     uint_t slab_size, int serrno)
8892 {
8893 	idn_send_cmdresp(domid, mtp, IDNCMD_SLABFREE, slab_offset, slab_size,
8894 	    serrno);
8895 }
8896 
8897 static void
8898 idn_retry_nodename_req(void *arg)
8899 {
8900 	int	domid = (int)(uintptr_t)arg;
8901 
8902 	idn_send_nodename_req(domid);
8903 }
8904 
8905 static void
8906 idn_send_nodename_req(int domid)
8907 {
8908 	caddr_t		b_bufp;
8909 	smr_offset_t	bufoffset;
8910 	int		serrno;
8911 	idn_domain_t	*dp = &idn_domain[domid];
8912 	procname_t	proc = "idn_send_nodename_req";
8913 
8914 	/*
8915 	 * Need to drop domain lock across
8916 	 * SMR allocation.
8917 	 */
8918 	serrno = smr_buf_alloc(domid, MAXDNAME+1, &b_bufp);
8919 
8920 	IDN_DLOCK_SHARED(domid);
8921 	if (dp->dstate != IDNDS_CONNECTED) {
8922 		/*
8923 		 * Lost connection.
8924 		 */
8925 		PR_PROTO("%s:%d: connection lost [dstate = %s]\n",
8926 		    proc, domid, idnds_str[dp->dstate]);
8927 		IDN_DUNLOCK(domid);
8928 		if (!serrno)
8929 			(void) smr_buf_free(domid, b_bufp, MAXDNAME+1);
8930 		return;
8931 	}
8932 	if (serrno) {
8933 		/*
8934 		 * Failed to allocate buffer, but still have
8935 		 * connection so keep trying.  We may have queried
8936 		 * the master a little too earlier.
8937 		 */
8938 		PR_PROTO("%s:%d: buffer alloc failed [dstate = %s]\n",
8939 		    proc, domid, idnds_str[dp->dstate]);
8940 		(void) timeout(idn_retry_nodename_req, (void *)(uintptr_t)domid,
8941 		    hz);
8942 		IDN_DUNLOCK(domid);
8943 		return;
8944 	}
8945 
8946 	*b_bufp = (char)MAXDNAME;
8947 	bufoffset = IDN_ADDR2OFFSET(b_bufp);
8948 
8949 	idn_send_cmd(domid, IDNCMD_NODENAME, bufoffset, 0, 0);
8950 	IDN_DUNLOCK(domid);
8951 }
8952 
8953 static void
8954 idn_send_nodename_resp(int domid, idn_msgtype_t *mtp, smr_offset_t bufoffset,
8955     int serrno)
8956 {
8957 	idn_send_cmdresp(domid, mtp, IDNCMD_NODENAME, (uint_t)bufoffset, 0,
8958 	    serrno);
8959 }
8960 
8961 static void
8962 idn_recv_nodename_req(int domid, idn_msgtype_t *mtp, smr_offset_t bufoffset)
8963 {
8964 	caddr_t		b_bufp;
8965 	int		length;
8966 	idn_domain_t	*ldp = &idn_domain[idn.localid];
8967 	procname_t	proc = "idn_recv_nodename_req";
8968 
8969 	IDN_DLOCK_EXCL(idn.localid);
8970 	if (!strlen(ldp->dname)) {
8971 		if (!strlen(utsname.nodename)) {
8972 			/*
8973 			 * Local domain's nodename hasn't been
8974 			 * set yet.
8975 			 */
8976 			IDN_DUNLOCK(idn.localid);
8977 			idn_send_cmd_nackresp(domid, mtp, IDNCMD_NODENAME,
8978 			    IDNNACK_RETRY);
8979 			return;
8980 		}
8981 		(void) strncpy(ldp->dname, utsname.nodename, MAXDNAME - 1);
8982 	}
8983 	IDN_DLOCK_DOWNGRADE(idn.localid);
8984 
8985 	if (!VALID_NWROFFSET(bufoffset, IDN_SMR_BUFSIZE)) {
8986 		PR_PROTO("%s:%d: invalid SMR offset received (0x%x)\n",
8987 		    proc, domid, bufoffset);
8988 		IDN_DUNLOCK(idn.localid);
8989 		idn_send_nodename_resp(domid, mtp, bufoffset, EINVAL);
8990 		return;
8991 	}
8992 
8993 	b_bufp = IDN_OFFSET2ADDR(bufoffset);
8994 	length = (int)(*b_bufp++ & 0xff);
8995 
8996 	if (length < strlen(ldp->dname)) {
8997 		PR_PROTO("%s:%d: buffer not big enough (req %lu, got %d)\n",
8998 		    proc, domid, strlen(ldp->dname), length);
8999 		IDN_DUNLOCK(idn.localid);
9000 		idn_send_nodename_resp(domid, mtp, bufoffset, EINVAL);
9001 		return;
9002 	}
9003 
9004 	(void) strncpy(b_bufp, ldp->dname, MAXDNAME);
9005 	b_bufp[MAXDNAME-1] = 0;
9006 	IDN_DUNLOCK(idn.localid);
9007 
9008 	idn_send_nodename_resp(domid, mtp, bufoffset, 0);
9009 }
9010 
9011 static void
9012 idn_recv_nodename_resp(int domid, smr_offset_t bufoffset, int serrno)
9013 {
9014 	caddr_t		b_bufp;
9015 	idn_domain_t	*dp = &idn_domain[domid];
9016 	procname_t	proc = "idn_recv_nodename_resp";
9017 
9018 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
9019 
9020 	if (!VALID_NWROFFSET(bufoffset, IDN_SMR_BUFSIZE)) {
9021 		PR_PROTO("%s:%d: invalid SMR offset received (0x%x)\n",
9022 		    proc, domid, bufoffset);
9023 		return;
9024 	}
9025 
9026 	if (serrno == 0) {
9027 		b_bufp = IDN_OFFSET2ADDR(bufoffset) + 1;
9028 		b_bufp[MAXDNAME-1] = 0;
9029 
9030 		if (strlen(b_bufp) > 0) {
9031 			(void) strncpy(dp->dname, b_bufp, MAXDNAME);
9032 			PR_PROTO("%s:%d: received nodename(%s)\n",
9033 			    proc, domid, dp->dname);
9034 		}
9035 	}
9036 
9037 	(void) smr_buf_free(domid, b_bufp - 1, MAXDNAME + 1);
9038 }
9039 
9040 /*
9041  * The master allocations the SMR management structures.
9042  */
9043 static int
9044 idn_master_init()
9045 {
9046 	idn_domain_t	*ldp = &idn_domain[idn.localid];
9047 	size_t		reserved_size = 0;
9048 	caddr_t		reserved_area = NULL;
9049 	procname_t	proc = "idn_master_init";
9050 
9051 	ASSERT(IDN_GLOCK_IS_EXCL());
9052 	ASSERT(IDN_DLOCK_IS_EXCL(idn.localid));
9053 
9054 	if (idn.mboxarea != NULL) {
9055 		PR_PROTO("%s: master data already initialized\n", proc);
9056 		return (0);
9057 	}
9058 
9059 	PR_PROTO("%s: initializing master data (domid = %d)\n",
9060 	    proc, idn.localid);
9061 
9062 	/*
9063 	 * Reserve an area of the SMR for mailbox usage.
9064 	 * This area is allocated to other domains via
9065 	 * the master.  Round it up to IDN_SMR_BUFSIZE multiple.
9066 	 */
9067 	reserved_size = IDNROUNDUP(IDN_MBOXAREA_SIZE, IDN_SMR_BUFSIZE);
9068 
9069 	PR_PROTO("%s: reserving %lu bytes for mailbox area\n",
9070 	    proc, reserved_size);
9071 
9072 #ifdef DEBUG
9073 	if (reserved_size > (size_t)IDN_SLAB_SIZE) {
9074 		PR_PROTO("%s: WARNING mbox area (%ld) > slab size (%d)\n",
9075 		    proc, reserved_size, IDN_SLAB_SIZE);
9076 	}
9077 #endif /* DEBUG */
9078 	/*
9079 	 * Initialize the pool of slabs and SMR I/O buffers.
9080 	 */
9081 	if (smr_slabpool_init(reserved_size, &reserved_area) != 0) {
9082 		idn_master_deinit();
9083 		return (-1);
9084 	}
9085 
9086 	ASSERT(idn.mboxarea == NULL);
9087 	ASSERT(reserved_area);
9088 
9089 	bzero(reserved_area, reserved_size);
9090 
9091 	idn.mboxarea = (idn_mboxtbl_t *)reserved_area;
9092 	ldp->dmbox.m_tbl = IDN_MBOXAREA_BASE(idn.mboxarea, idn.localid);
9093 	/*
9094 	 * Initialize the SMR pointers in the entire
9095 	 * mailbox table.
9096 	 */
9097 	idn_mboxarea_init(idn.mboxarea, IDN_MBOXAREA_SIZE / IDN_MBOXTBL_SIZE);
9098 
9099 	return (0);
9100 }
9101 
9102 static void
9103 idn_master_deinit()
9104 {
9105 	idn_domain_t	*ldp;
9106 	smr_slab_t	*sp;
9107 	procname_t	proc = "idn_master_deinit";
9108 
9109 	ASSERT(IDN_GLOCK_IS_EXCL());
9110 	ASSERT(IDN_DLOCK_IS_EXCL(idn.localid));
9111 
9112 	if (idn.mboxarea == NULL) {
9113 		PR_PROTO("%s: master data already deinitialized\n", proc);
9114 		return;
9115 	}
9116 
9117 	ldp = &idn_domain[idn.localid];
9118 
9119 	PR_PROTO("%s: deinitializing master data (domid = %d)\n",
9120 	    proc, idn.localid);
9121 
9122 	ldp->dmbox.m_tbl = NULL;
9123 	idn.mboxarea = NULL;
9124 	/*
9125 	 * Master may still be holding onto slabs of his own.
9126 	 */
9127 	DSLAB_LOCK_EXCL(idn.localid);
9128 	sp = ldp->dslab;
9129 	ldp->dslab = NULL;
9130 	ldp->dnslabs = 0;
9131 	if (sp)
9132 		smr_slab_free(idn.localid, sp);
9133 	ldp->dslab_state = DSLAB_STATE_UNKNOWN;
9134 	DSLAB_UNLOCK(idn.localid);
9135 
9136 	smr_slabpool_deinit();
9137 }
9138 
9139 static int
9140 idn_mark_awol(int domid, clock_t *atime)
9141 {
9142 	clock_t		awol;
9143 	idn_domain_t	*dp = &idn_domain[domid];
9144 
9145 	ASSERT(IDN_SYNC_IS_LOCKED());
9146 	ASSERT(IDN_GLOCK_IS_EXCL());
9147 
9148 	if (!DOMAIN_IN_SET(idn.domset.ds_awol, domid)) {
9149 		DOMAINSET_ADD(idn.domset.ds_awol, domid);
9150 		idn.nawols++;
9151 	}
9152 	awol = ddi_get_lbolt();
9153 	if (dp->dawol.a_count++ == 0)
9154 		dp->dawol.a_time = awol;
9155 	dp->dawol.a_last = awol;
9156 	if ((awol - dp->dawol.a_msg) >= (clock_t)(idn_awolmsg_interval * hz))
9157 		dp->dawol.a_msg = awol;
9158 	else
9159 		awol = 0;
9160 
9161 	*atime = awol;
9162 
9163 	idn_awol_event_set(dp->dhw.dh_boardset);
9164 
9165 	return (dp->dawol.a_count);
9166 }
9167 
9168 void
9169 idn_clear_awol(int domid)
9170 {
9171 	idn_domain_t	*dp = &idn_domain[domid];
9172 
9173 	ASSERT(IDN_SYNC_IS_LOCKED());
9174 	ASSERT(IDN_GLOCK_IS_EXCL());
9175 	if (DOMAIN_IN_SET(idn.domset.ds_awol, domid)) {
9176 		DOMAINSET_DEL(idn.domset.ds_awol, domid);
9177 		idn.nawols--;
9178 	}
9179 	if (dp->dawol.a_count > 0) {
9180 		dp->dawol.a_count = 0;
9181 		dp->dawol.a_last = dp->dawol.a_time;
9182 		dp->dawol.a_time = 0;
9183 		dp->dawol.a_msg = 0;
9184 
9185 		idn_awol_event_clear(dp->dhw.dh_boardset);
9186 	}
9187 }
9188 
9189 /*
9190  * A timer expired.
9191  */
9192 void
9193 idn_timer_expired(void *arg)
9194 {
9195 	idn_domain_t	*dp;
9196 	char		*op = "UNKNOWN";
9197 	clock_t		awol = 0;
9198 	int		awolcount, dcpu, domid;
9199 	idn_timer_t	*tp = (idn_timer_t *)arg;
9200 	idn_timerq_t	*tq = NULL;
9201 	uint_t		token;
9202 	char		dname[MAXDNAME];
9203 	procname_t	proc = "idn_timer_expired";
9204 	STRING(str);
9205 
9206 	tq = tp->t_q;
9207 
9208 	ASSERT(tp->t_domid != IDN_NIL_DOMID);
9209 
9210 	IDN_TIMERQ_LOCK(tq);
9211 
9212 	INUM2STR(tp->t_type, str);
9213 
9214 	if (tp->t_onq == 0) {
9215 		PR_TIMER("%s: timer CAUGHT TERMINATION (type = %s)\n",
9216 		    proc, str);
9217 		/*
9218 		 * Timer was dequeued.  Somebody is trying
9219 		 * to shut it down.
9220 		 */
9221 		IDN_TIMERQ_UNLOCK(tq);
9222 		return;
9223 	}
9224 
9225 	IDN_TIMER_DEQUEUE(tq, tp);
9226 
9227 	IDN_TIMERQ_UNLOCK(tq);
9228 
9229 	IDN_SYNC_LOCK();
9230 	IDN_DLOCK_EXCL(tp->t_domid);
9231 
9232 	domid = tp->t_domid;
9233 
9234 	dp = &idn_domain[domid];
9235 	(void) strcpy(dname, dp->dname);
9236 	dcpu = dp->dcpu;
9237 
9238 	IDN_TIMER_EXEC(tp);
9239 
9240 #ifdef DEBUG
9241 	PR_TIMER("%s:%d: [%s] timer EXPIRED (C=0x%x, P=0x%llx, X=0x%llx)\n",
9242 	    proc, tp->t_domid, str, tp->t_cookie,
9243 	    tp->t_posttime, tp->t_exectime);
9244 #endif /* DEBUG */
9245 
9246 	/*
9247 	 * IMPORTANT:
9248 	 * Each case is responsible for dropping SYNC_LOCK & DLOCK.
9249 	 */
9250 	switch (tp->t_type) {
9251 	case IDNP_DATA:
9252 		IDN_SYNC_UNLOCK();
9253 		/*
9254 		 * Timed out waiting for a data packet response.
9255 		 * We can't close domain since he may just be
9256 		 * temporarily AWOL.
9257 		 * Note that dio and diocheck do not get cleared.
9258 		 * This is taken care of when the domain restarts
9259 		 * or is fatally closed.
9260 		 * We only need a reader lock for this.
9261 		 */
9262 		IDN_DLOCK_DOWNGRADE(domid);
9263 		if (dp->diocheck && dp->dmbox.m_send) {
9264 			(void) idn_reclaim_mboxdata(domid, 0, -1);
9265 			if (dp->dio >= IDN_WINDOW_EMAX) {
9266 				idn_msgtype_t	mt;
9267 				/*
9268 				 * Restart timer for another
9269 				 * go around.
9270 				 */
9271 				IDN_MSGTIMER_START(domid, IDNP_DATA, 0,
9272 				    idn_msg_waittime[IDNP_DATA],
9273 				    &mt.mt_cookie);
9274 			} else {
9275 				lock_clear(&dp->diocheck);
9276 			}
9277 		}
9278 		IDN_DUNLOCK(domid);
9279 		break;
9280 
9281 	case IDNP_NEGO:
9282 		/*
9283 		 * If we're not in a NEGO transition, then
9284 		 * just ignore this timeout.
9285 		 */
9286 		if (dp->dxp == &xphase_nego) {
9287 			uint_t		token;
9288 
9289 			IDN_GLOCK_EXCL();
9290 			op = "CONNECT";
9291 			awolcount = idn_mark_awol(domid, &awol);
9292 			IDN_GUNLOCK();
9293 
9294 			idn_nego_cleanup_check(domid, IDN_NIL_DOMID,
9295 			    IDN_NIL_DCPU);
9296 
9297 			IDN_XSTATE_TRANSITION(dp, IDNXS_PEND);
9298 			token = IDN_RETRY_TOKEN(domid, IDNRETRY_NEGO);
9299 			idn_retry_submit(idn_retry_nego, NULL, token,
9300 			    idn_msg_retrytime[(int)IDNRETRY_NEGO]);
9301 		}
9302 		IDN_DUNLOCK(domid);
9303 		IDN_SYNC_UNLOCK();
9304 		break;
9305 
9306 	case IDNP_CMD:
9307 		/*
9308 		 * Timeouts on commands typically mean that the
9309 		 * the master is not responding.  Furthermore, we
9310 		 * can't FORCE a FIN disconnect since at this stage
9311 		 * we are CONNECTED and thus other domains may
9312 		 * have cache entries that we're sharing with them.
9313 		 * Only choice is to completely disconnect from
9314 		 * IDN and try to reestablish connection.
9315 		 *
9316 		 * However, timeouts attempting to get nodename
9317 		 * are not fatal.  Although we don't want to retry
9318 		 * either since each timeout is a lost buffer to
9319 		 * the remote domain.
9320 		 */
9321 		if (tp->t_subtype == (ushort_t)IDNCMD_NODENAME) {
9322 			PR_PROTO("%s:%d: timedout waiting for nodename\n",
9323 			    proc, domid);
9324 			IDN_DUNLOCK(domid);
9325 			IDN_SYNC_UNLOCK();
9326 			break;
9327 		}
9328 
9329 		IDN_GLOCK_EXCL();
9330 		if (idn.state == IDNGS_ONLINE) {
9331 			domainset_t	domset;
9332 			int		masterid = IDN_GET_MASTERID();
9333 
9334 			IDN_GKSTAT_GLOBAL_EVENT(gk_reconfigs,
9335 			    gk_reconfig_last);
9336 
9337 			PR_PROTO("%s:%d: RECONFIG trying old masterid = %d\n",
9338 			    proc, domid, masterid);
9339 
9340 			IDN_GSTATE_TRANSITION(IDNGS_RECONFIG);
9341 			IDN_SET_NEW_MASTERID(masterid);
9342 			IDN_GUNLOCK();
9343 			IDN_DUNLOCK(domid);
9344 
9345 			domset = idn.domset.ds_trans_on |
9346 			    idn.domset.ds_connected;
9347 
9348 			idn_unlink_domainset(domset, IDNFIN_NORMAL,
9349 			    IDNFIN_ARG_NONE, IDNFIN_OPT_RELINK,	BOARDSET_ALL);
9350 		} else {
9351 			IDN_GUNLOCK();
9352 			IDN_DUNLOCK(domid);
9353 		}
9354 		IDN_SYNC_UNLOCK();
9355 		break;
9356 
9357 	case IDNP_CON:
9358 		if (tp->t_subtype == (ushort_t)IDNCON_QUERY) {
9359 			/*
9360 			 * Timed out sending a CON-query.  This is
9361 			 * non-fatal.  We simply need to retry.
9362 			 */
9363 			IDN_GLOCK_EXCL();
9364 			op = "CONNECT";
9365 			awolcount = idn_mark_awol(domid, &awol);
9366 			IDN_GUNLOCK();
9367 			token = IDN_RETRY_TOKEN(domid, IDNRETRY_CONQ);
9368 			idn_retry_submit(idn_retry_query, NULL, token,
9369 			    idn_msg_retrytime[(int)IDNRETRY_CONQ]);
9370 			IDN_DUNLOCK(domid);
9371 			IDN_SYNC_UNLOCK();
9372 			break;
9373 		}
9374 		/*FALLTHROUGH*/
9375 	case IDNP_CFG:
9376 		/*
9377 		 * Any timeouts here we simply try to disconnect
9378 		 * and reestablish the link.  Since we haven't
9379 		 * reached the connected state w.r.t. this domain
9380 		 * we put his fin state to FORCE-HARD in order
9381 		 * to shoot right through without involving other
9382 		 * domains.  Recall that other domains may have
9383 		 * established connections with the given domain
9384 		 * which means any FIN queries to them will always
9385 		 * return connected to the given domain.  Since
9386 		 * neither the given domain nor the local domain
9387 		 * plan on disconnecting from the IDN the connection
9388 		 * to the other domains will remain thereby preventing
9389 		 * the local FIN from ever completing.  Recall that
9390 		 * a FIN depends on all member domains FIN'ing also.
9391 		 */
9392 		IDN_GLOCK_EXCL();
9393 		op = "CONNECT";
9394 		awolcount = idn_mark_awol(domid, &awol);
9395 		IDN_GUNLOCK();
9396 		DOMAINSET_ADD(idn.domset.ds_relink, domid);
9397 		IDN_HISTORY_LOG(IDNH_RELINK, domid, dp->dstate,
9398 		    idn.domset.ds_relink);
9399 		(void) idn_disconnect(domid, IDNFIN_FORCE_SOFT,
9400 		    IDNFIN_ARG_NONE, IDNFIN_SYNC_NO);
9401 		IDN_DUNLOCK(domid);
9402 		IDN_SYNC_UNLOCK();
9403 		break;
9404 
9405 	case IDNP_FIN:
9406 		/*
9407 		 * Timeouts here simply try to retry.
9408 		 */
9409 		IDN_GLOCK_EXCL();
9410 		op = "DISCONNECT";
9411 		awolcount = idn_mark_awol(domid, &awol);
9412 		IDN_GUNLOCK();
9413 		if (tp->t_subtype == (ushort_t)IDNFIN_QUERY) {
9414 			int		d;
9415 			domainset_t	rdyset;
9416 			/*
9417 			 * Timed out sending a FIN-query.  This is
9418 			 * non-fatal.  We simply need to retry.
9419 			 * If we were doing a forced unlink of any
9420 			 * domains, we don't want this awol guy
9421 			 * to hold us up.  Looks for any forced
9422 			 * unlinks and make them "ready" with
9423 			 * respect to this awol domain.
9424 			 */
9425 			rdyset = 0;
9426 			for (d = 0; d < MAX_DOMAINS; d++) {
9427 				if (FIN_IS_FORCE(idn_domain[d].dfin)) {
9428 					DOMAINSET_ADD(rdyset, d);
9429 				}
9430 			}
9431 			if (rdyset)
9432 				(void) idn_sync_register(domid,
9433 				    IDNSYNC_DISCONNECT,
9434 				    rdyset, IDNSYNC_REG_REG);
9435 
9436 			token = IDN_RETRY_TOKEN(domid, IDNRETRY_FINQ);
9437 			idn_retry_submit(idn_retry_query, NULL, token,
9438 			    idn_msg_retrytime[(int)IDNRETRY_FINQ]);
9439 			IDN_DUNLOCK(domid);
9440 			IDN_SYNC_UNLOCK();
9441 			break;
9442 		}
9443 
9444 		if (dp->dfin == IDNFIN_FORCE_SOFT) {
9445 			IDN_FSTATE_TRANSITION(dp, IDNFIN_FORCE_HARD);
9446 		}
9447 		/*
9448 		 * Anybody that was waiting on this domain and
9449 		 * had a hard-force in action gets this guy for
9450 		 * free in their base ready-set.
9451 		 */
9452 		idn_sync_register_awol(domid);
9453 
9454 		dp->dxp = &xphase_fin;
9455 		IDN_XSTATE_TRANSITION(dp, IDNXS_PEND);
9456 		token = IDN_RETRY_TOKEN(domid, IDNRETRY_FIN);
9457 		idn_retry_submit(idn_retry_fin, NULL, token,
9458 		    idn_msg_retrytime[(int)IDNRETRY_FIN]);
9459 		IDN_DUNLOCK(domid);
9460 		IDN_SYNC_UNLOCK();
9461 		break;
9462 
9463 	default:
9464 
9465 		ASSERT(0);
9466 		IDN_DUNLOCK(domid);
9467 		IDN_SYNC_UNLOCK();
9468 		break;
9469 	}
9470 
9471 	IDN_TIMER_FREE(tp);
9472 
9473 	if (awol) {
9474 		if (strlen(dname) > 0) {
9475 			cmn_err(CE_WARN,
9476 			    "IDN: 236: domain (%s) [ID %d] not "
9477 			    "responding to %s [#%d]",
9478 			    dname, domid, op, awolcount);
9479 		} else {
9480 			cmn_err(CE_WARN,
9481 			    "IDN: 236: domain [ID %d, CPU %d] not "
9482 			    "responding to %s [#%d]",
9483 			    domid, dcpu, op, awolcount);
9484 		}
9485 	}
9486 }
9487 
9488 #if 0
9489 static int
9490 idn_retry_check(uint_t token)
9491 {
9492 	int			i, count = 0;
9493 	int			domid = IDN_RETRY_TOKEN2DOMID(token);
9494 	int			key = IDN_RETRY_TOKEN2TYPE(token);
9495 	idn_retry_job_t		*rp;
9496 	idn_retry_queue_t	*qp;
9497 
9498 	qp = &idn.retryqueue;
9499 
9500 	mutex_enter(&qp->rq_mutex);
9501 
9502 	for (i = 0, rp = qp->rq_jobs; i < qp->rq_count; i++, rp = rp->rj_next)
9503 		if ((domid == IDN_RETRY_TOKEN2DOMID(rp->rj_token)) &&
9504 		    ((key == IDN_RETRY_TYPEALL) || (rp->rj_token == token)))
9505 			count++;
9506 
9507 	mutex_exit(&qp->rq_mutex);
9508 
9509 	return (count);
9510 }
9511 #endif /* 0 */
9512 
9513 static void
9514 idn_retry_execute(void *arg)
9515 {
9516 	idn_retry_job_t		*rp = (idn_retry_job_t *)arg;
9517 	idn_retry_queue_t	*qp;
9518 
9519 	qp = &idn.retryqueue;
9520 
9521 	mutex_enter(&qp->rq_mutex);
9522 	if (rp->rj_onq == 0) {
9523 		/*
9524 		 * Job has already been claimed by
9525 		 * retry termination routine.
9526 		 * Bail out.
9527 		 */
9528 		mutex_exit(&qp->rq_mutex);
9529 		return;
9530 	}
9531 	rp->rj_next->rj_prev = rp->rj_prev;
9532 	rp->rj_prev->rj_next = rp->rj_next;
9533 	if (--(qp->rq_count) == 0)
9534 		qp->rq_jobs = NULL;
9535 	else if (qp->rq_jobs == rp)
9536 		qp->rq_jobs = rp->rj_next;
9537 	mutex_exit(&qp->rq_mutex);
9538 
9539 	(*rp->rj_func)(rp->rj_token, rp->rj_arg);
9540 
9541 	IDNRETRY_FREEJOB(rp);
9542 }
9543 
9544 /*
9545  *
9546  */
9547 static void
9548 idn_retry_submit(void (*func)(uint_t token, void *arg), void *arg, uint_t token,
9549     clock_t ticks)
9550 {
9551 	idn_retry_job_t		*rp, *cp;
9552 	idn_retry_queue_t	*qp;
9553 	int			c;
9554 	procname_t		proc = "idn_retry_submit";
9555 
9556 	if (ticks < 0) {
9557 		PR_PROTO("%s: (token = 0x%x) WARNING ticks = %ld\n",
9558 		    proc, token, ticks);
9559 		return;
9560 	}
9561 	if (ticks == 0)		/* At least one tick to get into background */
9562 		ticks++;
9563 
9564 	PR_PROTO("%s: token = 0x%x\n", proc, token);
9565 
9566 	qp = &idn.retryqueue;
9567 
9568 	mutex_enter(&qp->rq_mutex);
9569 	for (c = 0, cp = qp->rq_jobs; c < qp->rq_count; cp = cp->rj_next, c++) {
9570 		if (cp->rj_token == token) {
9571 			PR_PROTO("%s: token = (%d,0x%x) already present\n",
9572 			    proc, IDN_RETRY_TOKEN2DOMID(token),
9573 			    IDN_RETRY_TOKEN2TYPE(token));
9574 			break;
9575 		}
9576 	}
9577 
9578 	if (c < qp->rq_count) {
9579 		mutex_exit(&qp->rq_mutex);
9580 		return;
9581 	}
9582 
9583 	rp = IDNRETRY_ALLOCJOB();
9584 	rp->rj_func = func;
9585 	rp->rj_arg = arg;
9586 	rp->rj_token = token;
9587 	rp->rj_prev = rp->rj_next = rp;
9588 
9589 	if (qp->rq_jobs == NULL) {
9590 		qp->rq_jobs = rp;
9591 	} else {
9592 		rp->rj_next = qp->rq_jobs;
9593 		rp->rj_prev = qp->rq_jobs->rj_prev;
9594 		rp->rj_next->rj_prev = rp;
9595 		rp->rj_prev->rj_next = rp;
9596 	}
9597 	rp->rj_onq = 1;
9598 	qp->rq_count++;
9599 	rp->rj_id = timeout(idn_retry_execute, (caddr_t)rp, ticks);
9600 	mutex_exit(&qp->rq_mutex);
9601 }
9602 
9603 int
9604 idn_retry_terminate(uint_t token)
9605 {
9606 	int			i, domid;
9607 	uint_t			key, count;
9608 	idn_retry_job_t		*rp, *nrp, *fp;
9609 	idn_retry_queue_t	*qp;
9610 	procname_t		proc = "idn_retry_terminate";
9611 
9612 	key = IDN_RETRY_TOKEN2TYPE(token);
9613 	domid = IDN_RETRY_TOKEN2DOMID(token);
9614 	fp = NULL;
9615 	qp = &idn.retryqueue;
9616 
9617 	mutex_enter(&qp->rq_mutex);
9618 	for (i = count = 0, rp = qp->rq_jobs; i < qp->rq_count; i++) {
9619 		nrp = rp->rj_next;
9620 		if ((domid == IDN_RETRY_TOKEN2DOMID(rp->rj_token)) &&
9621 		    ((key == IDN_RETRY_TYPEALL) ||
9622 		    (rp->rj_token == token))) {
9623 			/*
9624 			 * Turn off onq field as a signal to
9625 			 * the execution routine that this
9626 			 * retry has been terminated.  This
9627 			 * is necessary since we can't untimeout
9628 			 * while holding the rq_mutex otherwise
9629 			 * we'll deadlock with the execution
9630 			 * routine.  We'll untimeout these guys
9631 			 * _after_ we drop rq_mutex.
9632 			 */
9633 			rp->rj_onq = 0;
9634 			rp->rj_next->rj_prev = rp->rj_prev;
9635 			rp->rj_prev->rj_next = rp->rj_next;
9636 			if (qp->rq_jobs == rp)
9637 				qp->rq_jobs = rp->rj_next;
9638 			rp->rj_next = fp;
9639 			fp = rp;
9640 			count++;
9641 		}
9642 		rp = nrp;
9643 	}
9644 
9645 	if ((qp->rq_count -= count) == 0)
9646 		qp->rq_jobs = NULL;
9647 
9648 	mutex_exit(&qp->rq_mutex);
9649 
9650 	PR_PROTO("%s: token = (%d,0x%x), dequeued = %d\n",
9651 	    proc, domid, key, count);
9652 
9653 	for (; fp; fp = nrp) {
9654 		(void) untimeout(fp->rj_id);
9655 
9656 		nrp = fp->rj_next;
9657 		IDNRETRY_FREEJOB(fp);
9658 	}
9659 
9660 	return (count);
9661 }
9662 
9663 /*
9664  * -----------------------------------------------------------------------
9665  * The sole purpose of the idn_protocol_server is to manage the IDN
9666  * protocols between the various domains.  These messages do _not_ go
9667  * through the regular streams queues since they are not dependent on
9668  * any user process or module necessarily having the IDN driver open.
9669  * There may be multiple instances of these servers to enhance performance
9670  * of domain management.  Each server is assigned a idn_protoqueue_t
9671  * from which to obtain the work they need to do.
9672  * -----------------------------------------------------------------------
9673  */
9674 int
9675 idn_protocol_init(int nservers)
9676 {
9677 	int		i;
9678 	idn_protojob_t	*jp;
9679 	register idn_protoqueue_t	*protoq;
9680 
9681 	if (nservers <= 0) {
9682 		cmn_err(CE_WARN,
9683 		    "IDN: 237: invalid number (%d) of protocol servers",
9684 		    nservers);
9685 		return (-1);
9686 	}
9687 
9688 	idn.protocol.p_jobpool = kmem_cache_create("idn_protocol_jobcache",
9689 	    sizeof (idn_protojob_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
9690 	if (idn.protocol.p_jobpool == NULL) {
9691 		cmn_err(CE_WARN,
9692 		    "IDN: 238: kmem_cache_create(jobcache) failed");
9693 		return (-1);
9694 	}
9695 
9696 	/*
9697 	 * Initialize static cache for protojob.
9698 	 */
9699 	mutex_init(&idn_protojob_cache_lock, NULL, MUTEX_DRIVER, NULL);
9700 	jp = &idn_protojob_cache[0];
9701 	for (i = 1; i < IDN_DMV_PENDING_MAX; jp = jp->j_next, i++) {
9702 		jp->j_cache = 1;
9703 		jp->j_next = &idn_protojob_cache[i];
9704 	}
9705 	jp->j_cache = 1;
9706 	jp->j_next = NULL;
9707 	idn_protojob_cache_list = &idn_protojob_cache[0];
9708 
9709 	/*
9710 	 * Init morgue semaphore.
9711 	 */
9712 	sema_init(&idn.protocol.p_morgue, 0, NULL, SEMA_DEFAULT, NULL);
9713 	/*
9714 	 * Alloc server queues.
9715 	 */
9716 	idn.protocol.p_serverq = GETSTRUCT(idn_protoqueue_t, nservers);
9717 
9718 	/*
9719 	 * Init server queues.
9720 	 */
9721 	protoq = idn.protocol.p_serverq;
9722 	for (i = 0; i < nservers; protoq++, i++) {
9723 		mutex_init(&protoq->q_mutex, NULL, MUTEX_DRIVER, NULL);
9724 		cv_init(&protoq->q_cv, NULL, CV_DEFAULT, NULL);
9725 		protoq->q_id	  = i;
9726 		protoq->q_joblist = NULL;
9727 		protoq->q_joblist_tail = NULL;
9728 		protoq->q_die	  = 0;
9729 		protoq->q_morgue  = &idn.protocol.p_morgue;
9730 		/*
9731 		 * Create protocol server thread.
9732 		 */
9733 		protoq->q_threadp = thread_create(NULL, 0,
9734 		    idn_protocol_server, (caddr_t)&i, sizeof (i), &p0,
9735 		    TS_RUN, maxclsyspri);
9736 	}
9737 	/*
9738 	 * The servers are kept in the p_server[] array, however
9739 	 * we'll build a linked list of them to facilitate debugging.
9740 	 */
9741 	protoq = idn.protocol.p_serverq;
9742 	for (i = 0; i < (nservers - 1); protoq++, i++)
9743 		protoq->q_next = (protoq + 1);
9744 	protoq->q_next = NULL;
9745 
9746 	idn.nservers = nservers;
9747 
9748 	return (idn.nservers);
9749 }
9750 
9751 void
9752 idn_protocol_deinit()
9753 {
9754 	register int	i;
9755 	int		nservers;
9756 	register idn_protoqueue_t	*protoq;
9757 
9758 	nservers = idn.nservers;
9759 
9760 	if (nservers <= 0)
9761 		return;
9762 
9763 	/*
9764 	 * Make sure the servers are dead.
9765 	 */
9766 	idn_protocol_server_killall();
9767 	ASSERT(idn.nservers == 0);
9768 	/*
9769 	 * Destroy the mutexes.
9770 	 */
9771 	protoq = idn.protocol.p_serverq;
9772 	for (i = 0; i < nservers; protoq++, i++) {
9773 		mutex_destroy(&protoq->q_mutex);
9774 		cv_destroy(&protoq->q_cv);
9775 	}
9776 	/*
9777 	 * Free up the protoqueue memory.
9778 	 */
9779 	FREESTRUCT(idn.protocol.p_serverq, idn_protoqueue_t, nservers);
9780 	idn.protocol.p_serverq = NULL;
9781 	/*
9782 	 * Destroy the morgue semaphore.
9783 	 */
9784 	sema_destroy(&idn.protocol.p_morgue);
9785 
9786 	if (idn.protocol.p_jobpool) {
9787 		kmem_cache_destroy(idn.protocol.p_jobpool);
9788 		idn.protocol.p_jobpool = NULL;
9789 	}
9790 }
9791 
9792 static void
9793 idn_protocol_server(int *id)
9794 {
9795 	idn_protoqueue_t	*pq;
9796 	idn_protojob_t		*jl;
9797 	register idn_protojob_t	*jp;
9798 	procname_t		proc = "idn_protocol_server";
9799 
9800 	if (id == NULL) {
9801 		PR_PROTO("%s: id == NULL, thread exiting\n", proc);
9802 		return;
9803 	}
9804 	ASSERT((*id >= 0) && (*id < idn_protocol_nservers));
9805 
9806 	pq = &idn.protocol.p_serverq[*id];
9807 
9808 	ASSERT(pq->q_id == *id);
9809 
9810 	PR_PROTO("%s: id %d starting up (pq = 0x%p)\n",
9811 	    proc, pq->q_id, (void *)pq);
9812 
9813 	/*CONSTCOND*/
9814 	while (1) {
9815 		mutex_enter(&pq->q_mutex);
9816 
9817 		while (((jl = pq->q_joblist) == NULL) && !pq->q_die)
9818 			cv_wait(&pq->q_cv, &pq->q_mutex);
9819 
9820 		pq->q_joblist = pq->q_joblist_tail = NULL;
9821 
9822 		if (pq->q_die) {
9823 			/*
9824 			 * We've been killed.  Need to check-in
9825 			 * at the morgue.
9826 			 */
9827 			pq->q_threadp = NULL;
9828 			mutex_exit(&pq->q_mutex);
9829 			PR_PROTO("%s: thread (%d) killed...bye bye\n",
9830 			    proc, pq->q_id);
9831 			for (jp = jl; jp; jp = jl) {
9832 				jl = jp->j_next;
9833 				idn_protojob_free(jp);
9834 			}
9835 			sema_v(pq->q_morgue);
9836 			thread_exit();
9837 			/*NOTREACHED*/
9838 		}
9839 		mutex_exit(&pq->q_mutex);
9840 
9841 		/*
9842 		 * We can process the jobs asynchronously while more are
9843 		 * put on.
9844 		 */
9845 		for (jp = jl; jp; jp = jl) {
9846 			jl = jp->j_next;
9847 			idn_recv_proto(&(jp->j_msg));
9848 			idn_protojob_free(jp);
9849 		}
9850 	}
9851 }
9852 
9853 /*
9854  * Kill off all the protocol servers.
9855  */
9856 static void
9857 idn_protocol_server_killall()
9858 {
9859 	register idn_protoqueue_t	*pq;
9860 	int		i;
9861 	procname_t	proc = "idn_protocol_server_killall";
9862 
9863 	PR_PROTO("%s: killing off %d protocol servers\n",
9864 	    proc, idn.nservers);
9865 
9866 	pq = idn.protocol.p_serverq;
9867 	for (i = 0; i < idn.nservers; pq++, i++) {
9868 		mutex_enter(&pq->q_mutex);
9869 		pq->q_die = 1;
9870 		cv_signal(&pq->q_cv);
9871 		mutex_exit(&pq->q_mutex);
9872 	}
9873 
9874 	while (idn.nservers > 0) {
9875 		sema_p(&idn.protocol.p_morgue);
9876 		idn.nservers--;
9877 	}
9878 }
9879 
9880 idn_protojob_t *
9881 idn_protojob_alloc(int kmflag)
9882 {
9883 	idn_protojob_t	*jp;
9884 
9885 	jp = kmem_cache_alloc(idn.protocol.p_jobpool, kmflag);
9886 	if (jp == NULL) {
9887 		mutex_enter(&idn_protojob_cache_lock);
9888 		if ((jp = idn_protojob_cache_list) != NULL)
9889 			idn_protojob_cache_list = jp->j_next;
9890 		mutex_exit(&idn_protojob_cache_lock);
9891 	} else {
9892 		jp->j_cache = 0;
9893 	}
9894 
9895 	return (jp);
9896 }
9897 
9898 static void
9899 idn_protojob_free(idn_protojob_t *jp)
9900 {
9901 	ASSERT(jp);
9902 
9903 	if (jp->j_cache) {
9904 		mutex_enter(&idn_protojob_cache_lock);
9905 		jp->j_next = idn_protojob_cache_list;
9906 		idn_protojob_cache_list = jp;
9907 		mutex_exit(&idn_protojob_cache_lock);
9908 	} else {
9909 		kmem_cache_free(idn.protocol.p_jobpool, (void *)jp);
9910 	}
9911 }
9912 
9913 void
9914 idn_protojob_submit(int cookie, idn_protojob_t *jp)
9915 {
9916 	idn_protoqueue_t	*pq;
9917 	int			serverid;
9918 	procname_t		proc = "idn_protojob_submit";
9919 	STRING(str);
9920 
9921 	if (jp == NULL)
9922 		return;
9923 
9924 	serverid = IDN_PROTOCOL_SERVER_HASH(cookie);
9925 
9926 	pq = &idn.protocol.p_serverq[serverid];
9927 
9928 	INUM2STR(jp->j_msg.m_msgtype, str);
9929 	PR_PROTO("%s: job (d=%d, m=0x%x, %s) submitted to "
9930 	    "protocol server %d\n", proc, jp->j_msg.m_domid,
9931 	    jp->j_msg.m_msgtype, str, serverid);
9932 
9933 	mutex_enter(&pq->q_mutex);
9934 	/*
9935 	 * Can't submit jobs to dying servers.
9936 	 */
9937 	if (!pq->q_die) {
9938 		if (pq->q_joblist_tail) {
9939 			pq->q_joblist_tail->j_next = jp;
9940 			pq->q_joblist_tail = jp;
9941 		} else {
9942 			pq->q_joblist = pq->q_joblist_tail = jp;
9943 		}
9944 		jp->j_next = NULL;
9945 		cv_signal(&pq->q_cv);
9946 	} else {
9947 		PR_PROTO("%s: protocol server dead.  freeing protojob\n",
9948 		    proc);
9949 		idn_protojob_free(jp);
9950 	}
9951 	mutex_exit(&pq->q_mutex);
9952 }
9953 
9954 static void
9955 idn_mboxarea_init(idn_mboxtbl_t *mtp, register int ntbls)
9956 {
9957 	register int	d;
9958 	caddr_t		state_ptr = NULL, mtbasep = (caddr_t)mtp;
9959 	idn_mboxtbl_t	*amtp;
9960 	procname_t	proc = "idn_mboxarea_init";
9961 
9962 	ASSERT(mtp && (ntbls > 0));
9963 
9964 	PR_PROTO("%s: init mboxtbl (0x%p) ntbls = %d\n",
9965 	    proc, (void *)mtp, ntbls);
9966 
9967 	for (d = 0; d < ntbls; d++) {
9968 		register int	pd, sd;
9969 		register int	ch;
9970 
9971 		mtp->mt_header.mh_svr_active = 0;
9972 		mtp->mt_header.mh_svr_ready = 0;
9973 		/*
9974 		 * Initialize the header of each mbox table
9975 		 * with a cookie for identity.
9976 		 */
9977 		/*
9978 		 * Format: 0xc0c0DSCC
9979 		 *	 D = primary domain
9980 		 *	 S = sub-domain of primary
9981 		 *	CC = channel of sub-domain.
9982 		 */
9983 		pd = (d / MAX_DOMAINS) / IDN_MAX_NETS;
9984 		sd = (d / IDN_MAX_NETS) % MAX_DOMAINS;
9985 		ch = d % IDN_MAX_NETS;
9986 
9987 		/*
9988 		 * We point all sub-domains in the same channel
9989 		 * to the same active sync flag since a single server
9990 		 * services all domains in the same channel.
9991 		 */
9992 		amtp = IDN_MBOXTBL_ABS_PTR(mtbasep, pd, 0, ch);
9993 
9994 		state_ptr = (caddr_t)&amtp->mt_header.mh_svr_active;
9995 		mtp->mt_header.mh_svr_active_ptr = IDN_ADDR2OFFSET(state_ptr);
9996 
9997 		state_ptr = (caddr_t)&amtp->mt_header.mh_svr_ready;
9998 		mtp->mt_header.mh_svr_ready_ptr = IDN_ADDR2OFFSET(state_ptr);
9999 
10000 		mtp->mt_header.mh_cookie = IDN_MAKE_MBOXHDR_COOKIE(pd, sd, ch);
10001 
10002 		mtp->mt_header.mh_cksum = IDN_CKSUM_MBOX(&mtp->mt_header);
10003 
10004 		IDN_MBOXTBL_PTR_INC(mtp);
10005 	}
10006 	/*
10007 	 * Now that the master has initialized the entire mailbox
10008 	 * region the referenced memory may not necessarily be up-to-date
10009 	 * with respect to the actual SMR memory due to caching.
10010 	 * In order to make sure future connecting domains get a
10011 	 * consistent picture of the mailbox region, it's necessary
10012 	 * for the master to flush its caches.
10013 	 */
10014 	PR_PROTO("%s: flushing ecache's of local (master) domain\n", proc);
10015 
10016 	idnxf_flushall_ecache();
10017 }
10018 
10019 idn_mainmbox_t *
10020 idn_mainmbox_init(int domid, int mbx)
10021 {
10022 	idn_mainmbox_t	*mmp;
10023 	int		c;
10024 	idn_mainmbox_t	*cmp;
10025 	procname_t	proc = "idn_mainmbox_init";
10026 
10027 	ASSERT(idn_domain[domid].dcpu != IDN_NIL_DCPU);
10028 	ASSERT(IDN_DLOCK_IS_HELD(domid));
10029 
10030 	PR_PROTO("%s: initializing main %s mailbox for domain %d\n",
10031 	    proc, IDNMBOX_IS_RECV(mbx) ? "RECV" : "SEND", domid);
10032 
10033 	cmp = GETSTRUCT(idn_mainmbox_t, IDN_MAX_NETS);
10034 	for (c = 0; c < IDN_MAX_NETS; c++) {
10035 		mmp = &cmp[c];
10036 		mmp->mm_channel = (short)c;
10037 		mutex_init(&mmp->mm_mutex, NULL, MUTEX_DRIVER, NULL);
10038 		mmp->mm_domid = (short)domid;
10039 		mmp->mm_type = mbx;
10040 	}
10041 	mmp = cmp;
10042 	/*
10043 	 * The actual SMR mailbox (mmp->mm_smr_mboxp) gets setup
10044 	 * when the SMR is setup.
10045 	 */
10046 
10047 	return (mmp);
10048 }
10049 
10050 static void
10051 idn_mainmbox_reset(int domid, idn_mainmbox_t *cmp)
10052 {
10053 	idn_mainmbox_t	*mmp;
10054 	int		c;
10055 	procname_t	proc = "idn_mainmbox_reset";
10056 
10057 	ASSERT(IDN_DLOCK_IS_EXCL(domid));
10058 
10059 	PR_PROTO("%s: reseting main %s mailbox for domain %d\n",
10060 	    proc, IDNMBOX_IS_RECV(cmp->mm_type) ? "RECV" : "SEND", domid);
10061 
10062 	for (c = 0; c < IDN_MAX_NETS; c++) {
10063 		mmp = &cmp[c];
10064 
10065 		mmp->mm_channel = (short)c;
10066 		mmp->mm_domid = (short)domid;
10067 		mmp->mm_count = 0;
10068 		mmp->mm_flags = 0;
10069 		mmp->mm_qiget = mmp->mm_qiput = 0;
10070 		mmp->mm_csp = NULL;
10071 		ASSERT(mmp->mm_type == cmp->mm_type);
10072 	}
10073 }
10074 
10075 void
10076 idn_mainmbox_deinit(int domid, idn_mainmbox_t *mmp)
10077 {
10078 	procname_t	proc = "idn_mainmbox_deinit";
10079 
10080 	ASSERT(IDN_DLOCK_IS_HELD(domid));
10081 
10082 	PR_PROTO("%s: deinitializing main %s mailbox for domain %d\n",
10083 	    proc, IDNMBOX_IS_RECV(mmp->mm_type) ? "RECV" : "SEND", domid);
10084 
10085 	ASSERT(idn_domain_is_registered(domid, -1, NULL) == 0);
10086 
10087 	FREESTRUCT(mmp, idn_mainmbox_t, IDN_MAX_NETS);
10088 }
10089 
10090 static void
10091 idn_mainmbox_activate(int domid)
10092 {
10093 	register int	c;
10094 	idn_domain_t	*dp = &idn_domain[domid];
10095 	procname_t	proc = "idn_mainmbox_activate";
10096 
10097 	ASSERT(IDN_DLOCK_IS_HELD(domid));
10098 
10099 	PR_PROTO("%s:%d: activating main mailbox\n", proc, domid);
10100 
10101 	for (c = 0; c < IDN_MAX_NETS; c++)
10102 		idn_mainmbox_chan_register(domid, &dp->dmbox.m_send[c],
10103 		    &dp->dmbox.m_recv[c], c);
10104 }
10105 
10106 /*
10107  * Called upon disabling the SMR to deactivate all the mailboxes
10108  * so that they no longer reference the SMR that's going away.
10109  *
10110  * stopall - Indicates to stop all channel services, across the board.
10111  */
10112 static void
10113 idn_mainmbox_deactivate(ushort_t domset)
10114 {
10115 	int		svr_count;
10116 	procname_t	proc = "idn_mainmbox_deactivate";
10117 
10118 
10119 	if (domset == 0)
10120 		return;
10121 
10122 	PR_PROTO("%s: %s deactivating main mailboxes for domset 0x%x\n",
10123 	    proc, (domset == (ushort_t)-1) ? "STOP-ALL" : "NORMAL", domset);
10124 
10125 	svr_count = idn_mainmbox_chan_unregister(domset, -1);
10126 
10127 	PR_PROTO("%s: deactivated %d chansvrs (domset 0x%x)\n",
10128 	    proc, svr_count, domset);
10129 }
10130 
10131 static void
10132 idn_mainmbox_chan_register(int domid, idn_mainmbox_t *send_mmp,
10133     idn_mainmbox_t *recv_mmp, int channel)
10134 {
10135 	ASSERT(IDN_DLOCK_IS_HELD(domid));
10136 
10137 	/*
10138 	 * Obtain receive mailbox lock first.
10139 	 */
10140 	mutex_enter(&recv_mmp->mm_mutex);
10141 	mutex_enter(&send_mmp->mm_mutex);
10142 
10143 	ASSERT(recv_mmp->mm_channel == (short)channel);
10144 	ASSERT(send_mmp->mm_channel == (short)channel);
10145 
10146 	recv_mmp->mm_csp = &idn.chan_servers[channel];
10147 	recv_mmp->mm_count = 0;
10148 	recv_mmp->mm_dropped = 0;
10149 	recv_mmp->mm_flags = 0;
10150 
10151 	send_mmp->mm_csp = &idn.chan_servers[channel];
10152 	send_mmp->mm_count = 0;
10153 	send_mmp->mm_dropped = 0;
10154 	send_mmp->mm_flags = 0;
10155 
10156 	mutex_exit(&send_mmp->mm_mutex);
10157 	mutex_exit(&recv_mmp->mm_mutex);
10158 
10159 	/*
10160 	 * We have to add ourselves to the respective
10161 	 * channel server's service table.
10162 	 * Note that the channel may not necessarily be
10163 	 * active at this time.
10164 	 */
10165 	ASSERT(idn.chan_servers);
10166 	/*
10167 	 * Have to get the channel server under
10168 	 * control so we can add ourselves.
10169 	 * Returns w/c_mutex.
10170 	 */
10171 	IDN_CHAN_LOCK_GLOBAL(&idn.chan_servers[channel]);
10172 	/*
10173 	 * Add the following domain (mailbox) for monitoring
10174 	 * by the respective channel server.
10175 	 */
10176 	idn_chan_addmbox(channel, DOMAINSET(domid));
10177 
10178 	IDN_CHAN_UNLOCK_GLOBAL(&idn.chan_servers[channel]);
10179 }
10180 
10181 /*
10182  * Unregister the given domain from the specified channel(s) for monitoring.
10183  */
10184 static int
10185 idn_mainmbox_chan_unregister(ushort_t domset, int channel)
10186 {
10187 	int		c, dd_count;
10188 	int		min_chan, max_chan;
10189 	procname_t	proc = "idn_mainmbox_chan_unregister";
10190 
10191 	PR_CHAN("%s: deactivating main mailboxes (channel %d) "
10192 	    "for domset 0x%x\n", proc, channel, domset);
10193 
10194 	if (channel == -1) {
10195 		min_chan = 0;
10196 		max_chan = IDN_MAX_NETS - 1;
10197 	} else {
10198 		min_chan = max_chan = channel;
10199 	}
10200 	/*
10201 	 * Point all the data dispatchers to the same morgue
10202 	 * so we can kill them all at once.
10203 	 */
10204 	dd_count = 0;
10205 	for (c = min_chan; c <= max_chan; c++) {
10206 
10207 		/*
10208 		 * Have to get the channel server under
10209 		 * control so we can remove ourselves.
10210 		 * Returns w/c_mutex held.
10211 		 */
10212 		IDN_CHAN_LOCK_GLOBAL(&idn.chan_servers[c]);
10213 		/*
10214 		 * Delete the following domain (mailbox) from
10215 		 * monitoring by the respective channel server.
10216 		 */
10217 		idn_chan_delmbox(c, (ushort_t)domset);
10218 
10219 		IDN_CHAN_UNLOCK_GLOBAL(&idn.chan_servers[c]);
10220 		dd_count++;
10221 	}
10222 	PR_CHAN("%s: deactivated %d channel mboxes for domset 0x%x, chan %d\n",
10223 	    proc, dd_count, domset, channel);
10224 	return (dd_count);
10225 }
10226 
10227 /*
10228  * Check if the given domain is registered with the given channel(s).
10229  */
10230 int
10231 idn_domain_is_registered(int domid, int channel, idn_chanset_t *chansetp)
10232 {
10233 	int		regcount;
10234 	int		c, min_chan, max_chan;
10235 	idn_chanset_t	chanset;
10236 	procname_t	proc = "idn_domain_is_registered";
10237 
10238 	CHANSET_ZERO(chanset);
10239 
10240 	if (idn.chan_servers == NULL) {
10241 		PR_CHAN("%s: idn.chan_servers == NULL!!\n", proc);
10242 		return (0);
10243 	}
10244 
10245 	if (channel == -1) {
10246 		min_chan = 0;
10247 		max_chan = IDN_MAX_NETS - 1;
10248 	} else {
10249 		min_chan = max_chan = channel;
10250 	}
10251 
10252 	regcount = 0;
10253 
10254 	for (c = min_chan; c <= max_chan; c++) {
10255 		idn_chansvr_t	*csp;
10256 
10257 		csp = &idn.chan_servers[c];
10258 		IDN_CHAN_LOCK_SEND(csp);
10259 		/*
10260 		 * Don't really need recv side lock since registeration
10261 		 * can't change while we're holding send side.
10262 		 * No need to wait for send side to actually suspend
10263 		 * since all we want to do is prevent the registered
10264 		 * information from changing.
10265 		 */
10266 		if (IDN_CHAN_DOMAIN_IS_REGISTERED(csp, domid)) {
10267 			regcount++;
10268 			CHANSET_ADD(chanset, c);
10269 		}
10270 
10271 		IDN_CHAN_UNLOCK_SEND(csp);
10272 	}
10273 
10274 	PR_CHAN("%s: domid %d mbox reg'd with %d channels [0x%x] (req=%d)\n",
10275 	    proc, domid, regcount, chanset, channel);
10276 
10277 	if (chansetp)
10278 		*chansetp = chanset;
10279 
10280 	return (regcount);
10281 }
10282 
10283 static int
10284 idn_mainmbox_flush(int domid, idn_mainmbox_t *mmp)
10285 {
10286 	register int		qi;
10287 	register idn_mboxmsg_t	*mqp;
10288 	int		total_count = 0;
10289 	int		c, count;
10290 	int		mbox_type;
10291 	char		*mbox_str;
10292 	int		lost_io, total_lost_io = 0;
10293 	idn_chanset_t	chanset;
10294 	procname_t	proc = "idn_mainmbox_flush";
10295 
10296 
10297 	if (mmp == NULL)
10298 		return (0);
10299 
10300 	CHANSET_ZERO(chanset);
10301 
10302 	mbox_type = mmp->mm_type;
10303 	ASSERT((mbox_type == IDNMMBOX_TYPE_SEND) ||
10304 	    (mbox_type == IDNMMBOX_TYPE_RECV));
10305 
10306 	mbox_str = (mbox_type == IDNMMBOX_TYPE_SEND) ? "SEND" : "RECV";
10307 
10308 	/*
10309 	 * Determine which channels this domain is registered
10310 	 * with.  If he's not registered with any, then we
10311 	 * can't touch the SMR.
10312 	 */
10313 	(void) idn_domain_is_registered(domid, -1, &chanset);
10314 
10315 	for (c = 0; c < IDN_MAX_NETS; c++) {
10316 		ushort_t	mbox_csum;
10317 
10318 		if (mmp[c].mm_smr_mboxp == NULL)
10319 			continue;
10320 		mutex_enter(&mmp[c].mm_mutex);
10321 		ASSERT(mmp[c].mm_type == mbox_type);
10322 		if (CHAN_IN_SET(chanset, c) == 0) {
10323 			/*
10324 			 * Domain is no longer registered.
10325 			 * DON'T TOUCH THE SMR - IT'S POISON!
10326 			 */
10327 			if (mmp[c].mm_smr_mboxp) {
10328 				PR_CHAN("%s:%d:%s: domain unregistered "
10329 				    "w/chan %d - DUMPING SMR reference\n",
10330 				    proc, domid, mbox_str, c);
10331 				lost_io = IDN_MMBOXINDEX_DIFF(mmp[c].mm_qiput,
10332 				    mmp[c].mm_qiget);
10333 #ifdef DEBUG
10334 				if (mbox_type == IDNMMBOX_TYPE_RECV) {
10335 					PR_CHAN("%s:%d:%s: blowing away %d "
10336 					    "incoming pkts\n",
10337 					    proc, domid, mbox_str, lost_io);
10338 				} else {
10339 					PR_CHAN("%s:%d:%s: blowing away %d/%d "
10340 					    "outstanding pkts\n",
10341 					    proc, domid, mbox_str, lost_io,
10342 					    idn_domain[domid].dio);
10343 				}
10344 #endif /* DEBUG */
10345 			}
10346 			mmp[c].mm_qiput = mmp[c].mm_qiget = 0;
10347 			mmp[c].mm_smr_mboxp = NULL;
10348 			total_lost_io += lost_io;
10349 		}
10350 		if (mmp[c].mm_smr_mboxp) {
10351 			mbox_csum =
10352 			    IDN_CKSUM_MBOX(&mmp[c].mm_smr_mboxp->mt_header);
10353 			if (!VALID_NWRADDR(mmp[c].mm_smr_mboxp, 4) ||
10354 			    !VALID_MBOXHDR(&mmp[c].mm_smr_mboxp->mt_header,
10355 			    c, mbox_csum)) {
10356 				lost_io = IDN_MMBOXINDEX_DIFF(mmp[c].mm_qiput,
10357 				    mmp[c].mm_qiget);
10358 #ifdef DEBUG
10359 				if (mbox_type == IDNMMBOX_TYPE_RECV) {
10360 					PR_CHAN("%s:%d:%s: bad mbox.  blowing "
10361 					    "away %d incoming pkts\n",
10362 					    proc, domid, mbox_str, lost_io);
10363 				} else {
10364 					PR_CHAN("%s:%d:%s: bad mbox.  blowing "
10365 					    "away %d/%d outstanding pkts\n",
10366 					    proc, domid, mbox_str, lost_io,
10367 					    idn_domain[domid].dio);
10368 				}
10369 #endif /* DEBUG */
10370 				mmp[c].mm_smr_mboxp = NULL;
10371 				mmp[c].mm_qiput = mmp[c].mm_qiget = 0;
10372 				total_lost_io += lost_io;
10373 			}
10374 		}
10375 		if (mmp[c].mm_smr_mboxp == NULL) {
10376 			mutex_exit(&mmp[c].mm_mutex);
10377 			continue;
10378 		}
10379 		mqp = &mmp[c].mm_smr_mboxp->mt_queue[0];
10380 		qi = 0;
10381 		count = 0;
10382 		/*
10383 		 * It's quite possible the remote domain may be accessing
10384 		 * these mailbox entries at the exact same time we're
10385 		 * clearing the owner bit.  That's okay.  All we're trying
10386 		 * to do at this point is to minimize the number of packets
10387 		 * the remote domain might try to process unnecessarily.
10388 		 */
10389 		do {
10390 			if (mqp[qi].ms_owner)
10391 				count++;
10392 			mqp[qi].ms_owner = 0;
10393 			IDN_MMBOXINDEX_INC(qi);
10394 		} while (qi);
10395 
10396 		lost_io = IDN_MMBOXINDEX_DIFF(mmp[c].mm_qiput, mmp[c].mm_qiget);
10397 		total_lost_io += lost_io;
10398 
10399 		mmp[c].mm_qiput = mmp[c].mm_qiget = 0;
10400 		mmp[c].mm_smr_mboxp = NULL;
10401 		mutex_exit(&mmp[c].mm_mutex);
10402 
10403 		total_count += count;
10404 
10405 		PR_CHAN("%s:%d:%s: flushed out %d mbox entries for chan %d\n",
10406 		    proc, domid, mbox_str, count, c);
10407 	}
10408 
10409 	if (total_lost_io && (mbox_type == IDNMMBOX_TYPE_SEND)) {
10410 		int	lost_bufs;
10411 		/*
10412 		 * If we lost all our outstanding I/O.  We could
10413 		 * possible could have slabs now with mistakenly
10414 		 * outstanding I/O buffers.  Need to clean them up.
10415 		 * Clean up of leftovers our self.
10416 		 */
10417 		lost_bufs = smr_buf_free_all(domid);
10418 
10419 		PR_CHAN("%s:%d:%s: flushed %d/%d buffers from slabs\n",
10420 		    proc, domid, mbox_str, lost_bufs, total_lost_io);
10421 	}
10422 
10423 	PR_CHAN("%s:%d:%s: flushed total of %d mailbox entries (lost %d)\n",
10424 	    proc, domid, mbox_str, total_count, total_lost_io);
10425 
10426 	return (total_count);
10427 }
10428 
10429 void
10430 idn_chanserver_bind(int net, int cpuid)
10431 {
10432 	int		ocpuid;
10433 	cpu_t		*cp;
10434 	idn_chansvr_t	*csp;
10435 	kthread_id_t	tp;
10436 	procname_t	proc = "idn_chanserver_bind";
10437 
10438 	csp = &idn.chan_servers[net];
10439 	IDN_CHAN_LOCK_GLOBAL(csp);
10440 
10441 	mutex_enter(&cpu_lock);		/* protect checking cpu_ready_set */
10442 	ocpuid = csp->ch_bound_cpuid;
10443 	cp = cpu_get(cpuid);
10444 	if ((cpuid != -1) && ((cp == NULL) || !cpu_is_online(cp))) {
10445 		mutex_exit(&cpu_lock);
10446 		cmn_err(CE_WARN,
10447 		    "IDN: 239: invalid CPU ID (%d) specified for "
10448 		    "IDN net %d",
10449 		    cpuid, net);
10450 		IDN_CHAN_UNLOCK_GLOBAL(csp);
10451 		return;
10452 	}
10453 	if ((tp = csp->ch_recv_threadp) == NULL) {
10454 		/*
10455 		 * Thread is not yet active.  Set ch_bound_cpuid
10456 		 * so when thread activates it will automatically
10457 		 * bind itself.
10458 		 */
10459 		csp->ch_bound_cpuid = -1;
10460 		csp->ch_bound_cpuid_pending = cpuid;
10461 	} else {
10462 		if (ocpuid != -1) {
10463 			thread_affinity_clear(tp);
10464 			csp->ch_bound_cpuid = -1;
10465 		}
10466 		if (cpuid >= 0) {
10467 			thread_affinity_set(tp, cpuid);
10468 			csp->ch_bound_cpuid = cpuid;
10469 		}
10470 		csp->ch_bound_cpuid_pending = -1;
10471 	}
10472 	mutex_exit(&cpu_lock);
10473 
10474 	PR_CHAN("%s: bound net/channel (%d) from cpuid %d to%scpuid %d\n",
10475 	    proc, net, ocpuid, tp ? " " : " (pending) ", cpuid);
10476 
10477 	IDN_CHAN_UNLOCK_GLOBAL(csp);
10478 }
10479 
10480 #ifdef DEBUG
10481 static idn_mboxhdr_t	*prev_mhp[IDN_MAXMAX_NETS];
10482 #endif /* DEBUG */
10483 /*
10484  * Get access to the respective channel server's synchronization
10485  * header which resides in SMR space.
10486  */
10487 static idn_mboxhdr_t *
10488 idn_chan_server_syncheader(int channel)
10489 {
10490 	idn_domain_t	*ldp = &idn_domain[idn.localid];
10491 	idn_mboxtbl_t	*mtp;
10492 	idn_mboxhdr_t	*mhp;
10493 	ushort_t	mbox_csum;
10494 	procname_t	proc = "idn_chan_server_syncheader";
10495 
10496 	ASSERT(IDN_CHAN_RECV_IS_LOCKED(&idn.chan_servers[channel]));
10497 
10498 	IDN_DLOCK_SHARED(idn.localid);
10499 
10500 	if (ldp->dmbox.m_tbl == NULL) {
10501 		PR_CHAN("%s: local dmbox.m_tbl == NULL\n", proc);
10502 		IDN_DUNLOCK(idn.localid);
10503 		return (NULL);
10504 	}
10505 
10506 	mtp = IDN_MBOXTBL_PTR_CHAN(ldp->dmbox.m_tbl, channel);
10507 	mhp = &mtp->mt_header;
10508 	mbox_csum = IDN_CKSUM_MBOX(&mtp->mt_header);
10509 
10510 #ifdef DEBUG
10511 	if (mhp != prev_mhp[channel]) {
10512 		prev_mhp[channel] = mhp;
10513 		PR_CHAN("%s: chan_server (%d) cookie = 0x%x (exp 0x%x)\n",
10514 		    proc, channel, IDN_GET_MBOXHDR_COOKIE(mhp),
10515 		    IDN_MAKE_MBOXHDR_COOKIE(0, 0, channel));
10516 		PR_CHAN("%s: chan_server (%d) actv_ptr = 0x%x (exp 0x%x)\n",
10517 		    proc, channel, mhp->mh_svr_active_ptr,
10518 		    IDN_ADDR2OFFSET(&mhp->mh_svr_active));
10519 		PR_CHAN("%s: chan_server (%d) ready_ptr = 0x%x (exp 0x%x)\n",
10520 		    proc, channel, mhp->mh_svr_ready_ptr,
10521 		    IDN_ADDR2OFFSET(&mhp->mh_svr_ready));
10522 		PR_CHAN("%s: chan_server (%d) mbox_cksum = 0x%x (exp 0x%x)\n",
10523 		    proc, channel, (int)mhp->mh_cksum, (int)mbox_csum);
10524 	}
10525 #endif /* DEBUG */
10526 
10527 	if ((IDN_ADDR2OFFSET(&mhp->mh_svr_active) !=
10528 	    mhp->mh_svr_active_ptr) ||
10529 	    (IDN_ADDR2OFFSET(&mhp->mh_svr_ready) != mhp->mh_svr_ready_ptr) ||
10530 	    !VALID_MBOXHDR(mhp, channel, mbox_csum)) {
10531 		idn_chansvr_t	*csp;
10532 
10533 		csp = &idn.chan_servers[channel];
10534 		if (IDN_CHANNEL_IS_RECV_CORRUPTED(csp) == 0) {
10535 			IDN_CHANSVC_MARK_RECV_CORRUPTED(csp);
10536 
10537 			cmn_err(CE_WARN,
10538 			    "IDN: 240: (channel %d) SMR CORRUPTED "
10539 			    "- RELINK", channel);
10540 			cmn_err(CE_CONT,
10541 			    "IDN: 240: (channel %d) cookie "
10542 			    "(expected 0x%x, actual 0x%x)\n",
10543 			    channel,
10544 			    IDN_MAKE_MBOXHDR_COOKIE(0, 0, channel),
10545 			    mhp->mh_cookie);
10546 			cmn_err(CE_CONT,
10547 			    "IDN: 240: (channel %d) actv_flg "
10548 			    "(expected 0x%x, actual 0x%x)\n",
10549 			    channel, mhp->mh_svr_active_ptr,
10550 			    IDN_ADDR2OFFSET(&mhp->mh_svr_active));
10551 			cmn_err(CE_CONT,
10552 			    "IDN: 240: (channel %d) ready_flg "
10553 			    "(expected 0x%x, actual 0x%x)\n",
10554 			    channel, mhp->mh_svr_ready_ptr,
10555 			    IDN_ADDR2OFFSET(&mhp->mh_svr_ready));
10556 		}
10557 
10558 		mhp = NULL;
10559 	}
10560 	IDN_DUNLOCK(idn.localid);
10561 
10562 	PR_CHAN("%s: channel(%d) mainhp = 0x%p\n", proc, channel, (void *)mhp);
10563 
10564 	return (mhp);
10565 }
10566 
10567 #define	CHANSVR_SYNC_CACHE(csp, mmp, chan) \
10568 { \
10569 	ASSERT(IDN_CHAN_RECV_IS_LOCKED(csp)); \
10570 	if ((csp)->ch_recv_changed) { \
10571 		register int _d; \
10572 		(csp)->ch_recv_scanset = (csp)->ch_recv_scanset_pending; \
10573 		(csp)->ch_recv_domset = (csp)->ch_recv_domset_pending; \
10574 		for (_d = 0; _d < MAX_DOMAINS; _d++) { \
10575 			if (DOMAIN_IN_SET((csp)->ch_recv_domset, _d)) { \
10576 				(mmp)[_d] = \
10577 				    &idn_domain[_d].dmbox.m_recv[chan]; \
10578 			} else { \
10579 				(mmp)[_d] = NULL; \
10580 			} \
10581 		} \
10582 		(csp)->ch_recv_changed = 0; \
10583 	} \
10584 }
10585 #define	CHANSVR_NEXT_DOMID(csp, i, d) \
10586 { \
10587 	(i) = ((i) + 1) & (MAX_DOMAINS - 1); \
10588 	(d) = (int)(((csp)->ch_recv_scanset >> ((i) << 2)) & 0xf); \
10589 }
10590 #define	CHANSVR_RESET_INDEX(i)	((i) = -1)
10591 
10592 #ifdef DEBUG
10593 static idn_mainmbox_t	*Mmp[IDN_MAXMAX_NETS][MAX_DOMAINS];
10594 #endif /* DEBUG */
10595 
10596 static void
10597 idn_chan_server(idn_chansvr_t **cspp)
10598 {
10599 	idn_mboxhdr_t	*mainhp;
10600 	register idn_chansvr_t		*csp;
10601 	register idn_mboxmsg_t		*mqp;
10602 #ifdef DEBUG
10603 	idn_mainmbox_t			**mmp;
10604 #else
10605 	idn_mainmbox_t			*mmp[MAX_DOMAINS];
10606 #endif /* DEBUG */
10607 	register int	qi;
10608 	struct idn	*sip;
10609 	int		channel;
10610 	int		cpuid;
10611 	int		empty;
10612 	int		tot_pktcount, tot_dropcount;
10613 	register int	index;
10614 	register int	domid;
10615 	register int	idleloops;
10616 	procname_t	proc = "idn_chan_server";
10617 
10618 
10619 #ifdef DEBUG
10620 	mmp = &Mmp[(*cspp)->ch_id][0];
10621 	bzero(mmp, MAX_DOMAINS * sizeof (idn_mainmbox_t *));
10622 #else /* DEBUG */
10623 	bzero(mmp, sizeof (mmp));
10624 #endif /* DEBUG */
10625 
10626 	tot_pktcount = tot_dropcount = 0;
10627 
10628 	ASSERT(cspp && *cspp);
10629 
10630 	csp = *cspp;
10631 	channel = csp->ch_id;
10632 	sip = IDN_INST2SIP(channel);
10633 	ASSERT(sip);
10634 
10635 	PR_CHAN("%s: CHANNEL SERVER (channel %d) GOING ACTIVE...\n",
10636 	    proc, channel);
10637 
10638 	IDN_CHAN_LOCK_RECV(csp);
10639 	IDN_CHAN_RECV_INPROGRESS(csp);
10640 	ASSERT(csp->ch_recv_threadp == curthread);
10641 	mutex_enter(&cpu_lock);
10642 	if ((cpuid = csp->ch_bound_cpuid_pending) != -1) {
10643 		cpu_t	*cp = cpu_get(cpuid);
10644 		/*
10645 		 * We've been requested to bind to
10646 		 * a particular cpu.
10647 		 */
10648 		if ((cp == NULL) || !cpu_is_online(cp)) {
10649 			/*
10650 			 * Cpu seems to have gone away or gone offline
10651 			 * since originally requested.
10652 			 */
10653 			mutex_exit(&cpu_lock);
10654 			cmn_err(CE_WARN,
10655 			    "IDN: 239: invalid CPU ID (%d) specified for "
10656 			    "IDN net %d",
10657 			    cpuid, channel);
10658 		} else {
10659 			csp->ch_bound_cpuid = cpuid;
10660 			affinity_set(csp->ch_bound_cpuid);
10661 			mutex_exit(&cpu_lock);
10662 		}
10663 		csp->ch_bound_cpuid_pending = -1;
10664 	} else {
10665 		mutex_exit(&cpu_lock);
10666 	}
10667 	if (csp->ch_bound_cpuid != -1) {
10668 		PR_CHAN("%s: thread bound to cpuid %d\n",
10669 		    proc, csp->ch_bound_cpuid);
10670 	}
10671 	/*
10672 	 * Only the first (main) mbox header is used for
10673 	 * synchronization with data delivery since there is
10674 	 * only data server for all mailboxes for this
10675 	 * given channel.
10676 	 */
10677 	CHANSVR_SYNC_CACHE(csp, mmp, channel);
10678 
10679 	mainhp = ((csp->ch_recv_domcount > 0) &&
10680 	    IDN_CHANNEL_IS_RECV_ACTIVE(csp))
10681 	    ? idn_chan_server_syncheader(channel) : NULL;
10682 
10683 	if (mainhp && IDN_CHANNEL_IS_RECV_ACTIVE(csp))
10684 		mainhp->mh_svr_active = 1;
10685 
10686 	ASSERT(csp->ch_recv_domcount ?
10687 	    (csp->ch_recv_scanset && csp->ch_recv_domset) : 1);
10688 
10689 	IDN_CHAN_UNLOCK_RECV(csp);
10690 
10691 	empty = 0;
10692 	idleloops = 0;
10693 	CHANSVR_RESET_INDEX(index);
10694 
10695 	/*
10696 	 * ---------------------------------------------
10697 	 */
10698 	/*CONSTCOND*/
10699 	while (1) {
10700 		register int	pktcount;
10701 		register int	dropcount;
10702 		ushort_t		mbox_csum;
10703 		idn_mboxtbl_t	*smr_mboxp;	/* points to SMR space */
10704 		register smr_offset_t	bufoffset;
10705 #ifdef DEBUG
10706 		register smr_pkthdr_t	*hdrp;
10707 		idn_netaddr_t		netaddr;
10708 #endif /* DEBUG */
10709 
10710 		/*
10711 		 * Speed through and find the next available domid.
10712 		 */
10713 		CHANSVR_NEXT_DOMID(csp, index, domid);
10714 
10715 		if (!index) {
10716 			/*
10717 			 * We only check state changes when
10718 			 * we wrap around.  Done for performance.
10719 			 */
10720 			if (!IDN_CHANNEL_IS_RECV_ACTIVE(csp) ||
10721 			    csp->ch_recv.c_checkin ||
10722 			    (idn.state != IDNGS_ONLINE)) {
10723 
10724 				PR_DATA("%s: (channel %d) %s\n",
10725 				    proc, channel,
10726 				    IDN_CHANNEL_IS_DETACHED(csp)
10727 				    ? "DEAD" :
10728 				    IDN_CHANNEL_IS_PENDING(csp)
10729 				    ? "IDLED" :
10730 				    IDN_CHANNEL_IS_ACTIVE(csp)
10731 				    ? "ACTIVE" : "DISABLED");
10732 				goto cc_sleep;
10733 			}
10734 		}
10735 		if (csp->ch_recv.c_checkin)
10736 			goto cc_sleep;
10737 
10738 		if (empty == csp->ch_recv_domcount) {
10739 			empty = 0;
10740 			goto cc_slowdown;
10741 		}
10742 
10743 		ASSERT(mmp[domid] != NULL);
10744 
10745 		mutex_enter(&mmp[domid]->mm_mutex);
10746 		if ((smr_mboxp = mmp[domid]->mm_smr_mboxp) == NULL) {
10747 			/*
10748 			 * Somebody is trying to shut things down.
10749 			 */
10750 			empty++;
10751 			mutex_exit(&mmp[domid]->mm_mutex);
10752 			continue;
10753 		}
10754 		ASSERT(mmp[domid]->mm_channel == (short)channel);
10755 		/*
10756 		 * We don't care if the mm_smr_mboxp is nullified
10757 		 * after this point.  The thread attempting to shut
10758 		 * us down has to formally pause this channel before
10759 		 * anything is official anyway.  So, we can continue
10760 		 * with our local SMR reference until the thread
10761 		 * shutting us down really stops us.
10762 		 *
10763 		 * Need to get the qiget index _before_ we drop the
10764 		 * lock since it might get flushed (idn_mainmbox_flush)
10765 		 * once we drop the mm_mutex.
10766 		 *
10767 		 * We prefer not to hold the mm_mutex across the
10768 		 * idn_recv_mboxdata() call since that may be time-
10769 		 * consuming.
10770 		 */
10771 		qi  = mmp[domid]->mm_qiget;
10772 
10773 		/*
10774 		 * Check the mailbox header if checksum is turned on.
10775 		 */
10776 		mbox_csum = IDN_CKSUM_MBOX(&smr_mboxp->mt_header);
10777 		if (!VALID_MBOXHDR(&smr_mboxp->mt_header, channel, mbox_csum)) {
10778 			IDN_KSTAT_INC(sip, si_mboxcrc);
10779 			IDN_KSTAT_INC(sip, si_ierrors);
10780 			if (!(mmp[domid]->mm_flags & IDNMMBOX_FLAG_CORRUPTED)) {
10781 				cmn_err(CE_WARN,
10782 				    "IDN: 241: [recv] (domain %d, "
10783 				    "channel %d) SMR CORRUPTED - RELINK",
10784 				    domid, channel);
10785 				mmp[domid]->mm_flags |= IDNMMBOX_FLAG_CORRUPTED;
10786 			}
10787 			empty = 0;
10788 			mutex_exit(&mmp[domid]->mm_mutex);
10789 			goto cc_sleep;
10790 		}
10791 		mutex_exit(&mmp[domid]->mm_mutex);
10792 		mqp = &smr_mboxp->mt_queue[0];
10793 
10794 		pktcount = dropcount = 0;
10795 
10796 		if (mqp[qi].ms_owner == 0)
10797 			goto cc_next;
10798 
10799 		bufoffset = IDN_BFRAME2OFFSET(mqp[qi].ms_bframe);
10800 
10801 		if (!VALID_NWROFFSET(bufoffset, IDN_SMR_BUFSIZE)) {
10802 			/* ASSERT(0); */
10803 			mqp[qi].ms_flag |= IDN_MBOXMSG_FLAG_ERR_BADOFFSET;
10804 			mqp[qi].ms_owner = 0;
10805 			IDN_MMBOXINDEX_INC(qi);
10806 			dropcount++;
10807 
10808 			IDN_KSTAT_INC(sip, si_smraddr);
10809 			IDN_KSTAT_INC(sip, si_ierrors);
10810 
10811 		} else {
10812 			PR_DATA("%s: (channel %d) pkt (off 0x%x, "
10813 			    "qiget %d) from domain %d\n",
10814 			    proc, channel, bufoffset, qi, domid);
10815 #ifdef DEBUG
10816 
10817 			hdrp = IDN_BUF2HDR(IDN_OFFSET2ADDR(bufoffset));
10818 			netaddr.netaddr = hdrp->b_netaddr;
10819 			ASSERT(netaddr.net.chan == (ushort_t)channel);
10820 #endif /* DEBUG */
10821 
10822 			if (idn_recv_mboxdata(channel,
10823 			    IDN_OFFSET2ADDR(bufoffset)) < 0) {
10824 				mutex_enter(&mmp[domid]->mm_mutex);
10825 				if (!(mmp[domid]->mm_flags &
10826 				    IDNMMBOX_FLAG_CORRUPTED)) {
10827 					cmn_err(CE_WARN,
10828 					    "IDN: 241: [recv] (domain "
10829 					    "%d, channel %d) SMR "
10830 					    "CORRUPTED - RELINK",
10831 					    domid, channel);
10832 					mmp[domid]->mm_flags |=
10833 					    IDNMMBOX_FLAG_CORRUPTED;
10834 				}
10835 				mutex_exit(&mmp[domid]->mm_mutex);
10836 			}
10837 
10838 			mqp[qi].ms_owner = 0;
10839 			IDN_MMBOXINDEX_INC(qi);
10840 			pktcount++;
10841 		}
10842 
10843 cc_next:
10844 
10845 		mutex_enter(&mmp[domid]->mm_mutex);
10846 		if (mmp[domid]->mm_smr_mboxp) {
10847 			if (dropcount)
10848 				mmp[domid]->mm_dropped += dropcount;
10849 			mmp[domid]->mm_qiget = qi;
10850 			mmp[domid]->mm_count += pktcount;
10851 		}
10852 		mutex_exit(&mmp[domid]->mm_mutex);
10853 
10854 		if (pktcount == 0) {
10855 			empty++;
10856 		} else {
10857 			csp->ch_recv_waittime = IDN_NETSVR_WAIT_MIN;
10858 			empty = 0;
10859 			idleloops = 0;
10860 
10861 			PR_DATA("%s: (channel %d) dom=%d, pktcnt=%d\n",
10862 			    proc, channel, domid, pktcount);
10863 		}
10864 
10865 		continue;
10866 
10867 cc_slowdown:
10868 
10869 #ifdef DEBUG
10870 		if (idleloops == 0) {
10871 			PR_DATA("%s: (channel %d) going SOFT IDLE...\n",
10872 			    proc, channel);
10873 		}
10874 #endif /* DEBUG */
10875 		if (idleloops++ < IDN_NETSVR_SPIN_COUNT) {
10876 			/*
10877 			 * At this level we only busy-wait.
10878 			 * Get back into action.
10879 			 */
10880 			continue;
10881 		}
10882 		idleloops = 0;
10883 
10884 cc_sleep:
10885 
10886 		if (mainhp)
10887 			mainhp->mh_svr_active = 0;
10888 
10889 		IDN_CHAN_LOCK_RECV(csp);
10890 
10891 cc_die:
10892 
10893 		ASSERT(IDN_CHAN_RECV_IS_LOCKED(csp));
10894 
10895 		if (!IDN_CHANNEL_IS_RECV_ACTIVE(csp) &&
10896 		    IDN_CHANNEL_IS_DETACHED(csp)) {
10897 			/*
10898 			 * Time to die...
10899 			 */
10900 			PR_CHAN("%s: (channel %d) serviced %d "
10901 			    "packets, drop = %d\n", proc, channel,
10902 			    tot_pktcount, tot_dropcount);
10903 			PR_CHAN("%s: (channel %d) TERMINATING\n",
10904 			    proc, channel);
10905 			PR_CHAN("%s: (channel %d) ch_morguep = %p\n",
10906 			    proc, channel, (void *)csp->ch_recv_morguep);
10907 
10908 			csp->ch_recv_threadp = NULL;
10909 #ifdef DEBUG
10910 			for (index = 0; index < csp->ch_recv_domcount;
10911 			    index++) {
10912 				if ((int)((csp->ch_recv_scanset >>
10913 				    (index*4)) & 0xf) == domid) {
10914 					PR_DATA("%s: WARNING (channel %d) "
10915 					    "DROPPING domid %d...\n",
10916 					    proc, channel, domid);
10917 				}
10918 			}
10919 #endif /* DEBUG */
10920 			IDN_CHAN_RECV_DONE(csp);
10921 
10922 			sema_v(csp->ch_recv_morguep);
10923 
10924 			IDN_CHAN_UNLOCK_RECV(csp);
10925 
10926 			thread_exit();
10927 			/* not reached */
10928 		}
10929 
10930 		do {
10931 			if (IDN_CHANNEL_IS_DETACHED(csp)) {
10932 				PR_CHAN("%s: (channel %d) going to DIE...\n",
10933 				    proc, channel);
10934 				goto cc_die;
10935 			}
10936 #ifdef DEBUG
10937 			if (IDN_CHANNEL_IS_RECV_ACTIVE(csp) &&
10938 			    (csp->ch_recv_waittime <= IDN_NETSVR_WAIT_MAX)) {
10939 				PR_CHAN("%s: (channel %d) going SOFT IDLE "
10940 				    "(waittime = %d ticks)...\n",
10941 				    proc, channel,
10942 				    csp->ch_recv_waittime);
10943 			} else {
10944 				PR_CHAN("%s: (channel %d) going "
10945 				    "HARD IDLE...\n", proc, channel);
10946 			}
10947 #endif /* DEBUG */
10948 			IDN_CHAN_RECV_DONE(csp);
10949 
10950 			/*
10951 			 * If we're being asked to check-in then
10952 			 * go into a hard sleep.  Want to give the
10953 			 * thread requesting us to checkin a chance.
10954 			 */
10955 			while (csp->ch_recv.c_checkin)
10956 				cv_wait(&csp->ch_recv_cv,
10957 				    &csp->ch_recv.c_mutex);
10958 
10959 			if (csp->ch_recv_waittime > IDN_NETSVR_WAIT_MAX)
10960 				cv_wait(&csp->ch_recv_cv,
10961 				    &csp->ch_recv.c_mutex);
10962 			else
10963 				(void) cv_reltimedwait(&csp->ch_recv_cv,
10964 				    &csp->ch_recv.c_mutex,
10965 				    csp->ch_recv_waittime, TR_CLOCK_TICK);
10966 
10967 			IDN_CHAN_RECV_INPROGRESS(csp);
10968 
10969 			IDN_KSTAT_INC(sip, si_sigsvr);
10970 
10971 			if (csp->ch_recv_waittime <= IDN_NETSVR_WAIT_MAX)
10972 				csp->ch_recv_waittime <<=
10973 				    IDN_NETSVR_WAIT_SHIFT;
10974 
10975 		} while (!IDN_CHANNEL_IS_RECV_ACTIVE(csp));
10976 
10977 		/*
10978 		 * Before we see the world (and touch SMR space),
10979 		 * see if we've been told to die.
10980 		 */
10981 		mainhp = NULL;
10982 		/*
10983 		 * The world may have changed since we were
10984 		 * asleep.  Need to resync cache and check for a
10985 		 * new syncheader.
10986 		 *
10987 		 * Reset chansvr cache against any changes in
10988 		 * mbox fields we need (mm_qiget).
10989 		 */
10990 		CHANSVR_SYNC_CACHE(csp, mmp, channel);
10991 		if (csp->ch_recv_domcount <= 0) {
10992 			/*
10993 			 * Everybody disappeared on us.
10994 			 * Go back to sleep.
10995 			 */
10996 			goto cc_die;
10997 		}
10998 		ASSERT(csp->ch_recv_scanset && csp->ch_recv_domset);
10999 
11000 		mainhp = idn_chan_server_syncheader(channel);
11001 		if (mainhp == NULL) {
11002 			/*
11003 			 * Bummer...we're idling...
11004 			 */
11005 			goto cc_die;
11006 		}
11007 
11008 		mainhp->mh_svr_active = 1;
11009 
11010 		IDN_CHAN_UNLOCK_RECV(csp);
11011 		/*
11012 		 * Reset the domid index after sleeping.
11013 		 */
11014 		CHANSVR_RESET_INDEX(index);
11015 
11016 		empty = 0;
11017 		idleloops = 0;
11018 	}
11019 }
11020 
11021 #if 0
11022 /*
11023  * We maintain a separate function for flushing the STREAMs
11024  * queue of a channel because it must be done outside the
11025  * context of the idn_chan_action routine.  The streams flush
11026  * cannot occur inline with the idn_chan_action because
11027  * the act of flushing may cause IDN send functions to be called
11028  * directly and thus locks to be obtained which could result
11029  * in deadlocks.
11030  */
11031 static void
11032 idn_chan_flush(idn_chansvr_t *csp)
11033 {
11034 	queue_t		*rq;
11035 	struct idn	*sip;
11036 	int		flush_type = 0;
11037 	idn_chaninfo_t	*csend, *crecv;
11038 	procname_t	proc = "idn_chan_flush";
11039 
11040 	csend = &csp->ch_send;
11041 	crecv = &csp->ch_recv;
11042 
11043 	mutex_enter(&crecv->c_mutex);
11044 	mutex_enter(&csend->c_mutex);
11045 
11046 	if (crecv->c_state & IDN_CHANSVC_STATE_FLUSH)
11047 		flush_type |= FLUSHR;
11048 
11049 	if (csend->c_state & IDN_CHANSVC_STATE_FLUSH)
11050 		flush_type |= FLUSHW;
11051 
11052 	if (flush_type) {
11053 		rq = NULL;
11054 		rw_enter(&idn.struprwlock, RW_READER);
11055 		if ((sip = IDN_INST2SIP(csp->ch_id)) != NULL)
11056 			rq = sip->si_ipq;
11057 		rw_exit(&idn.struprwlock);
11058 		if (rq) {
11059 			/*
11060 			 * Flush the STREAM if possible
11061 			 * to get the channel server coherent
11062 			 * enough to respond to us.
11063 			 */
11064 			PR_CHAN("%s: sending FLUSH (%x) to channel %d\n",
11065 			    proc, flush_type, csp->ch_id);
11066 
11067 			(void) putnextctl1(rq, M_FLUSH, flush_type);
11068 		}
11069 		crecv->c_state &= ~IDN_CHANSVC_STATE_FLUSH;
11070 		csend->c_state &= ~IDN_CHANSVC_STATE_FLUSH;
11071 
11072 		if (crecv->c_waiters)
11073 			cv_broadcast(&crecv->c_cv);
11074 	}
11075 
11076 	mutex_exit(&csend->c_mutex);
11077 	mutex_exit(&crecv->c_mutex);
11078 }
11079 #endif /* 0 */
11080 
11081 /*
11082  * Locks are with respect to SEND/RECV locks (c_mutex).
11083  *
11084  * STOP/SUSPEND/DETACH
11085  *	- Entered with locks dropped, leave with locks held.
11086  *	  DETACH - lock dropped manually.
11087  * RESTART/RESUME
11088  *	- Entered with locks held, leave with locks dropped.
11089  * ATTACH
11090  *	- both enter and leave with locks dropped.
11091  */
11092 static void
11093 idn_chan_action(int channel, idn_chanaction_t chanaction, int wait)
11094 {
11095 	uchar_t		clr_state, set_state;
11096 	uint_t		is_running;
11097 	domainset_t	closed_slabwaiters = 0;
11098 	struct idn	*sip;
11099 	idn_chansvr_t	*csp;
11100 	idn_chaninfo_t	*csend, *crecv;
11101 	procname_t	proc = "idn_chan_action";
11102 
11103 	ASSERT((channel >= 0) && (channel < IDN_MAX_NETS));
11104 	ASSERT(idn.chan_servers);
11105 
11106 	csp = &idn.chan_servers[channel];
11107 
11108 	PR_CHAN("%s: requesting %s for channel %d\n",
11109 	    proc, chanaction_str[(int)chanaction], channel);
11110 
11111 	csend = &csp->ch_send;
11112 	crecv = &csp->ch_recv;
11113 
11114 	ASSERT(IDN_CHAN_GLOBAL_IS_LOCKED(csp));
11115 
11116 	clr_state = set_state = 0;
11117 
11118 	switch (chanaction) {
11119 	case IDNCHAN_ACTION_DETACH:
11120 		clr_state = IDN_CHANSVC_STATE_MASK;
11121 		/*FALLTHROUGH*/
11122 
11123 	case IDNCHAN_ACTION_STOP:
11124 		clr_state |= IDN_CHANSVC_STATE_ENABLED;
11125 		/*FALLTHROUGH*/
11126 
11127 	case IDNCHAN_ACTION_SUSPEND:
11128 		clr_state |= IDN_CHANSVC_STATE_ACTIVE;
11129 
11130 		/*
11131 		 * Must maintain this locking order.
11132 		 * Set asynchronous check-in flags.
11133 		 */
11134 		crecv->c_checkin = 1;
11135 		csend->c_checkin = 1;
11136 
11137 		is_running = 0;
11138 		if ((csend->c_inprogress || crecv->c_inprogress) &&
11139 		    wait && (csp->ch_recv_threadp != curthread)) {
11140 
11141 			rw_enter(&idn.struprwlock, RW_READER);
11142 			if ((sip = IDN_INST2SIP(channel)) != NULL) {
11143 				/*
11144 				 * Temporarily turn off the STREAM
11145 				 * to give a chance to breath.
11146 				 */
11147 				is_running = sip->si_flags & IDNRUNNING;
11148 				if (is_running)
11149 					sip->si_flags &= ~IDNRUNNING;
11150 			}
11151 			rw_exit(&idn.struprwlock);
11152 		}
11153 
11154 		mutex_enter(&crecv->c_mutex);
11155 		crecv->c_state &= ~clr_state;
11156 
11157 		mutex_enter(&csend->c_mutex);
11158 		csend->c_state &= ~clr_state;
11159 
11160 		/*
11161 		 * It's possible the channel server could come
11162 		 * through this flow itself due to putting data upstream
11163 		 * that ultimately turned around and came back down for
11164 		 * sending.  If this is the case we certainly don't
11165 		 * want to cv_wait, otherwise we'll obviously deadlock
11166 		 * waiting for ourself.  So, only block if somebody
11167 		 * other than the channel server we're attempting to
11168 		 * suspend/stop.
11169 		 */
11170 		if (wait && (csp->ch_recv_threadp != curthread)) {
11171 			int	do_flush = 0;
11172 
11173 			if (csend->c_inprogress || crecv->c_inprogress)
11174 				do_flush++;
11175 
11176 			if (do_flush) {
11177 				rw_enter(&idn.struprwlock, RW_READER);
11178 				if ((sip = IDN_INST2SIP(channel)) != NULL) {
11179 					/*
11180 					 * Temporarily turn off the STREAM
11181 					 * to give a chance to breath.
11182 					 */
11183 					if (sip->si_flags & IDNRUNNING) {
11184 						is_running = 1;
11185 						sip->si_flags &= ~IDNRUNNING;
11186 					}
11187 				}
11188 				rw_exit(&idn.struprwlock);
11189 			}
11190 
11191 			/*
11192 			 * If we have any senders in-progress
11193 			 * it's possible they're stuck waiting
11194 			 * down in smr_buf_alloc which may never
11195 			 * arrive if we're in an unlink process.
11196 			 * Rather than wait for it to timeout
11197 			 * let's be proactive so we can disconnect
11198 			 * asap.
11199 			 */
11200 			closed_slabwaiters = csp->ch_reg_domset;
11201 			DOMAINSET_ADD(closed_slabwaiters, idn.localid);
11202 			if (closed_slabwaiters)
11203 				smr_slabwaiter_close(closed_slabwaiters);
11204 
11205 			do {
11206 				/*
11207 				 * It's possible due to a STREAMs
11208 				 * loopback from read queue to write queue
11209 				 * that receiver and sender may be same
11210 				 * thread, i.e. receiver's inprogress
11211 				 * flag will never clear until sender's
11212 				 * inprogress flag clears.  So, we wait
11213 				 * for sender's inprogress first.
11214 				 */
11215 				while (csend->c_inprogress) {
11216 					mutex_exit(&crecv->c_mutex);
11217 					while (csend->c_inprogress) {
11218 						csend->c_waiters++;
11219 						cv_wait(&csend->c_cv,
11220 						    &csend->c_mutex);
11221 						csend->c_waiters--;
11222 					}
11223 					/*
11224 					 * Maintain lock ordering.
11225 					 * Eventually we will catch
11226 					 * him due to the flag settings.
11227 					 */
11228 					mutex_exit(&csend->c_mutex);
11229 					mutex_enter(&crecv->c_mutex);
11230 					mutex_enter(&csend->c_mutex);
11231 				}
11232 				if (crecv->c_inprogress) {
11233 					mutex_exit(&csend->c_mutex);
11234 					while (crecv->c_inprogress) {
11235 						crecv->c_waiters++;
11236 						cv_wait(&crecv->c_cv,
11237 						    &crecv->c_mutex);
11238 						crecv->c_waiters--;
11239 					}
11240 					mutex_enter(&csend->c_mutex);
11241 				}
11242 			} while (csend->c_inprogress);
11243 		}
11244 
11245 		if (is_running) {
11246 			/*
11247 			 * Restore the IDNRUNNING bit in
11248 			 * the flags to let them know the
11249 			 * channel is still alive.
11250 			 */
11251 			rw_enter(&idn.struprwlock, RW_READER);
11252 			if ((sip = IDN_INST2SIP(channel)) != NULL)
11253 				sip->si_flags |= IDNRUNNING;
11254 			rw_exit(&idn.struprwlock);
11255 		}
11256 
11257 		if (closed_slabwaiters) {
11258 			/*
11259 			 * We can reopen now since at this point no new
11260 			 * slabwaiters will attempt to come in and wait.
11261 			 */
11262 			smr_slabwaiter_open(csp->ch_reg_domset);
11263 		}
11264 
11265 		crecv->c_checkin = 0;
11266 		csend->c_checkin = 0;
11267 
11268 		/*
11269 		 * ALL leave with locks held.
11270 		 */
11271 		PR_CHAN("%s: action (%s) for channel %d - COMPLETED\n",
11272 		    proc, chanaction_str[(int)chanaction], channel);
11273 		break;
11274 
11275 	case IDNCHAN_ACTION_ATTACH:
11276 		mutex_enter(&crecv->c_mutex);
11277 		mutex_enter(&csend->c_mutex);
11278 		set_state |= csp->ch_state & IDN_CHANSVC_STATE_ATTACHED;
11279 		/*FALLTHROUGH*/
11280 
11281 	case IDNCHAN_ACTION_RESTART:
11282 		set_state |= csp->ch_state & IDN_CHANSVC_STATE_ENABLED;
11283 		/*FALLTHROUGH*/
11284 
11285 	case IDNCHAN_ACTION_RESUME:
11286 		ASSERT(IDN_CHAN_LOCAL_IS_LOCKED(csp));
11287 		set_state |= csp->ch_state & IDN_CHANSVC_STATE_ACTIVE;
11288 
11289 		crecv->c_state |= set_state;
11290 		csend->c_state |= set_state;
11291 
11292 		/*
11293 		 * The channel server itself could come through this
11294 		 * flow, so obviously no point in attempting to wake
11295 		 * ourself up!.
11296 		 */
11297 		if (csp->ch_recv_threadp && (csp->ch_recv_threadp != curthread))
11298 			cv_signal(&csp->ch_recv_cv);
11299 
11300 		PR_CHAN("%s: action (%s) for channel %d - COMPLETED\n",
11301 		    proc, chanaction_str[(int)chanaction], channel);
11302 
11303 		/*
11304 		 * Leaves with lock released.
11305 		 */
11306 		mutex_exit(&csend->c_mutex);
11307 		mutex_exit(&crecv->c_mutex);
11308 		break;
11309 
11310 	default:
11311 		ASSERT(0);
11312 		break;
11313 	}
11314 }
11315 
11316 static void
11317 idn_chan_addmbox(int channel, ushort_t domset)
11318 {
11319 	idn_chansvr_t	*csp;
11320 	register int	d;
11321 	procname_t	proc = "idn_chan_addmbox";
11322 
11323 	PR_CHAN("%s: adding domset 0x%x main mailboxes to channel %d\n",
11324 	    proc, domset, channel);
11325 
11326 	ASSERT(idn.chan_servers);
11327 
11328 	csp = &idn.chan_servers[channel];
11329 
11330 	/*
11331 	 * Adding domains to a channel can be
11332 	 * asynchonous, so we don't bother waiting.
11333 	 */
11334 	IDN_CHANNEL_SUSPEND(channel, 0);
11335 
11336 	/*
11337 	 * Now we have the sending and receiving sides blocked
11338 	 * for this channel.
11339 	 */
11340 	for (d = 0; d < MAX_DOMAINS; d++) {
11341 		if (!DOMAIN_IN_SET(domset, d))
11342 			continue;
11343 		if (IDN_CHAN_DOMAIN_IS_REGISTERED(csp, d)) {
11344 			DOMAINSET_DEL(domset, d);
11345 			continue;
11346 		}
11347 		IDN_CHANSVR_SCANSET_ADD_PENDING(csp, d);
11348 		DOMAINSET_ADD(csp->ch_recv_domset_pending, d);
11349 		IDN_CHAN_DOMAIN_REGISTER(csp, d);
11350 
11351 		PR_CHAN("%s: domain %d (channel %d) RECV (pending) "
11352 		    "scanset = 0x%lx\n", proc, d, channel,
11353 		    csp->ch_recv_scanset_pending);
11354 		PR_CHAN("%s: domain %d (channel %d) domset = 0x%x\n",
11355 		    proc, d, channel, (uint_t)csp->ch_reg_domset);
11356 
11357 		CHECKPOINT_OPENED(IDNSB_CHKPT_CHAN,
11358 		    idn_domain[d].dhw.dh_boardset, 1);
11359 	}
11360 	if (domset)
11361 		csp->ch_recv_changed = 1;
11362 
11363 	IDN_CHANNEL_RESUME(channel);
11364 }
11365 
11366 static void
11367 idn_chan_delmbox(int channel, ushort_t domset)
11368 {
11369 	idn_chansvr_t	*csp;
11370 	register int	d;
11371 	procname_t	proc = "idn_chan_delmbox";
11372 
11373 	PR_CHAN("%s: deleting domset 0x%x main mailboxes from channel %d\n",
11374 	    proc, domset, channel);
11375 
11376 	ASSERT(idn.chan_servers);
11377 
11378 	csp = &idn.chan_servers[channel];
11379 
11380 	/*
11381 	 * Here we have to wait for the channel server
11382 	 * as it's vital that we don't return without guaranteeing
11383 	 * that the given domset is no longer registered.
11384 	 */
11385 	IDN_CHANNEL_SUSPEND(channel, 1);
11386 
11387 	/*
11388 	 * Now we have the sending and receiving sides blocked
11389 	 * for this channel.
11390 	 */
11391 	for (d = 0; d < MAX_DOMAINS; d++) {
11392 		if (!DOMAIN_IN_SET(domset, d))
11393 			continue;
11394 		if (!IDN_CHAN_DOMAIN_IS_REGISTERED(csp, d)) {
11395 			DOMAINSET_DEL(domset, d);
11396 			continue;
11397 		}
11398 		/*
11399 		 * This domain has a mailbox hanging on this channel.
11400 		 * Get him out.
11401 		 *
11402 		 * First remove him from the receive side.
11403 		 */
11404 		ASSERT(csp->ch_recv_domcount > 0);
11405 		IDN_CHANSVR_SCANSET_DEL_PENDING(csp, d);
11406 		DOMAINSET_DEL(csp->ch_recv_domset_pending, d);
11407 		IDN_CHAN_DOMAIN_UNREGISTER(csp, d);
11408 
11409 		PR_CHAN("%s: domain %d (channel %d) RECV (pending) "
11410 		    "scanset = 0x%lx\n", proc, d, channel,
11411 		    csp->ch_recv_scanset_pending);
11412 		PR_CHAN("%s: domain %d (channel %d) domset = 0x%x\n",
11413 		    proc, d, channel, (uint_t)csp->ch_reg_domset);
11414 
11415 		CHECKPOINT_CLOSED(IDNSB_CHKPT_CHAN,
11416 		    idn_domain[d].dhw.dh_boardset, 2);
11417 
11418 	}
11419 	if (domset)
11420 		csp->ch_recv_changed = 1;
11421 
11422 	IDN_CHANNEL_RESUME(channel);
11423 }
11424 
11425 static int
11426 idn_valid_etherheader(struct ether_header *ehp)
11427 {
11428 	uchar_t	*eap;
11429 
11430 	eap = &ehp->ether_dhost.ether_addr_octet[0];
11431 
11432 	if ((eap[IDNETHER_ZERO] != 0) && (eap[IDNETHER_ZERO] != 0xff))
11433 		return (0);
11434 
11435 	if ((eap[IDNETHER_COOKIE1] != IDNETHER_COOKIE1_VAL) &&
11436 	    (eap[IDNETHER_COOKIE1] != 0xff))
11437 		return (0);
11438 
11439 	if ((eap[IDNETHER_COOKIE2] != IDNETHER_COOKIE2_VAL) &&
11440 	    (eap[IDNETHER_COOKIE2] != 0xff))
11441 		return (0);
11442 
11443 	if ((eap[IDNETHER_RESERVED] != IDNETHER_RESERVED_VAL) &&
11444 	    (eap[IDNETHER_RESERVED] != 0xff))
11445 		return (0);
11446 
11447 	if (!VALID_UCHANNEL(eap[IDNETHER_CHANNEL]) &&
11448 	    (eap[IDNETHER_CHANNEL] != 0xff))
11449 		return (0);
11450 
11451 	if (!VALID_UDOMAINID(IDN_NETID2DOMID(eap[IDNETHER_NETID])) &&
11452 	    (eap[IDNETHER_NETID] != 0xff))
11453 		return (0);
11454 
11455 	return (1);
11456 }
11457 
11458 /*
11459  * Packet header has already been filled in.
11460  * RETURNS:	0
11461  *		ENOLINK
11462  *		EPROTO
11463  *		ENOSPC
11464  */
11465 /*ARGSUSED*/
11466 static int
11467 idn_send_mboxdata(int domid, struct idn *sip, int channel, caddr_t bufp)
11468 {
11469 	idn_mainmbox_t	*mmp;
11470 	idn_mboxmsg_t	*mqp;
11471 	smr_pkthdr_t	*hdrp;
11472 	smr_offset_t	bufoffset;
11473 	idn_netaddr_t	dst;
11474 	ushort_t		mbox_csum;
11475 	int		rv = 0;
11476 	int		pktlen, qi;
11477 	procname_t	proc = "idn_send_mboxdata";
11478 
11479 	mmp = idn_domain[domid].dmbox.m_send;
11480 	if (mmp == NULL) {
11481 		PR_DATA("%s: dmbox.m_send == NULL\n", proc);
11482 		IDN_KSTAT_INC(sip, si_linkdown);
11483 		return (ENOLINK);
11484 	}
11485 
11486 	mmp += channel;
11487 	mutex_enter(&mmp->mm_mutex);
11488 
11489 	if (mmp->mm_smr_mboxp == NULL) {
11490 		PR_DATA("%s: (d %d, chn %d) mm_smr_mboxp == NULL\n",
11491 		    proc, domid, channel);
11492 		IDN_KSTAT_INC(sip, si_linkdown);
11493 		rv = ENOLINK;
11494 		goto send_err;
11495 	}
11496 	mbox_csum = IDN_CKSUM_MBOX(&mmp->mm_smr_mboxp->mt_header);
11497 	if (mbox_csum != mmp->mm_smr_mboxp->mt_header.mh_cksum) {
11498 		PR_DATA("%s: (d %d, chn %d) mbox hdr cksum (%d) "
11499 		    "!= actual (%d)\n",
11500 		    proc, domid, channel, mbox_csum,
11501 		    mmp->mm_smr_mboxp->mt_header.mh_cksum);
11502 		if ((mmp->mm_flags & IDNMMBOX_FLAG_CORRUPTED) == 0) {
11503 			cmn_err(CE_WARN,
11504 			    "IDN: 241: [send] (domain %d, "
11505 			    "channel %d) SMR CORRUPTED - RELINK",
11506 			    domid, channel);
11507 			mmp->mm_flags |= IDNMMBOX_FLAG_CORRUPTED;
11508 		}
11509 		IDN_KSTAT_INC(sip, si_mboxcrc);
11510 		IDN_KSTAT_INC(sip, si_oerrors);
11511 		rv = EPROTO;
11512 		goto send_err;
11513 	}
11514 
11515 	bufoffset = IDN_ADDR2OFFSET(bufp);
11516 	hdrp	  = IDN_BUF2HDR(bufp);
11517 	pktlen    = hdrp->b_length;
11518 	dst.netaddr = hdrp->b_netaddr;
11519 	ASSERT(dst.net.chan == (ushort_t)channel);
11520 
11521 	mqp = &mmp->mm_smr_mboxp->mt_queue[0];
11522 	qi  = mmp->mm_qiput;
11523 
11524 	if (mqp[qi].ms_owner) {
11525 		PR_DATA("%s: mailbox FULL (qiput=%d, qiget=%d)\n",
11526 		    proc, mmp->mm_qiput, mmp->mm_qiget);
11527 		IDN_KSTAT_INC(sip, si_txfull);
11528 		rv = ENOSPC;
11529 		goto send_err;
11530 	}
11531 	if (mqp[qi].ms_flag & IDN_MBOXMSG_FLAG_RECLAIM) {
11532 		smr_offset_t	recl_bufoffset;
11533 		/*
11534 		 * Remote domain finished with mailbox entry,
11535 		 * however it has not been reclaimed yet.  A reclaim
11536 		 * was done before coming into this routine, however
11537 		 * timing may have been such that the entry became
11538 		 * free just after the reclamation, but before
11539 		 * entry into here.  Go ahead and reclaim this entry.
11540 		 */
11541 		recl_bufoffset = IDN_BFRAME2OFFSET(mqp[qi].ms_bframe);
11542 
11543 		PR_DATA("%s: attempting reclaim (domain %d) "
11544 		    "(qiput=%d, b_off=0x%x)\n",
11545 		    proc, domid, qi, recl_bufoffset);
11546 
11547 		if (VALID_NWROFFSET(recl_bufoffset, IDN_SMR_BUFSIZE)) {
11548 			int		recl;
11549 			caddr_t		b_bufp;
11550 			smr_pkthdr_t	*b_hdrp;
11551 
11552 			b_bufp = IDN_OFFSET2ADDR(recl_bufoffset);
11553 			b_hdrp = IDN_BUF2HDR(b_bufp);
11554 
11555 			if (IDN_CKSUM_PKT(b_hdrp) != b_hdrp->b_cksum) {
11556 				IDN_KSTAT_INC(sip, si_crc);
11557 				IDN_KSTAT_INC(sip, si_fcs_errors);
11558 				IDN_KSTAT_INC(sip, si_reclaim);
11559 				IDN_KSTAT_INC(sip, si_oerrors);
11560 			}
11561 
11562 			recl = smr_buf_free(domid, b_bufp, b_hdrp->b_length);
11563 #ifdef DEBUG
11564 			if (recl == 0) {
11565 				PR_DATA("%s: SUCCESSFULLY reclaimed buf "
11566 				    "(domain %d)\n", proc, domid);
11567 			} else {
11568 				PR_DATA("%s: WARNING: reclaim failed (FREE) "
11569 				    "(domain %d)\n", proc, domid);
11570 			}
11571 #endif /* DEBUG */
11572 		} else {
11573 			IDN_KSTAT_INC(sip, si_smraddr);
11574 			IDN_KSTAT_INC(sip, si_reclaim);
11575 			PR_DATA("%s: WARNING: reclaim failed (BAD OFFSET) "
11576 			    "(domain %d)\n", proc, domid);
11577 		}
11578 	}
11579 
11580 	if (*mmp->mm_smr_readyp == 0) {
11581 		mmp->mm_qiput = qi;
11582 		IDN_KSTAT_INC(sip, si_linkdown);
11583 		rv = ENOLINK;
11584 		goto send_err;
11585 	}
11586 
11587 	mqp[qi].ms_flag = IDN_MBOXMSG_FLAG_RECLAIM;
11588 	mqp[qi].ms_bframe = IDN_OFFSET2BFRAME(bufoffset);
11589 	/* membar_stst(); */
11590 	mqp[qi].ms_owner = 1;
11591 
11592 	IDN_MMBOXINDEX_INC(qi);
11593 
11594 	mmp->mm_qiput = qi;
11595 
11596 	mmp->mm_count++;
11597 
11598 	if ((*mmp->mm_smr_readyp) && !(*mmp->mm_smr_activep)) {
11599 		idn_msgtype_t	mt;
11600 
11601 		mt.mt_mtype = IDNP_DATA;
11602 		mt.mt_atype = 0;
11603 		IDN_KSTAT_INC(sip, si_xdcall);
11604 		(void) IDNXDC(domid, &mt, (uint_t)dst.net.chan, 0, 0, 0);
11605 	}
11606 	mutex_exit(&mmp->mm_mutex);
11607 	IDN_KSTAT_INC(sip, si_opackets);
11608 	IDN_KSTAT_INC(sip, si_opackets64);
11609 	IDN_KSTAT_ADD(sip, si_xmtbytes, pktlen);
11610 	IDN_KSTAT_ADD(sip, si_obytes64, (uint64_t)pktlen);
11611 
11612 	return (0);
11613 
11614 send_err:
11615 	mmp->mm_dropped++;
11616 
11617 	mutex_exit(&mmp->mm_mutex);
11618 
11619 	return (rv);
11620 }
11621 
11622 static int
11623 idn_recv_mboxdata(int channel, caddr_t bufp)
11624 {
11625 	smr_pkthdr_t	*hdrp;
11626 	struct idn	*sip;
11627 	mblk_t		*mp = nilp(mblk_t);
11628 	int		pktlen;
11629 	int		apktlen;
11630 	int		rv = 0;
11631 	smr_offset_t	bufoffset;
11632 	ushort_t	csum;
11633 	idn_netaddr_t	dst, daddr;
11634 	procname_t	proc = "idn_recv_mboxdata";
11635 
11636 	hdrp = IDN_BUF2HDR(bufp);
11637 
11638 	csum = IDN_CKSUM_PKT(hdrp);
11639 
11640 	sip = IDN_INST2SIP(channel);
11641 	if (sip == NULL) {
11642 		/*LINTED*/
11643 		sip = IDN_INST2SIP(0);
11644 	}
11645 	ASSERT(sip);
11646 
11647 	if (csum != hdrp->b_cksum) {
11648 		PR_DATA("%s: bad checksum(%x) != expected(%x)\n",
11649 		    proc, (uint_t)csum, (uint_t)hdrp->b_cksum);
11650 		IDN_KSTAT_INC(sip, si_crc);
11651 		IDN_KSTAT_INC(sip, si_fcs_errors);
11652 		rv = -1;
11653 		goto recv_err;
11654 	}
11655 
11656 	daddr.net.chan = (ushort_t)channel;
11657 	daddr.net.netid = (ushort_t)idn.localid;
11658 
11659 	dst.netaddr = hdrp->b_netaddr;
11660 	bufoffset = hdrp->b_offset;
11661 
11662 	if (dst.netaddr != daddr.netaddr) {
11663 		PR_DATA("%s: wrong dest netaddr (0x%x), expected (0x%x)\n",
11664 		    proc, dst.netaddr, daddr.netaddr);
11665 		IDN_KSTAT_INC(sip, si_nolink);
11666 		IDN_KSTAT_INC(sip, si_macrcv_errors);
11667 		goto recv_err;
11668 	}
11669 	pktlen  = hdrp->b_length;
11670 	apktlen = pktlen;
11671 
11672 	if ((pktlen <= 0) || (pktlen > IDN_DATA_SIZE)) {
11673 		PR_DATA("%s: invalid packet length (%d) <= 0 || > %lu\n",
11674 		    proc, pktlen, IDN_DATA_SIZE);
11675 		IDN_KSTAT_INC(sip, si_buff);
11676 		IDN_KSTAT_INC(sip, si_toolong_errors);
11677 		goto recv_err;
11678 	}
11679 
11680 	mp = allocb(apktlen + IDN_ALIGNSIZE, BPRI_LO);
11681 	if (mp == nilp(mblk_t)) {
11682 		PR_DATA("%s: allocb(pkt) failed\n", proc);
11683 		IDN_KSTAT_INC(sip, si_allocbfail);
11684 		IDN_KSTAT_INC(sip, si_norcvbuf);	/* MIB II */
11685 		goto recv_err;
11686 	}
11687 	ASSERT(DB_TYPE(mp) == M_DATA);
11688 	/*
11689 	 * Copy data packet into its streams buffer.
11690 	 * Align pointers for maximum bcopy performance.
11691 	 */
11692 	mp->b_rptr = (uchar_t *)IDN_ALIGNPTR(mp->b_rptr, bufoffset);
11693 	bcopy(IDN_BUF2DATA(bufp, bufoffset), mp->b_rptr, apktlen);
11694 	mp->b_wptr = mp->b_rptr + pktlen;
11695 
11696 	if (IDN_CHECKSUM &&
11697 		!idn_valid_etherheader((struct ether_header *)mp->b_rptr)) {
11698 		freeb(mp);
11699 		mp = nilp(mblk_t);
11700 		PR_DATA("%s: etherheader CORRUPTED\n", proc);
11701 		IDN_KSTAT_INC(sip, si_crc);
11702 		IDN_KSTAT_INC(sip, si_fcs_errors);
11703 		rv = -1;
11704 		goto recv_err;
11705 	}
11706 
11707 	idndl_read(NULL, mp);
11708 
11709 recv_err:
11710 
11711 	if (mp == nilp(mblk_t)) {
11712 		IDN_KSTAT_INC(sip, si_ierrors);
11713 	}
11714 
11715 	return (rv);
11716 }
11717 
11718 /*
11719  * When on shutdown path (idn_active_resources) must call
11720  * idn_mainmbox_flush() _BEFORE_ calling idn_reclaim_mboxdata()
11721  * for any final data.  This is necessary incase the mailboxes
11722  * have been unregistered.  If they have then idn_mainmbox_flush()
11723  * will set mm_smr_mboxp to NULL which prevents us from touching
11724  * poison SMR space.
11725  */
11726 int
11727 idn_reclaim_mboxdata(int domid, int channel, int nbufs)
11728 {
11729 	idn_mainmbox_t	*mmp;
11730 	idn_mboxmsg_t	*mqp;
11731 	smr_pkthdr_t	*hdrp;
11732 	idn_domain_t	*dp;
11733 	int		qi;
11734 	int		mi;
11735 	int		reclaim_cnt = 0;
11736 	int		free_cnt;
11737 	ushort_t	csum;
11738 	struct idn	*sip;
11739 	smr_offset_t	reclaim_list, curr, prev;
11740 	procname_t	proc = "idn_reclaim_mboxdata";
11741 
11742 
11743 	sip = IDN_INST2SIP(channel);
11744 	if (sip == NULL) {
11745 		/*LINTED*/
11746 		sip = IDN_INST2SIP(0);
11747 	}
11748 	ASSERT(sip);
11749 
11750 	dp = &idn_domain[domid];
11751 
11752 	PR_DATA("%s: requested %d buffers from domain %d\n",
11753 	    proc, nbufs, domid);
11754 
11755 	if (lock_try(&dp->dreclaim_inprogress) == 0) {
11756 		/*
11757 		 * Reclaim is already in progress, don't
11758 		 * bother.
11759 		 */
11760 		PR_DATA("%s: reclaim already in progress\n", proc);
11761 		return (0);
11762 	}
11763 
11764 	if (dp->dmbox.m_send == NULL)
11765 		return (0);
11766 
11767 	reclaim_list = curr = prev = IDN_NIL_SMROFFSET;
11768 
11769 	mi = (int)dp->dreclaim_index;
11770 	do {
11771 		ushort_t	mbox_csum;
11772 
11773 		mmp = &dp->dmbox.m_send[mi];
11774 		/* do-while continues down */
11775 		ASSERT(mmp);
11776 		if (mutex_tryenter(&mmp->mm_mutex) == 0) {
11777 			/*
11778 			 * This channel is busy, move on.
11779 			 */
11780 			IDN_MBOXCHAN_INC(mi);
11781 			continue;
11782 		}
11783 
11784 		if (mmp->mm_smr_mboxp == NULL) {
11785 			PR_DATA("%s: no smr pointer for domid %d, chan %d\n",
11786 			    proc, domid, (int)mmp->mm_channel);
11787 			ASSERT(mmp->mm_qiget == mmp->mm_qiput);
11788 			mutex_exit(&mmp->mm_mutex);
11789 			IDN_MBOXCHAN_INC(mi);
11790 			continue;
11791 		}
11792 		mbox_csum = IDN_CKSUM_MBOX(&mmp->mm_smr_mboxp->mt_header);
11793 		if (mbox_csum != mmp->mm_smr_mboxp->mt_header.mh_cksum) {
11794 			PR_DATA("%s: (d %d, chn %d) mbox hdr "
11795 			    "cksum (%d) != actual (%d)\n",
11796 			    proc, domid, (int)mmp->mm_channel, mbox_csum,
11797 			    mmp->mm_smr_mboxp->mt_header.mh_cksum);
11798 			IDN_KSTAT_INC(sip, si_mboxcrc);
11799 			IDN_KSTAT_INC(sip, si_oerrors);
11800 			mutex_exit(&mmp->mm_mutex);
11801 			IDN_MBOXCHAN_INC(mi);
11802 			continue;
11803 		}
11804 		mqp = &mmp->mm_smr_mboxp->mt_queue[0];
11805 		qi  = mmp->mm_qiget;
11806 
11807 		while (!mqp[qi].ms_owner &&
11808 		    (mqp[qi].ms_flag & IDN_MBOXMSG_FLAG_RECLAIM) &&
11809 		    nbufs) {
11810 			idn_mboxmsg_t	*msp;
11811 			int		badbuf;
11812 
11813 			badbuf = 0;
11814 			msp = &mqp[qi];
11815 
11816 			if (msp->ms_flag & IDN_MBOXMSG_FLAG_ERRMASK) {
11817 				PR_DATA("%s: msg.flag ERROR(0x%x) (off=0x%x, "
11818 				    "domid=%d, qiget=%d)\n", proc,
11819 				    (uint_t)(msp->ms_flag &
11820 				    IDN_MBOXMSG_FLAG_ERRMASK),
11821 				    IDN_BFRAME2OFFSET(msp->ms_bframe),
11822 				    domid, qi);
11823 			}
11824 			prev = curr;
11825 			curr = IDN_BFRAME2OFFSET(mqp[qi].ms_bframe);
11826 
11827 			if (!VALID_NWROFFSET(curr, IDN_SMR_BUFSIZE)) {
11828 				badbuf = 1;
11829 				IDN_KSTAT_INC(sip, si_reclaim);
11830 			} else {
11831 				/*
11832 				 * Put the buffers onto a list that will be
11833 				 * formally reclaimed down below.  This allows
11834 				 * us to free up mboxq entries as fast as
11835 				 * possible.
11836 				 */
11837 				hdrp = IDN_BUF2HDR(IDN_OFFSET2ADDR(curr));
11838 				csum = IDN_CKSUM_PKT(hdrp);
11839 
11840 				if (csum != hdrp->b_cksum) {
11841 					badbuf = 1;
11842 					IDN_KSTAT_INC(sip, si_crc);
11843 					IDN_KSTAT_INC(sip, si_fcs_errors);
11844 					IDN_KSTAT_INC(sip, si_reclaim);
11845 					if (!(mmp->mm_flags &
11846 					    IDNMMBOX_FLAG_CORRUPTED)) {
11847 						cmn_err(CE_WARN,
11848 						    "IDN: 241: [send] "
11849 						    "(domain %d, channel "
11850 						    "%d) SMR CORRUPTED - "
11851 						    "RELINK",
11852 						    domid, channel);
11853 						mmp->mm_flags |=
11854 						    IDNMMBOX_FLAG_CORRUPTED;
11855 					}
11856 
11857 				} else if (reclaim_list == IDN_NIL_SMROFFSET) {
11858 					reclaim_list = curr;
11859 				} else {
11860 					caddr_t	bufp;
11861 
11862 					bufp = IDN_OFFSET2ADDR(prev);
11863 					hdrp = IDN_BUF2HDR(bufp);
11864 					hdrp->b_next = curr;
11865 				}
11866 			}
11867 
11868 			mqp[qi].ms_flag = 0;
11869 
11870 			IDN_MMBOXINDEX_INC(qi);
11871 
11872 			if (!badbuf) {
11873 				nbufs--;
11874 				reclaim_cnt++;
11875 			}
11876 
11877 			if (qi == mmp->mm_qiget)
11878 				break;
11879 		}
11880 		mmp->mm_qiget = qi;
11881 
11882 		mutex_exit(&mmp->mm_mutex);
11883 
11884 		IDN_MBOXCHAN_INC(mi);
11885 
11886 	} while ((mi != (int)dp->dreclaim_index) && nbufs);
11887 
11888 	dp->dreclaim_index = (uchar_t)mi;
11889 
11890 	if (reclaim_list != IDN_NIL_SMROFFSET) {
11891 		hdrp = IDN_BUF2HDR(IDN_OFFSET2ADDR(curr));
11892 		hdrp->b_next = IDN_NIL_SMROFFSET;
11893 	}
11894 
11895 	PR_DATA("%s: reclaimed %d buffers from domain %d\n",
11896 	    proc, reclaim_cnt, domid);
11897 
11898 	if (reclaim_cnt == 0) {
11899 		lock_clear(&dp->dreclaim_inprogress);
11900 		return (0);
11901 	}
11902 
11903 	/*
11904 	 * Now actually go and reclaim (free) the buffers.
11905 	 */
11906 	free_cnt = 0;
11907 
11908 	for (curr = reclaim_list; curr != IDN_NIL_SMROFFSET; ) {
11909 		caddr_t		bufp;
11910 
11911 		bufp = IDN_OFFSET2ADDR(curr);
11912 		hdrp = IDN_BUF2HDR(bufp);
11913 		csum = IDN_CKSUM_PKT(hdrp);
11914 		if (csum != hdrp->b_cksum) {
11915 			/*
11916 			 * Once corruption is detected we
11917 			 * can't trust our list any further.
11918 			 * These buffers are effectively lost.
11919 			 */
11920 			cmn_err(CE_WARN,
11921 			    "IDN: 241: [send] (domain %d, channel %d) SMR "
11922 			    "CORRUPTED - RELINK", domid, channel);
11923 			break;
11924 		}
11925 
11926 		curr = hdrp->b_next;
11927 
11928 		if (!smr_buf_free(domid, bufp, hdrp->b_length))
11929 			free_cnt++;
11930 	}
11931 
11932 	if ((dp->dio < IDN_WINDOW_EMAX) && dp->diocheck) {
11933 		lock_clear(&dp->diocheck);
11934 		IDN_MSGTIMER_STOP(domid, IDNP_DATA, 0);
11935 	}
11936 
11937 #ifdef DEBUG
11938 	if (free_cnt != reclaim_cnt) {
11939 		PR_DATA("%s: *** WARNING *** freecnt(%d) != reclaim_cnt (%d)\n",
11940 		    proc, free_cnt, reclaim_cnt);
11941 	}
11942 #endif /* DEBUG */
11943 
11944 	lock_clear(&dp->dreclaim_inprogress);
11945 
11946 	return (reclaim_cnt);
11947 }
11948 
11949 void
11950 idn_signal_data_server(int domid, ushort_t channel)
11951 {
11952 	idn_nack_t	nacktype = 0;
11953 	idn_domain_t	*dp;
11954 	idn_chansvr_t	*csp;
11955 	int		c, min_chan, max_chan;
11956 	idn_mainmbox_t	*mmp;
11957 	procname_t	proc = "idn_signal_data_server";
11958 
11959 
11960 	if (domid == IDN_NIL_DOMID)
11961 		return;
11962 
11963 	dp = &idn_domain[domid];
11964 
11965 	if (dp->dawol.a_count > 0) {
11966 		/*
11967 		 * Domain was previously AWOL, but no longer.
11968 		 */
11969 		IDN_SYNC_LOCK();
11970 		IDN_GLOCK_EXCL();
11971 		idn_clear_awol(domid);
11972 		IDN_GUNLOCK();
11973 		IDN_SYNC_UNLOCK();
11974 	}
11975 	/*
11976 	 * Do a precheck before wasting time trying to acquire the lock.
11977 	 */
11978 	if ((dp->dstate != IDNDS_CONNECTED) || !IDN_DLOCK_TRY_SHARED(domid)) {
11979 		/*
11980 		 * Either we're not connected or somebody is busy working
11981 		 * on the domain.  Bail on the signal for now, we'll catch
11982 		 * it on the next go around.
11983 		 */
11984 		return;
11985 	}
11986 	/*
11987 	 * We didn't have the drwlock on the first check of dstate,
11988 	 * but now that we do, make sure the world hasn't changed!
11989 	 */
11990 	if (dp->dstate != IDNDS_CONNECTED) {
11991 		/*
11992 		 * If we reach here, then no connection.
11993 		 * Send no response if this is the case.
11994 		 */
11995 		nacktype = IDNNACK_NOCONN;
11996 		goto send_dresp;
11997 	}
11998 
11999 	/*
12000 	 * No need to worry about locking mainmbox
12001 	 * because we're already holding reader
12002 	 * lock on domain, plus we're just reading
12003 	 * fields in the mainmbox which only change
12004 	 * (or go away) when the writer lock is
12005 	 * held on the domain.
12006 	 */
12007 	if ((mmp = dp->dmbox.m_recv) == NULL) {
12008 		/*
12009 		 * No local mailbox.
12010 		 */
12011 		nacktype = IDNNACK_BADCFG;
12012 		goto send_dresp;
12013 	}
12014 	if ((channel != IDN_BROADCAST_ALLCHAN) && (channel >= IDN_MAX_NETS)) {
12015 		nacktype = IDNNACK_BADCHAN;
12016 		goto send_dresp;
12017 	}
12018 	if (channel == IDN_BROADCAST_ALLCHAN) {
12019 		PR_DATA("%s: requested signal to ALL channels on domain %d\n",
12020 		    proc, domid);
12021 		min_chan = 0;
12022 		max_chan = IDN_MAX_NETS - 1;
12023 	} else {
12024 		PR_DATA("%s: requested signal to channel %d on domain %d\n",
12025 		    proc, channel, domid);
12026 		min_chan = max_chan = (int)channel;
12027 	}
12028 	mmp += min_chan;
12029 	for (c = min_chan; c <= max_chan; mmp++, c++) {
12030 
12031 		/*
12032 		 * We do a quick check for a pending channel.
12033 		 * If pending it will need activation and we rather
12034 		 * do that through a separate (proto) thread.
12035 		 */
12036 		csp = &idn.chan_servers[c];
12037 
12038 		if (csp->ch_recv.c_checkin) {
12039 			PR_DATA("%s: chansvr (%d) for domid %d CHECK-IN\n",
12040 			    proc, c, domid);
12041 			continue;
12042 		}
12043 
12044 		if (IDN_CHAN_TRYLOCK_RECV(csp) == 0) {
12045 			/*
12046 			 * Failed to grab lock, server must be active.
12047 			 */
12048 			PR_DATA("%s: chansvr (%d) for domid %d already actv\n",
12049 			    proc, c, domid);
12050 			continue;
12051 		}
12052 
12053 		if (IDN_CHANNEL_IS_PENDING(csp)) {
12054 			/*
12055 			 * Lock is pending.  Submit asynchronous
12056 			 * job to activate and move-on.
12057 			 */
12058 			IDN_CHAN_UNLOCK_RECV(csp);
12059 			idn_submit_chanactivate_job(c);
12060 			continue;
12061 		}
12062 
12063 		/*
12064 		 * If he ain't active, we ain't talkin'.
12065 		 */
12066 		if (IDN_CHANNEL_IS_RECV_ACTIVE(csp) == 0) {
12067 			IDN_CHAN_UNLOCK_RECV(csp);
12068 			PR_DATA("%s: chansvr (%d) for domid %d inactive\n",
12069 			    proc, c, domid);
12070 			continue;
12071 		}
12072 
12073 		if (mutex_tryenter(&mmp->mm_mutex) == 0) {
12074 			IDN_CHAN_UNLOCK_RECV(csp);
12075 			continue;
12076 		}
12077 
12078 		if (mmp->mm_csp != csp) {
12079 			/*
12080 			 * Not registered.
12081 			 */
12082 			mutex_exit(&mmp->mm_mutex);
12083 			IDN_CHAN_UNLOCK_RECV(csp);
12084 			continue;
12085 
12086 		}
12087 		if (mmp->mm_smr_mboxp == NULL) {
12088 			/*
12089 			 * No SMR mailbox.
12090 			 */
12091 			mutex_exit(&mmp->mm_mutex);
12092 			IDN_CHAN_UNLOCK_RECV(csp);
12093 			continue;
12094 		}
12095 		mutex_exit(&mmp->mm_mutex);
12096 
12097 		if (csp->ch_recv.c_inprogress) {
12098 			/*
12099 			 * Data server is already active.
12100 			 */
12101 			IDN_CHAN_UNLOCK_RECV(csp);
12102 			PR_DATA("%s: chansvr (%d) for domid %d already actv\n",
12103 			    proc, c, domid);
12104 			continue;
12105 		}
12106 		ASSERT(csp == &idn.chan_servers[c]);
12107 
12108 
12109 		PR_DATA("%s: signaling data dispatcher for chan %d dom %d\n",
12110 		    proc, c, domid);
12111 		ASSERT(csp);
12112 		cv_signal(&csp->ch_recv_cv);
12113 		IDN_CHAN_UNLOCK_RECV(csp);
12114 	}
12115 
12116 	if (!nacktype || (channel == IDN_BROADCAST_ALLCHAN)) {
12117 		/*
12118 		 * If there were no real errors or we were
12119 		 * handling multiple channels, then just
12120 		 * return.
12121 		 */
12122 		IDN_DUNLOCK(domid);
12123 		return;
12124 	}
12125 
12126 send_dresp:
12127 
12128 	PR_DATA("%s: sending NACK (%s) back to domain %d (cpu %d)\n",
12129 	    proc, idnnack_str[nacktype], domid, idn_domain[domid].dcpu);
12130 
12131 	idn_send_dataresp(domid, nacktype);
12132 
12133 	IDN_DUNLOCK(domid);
12134 }
12135 
12136 /*ARGSUSED*/
12137 static int
12138 idn_recv_data(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs)
12139 {
12140 #ifdef DEBUG
12141 	uint_t		msg = mtp ? mtp->mt_mtype : 0;
12142 	uint_t		msgarg = mtp ? mtp->mt_atype : 0;
12143 	procname_t	proc = "idn_recv_data";
12144 
12145 	PR_PROTO("%s:%d: DATA message received (msg = 0x%x, msgarg = 0x%x)\n",
12146 	    proc, domid, msg, msgarg);
12147 	PR_PROTO("%s:%d: xargs = (0x%x, 0x%x, 0x%x, 0x%x)\n",
12148 	    proc, domid, xargs[0], xargs[1], xargs[2], xargs[3]);
12149 #endif /* DEBUG */
12150 
12151 	return (0);
12152 }
12153 
12154 /*
12155  * Only used when sending a negative response.
12156  */
12157 static void
12158 idn_send_dataresp(int domid, idn_nack_t nacktype)
12159 {
12160 	idn_msgtype_t	mt;
12161 
12162 	ASSERT(IDN_DLOCK_IS_HELD(domid));
12163 
12164 	if (idn_domain[domid].dcpu == IDN_NIL_DCPU)
12165 		return;
12166 
12167 	mt.mt_mtype = IDNP_NACK;
12168 	mt.mt_atype = IDNP_DATA;
12169 
12170 	(void) IDNXDC(domid, &mt, (uint_t)nacktype, 0, 0, 0);
12171 }
12172 
12173 /*
12174  * Checksum routine used in checksum smr_pkthdr_t and idn_mboxhdr_t.
12175  */
12176 static ushort_t
12177 idn_cksum(register ushort_t *hdrp, register int count)
12178 {
12179 	register int		i;
12180 	register ushort_t	sum = 0;
12181 
12182 	for (i = 0; i < count; i++)
12183 		sum += hdrp[i];
12184 
12185 	sum = (sum >> 16) + (sum & 0xffff);
12186 	sum += (sum >> 16);
12187 
12188 	return (~sum);
12189 }
12190 
12191 /*
12192  * ------------------------------------------------
12193  */
12194 
12195 int
12196 idn_open_channel(int channel)
12197 {
12198 	int		masterid;
12199 	idn_chansvr_t	*csp;
12200 	struct idn	*sip;
12201 	procname_t	proc = "idn_open_channel";
12202 
12203 	if (channel >= IDN_MAX_NETS) {
12204 		cmn_err(CE_WARN,
12205 		    "IDN: 242: maximum channels (%d) already open",
12206 		    IDN_MAX_NETS);
12207 		return (-1);
12208 	}
12209 	IDN_GLOCK_EXCL();
12210 
12211 	ASSERT(idn.chan_servers != NULL);
12212 
12213 	csp = &idn.chan_servers[channel];
12214 
12215 	IDN_CHAN_LOCK_GLOBAL(csp);
12216 
12217 	if (IDN_CHANNEL_IS_ATTACHED(csp)) {
12218 		PR_CHAN("%s: channel %d already open\n", proc, channel);
12219 		IDN_CHAN_UNLOCK_GLOBAL(csp);
12220 		IDN_GUNLOCK();
12221 		return (0);
12222 	}
12223 
12224 	/*
12225 	 * Need to zero out the kstats now that we're activating
12226 	 * this channel.
12227 	 */
12228 	for (sip = idn.sip; sip; sip = sip->si_nextp) {
12229 		if (sip->si_dip && (ddi_get_instance(sip->si_dip) == channel)) {
12230 			bzero(&sip->si_kstat, sizeof (sip->si_kstat));
12231 			break;
12232 		}
12233 	}
12234 
12235 	IDN_CHANSVC_MARK_ATTACHED(csp);
12236 	idn.nchannels++;
12237 	CHANSET_ADD(idn.chanset, channel);
12238 	IDN_CHANNEL_ATTACH(channel);
12239 
12240 	IDN_CHAN_UNLOCK_GLOBAL(csp);
12241 
12242 	/*
12243 	 * We increase our window threshold each time a channel
12244 	 * is opened.
12245 	 */
12246 	ASSERT(idn.nchannels > 0);
12247 	IDN_WINDOW_EMAX = IDN_WINDOW_MAX +
12248 	    ((idn.nchannels - 1) * IDN_WINDOW_INCR);
12249 
12250 	PR_CHAN("%s: channel %d is OPEN (nchannels = %d)\n",
12251 	    proc, channel, idn.nchannels);
12252 
12253 	masterid = IDN_GET_MASTERID();
12254 	IDN_GUNLOCK();
12255 
12256 	/*
12257 	 * Check if there is an active master to which
12258 	 * we're connected.  If so, then activate channel.
12259 	 */
12260 	if (masterid != IDN_NIL_DOMID) {
12261 		idn_domain_t	*dp;
12262 
12263 		dp = &idn_domain[masterid];
12264 		IDN_DLOCK_SHARED(masterid);
12265 		if (dp->dvote.v.master && (dp->dstate == IDNDS_CONNECTED))
12266 			(void) idn_activate_channel(CHANSET(channel),
12267 			    IDNCHAN_ONLINE);
12268 		IDN_DUNLOCK(masterid);
12269 	}
12270 
12271 	return (0);
12272 }
12273 
12274 void
12275 idn_close_channel(int channel, idn_chanop_t chanop)
12276 {
12277 	idn_chansvr_t	*csp;
12278 	procname_t	proc = "idn_close_channel";
12279 
12280 
12281 	ASSERT(idn.chan_servers != NULL);
12282 
12283 	csp = &idn.chan_servers[channel];
12284 
12285 	IDN_GLOCK_EXCL();
12286 
12287 	IDN_CHAN_LOCK_GLOBAL(csp);
12288 	if (IDN_CHANNEL_IS_DETACHED(csp)) {
12289 		PR_CHAN("%s: channel %d already closed\n", proc, channel);
12290 		IDN_CHAN_UNLOCK_GLOBAL(csp);
12291 		IDN_GUNLOCK();
12292 		return;
12293 	}
12294 	IDN_CHAN_UNLOCK_GLOBAL(csp);
12295 
12296 	idn_deactivate_channel(CHANSET(channel), chanop);
12297 
12298 	IDN_CHAN_LOCK_GLOBAL(csp);
12299 
12300 	if (chanop == IDNCHAN_HARD_CLOSE) {
12301 		idn.nchannels--;
12302 		CHANSET_DEL(idn.chanset, channel);
12303 		/*
12304 		 * We increase our window threshold each time a channel
12305 		 * is opened.
12306 		 */
12307 		if (idn.nchannels <= 0)
12308 			IDN_WINDOW_EMAX = 0;
12309 		else
12310 			IDN_WINDOW_EMAX = IDN_WINDOW_MAX +
12311 			    ((idn.nchannels - 1) * IDN_WINDOW_INCR);
12312 	}
12313 
12314 	PR_CHAN("%s: channel %d is (%s) CLOSED (nchannels = %d)\n",
12315 	    proc, channel,
12316 	    (chanop == IDNCHAN_SOFT_CLOSE) ? "SOFT"
12317 	    : (chanop == IDNCHAN_HARD_CLOSE) ? "HARD" : "OFFLINE",
12318 	    idn.nchannels);
12319 
12320 	IDN_CHAN_UNLOCK_GLOBAL(csp);
12321 	IDN_GUNLOCK();
12322 }
12323 
12324 static int
12325 idn_activate_channel(idn_chanset_t chanset, idn_chanop_t chanop)
12326 {
12327 	int		c, rv = 0;
12328 	procname_t	proc = "idn_activate_channel";
12329 
12330 	PR_CHAN("%s: chanset = 0x%x, chanop = %s\n",
12331 	    proc, chanset, chanop_str[chanop]);
12332 
12333 	if (idn.state != IDNGS_ONLINE) {
12334 		/*
12335 		 * Can't activate any channels unless local
12336 		 * domain is connected and thus has a master.
12337 		 */
12338 		PR_CHAN("%s: local domain not connected.  no data servers\n",
12339 		    proc);
12340 		return (-1);
12341 	}
12342 
12343 	for (c = 0; c < IDN_MAX_NETS; c++) {
12344 		idn_chansvr_t	*csp;
12345 		idn_mboxhdr_t	*mainhp;
12346 		struct idn	*sip;
12347 
12348 		if (!CHAN_IN_SET(chanset, c))
12349 			continue;
12350 		csp = &idn.chan_servers[c];
12351 
12352 		if (chanop == IDNCHAN_ONLINE) {
12353 			IDN_CHAN_LOCK_GLOBAL(csp);
12354 		} else {
12355 			/*
12356 			 * We don't wait to grab the global lock
12357 			 * if IDNCHAN_OPEN since these occur along
12358 			 * critical data paths and will be retried
12359 			 * anyway if needed.
12360 			 */
12361 			if (IDN_CHAN_TRYLOCK_GLOBAL(csp) == 0) {
12362 				PR_CHAN("%s: failed to acquire global "
12363 				    "lock for channel %d\n",
12364 				    proc, c);
12365 				continue;
12366 			}
12367 		}
12368 
12369 		if (!IDN_CHANNEL_IS_ATTACHED(csp)) {
12370 			PR_CHAN("%s: channel %d NOT open\n", proc, c);
12371 			IDN_CHAN_UNLOCK_GLOBAL(csp);
12372 			continue;
12373 
12374 		}
12375 
12376 		if (IDN_CHANNEL_IS_ACTIVE(csp)) {
12377 
12378 			PR_CHAN("%s: channel %d already active\n", proc, c);
12379 			rv++;
12380 			IDN_CHAN_UNLOCK_GLOBAL(csp);
12381 			continue;
12382 
12383 		}
12384 		/*
12385 		 * Channel activation can happen asynchronously.
12386 		 */
12387 		IDN_CHANNEL_SUSPEND(c, 0);
12388 
12389 		if (IDN_CHANNEL_IS_PENDING(csp) && (chanop == IDNCHAN_OPEN)) {
12390 
12391 			PR_CHAN("%s: ACTIVATING channel %d\n", proc, c);
12392 
12393 			if (idn_activate_channel_services(c) >= 0) {
12394 				PR_CHAN("%s: Setting channel %d ACTIVE\n",
12395 				    proc, c);
12396 				IDN_CHANSVC_MARK_ACTIVE(csp);
12397 				rv++;
12398 			}
12399 		} else if (!IDN_CHANNEL_IS_PENDING(csp) &&
12400 		    (chanop == IDNCHAN_ONLINE)) {
12401 			PR_CHAN("%s: Setting channel %d PENDING\n", proc, c);
12402 
12403 			IDN_CHANSVC_MARK_PENDING(csp);
12404 		}
12405 		/*
12406 		 * Don't syncheader (i.e. touch SMR) unless
12407 		 * channel is at least ENABLED.  For a DISABLED
12408 		 * channel, the SMR may be invalid so do NOT
12409 		 * touch it.
12410 		 */
12411 		if (IDN_CHANNEL_IS_ENABLED(csp) &&
12412 		    ((mainhp = idn_chan_server_syncheader(c)) != NULL)) {
12413 			PR_CHAN("%s: marking chansvr (mhp=0x%p) %d READY\n",
12414 			    proc, (void *)mainhp, c);
12415 			mainhp->mh_svr_ready = 1;
12416 		}
12417 
12418 		IDN_CHANNEL_RESUME(c);
12419 		sip = IDN_INST2SIP(c);
12420 		ASSERT(sip);
12421 		if (sip->si_wantw) {
12422 			mutex_enter(&idn.sipwenlock);
12423 			idndl_wenable(sip);
12424 			mutex_exit(&idn.sipwenlock);
12425 		}
12426 		IDN_CHAN_UNLOCK_GLOBAL(csp);
12427 
12428 	}
12429 	/*
12430 	 * Returns "not active", i.e. value of 0 indicates
12431 	 * no channels are activated.
12432 	 */
12433 	return (rv == 0);
12434 }
12435 
12436 static void
12437 idn_deactivate_channel(idn_chanset_t chanset, idn_chanop_t chanop)
12438 {
12439 	int		c;
12440 	procname_t	proc = "idn_deactivate_channel";
12441 
12442 	PR_CHAN("%s: chanset = 0x%x, chanop = %s\n",
12443 	    proc, chanset, chanop_str[chanop]);
12444 
12445 	for (c = 0; c < IDN_MAX_NETS; c++) {
12446 		idn_chansvr_t	*csp;
12447 		idn_mboxhdr_t	*mainhp;
12448 
12449 		if (!CHAN_IN_SET(chanset, c))
12450 			continue;
12451 
12452 		csp = &idn.chan_servers[c];
12453 
12454 		IDN_CHAN_LOCK_GLOBAL(csp);
12455 
12456 		if (((chanop == IDNCHAN_SOFT_CLOSE) &&
12457 		    !IDN_CHANNEL_IS_ACTIVE(csp)) ||
12458 		    ((chanop == IDNCHAN_HARD_CLOSE) &&
12459 		    IDN_CHANNEL_IS_DETACHED(csp)) ||
12460 		    ((chanop == IDNCHAN_OFFLINE) &&
12461 		    !IDN_CHANNEL_IS_ENABLED(csp))) {
12462 
12463 			ASSERT(!IDN_CHANNEL_IS_RECV_ACTIVE(csp));
12464 			ASSERT(!IDN_CHANNEL_IS_SEND_ACTIVE(csp));
12465 
12466 			PR_CHAN("%s: channel %d already deactivated\n",
12467 			    proc, c);
12468 			IDN_CHAN_UNLOCK_GLOBAL(csp);
12469 			continue;
12470 		}
12471 
12472 		switch (chanop) {
12473 		case IDNCHAN_OFFLINE:
12474 			IDN_CHANSVC_MARK_IDLE(csp);
12475 			IDN_CHANSVC_MARK_DISABLED(csp);
12476 			IDN_CHANNEL_STOP(c, 1);
12477 			mainhp = idn_chan_server_syncheader(c);
12478 			if (mainhp != NULL)
12479 				mainhp->mh_svr_ready = 0;
12480 			break;
12481 
12482 		case IDNCHAN_HARD_CLOSE:
12483 			IDN_CHANSVC_MARK_DETACHED(csp);
12484 			IDN_CHANNEL_DETACH(c, 1);
12485 			mainhp = idn_chan_server_syncheader(c);
12486 			if (mainhp != NULL)
12487 				mainhp->mh_svr_ready = 0;
12488 			break;
12489 
12490 		default:
12491 			IDN_CHANSVC_MARK_IDLE(csp);
12492 			IDN_CHANNEL_SUSPEND(c, 1);
12493 			ASSERT(IDN_CHANNEL_IS_ATTACHED(csp));
12494 			break;
12495 		}
12496 
12497 		lock_clear(&csp->ch_actvlck);
12498 		lock_clear(&csp->ch_initlck);
12499 
12500 		PR_CHAN("%s: DEACTIVATING channel %d (%s)\n", proc, c,
12501 		    chanop_str[chanop]);
12502 		PR_CHAN("%s: removing chanset 0x%x data svrs for "
12503 		    "each domain link\n", proc, chanset);
12504 
12505 		(void) idn_deactivate_channel_services(c, chanop);
12506 	}
12507 	/*
12508 	 * Returns with channels unlocked.
12509 	 */
12510 }
12511 
12512 /*
12513  * The priority of the channel server must be less than that
12514  * of the protocol server since the protocol server tasks
12515  * are (can be) of more importance.
12516  *
12517  * Possible range: 60-99.
12518  */
12519 static pri_t	idn_chansvr_pri = (7 * MAXCLSYSPRI) / 8;
12520 
12521 static int
12522 idn_activate_channel_services(int channel)
12523 {
12524 	idn_chansvr_t	*csp;
12525 	procname_t	proc = "idn_activate_channel_services";
12526 
12527 
12528 	ASSERT((channel >= 0) && (channel < IDN_MAX_NETS));
12529 
12530 	csp = &idn.chan_servers[channel];
12531 
12532 	ASSERT(IDN_CHAN_GLOBAL_IS_LOCKED(csp));
12533 	ASSERT(IDN_CHAN_LOCAL_IS_LOCKED(csp));
12534 
12535 	if (csp->ch_recv_threadp) {
12536 		/*
12537 		 * There's an existing dispatcher!
12538 		 * Must have been idle'd during an earlier
12539 		 * stint.
12540 		 */
12541 		ASSERT(csp->ch_id == (uchar_t)channel);
12542 		PR_CHAN("%s: existing chansvr FOUND for (c=%d)\n",
12543 		    proc, channel);
12544 
12545 		if (IDN_CHANNEL_IS_PENDING(csp) == 0)
12546 			return (-1);
12547 
12548 		PR_CHAN("%s: chansvr (c=%d) Rstate = 0x%x, Sstate = 0x%x\n",
12549 		    proc, channel, csp->ch_recv.c_state,
12550 		    csp->ch_send.c_state);
12551 
12552 		cv_signal(&csp->ch_recv_cv);
12553 
12554 		return (0);
12555 	}
12556 
12557 	if (IDN_CHANNEL_IS_PENDING(csp) == 0)
12558 		return (-1);
12559 
12560 	csp->ch_id = (uchar_t)channel;
12561 
12562 	PR_CHAN("%s: init channel %d server\n", proc, channel);
12563 
12564 	csp->ch_recv_morguep = GETSTRUCT(ksema_t, 1);
12565 	sema_init(csp->ch_recv_morguep, 0, NULL, SEMA_DRIVER, NULL);
12566 
12567 	csp->ch_recv.c_inprogress = 0;
12568 	csp->ch_recv.c_waiters = 0;
12569 	csp->ch_recv.c_checkin = 0;
12570 	csp->ch_recv_changed = 1;
12571 
12572 	csp->ch_recv_domset = csp->ch_reg_domset;
12573 
12574 	csp->ch_recv_waittime = IDN_NETSVR_WAIT_MIN;
12575 
12576 	csp->ch_recv_threadp = thread_create(NULL, 0,
12577 	    idn_chan_server, &csp, sizeof (csp), &p0, TS_RUN, idn_chansvr_pri);
12578 
12579 	csp->ch_send.c_inprogress = 0;
12580 	csp->ch_send.c_waiters = 0;
12581 	csp->ch_send.c_checkin = 0;
12582 
12583 	return (0);
12584 }
12585 
12586 /*
12587  * This routine can handle terminating a set of channel
12588  * servers all at once, however currently only used
12589  * for serial killing, i.e. one-at-a-time.
12590  *
12591  * Entered with RECV locks held on chanset.
12592  * Acquires SEND locks if needed.
12593  * Leaves with all RECV and SEND locks dropped.
12594  */
12595 static int
12596 idn_deactivate_channel_services(int channel, idn_chanop_t chanop)
12597 {
12598 	idn_chansvr_t	*csp;
12599 	int		cs_count;
12600 	int		c;
12601 	idn_chanset_t	chanset;
12602 	ksema_t		*central_morguep = NULL;
12603 	procname_t	proc = "idn_deactivate_channel_services";
12604 
12605 
12606 	ASSERT(idn.chan_servers);
12607 
12608 	PR_CHAN("%s: deactivating channel %d services\n", proc, channel);
12609 
12610 	/*
12611 	 * XXX
12612 	 * Old code allowed us to deactivate multiple channel
12613 	 * servers at once.  Keep for now just in case.
12614 	 */
12615 	chanset = CHANSET(channel);
12616 
12617 	/*
12618 	 * Point all the data dispatchers to the same morgue
12619 	 * so we can kill them all at once.
12620 	 */
12621 	cs_count = 0;
12622 	for (c = 0; c < IDN_MAX_NETS; c++) {
12623 		if (!CHAN_IN_SET(chanset, c))
12624 			continue;
12625 
12626 		csp = &idn.chan_servers[c];
12627 		ASSERT(IDN_CHAN_GLOBAL_IS_LOCKED(csp));
12628 		ASSERT(IDN_CHAN_LOCAL_IS_LOCKED(csp));
12629 
12630 		if (csp->ch_recv_threadp == NULL) {
12631 			/*
12632 			 * No channel server home.
12633 			 * But we're still holding the c_mutex.
12634 			 * At mark him idle incase we start him up.
12635 			 */
12636 			PR_CHAN("%s: no channel server found for chan %d\n",
12637 			    proc, c);
12638 			IDN_CHAN_UNLOCK_LOCAL(csp);
12639 			IDN_CHAN_UNLOCK_GLOBAL(csp);
12640 			continue;
12641 		}
12642 		ASSERT(csp->ch_id == (uchar_t)c);
12643 
12644 		/*
12645 		 * Okay, now we've blocked the send and receive sides.
12646 		 */
12647 
12648 		if ((chanop == IDNCHAN_SOFT_CLOSE) ||
12649 		    (chanop == IDNCHAN_OFFLINE)) {
12650 			/*
12651 			 * We set turned off the ACTIVE flag, but there's
12652 			 * no guarantee he stopped because of it.  He may
12653 			 * have already been sleeping.  We need to be
12654 			 * sure he recognizes the IDLE, so we need to
12655 			 * signal him and give him a chance to see it.
12656 			 */
12657 			cv_signal(&csp->ch_recv_cv);
12658 			IDN_CHAN_UNLOCK_LOCAL(csp);
12659 			IDN_CHAN_UNLOCK_GLOBAL(csp);
12660 			cs_count++;
12661 			continue;
12662 		}
12663 
12664 		PR_CHAN("%s: pointing chansvr %d to morgue (0x%p)\n",
12665 		    proc, c, central_morguep ? (void *)central_morguep
12666 		    : (void *)(csp->ch_recv_morguep));
12667 
12668 		if (central_morguep == NULL) {
12669 			central_morguep = csp->ch_recv_morguep;
12670 		} else {
12671 			sema_destroy(csp->ch_recv_morguep);
12672 			FREESTRUCT(csp->ch_recv_morguep, ksema_t, 1);
12673 
12674 			csp->ch_recv_morguep = central_morguep;
12675 		}
12676 		cv_signal(&csp->ch_recv_cv);
12677 		if (csp->ch_recv.c_waiters > 0)
12678 			cv_broadcast(&csp->ch_recv.c_cv);
12679 		/*
12680 		 * Save any existing binding for next reincarnation.
12681 		 * Note that we're holding the local and global
12682 		 * locks so we're protected against others touchers
12683 		 * of the ch_bound_cpuid fields.
12684 		 */
12685 		csp->ch_bound_cpuid_pending = csp->ch_bound_cpuid;
12686 		csp->ch_bound_cpuid = -1;
12687 		IDN_CHAN_UNLOCK_LOCAL(csp);
12688 		IDN_CHAN_UNLOCK_GLOBAL(csp);
12689 		cs_count++;
12690 	}
12691 	PR_CHAN("%s: signaled %d chansvrs for chanset 0x%x\n",
12692 	    proc, cs_count, chanset);
12693 
12694 	if ((chanop == IDNCHAN_SOFT_CLOSE) || (chanop == IDNCHAN_OFFLINE))
12695 		return (cs_count);
12696 
12697 	PR_CHAN("%s: waiting for %d (chnset=0x%x) chan svrs to term\n",
12698 	    proc, cs_count, chanset);
12699 	PR_CHAN("%s: morguep = 0x%p\n", proc, (void *)central_morguep);
12700 
12701 	ASSERT((cs_count > 0) ? (central_morguep != NULL) : 1);
12702 	while (cs_count-- > 0)
12703 		sema_p(central_morguep);
12704 
12705 	if (central_morguep) {
12706 		sema_destroy(central_morguep);
12707 		FREESTRUCT(central_morguep, ksema_t, 1);
12708 	}
12709 
12710 	return (cs_count);
12711 }
12712 
12713 int
12714 idn_chanservers_init()
12715 {
12716 	int		c;
12717 	idn_chansvr_t	*csp;
12718 
12719 
12720 	if (idn.chan_servers)
12721 		return (0);
12722 
12723 	idn.chan_servers = GETSTRUCT(idn_chansvr_t, IDN_MAXMAX_NETS);
12724 
12725 	for (c = 0; c < IDN_MAXMAX_NETS; c++) {
12726 		csp = &idn.chan_servers[c];
12727 		mutex_init(&csp->ch_send.c_mutex, NULL, MUTEX_DEFAULT, NULL);
12728 		mutex_init(&csp->ch_recv.c_mutex, NULL, MUTEX_DEFAULT, NULL);
12729 		cv_init(&csp->ch_send.c_cv, NULL, CV_DRIVER, NULL);
12730 		cv_init(&csp->ch_recv.c_cv, NULL, CV_DRIVER, NULL);
12731 		cv_init(&csp->ch_recv_cv, NULL, CV_DRIVER, NULL);
12732 		csp->ch_bound_cpuid = -1;
12733 		csp->ch_bound_cpuid_pending = -1;
12734 	}
12735 
12736 	return (c);
12737 }
12738 
12739 void
12740 idn_chanservers_deinit()
12741 {
12742 	int		c;
12743 	idn_chansvr_t	*csp;
12744 
12745 
12746 	if (idn.chan_servers == NULL)
12747 		return;
12748 
12749 	for (c = 0; c < IDN_MAXMAX_NETS; c++) {
12750 		csp = &idn.chan_servers[c];
12751 
12752 		mutex_destroy(&csp->ch_send.c_mutex);
12753 		mutex_destroy(&csp->ch_recv.c_mutex);
12754 		cv_destroy(&csp->ch_send.c_cv);
12755 		cv_destroy(&csp->ch_recv.c_cv);
12756 		cv_destroy(&csp->ch_recv_cv);
12757 	}
12758 
12759 	FREESTRUCT(idn.chan_servers, idn_chansvr_t, IDN_MAXMAX_NETS);
12760 	idn.chan_servers = NULL;
12761 }
12762 
12763 static void
12764 idn_exec_chanactivate(void *chn)
12765 {
12766 	int		not_active, channel;
12767 	idn_chansvr_t	*csp;
12768 
12769 	channel = (int)(uintptr_t)chn;
12770 
12771 	IDN_GLOCK_SHARED();
12772 	if (idn.chan_servers == NULL) {
12773 		IDN_GUNLOCK();
12774 		return;
12775 	}
12776 	csp = &idn.chan_servers[channel];
12777 
12778 	if (IDN_CHAN_TRYLOCK_GLOBAL(csp) == 0) {
12779 		/*
12780 		 * If we can't grab the global lock, then
12781 		 * something is up, skip out.
12782 		 */
12783 		IDN_GUNLOCK();
12784 		return;
12785 	}
12786 	IDN_GUNLOCK();
12787 
12788 	if (IDN_CHANNEL_IS_PENDING(csp) && lock_try(&csp->ch_actvlck)) {
12789 		IDN_CHAN_UNLOCK_GLOBAL(csp);
12790 		not_active = idn_activate_channel(CHANSET(channel),
12791 		    IDNCHAN_OPEN);
12792 		if (not_active)
12793 			lock_clear(&csp->ch_actvlck);
12794 	} else {
12795 		IDN_CHAN_UNLOCK_GLOBAL(csp);
12796 	}
12797 }
12798 
12799 /*
12800  * Delayed activation of channel.  We don't want to do this within
12801  * idn_signal_data_server() since that's called within the context
12802  * of an XDC handler so we submit it as a timeout() call to be short
12803  * as soon as possible.
12804  * The ch_initlck & ch_actvlck are used to synchronize activation
12805  * of the channel so that we don't have multiple idn_activate_channel's
12806  * attempting to activate the same channel.
12807  */
12808 static void
12809 idn_submit_chanactivate_job(int channel)
12810 {
12811 	idn_chansvr_t	*csp;
12812 
12813 	if (idn.chan_servers == NULL)
12814 		return;
12815 	csp = &idn.chan_servers[channel];
12816 
12817 	if (lock_try(&csp->ch_initlck) == 0)
12818 		return;
12819 
12820 	(void) timeout(idn_exec_chanactivate, (caddr_t)(uintptr_t)channel, 1);
12821 }
12822 
12823 /*ARGSUSED0*/
12824 static void
12825 idn_xmit_monitor(void *unused)
12826 {
12827 	int		c, d;
12828 	idn_chansvr_t	*csp;
12829 	idn_chanset_t	wake_set;
12830 	domainset_t	conset;
12831 	smr_slab_t	*sp;
12832 	procname_t	proc = "idn_xmit_monitor";
12833 
12834 	CHANSET_ZERO(wake_set);
12835 
12836 	mutex_enter(&idn.xmit_lock);
12837 	if ((idn.xmit_tid == NULL) || !idn.xmit_chanset_wanted) {
12838 		idn.xmit_tid = NULL;
12839 		mutex_exit(&idn.xmit_lock);
12840 		PR_XMON("%s: bailing out\n", proc);
12841 		return;
12842 	}
12843 
12844 	/*
12845 	 * No point in transmitting unless state
12846 	 * is ONLINE.
12847 	 */
12848 	if (idn.state != IDNGS_ONLINE)
12849 		goto retry;
12850 
12851 	conset = idn.domset.ds_connected;
12852 
12853 	/*
12854 	 * Try and reclaim some buffers if possible.
12855 	 */
12856 	for (d = 0; d < MAX_DOMAINS; d++) {
12857 		if (!DOMAIN_IN_SET(conset, d))
12858 			continue;
12859 
12860 		if (!IDN_DLOCK_TRY_SHARED(d))
12861 			continue;
12862 
12863 		if (idn_domain[d].dcpu != IDN_NIL_DCPU)
12864 			(void) idn_reclaim_mboxdata(d, 0, -1);
12865 
12866 		IDN_DUNLOCK(d);
12867 	}
12868 
12869 	/*
12870 	 * Now check if we were successful in getting
12871 	 * any buffers.
12872 	 */
12873 	DSLAB_LOCK_SHARED(idn.localid);
12874 	sp = idn_domain[idn.localid].dslab;
12875 	for (; sp; sp = sp->sl_next)
12876 		if (sp->sl_free)
12877 			break;
12878 	DSLAB_UNLOCK(idn.localid);
12879 
12880 	/*
12881 	 * If there are no buffers available,
12882 	 * no point in reenabling the queues.
12883 	 */
12884 	if (sp == NULL)
12885 		goto retry;
12886 
12887 	CHANSET_ZERO(wake_set);
12888 	for (c = 0; c < IDN_MAX_NETS; c++) {
12889 		int		pending_bits;
12890 		struct idn	*sip;
12891 
12892 		if (!CHAN_IN_SET(idn.xmit_chanset_wanted, c))
12893 			continue;
12894 
12895 		csp = &idn.chan_servers[c];
12896 		if (!IDN_CHAN_TRYLOCK_GLOBAL(csp))
12897 			continue;
12898 
12899 		pending_bits = csp->ch_state & IDN_CHANSVC_PENDING_BITS;
12900 
12901 		sip = IDN_INST2SIP(c);
12902 
12903 		if (!csp->ch_send.c_checkin &&
12904 		    (pending_bits == IDN_CHANSVC_PENDING_BITS) &&
12905 		    sip && (sip->si_flags & IDNRUNNING)) {
12906 
12907 			IDN_CHAN_UNLOCK_GLOBAL(csp);
12908 			CHANSET_ADD(wake_set, c);
12909 
12910 			PR_XMON("%s: QENABLE for channel %d\n", proc, c);
12911 
12912 			rw_enter(&idn.struprwlock, RW_READER);
12913 			mutex_enter(&idn.sipwenlock);
12914 			idndl_wenable(sip);
12915 			mutex_exit(&idn.sipwenlock);
12916 			rw_exit(&idn.struprwlock);
12917 		} else {
12918 			IDN_CHAN_UNLOCK_GLOBAL(csp);
12919 		}
12920 	}
12921 
12922 	/*
12923 	 * Clear the channels we enabled.
12924 	 */
12925 	idn.xmit_chanset_wanted &= ~wake_set;
12926 
12927 retry:
12928 
12929 	if (idn.xmit_chanset_wanted == 0)
12930 		idn.xmit_tid = NULL;
12931 	else
12932 		idn.xmit_tid = timeout(idn_xmit_monitor, NULL,
12933 		    idn_xmit_monitor_freq);
12934 
12935 	mutex_exit(&idn.xmit_lock);
12936 }
12937 
12938 void
12939 idn_xmit_monitor_kickoff(int chan_wanted)
12940 {
12941 	procname_t	proc = "idn_xmit_monitor_kickoff";
12942 
12943 	mutex_enter(&idn.xmit_lock);
12944 
12945 	if (chan_wanted < 0) {
12946 		/*
12947 		 * Wants all channels.
12948 		 */
12949 		idn.xmit_chanset_wanted = CHANSET_ALL;
12950 	} else {
12951 		CHANSET_ADD(idn.xmit_chanset_wanted, chan_wanted);
12952 	}
12953 
12954 	if (idn.xmit_tid != (timeout_id_t)NULL) {
12955 		/*
12956 		 * A monitor is already running, so
12957 		 * he will catch the new "wants" when
12958 		 * he comes around.
12959 		 */
12960 		mutex_exit(&idn.xmit_lock);
12961 		return;
12962 	}
12963 
12964 	PR_XMON("%s: xmit_mon kicked OFF (chanset = 0x%x)\n",
12965 	    proc, idn.xmit_chanset_wanted);
12966 
12967 	idn.xmit_tid = timeout(idn_xmit_monitor, NULL, idn_xmit_monitor_freq);
12968 
12969 	mutex_exit(&idn.xmit_lock);
12970 }
12971