XXXX introduce drv_sectohz
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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25 /*
26 * PX Interrupt Block implementation
27 */
28
29 #include <sys/types.h>
30 #include <sys/kmem.h>
31 #include <sys/async.h>
32 #include <sys/systm.h> /* panicstr */
33 #include <sys/spl.h>
34 #include <sys/sunddi.h>
35 #include <sys/machsystm.h> /* intr_dist_add */
36 #include <sys/ddi_impldefs.h>
37 #include <sys/cpuvar.h>
38 #include <sys/time.h>
39 #include "px_obj.h"
40
41 /*LINTLIBRARY*/
42
43 static void px_ib_intr_redist(void *arg, int32_t weight_max, int32_t weight);
44 static void px_ib_cpu_ticks_to_ih_nsec(px_ib_t *ib_p, px_ih_t *ih_p,
45 uint32_t cpu_id);
46 static uint_t px_ib_intr_reset(void *arg);
47 static void px_fill_in_intr_devs(pcitool_intr_dev_t *dev, char *driver_name,
48 char *path_name, int instance);
49
50 extern uint64_t xc_tick_jump_limit;
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_ino_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 int
168 px_ib_intr_pend(dev_info_t *dip, sysino_t sysino)
169 {
170 int ret = DDI_SUCCESS;
171 hrtime_t start_time, prev, curr, interval, jump;
172 hrtime_t intr_timeout;
173 intr_state_t intr_state;
174
175 /* Disable the interrupt */
176 PX_INTR_DISABLE(dip, sysino);
177
178 intr_timeout = px_intrpend_timeout;
179 jump = TICK_TO_NSEC(xc_tick_jump_limit);
180
181 /* Busy wait on pending interrupt */
182 for (curr = start_time = gethrtime(); !panicstr &&
183 ((ret = px_lib_intr_getstate(dip, sysino,
184 &intr_state)) == DDI_SUCCESS) &&
185 (intr_state == INTR_DELIVERED_STATE); /* */) {
186 /*
187 * If we have a really large jump in hrtime, it is most
188 * probably because we entered the debugger (or OBP,
189 * in general). So, we adjust the timeout accordingly
190 * to prevent declaring an interrupt timeout. The
191 * master-interrupt mechanism in OBP should deliver
192 * the interrupts properly.
193 */
194 prev = curr;
195 curr = gethrtime();
196 interval = curr - prev;
197 if (interval > jump)
198 intr_timeout += interval;
199 if (curr - start_time > intr_timeout) {
200 ret = DDI_FAILURE;
201 break;
202 }
203 }
204 return (ret);
205 }
206
207 void
208 px_ib_intr_dist_en(dev_info_t *dip, cpuid_t cpu_id, devino_t ino,
209 boolean_t wait_flag)
210 {
211 uint32_t old_cpu_id;
212 sysino_t sysino;
213 intr_valid_state_t enabled = 0;
214
215 DBG(DBG_IB, dip, "px_ib_intr_dist_en: ino=0x%x\n", ino);
216
217 if (px_lib_intr_devino_to_sysino(dip, ino, &sysino) != DDI_SUCCESS) {
218 DBG(DBG_IB, dip, "px_ib_intr_dist_en: "
219 "px_intr_devino_to_sysino() failed, ino 0x%x\n", ino);
220 return;
221 }
222
223 /* Skip enabling disabled interrupts */
224 if (px_lib_intr_getvalid(dip, sysino, &enabled) != DDI_SUCCESS) {
225 DBG(DBG_IB, dip, "px_ib_intr_dist_en: px_intr_getvalid() "
226 "failed, sysino 0x%x\n", sysino);
227 return;
228 }
229 if (!enabled)
230 return;
231
232 /* Done if redistributed onto the same cpuid */
233 if (px_lib_intr_gettarget(dip, sysino, &old_cpu_id) != DDI_SUCCESS) {
234 DBG(DBG_IB, dip, "px_ib_intr_dist_en: "
235 "px_intr_gettarget() failed\n");
236 return;
237 }
238 if (cpu_id == old_cpu_id)
239 return;
240
241 /* Wait on pending interrupts */
242 if (wait_flag != 0 && px_ib_intr_pend(dip, sysino) != DDI_SUCCESS) {
243 cmn_err(CE_WARN,
244 "%s%d: px_ib_intr_dist_en: sysino 0x%lx(ino 0x%x) "
245 "from cpu id 0x%x to 0x%x timeout",
246 ddi_driver_name(dip), ddi_get_instance(dip),
247 sysino, ino, old_cpu_id, cpu_id);
248
249 DBG(DBG_IB, dip, "px_ib_intr_dist_en: failed, "
250 "ino 0x%x sysino 0x%x\n", ino, sysino);
251 }
252
253 PX_INTR_ENABLE(dip, sysino, cpu_id);
254 }
255
256 static void
257 px_ib_cpu_ticks_to_ih_nsec(px_ib_t *ib_p, px_ih_t *ih_p, uint32_t cpu_id)
258 {
259 extern kmutex_t pxintr_ks_template_lock;
260 hrtime_t ticks;
261
262 /*
263 * Because we are updating two fields in ih_t we must lock
264 * pxintr_ks_template_lock to prevent someone from reading the
265 * kstats after we set ih_ticks to 0 and before we increment
266 * ih_nsec to compensate.
267 *
268 * We must also protect against the interrupt arriving and incrementing
269 * ih_ticks between the time we read it and when we reset it to 0.
270 * To do this we use atomic_swap.
271 */
272
273 ASSERT(MUTEX_HELD(&ib_p->ib_ino_lst_mutex));
274
275 mutex_enter(&pxintr_ks_template_lock);
276 ticks = atomic_swap_64(&ih_p->ih_ticks, 0);
277 ih_p->ih_nsec += (uint64_t)tick2ns(ticks, cpu_id);
278 mutex_exit(&pxintr_ks_template_lock);
279 }
280
281
282 /*
283 * Redistribute interrupts of the specified weight. The first call has a weight
284 * of weight_max, which can be used to trigger initialization for
285 * redistribution. The inos with weight [weight_max, inf.) should be processed
286 * on the "weight == weight_max" call. This first call is followed by calls
287 * of decreasing weights, inos of that weight should be processed. The final
288 * call specifies a weight of zero, this can be used to trigger processing of
289 * stragglers.
290 */
291 static void
292 px_ib_intr_redist(void *arg, int32_t weight_max, int32_t weight)
293 {
294 px_ib_t *ib_p = (px_ib_t *)arg;
295 px_t *px_p = ib_p->ib_px_p;
296 dev_info_t *dip = px_p->px_dip;
297 px_ino_t *ino_p;
298 px_ino_pil_t *ipil_p;
299 px_ih_t *ih_lst;
300 int32_t dweight = 0;
301 int i;
302
303 /* Redistribute internal interrupts */
304 if (weight == 0) {
305 mutex_enter(&ib_p->ib_intr_lock);
306 px_ib_intr_dist_en(dip, intr_dist_cpuid(),
307 px_p->px_inos[PX_INTR_PEC], B_FALSE);
308 mutex_exit(&ib_p->ib_intr_lock);
309
310 px_hp_intr_redist(px_p);
311 }
312
313 /* Redistribute device interrupts */
314 mutex_enter(&ib_p->ib_ino_lst_mutex);
315 px_msiq_redist(px_p);
316
317 for (ino_p = ib_p->ib_ino_lst; ino_p; ino_p = ino_p->ino_next_p) {
318 /*
319 * Recomputes the sum of interrupt weights of devices that
320 * share the same ino upon first call marked by
321 * (weight == weight_max).
322 */
323 if (weight == weight_max) {
324 ino_p->ino_intr_weight = 0;
325
326 for (ipil_p = ino_p->ino_ipil_p; ipil_p;
327 ipil_p = ipil_p->ipil_next_p) {
328 for (i = 0, ih_lst = ipil_p->ipil_ih_head;
329 i < ipil_p->ipil_ih_size; i++,
330 ih_lst = ih_lst->ih_next) {
331 dweight = i_ddi_get_intr_weight(
332 ih_lst->ih_dip);
333 if (dweight > 0)
334 ino_p->ino_intr_weight +=
335 dweight;
336 }
337 }
338 }
339
340 /*
341 * As part of redistributing weighted interrupts over cpus,
342 * nexus redistributes device interrupts and updates
343 * cpu weight. The purpose is for the most light weighted
344 * cpu to take the next interrupt and gain weight, therefore
345 * attention demanding device gains more cpu attention by
346 * making itself heavy.
347 */
348 if ((weight == ino_p->ino_intr_weight) ||
349 ((weight >= weight_max) &&
350 (ino_p->ino_intr_weight >= weight_max))) {
351 uint32_t orig_cpuid = ino_p->ino_cpuid;
352
353 if (cpu[orig_cpuid] == NULL)
354 orig_cpuid = CPU->cpu_id;
355
356 DBG(DBG_IB, dip, "px_ib_intr_redist: sysino 0x%llx "
357 "current cpuid 0x%x current default cpuid 0x%x\n",
358 ino_p->ino_sysino, ino_p->ino_cpuid,
359 ino_p->ino_default_cpuid);
360
361 /* select target cpuid and mark ino established */
362 if (ino_p->ino_default_cpuid == -1)
363 ino_p->ino_cpuid = ino_p->ino_default_cpuid =
364 intr_dist_cpuid();
365 else if ((ino_p->ino_cpuid !=
366 ino_p->ino_default_cpuid) &&
367 cpu[ino_p->ino_default_cpuid] &&
368 cpu_intr_on(cpu[ino_p->ino_default_cpuid]))
369 ino_p->ino_cpuid = ino_p->ino_default_cpuid;
370 else if (!cpu_intr_on(cpu[ino_p->ino_cpuid]))
371 ino_p->ino_cpuid = intr_dist_cpuid();
372
373 DBG(DBG_IB, dip, "px_ib_intr_redist: sysino 0x%llx "
374 "new cpuid 0x%x new default cpuid 0x%x\n",
375 ino_p->ino_sysino, ino_p->ino_cpuid,
376 ino_p->ino_default_cpuid);
377
378 /* Add device weight to targeted cpu. */
379 for (ipil_p = ino_p->ino_ipil_p; ipil_p;
380 ipil_p = ipil_p->ipil_next_p) {
381 for (i = 0, ih_lst = ipil_p->ipil_ih_head;
382 i < ipil_p->ipil_ih_size; i++,
383 ih_lst = ih_lst->ih_next) {
384
385 dweight = i_ddi_get_intr_weight(
386 ih_lst->ih_dip);
387 intr_dist_cpuid_add_device_weight(
388 ino_p->ino_cpuid, ih_lst->ih_dip,
389 dweight);
390
391 /*
392 * Different cpus may have different
393 * clock speeds. to account for this,
394 * whenever an interrupt is moved to a
395 * new CPU, we convert the accumulated
396 * ticks into nsec, based upon the clock
397 * rate of the prior CPU.
398 *
399 * It is possible that the prior CPU no
400 * longer exists. In this case, fall
401 * back to using this CPU's clock rate.
402 *
403 * Note that the value in ih_ticks has
404 * already been corrected for any power
405 * savings mode which might have been
406 * in effect.
407 */
408 px_ib_cpu_ticks_to_ih_nsec(ib_p, ih_lst,
409 orig_cpuid);
410 }
411 }
412
413 /* enable interrupt on new targeted cpu */
414 px_ib_intr_dist_en(dip, ino_p->ino_cpuid,
415 ino_p->ino_ino, B_TRUE);
416 }
417 }
418 mutex_exit(&ib_p->ib_ino_lst_mutex);
419 }
420
421 /*
422 * Reset interrupts to IDLE. This function is called during
423 * panic handling after redistributing interrupts; it's needed to
424 * support dumping to network devices after 'sync' from OBP.
425 *
426 * N.B. This routine runs in a context where all other threads
427 * are permanently suspended.
428 */
429 static uint_t
430 px_ib_intr_reset(void *arg)
431 {
432 px_ib_t *ib_p = (px_ib_t *)arg;
433
434 DBG(DBG_IB, ib_p->ib_px_p->px_dip, "px_ib_intr_reset\n");
435
436 if (px_lib_intr_reset(ib_p->ib_px_p->px_dip) != DDI_SUCCESS)
437 return (BF_FATAL);
438
439 return (BF_NONE);
440 }
441
442 /*
443 * Locate px_ino_t structure on ib_p->ib_ino_lst according to ino#
444 * returns NULL if not found.
445 */
446 px_ino_t *
447 px_ib_locate_ino(px_ib_t *ib_p, devino_t ino_num)
448 {
449 px_ino_t *ino_p = ib_p->ib_ino_lst;
450
451 ASSERT(MUTEX_HELD(&ib_p->ib_ino_lst_mutex));
452
453 for (; ino_p && ino_p->ino_ino != ino_num; ino_p = ino_p->ino_next_p)
454 ;
455
456 return (ino_p);
457 }
458
459 px_ino_t *
460 px_ib_alloc_ino(px_ib_t *ib_p, devino_t ino_num)
461 {
462 sysino_t sysino;
463 px_ino_t *ino_p;
464
465 if (px_lib_intr_devino_to_sysino(ib_p->ib_px_p->px_dip,
466 ino_num, &sysino) != DDI_SUCCESS)
467 return (NULL);
468
469 ino_p = kmem_zalloc(sizeof (px_ino_t), KM_SLEEP);
470
471 ino_p->ino_next_p = ib_p->ib_ino_lst;
472 ib_p->ib_ino_lst = ino_p;
473
474 ino_p->ino_ino = ino_num;
475 ino_p->ino_sysino = sysino;
476 ino_p->ino_ib_p = ib_p;
477 ino_p->ino_unclaimed_intrs = 0;
478 ino_p->ino_lopil = 0;
479 ino_p->ino_cpuid = ino_p->ino_default_cpuid = (cpuid_t)-1;
480
481 return (ino_p);
482 }
483
484 px_ino_pil_t *
485 px_ib_new_ino_pil(px_ib_t *ib_p, devino_t ino_num, uint_t pil, px_ih_t *ih_p)
486 {
487 px_ino_pil_t *ipil_p = kmem_zalloc(sizeof (px_ino_pil_t), KM_SLEEP);
488 px_ino_t *ino_p;
489
490 if ((ino_p = px_ib_locate_ino(ib_p, ino_num)) == NULL)
491 ino_p = px_ib_alloc_ino(ib_p, ino_num);
492
493 ASSERT(ino_p != NULL);
494
495 ih_p->ih_next = ih_p;
496 ipil_p->ipil_pil = pil;
497 ipil_p->ipil_ih_head = ih_p;
498 ipil_p->ipil_ih_tail = ih_p;
499 ipil_p->ipil_ih_start = ih_p;
500 ipil_p->ipil_ih_size = 1;
501 ipil_p->ipil_ino_p = ino_p;
502
503 ipil_p->ipil_next_p = ino_p->ino_ipil_p;
504 ino_p->ino_ipil_p = ipil_p;
505 ino_p->ino_ipil_size++;
506
507 if ((ino_p->ino_lopil == 0) || (ino_p->ino_lopil > pil))
508 ino_p->ino_lopil = pil;
509
510 return (ipil_p);
511 }
512
513 void
514 px_ib_delete_ino_pil(px_ib_t *ib_p, px_ino_pil_t *ipil_p)
515 {
516 px_ino_t *ino_p = ipil_p->ipil_ino_p;
517 ushort_t pil = ipil_p->ipil_pil;
518 px_ino_pil_t *prev, *next;
519
520 ASSERT(MUTEX_HELD(&ib_p->ib_ino_lst_mutex));
521
522 if (ino_p->ino_ipil_p == ipil_p)
523 ino_p->ino_ipil_p = ipil_p->ipil_next_p;
524 else {
525 for (prev = next = ino_p->ino_ipil_p; next != ipil_p;
526 prev = next, next = next->ipil_next_p)
527 ;
528
529 if (prev)
530 prev->ipil_next_p = ipil_p->ipil_next_p;
531 }
532
533 kmem_free(ipil_p, sizeof (px_ino_pil_t));
534
535 if ((--ino_p->ino_ipil_size) && (ino_p->ino_lopil == pil)) {
536 for (next = ino_p->ino_ipil_p, pil = next->ipil_pil;
537 next; next = next->ipil_next_p) {
538
539 if (pil > next->ipil_pil)
540 pil = next->ipil_pil;
541 }
542
543 /*
544 * Value stored in pil should be the lowest pil.
545 */
546 ino_p->ino_lopil = pil;
547 }
548
549 if (ino_p->ino_ipil_size)
550 return;
551
552 ino_p->ino_lopil = 0;
553
554 if (ino_p->ino_msiq_p)
555 return;
556
557 if (ib_p->ib_ino_lst == ino_p)
558 ib_p->ib_ino_lst = ino_p->ino_next_p;
559 else {
560 px_ino_t *list = ib_p->ib_ino_lst;
561
562 for (; list->ino_next_p != ino_p; list = list->ino_next_p)
563 ;
564 list->ino_next_p = ino_p->ino_next_p;
565 }
566 }
567
568 /*
569 * Free all ino when we are detaching.
570 */
571 void
572 px_ib_free_ino_all(px_ib_t *ib_p)
573 {
574 px_ino_t *ino_p = ib_p->ib_ino_lst;
575 px_ino_t *next = NULL;
576
577 while (ino_p) {
578 next = ino_p->ino_next_p;
579 kmem_free(ino_p, sizeof (px_ino_t));
580 ino_p = next;
581 }
582 }
583
584 /*
585 * Locate px_ino_pil_t structure on ino_p->ino_ipil_p according to ino#
586 * returns NULL if not found.
587 */
588 px_ino_pil_t *
589 px_ib_ino_locate_ipil(px_ino_t *ino_p, uint_t pil)
590 {
591 px_ino_pil_t *ipil_p = ino_p->ino_ipil_p;
592
593 for (; ipil_p && ipil_p->ipil_pil != pil; ipil_p = ipil_p->ipil_next_p)
594 ;
595
596 return (ipil_p);
597 }
598
599 int
600 px_ib_ino_add_intr(px_t *px_p, px_ino_pil_t *ipil_p, px_ih_t *ih_p)
601 {
602 px_ino_t *ino_p = ipil_p->ipil_ino_p;
603 px_ib_t *ib_p = ino_p->ino_ib_p;
604 devino_t ino = ino_p->ino_ino;
605 sysino_t sysino = ino_p->ino_sysino;
606 dev_info_t *dip = px_p->px_dip;
607 cpuid_t curr_cpu;
608 int ret = DDI_SUCCESS;
609
610 ASSERT(MUTEX_HELD(&ib_p->ib_ino_lst_mutex));
611 ASSERT(ib_p == px_p->px_ib_p);
612
613 DBG(DBG_IB, dip, "px_ib_ino_add_intr ino=%x\n", ino_p->ino_ino);
614
615 /* Disable the interrupt */
616 if ((ret = px_lib_intr_gettarget(dip, sysino,
617 &curr_cpu)) != DDI_SUCCESS) {
618 DBG(DBG_IB, dip,
619 "px_ib_ino_add_intr px_intr_gettarget() failed\n");
620
621 return (ret);
622 }
623
624 /* Wait on pending interrupt */
625 if ((ret = px_ib_intr_pend(dip, sysino)) != DDI_SUCCESS) {
626 cmn_err(CE_WARN, "%s%d: px_ib_ino_add_intr: pending "
627 "sysino 0x%lx(ino 0x%x) timeout",
628 ddi_driver_name(dip), ddi_get_instance(dip),
629 sysino, ino);
630 }
631
632 /*
633 * If the interrupt was previously blocked (left in pending state)
634 * because of jabber we need to clear the pending state in case the
635 * jabber has gone away.
636 */
637 if (ino_p->ino_unclaimed_intrs > px_unclaimed_intr_max) {
638 cmn_err(CE_WARN,
639 "%s%d: px_ib_ino_add_intr: ino 0x%x has been unblocked",
640 ddi_driver_name(dip), ddi_get_instance(dip), ino);
641
642 ino_p->ino_unclaimed_intrs = 0;
643 ret = px_lib_intr_setstate(dip, sysino, INTR_IDLE_STATE);
644 }
645
646 if (ret != DDI_SUCCESS) {
647 DBG(DBG_IB, dip, "px_ib_ino_add_intr: failed, "
648 "ino 0x%x sysino 0x%x\n", ino, sysino);
649
650 return (ret);
651 }
652
653 /* Link up px_ih_t */
654 ih_p->ih_next = ipil_p->ipil_ih_head;
655 ipil_p->ipil_ih_tail->ih_next = ih_p;
656 ipil_p->ipil_ih_tail = ih_p;
657
658 ipil_p->ipil_ih_start = ipil_p->ipil_ih_head;
659 ipil_p->ipil_ih_size++;
660
661 /* Re-enable interrupt */
662 PX_INTR_ENABLE(dip, sysino, curr_cpu);
663
664 return (ret);
665 }
666
667 /*
668 * Removes px_ih_t from the ino's link list.
669 * uses hardware mutex to lock out interrupt threads.
670 * Side effects: interrupt belongs to that ino is turned off on return.
671 * if we are sharing PX slot with other inos, the caller needs
672 * to turn it back on.
673 */
674 int
675 px_ib_ino_rem_intr(px_t *px_p, px_ino_pil_t *ipil_p, px_ih_t *ih_p)
676 {
677 px_ino_t *ino_p = ipil_p->ipil_ino_p;
678 devino_t ino = ino_p->ino_ino;
679 sysino_t sysino = ino_p->ino_sysino;
680 dev_info_t *dip = px_p->px_dip;
681 px_ih_t *ih_lst = ipil_p->ipil_ih_head;
682 int i, ret = DDI_SUCCESS;
683
684 ASSERT(MUTEX_HELD(&ino_p->ino_ib_p->ib_ino_lst_mutex));
685
686 DBG(DBG_IB, px_p->px_dip, "px_ib_ino_rem_intr ino=%x\n",
687 ino_p->ino_ino);
688
689 /* Wait on pending interrupt */
690 if ((ret = px_ib_intr_pend(dip, sysino)) != DDI_SUCCESS) {
691 cmn_err(CE_WARN, "%s%d: px_ib_ino_rem_intr: pending "
692 "sysino 0x%lx(ino 0x%x) timeout",
693 ddi_driver_name(dip), ddi_get_instance(dip),
694 sysino, ino);
695 }
696
697 /*
698 * If the interrupt was previously blocked (left in pending state)
699 * because of jabber we need to clear the pending state in case the
700 * jabber has gone away.
701 */
702 if (ret == DDI_SUCCESS &&
703 ino_p->ino_unclaimed_intrs > px_unclaimed_intr_max) {
704 cmn_err(CE_WARN, "%s%d: px_ib_ino_rem_intr: "
705 "ino 0x%x has been unblocked",
706 ddi_driver_name(dip), ddi_get_instance(dip), ino);
707
708 ino_p->ino_unclaimed_intrs = 0;
709 ret = px_lib_intr_setstate(dip, sysino, INTR_IDLE_STATE);
710 }
711
712 if (ret != DDI_SUCCESS) {
713 DBG(DBG_IB, dip, "px_ib_ino_rem_intr: failed, "
714 "ino 0x%x sysino 0x%x\n", ino, sysino);
715
716 return (ret);
717 }
718
719 if (ipil_p->ipil_ih_size == 1) {
720 if (ih_lst != ih_p)
721 goto not_found;
722
723 /* No need to set head/tail as ino_p will be freed */
724 goto reset;
725 }
726
727 /* Search the link list for ih_p */
728 for (i = 0; (i < ipil_p->ipil_ih_size) &&
729 (ih_lst->ih_next != ih_p); i++, ih_lst = ih_lst->ih_next)
730 ;
731
732 if (ih_lst->ih_next != ih_p)
733 goto not_found;
734
735 /* Remove ih_p from the link list and maintain the head/tail */
736 ih_lst->ih_next = ih_p->ih_next;
737
738 if (ipil_p->ipil_ih_head == ih_p)
739 ipil_p->ipil_ih_head = ih_p->ih_next;
740 if (ipil_p->ipil_ih_tail == ih_p)
741 ipil_p->ipil_ih_tail = ih_lst;
742
743 ipil_p->ipil_ih_start = ipil_p->ipil_ih_head;
744
745 reset:
746 if (ih_p->ih_config_handle)
747 pci_config_teardown(&ih_p->ih_config_handle);
748 if (ih_p->ih_ksp != NULL)
749 kstat_delete(ih_p->ih_ksp);
750
751 kmem_free(ih_p, sizeof (px_ih_t));
752 ipil_p->ipil_ih_size--;
753
754 return (ret);
755
756 not_found:
757 DBG(DBG_R_INTX, ino_p->ino_ib_p->ib_px_p->px_dip,
758 "ino_p=%x does not have ih_p=%x\n", ino_p, ih_p);
759
760 return (DDI_FAILURE);
761 }
762
763 px_ih_t *
764 px_ib_intr_locate_ih(px_ino_pil_t *ipil_p, dev_info_t *rdip,
765 uint32_t inum, msiq_rec_type_t rec_type, msgcode_t msg_code)
766 {
767 px_ih_t *ih_p = ipil_p->ipil_ih_head;
768 int i;
769
770 for (i = 0; i < ipil_p->ipil_ih_size; i++, ih_p = ih_p->ih_next) {
771 if ((ih_p->ih_dip == rdip) && (ih_p->ih_inum == inum) &&
772 (ih_p->ih_rec_type == rec_type) &&
773 (ih_p->ih_msg_code == msg_code))
774 return (ih_p);
775 }
776
777 return ((px_ih_t *)NULL);
778 }
779
780 px_ih_t *
781 px_ib_alloc_ih(dev_info_t *rdip, uint32_t inum,
782 uint_t (*int_handler)(caddr_t int_handler_arg1, caddr_t int_handler_arg2),
783 caddr_t int_handler_arg1, caddr_t int_handler_arg2,
784 msiq_rec_type_t rec_type, msgcode_t msg_code)
785 {
786 px_ih_t *ih_p;
787
788 ih_p = kmem_alloc(sizeof (px_ih_t), KM_SLEEP);
789 ih_p->ih_dip = rdip;
790 ih_p->ih_inum = inum;
791 ih_p->ih_intr_state = PX_INTR_STATE_DISABLE;
792 ih_p->ih_intr_flags = PX_INTR_IDLE;
793 ih_p->ih_handler = int_handler;
794 ih_p->ih_handler_arg1 = int_handler_arg1;
795 ih_p->ih_handler_arg2 = int_handler_arg2;
796 ih_p->ih_config_handle = NULL;
797 ih_p->ih_rec_type = rec_type;
798 ih_p->ih_msg_code = msg_code;
799 ih_p->ih_nsec = 0;
800 ih_p->ih_ticks = 0;
801 ih_p->ih_ksp = NULL;
802
803 return (ih_p);
804 }
805
806 int
807 px_ib_update_intr_state(px_t *px_p, dev_info_t *rdip,
808 uint_t inum, devino_t ino, uint_t pil,
809 uint_t new_intr_state, msiq_rec_type_t rec_type,
810 msgcode_t msg_code)
811 {
812 px_ib_t *ib_p = px_p->px_ib_p;
813 px_ino_t *ino_p;
814 px_ino_pil_t *ipil_p;
815 px_ih_t *ih_p;
816 int ret = DDI_FAILURE;
817
818 DBG(DBG_IB, px_p->px_dip, "px_ib_update_intr_state: %s%d "
819 "inum %x devino %x pil %x state %x\n", ddi_driver_name(rdip),
820 ddi_get_instance(rdip), inum, ino, pil, new_intr_state);
821
822 mutex_enter(&ib_p->ib_ino_lst_mutex);
823
824 ino_p = px_ib_locate_ino(ib_p, ino);
825 if (ino_p && (ipil_p = px_ib_ino_locate_ipil(ino_p, pil))) {
826 if (ih_p = px_ib_intr_locate_ih(ipil_p, rdip, inum, rec_type,
827 msg_code)) {
828 ih_p->ih_intr_state = new_intr_state;
829 ret = DDI_SUCCESS;
830 }
831 }
832
833 mutex_exit(&ib_p->ib_ino_lst_mutex);
834 return (ret);
835 }
836
837
838 /*
839 * Get interrupt CPU for a given ino.
840 * Return info only for inos which are already mapped to devices.
841 */
842 /*ARGSUSED*/
843 int
844 px_ib_get_intr_target(px_t *px_p, devino_t ino, cpuid_t *cpu_id_p)
845 {
846 dev_info_t *dip = px_p->px_dip;
847 sysino_t sysino;
848 int ret;
849
850 DBG(DBG_IB, px_p->px_dip, "px_ib_get_intr_target: devino %x\n", ino);
851
852 /* Convert leaf-wide intr to system-wide intr */
853 if (px_lib_intr_devino_to_sysino(dip, ino, &sysino) != DDI_SUCCESS)
854 return (DDI_FAILURE);
855
856 ret = px_lib_intr_gettarget(dip, sysino, cpu_id_p);
857
858 DBG(DBG_IB, px_p->px_dip, "px_ib_get_intr_target: cpu_id %x\n",
859 *cpu_id_p);
860
861 return (ret);
862 }
863
864
865 /*
866 * Associate a new CPU with a given ino.
867 * Operate only on INOs which are already mapped to devices.
868 */
869 int
870 px_ib_set_intr_target(px_t *px_p, devino_t ino, cpuid_t cpu_id)
871 {
872 dev_info_t *dip = px_p->px_dip;
873 cpuid_t old_cpu_id;
874 sysino_t sysino;
875 int ret = DDI_SUCCESS;
876 extern const int _ncpu;
877 extern cpu_t *cpu[];
878
879 DBG(DBG_IB, px_p->px_dip, "px_ib_set_intr_target: devino %x "
880 "cpu_id %x\n", ino, cpu_id);
881
882 mutex_enter(&cpu_lock);
883
884 /* Convert leaf-wide intr to system-wide intr */
885 if (px_lib_intr_devino_to_sysino(dip, ino, &sysino) != DDI_SUCCESS) {
886 ret = DDI_FAILURE;
887 goto done;
888 }
889
890 if (px_lib_intr_gettarget(dip, sysino, &old_cpu_id) != DDI_SUCCESS) {
891 ret = DDI_FAILURE;
892 goto done;
893 }
894
895 /*
896 * Get lock, validate cpu and write it.
897 */
898 if ((cpu_id < _ncpu) && (cpu[cpu_id] && cpu_is_online(cpu[cpu_id]))) {
899 DBG(DBG_IB, dip, "px_ib_set_intr_target: Enabling CPU %d\n",
900 cpu_id);
901 px_ib_intr_dist_en(dip, cpu_id, ino, B_TRUE);
902 px_ib_log_new_cpu(px_p->px_ib_p, old_cpu_id, cpu_id, ino);
903 } else { /* Invalid cpu */
904 DBG(DBG_IB, dip, "px_ib_set_intr_target: Invalid cpuid %x\n",
905 cpu_id);
906 ret = DDI_EINVAL;
907 }
908
909 done:
910 mutex_exit(&cpu_lock);
911 return (ret);
912 }
913
914 hrtime_t px_ib_msix_retarget_timeout = 120ll * NANOSEC; /* 120 seconds */
915
916 /*
917 * Associate a new CPU with a given MSI/X.
918 * Operate only on MSI/Xs which are already mapped to devices.
919 */
920 int
921 px_ib_set_msix_target(px_t *px_p, ddi_intr_handle_impl_t *hdlp,
922 msinum_t msi_num, cpuid_t cpu_id)
923 {
924 px_ib_t *ib_p = px_p->px_ib_p;
925 px_msi_state_t *msi_state_p = &px_p->px_ib_p->ib_msi_state;
926 dev_info_t *dip = px_p->px_dip;
927 dev_info_t *rdip = hdlp->ih_dip;
928 msiqid_t msiq_id, old_msiq_id;
929 pci_msi_state_t msi_state;
930 msiq_rec_type_t msiq_rec_type;
931 msi_type_t msi_type;
932 px_ino_t *ino_p;
933 px_ih_t *ih_p, *old_ih_p;
934 cpuid_t old_cpu_id;
935 hrtime_t start_time, end_time;
936 int ret = DDI_SUCCESS;
937 extern const int _ncpu;
938 extern cpu_t *cpu[];
939
940 DBG(DBG_IB, dip, "px_ib_set_msix_target: msi_num %x new cpu_id %x\n",
941 msi_num, cpu_id);
942
943 mutex_enter(&cpu_lock);
944
945 /* Check for MSI64 support */
946 if ((hdlp->ih_cap & DDI_INTR_FLAG_MSI64) && msi_state_p->msi_addr64) {
947 msiq_rec_type = MSI64_REC;
948 msi_type = MSI64_TYPE;
949 } else {
950 msiq_rec_type = MSI32_REC;
951 msi_type = MSI32_TYPE;
952 }
953
954 if ((ret = px_lib_msi_getmsiq(dip, msi_num,
955 &old_msiq_id)) != DDI_SUCCESS) {
956
957 mutex_exit(&cpu_lock);
958 return (ret);
959 }
960
961 DBG(DBG_IB, dip, "px_ib_set_msix_target: current msiq 0x%x\n",
962 old_msiq_id);
963
964 if ((ret = px_ib_get_intr_target(px_p,
965 px_msiqid_to_devino(px_p, old_msiq_id),
966 &old_cpu_id)) != DDI_SUCCESS) {
967
968 mutex_exit(&cpu_lock);
969 return (ret);
970 }
971
972 DBG(DBG_IB, dip, "px_ib_set_msix_target: current cpuid 0x%x\n",
973 old_cpu_id);
974
975 if (cpu_id == old_cpu_id) {
976
977 mutex_exit(&cpu_lock);
978 return (DDI_SUCCESS);
979 }
980
981 /*
982 * Get lock, validate cpu and write it.
983 */
984 if (!((cpu_id < _ncpu) && (cpu[cpu_id] &&
985 cpu_is_online(cpu[cpu_id])))) {
986 /* Invalid cpu */
987 DBG(DBG_IB, dip, "px_ib_set_msix_target: Invalid cpuid %x\n",
988 cpu_id);
989
990 mutex_exit(&cpu_lock);
991 return (DDI_EINVAL);
992 }
993
994 DBG(DBG_IB, dip, "px_ib_set_msix_target: Enabling CPU %d\n", cpu_id);
995
996 if ((ret = px_add_msiq_intr(dip, rdip, hdlp,
997 msiq_rec_type, msi_num, cpu_id, &msiq_id)) != DDI_SUCCESS) {
998 DBG(DBG_IB, dip, "px_ib_set_msix_target: Add MSI handler "
999 "failed, rdip 0x%p msi 0x%x\n", rdip, msi_num);
1000
1001 mutex_exit(&cpu_lock);
1002 return (ret);
1003 }
1004
1005 if ((ret = px_lib_msi_setmsiq(dip, msi_num,
1006 msiq_id, msi_type)) != DDI_SUCCESS) {
1007 mutex_exit(&cpu_lock);
1008
1009 (void) px_rem_msiq_intr(dip, rdip,
1010 hdlp, msiq_rec_type, msi_num, msiq_id);
1011
1012 return (ret);
1013 }
1014
1015 if ((ret = px_ib_update_intr_state(px_p, rdip, hdlp->ih_inum,
1016 px_msiqid_to_devino(px_p, msiq_id), hdlp->ih_pri,
1017 PX_INTR_STATE_ENABLE, msiq_rec_type, msi_num)) != DDI_SUCCESS) {
1018 mutex_exit(&cpu_lock);
1019
1020 (void) px_rem_msiq_intr(dip, rdip,
1021 hdlp, msiq_rec_type, msi_num, msiq_id);
1022
1023 return (ret);
1024 }
1025
1026 mutex_exit(&cpu_lock);
1027
1028 /*
1029 * Remove the old handler, but first ensure it is finished.
1030 *
1031 * Each handler sets its PENDING flag before it clears the MSI state.
1032 * Then it clears that flag when finished. If a re-target occurs while
1033 * the MSI state is DELIVERED, then it is not yet known which of the
1034 * two handlers will take the interrupt. So the re-target operation
1035 * sets a RETARGET flag on both handlers in that case. Monitoring both
1036 * flags on both handlers then determines when the old handler can be
1037 * be safely removed.
1038 */
1039 mutex_enter(&ib_p->ib_ino_lst_mutex);
1040
1041 ino_p = px_ib_locate_ino(ib_p, px_msiqid_to_devino(px_p, old_msiq_id));
1042 old_ih_p = px_ib_intr_locate_ih(px_ib_ino_locate_ipil(ino_p,
1043 hdlp->ih_pri), rdip, hdlp->ih_inum, msiq_rec_type, msi_num);
1044
1045 ino_p = px_ib_locate_ino(ib_p, px_msiqid_to_devino(px_p, msiq_id));
1046 ih_p = px_ib_intr_locate_ih(px_ib_ino_locate_ipil(ino_p, hdlp->ih_pri),
1047 rdip, hdlp->ih_inum, msiq_rec_type, msi_num);
1048
1049 if ((ret = px_lib_msi_getstate(dip, msi_num,
1050 &msi_state)) != DDI_SUCCESS) {
1051 (void) px_rem_msiq_intr(dip, rdip,
1052 hdlp, msiq_rec_type, msi_num, msiq_id);
1053
1054 mutex_exit(&ib_p->ib_ino_lst_mutex);
1055 return (ret);
1056 }
1057
1058 if (msi_state == PCI_MSI_STATE_DELIVERED) {
1059 ih_p->ih_intr_flags |= PX_INTR_RETARGET;
1060 old_ih_p->ih_intr_flags |= PX_INTR_RETARGET;
1061 }
1062
1063 start_time = gethrtime();
1064 while (((ih_p->ih_intr_flags & PX_INTR_RETARGET) &&
1065 (old_ih_p->ih_intr_flags & PX_INTR_RETARGET)) ||
1066 (old_ih_p->ih_intr_flags & PX_INTR_PENDING)) {
1067
1068 /* Wait for one second */
1069 delay(drv_usectohz(1000000));
1070
1071 end_time = gethrtime() - start_time;
1072 if (end_time > px_ib_msix_retarget_timeout) {
1073 cmn_err(CE_WARN, "MSIX retarget %x is not completed, "
1074 "even after waiting %llx ticks\n",
1075 msi_num, end_time);
1076 break;
1077 }
1078 }
1079
1080 ih_p->ih_intr_flags &= ~(PX_INTR_RETARGET);
1081
1082 mutex_exit(&ib_p->ib_ino_lst_mutex);
1083
1084 ret = px_rem_msiq_intr(dip, rdip,
1085 hdlp, msiq_rec_type, msi_num, old_msiq_id);
1086
1087 return (ret);
1088 }
1089
1090
1091 static void
1092 px_fill_in_intr_devs(pcitool_intr_dev_t *dev, char *driver_name,
1093 char *path_name, int instance)
1094 {
1095 (void) strlcpy(dev->driver_name, driver_name, MAXMODCONFNAME);
1096 (void) strlcpy(dev->path, path_name, MAXPATHLEN);
1097 dev->dev_inst = instance;
1098 }
1099
1100
1101 /*
1102 * Return the dips or number of dips associated with a given interrupt block.
1103 * Size of dips array arg is passed in as dips_ret arg.
1104 * Number of dips returned is returned in dips_ret arg.
1105 * Array of dips gets returned in the dips argument.
1106 * Function returns number of dips existing for the given interrupt block.
1107 *
1108 * Note: this function assumes an enabled/valid INO, which is why it returns
1109 * the px node and (Internal) when it finds no other devices (and *devs_ret > 0)
1110 */
1111 uint8_t
1112 pxtool_ib_get_ino_devs(px_t *px_p, uint32_t ino, uint32_t msi_num,
1113 uint8_t *devs_ret, pcitool_intr_dev_t *devs)
1114 {
1115 px_ib_t *ib_p = px_p->px_ib_p;
1116 px_ino_t *ino_p;
1117 px_ino_pil_t *ipil_p;
1118 px_ih_t *ih_p;
1119 uint32_t num_devs = 0;
1120 char pathname[MAXPATHLEN];
1121 int i, j;
1122
1123 mutex_enter(&ib_p->ib_ino_lst_mutex);
1124 ino_p = px_ib_locate_ino(ib_p, ino);
1125 if (ino_p != NULL) {
1126 for (j = 0, ipil_p = ino_p->ino_ipil_p; ipil_p;
1127 ipil_p = ipil_p->ipil_next_p) {
1128 num_devs += ipil_p->ipil_ih_size;
1129
1130 for (i = 0, ih_p = ipil_p->ipil_ih_head;
1131 ((i < ipil_p->ipil_ih_size) && (i < *devs_ret));
1132 i++, j++, ih_p = ih_p->ih_next) {
1133 (void) ddi_pathname(ih_p->ih_dip, pathname);
1134
1135 if (ih_p->ih_msg_code == msi_num) {
1136 num_devs = *devs_ret = 1;
1137 px_fill_in_intr_devs(&devs[0],
1138 (char *)ddi_driver_name(
1139 ih_p->ih_dip), pathname,
1140 ddi_get_instance(ih_p->ih_dip));
1141 goto done;
1142 }
1143
1144 px_fill_in_intr_devs(&devs[j],
1145 (char *)ddi_driver_name(ih_p->ih_dip),
1146 pathname, ddi_get_instance(ih_p->ih_dip));
1147 }
1148 }
1149
1150 *devs_ret = j;
1151 } else if (*devs_ret > 0) {
1152 (void) ddi_pathname(px_p->px_dip, pathname);
1153 strcat(pathname, " (Internal)");
1154 px_fill_in_intr_devs(&devs[0],
1155 (char *)ddi_driver_name(px_p->px_dip), pathname,
1156 ddi_get_instance(px_p->px_dip));
1157 num_devs = *devs_ret = 1;
1158 }
1159
1160 done:
1161 mutex_exit(&ib_p->ib_ino_lst_mutex);
1162
1163 return (num_devs);
1164 }
1165
1166
1167 int
1168 pxtool_ib_get_msi_info(px_t *px_p, devino_t ino, msinum_t msi_num,
1169 ddi_intr_handle_impl_t *hdlp)
1170 {
1171 px_ib_t *ib_p = px_p->px_ib_p;
1172 px_ino_t *ino_p;
1173 px_ino_pil_t *ipil_p;
1174 px_ih_t *ih_p;
1175 int i;
1176
1177 mutex_enter(&ib_p->ib_ino_lst_mutex);
1178
1179 if ((ino_p = px_ib_locate_ino(ib_p, ino)) == NULL) {
1180 mutex_exit(&ib_p->ib_ino_lst_mutex);
1181 return (DDI_FAILURE);
1182 }
1183
1184 for (ipil_p = ino_p->ino_ipil_p; ipil_p;
1185 ipil_p = ipil_p->ipil_next_p) {
1186 for (i = 0, ih_p = ipil_p->ipil_ih_head;
1187 ((i < ipil_p->ipil_ih_size) && ih_p);
1188 i++, ih_p = ih_p->ih_next) {
1189
1190 if (ih_p->ih_msg_code != msi_num)
1191 continue;
1192
1193 hdlp->ih_dip = ih_p->ih_dip;
1194 hdlp->ih_inum = ih_p->ih_inum;
1195 hdlp->ih_cb_func = ih_p->ih_handler;
1196 hdlp->ih_cb_arg1 = ih_p->ih_handler_arg1;
1197 hdlp->ih_cb_arg2 = ih_p->ih_handler_arg2;
1198 if (ih_p->ih_rec_type == MSI64_REC)
1199 hdlp->ih_cap = DDI_INTR_FLAG_MSI64;
1200 hdlp->ih_pri = ipil_p->ipil_pil;
1201 hdlp->ih_ver = DDI_INTR_VERSION;
1202
1203 mutex_exit(&ib_p->ib_ino_lst_mutex);
1204 return (DDI_SUCCESS);
1205 }
1206 }
1207
1208 mutex_exit(&ib_p->ib_ino_lst_mutex);
1209 return (DDI_FAILURE);
1210 }
1211
1212 void
1213 px_ib_log_new_cpu(px_ib_t *ib_p, cpuid_t old_cpu_id, cpuid_t new_cpu_id,
1214 uint32_t ino)
1215 {
1216 px_ino_t *ino_p;
1217 px_ino_pil_t *ipil_p;
1218 px_ih_t *ih_p;
1219 int i;
1220
1221 mutex_enter(&ib_p->ib_ino_lst_mutex);
1222
1223 /* Log in OS data structures the new CPU. */
1224 if (ino_p = px_ib_locate_ino(ib_p, ino)) {
1225
1226 /* Log in OS data structures the new CPU. */
1227 ino_p->ino_cpuid = new_cpu_id;
1228
1229 for (ipil_p = ino_p->ino_ipil_p; ipil_p;
1230 ipil_p = ipil_p->ipil_next_p) {
1231 for (i = 0, ih_p = ipil_p->ipil_ih_head;
1232 (i < ipil_p->ipil_ih_size);
1233 i++, ih_p = ih_p->ih_next) {
1234 /*
1235 * Account for any residual time
1236 * to be logged for old cpu.
1237 */
1238 px_ib_cpu_ticks_to_ih_nsec(ib_p,
1239 ih_p, old_cpu_id);
1240 }
1241 }
1242 }
1243
1244 mutex_exit(&ib_p->ib_ino_lst_mutex);
1245 }
--- EOF ---