xref: /titanic_50/usr/src/uts/sun4u/starcat/io/scosmb.c (revision 07d06da50d310a325b457d6330165aebab1e0064)
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  * This file contains the Starcat Solaris Mailbox Client module.  This module
29  * handles mailbox messages from the SC to the OS (as opposed to messages sent
30  * to specific drivers) and vice versa.  Two task queues are created upon
31  * startup; one handles reading and processing of all incoming messages, while
32  * the other handles transmission of all outgoing messages.
33  */
34 
35 #include <sys/types.h>
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/sysmacros.h>
39 #include <sys/sunddi.h>
40 #include <sys/errno.h>
41 #include <sys/cmn_err.h>
42 #include <sys/condvar.h>
43 #include <sys/mutex.h>
44 #include <sys/disp.h>
45 #include <sys/thread.h>
46 #include <sys/debug.h>
47 #include <sys/cpu_sgnblk_defs.h>
48 #include <sys/machsystm.h>
49 #include <sys/modctl.h>
50 #include <sys/iosramio.h>
51 #include <sys/mboxsc.h>
52 #include <sys/promif.h>
53 #include <sys/uadmin.h>
54 #include <sys/cred.h>
55 #include <sys/taskq.h>
56 #include <sys/utsname.h>
57 #include <sys/plat_ecc_unum.h>
58 #include <sys/fm/protocol.h>
59 #include <sys/fm/util.h>
60 #include <sys/starcat.h>
61 #include <sys/plat_ecc_dimm.h>
62 #include <sys/plat_datapath.h>
63 
64 /* mailbox keys */
65 #define	SCDM_KEY	0x5343444d	/* 'S', 'C', 'D', 'M' */
66 #define	DMSC_KEY	0x444d5343	/* 'D', 'M', 'S', 'C' */
67 
68 /* mailbox commands */
69 #define	SCDM_CMD		('S' << 8)	/* generic SSP */
70 #define	SCDM_CMD_SUCCESS	(SCDM_CMD | 0x1)
71 #define	SCDM_GOTO_OBP		(SCDM_CMD | 0x2)
72 #define	SCDM_GOTO_PANIC		(SCDM_CMD | 0x3)
73 #define	SCDM_ENVIRON		(SCDM_CMD | 0x4) /* environmental intr */
74 #define	SCDM_SHUTDOWN		(SCDM_CMD | 0x5) /* setkeyswitch STANDBY */
75 #define	SCDM_GET_NODENAME	(SCDM_CMD | 0x6) /* get domain nodename */
76 #define	SCDM_LOG_ECC_ERROR	(SCDM_CMD | 0x7) /* ECC error logging */
77 #define	SCDM_LOG_ECC_INDICTMENT	(SCDM_CMD | 0x8) /* ECC indictment logging */
78 #define	SCDM_LOG_ECC		(SCDM_CMD | 0x9) /* ECC info */
79 #define	SCDM_LOG_ECC_CAP_INIT	(SCDM_CMD | 0xa) /* ECC Capability Init */
80 #define	SCDM_LOG_ECC_CAP_RESP	(SCDM_CMD | 0xb) /* ECC Capability Response */
81 #define	SCDM_DIMM_SERIAL_ID	(SCDM_CMD | 0xc) /* DIMM ser# req/resp */
82 #define	SCDM_DP_ERROR_MSG	(SCDM_CMD | 0xd) /* datapath error */
83 #define	SCDM_DP_FAULT_MSG	(SCDM_CMD | 0xe) /* datapath fault */
84 
85 /* general constants */
86 #define	GETMSG_TIMEOUT_MS	500
87 #define	PUTMSG_TIMEOUT_MS	6000
88 #define	MIN_INPUTQ_TASKS	2
89 #define	MAX_INPUTQ_TASKS	4
90 #define	MIN_OUTPUTQ_TASKS	2
91 #define	MAX_OUTPUTQ_TASKS	512
92 #ifndef TRUE
93 #define	TRUE	1
94 #endif
95 #ifndef FALSE
96 #define	FALSE	0
97 #endif
98 
99 clock_t ecc_message_timeout_ms = PUTMSG_TIMEOUT_MS;
100 
101 /*
102  * When a message needs to be sent to the SC, an scosmb_msgdata_t should be
103  * populated with the data to be used for the message, and a call to
104  * scosmb_process_output should be dispatched on the scosmb_output_taskq, with
105  * the address of the scosmb_msgdata_t structure as its arg.  The "length" and
106  * "data" fields can be used if the message needs to include data beyond the
107  * header fields (type, cmd, and transid) and that information must be recorded
108  * when the message is placed on the taskq.  If appropriate for the message type
109  * (e.g. nodename info that should always be the most recent available), the
110  * "data" field can be set to NULL and the additional data can be assembled
111  * immediately prior to sending the message in scosmb_process_output().
112  *
113  * If log_error is set, any errors in delivering the message cause a
114  * cmn_err() message to be issued.  If it is zero, the error is expressed
115  * only through return values.
116  */
117 typedef struct {
118 	uint32_t	type;
119 	uint32_t	cmd;
120 	uint64_t	transid;
121 	uint32_t	length;
122 	int		log_error;
123 	void		*data;
124 } scosmb_msgdata_t;
125 
126 /*
127  * Datapath error and fault messages arrive unsolicited.  The message data
128  * is contained in a plat_datapath_info_t structure.
129  */
130 typedef struct {
131 	uint8_t		type;		/* CDS, DX, EX, CP */
132 	uint8_t		pad;		/* for alignment */
133 	uint16_t	cpuid;		/* Safari ID of base CPU */
134 	uint32_t	t_value;	/* SERD timeout threshold (seconds) */
135 } plat_datapath_info_t;
136 
137 /* externally visible routines */
138 void scosmb_update_nodename(uint64_t transid);
139 
140 /* local routines */
141 static void scosmb_inbox_handler();
142 static void scosmb_process_input(void *unused);
143 static int scosmb_process_output(scosmb_msgdata_t *arg);
144 
145 /* local variables */
146 static uint8_t	scosmb_mboxsc_failed = FALSE;
147 static uint8_t	scosmb_mboxsc_timedout = FALSE;
148 static uint8_t	scosmb_nodename_event_pending = FALSE;
149 static char	scosmb_hdr[] = "SCOSMB:";
150 static kmutex_t scosmb_mutex;
151 static taskq_t	*scosmb_input_taskq = NULL;
152 static taskq_t	*scosmb_output_taskq = NULL;
153 
154 static char *dperrtype[] = {
155 	DP_ERROR_CDS,
156 	DP_ERROR_DX,
157 	DP_ERROR_EX,
158 	DP_ERROR_CP
159 };
160 
161 /*
162  * Structures from modctl.h used for loadable module support.
163  * SCOSMB is a "miscellaneous" module.
164  */
165 extern struct mod_ops mod_miscops;
166 
167 static struct modlmisc modlmisc = {
168 	&mod_miscops,
169 	"Sun Fire 15000 OS Mbox Client v1.10",
170 };
171 
172 static struct modlinkage modlinkage = {
173 	MODREV_1,
174 	(void *)&modlmisc,
175 	NULL
176 };
177 
178 
179 /*
180  * _init
181  *
182  * Loadable module support routine.  Initializes mutex and condition variables
183  * and starts thread.
184  */
185 int
_init(void)186 _init(void)
187 {
188 	int error;
189 
190 	/*
191 	 * Initialize the mailboxes
192 	 */
193 	if ((error = mboxsc_init(SCDM_KEY, MBOXSC_MBOX_IN,
194 	    scosmb_inbox_handler)) != 0) {
195 		cmn_err(CE_WARN, "%s mboxsc_init failed (0x%x)\n", scosmb_hdr,
196 		    error);
197 		return (error);
198 	}
199 
200 	if ((error = mboxsc_init(DMSC_KEY, MBOXSC_MBOX_OUT, NULL)) != 0) {
201 		cmn_err(CE_WARN, "%s mboxsc_init failed (0x%x)\n", scosmb_hdr,
202 		    error);
203 		(void) mboxsc_fini(SCDM_KEY);
204 		return (error);
205 	}
206 
207 	/*
208 	 * Initialize the global lock
209 	 */
210 	mutex_init(&scosmb_mutex, NULL, MUTEX_DEFAULT, NULL);
211 
212 	/*
213 	 * Create the task queues used for processing input and output messages
214 	 */
215 	scosmb_input_taskq = taskq_create("scosmb_input_taskq", 1,
216 	    minclsyspri, MIN_INPUTQ_TASKS, MAX_INPUTQ_TASKS, TASKQ_PREPOPULATE);
217 	scosmb_output_taskq = taskq_create("scosmb_output_taskq", 1,
218 	    minclsyspri, MIN_OUTPUTQ_TASKS, MAX_OUTPUTQ_TASKS,
219 	    TASKQ_PREPOPULATE);
220 
221 	/*
222 	 * Attempt to install the module.  If unsuccessful, uninitialize
223 	 * everything.
224 	 */
225 	error = mod_install(&modlinkage);
226 	if (error != 0) {
227 		taskq_destroy(scosmb_output_taskq);
228 		taskq_destroy(scosmb_input_taskq);
229 		mutex_destroy(&scosmb_mutex);
230 		(void) mboxsc_fini(DMSC_KEY);
231 		(void) mboxsc_fini(SCDM_KEY);
232 	}
233 
234 	return (error);
235 }
236 
237 /*
238  * _fini
239  *
240  * Loadable module support routine. Since this routine shouldn't be unloaded (it
241  * provides a critical service, and its symbols may be referenced externally),
242  * EBUSY is returned to prevent unloading.
243  */
244 int
_fini(void)245 _fini(void)
246 {
247 	return (EBUSY);
248 }
249 
250 /*
251  * _info
252  *
253  * Loadable module support routine.
254  */
255 int
_info(struct modinfo * modinfop)256 _info(struct modinfo *modinfop)
257 {
258 	int		error = 0;
259 
260 	error = mod_info(&modlinkage, modinfop);
261 	return (error);
262 }
263 
264 /*
265  * scosmb_inbox_handler() - mbox API event handler.
266  *
267  * This routine adds an entry to the scosmb_input_taskq that will cause the
268  * scosmb_process_input() routine to be called to service the SCDM mailbox.  The
269  * possibility that taskq_dispatch may fail when given KM_NOSLEEP is safely
270  * ignored because there can only be one message waiting in the mailbox at any
271  * given time, so the current message will end up being handled by one of the
272  * previously queued jobs (and a previous message presumably timed out before we
273  * got around to reading it).
274  */
275 static void
scosmb_inbox_handler()276 scosmb_inbox_handler()
277 {
278 	(void) taskq_dispatch(scosmb_input_taskq, scosmb_process_input, NULL,
279 	    KM_NOSLEEP);
280 }
281 
282 /*
283  * dp_get_cores()
284  *
285  * Checks cpu implementation for the input cpuid and returns
286  * the number of cores.
287  * If implementation cannot be determined, returns 1
288  */
289 static int
dp_get_cores(uint16_t cpuid)290 dp_get_cores(uint16_t cpuid)
291 {
292 	int	exp, ii, impl = 0, nc, slot;
293 
294 	exp = STARCAT_CPUID_TO_EXPANDER(cpuid);
295 	slot = STARCAT_CPUID_TO_BOARDSLOT(cpuid);
296 	if (slot == 1)
297 		nc = STARCAT_SLOT1_CPU_MAX;
298 	else
299 		nc = plat_max_cpu_units_per_board();
300 
301 	/* find first with valid implementation */
302 	for (ii = 0; ii < nc; ii++)
303 		if (cpu[MAKE_CPUID(exp, slot, ii)]) {
304 			impl = cpunodes[MAKE_CPUID(exp, slot, ii)].
305 			    implementation;
306 			break;
307 		}
308 
309 	if (IS_JAGUAR(impl) || IS_PANTHER(impl))
310 		return (2);
311 	else
312 		return (1);
313 
314 }
315 
316 /*
317  * dp_payload_add_cpus()
318  *
319  * From datapath mailbox message, determines the number of and safari IDs
320  * for affected cpus, then adds this info to the datapath ereport.
321  *
322  * Input maxcat (if set) is a count of maxcat cpus actually present - it is
323  * a count of cpuids, which takes into account multi-core architecture.
324  */
325 static int
dp_payload_add_cpus(plat_datapath_info_t * dpmsg,nvlist_t * erp,int maxcat)326 dp_payload_add_cpus(plat_datapath_info_t *dpmsg, nvlist_t *erp, int maxcat)
327 {
328 	int		jj = 0, numcpus = 0, nummaxcpus = 0;
329 	int		count, exp, ii, num, ncores, ret, slot, port;
330 	uint16_t	*dparray, cpuid;
331 	uint64_t	*snarray;
332 
333 	/* check for multiple core architectures */
334 	ncores = dp_get_cores(dpmsg->cpuid);
335 
336 	/*
337 	 * Determine the number of cpu cores impacted
338 	 */
339 	switch (dpmsg->type) {
340 		case DP_CDS_TYPE:
341 			if (maxcat)
342 				nummaxcpus = ncores;
343 			else
344 				numcpus = ncores;
345 			break;
346 
347 		case DP_DX_TYPE:
348 			if (maxcat)
349 				nummaxcpus = 2 * ncores;
350 			else
351 				numcpus = 2 * ncores;
352 			break;
353 
354 		case DP_EX_TYPE:
355 			if (maxcat)
356 				nummaxcpus = STARCAT_SLOT1_CPU_MAX;
357 			else
358 				numcpus = plat_max_cpu_units_per_board();
359 			break;
360 
361 		case DP_CP_TYPE:
362 			/*
363 			 * SC-DE supplies the base cpuid affected, if
364 			 * maxcat id was given, there's no slot 0 board
365 			 * present.
366 			 */
367 
368 			if (!maxcat) {
369 				/* Slot 0 id was given - set numcpus */
370 				numcpus = plat_max_cpu_units_per_board();
371 			}
372 
373 			/* there may/may not be maxcats. set a count anyway */
374 			nummaxcpus = STARCAT_SLOT1_CPU_MAX;
375 
376 			break;
377 
378 		default:
379 			ASSERT(0);
380 			return (-1);
381 	}
382 
383 	/* Allocate space for cores */
384 	num = numcpus + nummaxcpus;
385 	dparray = kmem_zalloc(num * sizeof (uint16_t *), KM_SLEEP);
386 
387 	/*
388 	 * populate dparray with impacted cores (only those present)
389 	 */
390 	exp = STARCAT_CPUID_TO_EXPANDER(dpmsg->cpuid);
391 	slot = STARCAT_CPUID_TO_BOARDSLOT(dpmsg->cpuid);
392 	port = STARCAT_CPUID_TO_LPORT(dpmsg->cpuid);
393 
394 	mutex_enter(&cpu_lock);
395 
396 	switch (dpmsg->type) {
397 		case DP_CDS_TYPE:
398 			/*
399 			 * For a CDS error, it's the reporting cpuid
400 			 * and it's other core (if present)
401 			 */
402 			cpuid = dpmsg->cpuid & 0xFFFB; 	/* core 0 */
403 			if (cpu[cpuid])
404 				dparray[jj++] = cpuid;
405 
406 			cpuid = dpmsg->cpuid | 0x4; 	/* core 1 */
407 			if (cpu[cpuid])
408 				dparray[jj++] = cpuid;
409 			break;
410 
411 		case DP_DX_TYPE:
412 			/*
413 			 * For a DX error, it's the reporting cpuid (all
414 			 * cores), and the other CPU sharing the same
415 			 * DX<-->DCDS interface (all cores)
416 			 */
417 
418 			/* reporting cpuid */
419 			cpuid = dpmsg->cpuid & 0xFFFB; 	/* core 0 */
420 
421 			if (cpu[cpuid])
422 				dparray[jj++] = cpuid;
423 
424 			cpuid = dpmsg->cpuid | 0x4; 	/* core 1 */
425 			if (cpu[cpuid])
426 				dparray[jj++] = cpuid;
427 
428 			/* find partner cpuid */
429 			if (port == 0 || port == 2)
430 				cpuid = dpmsg->cpuid | 0x1;
431 			else
432 				cpuid = dpmsg->cpuid & 0xFFFE;
433 
434 			/* add partner cpuid */
435 			cpuid &= 0xFFFB; 	/* core 0 */
436 			if (cpu[cpuid])
437 				dparray[jj++] = cpuid;
438 
439 			cpuid |= 0x4; 	/* core 1 */
440 			if (cpu[cpuid])
441 				dparray[jj++] = cpuid;
442 			break;
443 
444 		case DP_EX_TYPE:
445 			/*
446 			 * For an EX error, it is all cpuids (all cores)
447 			 * on the reporting board
448 			 */
449 
450 			if (slot == 1) 			/* maxcat */
451 				count = nummaxcpus;
452 			else
453 				count = numcpus;
454 
455 			for (ii = 0; ii < count; ii++) {
456 				cpuid = MAKE_CPUID(exp, slot, ii);
457 				if (cpu[cpuid])
458 					dparray[jj++] = cpuid;
459 			}
460 			break;
461 
462 		case DP_CP_TYPE:
463 			/*
464 			 * For a CP error, it is all cpuids (all cores)
465 			 * on both boards (SB & IO) in the boardset
466 			 */
467 
468 			/* Do slot 0 */
469 			for (ii = 0; ii < numcpus; ii++) {
470 				cpuid = MAKE_CPUID(exp, 0, ii);
471 				if (cpu[cpuid])
472 					dparray[jj++] = cpuid;
473 			}
474 
475 			/* Do slot 1 */
476 			for (ii = 0; ii < nummaxcpus; ii++) {
477 				cpuid = MAKE_CPUID(exp, 1, ii);
478 				if (cpu[cpuid])
479 					dparray[jj++] = cpuid;
480 			}
481 			break;
482 	}
483 
484 	mutex_exit(&cpu_lock);
485 
486 	/*
487 	 * The datapath message could not be associated with any
488 	 * configured CPU.
489 	 */
490 	if (!jj) {
491 		kmem_free(dparray, num * sizeof (uint16_t *));
492 		ret = nvlist_add_uint32(erp, DP_LIST_SIZE, jj);
493 		ASSERT(ret == 0);
494 		return (-1);
495 	}
496 
497 	snarray = kmem_zalloc(jj * sizeof (uint64_t *), KM_SLEEP);
498 	for (ii = 0; ii < jj; ii++)
499 		snarray[ii] = cpunodes[dparray[ii]].device_id;
500 
501 	ret = nvlist_add_uint32(erp, DP_LIST_SIZE, jj);
502 	ret |= nvlist_add_uint16_array(erp, DP_LIST, dparray, jj);
503 	ret |= nvlist_add_uint64_array(erp, SN_LIST, snarray, jj);
504 	ASSERT(ret == 0);
505 
506 	kmem_free(dparray, num * sizeof (uint16_t *));
507 	kmem_free(snarray, jj * sizeof (uint64_t *));
508 
509 	return (0);
510 }
511 
512 /*
513  * dp_trans_event() - datapath message handler.
514  *
515  * Process datapath error and fault messages received from the SC.  Checks
516  * for, and disregards, messages associated with I/O boards.  Otherwise,
517  * extracts message info to produce a datapath ereport.
518  */
519 static void
dp_trans_event(plat_datapath_info_t * dpmsg,int msgtype)520 dp_trans_event(plat_datapath_info_t *dpmsg, int msgtype)
521 {
522 	nvlist_t	*erp, *detector, *hcelem;
523 	char		buf[FM_MAX_CLASS];
524 	int		exp, slot, i, maxcat = 0;
525 
526 	/* check for I/O board message */
527 	exp = STARCAT_CPUID_TO_EXPANDER(dpmsg->cpuid);
528 	slot = STARCAT_CPUID_TO_BOARDSLOT(dpmsg->cpuid);
529 
530 	if (slot) {
531 		mutex_enter(&cpu_lock);
532 		for (i = 0; i < STARCAT_SLOT1_CPU_MAX; i++) {
533 			if (cpu[MAKE_CPUID(exp, slot, i)]) {
534 				/* maxcat cpu present */
535 				maxcat++;
536 			}
537 		}
538 		mutex_exit(&cpu_lock);
539 
540 		/*
541 		 * Ignore I/O board msg
542 		 */
543 		if (maxcat == 0)
544 			return;
545 	}
546 
547 	/* allocate space for ereport */
548 	erp = fm_nvlist_create(NULL);
549 
550 	/*
551 	 *
552 	 * Member Name	Data Type	   Comments
553 	 * -----------	---------	   -----------
554 	 * version	uint8		   0
555 	 * class	string		   "asic"
556 	 * ENA		uint64		   ENA Format 1
557 	 * detector	fmri		   aggregated ID data for SC-DE
558 	 *
559 	 * Datapath ereport subclasses and data payloads:
560 	 * There will be two types of ereports (error and fault) which will be
561 	 * identified by the "type" member.
562 	 *
563 	 * ereport.asic.starcat.cds.cds-dp
564 	 * ereport.asic.starcat.dx.dx-dp
565 	 * ereport.asic.starcat.sdi.sdi-dp
566 	 * ereport.asic.starcat.cp.cp-dp
567 	 *
568 	 * Member Name	Data Type	Comments
569 	 * -----------	---------	-----------
570 	 * erptype	uint16		derived from message type: error or
571 	 *				fault
572 	 * t-value	uint32		SC's datapath SERD timeout threshold
573 	 * dp-list-sz	uint8		number of dp-list array elements
574 	 * dp-list	array of uint16	Safari IDs of affected cpus
575 	 * sn-list	array of uint64	Serial numbers of affected cpus
576 	 *
577 	 */
578 
579 	/* compose common ereport elements */
580 	detector = fm_nvlist_create(NULL);
581 
582 	/*
583 	 * Create legacy FMRI for the detector
584 	 */
585 	switch (dpmsg->type) {
586 		case DP_CDS_TYPE:
587 		case DP_DX_TYPE:
588 			if (slot == 1)
589 				(void) snprintf(buf, FM_MAX_CLASS, "IO%d", exp);
590 			else
591 				(void) snprintf(buf, FM_MAX_CLASS, "SB%d", exp);
592 			break;
593 
594 		case DP_EX_TYPE:
595 			(void) snprintf(buf, FM_MAX_CLASS, "EX%d", exp);
596 			break;
597 
598 		case DP_CP_TYPE:
599 			(void) snprintf(buf, FM_MAX_CLASS, "CP");
600 			break;
601 
602 		default:
603 			(void) snprintf(buf, FM_MAX_CLASS, "UNKNOWN");
604 			break;
605 	}
606 
607 	hcelem = fm_nvlist_create(NULL);
608 
609 	(void) nvlist_add_string(hcelem, FM_FMRI_HC_NAME, FM_FMRI_LEGACY_HC);
610 	(void) nvlist_add_string(hcelem, FM_FMRI_HC_ID, buf);
611 
612 	(void) nvlist_add_uint8(detector, FM_VERSION, FM_HC_SCHEME_VERSION);
613 	(void) nvlist_add_string(detector, FM_FMRI_SCHEME, FM_FMRI_SCHEME_HC);
614 	(void) nvlist_add_string(detector, FM_FMRI_HC_ROOT, "");
615 	(void) nvlist_add_uint32(detector, FM_FMRI_HC_LIST_SZ, 1);
616 	(void) nvlist_add_nvlist_array(detector, FM_FMRI_HC_LIST, &hcelem, 1);
617 
618 	/* build ereport class name */
619 	(void) snprintf(buf, FM_MAX_CLASS, "asic.starcat.%s.%s-%s",
620 	    dperrtype[dpmsg->type], dperrtype[dpmsg->type],
621 	    FM_ERROR_DATAPATH);
622 
623 	fm_ereport_set(erp, FM_EREPORT_VERSION, buf,
624 	    fm_ena_generate(0, FM_ENA_FMT1), detector, NULL);
625 
626 	/* add payload elements */
627 	if (msgtype == SCDM_DP_ERROR_MSG) {
628 		fm_payload_set(erp,
629 		    DP_EREPORT_TYPE, DATA_TYPE_UINT16, DP_ERROR, NULL);
630 	} else {
631 		fm_payload_set(erp,
632 		    DP_EREPORT_TYPE, DATA_TYPE_UINT16, DP_FAULT, NULL);
633 	}
634 
635 	fm_payload_set(erp, DP_TVALUE, DATA_TYPE_UINT32, dpmsg->t_value, NULL);
636 
637 	if (dp_payload_add_cpus(dpmsg, erp, maxcat) == 0) {
638 		/* post ereport */
639 		fm_ereport_post(erp, EVCH_SLEEP);
640 	}
641 
642 	/* free ereport memory */
643 	fm_nvlist_destroy(erp, FM_NVA_FREE);
644 	fm_nvlist_destroy(detector, FM_NVA_FREE);
645 
646 }
647 
648 /*
649  * scosmb_process_input() - incoming message processing routine
650  *
651  * this routine attempts to read a message from the SCDM mailbox and, if
652  * successful, processes the command.  if an unrecoverable error is encountered,
653  * the scosmb_task thread will be terminated.
654  */
655 /* ARGSUSED0 */
656 static void
scosmb_process_input(void * unused)657 scosmb_process_input(void *unused)
658 {
659 	int 			error;
660 	scosmb_msgdata_t	 msg;
661 	proc_t			*initpp;
662 	plat_capability_data_t	*cap;	/* capability msg contents ptr */
663 	int			cap_size;
664 	int			cap_ver_len;
665 	scosmb_msgdata_t	*cap_msgdatap; /* capability msg response */
666 	int			max_size;
667 
668 	/*
669 	 * Attempt to read a message from the SCDM mailbox.
670 	 *
671 	 * Setup a local buffer to read incoming messages from the SC.
672 	 */
673 	cap_ver_len = strlen(utsname.release) + strlen(utsname.version) + 2;
674 	cap_size = sizeof (plat_capability_data_t) + cap_ver_len;
675 	max_size = MAX(cap_size, sizeof (plat_dimm_sid_board_data_t));
676 
677 	msg.type = 0;
678 	msg.cmd = 0;
679 	msg.transid = 0;
680 	msg.length = max_size;
681 	msg.log_error = 0;
682 	msg.data = kmem_zalloc(max_size, KM_SLEEP);
683 
684 	error = mboxsc_getmsg(SCDM_KEY, &msg.type, &msg.cmd, &msg.transid,
685 	    &msg.length, msg.data, GETMSG_TIMEOUT_MS);
686 
687 	/*
688 	 * If EAGAIN or ETIMEDOUT was received, give up.  The SC can just try
689 	 * again if it was important.  If any other non-zero error was
690 	 * encountered, the mailbox service is broken, and there's nothing more
691 	 * we can do.
692 	 */
693 	mutex_enter(&scosmb_mutex);
694 	if ((error == EAGAIN) || (error == ETIMEDOUT)) {
695 		mutex_exit(&scosmb_mutex);
696 		return;
697 	} else if (error != 0) {
698 		/*
699 		 * The mailbox service appears to be badly broken.  If it was
700 		 * working previously, generate a warning and set a flag to
701 		 * avoid repeating the warning on subsequent failures.
702 		 */
703 		if (!scosmb_mboxsc_failed) {
704 			scosmb_mboxsc_failed = TRUE;
705 			cmn_err(CE_WARN, "%s mboxsc error (0x%x)\n", scosmb_hdr,
706 			    error);
707 		}
708 		mutex_exit(&scosmb_mutex);
709 		return;
710 	} else {
711 		/*
712 		 * If the mailbox module failed previously, it appears to have
713 		 * recovered, so we'll want to generate a warning if it fails
714 		 * again.
715 		 */
716 		scosmb_mboxsc_failed = FALSE;
717 	}
718 	mutex_exit(&scosmb_mutex);
719 
720 	/*
721 	 * A message was successfully received, so go ahead and process it.
722 	 */
723 	switch (msg.cmd) {
724 
725 	case SCDM_GOTO_OBP:	/* jump to OBP */
726 		debug_enter("SC requested jump to OBP");
727 		break;
728 
729 	case SCDM_GOTO_PANIC:	/* Panic the domain */
730 		cmn_err(CE_PANIC, "%s SC requested PANIC\n", scosmb_hdr);
731 		break;
732 
733 	case SCDM_SHUTDOWN:	/* graceful shutdown */
734 		cmn_err(CE_WARN, "%s SC requested a shutdown ", scosmb_hdr);
735 		(void) kadmin(A_SHUTDOWN, AD_HALT, NULL, kcred);
736 		/*
737 		 * In the event kadmin does not bring down the
738 		 * domain, environmental shutdown is forced
739 		 */
740 		/*FALLTHROUGH*/
741 	case SCDM_ENVIRON:	/* environmental shutdown */
742 		/*
743 		 * Send SIGPWR to init(1) it will run rc0,
744 		 * which will uadmin to power down.
745 		 */
746 		mutex_enter(&pidlock);
747 		initpp = prfind(P_INITPID);
748 		mutex_exit(&pidlock);
749 
750 
751 		/*
752 		 * If we're still booting and init(1) isn't set up yet,
753 		 * simply halt.
754 		 */
755 		if (initpp == NULL) {
756 			extern void halt(char *);
757 			cmn_err(CE_WARN, "%s Environmental Interrupt",
758 			    scosmb_hdr);
759 			power_down((char *)NULL);
760 			halt("Power off the System!\n");
761 		}
762 
763 		/*
764 		 * else, graceful shutdown with inittab and all
765 		 * getting involved
766 		 */
767 		psignal(initpp, SIGPWR);
768 		break;
769 
770 	case SCDM_GET_NODENAME:
771 		scosmb_update_nodename(msg.transid);
772 		break;
773 
774 	case SCDM_LOG_ECC_CAP_RESP:
775 		/*
776 		 * The SC has responded to our initiator capability message
777 		 * issued during the boot flow via scosmb_update_nodename().
778 		 *
779 		 * Parse the incoming data, and appropriately set SC
780 		 * capabilities...
781 		 */
782 		cap = (plat_capability_data_t *)msg.data;
783 		plat_ecc_capability_sc_set(cap->capd_capability);
784 		break;
785 
786 	case SCDM_LOG_ECC_CAP_INIT:
787 		/*
788 		 * The SC has initiated a capability messaging exchange with
789 		 * the OS.
790 		 *
791 		 * We start out just as we do for an SC response capability
792 		 * message, a parse of incoming data to appropriately set SC
793 		 * described capabilities...
794 		 */
795 		cap = (plat_capability_data_t *)msg.data;
796 		plat_ecc_capability_sc_set(cap->capd_capability);
797 		/*
798 		 * The next step is setting up our Response to the SC.
799 		 *
800 		 * Allocate memory for message data, initialize appropriately,
801 		 * and place a new job on the scosmb_output_taskq for
802 		 * SCDM_LOG_ECC_CAP_RESP, our OS capability messaging response
803 		 * to the SC initiated sequence detected here.
804 		 */
805 		cap_msgdatap = kmem_zalloc(sizeof (scosmb_msgdata_t), KM_SLEEP);
806 		cap_msgdatap->type = MBOXSC_MSG_EVENT;
807 		cap_msgdatap->cmd = SCDM_LOG_ECC_CAP_RESP;
808 		cap_msgdatap->transid = 0;
809 		(void) taskq_dispatch(scosmb_output_taskq,
810 		    (task_func_t *)scosmb_process_output, cap_msgdatap,
811 		    KM_SLEEP);
812 		break;
813 
814 	case SCDM_DP_ERROR_MSG:
815 	case SCDM_DP_FAULT_MSG:
816 		dp_trans_event(msg.data, msg.cmd);
817 		break;
818 
819 	case SCDM_DIMM_SERIAL_ID:
820 		(void) plat_store_mem_sids(msg.data);
821 		break;
822 
823 	default:
824 		cmn_err(CE_WARN, "%s invalid command (0x%x)\n", scosmb_hdr,
825 		    msg.cmd);
826 		break;
827 	}
828 
829 	/*
830 	 * Free up buffer for incoming messasge data that we allocated earlier
831 	 */
832 	kmem_free(msg.data, max_size);
833 }
834 
835 /*
836  * scosmb_process_output() - outgoing message processing routine
837  *
838  * This routine handles jobs that are queued on the scosmb_output_taskq, or
839  * sent directly from scosmb_log_ecc_error.  Each job corresponds to a single
840  * mailbox message that needs to be sent to the SC via the DMSC mailbox.  Some
841  * processing of the message may be performed before it is sent to the SC,
842  * depending on the value of the command field.
843  */
844 static int
scosmb_process_output(scosmb_msgdata_t * msgdatap)845 scosmb_process_output(scosmb_msgdata_t *msgdatap)
846 {
847 	int 			error;
848 	int			length;
849 	char			nodename[_SYS_NMLN];
850 	void			*free_data;
851 	int			free_data_len;
852 	int			cap_size;
853 	int			cap_ver_len;
854 	plat_capability_data_t	*cap = NULL;
855 
856 	/*
857 	 * This shouldn't ever happen, but it can't hurt to check anyway.
858 	 */
859 	if (msgdatap == NULL) {
860 		return (EINVAL);
861 	}
862 
863 	/*
864 	 * If data was passed in, we'll need to free it before returning.
865 	 */
866 	free_data = msgdatap->data;
867 	free_data_len = msgdatap->length;
868 
869 	/*
870 	 * Some commands may need additional processing prior to transmission.
871 	 */
872 	switch (msgdatap->cmd) {
873 		/*
874 		 * Since the SC is only interested in the most recent value of
875 		 * utsname.nodename, we wait until now to collect that data.  We
876 		 * also use a global flag to prevent multiple event-type
877 		 * nodename messages from being queued at the same time for the
878 		 * same reason.
879 		 */
880 		case SCDM_GET_NODENAME:
881 			mutex_enter(&scosmb_mutex);
882 			length = strlen(utsname.nodename);
883 			ASSERT(length < _SYS_NMLN);
884 			if (length == 0) {
885 				msgdatap->length = 0;
886 				msgdatap->data = NULL;
887 			} else {
888 				bcopy(utsname.nodename, nodename, length);
889 				nodename[length++] = '\0';
890 				msgdatap->data = nodename;
891 				msgdatap->length = length;
892 			}
893 			if (msgdatap->transid == 0) {
894 				scosmb_nodename_event_pending = FALSE;
895 			}
896 			mutex_exit(&scosmb_mutex);
897 			break;
898 
899 		/*
900 		 * SCDM_LOG_ECC_CAP_INIT
901 		 * Initiator Capability message from OS to SC
902 		 *
903 		 * We construct and send an initiator capability message
904 		 * every time we go through scosmb_update_nodename(), which
905 		 * works out to getting an "initiator" capability message
906 		 * sent from the OS to the SC during the OS boot flow.
907 		 *
908 		 * The SC also issues a request to scosmb_update_nodename()
909 		 * during an SC reboot.  Which results in an additional
910 		 * capability message exchange during SC reboot scenarios.
911 		 *
912 		 * SCDM_LOG_ECC_CAP_RESP
913 		 * Response Capability message from SC to OS
914 		 *
915 		 * In certain scenarios, the SC could initiate a capability
916 		 * messaging exchange with the OS.  Processing starts in
917 		 * scosmb_process_input(), where we detect an incoming
918 		 * initiator capability message from the SC.  We finish
919 		 * processing here, by sending a response capability message
920 		 * back to the SC that reflects OS capabilities.
921 		 */
922 		case SCDM_LOG_ECC_CAP_INIT:
923 			/*FALLTHROUGH*/
924 		case SCDM_LOG_ECC_CAP_RESP:
925 			mutex_enter(&scosmb_mutex);
926 
927 			cap_ver_len = strlen(utsname.release) +
928 			    strlen(utsname.version) + 2;
929 
930 			cap_size = sizeof (plat_capability_data_t) +
931 			    cap_ver_len;
932 
933 			cap =  kmem_zalloc(cap_size, KM_SLEEP);
934 
935 			cap->capd_major_version = PLAT_ECC_CAP_VERSION_MAJOR;
936 			cap->capd_minor_version = PLAT_ECC_CAP_VERSION_MINOR;
937 			cap->capd_msg_type = PLAT_ECC_CAPABILITY_MESSAGE;
938 			cap->capd_msg_length =  cap_size;
939 
940 			cap->capd_capability =
941 			    PLAT_ECC_CAPABILITY_DOMAIN_DEFAULT;
942 
943 			/*
944 			 * Build the capability solaris_version string:
945 			 * utsname.release + " " + utsname.version
946 			 */
947 			(void) snprintf(cap->capd_solaris_version,
948 			    cap_ver_len, "%s %s", utsname.release,
949 			    utsname.version);
950 
951 			/*
952 			 * The capability message is constructed, now plug it
953 			 * into the starcat msgdatap:
954 			 */
955 			msgdatap->data   = (plat_capability_data_t *)cap;
956 			msgdatap->length = cap_size;
957 
958 			/*
959 			 * Finished with initiator/response capability
960 			 * message set up.
961 			 *
962 			 * Note that after sending an "initiator" capability
963 			 * message, we can expect a subsequent "response"
964 			 * capability message from the SC, which we will
965 			 * pick up and minimally handle later,
966 			 * in scosmb_process_input().
967 			 *
968 			 * If we're sending a "response" capability message
969 			 * to the SC, then we're done once the message is sent.
970 			 */
971 
972 			if (msgdatap->transid == 0) {
973 				scosmb_nodename_event_pending = FALSE;
974 			}
975 			mutex_exit(&scosmb_mutex);
976 			break;
977 
978 		default:
979 			break;
980 	}
981 
982 	/*
983 	 * Attempt to send the message.
984 	 */
985 	error = mboxsc_putmsg(DMSC_KEY, msgdatap->type, msgdatap->cmd,
986 	    &msgdatap->transid, msgdatap->length, msgdatap->data,
987 	    ecc_message_timeout_ms);
988 
989 	/*
990 	 * Free any allocated memory that was passed in.
991 	 */
992 	if (free_data != NULL) {
993 		kmem_free(free_data, free_data_len);
994 	}
995 
996 	if (cap != NULL) {
997 		kmem_free(cap, cap_size);
998 	}
999 
1000 	kmem_free(msgdatap, sizeof (scosmb_msgdata_t));
1001 
1002 	/*
1003 	 * If EAGAIN or ETIMEDOUT was received, give up.  The sender can try
1004 	 * again if it was important.  If any other non-zero error was
1005 	 * encountered, the mailbox service is broken, and there's nothing more
1006 	 * we can do.
1007 	 */
1008 	mutex_enter(&scosmb_mutex);
1009 	if ((error == EAGAIN) || (error == ETIMEDOUT)) {
1010 		if (msgdatap->log_error && !scosmb_mboxsc_timedout) {
1011 			/*
1012 			 * Indictment mailbox messages use the return value to
1013 			 * indicate a problem in the mailbox.  For Error
1014 			 * mailbox messages, we'll have to use a syslog message.
1015 			 */
1016 			scosmb_mboxsc_timedout = TRUE;
1017 			cmn_err(CE_NOTE, "!Solaris failed to send a message "
1018 			    "(0x%x/0x%x) to the System Controller. Error: %d",
1019 			    msgdatap->type, msgdatap->cmd, error);
1020 		}
1021 	} else if (error != 0) {
1022 		/*
1023 		 * The mailbox service appears to be badly broken.  If it was
1024 		 * working previously, generate a warning and set a flag to
1025 		 * avoid repeating the warning on subsequent failures.
1026 		 */
1027 		if (msgdatap->log_error && !scosmb_mboxsc_failed) {
1028 			scosmb_mboxsc_failed = TRUE;
1029 			cmn_err(CE_NOTE, "!An internal error (%d) occurred "
1030 			    "while processing this message (0x%x/0x%x)",
1031 			    error, msgdatap->type, msgdatap->cmd);
1032 		}
1033 	} else {
1034 		/*
1035 		 * If the mailbox module failed previously, it appears to have
1036 		 * recovered, so we'll want to generate a warning if it fails
1037 		 * again.
1038 		 */
1039 		scosmb_mboxsc_failed = scosmb_mboxsc_timedout = FALSE;
1040 	}
1041 	mutex_exit(&scosmb_mutex);
1042 	return (error);
1043 }
1044 
1045 /*
1046  * scosmb_update_nodename() - nodename update routine
1047  *
1048  * this routine, which may be invoked from outside of the scosmb module, will
1049  * cause the current nodename to be sent to the SC.  The mailbox message sent to
1050  * the SC will use the indicated transaction ID, and will either be a reply
1051  * message if the ID is non-zero or an event message if it is 0.
1052  *
1053  * Capability messaging enhancements:
1054  *    Every time we move through this code flow, we put an "initiator
1055  *    capability message" on the message output taskq.  This action will
1056  *    get a capability message sent to the SC from the OS during boot
1057  *    scenarios.  A capability message exchange will also happen for
1058  *    SC reboot scenarios, as the SC will initiate a nodename update
1059  *    as a matter of course while coming back up.
1060  *
1061  *    We'll also get an extraneous capability message sent
1062  *    to the SC from time to time, but that won't hurt anything.
1063  */
1064 void
scosmb_update_nodename(uint64_t transid)1065 scosmb_update_nodename(uint64_t transid)
1066 {
1067 	scosmb_msgdata_t	*msgdatap, *cap_msgdatap;
1068 
1069 	/*
1070 	 * If we're generating an unsolicited nodename update (presumably having
1071 	 * been called from platmod:plat_nodename_set()), there's no need to add
1072 	 * a new job to the queue if there is already one on it that will be
1073 	 * sending the latest nodename data.
1074 	 */
1075 	mutex_enter(&scosmb_mutex);
1076 	if (transid == 0) {
1077 		if (scosmb_nodename_event_pending) {
1078 			mutex_exit(&scosmb_mutex);
1079 			return;
1080 		} else {
1081 			scosmb_nodename_event_pending = TRUE;
1082 		}
1083 	}
1084 	mutex_exit(&scosmb_mutex);
1085 
1086 	/*
1087 	 * Allocate memory for the message data, initialize it, and place a new
1088 	 * job on the scosmb_output_taskq for SCDM_GET_NODENAME.
1089 	 */
1090 	msgdatap = (scosmb_msgdata_t *)kmem_zalloc(sizeof (scosmb_msgdata_t),
1091 	    KM_SLEEP);
1092 
1093 	msgdatap->type = (transid == 0) ? MBOXSC_MSG_EVENT : MBOXSC_MSG_REPLY;
1094 	msgdatap->cmd = SCDM_GET_NODENAME;
1095 	msgdatap->transid = transid;
1096 	msgdatap->log_error = 1;
1097 
1098 	(void) taskq_dispatch(scosmb_output_taskq,
1099 	    (task_func_t *)scosmb_process_output, msgdatap, KM_SLEEP);
1100 
1101 	/*
1102 	 * Next, allocate memory, initialize, and place a new job on the
1103 	 * scosmb_output_taskq for SCDM_LOG_ECC_CAP_INIT.  That's a
1104 	 * capability message, where we're the initiator.
1105 	 */
1106 	cap_msgdatap = kmem_zalloc(sizeof (scosmb_msgdata_t), KM_SLEEP);
1107 
1108 	cap_msgdatap->type = (transid == 0) ?
1109 	    MBOXSC_MSG_EVENT : MBOXSC_MSG_REPLY;
1110 	cap_msgdatap->cmd = SCDM_LOG_ECC_CAP_INIT;
1111 	cap_msgdatap->transid = transid;
1112 	cap_msgdatap->log_error = 1;
1113 
1114 	(void) taskq_dispatch(scosmb_output_taskq,
1115 	    (task_func_t *)scosmb_process_output, cap_msgdatap, KM_SLEEP);
1116 }
1117 
1118 /*
1119  * scosmb_log_ecc_error() - Record ECC error information to SC
1120  * For ECC error messages, send the messages through a taskq mechanism
1121  * to prevent impaired system performance during ECC floods.  Indictment
1122  * messages have already passed through a taskq, so directly call the
1123  * output function.
1124  */
1125 int
scosmb_log_ecc_error(plat_ecc_message_type_t msg_type,void * datap)1126 scosmb_log_ecc_error(plat_ecc_message_type_t msg_type, void *datap)
1127 {
1128 	scosmb_msgdata_t	*msg_header_ptr;
1129 	uint32_t		msg_cmd, msg_length;
1130 	int			sleep_flag, log_error;
1131 	int			do_queue;	/* Set to 1 if taskq needed */
1132 
1133 	/*
1134 	 * Set header type and length for message
1135 	 */
1136 	switch (msg_type) {
1137 	case PLAT_ECC_ERROR_MESSAGE:
1138 		/*
1139 		 * We do not want to sleep in an error logging thread.  So,
1140 		 * we set the NOSLEEP flag and go through a taskq before we
1141 		 * send the message.
1142 		 */
1143 		msg_cmd = SCDM_LOG_ECC_ERROR;
1144 		msg_length = sizeof (plat_ecc_error_data_t);
1145 		sleep_flag = KM_NOSLEEP;
1146 		log_error = 1;
1147 		do_queue = 1;
1148 		break;
1149 	case PLAT_ECC_ERROR2_MESSAGE:
1150 		msg_cmd = SCDM_LOG_ECC;
1151 		msg_length = sizeof (plat_ecc_error2_data_t);
1152 		sleep_flag = KM_NOSLEEP;
1153 		log_error = 1;
1154 		do_queue = 1;
1155 		break;
1156 	case PLAT_ECC_INDICTMENT_MESSAGE:
1157 		/*
1158 		 * For indictment messages, we're allowed to sleep, and we
1159 		 * can directly call the output function, since we've already
1160 		 * gone through a taskq
1161 		 */
1162 		msg_cmd = SCDM_LOG_ECC_INDICTMENT;
1163 		msg_length = sizeof (plat_ecc_indictment_data_t);
1164 		sleep_flag = KM_SLEEP;
1165 		log_error = 0;
1166 		do_queue = 0;
1167 		break;
1168 	case PLAT_ECC_INDICTMENT2_MESSAGE:
1169 		/*
1170 		 * For indictment2 messages, we're allowed to sleep, and we
1171 		 * can directly call the output function, since we've already
1172 		 * gone through a taskq
1173 		 */
1174 		msg_cmd = SCDM_LOG_ECC;
1175 		msg_length = sizeof (plat_ecc_indictment2_data_t);
1176 		sleep_flag = KM_SLEEP;
1177 		log_error = 0;
1178 		do_queue = 0;
1179 		break;
1180 
1181 	case PLAT_ECC_DIMM_SID_MESSAGE:
1182 		/*
1183 		 * For DIMM sid request messages, we're allowed to sleep, and we
1184 		 * can directly call the output function, since we've already
1185 		 * gone through a taskq
1186 		 */
1187 		msg_cmd = SCDM_DIMM_SERIAL_ID;
1188 		msg_length = sizeof (plat_dimm_sid_request_data_t);
1189 		sleep_flag = KM_SLEEP;
1190 		log_error = 0;
1191 		do_queue = 0;
1192 		break;
1193 
1194 	default:
1195 		return (EINVAL);
1196 	}
1197 
1198 	/*
1199 	 * Allocate memory for the mailbox message header.
1200 	 */
1201 	msg_header_ptr =
1202 	    (scosmb_msgdata_t *)kmem_zalloc(sizeof (scosmb_msgdata_t),
1203 	    sleep_flag);
1204 
1205 	if (msg_header_ptr == NULL) {
1206 #ifdef DEBUG
1207 		cmn_err(CE_WARN, "failed to allocate space for scosmb "
1208 		    "message header.");
1209 #endif	/* DEBUG */
1210 		return (ENOMEM);
1211 	}
1212 
1213 	msg_header_ptr->type = MBOXSC_MSG_EVENT;
1214 	msg_header_ptr->cmd = msg_cmd;
1215 	msg_header_ptr->transid = 0;
1216 	msg_header_ptr->log_error = log_error;
1217 
1218 	/*
1219 	 * Allocate memory for the mailbox message payload.
1220 	 */
1221 	msg_header_ptr->length = msg_length;
1222 	msg_header_ptr->data = kmem_zalloc((size_t)msg_length, sleep_flag);
1223 
1224 	if (msg_header_ptr->data == NULL) {
1225 #ifdef DEBUG
1226 		cmn_err(CE_WARN, "failed to allocate space for scosmb "
1227 		    "message data.");
1228 #endif	/* DEBUG */
1229 		kmem_free(msg_header_ptr, sizeof (scosmb_msgdata_t));
1230 		return (ENOMEM);
1231 	}
1232 
1233 	bcopy(datap, msg_header_ptr->data, (size_t)msg_length);
1234 
1235 	/*
1236 	 * Based on our earlier look at the message type, we either go through
1237 	 * a taskq or directly call the output function.
1238 	 */
1239 	if (do_queue != 0) {
1240 		/*
1241 		 * Place a new job on the scosmb_output_taskq.
1242 		 */
1243 		if (taskq_dispatch(scosmb_output_taskq,
1244 		    (task_func_t *)scosmb_process_output,
1245 		    (void *)msg_header_ptr, TQ_NOSLEEP) == 0) {
1246 #ifdef DEBUG
1247 			cmn_err(CE_WARN, "failed to dispatch a task to send "
1248 			    "ECC mailbox message.");
1249 #endif	/* DEBUG */
1250 			kmem_free(msg_header_ptr->data, msg_header_ptr->length);
1251 			kmem_free(msg_header_ptr, sizeof (scosmb_msgdata_t));
1252 			return (ENOMEM);
1253 		}
1254 		return (0);
1255 	} else {
1256 		return (scosmb_process_output(msg_header_ptr));
1257 	}
1258 }
1259