xref: /titanic_44/usr/src/uts/sun4u/starfire/os/bbus_intr.c (revision 1cb6af97c6f66f456d4f726ef056e1ebc0f73305)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/types.h>
30 #include <sys/param.h>
31 #include <sys/kmem.h>
32 #include <sys/cpu_sgnblk_defs.h>
33 #include <vm/seg.h>
34 #include <sys/iommu.h>
35 #include <sys/vtrace.h>
36 #include <sys/intreg.h>
37 #include <sys/ivintr.h>
38 #include <sys/cpuvar.h>
39 #include <sys/systm.h>
40 #include <sys/machsystm.h>
41 #include <sys/cyclic.h>
42 #include <sys/cpu_sgn.h>
43 
44 extern	cpu_sgnblk_t *cpu_sgnblkp[NCPU];
45 extern struct cpu *SIGBCPU;
46 extern	void power_down(const char *);
47 
48 uint_t bbus_intr_inum;
49 uint_t bbus_poll_inum;
50 
51 /*
52  * Support for sgnblk polling.
53  */
54 
55 /* Internal function prototypes */
56 static void sgnblk_poll_init();
57 static uint_t bbus_poll(caddr_t arg1, caddr_t arg2);
58 static void sgnblk_poll_handler(void *unused);
59 #ifdef THROTTLE
60 static void sgnblk_poll_throttle(uint64_t interval);
61 #endif /* THROTTLE */
62 
63 /*  Default sgnblk polling interval is every 5 seconds. */
64 #define	ONE_SECOND		(1000000)	/* in usecs */
65 #ifdef THROTTLE
66 #define	SGNBLK_POLL_INTERVAL	(5 * ONE_SECOND)
67 #define	SGNBLK_POLL_FAST	(ONE_SECOND >> 1)
68 #define	SGNBLK_POLL_FAST_WIN	((60 * ONE_SECOND) / \
69 					SGNBLK_POLL_FAST)
70 #else /* THROTTLE */
71 /*
72  * Until we can find a way to throttle back to 0.5 second intervals
73  * we're stuck fixed on 2.5 second intervals.
74  */
75 #define	SGNBLK_POLL_INTERVAL	((2 * ONE_SECOND) + (ONE_SECOND >> 1))
76 #endif /* THROTTLE */
77 
78 #define	MAX_SGNBLK_POLL_CLNT	5
79 
80 void (*pollclntfunc[MAX_SGNBLK_POLL_CLNT])();
81 /*
82  * sgnblk_mutex		Protects juggling & sgnblk_poll_refs[].
83  * sgnblk_poll_mutex	Protects pollclntfunc[].
84  */
85 kmutex_t	sgnblk_mutex;
86 kmutex_t	sgnblk_poll_mutex;
87 static uint64_t	sgnblk_poll_interval = SGNBLK_POLL_INTERVAL;
88 #ifdef THROTTLE
89 static uint64_t	sgnblk_poll_fast = SGNBLK_POLL_FAST;
90 static int64_t	sgnblk_poll_fast_win = SGNBLK_POLL_FAST_WIN;
91 #endif /* THROTTLE */
92 static processorid_t	sgnblk_pollcpu = -1;
93 /*
94  * Note that the sigblock polling depends on CY_HIGH_LEVEL
95  * being higher than PIL_13 since we ultimately need to
96  * dispatch a PIL_13 soft handler.
97  * Also, we assume one sgnblk handler for the entire system.
98  * Once upon a time we had them per-cpu.  With the Cyclic stuff
99  * we would have to bind our cyclic handler to a cpu and doing
100  * this prevents that cpu from being offlined.  Since the Cyclic
101  * subsystem could indirectly juggle us without us knowing we
102  * have to assume we're running from any possible cpu and not
103  * always SIGBCPU.
104  */
105 #ifdef THROTTLE
106 static cyclic_id_t	sgnblk_poll_cycid = CYCLIC_NONE;
107 #endif /* THROTTLE */
108 static cyc_handler_t	sgnblk_poll_cychandler = {
109 	sgnblk_poll_handler,
110 	NULL,
111 	CY_HIGH_LEVEL
112 };
113 static cyc_time_t	sgnblk_poll_time;
114 
115 /*
116  * Anybody that references the polling (SIGBCPU) can
117  * register a callback function that will be called if
118  * the polling cpu is juggled, e.g. during a DR operation.
119  */
120 #define	MAX_SGNBLK_POLL_REFS	10
121 
122 struct sgnblk_poll_refs {
123 	void	(*callback)(cpu_sgnblk_t *sigbp, void *arg);
124 	void	*arg;
125 }	sgnblk_poll_refs[MAX_SGNBLK_POLL_REFS];
126 
127 /*
128  * Bootbus intr handler: Generic handler for all SSP/CBS
129  * interrupt requests initiated via the hw bootbus intr
130  * mechanism. This is similar to the level15
131  * interrupt handling for sigb commands in the CS6400.
132  * Most of these code were stolen from the sigb stuff in
133  * in CS6400.
134  */
135 
136 extern struct cpu cpu0;
137 
138 /*ARGSUSED*/
139 static uint_t
140 bbus_intr(caddr_t arg)
141 {
142 	int	cmd = 0;
143 	processorid_t	cpu_id = CPU->cpu_id;
144 	int	retflag;
145 	int	resp = 0;
146 	proc_t	*initpp;
147 
148 	ASSERT(cpu_sgnblkp[cpu_id] != NULL);
149 
150 	/*
151 	 * Check for unsolicited messages in the host's mailbox.
152 	 */
153 	retflag = cpu_sgnblkp[cpu_id]->sigb_host_mbox.flag;
154 
155 	switch (retflag) {
156 	case CBS_TO_HOST:
157 		retflag = HOST_TO_CBS;
158 		break;
159 	default:
160 		retflag = SIGB_MBOX_EMPTY;
161 		break;
162 	}
163 	if (retflag == SIGB_MBOX_EMPTY)
164 		return (0);	/* interrupt not claimed */
165 
166 	/*
167 	 * We only look for UNSOLICITED messages, i.e. commands.
168 	 * Responses to these commands are returned into the same
169 	 * mailbox from which the command was received, i.e. host's.
170 	 *
171 	 * If the host should solicit a message from the SSP, that
172 	 * message/command goes into the SSP's mailbox (sigb_ssp_mbox).
173 	 * The responses (from the SSP) to these messages will be
174 	 * read from the ssp mailbox by whomever solicited it, but
175 	 * will NOT be handled through this level 15 interrupt
176 	 * mechanism.
177 	 *
178 	 * Note that use of the flag field of the signature block mailbox
179 	 * structure and the mailbox protocol itself, serializes access
180 	 * to these mailboxes.
181 	 */
182 
183 	resp = 0;
184 
185 	/*
186 	 * The first sizeof (uint_t) bytes of the data field
187 	 * is the command.
188 	 */
189 	cmd = cpu_sgnblkp[cpu_id]->sigb_host_mbox.cmd;
190 
191 	switch (cmd) {
192 		case SSP_GOTO_OBP:
193 		/*
194 		 * Let's set the mailbox flag to BUSY while we are in OBP
195 		 */
196 		cpu_sgnblkp[cpu_id]->sigb_host_mbox.flag = SIGB_MBOX_BUSY;
197 
198 		debug_enter("SSP requested (SSP_GOTO_OBP)");
199 		/*
200 		 * This command does NOT require a response.
201 		 */
202 		resp = 0;
203 		break;
204 
205 		case SSP_GOTO_PANIC:
206 		/*
207 		 * Let's reset the mailbox flag before we bail.
208 		 */
209 		cpu_sgnblkp[cpu_id]->sigb_host_mbox.flag = SIGB_MBOX_EMPTY;
210 
211 		cmn_err(CE_PANIC, "SSP requested (SSP_GOTO_PANIC)\n");
212 		/* should never reach this point */
213 
214 		resp = 0;
215 		break;
216 		case SSP_ENVIRON:
217 		/*
218 		 * Environmental Interrupt.
219 		 */
220 
221 		/*
222 		 * Send SIGPWR to init(1) it will run rc0, which will uadmin to
223 		 * powerdown.
224 		 */
225 
226 		mutex_enter(&pidlock);
227 		initpp = prfind(P_INITPID);
228 		mutex_exit(&pidlock);
229 
230 		/*
231 		 * If we're still booting and init(1) isn't set up yet,
232 		 * simply halt.
233 		 */
234 		if (initpp == NULL) {
235 			extern void halt(char *);
236 			cmn_err(CE_WARN, "?Environmental Interrupt");
237 			power_down((char *)NULL);
238 			halt("Power off the System!\n"); /* just in case */
239 		}
240 
241 		/*
242 		 * else, graceful shutdown with inittab and all getting involved
243 		 *
244 		 * XXX: Do we Need to modify the init process for the Cray 6400!
245 		 */
246 		psignal(initpp, SIGPWR);
247 
248 		/*
249 		 * XXX: kick off a sanity timeout panic in case the /etc/inittab
250 		 * or /etc/rc0 files are hosed.  The 6400 needs to hang here
251 		 * when we return from psignal.
252 		 *
253 		 * cmn_err(CE_PANIC, "SSP requested (SSP_ENVIRON)\n");
254 		 * should never reach this point
255 		 */
256 
257 		resp = 0;
258 		break;
259 		/*
260 		 * Could handle more mailbox commands right here.
261 		 */
262 
263 		default:
264 		resp = SIGB_BAD_MBOX_CMD;
265 		break;
266 	}
267 
268 	/*
269 	 * If resp is non-zero then we'll automatically reset
270 	 * the handler_sigb lock once we've sent the response,
271 	 * however if no response is needed, then resetlck must
272 	 * be set so that the handler_sigb lock is reset.
273 	 */
274 	if (resp != 0) {
275 		/*
276 		 * Had some kind of trouble handling the mailbox
277 		 * command.  Need to send back an error response
278 		 * and back out of the cpu_sgnblk handling.
279 		 */
280 		cpu_sgnblkp[cpu_id]->sigb_host_mbox.cmd = resp;
281 		bcopy((caddr_t)&cmd,
282 			(caddr_t)&cpu_sgnblkp[cpu_id]->sigb_host_mbox.data[0],
283 			sizeof (cmd));
284 		cpu_sgnblkp[cpu_id]->sigb_host_mbox.flag = retflag;
285 	} else {
286 		/*
287 		 * No response expected, but we still have to
288 		 * reset the flag to empty for the next person.
289 		 */
290 		cpu_sgnblkp[cpu_id]->sigb_host_mbox.flag = SIGB_MBOX_EMPTY;
291 	}
292 	return (1);	/* interrupt claimed */
293 }
294 
295 void
296 register_bbus_intr()
297 {
298 	/*
299 	 * Starfire's ASIC have the capability to generate a mondo
300 	 * vector. The SSP uses this capability via the Boot Bus to
301 	 * send an interrupt to a domain.
302 	 *
303 	 * The SSP generates a mondo with:
304 	 *	ign = UPAID_TO_IGN(bootcpu_upaid)
305 	 *	ino = 0
306 	 *
307 	 * An interrupt handler is added for this inum.
308 	 */
309 	bbus_intr_inum = UPAID_TO_IGN(cpu0.cpu_id) * MAX_INO;
310 	VERIFY(add_ivintr(bbus_intr_inum, PIL_13, bbus_intr, 0, 0) == 0);
311 
312 
313 	/*
314 	 * Due to a HW flaw in starfire, liberal use
315 	 * of bootbus intrs under heavy system load
316 	 * may cause the machine to arbstop. The workaround
317 	 * is to provide a polling mechanism thru the signature
318 	 * block interface to allow another way for the SSP to
319 	 * interrupt the host. Applications like IDN which generate
320 	 * a high degree of SSP to host interruptions for
321 	 * synchronization will need to use the polling facility
322 	 * instead of the hw bootbus interrupt mechanism.
323 	 * The HW bootbus intr support is left intact as it
324 	 * will still be used by existing SSP applications for system
325 	 * recovery in the event of system hangs etc.. In such situations,
326 	 * HW bootbus intr is a better mechanism as it is HW generated
327 	 * level 15 interrupt that has a better chance of kicking
328 	 * a otherwise hung OS into recovery.
329 	 *
330 	 * Polling is done by scheduling a constant tick timer
331 	 * interrupt at a certain predefined interval.
332 	 * The handler will do a poll and if there is a
333 	 * "intr" request, scheduled a soft level 13 intr
334 	 * to handle it. Allocate the inum for the level
335 	 * 13 intr here.
336 	 */
337 	bbus_poll_inum = add_softintr(PIL_13, bbus_poll, 0);
338 }
339 
340 static void
341 sgnblk_poll_init()
342 {
343 	ASSERT(MUTEX_HELD(&sgnblk_mutex));
344 
345 	mutex_init(&sgnblk_poll_mutex, NULL,
346 			MUTEX_SPIN, (void *)ipltospl(PIL_14));
347 	sgnblk_pollcpu = SIGBCPU->cpu_id;
348 	mutex_enter(&cpu_lock);
349 	sgnblk_poll_time.cyt_when = 0ull;
350 	sgnblk_poll_time.cyt_interval = sgnblk_poll_interval * 1000ull;
351 #ifdef THROTTLE
352 	sgnblk_poll_cycid = cyclic_add(&sgnblk_poll_cychandler,
353 					&sgnblk_poll_time);
354 #else /* THROTTLE */
355 	(void) cyclic_add(&sgnblk_poll_cychandler, &sgnblk_poll_time);
356 #endif /* THROTTLE */
357 	mutex_exit(&cpu_lock);
358 	ASSERT(sgnblk_pollcpu == SIGBCPU->cpu_id);
359 }
360 
361 int
362 sgnblk_poll_register(void(*func)(processorid_t cpu_id,
363 				cpu_sgnblk_t *cpu_sgnblkp))
364 {
365 	int i;
366 
367 	/*
368 	 * See if we need to initialize
369 	 * sgnblk polling
370 	 */
371 	mutex_enter(&sgnblk_mutex);
372 	if (sgnblk_pollcpu == -1)
373 		sgnblk_poll_init();
374 	mutex_exit(&sgnblk_mutex);
375 
376 	mutex_enter(&sgnblk_poll_mutex);
377 
378 	/*
379 	 * Look for a empty slot
380 	 */
381 	for (i = 0; i < MAX_SGNBLK_POLL_CLNT; i++) {
382 		if (pollclntfunc[i] == NULL) {
383 			pollclntfunc[i] = func;
384 			mutex_exit(&sgnblk_poll_mutex);
385 			return (1);
386 		}
387 	}
388 	mutex_exit(&sgnblk_poll_mutex);
389 	return (0);	/* failed */
390 }
391 
392 int
393 sgnblk_poll_unregister(void(*func)(processorid_t cpu_id,
394 				cpu_sgnblk_t *cpu_sgnblkp))
395 {
396 	int i;
397 
398 	mutex_enter(&sgnblk_poll_mutex);
399 
400 	/*
401 	 * Look for the slot matching the function passed in.
402 	 */
403 	for (i = 0; i < MAX_SGNBLK_POLL_CLNT; i++) {
404 		if (pollclntfunc[i] == func) {
405 			pollclntfunc[i] = NULL;
406 			mutex_exit(&sgnblk_poll_mutex);
407 			return (1);
408 		}
409 	}
410 	mutex_exit(&sgnblk_poll_mutex);
411 	return (0);	/* failed */
412 }
413 
414 
415 /*
416  * For DR support.
417  * Juggle poll tick client to another cpu
418  * Assumed to be called single threaded.
419  */
420 void
421 juggle_sgnblk_poll(struct cpu *cp)
422 {
423 	int		i;
424 
425 	mutex_enter(&sgnblk_mutex);
426 
427 	if (sgnblk_pollcpu == -1 ||
428 	    (cp != NULL && sgnblk_pollcpu == cp->cpu_id)) {
429 		mutex_exit(&sgnblk_mutex);
430 		return;
431 	}
432 
433 	/*
434 	 * Disable by simply returning here
435 	 * Passing a null cp is assumed to be
436 	 * sgnpoll disable request.
437 	 */
438 	if (cp == NULL) {
439 		for (i = 0; i < MAX_SGNBLK_POLL_REFS; i++) {
440 			void	(*func)(), *arg;
441 
442 			if ((func = sgnblk_poll_refs[i].callback) != NULL) {
443 				arg = sgnblk_poll_refs[i].arg;
444 				(*func)(NULL, arg);
445 			}
446 		}
447 		mutex_exit(&sgnblk_mutex);
448 		return;
449 	}
450 
451 	sgnblk_pollcpu = cp->cpu_id;
452 
453 	for (i = 0; i < MAX_SGNBLK_POLL_REFS; i++) {
454 		void	(*func)(), *arg;
455 
456 		if ((func = sgnblk_poll_refs[i].callback) != NULL) {
457 			arg = sgnblk_poll_refs[i].arg;
458 			(*func)(cpu_sgnblkp[sgnblk_pollcpu], arg);
459 		}
460 	}
461 
462 	mutex_exit(&sgnblk_mutex);
463 }
464 
465 #ifdef THROTTLE
466 /*ARGSUSED0*/
467 static void
468 _sgnblk_poll_throttle(void *unused)
469 {
470 	mutex_enter(&cpu_lock);
471 	if (sgnblk_poll_cycid != CYCLIC_NONE) {
472 		cyclic_remove(sgnblk_poll_cycid);
473 		sgnblk_poll_cycid = CYCLIC_NONE;
474 	}
475 
476 	if (sgnblk_poll_time.cyt_interval > 0ull)
477 		sgnblk_poll_cycid = cyclic_add(&sgnblk_poll_cychandler,
478 						&sgnblk_poll_time);
479 	mutex_exit(&cpu_lock);
480 }
481 
482 /*
483  * We don't want to remove the cyclic within the context of
484  * the handler so we kick off the throttle in background
485  * via a timeout call.
486  */
487 static void
488 sgnblk_poll_throttle(uint64_t new_interval)
489 {
490 	mutex_enter(&cpu_lock);
491 	sgnblk_poll_time.cyt_when = 0ull;
492 	sgnblk_poll_time.cyt_interval = new_interval * 1000ull;
493 	mutex_exit(&cpu_lock);
494 
495 	(void) timeout(_sgnblk_poll_throttle, NULL, (clock_t)0);
496 }
497 #endif /* THROTTLE */
498 
499 /*
500  * High priority interrupt handler (PIL_14)
501  * for signature block mbox polling.
502  */
503 /*ARGSUSED0*/
504 static void
505 sgnblk_poll_handler(void *unused)
506 {
507 	processorid_t	cpuid = SIGBCPU->cpu_id;
508 #ifdef THROTTLE
509 	static int64_t	sb_window = -1;
510 	static uint64_t	sb_interval = 0;
511 #endif /* THROTTLE */
512 
513 	if (cpu_sgnblkp[cpuid] == NULL)
514 		return;
515 
516 	/*
517 	 * Poll for SSP requests
518 	 */
519 	if (cpu_sgnblkp[cpuid]->sigb_host_mbox.intr == SIGB_INTR_SEND) {
520 		/* reset the flag - sure hope this is atomic */
521 		cpu_sgnblkp[cpuid]->sigb_host_mbox.intr = SIGB_INTR_OFF;
522 
523 #ifdef THROTTLE
524 		/*
525 		 * Go into fast poll mode for a short duration
526 		 * (SGNBLK_POLL_FAST_WIN) in SGNBLK_POLL_FAST interval.
527 		 * The assumption here is that we just got activity
528 		 * on the mbox poll, the probability of more coming down
529 		 * the pipe is high - so let's look more often.
530 		 */
531 		if ((sb_window < 0) && (sb_interval > sgnblk_poll_fast)) {
532 			sb_interval = sgnblk_poll_fast;
533 			sgnblk_poll_throttle(sb_interval);
534 		}
535 		sb_window = sgnblk_poll_fast_win;
536 #endif /* THROTTLE */
537 
538 		/* schedule poll processing */
539 		setsoftint(bbus_poll_inum);
540 
541 #ifdef THROTTLE
542 	} else if (sb_window >= 0) {
543 		/* Revert to slow polling once fast window ends */
544 		if ((--sb_window < 0) &&
545 		    (sb_interval < sgnblk_poll_interval)) {
546 			sb_interval = sgnblk_poll_interval;
547 			sgnblk_poll_throttle(sb_interval);
548 		}
549 #endif /* THROTTLE */
550 	}
551 }
552 
553 /*ARGSUSED*/
554 static uint_t
555 bbus_poll(caddr_t arg1, caddr_t arg2)
556 {
557 	int i;
558 	processorid_t cpu_id = SIGBCPU->cpu_id;
559 	cpu_sgnblk_t *sgnblkp = cpu_sgnblkp[cpu_id];
560 
561 	/*
562 	 * Go thru the poll client array and call the
563 	 * poll client functions one by one
564 	 */
565 	mutex_enter(&sgnblk_poll_mutex);
566 
567 	for (i = 0; i < MAX_SGNBLK_POLL_CLNT; i++) {
568 		void (*func)(processorid_t cpuid, cpu_sgnblk_t *sgnblkp);
569 
570 		if ((func = pollclntfunc[i]) != NULL) {
571 			mutex_exit(&sgnblk_poll_mutex);
572 			(*func)(cpu_id, sgnblkp);
573 			mutex_enter(&sgnblk_poll_mutex);
574 		}
575 	}
576 	mutex_exit(&sgnblk_poll_mutex);
577 
578 	return (1);
579 }
580 
581 int
582 sgnblk_poll_reference(void (*callback)(cpu_sgnblk_t *sigb, void *arg),
583 	void *arg)
584 {
585 	int		i, slot;
586 	cpu_sgnblk_t	*sigbp;
587 
588 	if (callback == NULL)
589 		return (-1);
590 
591 	mutex_enter(&sgnblk_mutex);
592 	/*
593 	 * First verify caller is not already registered.
594 	 */
595 	slot = -1;
596 	for (i = 0; i < MAX_SGNBLK_POLL_REFS; i++) {
597 		if ((slot == -1) && (sgnblk_poll_refs[i].callback == NULL)) {
598 			slot = i;
599 			continue;
600 		}
601 		if (sgnblk_poll_refs[i].callback == callback) {
602 			mutex_exit(&sgnblk_mutex);
603 			return (-1);
604 		}
605 	}
606 	/*
607 	 * Now find an empty entry.
608 	 */
609 	if (slot == -1) {
610 		mutex_exit(&sgnblk_mutex);
611 		return (-1);
612 	}
613 	sgnblk_poll_refs[slot].callback = callback;
614 	sgnblk_poll_refs[slot].arg = arg;
615 
616 	sigbp = (sgnblk_pollcpu != -1) ? cpu_sgnblkp[sgnblk_pollcpu] : NULL;
617 
618 	(*callback)(sigbp, arg);
619 
620 	mutex_exit(&sgnblk_mutex);
621 
622 	return (0);
623 }
624 
625 void
626 sgnblk_poll_unreference(void (*callback)(cpu_sgnblk_t *sigb, void *arg))
627 {
628 	int	i;
629 
630 	mutex_enter(&sgnblk_mutex);
631 	for (i = 0; i < MAX_SGNBLK_POLL_REFS; i++) {
632 		if (sgnblk_poll_refs[i].callback == callback) {
633 			void	*arg;
634 
635 			arg = sgnblk_poll_refs[i].arg;
636 			(*callback)(NULL, arg);
637 			sgnblk_poll_refs[i].callback = NULL;
638 			sgnblk_poll_refs[i].arg = NULL;
639 			break;
640 		}
641 	}
642 	mutex_exit(&sgnblk_mutex);
643 }
644