xref: /illumos-gate/usr/src/uts/sun4/io/px/px_ib.c (revision 01689544)
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  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * PX Interrupt Block implementation
30  */
31 
32 #include <sys/types.h>
33 #include <sys/kmem.h>
34 #include <sys/async.h>
35 #include <sys/systm.h>		/* panicstr */
36 #include <sys/spl.h>
37 #include <sys/sunddi.h>
38 #include <sys/machsystm.h>	/* intr_dist_add */
39 #include <sys/ddi_impldefs.h>
40 #include <sys/cpuvar.h>
41 #include "px_obj.h"
42 
43 /*LINTLIBRARY*/
44 
45 static void px_ib_intr_redist(void *arg, int32_t weight_max, int32_t weight);
46 static void px_ib_cpu_ticks_to_ih_nsec(px_ib_t *ib_p, px_ih_t *ih_p,
47     uint32_t cpu_id);
48 static uint_t px_ib_intr_reset(void *arg);
49 static void px_fill_in_intr_devs(pcitool_intr_dev_t *dev, char *driver_name,
50     char *path_name, int instance);
51 
52 int
53 px_ib_attach(px_t *px_p)
54 {
55 	dev_info_t	*dip = px_p->px_dip;
56 	px_ib_t		*ib_p;
57 	sysino_t	sysino;
58 	px_fault_t	*fault_p = &px_p->px_fault;
59 
60 	DBG(DBG_IB, dip, "px_ib_attach\n");
61 
62 	if (px_lib_intr_devino_to_sysino(px_p->px_dip,
63 	    px_p->px_inos[PX_INTR_PEC], &sysino) != DDI_SUCCESS)
64 		return (DDI_FAILURE);
65 
66 	/*
67 	 * Allocate interrupt block state structure and link it to
68 	 * the px state structure.
69 	 */
70 	ib_p = kmem_zalloc(sizeof (px_ib_t), KM_SLEEP);
71 	px_p->px_ib_p = ib_p;
72 	ib_p->ib_px_p = px_p;
73 	ib_p->ib_ino_lst = (px_ib_ino_info_t *)NULL;
74 
75 	mutex_init(&ib_p->ib_intr_lock, NULL, MUTEX_DRIVER, NULL);
76 	mutex_init(&ib_p->ib_ino_lst_mutex, NULL, MUTEX_DRIVER, NULL);
77 
78 	bus_func_register(BF_TYPE_RESINTR, px_ib_intr_reset, ib_p);
79 
80 	intr_dist_add_weighted(px_ib_intr_redist, ib_p);
81 
82 	/*
83 	 * Initialize PEC fault data structure
84 	 */
85 	fault_p->px_fh_dip = dip;
86 	fault_p->px_fh_sysino = sysino;
87 	fault_p->px_err_func = px_err_dmc_pec_intr;
88 	fault_p->px_intr_ino = px_p->px_inos[PX_INTR_PEC];
89 
90 	return (DDI_SUCCESS);
91 }
92 
93 void
94 px_ib_detach(px_t *px_p)
95 {
96 	px_ib_t		*ib_p = px_p->px_ib_p;
97 	dev_info_t	*dip = px_p->px_dip;
98 
99 	DBG(DBG_IB, dip, "px_ib_detach\n");
100 
101 	bus_func_unregister(BF_TYPE_RESINTR, px_ib_intr_reset, ib_p);
102 	intr_dist_rem_weighted(px_ib_intr_redist, ib_p);
103 
104 	mutex_destroy(&ib_p->ib_ino_lst_mutex);
105 	mutex_destroy(&ib_p->ib_intr_lock);
106 
107 	px_ib_free_ino_all(ib_p);
108 
109 	px_p->px_ib_p = NULL;
110 	kmem_free(ib_p, sizeof (px_ib_t));
111 }
112 
113 void
114 px_ib_intr_enable(px_t *px_p, cpuid_t cpu_id, devino_t ino)
115 {
116 	px_ib_t		*ib_p = px_p->px_ib_p;
117 	sysino_t	sysino;
118 
119 	/*
120 	 * Determine the cpu for the interrupt
121 	 */
122 	mutex_enter(&ib_p->ib_intr_lock);
123 
124 	DBG(DBG_IB, px_p->px_dip,
125 	    "px_ib_intr_enable: ino=%x cpu_id=%x\n", ino, cpu_id);
126 
127 	if (px_lib_intr_devino_to_sysino(px_p->px_dip, ino,
128 	    &sysino) != DDI_SUCCESS) {
129 		DBG(DBG_IB, px_p->px_dip,
130 		    "px_ib_intr_enable: px_intr_devino_to_sysino() failed\n");
131 
132 		mutex_exit(&ib_p->ib_intr_lock);
133 		return;
134 	}
135 
136 	PX_INTR_ENABLE(px_p->px_dip, sysino, cpu_id);
137 	px_lib_intr_setstate(px_p->px_dip, sysino, INTR_IDLE_STATE);
138 
139 	mutex_exit(&ib_p->ib_intr_lock);
140 }
141 
142 /*ARGSUSED*/
143 void
144 px_ib_intr_disable(px_ib_t *ib_p, devino_t ino, int wait)
145 {
146 	sysino_t	sysino;
147 
148 	mutex_enter(&ib_p->ib_intr_lock);
149 
150 	DBG(DBG_IB, ib_p->ib_px_p->px_dip, "px_ib_intr_disable: ino=%x\n", ino);
151 
152 	/* Disable the interrupt */
153 	if (px_lib_intr_devino_to_sysino(ib_p->ib_px_p->px_dip, ino,
154 	    &sysino) != DDI_SUCCESS) {
155 		DBG(DBG_IB, ib_p->ib_px_p->px_dip,
156 		    "px_ib_intr_disable: px_intr_devino_to_sysino() failed\n");
157 
158 		mutex_exit(&ib_p->ib_intr_lock);
159 		return;
160 	}
161 
162 	PX_INTR_DISABLE(ib_p->ib_px_p->px_dip, sysino);
163 
164 	mutex_exit(&ib_p->ib_intr_lock);
165 }
166 
167 
168 void
169 px_ib_intr_dist_en(dev_info_t *dip, cpuid_t cpu_id, devino_t ino,
170     boolean_t wait_flag)
171 {
172 	uint32_t	old_cpu_id;
173 	sysino_t	sysino;
174 	intr_valid_state_t	enabled = 0;
175 	hrtime_t	start_time;
176 	intr_state_t	intr_state;
177 	int		e = DDI_SUCCESS;
178 
179 	DBG(DBG_IB, dip, "px_ib_intr_dist_en: ino=0x%x\n", ino);
180 
181 	if (px_lib_intr_devino_to_sysino(dip, ino, &sysino) != DDI_SUCCESS) {
182 		DBG(DBG_IB, dip, "px_ib_intr_dist_en: "
183 		    "px_intr_devino_to_sysino() failed, ino 0x%x\n", ino);
184 		return;
185 	}
186 
187 	/* Skip enabling disabled interrupts */
188 	if (px_lib_intr_getvalid(dip, sysino, &enabled) != DDI_SUCCESS) {
189 		DBG(DBG_IB, dip, "px_ib_intr_dist_en: px_intr_getvalid() "
190 		    "failed, sysino 0x%x\n", sysino);
191 		return;
192 	}
193 	if (!enabled)
194 		return;
195 
196 	/* Done if redistributed onto the same cpuid */
197 	if (px_lib_intr_gettarget(dip, sysino, &old_cpu_id) != DDI_SUCCESS) {
198 		DBG(DBG_IB, dip, "px_ib_intr_dist_en: "
199 		    "px_intr_gettarget() failed\n");
200 		return;
201 	}
202 	if (cpu_id == old_cpu_id)
203 		return;
204 
205 	if (!wait_flag)
206 		goto done;
207 
208 	/* Busy wait on pending interrupts */
209 	PX_INTR_DISABLE(dip, sysino);
210 
211 	for (start_time = gethrtime(); !panicstr &&
212 	    ((e = px_lib_intr_getstate(dip, sysino, &intr_state)) ==
213 		DDI_SUCCESS) &&
214 	    (intr_state == INTR_DELIVERED_STATE); /* */) {
215 		if (gethrtime() - start_time > px_intrpend_timeout) {
216 			cmn_err(CE_WARN,
217 			    "%s%d: px_ib_intr_dist_en: sysino 0x%lx(ino 0x%x) "
218 			    "from cpu id 0x%x to 0x%x timeout",
219 			    ddi_driver_name(dip), ddi_get_instance(dip),
220 			    sysino, ino, old_cpu_id, cpu_id);
221 
222 			e = DDI_FAILURE;
223 			break;
224 		}
225 	}
226 
227 	if (e != DDI_SUCCESS)
228 		DBG(DBG_IB, dip, "px_ib_intr_dist_en: failed, "
229 		    "ino 0x%x sysino 0x%x\n", ino, sysino);
230 
231 done:
232 	PX_INTR_ENABLE(dip, sysino, cpu_id);
233 }
234 
235 static void
236 px_ib_cpu_ticks_to_ih_nsec(px_ib_t *ib_p, px_ih_t *ih_p, uint32_t cpu_id)
237 {
238 	extern kmutex_t pxintr_ks_template_lock;
239 	hrtime_t ticks;
240 
241 	/*
242 	 * Because we are updating two fields in ih_t we must lock
243 	 * pxintr_ks_template_lock to prevent someone from reading the
244 	 * kstats after we set ih_ticks to 0 and before we increment
245 	 * ih_nsec to compensate.
246 	 *
247 	 * We must also protect against the interrupt arriving and incrementing
248 	 * ih_ticks between the time we read it and when we reset it to 0.
249 	 * To do this we use atomic_swap.
250 	 */
251 
252 	ASSERT(MUTEX_HELD(&ib_p->ib_ino_lst_mutex));
253 
254 	mutex_enter(&pxintr_ks_template_lock);
255 	ticks = atomic_swap_64(&ih_p->ih_ticks, 0);
256 	ih_p->ih_nsec += (uint64_t)tick2ns(ticks, cpu_id);
257 	mutex_exit(&pxintr_ks_template_lock);
258 }
259 
260 
261 /*
262  * Redistribute interrupts of the specified weight. The first call has a weight
263  * of weight_max, which can be used to trigger initialization for
264  * redistribution. The inos with weight [weight_max, inf.) should be processed
265  * on the "weight == weight_max" call.  This first call is followed by calls
266  * of decreasing weights, inos of that weight should be processed.  The final
267  * call specifies a weight of zero, this can be used to trigger processing of
268  * stragglers.
269  */
270 static void
271 px_ib_intr_redist(void *arg, int32_t weight_max, int32_t weight)
272 {
273 	px_ib_t		*ib_p = (px_ib_t *)arg;
274 	px_t		*px_p = ib_p->ib_px_p;
275 	dev_info_t	*dip = px_p->px_dip;
276 	px_ib_ino_info_t *ino_p;
277 	px_ih_t		*ih_lst;
278 	int32_t		dweight = 0;
279 	int		i;
280 
281 	/* Redistribute internal interrupts */
282 	if (weight == 0) {
283 		mutex_enter(&ib_p->ib_intr_lock);
284 		px_ib_intr_dist_en(dip, intr_dist_cpuid(),
285 		    px_p->px_inos[PX_INTR_PEC], B_FALSE);
286 		mutex_exit(&ib_p->ib_intr_lock);
287 
288 		px_cb_intr_redist(px_p);
289 	}
290 
291 	/* Redistribute device interrupts */
292 	mutex_enter(&ib_p->ib_ino_lst_mutex);
293 
294 	for (ino_p = ib_p->ib_ino_lst; ino_p; ino_p = ino_p->ino_next) {
295 		uint32_t orig_cpuid;
296 
297 		/*
298 		 * Recomputes the sum of interrupt weights of devices that
299 		 * share the same ino upon first call marked by
300 		 * (weight == weight_max).
301 		 */
302 		if (weight == weight_max) {
303 			ino_p->ino_intr_weight = 0;
304 			for (i = 0, ih_lst = ino_p->ino_ih_head;
305 			    i < ino_p->ino_ih_size;
306 			    i++, ih_lst = ih_lst->ih_next) {
307 				dweight = i_ddi_get_intr_weight(ih_lst->ih_dip);
308 				if (dweight > 0)
309 					ino_p->ino_intr_weight += dweight;
310 			}
311 		}
312 
313 		/*
314 		 * As part of redistributing weighted interrupts over cpus,
315 		 * nexus redistributes device interrupts and updates
316 		 * cpu weight. The purpose is for the most light weighted
317 		 * cpu to take the next interrupt and gain weight, therefore
318 		 * attention demanding device gains more cpu attention by
319 		 * making itself heavy.
320 		 */
321 		if ((weight == ino_p->ino_intr_weight) ||
322 		    ((weight >= weight_max) &&
323 		    (ino_p->ino_intr_weight >= weight_max))) {
324 			orig_cpuid = ino_p->ino_cpuid;
325 			if (cpu[orig_cpuid] == NULL)
326 				orig_cpuid = CPU->cpu_id;
327 
328 			/* select cpuid to target and mark ino established */
329 			ino_p->ino_cpuid = intr_dist_cpuid();
330 
331 			/* Add device weight to targeted cpu. */
332 			for (i = 0, ih_lst = ino_p->ino_ih_head;
333 			    i < ino_p->ino_ih_size;
334 			    i++, ih_lst = ih_lst->ih_next) {
335 
336 				dweight = i_ddi_get_intr_weight(ih_lst->ih_dip);
337 				intr_dist_cpuid_add_device_weight(
338 				    ino_p->ino_cpuid, ih_lst->ih_dip, dweight);
339 
340 				/*
341 				 * Different cpus may have different clock
342 				 * speeds. to account for this, whenever an
343 				 * interrupt is moved to a new CPU, we
344 				 * convert the accumulated ticks into nsec,
345 				 * based upon the clock rate of the prior
346 				 * CPU.
347 				 *
348 				 * It is possible that the prior CPU no longer
349 				 * exists. In this case, fall back to using
350 				 * this CPU's clock rate.
351 				 *
352 				 * Note that the value in ih_ticks has already
353 				 * been corrected for any power savings mode
354 				 * which might have been in effect.
355 				 */
356 				px_ib_cpu_ticks_to_ih_nsec(ib_p, ih_lst,
357 				    orig_cpuid);
358 			}
359 
360 			/* enable interrupt on new targeted cpu */
361 			px_ib_intr_dist_en(dip, ino_p->ino_cpuid,
362 			    ino_p->ino_ino, B_TRUE);
363 		}
364 	}
365 	mutex_exit(&ib_p->ib_ino_lst_mutex);
366 }
367 
368 /*
369  * Reset interrupts to IDLE.  This function is called during
370  * panic handling after redistributing interrupts; it's needed to
371  * support dumping to network devices after 'sync' from OBP.
372  *
373  * N.B.  This routine runs in a context where all other threads
374  * are permanently suspended.
375  */
376 static uint_t
377 px_ib_intr_reset(void *arg)
378 {
379 	px_ib_t		*ib_p = (px_ib_t *)arg;
380 
381 	DBG(DBG_IB, ib_p->ib_px_p->px_dip, "px_ib_intr_reset\n");
382 
383 	if (px_lib_intr_reset(ib_p->ib_px_p->px_dip) != DDI_SUCCESS)
384 		return (BF_FATAL);
385 
386 	return (BF_NONE);
387 }
388 
389 /*
390  * Locate ino_info structure on ib_p->ib_ino_lst according to ino#
391  * returns NULL if not found.
392  */
393 px_ib_ino_info_t *
394 px_ib_locate_ino(px_ib_t *ib_p, devino_t ino_num)
395 {
396 	px_ib_ino_info_t	*ino_p = ib_p->ib_ino_lst;
397 
398 	ASSERT(MUTEX_HELD(&ib_p->ib_ino_lst_mutex));
399 
400 	for (; ino_p && ino_p->ino_ino != ino_num; ino_p = ino_p->ino_next);
401 
402 	return (ino_p);
403 }
404 
405 px_ib_ino_info_t *
406 px_ib_new_ino(px_ib_t *ib_p, devino_t ino_num, px_ih_t *ih_p)
407 {
408 	px_ib_ino_info_t	*ino_p = kmem_alloc(sizeof (px_ib_ino_info_t),
409 	    KM_SLEEP);
410 	sysino_t	sysino;
411 
412 	ino_p->ino_ino = ino_num;
413 	ino_p->ino_ib_p = ib_p;
414 	ino_p->ino_unclaimed = 0;
415 
416 	if (px_lib_intr_devino_to_sysino(ib_p->ib_px_p->px_dip, ino_p->ino_ino,
417 	    &sysino) != DDI_SUCCESS)
418 		return (NULL);
419 
420 	ino_p->ino_sysino = sysino;
421 
422 	/*
423 	 * Cannot disable interrupt since we might share slot
424 	 */
425 	ih_p->ih_next = ih_p;
426 	ino_p->ino_ih_head = ih_p;
427 	ino_p->ino_ih_tail = ih_p;
428 	ino_p->ino_ih_start = ih_p;
429 	ino_p->ino_ih_size = 1;
430 
431 	ino_p->ino_next = ib_p->ib_ino_lst;
432 	ib_p->ib_ino_lst = ino_p;
433 
434 	return (ino_p);
435 }
436 
437 /*
438  * The ino_p is retrieved by previous call to px_ib_locate_ino().
439  */
440 void
441 px_ib_delete_ino(px_ib_t *ib_p, px_ib_ino_info_t *ino_p)
442 {
443 	px_ib_ino_info_t	*list = ib_p->ib_ino_lst;
444 
445 	ASSERT(MUTEX_HELD(&ib_p->ib_ino_lst_mutex));
446 
447 	if (list == ino_p)
448 		ib_p->ib_ino_lst = list->ino_next;
449 	else {
450 		for (; list->ino_next != ino_p; list = list->ino_next);
451 		list->ino_next = ino_p->ino_next;
452 	}
453 }
454 
455 /*
456  * Free all ino when we are detaching.
457  */
458 void
459 px_ib_free_ino_all(px_ib_t *ib_p)
460 {
461 	px_ib_ino_info_t	*tmp = ib_p->ib_ino_lst;
462 	px_ib_ino_info_t	*next = NULL;
463 
464 	while (tmp) {
465 		next = tmp->ino_next;
466 		kmem_free(tmp, sizeof (px_ib_ino_info_t));
467 		tmp = next;
468 	}
469 }
470 
471 int
472 px_ib_ino_add_intr(px_t *px_p, px_ib_ino_info_t *ino_p, px_ih_t *ih_p)
473 {
474 	px_ib_t		*ib_p = ino_p->ino_ib_p;
475 	devino_t	ino = ino_p->ino_ino;
476 	sysino_t	sysino = ino_p->ino_sysino;
477 	dev_info_t	*dip = px_p->px_dip;
478 	cpuid_t		curr_cpu;
479 	hrtime_t	start_time;
480 	intr_state_t	intr_state;
481 	int		ret = DDI_SUCCESS;
482 
483 	ASSERT(MUTEX_HELD(&ib_p->ib_ino_lst_mutex));
484 	ASSERT(ib_p == px_p->px_ib_p);
485 
486 	DBG(DBG_IB, dip, "px_ib_ino_add_intr ino=%x\n", ino_p->ino_ino);
487 
488 	/* Disable the interrupt */
489 	if ((ret = px_lib_intr_gettarget(dip, sysino,
490 	    &curr_cpu)) != DDI_SUCCESS) {
491 		DBG(DBG_IB, dip,
492 		    "px_ib_ino_add_intr px_intr_gettarget() failed\n");
493 
494 		return (ret);
495 	}
496 
497 	PX_INTR_DISABLE(dip, sysino);
498 
499 	/* Busy wait on pending interrupt */
500 	for (start_time = gethrtime(); !panicstr &&
501 	    ((ret = px_lib_intr_getstate(dip, sysino, &intr_state))
502 	    == DDI_SUCCESS) && (intr_state == INTR_DELIVERED_STATE); /* */) {
503 		if (gethrtime() - start_time > px_intrpend_timeout) {
504 			cmn_err(CE_WARN, "%s%d: px_ib_ino_add_intr: pending "
505 			    "sysino 0x%lx(ino 0x%x) timeout",
506 			    ddi_driver_name(dip), ddi_get_instance(dip),
507 			    sysino, ino);
508 
509 			ret = DDI_FAILURE;
510 			break;
511 		}
512 	}
513 
514 	if (ret != DDI_SUCCESS) {
515 		DBG(DBG_IB, dip, "px_ib_ino_add_intr: failed, "
516 		    "ino 0x%x sysino 0x%x\n", ino, sysino);
517 
518 		return (ret);
519 	}
520 
521 	/* Link up px_ispec_t portion of the ppd */
522 	ih_p->ih_next = ino_p->ino_ih_head;
523 	ino_p->ino_ih_tail->ih_next = ih_p;
524 	ino_p->ino_ih_tail = ih_p;
525 
526 	ino_p->ino_ih_start = ino_p->ino_ih_head;
527 	ino_p->ino_ih_size++;
528 
529 	/*
530 	 * If the interrupt was previously blocked (left in pending state)
531 	 * because of jabber we need to clear the pending state in case the
532 	 * jabber has gone away.
533 	 */
534 	if (ino_p->ino_unclaimed > px_unclaimed_intr_max) {
535 		cmn_err(CE_WARN,
536 		    "%s%d: px_ib_ino_add_intr: ino 0x%x has been unblocked",
537 		    ddi_driver_name(dip), ddi_get_instance(dip), ino);
538 
539 		ino_p->ino_unclaimed = 0;
540 		if ((ret = px_lib_intr_setstate(dip, sysino,
541 		    INTR_IDLE_STATE)) != DDI_SUCCESS) {
542 			DBG(DBG_IB, px_p->px_dip,
543 			    "px_ib_ino_add_intr px_intr_setstate failed\n");
544 
545 			return (ret);
546 		}
547 	}
548 
549 	/* Re-enable interrupt */
550 	PX_INTR_ENABLE(dip, sysino, curr_cpu);
551 
552 	return (ret);
553 }
554 
555 /*
556  * Removes px_ispec_t from the ino's link list.
557  * uses hardware mutex to lock out interrupt threads.
558  * Side effects: interrupt belongs to that ino is turned off on return.
559  * if we are sharing PX slot with other inos, the caller needs
560  * to turn it back on.
561  */
562 int
563 px_ib_ino_rem_intr(px_t *px_p, px_ib_ino_info_t *ino_p, px_ih_t *ih_p)
564 {
565 	devino_t	ino = ino_p->ino_ino;
566 	sysino_t	sysino = ino_p->ino_sysino;
567 	dev_info_t	*dip = px_p->px_dip;
568 	px_ih_t		*ih_lst = ino_p->ino_ih_head;
569 	hrtime_t	start_time;
570 	intr_state_t	intr_state;
571 	int		i, ret = DDI_SUCCESS;
572 
573 	ASSERT(MUTEX_HELD(&ino_p->ino_ib_p->ib_ino_lst_mutex));
574 
575 	DBG(DBG_IB, px_p->px_dip, "px_ib_ino_rem_intr ino=%x\n",
576 	    ino_p->ino_ino);
577 
578 	/* Disable the interrupt */
579 	PX_INTR_DISABLE(px_p->px_dip, sysino);
580 
581 	if (ino_p->ino_ih_size == 1) {
582 		if (ih_lst != ih_p)
583 			goto not_found;
584 
585 		/* No need to set head/tail as ino_p will be freed */
586 		goto reset;
587 	}
588 
589 	/* Busy wait on pending interrupt */
590 	for (start_time = gethrtime(); !panicstr &&
591 	    ((ret = px_lib_intr_getstate(dip, sysino, &intr_state))
592 	    == DDI_SUCCESS) && (intr_state == INTR_DELIVERED_STATE); /* */) {
593 		if (gethrtime() - start_time > px_intrpend_timeout) {
594 			cmn_err(CE_WARN, "%s%d: px_ib_ino_rem_intr: pending "
595 			    "sysino 0x%lx(ino 0x%x) timeout",
596 			    ddi_driver_name(dip), ddi_get_instance(dip),
597 			    sysino, ino);
598 
599 			ret = DDI_FAILURE;
600 			break;
601 		}
602 	}
603 
604 	if (ret != DDI_SUCCESS) {
605 		DBG(DBG_IB, dip, "px_ib_ino_rem_intr: failed, "
606 		    "ino 0x%x sysino 0x%x\n", ino, sysino);
607 
608 		return (ret);
609 	}
610 
611 	/*
612 	 * If the interrupt was previously blocked (left in pending state)
613 	 * because of jabber we need to clear the pending state in case the
614 	 * jabber has gone away.
615 	 */
616 	if (ino_p->ino_unclaimed > px_unclaimed_intr_max) {
617 		cmn_err(CE_WARN, "%s%d: px_ib_ino_rem_intr: "
618 		    "ino 0x%x has been unblocked",
619 		    ddi_driver_name(dip), ddi_get_instance(dip), ino);
620 
621 		ino_p->ino_unclaimed = 0;
622 		if ((ret = px_lib_intr_setstate(dip, sysino,
623 		    INTR_IDLE_STATE)) != DDI_SUCCESS) {
624 			DBG(DBG_IB, px_p->px_dip,
625 			    "px_ib_ino_rem_intr px_intr_setstate failed\n");
626 
627 			return (ret);
628 		}
629 	}
630 
631 	/* Search the link list for ih_p */
632 	for (i = 0; (i < ino_p->ino_ih_size) &&
633 	    (ih_lst->ih_next != ih_p); i++, ih_lst = ih_lst->ih_next);
634 
635 	if (ih_lst->ih_next != ih_p)
636 		goto not_found;
637 
638 	/* Remove ih_p from the link list and maintain the head/tail */
639 	ih_lst->ih_next = ih_p->ih_next;
640 
641 	if (ino_p->ino_ih_head == ih_p)
642 		ino_p->ino_ih_head = ih_p->ih_next;
643 	if (ino_p->ino_ih_tail == ih_p)
644 		ino_p->ino_ih_tail = ih_lst;
645 
646 	ino_p->ino_ih_start = ino_p->ino_ih_head;
647 
648 reset:
649 	if (ih_p->ih_config_handle)
650 		pci_config_teardown(&ih_p->ih_config_handle);
651 	if (ih_p->ih_ksp != NULL)
652 		kstat_delete(ih_p->ih_ksp);
653 
654 	kmem_free(ih_p, sizeof (px_ih_t));
655 	ino_p->ino_ih_size--;
656 
657 	return (ret);
658 
659 not_found:
660 	DBG(DBG_R_INTX, ino_p->ino_ib_p->ib_px_p->px_dip,
661 		"ino_p=%x does not have ih_p=%x\n", ino_p, ih_p);
662 
663 	return (DDI_FAILURE);
664 }
665 
666 px_ih_t *
667 px_ib_ino_locate_intr(px_ib_ino_info_t *ino_p, dev_info_t *rdip,
668     uint32_t inum, msiq_rec_type_t rec_type, msgcode_t msg_code)
669 {
670 	px_ih_t	*ih_lst = ino_p->ino_ih_head;
671 	int	i;
672 
673 	for (i = 0; i < ino_p->ino_ih_size; i++, ih_lst = ih_lst->ih_next) {
674 		if ((ih_lst->ih_dip == rdip) && (ih_lst->ih_inum == inum) &&
675 		    (ih_lst->ih_rec_type == rec_type) &&
676 		    (ih_lst->ih_msg_code == msg_code))
677 			return (ih_lst);
678 	}
679 
680 	return ((px_ih_t *)NULL);
681 }
682 
683 px_ih_t *
684 px_ib_alloc_ih(dev_info_t *rdip, uint32_t inum,
685     uint_t (*int_handler)(caddr_t int_handler_arg1, caddr_t int_handler_arg2),
686     caddr_t int_handler_arg1, caddr_t int_handler_arg2,
687     msiq_rec_type_t rec_type, msgcode_t msg_code)
688 {
689 	px_ih_t	*ih_p;
690 
691 	ih_p = kmem_alloc(sizeof (px_ih_t), KM_SLEEP);
692 	ih_p->ih_dip = rdip;
693 	ih_p->ih_inum = inum;
694 	ih_p->ih_intr_state = PX_INTR_STATE_DISABLE;
695 	ih_p->ih_handler = int_handler;
696 	ih_p->ih_handler_arg1 = int_handler_arg1;
697 	ih_p->ih_handler_arg2 = int_handler_arg2;
698 	ih_p->ih_config_handle = NULL;
699 	ih_p->ih_rec_type = rec_type;
700 	ih_p->ih_msg_code = msg_code;
701 	ih_p->ih_nsec = 0;
702 	ih_p->ih_ticks = 0;
703 	ih_p->ih_ksp = NULL;
704 
705 	return (ih_p);
706 }
707 
708 int
709 px_ib_update_intr_state(px_t *px_p, dev_info_t *rdip,
710     uint_t inum, devino_t ino, uint_t new_intr_state,
711     msiq_rec_type_t rec_type, msgcode_t msg_code)
712 {
713 	px_ib_t		*ib_p = px_p->px_ib_p;
714 	px_ib_ino_info_t *ino_p;
715 	px_ih_t		*ih_p;
716 	int		ret = DDI_FAILURE;
717 
718 	DBG(DBG_IB, px_p->px_dip, "ib_update_intr_state: %s%d "
719 	    "inum %x devino %x state %x\n", ddi_driver_name(rdip),
720 	    ddi_get_instance(rdip), inum, ino, new_intr_state);
721 
722 	mutex_enter(&ib_p->ib_ino_lst_mutex);
723 
724 	if (ino_p = px_ib_locate_ino(ib_p, ino)) {
725 		if (ih_p = px_ib_ino_locate_intr(ino_p, rdip, inum, rec_type,
726 		    msg_code)) {
727 			ih_p->ih_intr_state = new_intr_state;
728 			ret = DDI_SUCCESS;
729 		}
730 	}
731 
732 	mutex_exit(&ib_p->ib_ino_lst_mutex);
733 	return (ret);
734 }
735 
736 
737 static void
738 px_fill_in_intr_devs(pcitool_intr_dev_t *dev, char *driver_name,
739     char *path_name, int instance)
740 {
741 	(void) strncpy(dev->driver_name, driver_name, MAXMODCONFNAME-1);
742 	dev->driver_name[MAXMODCONFNAME] = '\0';
743 	(void) strncpy(dev->path, path_name, MAXPATHLEN-1);
744 	dev->dev_inst = instance;
745 }
746 
747 
748 /*
749  * Return the dips or number of dips associated with a given interrupt block.
750  * Size of dips array arg is passed in as dips_ret arg.
751  * Number of dips returned is returned in dips_ret arg.
752  * Array of dips gets returned in the dips argument.
753  * Function returns number of dips existing for the given interrupt block.
754  *
755  * Note: this function assumes an enabled/valid INO, which is why it returns
756  * the px node and (Internal) when it finds no other devices (and *devs_ret > 0)
757  */
758 uint8_t
759 pxtool_ib_get_ino_devs(
760     px_t *px_p, uint32_t ino, uint8_t *devs_ret, pcitool_intr_dev_t *devs)
761 {
762 	px_ib_t *ib_p = px_p->px_ib_p;
763 	px_ib_ino_info_t *ino_p;
764 	px_ih_t *ih_p;
765 	uint32_t num_devs = 0;
766 	char pathname[MAXPATHLEN];
767 	int i;
768 
769 	mutex_enter(&ib_p->ib_ino_lst_mutex);
770 	ino_p = px_ib_locate_ino(ib_p, ino);
771 	if (ino_p != NULL) {
772 		num_devs = ino_p->ino_ih_size;
773 		for (i = 0, ih_p = ino_p->ino_ih_head;
774 		    ((i < ino_p->ino_ih_size) && (i < *devs_ret));
775 		    i++, ih_p = ih_p->ih_next) {
776 			(void) ddi_pathname(ih_p->ih_dip, pathname);
777 			px_fill_in_intr_devs(&devs[i],
778 			    (char *)ddi_driver_name(ih_p->ih_dip),  pathname,
779 			    ddi_get_instance(ih_p->ih_dip));
780 		}
781 		*devs_ret = i;
782 
783 	} else if (*devs_ret > 0) {
784 		(void) ddi_pathname(px_p->px_dip, pathname);
785 		strcat(pathname, " (Internal)");
786 		px_fill_in_intr_devs(&devs[0],
787 		    (char *)ddi_driver_name(px_p->px_dip),  pathname,
788 		    ddi_get_instance(px_p->px_dip));
789 		num_devs = *devs_ret = 1;
790 	}
791 
792 	mutex_exit(&ib_p->ib_ino_lst_mutex);
793 
794 	return (num_devs);
795 }
796 
797 
798 void
799 px_ib_log_new_cpu(px_ib_t *ib_p, uint32_t old_cpu_id, uint32_t new_cpu_id,
800     uint32_t ino)
801 {
802 	px_ib_ino_info_t *ino_p;
803 
804 	mutex_enter(&ib_p->ib_ino_lst_mutex);
805 
806 	/* Log in OS data structures the new CPU. */
807 	ino_p = px_ib_locate_ino(ib_p, ino);
808 	if (ino_p != NULL) {
809 
810 		/* Log in OS data structures the new CPU. */
811 		ino_p->ino_cpuid = new_cpu_id;
812 
813 		/* Account for any residual time to be logged for old cpu. */
814 		px_ib_cpu_ticks_to_ih_nsec(ib_p, ino_p->ino_ih_head,
815 		    old_cpu_id);
816 	}
817 
818 	mutex_exit(&ib_p->ib_ino_lst_mutex);
819 }
820