17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 544bb982bSgovinda * Common Development and Distribution License (the "License"). 644bb982bSgovinda * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 22d17daf0bSScott Carter, SD IOSW * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate /* 277c478bd9Sstevel@tonic-gate * PX Interrupt Block implementation 287c478bd9Sstevel@tonic-gate */ 297c478bd9Sstevel@tonic-gate 307c478bd9Sstevel@tonic-gate #include <sys/types.h> 317c478bd9Sstevel@tonic-gate #include <sys/kmem.h> 327c478bd9Sstevel@tonic-gate #include <sys/async.h> 337c478bd9Sstevel@tonic-gate #include <sys/systm.h> /* panicstr */ 347c478bd9Sstevel@tonic-gate #include <sys/spl.h> 357c478bd9Sstevel@tonic-gate #include <sys/sunddi.h> 367c478bd9Sstevel@tonic-gate #include <sys/machsystm.h> /* intr_dist_add */ 377c478bd9Sstevel@tonic-gate #include <sys/ddi_impldefs.h> 387c478bd9Sstevel@tonic-gate #include <sys/cpuvar.h> 3925cf1a30Sjl #include <sys/time.h> 407c478bd9Sstevel@tonic-gate #include "px_obj.h" 417c478bd9Sstevel@tonic-gate 427c478bd9Sstevel@tonic-gate /*LINTLIBRARY*/ 437c478bd9Sstevel@tonic-gate 447c478bd9Sstevel@tonic-gate static void px_ib_intr_redist(void *arg, int32_t weight_max, int32_t weight); 4569cd775fSschwartz static void px_ib_cpu_ticks_to_ih_nsec(px_ib_t *ib_p, px_ih_t *ih_p, 4669cd775fSschwartz uint32_t cpu_id); 477c478bd9Sstevel@tonic-gate static uint_t px_ib_intr_reset(void *arg); 4869cd775fSschwartz static void px_fill_in_intr_devs(pcitool_intr_dev_t *dev, char *driver_name, 4969cd775fSschwartz char *path_name, int instance); 507c478bd9Sstevel@tonic-gate 5125cf1a30Sjl extern uint64_t xc_tick_jump_limit; 5225cf1a30Sjl 537c478bd9Sstevel@tonic-gate int 547c478bd9Sstevel@tonic-gate px_ib_attach(px_t *px_p) 557c478bd9Sstevel@tonic-gate { 567c478bd9Sstevel@tonic-gate dev_info_t *dip = px_p->px_dip; 577c478bd9Sstevel@tonic-gate px_ib_t *ib_p; 587c478bd9Sstevel@tonic-gate sysino_t sysino; 597c478bd9Sstevel@tonic-gate px_fault_t *fault_p = &px_p->px_fault; 607c478bd9Sstevel@tonic-gate 617c478bd9Sstevel@tonic-gate DBG(DBG_IB, dip, "px_ib_attach\n"); 627c478bd9Sstevel@tonic-gate 637c478bd9Sstevel@tonic-gate if (px_lib_intr_devino_to_sysino(px_p->px_dip, 64f8d2de6bSjchu px_p->px_inos[PX_INTR_PEC], &sysino) != DDI_SUCCESS) 657c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 667c478bd9Sstevel@tonic-gate 677c478bd9Sstevel@tonic-gate /* 687c478bd9Sstevel@tonic-gate * Allocate interrupt block state structure and link it to 697c478bd9Sstevel@tonic-gate * the px state structure. 707c478bd9Sstevel@tonic-gate */ 717c478bd9Sstevel@tonic-gate ib_p = kmem_zalloc(sizeof (px_ib_t), KM_SLEEP); 727c478bd9Sstevel@tonic-gate px_p->px_ib_p = ib_p; 737c478bd9Sstevel@tonic-gate ib_p->ib_px_p = px_p; 74b0fc0e77Sgovinda ib_p->ib_ino_lst = (px_ino_t *)NULL; 757c478bd9Sstevel@tonic-gate 767c478bd9Sstevel@tonic-gate mutex_init(&ib_p->ib_intr_lock, NULL, MUTEX_DRIVER, NULL); 777c478bd9Sstevel@tonic-gate mutex_init(&ib_p->ib_ino_lst_mutex, NULL, MUTEX_DRIVER, NULL); 787c478bd9Sstevel@tonic-gate 797c478bd9Sstevel@tonic-gate bus_func_register(BF_TYPE_RESINTR, px_ib_intr_reset, ib_p); 807c478bd9Sstevel@tonic-gate 817c478bd9Sstevel@tonic-gate intr_dist_add_weighted(px_ib_intr_redist, ib_p); 827c478bd9Sstevel@tonic-gate 837c478bd9Sstevel@tonic-gate /* 847c478bd9Sstevel@tonic-gate * Initialize PEC fault data structure 857c478bd9Sstevel@tonic-gate */ 867c478bd9Sstevel@tonic-gate fault_p->px_fh_dip = dip; 877c478bd9Sstevel@tonic-gate fault_p->px_fh_sysino = sysino; 88f8d2de6bSjchu fault_p->px_err_func = px_err_dmc_pec_intr; 89f8d2de6bSjchu fault_p->px_intr_ino = px_p->px_inos[PX_INTR_PEC]; 907c478bd9Sstevel@tonic-gate 917c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 927c478bd9Sstevel@tonic-gate } 937c478bd9Sstevel@tonic-gate 947c478bd9Sstevel@tonic-gate void 957c478bd9Sstevel@tonic-gate px_ib_detach(px_t *px_p) 967c478bd9Sstevel@tonic-gate { 977c478bd9Sstevel@tonic-gate px_ib_t *ib_p = px_p->px_ib_p; 987c478bd9Sstevel@tonic-gate dev_info_t *dip = px_p->px_dip; 997c478bd9Sstevel@tonic-gate 1007c478bd9Sstevel@tonic-gate DBG(DBG_IB, dip, "px_ib_detach\n"); 1017c478bd9Sstevel@tonic-gate 1027c478bd9Sstevel@tonic-gate bus_func_unregister(BF_TYPE_RESINTR, px_ib_intr_reset, ib_p); 1037c478bd9Sstevel@tonic-gate intr_dist_rem_weighted(px_ib_intr_redist, ib_p); 1047c478bd9Sstevel@tonic-gate 1057c478bd9Sstevel@tonic-gate mutex_destroy(&ib_p->ib_ino_lst_mutex); 1067c478bd9Sstevel@tonic-gate mutex_destroy(&ib_p->ib_intr_lock); 1077c478bd9Sstevel@tonic-gate 1087c478bd9Sstevel@tonic-gate px_ib_free_ino_all(ib_p); 1097c478bd9Sstevel@tonic-gate 1107c478bd9Sstevel@tonic-gate px_p->px_ib_p = NULL; 1117c478bd9Sstevel@tonic-gate kmem_free(ib_p, sizeof (px_ib_t)); 1127c478bd9Sstevel@tonic-gate } 1137c478bd9Sstevel@tonic-gate 1147c478bd9Sstevel@tonic-gate void 1157c478bd9Sstevel@tonic-gate px_ib_intr_enable(px_t *px_p, cpuid_t cpu_id, devino_t ino) 1167c478bd9Sstevel@tonic-gate { 1177c478bd9Sstevel@tonic-gate px_ib_t *ib_p = px_p->px_ib_p; 1187c478bd9Sstevel@tonic-gate sysino_t sysino; 1197c478bd9Sstevel@tonic-gate 1207c478bd9Sstevel@tonic-gate /* 1217c478bd9Sstevel@tonic-gate * Determine the cpu for the interrupt 1227c478bd9Sstevel@tonic-gate */ 1237c478bd9Sstevel@tonic-gate mutex_enter(&ib_p->ib_intr_lock); 1247c478bd9Sstevel@tonic-gate 1257c478bd9Sstevel@tonic-gate DBG(DBG_IB, px_p->px_dip, 1267c478bd9Sstevel@tonic-gate "px_ib_intr_enable: ino=%x cpu_id=%x\n", ino, cpu_id); 1277c478bd9Sstevel@tonic-gate 1287c478bd9Sstevel@tonic-gate if (px_lib_intr_devino_to_sysino(px_p->px_dip, ino, 1297c478bd9Sstevel@tonic-gate &sysino) != DDI_SUCCESS) { 1307c478bd9Sstevel@tonic-gate DBG(DBG_IB, px_p->px_dip, 1317c478bd9Sstevel@tonic-gate "px_ib_intr_enable: px_intr_devino_to_sysino() failed\n"); 1327c478bd9Sstevel@tonic-gate 1337c478bd9Sstevel@tonic-gate mutex_exit(&ib_p->ib_intr_lock); 1347c478bd9Sstevel@tonic-gate return; 1357c478bd9Sstevel@tonic-gate } 1367c478bd9Sstevel@tonic-gate 1377c478bd9Sstevel@tonic-gate PX_INTR_ENABLE(px_p->px_dip, sysino, cpu_id); 138a195726fSgovinda px_lib_intr_setstate(px_p->px_dip, sysino, INTR_IDLE_STATE); 1397c478bd9Sstevel@tonic-gate 1407c478bd9Sstevel@tonic-gate mutex_exit(&ib_p->ib_intr_lock); 1417c478bd9Sstevel@tonic-gate } 1427c478bd9Sstevel@tonic-gate 1437c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 1447c478bd9Sstevel@tonic-gate void 1457c478bd9Sstevel@tonic-gate px_ib_intr_disable(px_ib_t *ib_p, devino_t ino, int wait) 1467c478bd9Sstevel@tonic-gate { 1477c478bd9Sstevel@tonic-gate sysino_t sysino; 1487c478bd9Sstevel@tonic-gate 1497c478bd9Sstevel@tonic-gate mutex_enter(&ib_p->ib_intr_lock); 1507c478bd9Sstevel@tonic-gate 1517c478bd9Sstevel@tonic-gate DBG(DBG_IB, ib_p->ib_px_p->px_dip, "px_ib_intr_disable: ino=%x\n", ino); 1527c478bd9Sstevel@tonic-gate 1537c478bd9Sstevel@tonic-gate /* Disable the interrupt */ 1547c478bd9Sstevel@tonic-gate if (px_lib_intr_devino_to_sysino(ib_p->ib_px_p->px_dip, ino, 1557c478bd9Sstevel@tonic-gate &sysino) != DDI_SUCCESS) { 1567c478bd9Sstevel@tonic-gate DBG(DBG_IB, ib_p->ib_px_p->px_dip, 1577c478bd9Sstevel@tonic-gate "px_ib_intr_disable: px_intr_devino_to_sysino() failed\n"); 1587c478bd9Sstevel@tonic-gate 1597c478bd9Sstevel@tonic-gate mutex_exit(&ib_p->ib_intr_lock); 1607c478bd9Sstevel@tonic-gate return; 1617c478bd9Sstevel@tonic-gate } 1627c478bd9Sstevel@tonic-gate 1637c478bd9Sstevel@tonic-gate PX_INTR_DISABLE(ib_p->ib_px_p->px_dip, sysino); 1647c478bd9Sstevel@tonic-gate 1657c478bd9Sstevel@tonic-gate mutex_exit(&ib_p->ib_intr_lock); 1667c478bd9Sstevel@tonic-gate } 1677c478bd9Sstevel@tonic-gate 1687c478bd9Sstevel@tonic-gate 16969cd775fSschwartz void 1707c478bd9Sstevel@tonic-gate px_ib_intr_dist_en(dev_info_t *dip, cpuid_t cpu_id, devino_t ino, 1717c478bd9Sstevel@tonic-gate boolean_t wait_flag) 1727c478bd9Sstevel@tonic-gate { 1737c478bd9Sstevel@tonic-gate uint32_t old_cpu_id; 1747c478bd9Sstevel@tonic-gate sysino_t sysino; 1757c478bd9Sstevel@tonic-gate intr_valid_state_t enabled = 0; 17625cf1a30Sjl hrtime_t start_time, prev, curr, interval, jump; 17725cf1a30Sjl hrtime_t intr_timeout; 1787c478bd9Sstevel@tonic-gate intr_state_t intr_state; 179f8d2de6bSjchu int e = DDI_SUCCESS; 1807c478bd9Sstevel@tonic-gate 1817c478bd9Sstevel@tonic-gate DBG(DBG_IB, dip, "px_ib_intr_dist_en: ino=0x%x\n", ino); 1827c478bd9Sstevel@tonic-gate 1837c478bd9Sstevel@tonic-gate if (px_lib_intr_devino_to_sysino(dip, ino, &sysino) != DDI_SUCCESS) { 1847c478bd9Sstevel@tonic-gate DBG(DBG_IB, dip, "px_ib_intr_dist_en: " 1857c478bd9Sstevel@tonic-gate "px_intr_devino_to_sysino() failed, ino 0x%x\n", ino); 1867c478bd9Sstevel@tonic-gate return; 1877c478bd9Sstevel@tonic-gate } 1887c478bd9Sstevel@tonic-gate 1897c478bd9Sstevel@tonic-gate /* Skip enabling disabled interrupts */ 1907c478bd9Sstevel@tonic-gate if (px_lib_intr_getvalid(dip, sysino, &enabled) != DDI_SUCCESS) { 1917c478bd9Sstevel@tonic-gate DBG(DBG_IB, dip, "px_ib_intr_dist_en: px_intr_getvalid() " 1927c478bd9Sstevel@tonic-gate "failed, sysino 0x%x\n", sysino); 1937c478bd9Sstevel@tonic-gate return; 1947c478bd9Sstevel@tonic-gate } 1957c478bd9Sstevel@tonic-gate if (!enabled) 1967c478bd9Sstevel@tonic-gate return; 1977c478bd9Sstevel@tonic-gate 1987c478bd9Sstevel@tonic-gate /* Done if redistributed onto the same cpuid */ 1997c478bd9Sstevel@tonic-gate if (px_lib_intr_gettarget(dip, sysino, &old_cpu_id) != DDI_SUCCESS) { 2007c478bd9Sstevel@tonic-gate DBG(DBG_IB, dip, "px_ib_intr_dist_en: " 2017c478bd9Sstevel@tonic-gate "px_intr_gettarget() failed\n"); 2027c478bd9Sstevel@tonic-gate return; 2037c478bd9Sstevel@tonic-gate } 2047c478bd9Sstevel@tonic-gate if (cpu_id == old_cpu_id) 2057c478bd9Sstevel@tonic-gate return; 2067c478bd9Sstevel@tonic-gate 2077c478bd9Sstevel@tonic-gate if (!wait_flag) 2087c478bd9Sstevel@tonic-gate goto done; 2097c478bd9Sstevel@tonic-gate 2107c478bd9Sstevel@tonic-gate /* Busy wait on pending interrupts */ 2117c478bd9Sstevel@tonic-gate PX_INTR_DISABLE(dip, sysino); 2127c478bd9Sstevel@tonic-gate 21325cf1a30Sjl intr_timeout = px_intrpend_timeout; 21425cf1a30Sjl jump = TICK_TO_NSEC(xc_tick_jump_limit); 21525cf1a30Sjl 21625cf1a30Sjl for (curr = start_time = gethrtime(); !panicstr && 2177c478bd9Sstevel@tonic-gate ((e = px_lib_intr_getstate(dip, sysino, &intr_state)) == 218f0d69850Srameshc DDI_SUCCESS) && 2197c478bd9Sstevel@tonic-gate (intr_state == INTR_DELIVERED_STATE); /* */) { 22025cf1a30Sjl /* 22125cf1a30Sjl * If we have a really large jump in hrtime, it is most 22225cf1a30Sjl * probably because we entered the debugger (or OBP, 22325cf1a30Sjl * in general). So, we adjust the timeout accordingly 22425cf1a30Sjl * to prevent declaring an interrupt timeout. The 22525cf1a30Sjl * master-interrupt mechanism in OBP should deliver 22625cf1a30Sjl * the interrupts properly. 22725cf1a30Sjl */ 22825cf1a30Sjl prev = curr; 22925cf1a30Sjl curr = gethrtime(); 23025cf1a30Sjl interval = curr - prev; 23125cf1a30Sjl if (interval > jump) 23225cf1a30Sjl intr_timeout += interval; 23325cf1a30Sjl if (curr - start_time > intr_timeout) { 2347c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, 235b40cec45Skrishnae "%s%d: px_ib_intr_dist_en: sysino 0x%lx(ino 0x%x) " 2367c478bd9Sstevel@tonic-gate "from cpu id 0x%x to 0x%x timeout", 2377c478bd9Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip), 2387c478bd9Sstevel@tonic-gate sysino, ino, old_cpu_id, cpu_id); 2397c478bd9Sstevel@tonic-gate 2407c478bd9Sstevel@tonic-gate e = DDI_FAILURE; 2417c478bd9Sstevel@tonic-gate break; 2427c478bd9Sstevel@tonic-gate } 2437c478bd9Sstevel@tonic-gate } 2447c478bd9Sstevel@tonic-gate 2457c478bd9Sstevel@tonic-gate if (e != DDI_SUCCESS) 2467c478bd9Sstevel@tonic-gate DBG(DBG_IB, dip, "px_ib_intr_dist_en: failed, " 2477c478bd9Sstevel@tonic-gate "ino 0x%x sysino 0x%x\n", ino, sysino); 2487c478bd9Sstevel@tonic-gate 2497c478bd9Sstevel@tonic-gate done: 2507c478bd9Sstevel@tonic-gate PX_INTR_ENABLE(dip, sysino, cpu_id); 2517c478bd9Sstevel@tonic-gate } 2527c478bd9Sstevel@tonic-gate 25369cd775fSschwartz static void 25469cd775fSschwartz px_ib_cpu_ticks_to_ih_nsec(px_ib_t *ib_p, px_ih_t *ih_p, uint32_t cpu_id) 25569cd775fSschwartz { 25669cd775fSschwartz extern kmutex_t pxintr_ks_template_lock; 25769cd775fSschwartz hrtime_t ticks; 25869cd775fSschwartz 25969cd775fSschwartz /* 26069cd775fSschwartz * Because we are updating two fields in ih_t we must lock 26169cd775fSschwartz * pxintr_ks_template_lock to prevent someone from reading the 26269cd775fSschwartz * kstats after we set ih_ticks to 0 and before we increment 26369cd775fSschwartz * ih_nsec to compensate. 26469cd775fSschwartz * 26569cd775fSschwartz * We must also protect against the interrupt arriving and incrementing 26669cd775fSschwartz * ih_ticks between the time we read it and when we reset it to 0. 26769cd775fSschwartz * To do this we use atomic_swap. 26869cd775fSschwartz */ 26969cd775fSschwartz 27069cd775fSschwartz ASSERT(MUTEX_HELD(&ib_p->ib_ino_lst_mutex)); 27169cd775fSschwartz 27269cd775fSschwartz mutex_enter(&pxintr_ks_template_lock); 27369cd775fSschwartz ticks = atomic_swap_64(&ih_p->ih_ticks, 0); 27469cd775fSschwartz ih_p->ih_nsec += (uint64_t)tick2ns(ticks, cpu_id); 27569cd775fSschwartz mutex_exit(&pxintr_ks_template_lock); 27669cd775fSschwartz } 27769cd775fSschwartz 2787c478bd9Sstevel@tonic-gate 2797c478bd9Sstevel@tonic-gate /* 2807c478bd9Sstevel@tonic-gate * Redistribute interrupts of the specified weight. The first call has a weight 2817c478bd9Sstevel@tonic-gate * of weight_max, which can be used to trigger initialization for 2827c478bd9Sstevel@tonic-gate * redistribution. The inos with weight [weight_max, inf.) should be processed 2837c478bd9Sstevel@tonic-gate * on the "weight == weight_max" call. This first call is followed by calls 2847c478bd9Sstevel@tonic-gate * of decreasing weights, inos of that weight should be processed. The final 2857c478bd9Sstevel@tonic-gate * call specifies a weight of zero, this can be used to trigger processing of 2867c478bd9Sstevel@tonic-gate * stragglers. 2877c478bd9Sstevel@tonic-gate */ 2887c478bd9Sstevel@tonic-gate static void 2897c478bd9Sstevel@tonic-gate px_ib_intr_redist(void *arg, int32_t weight_max, int32_t weight) 2907c478bd9Sstevel@tonic-gate { 2917c478bd9Sstevel@tonic-gate px_ib_t *ib_p = (px_ib_t *)arg; 2927c478bd9Sstevel@tonic-gate px_t *px_p = ib_p->ib_px_p; 2937c478bd9Sstevel@tonic-gate dev_info_t *dip = px_p->px_dip; 294b0fc0e77Sgovinda px_ino_t *ino_p; 295b0fc0e77Sgovinda px_ino_pil_t *ipil_p; 2967c478bd9Sstevel@tonic-gate px_ih_t *ih_lst; 2977c478bd9Sstevel@tonic-gate int32_t dweight = 0; 2987c478bd9Sstevel@tonic-gate int i; 2997c478bd9Sstevel@tonic-gate 3007c478bd9Sstevel@tonic-gate /* Redistribute internal interrupts */ 3017c478bd9Sstevel@tonic-gate if (weight == 0) { 3027c478bd9Sstevel@tonic-gate mutex_enter(&ib_p->ib_intr_lock); 30301689544Sjchu px_ib_intr_dist_en(dip, intr_dist_cpuid(), 30401689544Sjchu px_p->px_inos[PX_INTR_PEC], B_FALSE); 3057c478bd9Sstevel@tonic-gate mutex_exit(&ib_p->ib_intr_lock); 306ab4471cdSscarter 307ab4471cdSscarter px_hp_intr_redist(px_p); 3087c478bd9Sstevel@tonic-gate } 3097c478bd9Sstevel@tonic-gate 3107c478bd9Sstevel@tonic-gate /* Redistribute device interrupts */ 3117c478bd9Sstevel@tonic-gate mutex_enter(&ib_p->ib_ino_lst_mutex); 31209b1eac2SEvan Yan px_msiq_redist(px_p); 3137c478bd9Sstevel@tonic-gate 314b0fc0e77Sgovinda for (ino_p = ib_p->ib_ino_lst; ino_p; ino_p = ino_p->ino_next_p) { 3157c478bd9Sstevel@tonic-gate /* 3167c478bd9Sstevel@tonic-gate * Recomputes the sum of interrupt weights of devices that 3177c478bd9Sstevel@tonic-gate * share the same ino upon first call marked by 3187c478bd9Sstevel@tonic-gate * (weight == weight_max). 3197c478bd9Sstevel@tonic-gate */ 3207c478bd9Sstevel@tonic-gate if (weight == weight_max) { 3217c478bd9Sstevel@tonic-gate ino_p->ino_intr_weight = 0; 322b0fc0e77Sgovinda 323b0fc0e77Sgovinda for (ipil_p = ino_p->ino_ipil_p; ipil_p; 324b0fc0e77Sgovinda ipil_p = ipil_p->ipil_next_p) { 325b0fc0e77Sgovinda for (i = 0, ih_lst = ipil_p->ipil_ih_head; 326b0fc0e77Sgovinda i < ipil_p->ipil_ih_size; i++, 327b0fc0e77Sgovinda ih_lst = ih_lst->ih_next) { 328b0fc0e77Sgovinda dweight = i_ddi_get_intr_weight( 329b0fc0e77Sgovinda ih_lst->ih_dip); 330b0fc0e77Sgovinda if (dweight > 0) 331b0fc0e77Sgovinda ino_p->ino_intr_weight += 332b0fc0e77Sgovinda dweight; 333b0fc0e77Sgovinda } 3347c478bd9Sstevel@tonic-gate } 3357c478bd9Sstevel@tonic-gate } 3367c478bd9Sstevel@tonic-gate 3377c478bd9Sstevel@tonic-gate /* 3387c478bd9Sstevel@tonic-gate * As part of redistributing weighted interrupts over cpus, 3397c478bd9Sstevel@tonic-gate * nexus redistributes device interrupts and updates 3407c478bd9Sstevel@tonic-gate * cpu weight. The purpose is for the most light weighted 3417c478bd9Sstevel@tonic-gate * cpu to take the next interrupt and gain weight, therefore 3427c478bd9Sstevel@tonic-gate * attention demanding device gains more cpu attention by 3437c478bd9Sstevel@tonic-gate * making itself heavy. 3447c478bd9Sstevel@tonic-gate */ 3457c478bd9Sstevel@tonic-gate if ((weight == ino_p->ino_intr_weight) || 3467c478bd9Sstevel@tonic-gate ((weight >= weight_max) && 3477c478bd9Sstevel@tonic-gate (ino_p->ino_intr_weight >= weight_max))) { 34809b1eac2SEvan Yan uint32_t orig_cpuid = ino_p->ino_cpuid; 34909b1eac2SEvan Yan 3507c478bd9Sstevel@tonic-gate if (cpu[orig_cpuid] == NULL) 3517c478bd9Sstevel@tonic-gate orig_cpuid = CPU->cpu_id; 3527c478bd9Sstevel@tonic-gate 35309b1eac2SEvan Yan DBG(DBG_IB, dip, "px_ib_intr_redist: sysino 0x%llx " 35409b1eac2SEvan Yan "current cpuid 0x%x current default cpuid 0x%x\n", 35509b1eac2SEvan Yan ino_p->ino_sysino, ino_p->ino_cpuid, 35609b1eac2SEvan Yan ino_p->ino_default_cpuid); 35709b1eac2SEvan Yan 35809b1eac2SEvan Yan /* select target cpuid and mark ino established */ 35909b1eac2SEvan Yan if (ino_p->ino_default_cpuid == -1) 36009b1eac2SEvan Yan ino_p->ino_cpuid = ino_p->ino_default_cpuid = 36109b1eac2SEvan Yan intr_dist_cpuid(); 36209b1eac2SEvan Yan else if ((ino_p->ino_cpuid != 36309b1eac2SEvan Yan ino_p->ino_default_cpuid) && 364c2b9b7a9SGovinda Tatti cpu[ino_p->ino_default_cpuid] && 365c2b9b7a9SGovinda Tatti cpu_intr_on(cpu[ino_p->ino_default_cpuid])) 36609b1eac2SEvan Yan ino_p->ino_cpuid = ino_p->ino_default_cpuid; 36709b1eac2SEvan Yan else if (!cpu_intr_on(cpu[ino_p->ino_cpuid])) 36809b1eac2SEvan Yan ino_p->ino_cpuid = intr_dist_cpuid(); 36909b1eac2SEvan Yan 37009b1eac2SEvan Yan DBG(DBG_IB, dip, "px_ib_intr_redist: sysino 0x%llx " 37109b1eac2SEvan Yan "new cpuid 0x%x new default cpuid 0x%x\n", 37209b1eac2SEvan Yan ino_p->ino_sysino, ino_p->ino_cpuid, 37309b1eac2SEvan Yan ino_p->ino_default_cpuid); 3747c478bd9Sstevel@tonic-gate 3757c478bd9Sstevel@tonic-gate /* Add device weight to targeted cpu. */ 376b0fc0e77Sgovinda for (ipil_p = ino_p->ino_ipil_p; ipil_p; 377b0fc0e77Sgovinda ipil_p = ipil_p->ipil_next_p) { 378b0fc0e77Sgovinda for (i = 0, ih_lst = ipil_p->ipil_ih_head; 379b0fc0e77Sgovinda i < ipil_p->ipil_ih_size; i++, 380b0fc0e77Sgovinda ih_lst = ih_lst->ih_next) { 381b0fc0e77Sgovinda 382b0fc0e77Sgovinda dweight = i_ddi_get_intr_weight( 383b0fc0e77Sgovinda ih_lst->ih_dip); 384b0fc0e77Sgovinda intr_dist_cpuid_add_device_weight( 385b0fc0e77Sgovinda ino_p->ino_cpuid, ih_lst->ih_dip, 386b0fc0e77Sgovinda dweight); 387b0fc0e77Sgovinda 388b0fc0e77Sgovinda /* 389b0fc0e77Sgovinda * Different cpus may have different 390b0fc0e77Sgovinda * clock speeds. to account for this, 391b0fc0e77Sgovinda * whenever an interrupt is moved to a 392b0fc0e77Sgovinda * new CPU, we convert the accumulated 393b0fc0e77Sgovinda * ticks into nsec, based upon the clock 394b0fc0e77Sgovinda * rate of the prior CPU. 395b0fc0e77Sgovinda * 396b0fc0e77Sgovinda * It is possible that the prior CPU no 397b0fc0e77Sgovinda * longer exists. In this case, fall 398b0fc0e77Sgovinda * back to using this CPU's clock rate. 399b0fc0e77Sgovinda * 400b0fc0e77Sgovinda * Note that the value in ih_ticks has 401b0fc0e77Sgovinda * already been corrected for any power 402b0fc0e77Sgovinda * savings mode which might have been 403b0fc0e77Sgovinda * in effect. 404b0fc0e77Sgovinda */ 405b0fc0e77Sgovinda px_ib_cpu_ticks_to_ih_nsec(ib_p, ih_lst, 406b0fc0e77Sgovinda orig_cpuid); 407b0fc0e77Sgovinda } 4087c478bd9Sstevel@tonic-gate } 4097c478bd9Sstevel@tonic-gate 4107c478bd9Sstevel@tonic-gate /* enable interrupt on new targeted cpu */ 4117c478bd9Sstevel@tonic-gate px_ib_intr_dist_en(dip, ino_p->ino_cpuid, 4127c478bd9Sstevel@tonic-gate ino_p->ino_ino, B_TRUE); 4137c478bd9Sstevel@tonic-gate } 4147c478bd9Sstevel@tonic-gate } 4157c478bd9Sstevel@tonic-gate mutex_exit(&ib_p->ib_ino_lst_mutex); 4167c478bd9Sstevel@tonic-gate } 4177c478bd9Sstevel@tonic-gate 4187c478bd9Sstevel@tonic-gate /* 4197c478bd9Sstevel@tonic-gate * Reset interrupts to IDLE. This function is called during 4207c478bd9Sstevel@tonic-gate * panic handling after redistributing interrupts; it's needed to 4217c478bd9Sstevel@tonic-gate * support dumping to network devices after 'sync' from OBP. 4227c478bd9Sstevel@tonic-gate * 4237c478bd9Sstevel@tonic-gate * N.B. This routine runs in a context where all other threads 4247c478bd9Sstevel@tonic-gate * are permanently suspended. 4257c478bd9Sstevel@tonic-gate */ 4267c478bd9Sstevel@tonic-gate static uint_t 4277c478bd9Sstevel@tonic-gate px_ib_intr_reset(void *arg) 4287c478bd9Sstevel@tonic-gate { 4297c478bd9Sstevel@tonic-gate px_ib_t *ib_p = (px_ib_t *)arg; 4307c478bd9Sstevel@tonic-gate 4317c478bd9Sstevel@tonic-gate DBG(DBG_IB, ib_p->ib_px_p->px_dip, "px_ib_intr_reset\n"); 4327c478bd9Sstevel@tonic-gate 4337c478bd9Sstevel@tonic-gate if (px_lib_intr_reset(ib_p->ib_px_p->px_dip) != DDI_SUCCESS) 4347c478bd9Sstevel@tonic-gate return (BF_FATAL); 4357c478bd9Sstevel@tonic-gate 4367c478bd9Sstevel@tonic-gate return (BF_NONE); 4377c478bd9Sstevel@tonic-gate } 4387c478bd9Sstevel@tonic-gate 4397c478bd9Sstevel@tonic-gate /* 440b0fc0e77Sgovinda * Locate px_ino_t structure on ib_p->ib_ino_lst according to ino# 4417c478bd9Sstevel@tonic-gate * returns NULL if not found. 4427c478bd9Sstevel@tonic-gate */ 443b0fc0e77Sgovinda px_ino_t * 4447c478bd9Sstevel@tonic-gate px_ib_locate_ino(px_ib_t *ib_p, devino_t ino_num) 4457c478bd9Sstevel@tonic-gate { 446b0fc0e77Sgovinda px_ino_t *ino_p = ib_p->ib_ino_lst; 4477c478bd9Sstevel@tonic-gate 4487c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&ib_p->ib_ino_lst_mutex)); 4497c478bd9Sstevel@tonic-gate 450f0d69850Srameshc for (; ino_p && ino_p->ino_ino != ino_num; ino_p = ino_p->ino_next_p) 451f0d69850Srameshc ; 4527c478bd9Sstevel@tonic-gate 4537c478bd9Sstevel@tonic-gate return (ino_p); 4547c478bd9Sstevel@tonic-gate } 4557c478bd9Sstevel@tonic-gate 45609b1eac2SEvan Yan px_ino_t * 45709b1eac2SEvan Yan px_ib_alloc_ino(px_ib_t *ib_p, devino_t ino_num) 4587c478bd9Sstevel@tonic-gate { 45909b1eac2SEvan Yan sysino_t sysino; 460b0fc0e77Sgovinda px_ino_t *ino_p; 4617c478bd9Sstevel@tonic-gate 46209b1eac2SEvan Yan if (px_lib_intr_devino_to_sysino(ib_p->ib_px_p->px_dip, 46309b1eac2SEvan Yan ino_num, &sysino) != DDI_SUCCESS) 46409b1eac2SEvan Yan return (NULL); 4657c478bd9Sstevel@tonic-gate 46609b1eac2SEvan Yan ino_p = kmem_zalloc(sizeof (px_ino_t), KM_SLEEP); 4677c478bd9Sstevel@tonic-gate 46809b1eac2SEvan Yan ino_p->ino_next_p = ib_p->ib_ino_lst; 46909b1eac2SEvan Yan ib_p->ib_ino_lst = ino_p; 470b0fc0e77Sgovinda 47109b1eac2SEvan Yan ino_p->ino_ino = ino_num; 47209b1eac2SEvan Yan ino_p->ino_sysino = sysino; 47309b1eac2SEvan Yan ino_p->ino_ib_p = ib_p; 47409b1eac2SEvan Yan ino_p->ino_unclaimed_intrs = 0; 47509b1eac2SEvan Yan ino_p->ino_lopil = 0; 47609b1eac2SEvan Yan ino_p->ino_cpuid = ino_p->ino_default_cpuid = (cpuid_t)-1; 477b0fc0e77Sgovinda 47809b1eac2SEvan Yan return (ino_p); 47909b1eac2SEvan Yan } 48009b1eac2SEvan Yan 48109b1eac2SEvan Yan px_ino_pil_t * 48209b1eac2SEvan Yan px_ib_new_ino_pil(px_ib_t *ib_p, devino_t ino_num, uint_t pil, px_ih_t *ih_p) 48309b1eac2SEvan Yan { 48409b1eac2SEvan Yan px_ino_pil_t *ipil_p = kmem_zalloc(sizeof (px_ino_pil_t), KM_SLEEP); 48509b1eac2SEvan Yan px_ino_t *ino_p; 48609b1eac2SEvan Yan 48709b1eac2SEvan Yan if ((ino_p = px_ib_locate_ino(ib_p, ino_num)) == NULL) 48809b1eac2SEvan Yan ino_p = px_ib_alloc_ino(ib_p, ino_num); 48909b1eac2SEvan Yan 49009b1eac2SEvan Yan ASSERT(ino_p != NULL); 4917c478bd9Sstevel@tonic-gate 4927c478bd9Sstevel@tonic-gate ih_p->ih_next = ih_p; 493b0fc0e77Sgovinda ipil_p->ipil_pil = pil; 494b0fc0e77Sgovinda ipil_p->ipil_ih_head = ih_p; 495b0fc0e77Sgovinda ipil_p->ipil_ih_tail = ih_p; 496b0fc0e77Sgovinda ipil_p->ipil_ih_start = ih_p; 497b0fc0e77Sgovinda ipil_p->ipil_ih_size = 1; 498b0fc0e77Sgovinda ipil_p->ipil_ino_p = ino_p; 4997c478bd9Sstevel@tonic-gate 500b0fc0e77Sgovinda ipil_p->ipil_next_p = ino_p->ino_ipil_p; 501b0fc0e77Sgovinda ino_p->ino_ipil_p = ipil_p; 502b0fc0e77Sgovinda ino_p->ino_ipil_size++; 5037c478bd9Sstevel@tonic-gate 50409b1eac2SEvan Yan if ((ino_p->ino_lopil == 0) || (ino_p->ino_lopil > pil)) 505b0fc0e77Sgovinda ino_p->ino_lopil = pil; 506b0fc0e77Sgovinda 507b0fc0e77Sgovinda return (ipil_p); 5087c478bd9Sstevel@tonic-gate } 5097c478bd9Sstevel@tonic-gate 5107c478bd9Sstevel@tonic-gate void 511b0fc0e77Sgovinda px_ib_delete_ino_pil(px_ib_t *ib_p, px_ino_pil_t *ipil_p) 5127c478bd9Sstevel@tonic-gate { 513b0fc0e77Sgovinda px_ino_t *ino_p = ipil_p->ipil_ino_p; 514b0fc0e77Sgovinda ushort_t pil = ipil_p->ipil_pil; 515b0fc0e77Sgovinda px_ino_pil_t *prev, *next; 5167c478bd9Sstevel@tonic-gate 5177c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&ib_p->ib_ino_lst_mutex)); 5187c478bd9Sstevel@tonic-gate 519b0fc0e77Sgovinda if (ino_p->ino_ipil_p == ipil_p) 520b0fc0e77Sgovinda ino_p->ino_ipil_p = ipil_p->ipil_next_p; 521b0fc0e77Sgovinda else { 522b0fc0e77Sgovinda for (prev = next = ino_p->ino_ipil_p; next != ipil_p; 523f0d69850Srameshc prev = next, next = next->ipil_next_p) 524f0d69850Srameshc ; 525b0fc0e77Sgovinda 526b0fc0e77Sgovinda if (prev) 527b0fc0e77Sgovinda prev->ipil_next_p = ipil_p->ipil_next_p; 528b0fc0e77Sgovinda } 529b0fc0e77Sgovinda 530b0fc0e77Sgovinda kmem_free(ipil_p, sizeof (px_ino_pil_t)); 531b0fc0e77Sgovinda 532e4517573Srameshc if ((--ino_p->ino_ipil_size) && (ino_p->ino_lopil == pil)) { 533e4517573Srameshc for (next = ino_p->ino_ipil_p, pil = next->ipil_pil; 534e4517573Srameshc next; next = next->ipil_next_p) { 535e4517573Srameshc 536b0fc0e77Sgovinda if (pil > next->ipil_pil) 537b0fc0e77Sgovinda pil = next->ipil_pil; 538b0fc0e77Sgovinda } 53909b1eac2SEvan Yan 540e4517573Srameshc /* 541e4517573Srameshc * Value stored in pil should be the lowest pil. 542e4517573Srameshc */ 543b0fc0e77Sgovinda ino_p->ino_lopil = pil; 544b0fc0e77Sgovinda } 545b0fc0e77Sgovinda 546e4517573Srameshc if (ino_p->ino_ipil_size) 547b0fc0e77Sgovinda return; 548b0fc0e77Sgovinda 54909b1eac2SEvan Yan ino_p->ino_lopil = 0; 55009b1eac2SEvan Yan 55109b1eac2SEvan Yan if (ino_p->ino_msiq_p) 55209b1eac2SEvan Yan return; 55309b1eac2SEvan Yan 554b0fc0e77Sgovinda if (ib_p->ib_ino_lst == ino_p) 555b0fc0e77Sgovinda ib_p->ib_ino_lst = ino_p->ino_next_p; 5567c478bd9Sstevel@tonic-gate else { 557b0fc0e77Sgovinda px_ino_t *list = ib_p->ib_ino_lst; 558b0fc0e77Sgovinda 559f0d69850Srameshc for (; list->ino_next_p != ino_p; list = list->ino_next_p) 560f0d69850Srameshc ; 561b0fc0e77Sgovinda list->ino_next_p = ino_p->ino_next_p; 5627c478bd9Sstevel@tonic-gate } 5637c478bd9Sstevel@tonic-gate } 5647c478bd9Sstevel@tonic-gate 5657c478bd9Sstevel@tonic-gate /* 5667c478bd9Sstevel@tonic-gate * Free all ino when we are detaching. 5677c478bd9Sstevel@tonic-gate */ 5687c478bd9Sstevel@tonic-gate void 5697c478bd9Sstevel@tonic-gate px_ib_free_ino_all(px_ib_t *ib_p) 5707c478bd9Sstevel@tonic-gate { 571b0fc0e77Sgovinda px_ino_t *ino_p = ib_p->ib_ino_lst; 572b0fc0e77Sgovinda px_ino_t *next = NULL; 5737c478bd9Sstevel@tonic-gate 574b0fc0e77Sgovinda while (ino_p) { 575b0fc0e77Sgovinda next = ino_p->ino_next_p; 576b0fc0e77Sgovinda kmem_free(ino_p, sizeof (px_ino_t)); 577b0fc0e77Sgovinda ino_p = next; 5787c478bd9Sstevel@tonic-gate } 5797c478bd9Sstevel@tonic-gate } 5807c478bd9Sstevel@tonic-gate 581b0fc0e77Sgovinda /* 582b0fc0e77Sgovinda * Locate px_ino_pil_t structure on ino_p->ino_ipil_p according to ino# 583b0fc0e77Sgovinda * returns NULL if not found. 584b0fc0e77Sgovinda */ 585b0fc0e77Sgovinda px_ino_pil_t * 586b0fc0e77Sgovinda px_ib_ino_locate_ipil(px_ino_t *ino_p, uint_t pil) 587b0fc0e77Sgovinda { 588b0fc0e77Sgovinda px_ino_pil_t *ipil_p = ino_p->ino_ipil_p; 589b0fc0e77Sgovinda 590f0d69850Srameshc for (; ipil_p && ipil_p->ipil_pil != pil; ipil_p = ipil_p->ipil_next_p) 591f0d69850Srameshc ; 592b0fc0e77Sgovinda 593b0fc0e77Sgovinda return (ipil_p); 594b0fc0e77Sgovinda } 595b0fc0e77Sgovinda 5967c478bd9Sstevel@tonic-gate int 597b0fc0e77Sgovinda px_ib_ino_add_intr(px_t *px_p, px_ino_pil_t *ipil_p, px_ih_t *ih_p) 5987c478bd9Sstevel@tonic-gate { 599b0fc0e77Sgovinda px_ino_t *ino_p = ipil_p->ipil_ino_p; 6007c478bd9Sstevel@tonic-gate px_ib_t *ib_p = ino_p->ino_ib_p; 6017c478bd9Sstevel@tonic-gate devino_t ino = ino_p->ino_ino; 6027c478bd9Sstevel@tonic-gate sysino_t sysino = ino_p->ino_sysino; 6037c478bd9Sstevel@tonic-gate dev_info_t *dip = px_p->px_dip; 6047c478bd9Sstevel@tonic-gate cpuid_t curr_cpu; 6057c478bd9Sstevel@tonic-gate hrtime_t start_time; 6067c478bd9Sstevel@tonic-gate intr_state_t intr_state; 6077c478bd9Sstevel@tonic-gate int ret = DDI_SUCCESS; 6087c478bd9Sstevel@tonic-gate 6097c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&ib_p->ib_ino_lst_mutex)); 6107c478bd9Sstevel@tonic-gate ASSERT(ib_p == px_p->px_ib_p); 6117c478bd9Sstevel@tonic-gate 6127c478bd9Sstevel@tonic-gate DBG(DBG_IB, dip, "px_ib_ino_add_intr ino=%x\n", ino_p->ino_ino); 6137c478bd9Sstevel@tonic-gate 6147c478bd9Sstevel@tonic-gate /* Disable the interrupt */ 6157c478bd9Sstevel@tonic-gate if ((ret = px_lib_intr_gettarget(dip, sysino, 6167c478bd9Sstevel@tonic-gate &curr_cpu)) != DDI_SUCCESS) { 6177c478bd9Sstevel@tonic-gate DBG(DBG_IB, dip, 6187c478bd9Sstevel@tonic-gate "px_ib_ino_add_intr px_intr_gettarget() failed\n"); 6197c478bd9Sstevel@tonic-gate 6207c478bd9Sstevel@tonic-gate return (ret); 6217c478bd9Sstevel@tonic-gate } 6227c478bd9Sstevel@tonic-gate 6237c478bd9Sstevel@tonic-gate PX_INTR_DISABLE(dip, sysino); 6247c478bd9Sstevel@tonic-gate 6257c478bd9Sstevel@tonic-gate /* Busy wait on pending interrupt */ 6267c478bd9Sstevel@tonic-gate for (start_time = gethrtime(); !panicstr && 6277c478bd9Sstevel@tonic-gate ((ret = px_lib_intr_getstate(dip, sysino, &intr_state)) 6287c478bd9Sstevel@tonic-gate == DDI_SUCCESS) && (intr_state == INTR_DELIVERED_STATE); /* */) { 6297c478bd9Sstevel@tonic-gate if (gethrtime() - start_time > px_intrpend_timeout) { 6307c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s%d: px_ib_ino_add_intr: pending " 631b40cec45Skrishnae "sysino 0x%lx(ino 0x%x) timeout", 6327c478bd9Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip), 6337c478bd9Sstevel@tonic-gate sysino, ino); 6347c478bd9Sstevel@tonic-gate 6357c478bd9Sstevel@tonic-gate ret = DDI_FAILURE; 6367c478bd9Sstevel@tonic-gate break; 6377c478bd9Sstevel@tonic-gate } 6387c478bd9Sstevel@tonic-gate } 6397c478bd9Sstevel@tonic-gate 6407c478bd9Sstevel@tonic-gate /* 6417c478bd9Sstevel@tonic-gate * If the interrupt was previously blocked (left in pending state) 6427c478bd9Sstevel@tonic-gate * because of jabber we need to clear the pending state in case the 6437c478bd9Sstevel@tonic-gate * jabber has gone away. 6447c478bd9Sstevel@tonic-gate */ 645b0fc0e77Sgovinda if (ino_p->ino_unclaimed_intrs > px_unclaimed_intr_max) { 6467c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, 6477c478bd9Sstevel@tonic-gate "%s%d: px_ib_ino_add_intr: ino 0x%x has been unblocked", 6487c478bd9Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip), ino); 6497c478bd9Sstevel@tonic-gate 650b0fc0e77Sgovinda ino_p->ino_unclaimed_intrs = 0; 651b0fc0e77Sgovinda ret = px_lib_intr_setstate(dip, sysino, INTR_IDLE_STATE); 652b0fc0e77Sgovinda } 653b0fc0e77Sgovinda 654b0fc0e77Sgovinda if (ret != DDI_SUCCESS) { 655b0fc0e77Sgovinda DBG(DBG_IB, dip, "px_ib_ino_add_intr: failed, " 656b0fc0e77Sgovinda "ino 0x%x sysino 0x%x\n", ino, sysino); 6577c478bd9Sstevel@tonic-gate 658b0fc0e77Sgovinda return (ret); 6597c478bd9Sstevel@tonic-gate } 6607c478bd9Sstevel@tonic-gate 661b0fc0e77Sgovinda /* Link up px_ih_t */ 662b0fc0e77Sgovinda ih_p->ih_next = ipil_p->ipil_ih_head; 663b0fc0e77Sgovinda ipil_p->ipil_ih_tail->ih_next = ih_p; 664b0fc0e77Sgovinda ipil_p->ipil_ih_tail = ih_p; 665b0fc0e77Sgovinda 666b0fc0e77Sgovinda ipil_p->ipil_ih_start = ipil_p->ipil_ih_head; 667b0fc0e77Sgovinda ipil_p->ipil_ih_size++; 668b0fc0e77Sgovinda 6697c478bd9Sstevel@tonic-gate /* Re-enable interrupt */ 6707c478bd9Sstevel@tonic-gate PX_INTR_ENABLE(dip, sysino, curr_cpu); 6717c478bd9Sstevel@tonic-gate 6727c478bd9Sstevel@tonic-gate return (ret); 6737c478bd9Sstevel@tonic-gate } 6747c478bd9Sstevel@tonic-gate 6757c478bd9Sstevel@tonic-gate /* 676b0fc0e77Sgovinda * Removes px_ih_t from the ino's link list. 6777c478bd9Sstevel@tonic-gate * uses hardware mutex to lock out interrupt threads. 6787c478bd9Sstevel@tonic-gate * Side effects: interrupt belongs to that ino is turned off on return. 6797c478bd9Sstevel@tonic-gate * if we are sharing PX slot with other inos, the caller needs 6807c478bd9Sstevel@tonic-gate * to turn it back on. 6817c478bd9Sstevel@tonic-gate */ 6827c478bd9Sstevel@tonic-gate int 683b0fc0e77Sgovinda px_ib_ino_rem_intr(px_t *px_p, px_ino_pil_t *ipil_p, px_ih_t *ih_p) 6847c478bd9Sstevel@tonic-gate { 685b0fc0e77Sgovinda px_ino_t *ino_p = ipil_p->ipil_ino_p; 6867c478bd9Sstevel@tonic-gate devino_t ino = ino_p->ino_ino; 6877c478bd9Sstevel@tonic-gate sysino_t sysino = ino_p->ino_sysino; 6887c478bd9Sstevel@tonic-gate dev_info_t *dip = px_p->px_dip; 689b0fc0e77Sgovinda px_ih_t *ih_lst = ipil_p->ipil_ih_head; 6907c478bd9Sstevel@tonic-gate hrtime_t start_time; 6917c478bd9Sstevel@tonic-gate intr_state_t intr_state; 6927c478bd9Sstevel@tonic-gate int i, ret = DDI_SUCCESS; 6937c478bd9Sstevel@tonic-gate 6947c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&ino_p->ino_ib_p->ib_ino_lst_mutex)); 6957c478bd9Sstevel@tonic-gate 6967c478bd9Sstevel@tonic-gate DBG(DBG_IB, px_p->px_dip, "px_ib_ino_rem_intr ino=%x\n", 6977c478bd9Sstevel@tonic-gate ino_p->ino_ino); 6987c478bd9Sstevel@tonic-gate 6997c478bd9Sstevel@tonic-gate /* Disable the interrupt */ 7007c478bd9Sstevel@tonic-gate PX_INTR_DISABLE(px_p->px_dip, sysino); 7017c478bd9Sstevel@tonic-gate 7027c478bd9Sstevel@tonic-gate /* Busy wait on pending interrupt */ 7037c478bd9Sstevel@tonic-gate for (start_time = gethrtime(); !panicstr && 7047c478bd9Sstevel@tonic-gate ((ret = px_lib_intr_getstate(dip, sysino, &intr_state)) 7057c478bd9Sstevel@tonic-gate == DDI_SUCCESS) && (intr_state == INTR_DELIVERED_STATE); /* */) { 7067c478bd9Sstevel@tonic-gate if (gethrtime() - start_time > px_intrpend_timeout) { 7077c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s%d: px_ib_ino_rem_intr: pending " 708b40cec45Skrishnae "sysino 0x%lx(ino 0x%x) timeout", 7097c478bd9Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip), 7107c478bd9Sstevel@tonic-gate sysino, ino); 7117c478bd9Sstevel@tonic-gate 7127c478bd9Sstevel@tonic-gate ret = DDI_FAILURE; 7137c478bd9Sstevel@tonic-gate break; 7147c478bd9Sstevel@tonic-gate } 7157c478bd9Sstevel@tonic-gate } 7167c478bd9Sstevel@tonic-gate 7177c478bd9Sstevel@tonic-gate /* 7187c478bd9Sstevel@tonic-gate * If the interrupt was previously blocked (left in pending state) 7197c478bd9Sstevel@tonic-gate * because of jabber we need to clear the pending state in case the 7207c478bd9Sstevel@tonic-gate * jabber has gone away. 7217c478bd9Sstevel@tonic-gate */ 722*1a92841dSDaniel Ice if (ret == DDI_SUCCESS && 723*1a92841dSDaniel Ice ino_p->ino_unclaimed_intrs > px_unclaimed_intr_max) { 7247c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s%d: px_ib_ino_rem_intr: " 7257c478bd9Sstevel@tonic-gate "ino 0x%x has been unblocked", 7267c478bd9Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip), ino); 7277c478bd9Sstevel@tonic-gate 728b0fc0e77Sgovinda ino_p->ino_unclaimed_intrs = 0; 729b0fc0e77Sgovinda ret = px_lib_intr_setstate(dip, sysino, INTR_IDLE_STATE); 730b0fc0e77Sgovinda } 7317c478bd9Sstevel@tonic-gate 732b0fc0e77Sgovinda if (ret != DDI_SUCCESS) { 733b0fc0e77Sgovinda DBG(DBG_IB, dip, "px_ib_ino_rem_intr: failed, " 734b0fc0e77Sgovinda "ino 0x%x sysino 0x%x\n", ino, sysino); 735b0fc0e77Sgovinda 736b0fc0e77Sgovinda return (ret); 7377c478bd9Sstevel@tonic-gate } 7387c478bd9Sstevel@tonic-gate 739*1a92841dSDaniel Ice if (ipil_p->ipil_ih_size == 1) { 740*1a92841dSDaniel Ice if (ih_lst != ih_p) 741*1a92841dSDaniel Ice goto not_found; 742*1a92841dSDaniel Ice 743*1a92841dSDaniel Ice /* No need to set head/tail as ino_p will be freed */ 744*1a92841dSDaniel Ice goto reset; 745*1a92841dSDaniel Ice } 746*1a92841dSDaniel Ice 7477c478bd9Sstevel@tonic-gate /* Search the link list for ih_p */ 748b0fc0e77Sgovinda for (i = 0; (i < ipil_p->ipil_ih_size) && 749f0d69850Srameshc (ih_lst->ih_next != ih_p); i++, ih_lst = ih_lst->ih_next) 750f0d69850Srameshc ; 7517c478bd9Sstevel@tonic-gate 7527c478bd9Sstevel@tonic-gate if (ih_lst->ih_next != ih_p) 7537c478bd9Sstevel@tonic-gate goto not_found; 7547c478bd9Sstevel@tonic-gate 7557c478bd9Sstevel@tonic-gate /* Remove ih_p from the link list and maintain the head/tail */ 7567c478bd9Sstevel@tonic-gate ih_lst->ih_next = ih_p->ih_next; 7577c478bd9Sstevel@tonic-gate 758b0fc0e77Sgovinda if (ipil_p->ipil_ih_head == ih_p) 759b0fc0e77Sgovinda ipil_p->ipil_ih_head = ih_p->ih_next; 760b0fc0e77Sgovinda if (ipil_p->ipil_ih_tail == ih_p) 761b0fc0e77Sgovinda ipil_p->ipil_ih_tail = ih_lst; 7627c478bd9Sstevel@tonic-gate 763b0fc0e77Sgovinda ipil_p->ipil_ih_start = ipil_p->ipil_ih_head; 7647c478bd9Sstevel@tonic-gate 7657c478bd9Sstevel@tonic-gate reset: 7667c478bd9Sstevel@tonic-gate if (ih_p->ih_config_handle) 7677c478bd9Sstevel@tonic-gate pci_config_teardown(&ih_p->ih_config_handle); 7687c478bd9Sstevel@tonic-gate if (ih_p->ih_ksp != NULL) 7697c478bd9Sstevel@tonic-gate kstat_delete(ih_p->ih_ksp); 7707c478bd9Sstevel@tonic-gate 7717c478bd9Sstevel@tonic-gate kmem_free(ih_p, sizeof (px_ih_t)); 772b0fc0e77Sgovinda ipil_p->ipil_ih_size--; 7737c478bd9Sstevel@tonic-gate 7747c478bd9Sstevel@tonic-gate return (ret); 7757c478bd9Sstevel@tonic-gate 7767c478bd9Sstevel@tonic-gate not_found: 7777c478bd9Sstevel@tonic-gate DBG(DBG_R_INTX, ino_p->ino_ib_p->ib_px_p->px_dip, 778f0d69850Srameshc "ino_p=%x does not have ih_p=%x\n", ino_p, ih_p); 7797c478bd9Sstevel@tonic-gate 7807c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 7817c478bd9Sstevel@tonic-gate } 7827c478bd9Sstevel@tonic-gate 7837c478bd9Sstevel@tonic-gate px_ih_t * 784b0fc0e77Sgovinda px_ib_intr_locate_ih(px_ino_pil_t *ipil_p, dev_info_t *rdip, 7857c478bd9Sstevel@tonic-gate uint32_t inum, msiq_rec_type_t rec_type, msgcode_t msg_code) 7867c478bd9Sstevel@tonic-gate { 787b0fc0e77Sgovinda px_ih_t *ih_p = ipil_p->ipil_ih_head; 7887c478bd9Sstevel@tonic-gate int i; 7897c478bd9Sstevel@tonic-gate 790b0fc0e77Sgovinda for (i = 0; i < ipil_p->ipil_ih_size; i++, ih_p = ih_p->ih_next) { 791b0fc0e77Sgovinda if ((ih_p->ih_dip == rdip) && (ih_p->ih_inum == inum) && 792b0fc0e77Sgovinda (ih_p->ih_rec_type == rec_type) && 793b0fc0e77Sgovinda (ih_p->ih_msg_code == msg_code)) 794b0fc0e77Sgovinda return (ih_p); 7957c478bd9Sstevel@tonic-gate } 7967c478bd9Sstevel@tonic-gate 7977c478bd9Sstevel@tonic-gate return ((px_ih_t *)NULL); 7987c478bd9Sstevel@tonic-gate } 7997c478bd9Sstevel@tonic-gate 8007c478bd9Sstevel@tonic-gate px_ih_t * 8017c478bd9Sstevel@tonic-gate px_ib_alloc_ih(dev_info_t *rdip, uint32_t inum, 8027c478bd9Sstevel@tonic-gate uint_t (*int_handler)(caddr_t int_handler_arg1, caddr_t int_handler_arg2), 8037c478bd9Sstevel@tonic-gate caddr_t int_handler_arg1, caddr_t int_handler_arg2, 8047c478bd9Sstevel@tonic-gate msiq_rec_type_t rec_type, msgcode_t msg_code) 8057c478bd9Sstevel@tonic-gate { 8067c478bd9Sstevel@tonic-gate px_ih_t *ih_p; 8077c478bd9Sstevel@tonic-gate 8087c478bd9Sstevel@tonic-gate ih_p = kmem_alloc(sizeof (px_ih_t), KM_SLEEP); 8097c478bd9Sstevel@tonic-gate ih_p->ih_dip = rdip; 8107c478bd9Sstevel@tonic-gate ih_p->ih_inum = inum; 8117c478bd9Sstevel@tonic-gate ih_p->ih_intr_state = PX_INTR_STATE_DISABLE; 812d17daf0bSScott Carter, SD IOSW ih_p->ih_intr_flags = PX_INTR_IDLE; 8137c478bd9Sstevel@tonic-gate ih_p->ih_handler = int_handler; 8147c478bd9Sstevel@tonic-gate ih_p->ih_handler_arg1 = int_handler_arg1; 8157c478bd9Sstevel@tonic-gate ih_p->ih_handler_arg2 = int_handler_arg2; 8167c478bd9Sstevel@tonic-gate ih_p->ih_config_handle = NULL; 8177c478bd9Sstevel@tonic-gate ih_p->ih_rec_type = rec_type; 8187c478bd9Sstevel@tonic-gate ih_p->ih_msg_code = msg_code; 8197c478bd9Sstevel@tonic-gate ih_p->ih_nsec = 0; 8207c478bd9Sstevel@tonic-gate ih_p->ih_ticks = 0; 8217c478bd9Sstevel@tonic-gate ih_p->ih_ksp = NULL; 8227c478bd9Sstevel@tonic-gate 8237c478bd9Sstevel@tonic-gate return (ih_p); 8247c478bd9Sstevel@tonic-gate } 8257c478bd9Sstevel@tonic-gate 8267c478bd9Sstevel@tonic-gate int 8277c478bd9Sstevel@tonic-gate px_ib_update_intr_state(px_t *px_p, dev_info_t *rdip, 828b0fc0e77Sgovinda uint_t inum, devino_t ino, uint_t pil, 829b0fc0e77Sgovinda uint_t new_intr_state, msiq_rec_type_t rec_type, 830b0fc0e77Sgovinda msgcode_t msg_code) 8317c478bd9Sstevel@tonic-gate { 8327c478bd9Sstevel@tonic-gate px_ib_t *ib_p = px_p->px_ib_p; 833b0fc0e77Sgovinda px_ino_t *ino_p; 834b0fc0e77Sgovinda px_ino_pil_t *ipil_p; 8357c478bd9Sstevel@tonic-gate px_ih_t *ih_p; 8367c478bd9Sstevel@tonic-gate int ret = DDI_FAILURE; 8377c478bd9Sstevel@tonic-gate 838b0fc0e77Sgovinda DBG(DBG_IB, px_p->px_dip, "px_ib_update_intr_state: %s%d " 839b0fc0e77Sgovinda "inum %x devino %x pil %x state %x\n", ddi_driver_name(rdip), 840b0fc0e77Sgovinda ddi_get_instance(rdip), inum, ino, pil, new_intr_state); 8417c478bd9Sstevel@tonic-gate 8427c478bd9Sstevel@tonic-gate mutex_enter(&ib_p->ib_ino_lst_mutex); 8437c478bd9Sstevel@tonic-gate 844b0fc0e77Sgovinda ino_p = px_ib_locate_ino(ib_p, ino); 845b0fc0e77Sgovinda if (ino_p && (ipil_p = px_ib_ino_locate_ipil(ino_p, pil))) { 846b0fc0e77Sgovinda if (ih_p = px_ib_intr_locate_ih(ipil_p, rdip, inum, rec_type, 84736fe4a92Segillett msg_code)) { 8487c478bd9Sstevel@tonic-gate ih_p->ih_intr_state = new_intr_state; 8497c478bd9Sstevel@tonic-gate ret = DDI_SUCCESS; 8507c478bd9Sstevel@tonic-gate } 8517c478bd9Sstevel@tonic-gate } 8527c478bd9Sstevel@tonic-gate 8537c478bd9Sstevel@tonic-gate mutex_exit(&ib_p->ib_ino_lst_mutex); 8547c478bd9Sstevel@tonic-gate return (ret); 8557c478bd9Sstevel@tonic-gate } 85669cd775fSschwartz 85769cd775fSschwartz 85809b1eac2SEvan Yan /* 85909b1eac2SEvan Yan * Get interrupt CPU for a given ino. 86009b1eac2SEvan Yan * Return info only for inos which are already mapped to devices. 86109b1eac2SEvan Yan */ 86209b1eac2SEvan Yan /*ARGSUSED*/ 86309b1eac2SEvan Yan int 86409b1eac2SEvan Yan px_ib_get_intr_target(px_t *px_p, devino_t ino, cpuid_t *cpu_id_p) 86509b1eac2SEvan Yan { 86609b1eac2SEvan Yan dev_info_t *dip = px_p->px_dip; 86709b1eac2SEvan Yan sysino_t sysino; 86809b1eac2SEvan Yan int ret; 86909b1eac2SEvan Yan 87009b1eac2SEvan Yan DBG(DBG_IB, px_p->px_dip, "px_ib_get_intr_target: devino %x\n", ino); 87109b1eac2SEvan Yan 87209b1eac2SEvan Yan /* Convert leaf-wide intr to system-wide intr */ 87309b1eac2SEvan Yan if (px_lib_intr_devino_to_sysino(dip, ino, &sysino) != DDI_SUCCESS) 87409b1eac2SEvan Yan return (DDI_FAILURE); 87509b1eac2SEvan Yan 87609b1eac2SEvan Yan ret = px_lib_intr_gettarget(dip, sysino, cpu_id_p); 87709b1eac2SEvan Yan 87809b1eac2SEvan Yan DBG(DBG_IB, px_p->px_dip, "px_ib_get_intr_target: cpu_id %x\n", 87909b1eac2SEvan Yan *cpu_id_p); 88009b1eac2SEvan Yan 88109b1eac2SEvan Yan return (ret); 88209b1eac2SEvan Yan } 88309b1eac2SEvan Yan 88409b1eac2SEvan Yan 88509b1eac2SEvan Yan /* 88609b1eac2SEvan Yan * Associate a new CPU with a given ino. 88709b1eac2SEvan Yan * Operate only on INOs which are already mapped to devices. 88809b1eac2SEvan Yan */ 88909b1eac2SEvan Yan int 89009b1eac2SEvan Yan px_ib_set_intr_target(px_t *px_p, devino_t ino, cpuid_t cpu_id) 89109b1eac2SEvan Yan { 89209b1eac2SEvan Yan dev_info_t *dip = px_p->px_dip; 89309b1eac2SEvan Yan cpuid_t old_cpu_id; 89409b1eac2SEvan Yan sysino_t sysino; 89509b1eac2SEvan Yan int ret = DDI_SUCCESS; 89609b1eac2SEvan Yan extern const int _ncpu; 89709b1eac2SEvan Yan extern cpu_t *cpu[]; 89809b1eac2SEvan Yan 89909b1eac2SEvan Yan DBG(DBG_IB, px_p->px_dip, "px_ib_set_intr_target: devino %x " 90009b1eac2SEvan Yan "cpu_id %x\n", ino, cpu_id); 90109b1eac2SEvan Yan 90209b1eac2SEvan Yan mutex_enter(&cpu_lock); 90309b1eac2SEvan Yan 90409b1eac2SEvan Yan /* Convert leaf-wide intr to system-wide intr */ 90509b1eac2SEvan Yan if (px_lib_intr_devino_to_sysino(dip, ino, &sysino) != DDI_SUCCESS) { 90609b1eac2SEvan Yan ret = DDI_FAILURE; 90709b1eac2SEvan Yan goto done; 90809b1eac2SEvan Yan } 90909b1eac2SEvan Yan 91009b1eac2SEvan Yan if (px_lib_intr_gettarget(dip, sysino, &old_cpu_id) != DDI_SUCCESS) { 91109b1eac2SEvan Yan ret = DDI_FAILURE; 91209b1eac2SEvan Yan goto done; 91309b1eac2SEvan Yan } 91409b1eac2SEvan Yan 91509b1eac2SEvan Yan /* 91609b1eac2SEvan Yan * Get lock, validate cpu and write it. 91709b1eac2SEvan Yan */ 91809b1eac2SEvan Yan if ((cpu_id < _ncpu) && (cpu[cpu_id] && cpu_is_online(cpu[cpu_id]))) { 91909b1eac2SEvan Yan DBG(DBG_IB, dip, "px_ib_set_intr_target: Enabling CPU %d\n", 92009b1eac2SEvan Yan cpu_id); 92109b1eac2SEvan Yan px_ib_intr_dist_en(dip, cpu_id, ino, B_TRUE); 92209b1eac2SEvan Yan px_ib_log_new_cpu(px_p->px_ib_p, old_cpu_id, cpu_id, ino); 92309b1eac2SEvan Yan } else { /* Invalid cpu */ 92409b1eac2SEvan Yan DBG(DBG_IB, dip, "px_ib_set_intr_target: Invalid cpuid %x\n", 92509b1eac2SEvan Yan cpu_id); 92609b1eac2SEvan Yan ret = DDI_EINVAL; 92709b1eac2SEvan Yan } 92809b1eac2SEvan Yan 92909b1eac2SEvan Yan done: 93009b1eac2SEvan Yan mutex_exit(&cpu_lock); 93109b1eac2SEvan Yan return (ret); 93209b1eac2SEvan Yan } 93309b1eac2SEvan Yan 93409b1eac2SEvan Yan hrtime_t px_ib_msix_retarget_timeout = 120ll * NANOSEC; /* 120 seconds */ 93509b1eac2SEvan Yan 93609b1eac2SEvan Yan /* 93709b1eac2SEvan Yan * Associate a new CPU with a given MSI/X. 93809b1eac2SEvan Yan * Operate only on MSI/Xs which are already mapped to devices. 93909b1eac2SEvan Yan */ 94009b1eac2SEvan Yan int 94109b1eac2SEvan Yan px_ib_set_msix_target(px_t *px_p, ddi_intr_handle_impl_t *hdlp, 94209b1eac2SEvan Yan msinum_t msi_num, cpuid_t cpu_id) 94309b1eac2SEvan Yan { 94409b1eac2SEvan Yan px_ib_t *ib_p = px_p->px_ib_p; 94509b1eac2SEvan Yan px_msi_state_t *msi_state_p = &px_p->px_ib_p->ib_msi_state; 94609b1eac2SEvan Yan dev_info_t *dip = px_p->px_dip; 94709b1eac2SEvan Yan dev_info_t *rdip = hdlp->ih_dip; 94809b1eac2SEvan Yan msiqid_t msiq_id, old_msiq_id; 94909b1eac2SEvan Yan pci_msi_state_t msi_state; 95009b1eac2SEvan Yan msiq_rec_type_t msiq_rec_type; 95109b1eac2SEvan Yan msi_type_t msi_type; 95209b1eac2SEvan Yan px_ino_t *ino_p; 95309b1eac2SEvan Yan px_ih_t *ih_p, *old_ih_p; 95409b1eac2SEvan Yan cpuid_t old_cpu_id; 95509b1eac2SEvan Yan hrtime_t start_time, end_time; 95609b1eac2SEvan Yan int ret = DDI_SUCCESS; 95709b1eac2SEvan Yan extern const int _ncpu; 95809b1eac2SEvan Yan extern cpu_t *cpu[]; 95909b1eac2SEvan Yan 96009b1eac2SEvan Yan DBG(DBG_IB, dip, "px_ib_set_msix_target: msi_num %x new cpu_id %x\n", 96109b1eac2SEvan Yan msi_num, cpu_id); 96209b1eac2SEvan Yan 96309b1eac2SEvan Yan mutex_enter(&cpu_lock); 96409b1eac2SEvan Yan 96509b1eac2SEvan Yan /* Check for MSI64 support */ 96609b1eac2SEvan Yan if ((hdlp->ih_cap & DDI_INTR_FLAG_MSI64) && msi_state_p->msi_addr64) { 96709b1eac2SEvan Yan msiq_rec_type = MSI64_REC; 96809b1eac2SEvan Yan msi_type = MSI64_TYPE; 96909b1eac2SEvan Yan } else { 97009b1eac2SEvan Yan msiq_rec_type = MSI32_REC; 97109b1eac2SEvan Yan msi_type = MSI32_TYPE; 97209b1eac2SEvan Yan } 97309b1eac2SEvan Yan 97409b1eac2SEvan Yan if ((ret = px_lib_msi_getmsiq(dip, msi_num, 97509b1eac2SEvan Yan &old_msiq_id)) != DDI_SUCCESS) { 97609b1eac2SEvan Yan 97709b1eac2SEvan Yan mutex_exit(&cpu_lock); 97809b1eac2SEvan Yan return (ret); 97909b1eac2SEvan Yan } 98009b1eac2SEvan Yan 98109b1eac2SEvan Yan DBG(DBG_IB, dip, "px_ib_set_msix_target: current msiq 0x%x\n", 98209b1eac2SEvan Yan old_msiq_id); 98309b1eac2SEvan Yan 98409b1eac2SEvan Yan if ((ret = px_ib_get_intr_target(px_p, 98509b1eac2SEvan Yan px_msiqid_to_devino(px_p, old_msiq_id), 98609b1eac2SEvan Yan &old_cpu_id)) != DDI_SUCCESS) { 98709b1eac2SEvan Yan 98809b1eac2SEvan Yan mutex_exit(&cpu_lock); 98909b1eac2SEvan Yan return (ret); 99009b1eac2SEvan Yan } 99109b1eac2SEvan Yan 99209b1eac2SEvan Yan DBG(DBG_IB, dip, "px_ib_set_msix_target: current cpuid 0x%x\n", 99309b1eac2SEvan Yan old_cpu_id); 99409b1eac2SEvan Yan 99509b1eac2SEvan Yan if (cpu_id == old_cpu_id) { 99609b1eac2SEvan Yan 99709b1eac2SEvan Yan mutex_exit(&cpu_lock); 99809b1eac2SEvan Yan return (DDI_SUCCESS); 99909b1eac2SEvan Yan } 100009b1eac2SEvan Yan 100109b1eac2SEvan Yan /* 100209b1eac2SEvan Yan * Get lock, validate cpu and write it. 100309b1eac2SEvan Yan */ 100409b1eac2SEvan Yan if (!((cpu_id < _ncpu) && (cpu[cpu_id] && 100509b1eac2SEvan Yan cpu_is_online(cpu[cpu_id])))) { 100609b1eac2SEvan Yan /* Invalid cpu */ 100709b1eac2SEvan Yan DBG(DBG_IB, dip, "px_ib_set_msix_target: Invalid cpuid %x\n", 100809b1eac2SEvan Yan cpu_id); 100909b1eac2SEvan Yan 101009b1eac2SEvan Yan mutex_exit(&cpu_lock); 101109b1eac2SEvan Yan return (DDI_EINVAL); 101209b1eac2SEvan Yan } 101309b1eac2SEvan Yan 101409b1eac2SEvan Yan DBG(DBG_IB, dip, "px_ib_set_msix_target: Enabling CPU %d\n", cpu_id); 101509b1eac2SEvan Yan 101609b1eac2SEvan Yan if ((ret = px_add_msiq_intr(dip, rdip, hdlp, 101709b1eac2SEvan Yan msiq_rec_type, msi_num, cpu_id, &msiq_id)) != DDI_SUCCESS) { 101809b1eac2SEvan Yan DBG(DBG_IB, dip, "px_ib_set_msix_target: Add MSI handler " 101909b1eac2SEvan Yan "failed, rdip 0x%p msi 0x%x\n", rdip, msi_num); 102009b1eac2SEvan Yan 102109b1eac2SEvan Yan mutex_exit(&cpu_lock); 102209b1eac2SEvan Yan return (ret); 102309b1eac2SEvan Yan } 102409b1eac2SEvan Yan 102509b1eac2SEvan Yan if ((ret = px_lib_msi_setmsiq(dip, msi_num, 102609b1eac2SEvan Yan msiq_id, msi_type)) != DDI_SUCCESS) { 1027d17daf0bSScott Carter, SD IOSW mutex_exit(&cpu_lock); 1028d17daf0bSScott Carter, SD IOSW 102909b1eac2SEvan Yan (void) px_rem_msiq_intr(dip, rdip, 103009b1eac2SEvan Yan hdlp, msiq_rec_type, msi_num, msiq_id); 103109b1eac2SEvan Yan 103209b1eac2SEvan Yan return (ret); 103309b1eac2SEvan Yan } 103409b1eac2SEvan Yan 103509b1eac2SEvan Yan if ((ret = px_ib_update_intr_state(px_p, rdip, hdlp->ih_inum, 103609b1eac2SEvan Yan px_msiqid_to_devino(px_p, msiq_id), hdlp->ih_pri, 103709b1eac2SEvan Yan PX_INTR_STATE_ENABLE, msiq_rec_type, msi_num)) != DDI_SUCCESS) { 1038d17daf0bSScott Carter, SD IOSW mutex_exit(&cpu_lock); 1039d17daf0bSScott Carter, SD IOSW 104009b1eac2SEvan Yan (void) px_rem_msiq_intr(dip, rdip, 104109b1eac2SEvan Yan hdlp, msiq_rec_type, msi_num, msiq_id); 104209b1eac2SEvan Yan 104309b1eac2SEvan Yan return (ret); 104409b1eac2SEvan Yan } 104509b1eac2SEvan Yan 104609b1eac2SEvan Yan mutex_exit(&cpu_lock); 1047d17daf0bSScott Carter, SD IOSW 1048d17daf0bSScott Carter, SD IOSW /* 1049d17daf0bSScott Carter, SD IOSW * Remove the old handler, but first ensure it is finished. 1050d17daf0bSScott Carter, SD IOSW * 1051d17daf0bSScott Carter, SD IOSW * Each handler sets its PENDING flag before it clears the MSI state. 1052d17daf0bSScott Carter, SD IOSW * Then it clears that flag when finished. If a re-target occurs while 1053d17daf0bSScott Carter, SD IOSW * the MSI state is DELIVERED, then it is not yet known which of the 1054d17daf0bSScott Carter, SD IOSW * two handlers will take the interrupt. So the re-target operation 1055d17daf0bSScott Carter, SD IOSW * sets a RETARGET flag on both handlers in that case. Monitoring both 1056d17daf0bSScott Carter, SD IOSW * flags on both handlers then determines when the old handler can be 1057d17daf0bSScott Carter, SD IOSW * be safely removed. 1058d17daf0bSScott Carter, SD IOSW */ 105909b1eac2SEvan Yan mutex_enter(&ib_p->ib_ino_lst_mutex); 106009b1eac2SEvan Yan 106109b1eac2SEvan Yan ino_p = px_ib_locate_ino(ib_p, px_msiqid_to_devino(px_p, old_msiq_id)); 106209b1eac2SEvan Yan old_ih_p = px_ib_intr_locate_ih(px_ib_ino_locate_ipil(ino_p, 106309b1eac2SEvan Yan hdlp->ih_pri), rdip, hdlp->ih_inum, msiq_rec_type, msi_num); 106409b1eac2SEvan Yan 106509b1eac2SEvan Yan ino_p = px_ib_locate_ino(ib_p, px_msiqid_to_devino(px_p, msiq_id)); 106609b1eac2SEvan Yan ih_p = px_ib_intr_locate_ih(px_ib_ino_locate_ipil(ino_p, hdlp->ih_pri), 106709b1eac2SEvan Yan rdip, hdlp->ih_inum, msiq_rec_type, msi_num); 106809b1eac2SEvan Yan 106909b1eac2SEvan Yan if ((ret = px_lib_msi_getstate(dip, msi_num, 107009b1eac2SEvan Yan &msi_state)) != DDI_SUCCESS) { 107109b1eac2SEvan Yan (void) px_rem_msiq_intr(dip, rdip, 107209b1eac2SEvan Yan hdlp, msiq_rec_type, msi_num, msiq_id); 107309b1eac2SEvan Yan 107409b1eac2SEvan Yan mutex_exit(&ib_p->ib_ino_lst_mutex); 107509b1eac2SEvan Yan return (ret); 107609b1eac2SEvan Yan } 107709b1eac2SEvan Yan 1078d17daf0bSScott Carter, SD IOSW if (msi_state == PCI_MSI_STATE_DELIVERED) { 1079d17daf0bSScott Carter, SD IOSW ih_p->ih_intr_flags |= PX_INTR_RETARGET; 1080d17daf0bSScott Carter, SD IOSW old_ih_p->ih_intr_flags |= PX_INTR_RETARGET; 1081d17daf0bSScott Carter, SD IOSW } 108209b1eac2SEvan Yan 108309b1eac2SEvan Yan start_time = gethrtime(); 1084d17daf0bSScott Carter, SD IOSW while (((ih_p->ih_intr_flags & PX_INTR_RETARGET) && 1085d17daf0bSScott Carter, SD IOSW (old_ih_p->ih_intr_flags & PX_INTR_RETARGET)) || 1086d17daf0bSScott Carter, SD IOSW (old_ih_p->ih_intr_flags & PX_INTR_PENDING)) { 1087d17daf0bSScott Carter, SD IOSW 1088d17daf0bSScott Carter, SD IOSW /* Wait for one second */ 1089d17daf0bSScott Carter, SD IOSW delay(drv_usectohz(1000000)); 1090d17daf0bSScott Carter, SD IOSW 1091d17daf0bSScott Carter, SD IOSW end_time = gethrtime() - start_time; 1092d17daf0bSScott Carter, SD IOSW if (end_time > px_ib_msix_retarget_timeout) { 109309b1eac2SEvan Yan cmn_err(CE_WARN, "MSIX retarget %x is not completed, " 109409b1eac2SEvan Yan "even after waiting %llx ticks\n", 109509b1eac2SEvan Yan msi_num, end_time); 109609b1eac2SEvan Yan break; 109709b1eac2SEvan Yan } 109809b1eac2SEvan Yan } 109909b1eac2SEvan Yan 1100d17daf0bSScott Carter, SD IOSW ih_p->ih_intr_flags &= ~(PX_INTR_RETARGET); 1101d17daf0bSScott Carter, SD IOSW 110209b1eac2SEvan Yan mutex_exit(&ib_p->ib_ino_lst_mutex); 110309b1eac2SEvan Yan 110409b1eac2SEvan Yan ret = px_rem_msiq_intr(dip, rdip, 110509b1eac2SEvan Yan hdlp, msiq_rec_type, msi_num, old_msiq_id); 110609b1eac2SEvan Yan 110709b1eac2SEvan Yan return (ret); 110809b1eac2SEvan Yan } 110909b1eac2SEvan Yan 111009b1eac2SEvan Yan 111169cd775fSschwartz static void 111269cd775fSschwartz px_fill_in_intr_devs(pcitool_intr_dev_t *dev, char *driver_name, 111369cd775fSschwartz char *path_name, int instance) 111469cd775fSschwartz { 111569cd775fSschwartz (void) strncpy(dev->driver_name, driver_name, MAXMODCONFNAME-1); 111669cd775fSschwartz dev->driver_name[MAXMODCONFNAME] = '\0'; 111769cd775fSschwartz (void) strncpy(dev->path, path_name, MAXPATHLEN-1); 111869cd775fSschwartz dev->dev_inst = instance; 111969cd775fSschwartz } 112069cd775fSschwartz 112169cd775fSschwartz 112269cd775fSschwartz /* 112369cd775fSschwartz * Return the dips or number of dips associated with a given interrupt block. 112469cd775fSschwartz * Size of dips array arg is passed in as dips_ret arg. 112569cd775fSschwartz * Number of dips returned is returned in dips_ret arg. 112669cd775fSschwartz * Array of dips gets returned in the dips argument. 112769cd775fSschwartz * Function returns number of dips existing for the given interrupt block. 112869cd775fSschwartz * 112969cd775fSschwartz * Note: this function assumes an enabled/valid INO, which is why it returns 113069cd775fSschwartz * the px node and (Internal) when it finds no other devices (and *devs_ret > 0) 113169cd775fSschwartz */ 113269cd775fSschwartz uint8_t 113309b1eac2SEvan Yan pxtool_ib_get_ino_devs(px_t *px_p, uint32_t ino, uint32_t msi_num, 113409b1eac2SEvan Yan uint8_t *devs_ret, pcitool_intr_dev_t *devs) 113569cd775fSschwartz { 1136b0fc0e77Sgovinda px_ib_t *ib_p = px_p->px_ib_p; 1137b0fc0e77Sgovinda px_ino_t *ino_p; 1138b0fc0e77Sgovinda px_ino_pil_t *ipil_p; 1139b0fc0e77Sgovinda px_ih_t *ih_p; 1140b0fc0e77Sgovinda uint32_t num_devs = 0; 1141b0fc0e77Sgovinda char pathname[MAXPATHLEN]; 1142b0fc0e77Sgovinda int i, j; 114369cd775fSschwartz 114469cd775fSschwartz mutex_enter(&ib_p->ib_ino_lst_mutex); 114569cd775fSschwartz ino_p = px_ib_locate_ino(ib_p, ino); 114669cd775fSschwartz if (ino_p != NULL) { 1147b0fc0e77Sgovinda for (j = 0, ipil_p = ino_p->ino_ipil_p; ipil_p; 1148b0fc0e77Sgovinda ipil_p = ipil_p->ipil_next_p) { 1149b0fc0e77Sgovinda num_devs += ipil_p->ipil_ih_size; 1150b0fc0e77Sgovinda 1151b0fc0e77Sgovinda for (i = 0, ih_p = ipil_p->ipil_ih_head; 1152b0fc0e77Sgovinda ((i < ipil_p->ipil_ih_size) && (i < *devs_ret)); 1153b0fc0e77Sgovinda i++, j++, ih_p = ih_p->ih_next) { 1154b0fc0e77Sgovinda (void) ddi_pathname(ih_p->ih_dip, pathname); 115509b1eac2SEvan Yan 115609b1eac2SEvan Yan if (ih_p->ih_msg_code == msi_num) { 115709b1eac2SEvan Yan num_devs = *devs_ret = 1; 115809b1eac2SEvan Yan px_fill_in_intr_devs(&devs[0], 115909b1eac2SEvan Yan (char *)ddi_driver_name( 116009b1eac2SEvan Yan ih_p->ih_dip), pathname, 116109b1eac2SEvan Yan ddi_get_instance(ih_p->ih_dip)); 116209b1eac2SEvan Yan goto done; 116309b1eac2SEvan Yan } 116409b1eac2SEvan Yan 116509b1eac2SEvan Yan px_fill_in_intr_devs(&devs[j], 1166b0fc0e77Sgovinda (char *)ddi_driver_name(ih_p->ih_dip), 1167b0fc0e77Sgovinda pathname, ddi_get_instance(ih_p->ih_dip)); 1168b0fc0e77Sgovinda } 116969cd775fSschwartz } 117069cd775fSschwartz 1171b0fc0e77Sgovinda *devs_ret = j; 117269cd775fSschwartz } else if (*devs_ret > 0) { 117369cd775fSschwartz (void) ddi_pathname(px_p->px_dip, pathname); 117469cd775fSschwartz strcat(pathname, " (Internal)"); 117569cd775fSschwartz px_fill_in_intr_devs(&devs[0], 117669cd775fSschwartz (char *)ddi_driver_name(px_p->px_dip), pathname, 117769cd775fSschwartz ddi_get_instance(px_p->px_dip)); 117869cd775fSschwartz num_devs = *devs_ret = 1; 117969cd775fSschwartz } 118069cd775fSschwartz 118109b1eac2SEvan Yan done: 118269cd775fSschwartz mutex_exit(&ib_p->ib_ino_lst_mutex); 118369cd775fSschwartz 118469cd775fSschwartz return (num_devs); 118569cd775fSschwartz } 118669cd775fSschwartz 118769cd775fSschwartz 118809b1eac2SEvan Yan int 118909b1eac2SEvan Yan pxtool_ib_get_msi_info(px_t *px_p, devino_t ino, msinum_t msi_num, 119009b1eac2SEvan Yan ddi_intr_handle_impl_t *hdlp) 119109b1eac2SEvan Yan { 119209b1eac2SEvan Yan px_ib_t *ib_p = px_p->px_ib_p; 119309b1eac2SEvan Yan px_ino_t *ino_p; 119409b1eac2SEvan Yan px_ino_pil_t *ipil_p; 119509b1eac2SEvan Yan px_ih_t *ih_p; 119609b1eac2SEvan Yan int i; 119709b1eac2SEvan Yan 119809b1eac2SEvan Yan mutex_enter(&ib_p->ib_ino_lst_mutex); 119909b1eac2SEvan Yan 120009b1eac2SEvan Yan if ((ino_p = px_ib_locate_ino(ib_p, ino)) == NULL) { 120109b1eac2SEvan Yan mutex_exit(&ib_p->ib_ino_lst_mutex); 120209b1eac2SEvan Yan return (DDI_FAILURE); 120309b1eac2SEvan Yan } 120409b1eac2SEvan Yan 120509b1eac2SEvan Yan for (ipil_p = ino_p->ino_ipil_p; ipil_p; 120609b1eac2SEvan Yan ipil_p = ipil_p->ipil_next_p) { 120709b1eac2SEvan Yan for (i = 0, ih_p = ipil_p->ipil_ih_head; 120809b1eac2SEvan Yan ((i < ipil_p->ipil_ih_size) && ih_p); 120909b1eac2SEvan Yan i++, ih_p = ih_p->ih_next) { 121009b1eac2SEvan Yan 121109b1eac2SEvan Yan if (ih_p->ih_msg_code != msi_num) 121209b1eac2SEvan Yan continue; 121309b1eac2SEvan Yan 121409b1eac2SEvan Yan hdlp->ih_dip = ih_p->ih_dip; 121509b1eac2SEvan Yan hdlp->ih_inum = ih_p->ih_inum; 121609b1eac2SEvan Yan hdlp->ih_cb_func = ih_p->ih_handler; 121709b1eac2SEvan Yan hdlp->ih_cb_arg1 = ih_p->ih_handler_arg1; 121809b1eac2SEvan Yan hdlp->ih_cb_arg2 = ih_p->ih_handler_arg2; 121909b1eac2SEvan Yan if (ih_p->ih_rec_type == MSI64_REC) 122009b1eac2SEvan Yan hdlp->ih_cap = DDI_INTR_FLAG_MSI64; 122109b1eac2SEvan Yan hdlp->ih_pri = ipil_p->ipil_pil; 122209b1eac2SEvan Yan hdlp->ih_ver = DDI_INTR_VERSION; 122309b1eac2SEvan Yan 122409b1eac2SEvan Yan mutex_exit(&ib_p->ib_ino_lst_mutex); 122509b1eac2SEvan Yan return (DDI_SUCCESS); 122609b1eac2SEvan Yan } 122709b1eac2SEvan Yan } 122809b1eac2SEvan Yan 122909b1eac2SEvan Yan mutex_exit(&ib_p->ib_ino_lst_mutex); 123009b1eac2SEvan Yan return (DDI_FAILURE); 123109b1eac2SEvan Yan } 123209b1eac2SEvan Yan 123344bb982bSgovinda void 123409b1eac2SEvan Yan px_ib_log_new_cpu(px_ib_t *ib_p, cpuid_t old_cpu_id, cpuid_t new_cpu_id, 123569cd775fSschwartz uint32_t ino) 123669cd775fSschwartz { 1237b0fc0e77Sgovinda px_ino_t *ino_p; 1238b0fc0e77Sgovinda px_ino_pil_t *ipil_p; 1239b0fc0e77Sgovinda px_ih_t *ih_p; 1240b0fc0e77Sgovinda int i; 124169cd775fSschwartz 124269cd775fSschwartz mutex_enter(&ib_p->ib_ino_lst_mutex); 124369cd775fSschwartz 124469cd775fSschwartz /* Log in OS data structures the new CPU. */ 1245b0fc0e77Sgovinda if (ino_p = px_ib_locate_ino(ib_p, ino)) { 124669cd775fSschwartz 124769cd775fSschwartz /* Log in OS data structures the new CPU. */ 124869cd775fSschwartz ino_p->ino_cpuid = new_cpu_id; 124969cd775fSschwartz 1250b0fc0e77Sgovinda for (ipil_p = ino_p->ino_ipil_p; ipil_p; 1251b0fc0e77Sgovinda ipil_p = ipil_p->ipil_next_p) { 1252b0fc0e77Sgovinda for (i = 0, ih_p = ipil_p->ipil_ih_head; 1253b0fc0e77Sgovinda (i < ipil_p->ipil_ih_size); 1254b0fc0e77Sgovinda i++, ih_p = ih_p->ih_next) { 1255b0fc0e77Sgovinda /* 1256b0fc0e77Sgovinda * Account for any residual time 1257b0fc0e77Sgovinda * to be logged for old cpu. 1258b0fc0e77Sgovinda */ 1259b0fc0e77Sgovinda px_ib_cpu_ticks_to_ih_nsec(ib_p, 1260b0fc0e77Sgovinda ih_p, old_cpu_id); 1261b0fc0e77Sgovinda } 1262b0fc0e77Sgovinda } 126369cd775fSschwartz } 126469cd775fSschwartz 126569cd775fSschwartz mutex_exit(&ib_p->ib_ino_lst_mutex); 126669cd775fSschwartz } 1267