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_sectohz(1));
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 }