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 /* 22c17ca212SDavid Major * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 237c478bd9Sstevel@tonic-gate */ 247c478bd9Sstevel@tonic-gate 257c478bd9Sstevel@tonic-gate /* 267c478bd9Sstevel@tonic-gate * PX Interrupt Block implementation 277c478bd9Sstevel@tonic-gate */ 287c478bd9Sstevel@tonic-gate 297c478bd9Sstevel@tonic-gate #include <sys/types.h> 307c478bd9Sstevel@tonic-gate #include <sys/kmem.h> 317c478bd9Sstevel@tonic-gate #include <sys/async.h> 327c478bd9Sstevel@tonic-gate #include <sys/systm.h> /* panicstr */ 337c478bd9Sstevel@tonic-gate #include <sys/spl.h> 347c478bd9Sstevel@tonic-gate #include <sys/sunddi.h> 357c478bd9Sstevel@tonic-gate #include <sys/machsystm.h> /* intr_dist_add */ 367c478bd9Sstevel@tonic-gate #include <sys/ddi_impldefs.h> 377c478bd9Sstevel@tonic-gate #include <sys/cpuvar.h> 3825cf1a30Sjl139090 #include <sys/time.h> 397c478bd9Sstevel@tonic-gate #include "px_obj.h" 407c478bd9Sstevel@tonic-gate 417c478bd9Sstevel@tonic-gate /*LINTLIBRARY*/ 427c478bd9Sstevel@tonic-gate 437c478bd9Sstevel@tonic-gate static void px_ib_intr_redist(void *arg, int32_t weight_max, int32_t weight); 4469cd775fSschwartz static void px_ib_cpu_ticks_to_ih_nsec(px_ib_t *ib_p, px_ih_t *ih_p, 4569cd775fSschwartz uint32_t cpu_id); 467c478bd9Sstevel@tonic-gate static uint_t px_ib_intr_reset(void *arg); 4769cd775fSschwartz static void px_fill_in_intr_devs(pcitool_intr_dev_t *dev, char *driver_name, 4869cd775fSschwartz char *path_name, int instance); 497c478bd9Sstevel@tonic-gate 5025cf1a30Sjl139090 extern uint64_t xc_tick_jump_limit; 5125cf1a30Sjl139090 527c478bd9Sstevel@tonic-gate int 537c478bd9Sstevel@tonic-gate px_ib_attach(px_t *px_p) 547c478bd9Sstevel@tonic-gate { 557c478bd9Sstevel@tonic-gate dev_info_t *dip = px_p->px_dip; 567c478bd9Sstevel@tonic-gate px_ib_t *ib_p; 577c478bd9Sstevel@tonic-gate sysino_t sysino; 587c478bd9Sstevel@tonic-gate px_fault_t *fault_p = &px_p->px_fault; 597c478bd9Sstevel@tonic-gate 607c478bd9Sstevel@tonic-gate DBG(DBG_IB, dip, "px_ib_attach\n"); 617c478bd9Sstevel@tonic-gate 627c478bd9Sstevel@tonic-gate if (px_lib_intr_devino_to_sysino(px_p->px_dip, 63f8d2de6bSjchu px_p->px_inos[PX_INTR_PEC], &sysino) != DDI_SUCCESS) 647c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 657c478bd9Sstevel@tonic-gate 667c478bd9Sstevel@tonic-gate /* 677c478bd9Sstevel@tonic-gate * Allocate interrupt block state structure and link it to 687c478bd9Sstevel@tonic-gate * the px state structure. 697c478bd9Sstevel@tonic-gate */ 707c478bd9Sstevel@tonic-gate ib_p = kmem_zalloc(sizeof (px_ib_t), KM_SLEEP); 717c478bd9Sstevel@tonic-gate px_p->px_ib_p = ib_p; 727c478bd9Sstevel@tonic-gate ib_p->ib_px_p = px_p; 73b0fc0e77Sgovinda ib_p->ib_ino_lst = (px_ino_t *)NULL; 747c478bd9Sstevel@tonic-gate 757c478bd9Sstevel@tonic-gate mutex_init(&ib_p->ib_intr_lock, NULL, MUTEX_DRIVER, NULL); 767c478bd9Sstevel@tonic-gate mutex_init(&ib_p->ib_ino_lst_mutex, NULL, MUTEX_DRIVER, NULL); 777c478bd9Sstevel@tonic-gate 787c478bd9Sstevel@tonic-gate bus_func_register(BF_TYPE_RESINTR, px_ib_intr_reset, ib_p); 797c478bd9Sstevel@tonic-gate 807c478bd9Sstevel@tonic-gate intr_dist_add_weighted(px_ib_intr_redist, ib_p); 817c478bd9Sstevel@tonic-gate 827c478bd9Sstevel@tonic-gate /* 837c478bd9Sstevel@tonic-gate * Initialize PEC fault data structure 847c478bd9Sstevel@tonic-gate */ 857c478bd9Sstevel@tonic-gate fault_p->px_fh_dip = dip; 867c478bd9Sstevel@tonic-gate fault_p->px_fh_sysino = sysino; 87f8d2de6bSjchu fault_p->px_err_func = px_err_dmc_pec_intr; 88f8d2de6bSjchu fault_p->px_intr_ino = px_p->px_inos[PX_INTR_PEC]; 897c478bd9Sstevel@tonic-gate 907c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 917c478bd9Sstevel@tonic-gate } 927c478bd9Sstevel@tonic-gate 937c478bd9Sstevel@tonic-gate void 947c478bd9Sstevel@tonic-gate px_ib_detach(px_t *px_p) 957c478bd9Sstevel@tonic-gate { 967c478bd9Sstevel@tonic-gate px_ib_t *ib_p = px_p->px_ib_p; 977c478bd9Sstevel@tonic-gate dev_info_t *dip = px_p->px_dip; 987c478bd9Sstevel@tonic-gate 997c478bd9Sstevel@tonic-gate DBG(DBG_IB, dip, "px_ib_detach\n"); 1007c478bd9Sstevel@tonic-gate 1017c478bd9Sstevel@tonic-gate bus_func_unregister(BF_TYPE_RESINTR, px_ib_intr_reset, ib_p); 1027c478bd9Sstevel@tonic-gate intr_dist_rem_weighted(px_ib_intr_redist, ib_p); 1037c478bd9Sstevel@tonic-gate 1047c478bd9Sstevel@tonic-gate mutex_destroy(&ib_p->ib_ino_lst_mutex); 1057c478bd9Sstevel@tonic-gate mutex_destroy(&ib_p->ib_intr_lock); 1067c478bd9Sstevel@tonic-gate 1077c478bd9Sstevel@tonic-gate px_ib_free_ino_all(ib_p); 1087c478bd9Sstevel@tonic-gate 1097c478bd9Sstevel@tonic-gate px_p->px_ib_p = NULL; 1107c478bd9Sstevel@tonic-gate kmem_free(ib_p, sizeof (px_ib_t)); 1117c478bd9Sstevel@tonic-gate } 1127c478bd9Sstevel@tonic-gate 1137c478bd9Sstevel@tonic-gate void 1147c478bd9Sstevel@tonic-gate px_ib_intr_enable(px_t *px_p, cpuid_t cpu_id, devino_t ino) 1157c478bd9Sstevel@tonic-gate { 1167c478bd9Sstevel@tonic-gate px_ib_t *ib_p = px_p->px_ib_p; 1177c478bd9Sstevel@tonic-gate sysino_t sysino; 1187c478bd9Sstevel@tonic-gate 1197c478bd9Sstevel@tonic-gate /* 1207c478bd9Sstevel@tonic-gate * Determine the cpu for the interrupt 1217c478bd9Sstevel@tonic-gate */ 1227c478bd9Sstevel@tonic-gate mutex_enter(&ib_p->ib_intr_lock); 1237c478bd9Sstevel@tonic-gate 1247c478bd9Sstevel@tonic-gate DBG(DBG_IB, px_p->px_dip, 1257c478bd9Sstevel@tonic-gate "px_ib_intr_enable: ino=%x cpu_id=%x\n", ino, cpu_id); 1267c478bd9Sstevel@tonic-gate 1277c478bd9Sstevel@tonic-gate if (px_lib_intr_devino_to_sysino(px_p->px_dip, ino, 1287c478bd9Sstevel@tonic-gate &sysino) != DDI_SUCCESS) { 1297c478bd9Sstevel@tonic-gate DBG(DBG_IB, px_p->px_dip, 1307c478bd9Sstevel@tonic-gate "px_ib_intr_enable: px_intr_devino_to_sysino() failed\n"); 1317c478bd9Sstevel@tonic-gate 1327c478bd9Sstevel@tonic-gate mutex_exit(&ib_p->ib_intr_lock); 1337c478bd9Sstevel@tonic-gate return; 1347c478bd9Sstevel@tonic-gate } 1357c478bd9Sstevel@tonic-gate 1367c478bd9Sstevel@tonic-gate PX_INTR_ENABLE(px_p->px_dip, sysino, cpu_id); 137a195726fSgovinda px_lib_intr_setstate(px_p->px_dip, sysino, INTR_IDLE_STATE); 1387c478bd9Sstevel@tonic-gate 1397c478bd9Sstevel@tonic-gate mutex_exit(&ib_p->ib_intr_lock); 1407c478bd9Sstevel@tonic-gate } 1417c478bd9Sstevel@tonic-gate 1427c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 1437c478bd9Sstevel@tonic-gate void 1447c478bd9Sstevel@tonic-gate px_ib_intr_disable(px_ib_t *ib_p, devino_t ino, int wait) 1457c478bd9Sstevel@tonic-gate { 1467c478bd9Sstevel@tonic-gate sysino_t sysino; 1477c478bd9Sstevel@tonic-gate 1487c478bd9Sstevel@tonic-gate mutex_enter(&ib_p->ib_intr_lock); 1497c478bd9Sstevel@tonic-gate 1507c478bd9Sstevel@tonic-gate DBG(DBG_IB, ib_p->ib_px_p->px_dip, "px_ib_intr_disable: ino=%x\n", ino); 1517c478bd9Sstevel@tonic-gate 1527c478bd9Sstevel@tonic-gate /* Disable the interrupt */ 1537c478bd9Sstevel@tonic-gate if (px_lib_intr_devino_to_sysino(ib_p->ib_px_p->px_dip, ino, 1547c478bd9Sstevel@tonic-gate &sysino) != DDI_SUCCESS) { 1557c478bd9Sstevel@tonic-gate DBG(DBG_IB, ib_p->ib_px_p->px_dip, 1567c478bd9Sstevel@tonic-gate "px_ib_intr_disable: px_intr_devino_to_sysino() failed\n"); 1577c478bd9Sstevel@tonic-gate 1587c478bd9Sstevel@tonic-gate mutex_exit(&ib_p->ib_intr_lock); 1597c478bd9Sstevel@tonic-gate return; 1607c478bd9Sstevel@tonic-gate } 1617c478bd9Sstevel@tonic-gate 1627c478bd9Sstevel@tonic-gate PX_INTR_DISABLE(ib_p->ib_px_p->px_dip, sysino); 1637c478bd9Sstevel@tonic-gate 1647c478bd9Sstevel@tonic-gate mutex_exit(&ib_p->ib_intr_lock); 1657c478bd9Sstevel@tonic-gate } 1667c478bd9Sstevel@tonic-gate 167c17ca212SDavid Major int 168c17ca212SDavid Major px_ib_intr_pend(dev_info_t *dip, sysino_t sysino) 169c17ca212SDavid Major { 170c17ca212SDavid Major int ret = DDI_SUCCESS; 171c17ca212SDavid Major hrtime_t start_time, prev, curr, interval, jump; 172c17ca212SDavid Major hrtime_t intr_timeout; 173c17ca212SDavid Major intr_state_t intr_state; 174c17ca212SDavid Major 175c17ca212SDavid Major /* Disable the interrupt */ 176c17ca212SDavid Major PX_INTR_DISABLE(dip, sysino); 177c17ca212SDavid Major 178c17ca212SDavid Major intr_timeout = px_intrpend_timeout; 179c17ca212SDavid Major jump = TICK_TO_NSEC(xc_tick_jump_limit); 180c17ca212SDavid Major 181c17ca212SDavid Major /* Busy wait on pending interrupt */ 182c17ca212SDavid Major for (curr = start_time = gethrtime(); !panicstr && 183c17ca212SDavid Major ((ret = px_lib_intr_getstate(dip, sysino, 184c17ca212SDavid Major &intr_state)) == DDI_SUCCESS) && 185c17ca212SDavid Major (intr_state == INTR_DELIVERED_STATE); /* */) { 186c17ca212SDavid Major /* 187c17ca212SDavid Major * If we have a really large jump in hrtime, it is most 188c17ca212SDavid Major * probably because we entered the debugger (or OBP, 189c17ca212SDavid Major * in general). So, we adjust the timeout accordingly 190c17ca212SDavid Major * to prevent declaring an interrupt timeout. The 191c17ca212SDavid Major * master-interrupt mechanism in OBP should deliver 192c17ca212SDavid Major * the interrupts properly. 193c17ca212SDavid Major */ 194c17ca212SDavid Major prev = curr; 195c17ca212SDavid Major curr = gethrtime(); 196c17ca212SDavid Major interval = curr - prev; 197c17ca212SDavid Major if (interval > jump) 198c17ca212SDavid Major intr_timeout += interval; 199c17ca212SDavid Major if (curr - start_time > intr_timeout) { 200c17ca212SDavid Major ret = DDI_FAILURE; 201c17ca212SDavid Major break; 202c17ca212SDavid Major } 203c17ca212SDavid Major } 204c17ca212SDavid Major return (ret); 205c17ca212SDavid Major } 2067c478bd9Sstevel@tonic-gate 20769cd775fSschwartz void 2087c478bd9Sstevel@tonic-gate px_ib_intr_dist_en(dev_info_t *dip, cpuid_t cpu_id, devino_t ino, 2097c478bd9Sstevel@tonic-gate boolean_t wait_flag) 2107c478bd9Sstevel@tonic-gate { 2117c478bd9Sstevel@tonic-gate uint32_t old_cpu_id; 2127c478bd9Sstevel@tonic-gate sysino_t sysino; 2137c478bd9Sstevel@tonic-gate intr_valid_state_t enabled = 0; 2147c478bd9Sstevel@tonic-gate 2157c478bd9Sstevel@tonic-gate DBG(DBG_IB, dip, "px_ib_intr_dist_en: ino=0x%x\n", ino); 2167c478bd9Sstevel@tonic-gate 2177c478bd9Sstevel@tonic-gate if (px_lib_intr_devino_to_sysino(dip, ino, &sysino) != DDI_SUCCESS) { 2187c478bd9Sstevel@tonic-gate DBG(DBG_IB, dip, "px_ib_intr_dist_en: " 2197c478bd9Sstevel@tonic-gate "px_intr_devino_to_sysino() failed, ino 0x%x\n", ino); 2207c478bd9Sstevel@tonic-gate return; 2217c478bd9Sstevel@tonic-gate } 2227c478bd9Sstevel@tonic-gate 2237c478bd9Sstevel@tonic-gate /* Skip enabling disabled interrupts */ 2247c478bd9Sstevel@tonic-gate if (px_lib_intr_getvalid(dip, sysino, &enabled) != DDI_SUCCESS) { 2257c478bd9Sstevel@tonic-gate DBG(DBG_IB, dip, "px_ib_intr_dist_en: px_intr_getvalid() " 2267c478bd9Sstevel@tonic-gate "failed, sysino 0x%x\n", sysino); 2277c478bd9Sstevel@tonic-gate return; 2287c478bd9Sstevel@tonic-gate } 2297c478bd9Sstevel@tonic-gate if (!enabled) 2307c478bd9Sstevel@tonic-gate return; 2317c478bd9Sstevel@tonic-gate 2327c478bd9Sstevel@tonic-gate /* Done if redistributed onto the same cpuid */ 2337c478bd9Sstevel@tonic-gate if (px_lib_intr_gettarget(dip, sysino, &old_cpu_id) != DDI_SUCCESS) { 2347c478bd9Sstevel@tonic-gate DBG(DBG_IB, dip, "px_ib_intr_dist_en: " 2357c478bd9Sstevel@tonic-gate "px_intr_gettarget() failed\n"); 2367c478bd9Sstevel@tonic-gate return; 2377c478bd9Sstevel@tonic-gate } 2387c478bd9Sstevel@tonic-gate if (cpu_id == old_cpu_id) 2397c478bd9Sstevel@tonic-gate return; 2407c478bd9Sstevel@tonic-gate 241c17ca212SDavid Major /* Wait on pending interrupts */ 242c17ca212SDavid Major if (wait_flag != 0 && px_ib_intr_pend(dip, sysino) != DDI_SUCCESS) { 2437c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, 244b40cec45Skrishnae "%s%d: px_ib_intr_dist_en: sysino 0x%lx(ino 0x%x) " 2457c478bd9Sstevel@tonic-gate "from cpu id 0x%x to 0x%x timeout", 2467c478bd9Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip), 2477c478bd9Sstevel@tonic-gate sysino, ino, old_cpu_id, cpu_id); 2487c478bd9Sstevel@tonic-gate 2497c478bd9Sstevel@tonic-gate DBG(DBG_IB, dip, "px_ib_intr_dist_en: failed, " 2507c478bd9Sstevel@tonic-gate "ino 0x%x sysino 0x%x\n", ino, sysino); 251c17ca212SDavid Major } 2527c478bd9Sstevel@tonic-gate 2537c478bd9Sstevel@tonic-gate PX_INTR_ENABLE(dip, sysino, cpu_id); 2547c478bd9Sstevel@tonic-gate } 2557c478bd9Sstevel@tonic-gate 25669cd775fSschwartz static void 25769cd775fSschwartz px_ib_cpu_ticks_to_ih_nsec(px_ib_t *ib_p, px_ih_t *ih_p, uint32_t cpu_id) 25869cd775fSschwartz { 25969cd775fSschwartz extern kmutex_t pxintr_ks_template_lock; 26069cd775fSschwartz hrtime_t ticks; 26169cd775fSschwartz 26269cd775fSschwartz /* 26369cd775fSschwartz * Because we are updating two fields in ih_t we must lock 26469cd775fSschwartz * pxintr_ks_template_lock to prevent someone from reading the 26569cd775fSschwartz * kstats after we set ih_ticks to 0 and before we increment 26669cd775fSschwartz * ih_nsec to compensate. 26769cd775fSschwartz * 26869cd775fSschwartz * We must also protect against the interrupt arriving and incrementing 26969cd775fSschwartz * ih_ticks between the time we read it and when we reset it to 0. 27069cd775fSschwartz * To do this we use atomic_swap. 27169cd775fSschwartz */ 27269cd775fSschwartz 27369cd775fSschwartz ASSERT(MUTEX_HELD(&ib_p->ib_ino_lst_mutex)); 27469cd775fSschwartz 27569cd775fSschwartz mutex_enter(&pxintr_ks_template_lock); 27669cd775fSschwartz ticks = atomic_swap_64(&ih_p->ih_ticks, 0); 27769cd775fSschwartz ih_p->ih_nsec += (uint64_t)tick2ns(ticks, cpu_id); 27869cd775fSschwartz mutex_exit(&pxintr_ks_template_lock); 27969cd775fSschwartz } 28069cd775fSschwartz 2817c478bd9Sstevel@tonic-gate 2827c478bd9Sstevel@tonic-gate /* 2837c478bd9Sstevel@tonic-gate * Redistribute interrupts of the specified weight. The first call has a weight 2847c478bd9Sstevel@tonic-gate * of weight_max, which can be used to trigger initialization for 2857c478bd9Sstevel@tonic-gate * redistribution. The inos with weight [weight_max, inf.) should be processed 2867c478bd9Sstevel@tonic-gate * on the "weight == weight_max" call. This first call is followed by calls 2877c478bd9Sstevel@tonic-gate * of decreasing weights, inos of that weight should be processed. The final 2887c478bd9Sstevel@tonic-gate * call specifies a weight of zero, this can be used to trigger processing of 2897c478bd9Sstevel@tonic-gate * stragglers. 2907c478bd9Sstevel@tonic-gate */ 2917c478bd9Sstevel@tonic-gate static void 2927c478bd9Sstevel@tonic-gate px_ib_intr_redist(void *arg, int32_t weight_max, int32_t weight) 2937c478bd9Sstevel@tonic-gate { 2947c478bd9Sstevel@tonic-gate px_ib_t *ib_p = (px_ib_t *)arg; 2957c478bd9Sstevel@tonic-gate px_t *px_p = ib_p->ib_px_p; 2967c478bd9Sstevel@tonic-gate dev_info_t *dip = px_p->px_dip; 297b0fc0e77Sgovinda px_ino_t *ino_p; 298b0fc0e77Sgovinda px_ino_pil_t *ipil_p; 2997c478bd9Sstevel@tonic-gate px_ih_t *ih_lst; 3007c478bd9Sstevel@tonic-gate int32_t dweight = 0; 3017c478bd9Sstevel@tonic-gate int i; 3027c478bd9Sstevel@tonic-gate 3037c478bd9Sstevel@tonic-gate /* Redistribute internal interrupts */ 3047c478bd9Sstevel@tonic-gate if (weight == 0) { 3057c478bd9Sstevel@tonic-gate mutex_enter(&ib_p->ib_intr_lock); 30601689544Sjchu px_ib_intr_dist_en(dip, intr_dist_cpuid(), 30701689544Sjchu px_p->px_inos[PX_INTR_PEC], B_FALSE); 3087c478bd9Sstevel@tonic-gate mutex_exit(&ib_p->ib_intr_lock); 309ab4471cdSscarter 310ab4471cdSscarter px_hp_intr_redist(px_p); 3117c478bd9Sstevel@tonic-gate } 3127c478bd9Sstevel@tonic-gate 3137c478bd9Sstevel@tonic-gate /* Redistribute device interrupts */ 3147c478bd9Sstevel@tonic-gate mutex_enter(&ib_p->ib_ino_lst_mutex); 31509b1eac2SEvan Yan px_msiq_redist(px_p); 3167c478bd9Sstevel@tonic-gate 317b0fc0e77Sgovinda for (ino_p = ib_p->ib_ino_lst; ino_p; ino_p = ino_p->ino_next_p) { 3187c478bd9Sstevel@tonic-gate /* 3197c478bd9Sstevel@tonic-gate * Recomputes the sum of interrupt weights of devices that 3207c478bd9Sstevel@tonic-gate * share the same ino upon first call marked by 3217c478bd9Sstevel@tonic-gate * (weight == weight_max). 3227c478bd9Sstevel@tonic-gate */ 3237c478bd9Sstevel@tonic-gate if (weight == weight_max) { 3247c478bd9Sstevel@tonic-gate ino_p->ino_intr_weight = 0; 325b0fc0e77Sgovinda 326b0fc0e77Sgovinda for (ipil_p = ino_p->ino_ipil_p; ipil_p; 327b0fc0e77Sgovinda ipil_p = ipil_p->ipil_next_p) { 328b0fc0e77Sgovinda for (i = 0, ih_lst = ipil_p->ipil_ih_head; 329b0fc0e77Sgovinda i < ipil_p->ipil_ih_size; i++, 330b0fc0e77Sgovinda ih_lst = ih_lst->ih_next) { 331b0fc0e77Sgovinda dweight = i_ddi_get_intr_weight( 332b0fc0e77Sgovinda ih_lst->ih_dip); 3337c478bd9Sstevel@tonic-gate if (dweight > 0) 334b0fc0e77Sgovinda ino_p->ino_intr_weight += 335b0fc0e77Sgovinda dweight; 336b0fc0e77Sgovinda } 3377c478bd9Sstevel@tonic-gate } 3387c478bd9Sstevel@tonic-gate } 3397c478bd9Sstevel@tonic-gate 3407c478bd9Sstevel@tonic-gate /* 3417c478bd9Sstevel@tonic-gate * As part of redistributing weighted interrupts over cpus, 3427c478bd9Sstevel@tonic-gate * nexus redistributes device interrupts and updates 3437c478bd9Sstevel@tonic-gate * cpu weight. The purpose is for the most light weighted 3447c478bd9Sstevel@tonic-gate * cpu to take the next interrupt and gain weight, therefore 3457c478bd9Sstevel@tonic-gate * attention demanding device gains more cpu attention by 3467c478bd9Sstevel@tonic-gate * making itself heavy. 3477c478bd9Sstevel@tonic-gate */ 3487c478bd9Sstevel@tonic-gate if ((weight == ino_p->ino_intr_weight) || 3497c478bd9Sstevel@tonic-gate ((weight >= weight_max) && 3507c478bd9Sstevel@tonic-gate (ino_p->ino_intr_weight >= weight_max))) { 35109b1eac2SEvan Yan uint32_t orig_cpuid = ino_p->ino_cpuid; 35209b1eac2SEvan Yan 3537c478bd9Sstevel@tonic-gate if (cpu[orig_cpuid] == NULL) 3547c478bd9Sstevel@tonic-gate orig_cpuid = CPU->cpu_id; 3557c478bd9Sstevel@tonic-gate 35609b1eac2SEvan Yan DBG(DBG_IB, dip, "px_ib_intr_redist: sysino 0x%llx " 35709b1eac2SEvan Yan "current cpuid 0x%x current default cpuid 0x%x\n", 35809b1eac2SEvan Yan ino_p->ino_sysino, ino_p->ino_cpuid, 35909b1eac2SEvan Yan ino_p->ino_default_cpuid); 36009b1eac2SEvan Yan 36109b1eac2SEvan Yan /* select target cpuid and mark ino established */ 36209b1eac2SEvan Yan if (ino_p->ino_default_cpuid == -1) 36309b1eac2SEvan Yan ino_p->ino_cpuid = ino_p->ino_default_cpuid = 36409b1eac2SEvan Yan intr_dist_cpuid(); 36509b1eac2SEvan Yan else if ((ino_p->ino_cpuid != 36609b1eac2SEvan Yan ino_p->ino_default_cpuid) && 367c2b9b7a9SGovinda Tatti cpu[ino_p->ino_default_cpuid] && 368c2b9b7a9SGovinda Tatti cpu_intr_on(cpu[ino_p->ino_default_cpuid])) 36909b1eac2SEvan Yan ino_p->ino_cpuid = ino_p->ino_default_cpuid; 37009b1eac2SEvan Yan else if (!cpu_intr_on(cpu[ino_p->ino_cpuid])) 3717c478bd9Sstevel@tonic-gate ino_p->ino_cpuid = intr_dist_cpuid(); 3727c478bd9Sstevel@tonic-gate 37309b1eac2SEvan Yan DBG(DBG_IB, dip, "px_ib_intr_redist: sysino 0x%llx " 37409b1eac2SEvan Yan "new cpuid 0x%x new default cpuid 0x%x\n", 37509b1eac2SEvan Yan ino_p->ino_sysino, ino_p->ino_cpuid, 37609b1eac2SEvan Yan ino_p->ino_default_cpuid); 37709b1eac2SEvan Yan 3787c478bd9Sstevel@tonic-gate /* Add device weight to targeted cpu. */ 379b0fc0e77Sgovinda for (ipil_p = ino_p->ino_ipil_p; ipil_p; 380b0fc0e77Sgovinda ipil_p = ipil_p->ipil_next_p) { 381b0fc0e77Sgovinda for (i = 0, ih_lst = ipil_p->ipil_ih_head; 382b0fc0e77Sgovinda i < ipil_p->ipil_ih_size; i++, 383b0fc0e77Sgovinda ih_lst = ih_lst->ih_next) { 3847c478bd9Sstevel@tonic-gate 385b0fc0e77Sgovinda dweight = i_ddi_get_intr_weight( 386b0fc0e77Sgovinda ih_lst->ih_dip); 3877c478bd9Sstevel@tonic-gate intr_dist_cpuid_add_device_weight( 388b0fc0e77Sgovinda ino_p->ino_cpuid, ih_lst->ih_dip, 389b0fc0e77Sgovinda dweight); 3907c478bd9Sstevel@tonic-gate 3917c478bd9Sstevel@tonic-gate /* 392b0fc0e77Sgovinda * Different cpus may have different 393b0fc0e77Sgovinda * clock speeds. to account for this, 394b0fc0e77Sgovinda * whenever an interrupt is moved to a 395b0fc0e77Sgovinda * new CPU, we convert the accumulated 396b0fc0e77Sgovinda * ticks into nsec, based upon the clock 397b0fc0e77Sgovinda * rate of the prior CPU. 3987c478bd9Sstevel@tonic-gate * 399b0fc0e77Sgovinda * It is possible that the prior CPU no 400b0fc0e77Sgovinda * longer exists. In this case, fall 401b0fc0e77Sgovinda * back to using this CPU's clock rate. 4027c478bd9Sstevel@tonic-gate * 403b0fc0e77Sgovinda * Note that the value in ih_ticks has 404b0fc0e77Sgovinda * already been corrected for any power 405b0fc0e77Sgovinda * savings mode which might have been 406b0fc0e77Sgovinda * in effect. 4077c478bd9Sstevel@tonic-gate */ 40869cd775fSschwartz px_ib_cpu_ticks_to_ih_nsec(ib_p, ih_lst, 40969cd775fSschwartz orig_cpuid); 4107c478bd9Sstevel@tonic-gate } 411b0fc0e77Sgovinda } 4127c478bd9Sstevel@tonic-gate 4137c478bd9Sstevel@tonic-gate /* enable interrupt on new targeted cpu */ 4147c478bd9Sstevel@tonic-gate px_ib_intr_dist_en(dip, ino_p->ino_cpuid, 4157c478bd9Sstevel@tonic-gate ino_p->ino_ino, B_TRUE); 4167c478bd9Sstevel@tonic-gate } 4177c478bd9Sstevel@tonic-gate } 4187c478bd9Sstevel@tonic-gate mutex_exit(&ib_p->ib_ino_lst_mutex); 4197c478bd9Sstevel@tonic-gate } 4207c478bd9Sstevel@tonic-gate 4217c478bd9Sstevel@tonic-gate /* 4227c478bd9Sstevel@tonic-gate * Reset interrupts to IDLE. This function is called during 4237c478bd9Sstevel@tonic-gate * panic handling after redistributing interrupts; it's needed to 4247c478bd9Sstevel@tonic-gate * support dumping to network devices after 'sync' from OBP. 4257c478bd9Sstevel@tonic-gate * 4267c478bd9Sstevel@tonic-gate * N.B. This routine runs in a context where all other threads 4277c478bd9Sstevel@tonic-gate * are permanently suspended. 4287c478bd9Sstevel@tonic-gate */ 4297c478bd9Sstevel@tonic-gate static uint_t 4307c478bd9Sstevel@tonic-gate px_ib_intr_reset(void *arg) 4317c478bd9Sstevel@tonic-gate { 4327c478bd9Sstevel@tonic-gate px_ib_t *ib_p = (px_ib_t *)arg; 4337c478bd9Sstevel@tonic-gate 4347c478bd9Sstevel@tonic-gate DBG(DBG_IB, ib_p->ib_px_p->px_dip, "px_ib_intr_reset\n"); 4357c478bd9Sstevel@tonic-gate 4367c478bd9Sstevel@tonic-gate if (px_lib_intr_reset(ib_p->ib_px_p->px_dip) != DDI_SUCCESS) 4377c478bd9Sstevel@tonic-gate return (BF_FATAL); 4387c478bd9Sstevel@tonic-gate 4397c478bd9Sstevel@tonic-gate return (BF_NONE); 4407c478bd9Sstevel@tonic-gate } 4417c478bd9Sstevel@tonic-gate 4427c478bd9Sstevel@tonic-gate /* 443b0fc0e77Sgovinda * Locate px_ino_t structure on ib_p->ib_ino_lst according to ino# 4447c478bd9Sstevel@tonic-gate * returns NULL if not found. 4457c478bd9Sstevel@tonic-gate */ 446b0fc0e77Sgovinda px_ino_t * 4477c478bd9Sstevel@tonic-gate px_ib_locate_ino(px_ib_t *ib_p, devino_t ino_num) 4487c478bd9Sstevel@tonic-gate { 449b0fc0e77Sgovinda px_ino_t *ino_p = ib_p->ib_ino_lst; 4507c478bd9Sstevel@tonic-gate 4517c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&ib_p->ib_ino_lst_mutex)); 4527c478bd9Sstevel@tonic-gate 453f0d69850Srameshc for (; ino_p && ino_p->ino_ino != ino_num; ino_p = ino_p->ino_next_p) 454f0d69850Srameshc ; 4557c478bd9Sstevel@tonic-gate 4567c478bd9Sstevel@tonic-gate return (ino_p); 4577c478bd9Sstevel@tonic-gate } 4587c478bd9Sstevel@tonic-gate 45909b1eac2SEvan Yan px_ino_t * 46009b1eac2SEvan Yan px_ib_alloc_ino(px_ib_t *ib_p, devino_t ino_num) 4617c478bd9Sstevel@tonic-gate { 4627c478bd9Sstevel@tonic-gate sysino_t sysino; 46309b1eac2SEvan Yan px_ino_t *ino_p; 4647c478bd9Sstevel@tonic-gate 465b0fc0e77Sgovinda if (px_lib_intr_devino_to_sysino(ib_p->ib_px_p->px_dip, 466b0fc0e77Sgovinda ino_num, &sysino) != DDI_SUCCESS) 4677c478bd9Sstevel@tonic-gate return (NULL); 4687c478bd9Sstevel@tonic-gate 469b0fc0e77Sgovinda ino_p = kmem_zalloc(sizeof (px_ino_t), KM_SLEEP); 4707c478bd9Sstevel@tonic-gate 471b0fc0e77Sgovinda ino_p->ino_next_p = ib_p->ib_ino_lst; 4727c478bd9Sstevel@tonic-gate ib_p->ib_ino_lst = ino_p; 4737c478bd9Sstevel@tonic-gate 474b0fc0e77Sgovinda ino_p->ino_ino = ino_num; 475b0fc0e77Sgovinda ino_p->ino_sysino = sysino; 476b0fc0e77Sgovinda ino_p->ino_ib_p = ib_p; 477b0fc0e77Sgovinda ino_p->ino_unclaimed_intrs = 0; 47809b1eac2SEvan Yan ino_p->ino_lopil = 0; 47909b1eac2SEvan Yan ino_p->ino_cpuid = ino_p->ino_default_cpuid = (cpuid_t)-1; 48009b1eac2SEvan Yan 48109b1eac2SEvan Yan return (ino_p); 4827c478bd9Sstevel@tonic-gate } 4837c478bd9Sstevel@tonic-gate 48409b1eac2SEvan Yan px_ino_pil_t * 48509b1eac2SEvan Yan px_ib_new_ino_pil(px_ib_t *ib_p, devino_t ino_num, uint_t pil, px_ih_t *ih_p) 48609b1eac2SEvan Yan { 48709b1eac2SEvan Yan px_ino_pil_t *ipil_p = kmem_zalloc(sizeof (px_ino_pil_t), KM_SLEEP); 48809b1eac2SEvan Yan px_ino_t *ino_p; 48909b1eac2SEvan Yan 49009b1eac2SEvan Yan if ((ino_p = px_ib_locate_ino(ib_p, ino_num)) == NULL) 49109b1eac2SEvan Yan ino_p = px_ib_alloc_ino(ib_p, ino_num); 49209b1eac2SEvan Yan 49309b1eac2SEvan Yan ASSERT(ino_p != NULL); 49409b1eac2SEvan Yan 495b0fc0e77Sgovinda ih_p->ih_next = ih_p; 496b0fc0e77Sgovinda ipil_p->ipil_pil = pil; 497b0fc0e77Sgovinda ipil_p->ipil_ih_head = ih_p; 498b0fc0e77Sgovinda ipil_p->ipil_ih_tail = ih_p; 499b0fc0e77Sgovinda ipil_p->ipil_ih_start = ih_p; 500b0fc0e77Sgovinda ipil_p->ipil_ih_size = 1; 501b0fc0e77Sgovinda ipil_p->ipil_ino_p = ino_p; 502b0fc0e77Sgovinda 503b0fc0e77Sgovinda ipil_p->ipil_next_p = ino_p->ino_ipil_p; 504b0fc0e77Sgovinda ino_p->ino_ipil_p = ipil_p; 505b0fc0e77Sgovinda ino_p->ino_ipil_size++; 506b0fc0e77Sgovinda 50709b1eac2SEvan Yan if ((ino_p->ino_lopil == 0) || (ino_p->ino_lopil > pil)) 508b0fc0e77Sgovinda ino_p->ino_lopil = pil; 509b0fc0e77Sgovinda 510b0fc0e77Sgovinda return (ipil_p); 511b0fc0e77Sgovinda } 512b0fc0e77Sgovinda 5137c478bd9Sstevel@tonic-gate void 514b0fc0e77Sgovinda px_ib_delete_ino_pil(px_ib_t *ib_p, px_ino_pil_t *ipil_p) 5157c478bd9Sstevel@tonic-gate { 516b0fc0e77Sgovinda px_ino_t *ino_p = ipil_p->ipil_ino_p; 517b0fc0e77Sgovinda ushort_t pil = ipil_p->ipil_pil; 518b0fc0e77Sgovinda px_ino_pil_t *prev, *next; 5197c478bd9Sstevel@tonic-gate 5207c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&ib_p->ib_ino_lst_mutex)); 5217c478bd9Sstevel@tonic-gate 522b0fc0e77Sgovinda if (ino_p->ino_ipil_p == ipil_p) 523b0fc0e77Sgovinda ino_p->ino_ipil_p = ipil_p->ipil_next_p; 5247c478bd9Sstevel@tonic-gate else { 525b0fc0e77Sgovinda for (prev = next = ino_p->ino_ipil_p; next != ipil_p; 526f0d69850Srameshc prev = next, next = next->ipil_next_p) 527f0d69850Srameshc ; 528b0fc0e77Sgovinda 529b0fc0e77Sgovinda if (prev) 530b0fc0e77Sgovinda prev->ipil_next_p = ipil_p->ipil_next_p; 531b0fc0e77Sgovinda } 532b0fc0e77Sgovinda 533b0fc0e77Sgovinda kmem_free(ipil_p, sizeof (px_ino_pil_t)); 534b0fc0e77Sgovinda 535e4517573Srameshc if ((--ino_p->ino_ipil_size) && (ino_p->ino_lopil == pil)) { 536e4517573Srameshc for (next = ino_p->ino_ipil_p, pil = next->ipil_pil; 537e4517573Srameshc next; next = next->ipil_next_p) { 538e4517573Srameshc 539b0fc0e77Sgovinda if (pil > next->ipil_pil) 540b0fc0e77Sgovinda pil = next->ipil_pil; 541b0fc0e77Sgovinda } 54209b1eac2SEvan Yan 543e4517573Srameshc /* 544e4517573Srameshc * Value stored in pil should be the lowest pil. 545e4517573Srameshc */ 546b0fc0e77Sgovinda ino_p->ino_lopil = pil; 547b0fc0e77Sgovinda } 548b0fc0e77Sgovinda 549e4517573Srameshc if (ino_p->ino_ipil_size) 550b0fc0e77Sgovinda return; 551b0fc0e77Sgovinda 55209b1eac2SEvan Yan ino_p->ino_lopil = 0; 55309b1eac2SEvan Yan 55409b1eac2SEvan Yan if (ino_p->ino_msiq_p) 55509b1eac2SEvan Yan return; 55609b1eac2SEvan Yan 557b0fc0e77Sgovinda if (ib_p->ib_ino_lst == ino_p) 558b0fc0e77Sgovinda ib_p->ib_ino_lst = ino_p->ino_next_p; 559b0fc0e77Sgovinda else { 560b0fc0e77Sgovinda px_ino_t *list = ib_p->ib_ino_lst; 561b0fc0e77Sgovinda 562f0d69850Srameshc for (; list->ino_next_p != ino_p; list = list->ino_next_p) 563f0d69850Srameshc ; 564b0fc0e77Sgovinda list->ino_next_p = ino_p->ino_next_p; 5657c478bd9Sstevel@tonic-gate } 5667c478bd9Sstevel@tonic-gate } 5677c478bd9Sstevel@tonic-gate 5687c478bd9Sstevel@tonic-gate /* 5697c478bd9Sstevel@tonic-gate * Free all ino when we are detaching. 5707c478bd9Sstevel@tonic-gate */ 5717c478bd9Sstevel@tonic-gate void 5727c478bd9Sstevel@tonic-gate px_ib_free_ino_all(px_ib_t *ib_p) 5737c478bd9Sstevel@tonic-gate { 574b0fc0e77Sgovinda px_ino_t *ino_p = ib_p->ib_ino_lst; 575b0fc0e77Sgovinda px_ino_t *next = NULL; 5767c478bd9Sstevel@tonic-gate 577b0fc0e77Sgovinda while (ino_p) { 578b0fc0e77Sgovinda next = ino_p->ino_next_p; 579b0fc0e77Sgovinda kmem_free(ino_p, sizeof (px_ino_t)); 580b0fc0e77Sgovinda ino_p = next; 5817c478bd9Sstevel@tonic-gate } 5827c478bd9Sstevel@tonic-gate } 5837c478bd9Sstevel@tonic-gate 584b0fc0e77Sgovinda /* 585b0fc0e77Sgovinda * Locate px_ino_pil_t structure on ino_p->ino_ipil_p according to ino# 586b0fc0e77Sgovinda * returns NULL if not found. 587b0fc0e77Sgovinda */ 588b0fc0e77Sgovinda px_ino_pil_t * 589b0fc0e77Sgovinda px_ib_ino_locate_ipil(px_ino_t *ino_p, uint_t pil) 590b0fc0e77Sgovinda { 591b0fc0e77Sgovinda px_ino_pil_t *ipil_p = ino_p->ino_ipil_p; 592b0fc0e77Sgovinda 593f0d69850Srameshc for (; ipil_p && ipil_p->ipil_pil != pil; ipil_p = ipil_p->ipil_next_p) 594f0d69850Srameshc ; 595b0fc0e77Sgovinda 596b0fc0e77Sgovinda return (ipil_p); 597b0fc0e77Sgovinda } 598b0fc0e77Sgovinda 5997c478bd9Sstevel@tonic-gate int 600b0fc0e77Sgovinda px_ib_ino_add_intr(px_t *px_p, px_ino_pil_t *ipil_p, px_ih_t *ih_p) 6017c478bd9Sstevel@tonic-gate { 602b0fc0e77Sgovinda px_ino_t *ino_p = ipil_p->ipil_ino_p; 6037c478bd9Sstevel@tonic-gate px_ib_t *ib_p = ino_p->ino_ib_p; 6047c478bd9Sstevel@tonic-gate devino_t ino = ino_p->ino_ino; 6057c478bd9Sstevel@tonic-gate sysino_t sysino = ino_p->ino_sysino; 6067c478bd9Sstevel@tonic-gate dev_info_t *dip = px_p->px_dip; 6077c478bd9Sstevel@tonic-gate cpuid_t curr_cpu; 6087c478bd9Sstevel@tonic-gate int ret = DDI_SUCCESS; 6097c478bd9Sstevel@tonic-gate 6107c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&ib_p->ib_ino_lst_mutex)); 6117c478bd9Sstevel@tonic-gate ASSERT(ib_p == px_p->px_ib_p); 6127c478bd9Sstevel@tonic-gate 6137c478bd9Sstevel@tonic-gate DBG(DBG_IB, dip, "px_ib_ino_add_intr ino=%x\n", ino_p->ino_ino); 6147c478bd9Sstevel@tonic-gate 6157c478bd9Sstevel@tonic-gate /* Disable the interrupt */ 6167c478bd9Sstevel@tonic-gate if ((ret = px_lib_intr_gettarget(dip, sysino, 6177c478bd9Sstevel@tonic-gate &curr_cpu)) != DDI_SUCCESS) { 6187c478bd9Sstevel@tonic-gate DBG(DBG_IB, dip, 6197c478bd9Sstevel@tonic-gate "px_ib_ino_add_intr px_intr_gettarget() failed\n"); 6207c478bd9Sstevel@tonic-gate 6217c478bd9Sstevel@tonic-gate return (ret); 6227c478bd9Sstevel@tonic-gate } 6237c478bd9Sstevel@tonic-gate 624c17ca212SDavid Major /* Wait on pending interrupt */ 625c17ca212SDavid Major if ((ret = px_ib_intr_pend(dip, sysino)) != DDI_SUCCESS) { 6267c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s%d: px_ib_ino_add_intr: pending " 627b40cec45Skrishnae "sysino 0x%lx(ino 0x%x) timeout", 6287c478bd9Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip), 6297c478bd9Sstevel@tonic-gate sysino, ino); 6307c478bd9Sstevel@tonic-gate } 6317c478bd9Sstevel@tonic-gate 632b0fc0e77Sgovinda /* 633b0fc0e77Sgovinda * If the interrupt was previously blocked (left in pending state) 634b0fc0e77Sgovinda * because of jabber we need to clear the pending state in case the 635b0fc0e77Sgovinda * jabber has gone away. 636b0fc0e77Sgovinda */ 637b0fc0e77Sgovinda if (ino_p->ino_unclaimed_intrs > px_unclaimed_intr_max) { 638b0fc0e77Sgovinda cmn_err(CE_WARN, 639b0fc0e77Sgovinda "%s%d: px_ib_ino_add_intr: ino 0x%x has been unblocked", 640b0fc0e77Sgovinda ddi_driver_name(dip), ddi_get_instance(dip), ino); 641b0fc0e77Sgovinda 642b0fc0e77Sgovinda ino_p->ino_unclaimed_intrs = 0; 643b0fc0e77Sgovinda ret = px_lib_intr_setstate(dip, sysino, INTR_IDLE_STATE); 644b0fc0e77Sgovinda } 645b0fc0e77Sgovinda 6467c478bd9Sstevel@tonic-gate if (ret != DDI_SUCCESS) { 6477c478bd9Sstevel@tonic-gate DBG(DBG_IB, dip, "px_ib_ino_add_intr: failed, " 6487c478bd9Sstevel@tonic-gate "ino 0x%x sysino 0x%x\n", ino, sysino); 6497c478bd9Sstevel@tonic-gate 6507c478bd9Sstevel@tonic-gate return (ret); 6517c478bd9Sstevel@tonic-gate } 6527c478bd9Sstevel@tonic-gate 653b0fc0e77Sgovinda /* Link up px_ih_t */ 654b0fc0e77Sgovinda ih_p->ih_next = ipil_p->ipil_ih_head; 655b0fc0e77Sgovinda ipil_p->ipil_ih_tail->ih_next = ih_p; 656b0fc0e77Sgovinda ipil_p->ipil_ih_tail = ih_p; 6577c478bd9Sstevel@tonic-gate 658b0fc0e77Sgovinda ipil_p->ipil_ih_start = ipil_p->ipil_ih_head; 659b0fc0e77Sgovinda ipil_p->ipil_ih_size++; 6607c478bd9Sstevel@tonic-gate 6617c478bd9Sstevel@tonic-gate /* Re-enable interrupt */ 6627c478bd9Sstevel@tonic-gate PX_INTR_ENABLE(dip, sysino, curr_cpu); 6637c478bd9Sstevel@tonic-gate 6647c478bd9Sstevel@tonic-gate return (ret); 6657c478bd9Sstevel@tonic-gate } 6667c478bd9Sstevel@tonic-gate 6677c478bd9Sstevel@tonic-gate /* 668b0fc0e77Sgovinda * Removes px_ih_t from the ino's link list. 6697c478bd9Sstevel@tonic-gate * uses hardware mutex to lock out interrupt threads. 6707c478bd9Sstevel@tonic-gate * Side effects: interrupt belongs to that ino is turned off on return. 6717c478bd9Sstevel@tonic-gate * if we are sharing PX slot with other inos, the caller needs 6727c478bd9Sstevel@tonic-gate * to turn it back on. 6737c478bd9Sstevel@tonic-gate */ 6747c478bd9Sstevel@tonic-gate int 675b0fc0e77Sgovinda px_ib_ino_rem_intr(px_t *px_p, px_ino_pil_t *ipil_p, px_ih_t *ih_p) 6767c478bd9Sstevel@tonic-gate { 677b0fc0e77Sgovinda px_ino_t *ino_p = ipil_p->ipil_ino_p; 6787c478bd9Sstevel@tonic-gate devino_t ino = ino_p->ino_ino; 6797c478bd9Sstevel@tonic-gate sysino_t sysino = ino_p->ino_sysino; 6807c478bd9Sstevel@tonic-gate dev_info_t *dip = px_p->px_dip; 681b0fc0e77Sgovinda px_ih_t *ih_lst = ipil_p->ipil_ih_head; 6827c478bd9Sstevel@tonic-gate int i, ret = DDI_SUCCESS; 6837c478bd9Sstevel@tonic-gate 6847c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&ino_p->ino_ib_p->ib_ino_lst_mutex)); 6857c478bd9Sstevel@tonic-gate 6867c478bd9Sstevel@tonic-gate DBG(DBG_IB, px_p->px_dip, "px_ib_ino_rem_intr ino=%x\n", 6877c478bd9Sstevel@tonic-gate ino_p->ino_ino); 6887c478bd9Sstevel@tonic-gate 689c17ca212SDavid Major /* Wait on pending interrupt */ 690c17ca212SDavid Major if ((ret = px_ib_intr_pend(dip, sysino)) != DDI_SUCCESS) { 6917c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s%d: px_ib_ino_rem_intr: pending " 692b40cec45Skrishnae "sysino 0x%lx(ino 0x%x) timeout", 6937c478bd9Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip), 6947c478bd9Sstevel@tonic-gate sysino, ino); 6957c478bd9Sstevel@tonic-gate } 6967c478bd9Sstevel@tonic-gate 697b0fc0e77Sgovinda /* 698b0fc0e77Sgovinda * If the interrupt was previously blocked (left in pending state) 699b0fc0e77Sgovinda * because of jabber we need to clear the pending state in case the 700b0fc0e77Sgovinda * jabber has gone away. 701b0fc0e77Sgovinda */ 7021a92841dSDaniel Ice if (ret == DDI_SUCCESS && 7031a92841dSDaniel Ice ino_p->ino_unclaimed_intrs > px_unclaimed_intr_max) { 704b0fc0e77Sgovinda cmn_err(CE_WARN, "%s%d: px_ib_ino_rem_intr: " 705b0fc0e77Sgovinda "ino 0x%x has been unblocked", 706b0fc0e77Sgovinda ddi_driver_name(dip), ddi_get_instance(dip), ino); 707b0fc0e77Sgovinda 708b0fc0e77Sgovinda ino_p->ino_unclaimed_intrs = 0; 709b0fc0e77Sgovinda ret = px_lib_intr_setstate(dip, sysino, INTR_IDLE_STATE); 710b0fc0e77Sgovinda } 711b0fc0e77Sgovinda 7127c478bd9Sstevel@tonic-gate if (ret != DDI_SUCCESS) { 7137c478bd9Sstevel@tonic-gate DBG(DBG_IB, dip, "px_ib_ino_rem_intr: failed, " 7147c478bd9Sstevel@tonic-gate "ino 0x%x sysino 0x%x\n", ino, sysino); 7157c478bd9Sstevel@tonic-gate 7167c478bd9Sstevel@tonic-gate return (ret); 7177c478bd9Sstevel@tonic-gate } 7187c478bd9Sstevel@tonic-gate 7191a92841dSDaniel Ice if (ipil_p->ipil_ih_size == 1) { 7201a92841dSDaniel Ice if (ih_lst != ih_p) 7211a92841dSDaniel Ice goto not_found; 7221a92841dSDaniel Ice 7231a92841dSDaniel Ice /* No need to set head/tail as ino_p will be freed */ 7241a92841dSDaniel Ice goto reset; 7251a92841dSDaniel Ice } 7261a92841dSDaniel Ice 7277c478bd9Sstevel@tonic-gate /* Search the link list for ih_p */ 728b0fc0e77Sgovinda for (i = 0; (i < ipil_p->ipil_ih_size) && 729f0d69850Srameshc (ih_lst->ih_next != ih_p); i++, ih_lst = ih_lst->ih_next) 730f0d69850Srameshc ; 7317c478bd9Sstevel@tonic-gate 7327c478bd9Sstevel@tonic-gate if (ih_lst->ih_next != ih_p) 7337c478bd9Sstevel@tonic-gate goto not_found; 7347c478bd9Sstevel@tonic-gate 7357c478bd9Sstevel@tonic-gate /* Remove ih_p from the link list and maintain the head/tail */ 7367c478bd9Sstevel@tonic-gate ih_lst->ih_next = ih_p->ih_next; 7377c478bd9Sstevel@tonic-gate 738b0fc0e77Sgovinda if (ipil_p->ipil_ih_head == ih_p) 739b0fc0e77Sgovinda ipil_p->ipil_ih_head = ih_p->ih_next; 740b0fc0e77Sgovinda if (ipil_p->ipil_ih_tail == ih_p) 741b0fc0e77Sgovinda ipil_p->ipil_ih_tail = ih_lst; 7427c478bd9Sstevel@tonic-gate 743b0fc0e77Sgovinda ipil_p->ipil_ih_start = ipil_p->ipil_ih_head; 7447c478bd9Sstevel@tonic-gate 7457c478bd9Sstevel@tonic-gate reset: 7467c478bd9Sstevel@tonic-gate if (ih_p->ih_config_handle) 7477c478bd9Sstevel@tonic-gate pci_config_teardown(&ih_p->ih_config_handle); 7487c478bd9Sstevel@tonic-gate if (ih_p->ih_ksp != NULL) 7497c478bd9Sstevel@tonic-gate kstat_delete(ih_p->ih_ksp); 7507c478bd9Sstevel@tonic-gate 7517c478bd9Sstevel@tonic-gate kmem_free(ih_p, sizeof (px_ih_t)); 752b0fc0e77Sgovinda ipil_p->ipil_ih_size--; 7537c478bd9Sstevel@tonic-gate 7547c478bd9Sstevel@tonic-gate return (ret); 7557c478bd9Sstevel@tonic-gate 7567c478bd9Sstevel@tonic-gate not_found: 7577c478bd9Sstevel@tonic-gate DBG(DBG_R_INTX, ino_p->ino_ib_p->ib_px_p->px_dip, 7587c478bd9Sstevel@tonic-gate "ino_p=%x does not have ih_p=%x\n", ino_p, ih_p); 7597c478bd9Sstevel@tonic-gate 7607c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 7617c478bd9Sstevel@tonic-gate } 7627c478bd9Sstevel@tonic-gate 7637c478bd9Sstevel@tonic-gate px_ih_t * 764b0fc0e77Sgovinda px_ib_intr_locate_ih(px_ino_pil_t *ipil_p, dev_info_t *rdip, 7657c478bd9Sstevel@tonic-gate uint32_t inum, msiq_rec_type_t rec_type, msgcode_t msg_code) 7667c478bd9Sstevel@tonic-gate { 767b0fc0e77Sgovinda px_ih_t *ih_p = ipil_p->ipil_ih_head; 7687c478bd9Sstevel@tonic-gate int i; 7697c478bd9Sstevel@tonic-gate 770b0fc0e77Sgovinda for (i = 0; i < ipil_p->ipil_ih_size; i++, ih_p = ih_p->ih_next) { 771b0fc0e77Sgovinda if ((ih_p->ih_dip == rdip) && (ih_p->ih_inum == inum) && 772b0fc0e77Sgovinda (ih_p->ih_rec_type == rec_type) && 773b0fc0e77Sgovinda (ih_p->ih_msg_code == msg_code)) 774b0fc0e77Sgovinda return (ih_p); 7757c478bd9Sstevel@tonic-gate } 7767c478bd9Sstevel@tonic-gate 7777c478bd9Sstevel@tonic-gate return ((px_ih_t *)NULL); 7787c478bd9Sstevel@tonic-gate } 7797c478bd9Sstevel@tonic-gate 7807c478bd9Sstevel@tonic-gate px_ih_t * 7817c478bd9Sstevel@tonic-gate px_ib_alloc_ih(dev_info_t *rdip, uint32_t inum, 7827c478bd9Sstevel@tonic-gate uint_t (*int_handler)(caddr_t int_handler_arg1, caddr_t int_handler_arg2), 7837c478bd9Sstevel@tonic-gate caddr_t int_handler_arg1, caddr_t int_handler_arg2, 7847c478bd9Sstevel@tonic-gate msiq_rec_type_t rec_type, msgcode_t msg_code) 7857c478bd9Sstevel@tonic-gate { 7867c478bd9Sstevel@tonic-gate px_ih_t *ih_p; 7877c478bd9Sstevel@tonic-gate 7887c478bd9Sstevel@tonic-gate ih_p = kmem_alloc(sizeof (px_ih_t), KM_SLEEP); 7897c478bd9Sstevel@tonic-gate ih_p->ih_dip = rdip; 7907c478bd9Sstevel@tonic-gate ih_p->ih_inum = inum; 7917c478bd9Sstevel@tonic-gate ih_p->ih_intr_state = PX_INTR_STATE_DISABLE; 792d17daf0bSScott Carter, SD IOSW ih_p->ih_intr_flags = PX_INTR_IDLE; 7937c478bd9Sstevel@tonic-gate ih_p->ih_handler = int_handler; 7947c478bd9Sstevel@tonic-gate ih_p->ih_handler_arg1 = int_handler_arg1; 7957c478bd9Sstevel@tonic-gate ih_p->ih_handler_arg2 = int_handler_arg2; 7967c478bd9Sstevel@tonic-gate ih_p->ih_config_handle = NULL; 7977c478bd9Sstevel@tonic-gate ih_p->ih_rec_type = rec_type; 7987c478bd9Sstevel@tonic-gate ih_p->ih_msg_code = msg_code; 7997c478bd9Sstevel@tonic-gate ih_p->ih_nsec = 0; 8007c478bd9Sstevel@tonic-gate ih_p->ih_ticks = 0; 8017c478bd9Sstevel@tonic-gate ih_p->ih_ksp = NULL; 8027c478bd9Sstevel@tonic-gate 8037c478bd9Sstevel@tonic-gate return (ih_p); 8047c478bd9Sstevel@tonic-gate } 8057c478bd9Sstevel@tonic-gate 8067c478bd9Sstevel@tonic-gate int 8077c478bd9Sstevel@tonic-gate px_ib_update_intr_state(px_t *px_p, dev_info_t *rdip, 808b0fc0e77Sgovinda uint_t inum, devino_t ino, uint_t pil, 809b0fc0e77Sgovinda uint_t new_intr_state, msiq_rec_type_t rec_type, 810b0fc0e77Sgovinda msgcode_t msg_code) 8117c478bd9Sstevel@tonic-gate { 8127c478bd9Sstevel@tonic-gate px_ib_t *ib_p = px_p->px_ib_p; 813b0fc0e77Sgovinda px_ino_t *ino_p; 814b0fc0e77Sgovinda px_ino_pil_t *ipil_p; 8157c478bd9Sstevel@tonic-gate px_ih_t *ih_p; 8167c478bd9Sstevel@tonic-gate int ret = DDI_FAILURE; 8177c478bd9Sstevel@tonic-gate 818b0fc0e77Sgovinda DBG(DBG_IB, px_p->px_dip, "px_ib_update_intr_state: %s%d " 819b0fc0e77Sgovinda "inum %x devino %x pil %x state %x\n", ddi_driver_name(rdip), 820b0fc0e77Sgovinda ddi_get_instance(rdip), inum, ino, pil, new_intr_state); 8217c478bd9Sstevel@tonic-gate 8227c478bd9Sstevel@tonic-gate mutex_enter(&ib_p->ib_ino_lst_mutex); 8237c478bd9Sstevel@tonic-gate 824b0fc0e77Sgovinda ino_p = px_ib_locate_ino(ib_p, ino); 825b0fc0e77Sgovinda if (ino_p && (ipil_p = px_ib_ino_locate_ipil(ino_p, pil))) { 826b0fc0e77Sgovinda if (ih_p = px_ib_intr_locate_ih(ipil_p, rdip, inum, rec_type, 82736fe4a92Segillett msg_code)) { 8287c478bd9Sstevel@tonic-gate ih_p->ih_intr_state = new_intr_state; 8297c478bd9Sstevel@tonic-gate ret = DDI_SUCCESS; 8307c478bd9Sstevel@tonic-gate } 8317c478bd9Sstevel@tonic-gate } 8327c478bd9Sstevel@tonic-gate 8337c478bd9Sstevel@tonic-gate mutex_exit(&ib_p->ib_ino_lst_mutex); 8347c478bd9Sstevel@tonic-gate return (ret); 8357c478bd9Sstevel@tonic-gate } 83669cd775fSschwartz 83769cd775fSschwartz 83809b1eac2SEvan Yan /* 83909b1eac2SEvan Yan * Get interrupt CPU for a given ino. 84009b1eac2SEvan Yan * Return info only for inos which are already mapped to devices. 84109b1eac2SEvan Yan */ 84209b1eac2SEvan Yan /*ARGSUSED*/ 84309b1eac2SEvan Yan int 84409b1eac2SEvan Yan px_ib_get_intr_target(px_t *px_p, devino_t ino, cpuid_t *cpu_id_p) 84509b1eac2SEvan Yan { 84609b1eac2SEvan Yan dev_info_t *dip = px_p->px_dip; 84709b1eac2SEvan Yan sysino_t sysino; 84809b1eac2SEvan Yan int ret; 84909b1eac2SEvan Yan 85009b1eac2SEvan Yan DBG(DBG_IB, px_p->px_dip, "px_ib_get_intr_target: devino %x\n", ino); 85109b1eac2SEvan Yan 85209b1eac2SEvan Yan /* Convert leaf-wide intr to system-wide intr */ 85309b1eac2SEvan Yan if (px_lib_intr_devino_to_sysino(dip, ino, &sysino) != DDI_SUCCESS) 85409b1eac2SEvan Yan return (DDI_FAILURE); 85509b1eac2SEvan Yan 85609b1eac2SEvan Yan ret = px_lib_intr_gettarget(dip, sysino, cpu_id_p); 85709b1eac2SEvan Yan 85809b1eac2SEvan Yan DBG(DBG_IB, px_p->px_dip, "px_ib_get_intr_target: cpu_id %x\n", 85909b1eac2SEvan Yan *cpu_id_p); 86009b1eac2SEvan Yan 86109b1eac2SEvan Yan return (ret); 86209b1eac2SEvan Yan } 86309b1eac2SEvan Yan 86409b1eac2SEvan Yan 86509b1eac2SEvan Yan /* 86609b1eac2SEvan Yan * Associate a new CPU with a given ino. 86709b1eac2SEvan Yan * Operate only on INOs which are already mapped to devices. 86809b1eac2SEvan Yan */ 86909b1eac2SEvan Yan int 87009b1eac2SEvan Yan px_ib_set_intr_target(px_t *px_p, devino_t ino, cpuid_t cpu_id) 87109b1eac2SEvan Yan { 87209b1eac2SEvan Yan dev_info_t *dip = px_p->px_dip; 87309b1eac2SEvan Yan cpuid_t old_cpu_id; 87409b1eac2SEvan Yan sysino_t sysino; 87509b1eac2SEvan Yan int ret = DDI_SUCCESS; 87609b1eac2SEvan Yan extern const int _ncpu; 87709b1eac2SEvan Yan extern cpu_t *cpu[]; 87809b1eac2SEvan Yan 87909b1eac2SEvan Yan DBG(DBG_IB, px_p->px_dip, "px_ib_set_intr_target: devino %x " 88009b1eac2SEvan Yan "cpu_id %x\n", ino, cpu_id); 88109b1eac2SEvan Yan 88209b1eac2SEvan Yan mutex_enter(&cpu_lock); 88309b1eac2SEvan Yan 88409b1eac2SEvan Yan /* Convert leaf-wide intr to system-wide intr */ 88509b1eac2SEvan Yan if (px_lib_intr_devino_to_sysino(dip, ino, &sysino) != DDI_SUCCESS) { 88609b1eac2SEvan Yan ret = DDI_FAILURE; 88709b1eac2SEvan Yan goto done; 88809b1eac2SEvan Yan } 88909b1eac2SEvan Yan 89009b1eac2SEvan Yan if (px_lib_intr_gettarget(dip, sysino, &old_cpu_id) != DDI_SUCCESS) { 89109b1eac2SEvan Yan ret = DDI_FAILURE; 89209b1eac2SEvan Yan goto done; 89309b1eac2SEvan Yan } 89409b1eac2SEvan Yan 89509b1eac2SEvan Yan /* 89609b1eac2SEvan Yan * Get lock, validate cpu and write it. 89709b1eac2SEvan Yan */ 89809b1eac2SEvan Yan if ((cpu_id < _ncpu) && (cpu[cpu_id] && cpu_is_online(cpu[cpu_id]))) { 89909b1eac2SEvan Yan DBG(DBG_IB, dip, "px_ib_set_intr_target: Enabling CPU %d\n", 90009b1eac2SEvan Yan cpu_id); 90109b1eac2SEvan Yan px_ib_intr_dist_en(dip, cpu_id, ino, B_TRUE); 90209b1eac2SEvan Yan px_ib_log_new_cpu(px_p->px_ib_p, old_cpu_id, cpu_id, ino); 90309b1eac2SEvan Yan } else { /* Invalid cpu */ 90409b1eac2SEvan Yan DBG(DBG_IB, dip, "px_ib_set_intr_target: Invalid cpuid %x\n", 90509b1eac2SEvan Yan cpu_id); 90609b1eac2SEvan Yan ret = DDI_EINVAL; 90709b1eac2SEvan Yan } 90809b1eac2SEvan Yan 90909b1eac2SEvan Yan done: 91009b1eac2SEvan Yan mutex_exit(&cpu_lock); 91109b1eac2SEvan Yan return (ret); 91209b1eac2SEvan Yan } 91309b1eac2SEvan Yan 91409b1eac2SEvan Yan hrtime_t px_ib_msix_retarget_timeout = 120ll * NANOSEC; /* 120 seconds */ 91509b1eac2SEvan Yan 91609b1eac2SEvan Yan /* 91709b1eac2SEvan Yan * Associate a new CPU with a given MSI/X. 91809b1eac2SEvan Yan * Operate only on MSI/Xs which are already mapped to devices. 91909b1eac2SEvan Yan */ 92009b1eac2SEvan Yan int 92109b1eac2SEvan Yan px_ib_set_msix_target(px_t *px_p, ddi_intr_handle_impl_t *hdlp, 92209b1eac2SEvan Yan msinum_t msi_num, cpuid_t cpu_id) 92309b1eac2SEvan Yan { 92409b1eac2SEvan Yan px_ib_t *ib_p = px_p->px_ib_p; 92509b1eac2SEvan Yan px_msi_state_t *msi_state_p = &px_p->px_ib_p->ib_msi_state; 92609b1eac2SEvan Yan dev_info_t *dip = px_p->px_dip; 92709b1eac2SEvan Yan dev_info_t *rdip = hdlp->ih_dip; 92809b1eac2SEvan Yan msiqid_t msiq_id, old_msiq_id; 92909b1eac2SEvan Yan pci_msi_state_t msi_state; 93009b1eac2SEvan Yan msiq_rec_type_t msiq_rec_type; 93109b1eac2SEvan Yan msi_type_t msi_type; 93209b1eac2SEvan Yan px_ino_t *ino_p; 93309b1eac2SEvan Yan px_ih_t *ih_p, *old_ih_p; 93409b1eac2SEvan Yan cpuid_t old_cpu_id; 93509b1eac2SEvan Yan hrtime_t start_time, end_time; 93609b1eac2SEvan Yan int ret = DDI_SUCCESS; 93709b1eac2SEvan Yan extern const int _ncpu; 93809b1eac2SEvan Yan extern cpu_t *cpu[]; 93909b1eac2SEvan Yan 94009b1eac2SEvan Yan DBG(DBG_IB, dip, "px_ib_set_msix_target: msi_num %x new cpu_id %x\n", 94109b1eac2SEvan Yan msi_num, cpu_id); 94209b1eac2SEvan Yan 94309b1eac2SEvan Yan mutex_enter(&cpu_lock); 94409b1eac2SEvan Yan 94509b1eac2SEvan Yan /* Check for MSI64 support */ 94609b1eac2SEvan Yan if ((hdlp->ih_cap & DDI_INTR_FLAG_MSI64) && msi_state_p->msi_addr64) { 94709b1eac2SEvan Yan msiq_rec_type = MSI64_REC; 94809b1eac2SEvan Yan msi_type = MSI64_TYPE; 94909b1eac2SEvan Yan } else { 95009b1eac2SEvan Yan msiq_rec_type = MSI32_REC; 95109b1eac2SEvan Yan msi_type = MSI32_TYPE; 95209b1eac2SEvan Yan } 95309b1eac2SEvan Yan 95409b1eac2SEvan Yan if ((ret = px_lib_msi_getmsiq(dip, msi_num, 95509b1eac2SEvan Yan &old_msiq_id)) != DDI_SUCCESS) { 95609b1eac2SEvan Yan 95709b1eac2SEvan Yan mutex_exit(&cpu_lock); 95809b1eac2SEvan Yan return (ret); 95909b1eac2SEvan Yan } 96009b1eac2SEvan Yan 96109b1eac2SEvan Yan DBG(DBG_IB, dip, "px_ib_set_msix_target: current msiq 0x%x\n", 96209b1eac2SEvan Yan old_msiq_id); 96309b1eac2SEvan Yan 96409b1eac2SEvan Yan if ((ret = px_ib_get_intr_target(px_p, 96509b1eac2SEvan Yan px_msiqid_to_devino(px_p, old_msiq_id), 96609b1eac2SEvan Yan &old_cpu_id)) != DDI_SUCCESS) { 96709b1eac2SEvan Yan 96809b1eac2SEvan Yan mutex_exit(&cpu_lock); 96909b1eac2SEvan Yan return (ret); 97009b1eac2SEvan Yan } 97109b1eac2SEvan Yan 97209b1eac2SEvan Yan DBG(DBG_IB, dip, "px_ib_set_msix_target: current cpuid 0x%x\n", 97309b1eac2SEvan Yan old_cpu_id); 97409b1eac2SEvan Yan 97509b1eac2SEvan Yan if (cpu_id == old_cpu_id) { 97609b1eac2SEvan Yan 97709b1eac2SEvan Yan mutex_exit(&cpu_lock); 97809b1eac2SEvan Yan return (DDI_SUCCESS); 97909b1eac2SEvan Yan } 98009b1eac2SEvan Yan 98109b1eac2SEvan Yan /* 98209b1eac2SEvan Yan * Get lock, validate cpu and write it. 98309b1eac2SEvan Yan */ 98409b1eac2SEvan Yan if (!((cpu_id < _ncpu) && (cpu[cpu_id] && 98509b1eac2SEvan Yan cpu_is_online(cpu[cpu_id])))) { 98609b1eac2SEvan Yan /* Invalid cpu */ 98709b1eac2SEvan Yan DBG(DBG_IB, dip, "px_ib_set_msix_target: Invalid cpuid %x\n", 98809b1eac2SEvan Yan cpu_id); 98909b1eac2SEvan Yan 99009b1eac2SEvan Yan mutex_exit(&cpu_lock); 99109b1eac2SEvan Yan return (DDI_EINVAL); 99209b1eac2SEvan Yan } 99309b1eac2SEvan Yan 99409b1eac2SEvan Yan DBG(DBG_IB, dip, "px_ib_set_msix_target: Enabling CPU %d\n", cpu_id); 99509b1eac2SEvan Yan 99609b1eac2SEvan Yan if ((ret = px_add_msiq_intr(dip, rdip, hdlp, 99709b1eac2SEvan Yan msiq_rec_type, msi_num, cpu_id, &msiq_id)) != DDI_SUCCESS) { 99809b1eac2SEvan Yan DBG(DBG_IB, dip, "px_ib_set_msix_target: Add MSI handler " 99909b1eac2SEvan Yan "failed, rdip 0x%p msi 0x%x\n", rdip, msi_num); 100009b1eac2SEvan Yan 100109b1eac2SEvan Yan mutex_exit(&cpu_lock); 100209b1eac2SEvan Yan return (ret); 100309b1eac2SEvan Yan } 100409b1eac2SEvan Yan 100509b1eac2SEvan Yan if ((ret = px_lib_msi_setmsiq(dip, msi_num, 100609b1eac2SEvan Yan msiq_id, msi_type)) != DDI_SUCCESS) { 1007d17daf0bSScott Carter, SD IOSW mutex_exit(&cpu_lock); 1008d17daf0bSScott Carter, SD IOSW 100909b1eac2SEvan Yan (void) px_rem_msiq_intr(dip, rdip, 101009b1eac2SEvan Yan hdlp, msiq_rec_type, msi_num, msiq_id); 101109b1eac2SEvan Yan 101209b1eac2SEvan Yan return (ret); 101309b1eac2SEvan Yan } 101409b1eac2SEvan Yan 101509b1eac2SEvan Yan if ((ret = px_ib_update_intr_state(px_p, rdip, hdlp->ih_inum, 101609b1eac2SEvan Yan px_msiqid_to_devino(px_p, msiq_id), hdlp->ih_pri, 101709b1eac2SEvan Yan PX_INTR_STATE_ENABLE, msiq_rec_type, msi_num)) != DDI_SUCCESS) { 1018d17daf0bSScott Carter, SD IOSW mutex_exit(&cpu_lock); 1019d17daf0bSScott Carter, SD IOSW 102009b1eac2SEvan Yan (void) px_rem_msiq_intr(dip, rdip, 102109b1eac2SEvan Yan hdlp, msiq_rec_type, msi_num, msiq_id); 102209b1eac2SEvan Yan 102309b1eac2SEvan Yan return (ret); 102409b1eac2SEvan Yan } 102509b1eac2SEvan Yan 102609b1eac2SEvan Yan mutex_exit(&cpu_lock); 1027d17daf0bSScott Carter, SD IOSW 1028d17daf0bSScott Carter, SD IOSW /* 1029d17daf0bSScott Carter, SD IOSW * Remove the old handler, but first ensure it is finished. 1030d17daf0bSScott Carter, SD IOSW * 1031d17daf0bSScott Carter, SD IOSW * Each handler sets its PENDING flag before it clears the MSI state. 1032d17daf0bSScott Carter, SD IOSW * Then it clears that flag when finished. If a re-target occurs while 1033d17daf0bSScott Carter, SD IOSW * the MSI state is DELIVERED, then it is not yet known which of the 1034d17daf0bSScott Carter, SD IOSW * two handlers will take the interrupt. So the re-target operation 1035d17daf0bSScott Carter, SD IOSW * sets a RETARGET flag on both handlers in that case. Monitoring both 1036d17daf0bSScott Carter, SD IOSW * flags on both handlers then determines when the old handler can be 1037d17daf0bSScott Carter, SD IOSW * be safely removed. 1038d17daf0bSScott Carter, SD IOSW */ 103909b1eac2SEvan Yan mutex_enter(&ib_p->ib_ino_lst_mutex); 104009b1eac2SEvan Yan 104109b1eac2SEvan Yan ino_p = px_ib_locate_ino(ib_p, px_msiqid_to_devino(px_p, old_msiq_id)); 104209b1eac2SEvan Yan old_ih_p = px_ib_intr_locate_ih(px_ib_ino_locate_ipil(ino_p, 104309b1eac2SEvan Yan hdlp->ih_pri), rdip, hdlp->ih_inum, msiq_rec_type, msi_num); 104409b1eac2SEvan Yan 104509b1eac2SEvan Yan ino_p = px_ib_locate_ino(ib_p, px_msiqid_to_devino(px_p, msiq_id)); 104609b1eac2SEvan Yan ih_p = px_ib_intr_locate_ih(px_ib_ino_locate_ipil(ino_p, hdlp->ih_pri), 104709b1eac2SEvan Yan rdip, hdlp->ih_inum, msiq_rec_type, msi_num); 104809b1eac2SEvan Yan 104909b1eac2SEvan Yan if ((ret = px_lib_msi_getstate(dip, msi_num, 105009b1eac2SEvan Yan &msi_state)) != DDI_SUCCESS) { 105109b1eac2SEvan Yan (void) px_rem_msiq_intr(dip, rdip, 105209b1eac2SEvan Yan hdlp, msiq_rec_type, msi_num, msiq_id); 105309b1eac2SEvan Yan 105409b1eac2SEvan Yan mutex_exit(&ib_p->ib_ino_lst_mutex); 105509b1eac2SEvan Yan return (ret); 105609b1eac2SEvan Yan } 105709b1eac2SEvan Yan 1058d17daf0bSScott Carter, SD IOSW if (msi_state == PCI_MSI_STATE_DELIVERED) { 1059d17daf0bSScott Carter, SD IOSW ih_p->ih_intr_flags |= PX_INTR_RETARGET; 1060d17daf0bSScott Carter, SD IOSW old_ih_p->ih_intr_flags |= PX_INTR_RETARGET; 1061d17daf0bSScott Carter, SD IOSW } 106209b1eac2SEvan Yan 106309b1eac2SEvan Yan start_time = gethrtime(); 1064d17daf0bSScott Carter, SD IOSW while (((ih_p->ih_intr_flags & PX_INTR_RETARGET) && 1065d17daf0bSScott Carter, SD IOSW (old_ih_p->ih_intr_flags & PX_INTR_RETARGET)) || 1066d17daf0bSScott Carter, SD IOSW (old_ih_p->ih_intr_flags & PX_INTR_PENDING)) { 106709b1eac2SEvan Yan 106809b1eac2SEvan Yan /* Wait for one second */ 106909b1eac2SEvan Yan delay(drv_usectohz(1000000)); 1070d17daf0bSScott Carter, SD IOSW 1071d17daf0bSScott Carter, SD IOSW end_time = gethrtime() - start_time; 1072d17daf0bSScott Carter, SD IOSW if (end_time > px_ib_msix_retarget_timeout) { 1073d17daf0bSScott Carter, SD IOSW cmn_err(CE_WARN, "MSIX retarget %x is not completed, " 1074d17daf0bSScott Carter, SD IOSW "even after waiting %llx ticks\n", 1075d17daf0bSScott Carter, SD IOSW msi_num, end_time); 1076d17daf0bSScott Carter, SD IOSW break; 107709b1eac2SEvan Yan } 1078d17daf0bSScott Carter, SD IOSW } 1079d17daf0bSScott Carter, SD IOSW 1080d17daf0bSScott Carter, SD IOSW ih_p->ih_intr_flags &= ~(PX_INTR_RETARGET); 108109b1eac2SEvan Yan 108209b1eac2SEvan Yan mutex_exit(&ib_p->ib_ino_lst_mutex); 108309b1eac2SEvan Yan 108409b1eac2SEvan Yan ret = px_rem_msiq_intr(dip, rdip, 108509b1eac2SEvan Yan hdlp, msiq_rec_type, msi_num, old_msiq_id); 108609b1eac2SEvan Yan 108709b1eac2SEvan Yan return (ret); 108809b1eac2SEvan Yan } 108909b1eac2SEvan Yan 109009b1eac2SEvan Yan 109169cd775fSschwartz static void 109269cd775fSschwartz px_fill_in_intr_devs(pcitool_intr_dev_t *dev, char *driver_name, 109369cd775fSschwartz char *path_name, int instance) 109469cd775fSschwartz { 1095*5963c4f9SRichard Lowe (void) strlcpy(dev->driver_name, driver_name, MAXMODCONFNAME); 1096*5963c4f9SRichard Lowe (void) strlcpy(dev->path, path_name, MAXPATHLEN); 109769cd775fSschwartz dev->dev_inst = instance; 109869cd775fSschwartz } 109969cd775fSschwartz 110069cd775fSschwartz 110169cd775fSschwartz /* 110269cd775fSschwartz * Return the dips or number of dips associated with a given interrupt block. 110369cd775fSschwartz * Size of dips array arg is passed in as dips_ret arg. 110469cd775fSschwartz * Number of dips returned is returned in dips_ret arg. 110569cd775fSschwartz * Array of dips gets returned in the dips argument. 110669cd775fSschwartz * Function returns number of dips existing for the given interrupt block. 110769cd775fSschwartz * 110869cd775fSschwartz * Note: this function assumes an enabled/valid INO, which is why it returns 110969cd775fSschwartz * the px node and (Internal) when it finds no other devices (and *devs_ret > 0) 111069cd775fSschwartz */ 111169cd775fSschwartz uint8_t 111209b1eac2SEvan Yan pxtool_ib_get_ino_devs(px_t *px_p, uint32_t ino, uint32_t msi_num, 111309b1eac2SEvan Yan uint8_t *devs_ret, pcitool_intr_dev_t *devs) 111469cd775fSschwartz { 111569cd775fSschwartz px_ib_t *ib_p = px_p->px_ib_p; 1116b0fc0e77Sgovinda px_ino_t *ino_p; 1117b0fc0e77Sgovinda px_ino_pil_t *ipil_p; 111869cd775fSschwartz px_ih_t *ih_p; 111969cd775fSschwartz uint32_t num_devs = 0; 112069cd775fSschwartz char pathname[MAXPATHLEN]; 1121b0fc0e77Sgovinda int i, j; 112269cd775fSschwartz 112369cd775fSschwartz mutex_enter(&ib_p->ib_ino_lst_mutex); 112469cd775fSschwartz ino_p = px_ib_locate_ino(ib_p, ino); 112569cd775fSschwartz if (ino_p != NULL) { 1126b0fc0e77Sgovinda for (j = 0, ipil_p = ino_p->ino_ipil_p; ipil_p; 1127b0fc0e77Sgovinda ipil_p = ipil_p->ipil_next_p) { 1128b0fc0e77Sgovinda num_devs += ipil_p->ipil_ih_size; 1129b0fc0e77Sgovinda 1130b0fc0e77Sgovinda for (i = 0, ih_p = ipil_p->ipil_ih_head; 1131b0fc0e77Sgovinda ((i < ipil_p->ipil_ih_size) && (i < *devs_ret)); 1132b0fc0e77Sgovinda i++, j++, ih_p = ih_p->ih_next) { 113369cd775fSschwartz (void) ddi_pathname(ih_p->ih_dip, pathname); 113409b1eac2SEvan Yan 113509b1eac2SEvan Yan if (ih_p->ih_msg_code == msi_num) { 113609b1eac2SEvan Yan num_devs = *devs_ret = 1; 113709b1eac2SEvan Yan px_fill_in_intr_devs(&devs[0], 113809b1eac2SEvan Yan (char *)ddi_driver_name( 113909b1eac2SEvan Yan ih_p->ih_dip), pathname, 114009b1eac2SEvan Yan ddi_get_instance(ih_p->ih_dip)); 114109b1eac2SEvan Yan goto done; 114209b1eac2SEvan Yan } 114309b1eac2SEvan Yan 114409b1eac2SEvan Yan px_fill_in_intr_devs(&devs[j], 1145b0fc0e77Sgovinda (char *)ddi_driver_name(ih_p->ih_dip), 1146b0fc0e77Sgovinda pathname, ddi_get_instance(ih_p->ih_dip)); 114769cd775fSschwartz } 1148b0fc0e77Sgovinda } 114969cd775fSschwartz 1150b0fc0e77Sgovinda *devs_ret = j; 115169cd775fSschwartz } else if (*devs_ret > 0) { 115269cd775fSschwartz (void) ddi_pathname(px_p->px_dip, pathname); 115369cd775fSschwartz strcat(pathname, " (Internal)"); 115469cd775fSschwartz px_fill_in_intr_devs(&devs[0], 115569cd775fSschwartz (char *)ddi_driver_name(px_p->px_dip), pathname, 115669cd775fSschwartz ddi_get_instance(px_p->px_dip)); 115769cd775fSschwartz num_devs = *devs_ret = 1; 115869cd775fSschwartz } 115969cd775fSschwartz 116009b1eac2SEvan Yan done: 116169cd775fSschwartz mutex_exit(&ib_p->ib_ino_lst_mutex); 116269cd775fSschwartz 116369cd775fSschwartz return (num_devs); 116469cd775fSschwartz } 116569cd775fSschwartz 116669cd775fSschwartz 116709b1eac2SEvan Yan int 116809b1eac2SEvan Yan pxtool_ib_get_msi_info(px_t *px_p, devino_t ino, msinum_t msi_num, 116909b1eac2SEvan Yan ddi_intr_handle_impl_t *hdlp) 117009b1eac2SEvan Yan { 117109b1eac2SEvan Yan px_ib_t *ib_p = px_p->px_ib_p; 117209b1eac2SEvan Yan px_ino_t *ino_p; 117309b1eac2SEvan Yan px_ino_pil_t *ipil_p; 117409b1eac2SEvan Yan px_ih_t *ih_p; 117509b1eac2SEvan Yan int i; 117609b1eac2SEvan Yan 117709b1eac2SEvan Yan mutex_enter(&ib_p->ib_ino_lst_mutex); 117809b1eac2SEvan Yan 117909b1eac2SEvan Yan if ((ino_p = px_ib_locate_ino(ib_p, ino)) == NULL) { 118009b1eac2SEvan Yan mutex_exit(&ib_p->ib_ino_lst_mutex); 118109b1eac2SEvan Yan return (DDI_FAILURE); 118209b1eac2SEvan Yan } 118309b1eac2SEvan Yan 118409b1eac2SEvan Yan for (ipil_p = ino_p->ino_ipil_p; ipil_p; 118509b1eac2SEvan Yan ipil_p = ipil_p->ipil_next_p) { 118609b1eac2SEvan Yan for (i = 0, ih_p = ipil_p->ipil_ih_head; 118709b1eac2SEvan Yan ((i < ipil_p->ipil_ih_size) && ih_p); 118809b1eac2SEvan Yan i++, ih_p = ih_p->ih_next) { 118909b1eac2SEvan Yan 119009b1eac2SEvan Yan if (ih_p->ih_msg_code != msi_num) 119109b1eac2SEvan Yan continue; 119209b1eac2SEvan Yan 119309b1eac2SEvan Yan hdlp->ih_dip = ih_p->ih_dip; 119409b1eac2SEvan Yan hdlp->ih_inum = ih_p->ih_inum; 119509b1eac2SEvan Yan hdlp->ih_cb_func = ih_p->ih_handler; 119609b1eac2SEvan Yan hdlp->ih_cb_arg1 = ih_p->ih_handler_arg1; 119709b1eac2SEvan Yan hdlp->ih_cb_arg2 = ih_p->ih_handler_arg2; 119809b1eac2SEvan Yan if (ih_p->ih_rec_type == MSI64_REC) 119909b1eac2SEvan Yan hdlp->ih_cap = DDI_INTR_FLAG_MSI64; 120009b1eac2SEvan Yan hdlp->ih_pri = ipil_p->ipil_pil; 120109b1eac2SEvan Yan hdlp->ih_ver = DDI_INTR_VERSION; 120209b1eac2SEvan Yan 120309b1eac2SEvan Yan mutex_exit(&ib_p->ib_ino_lst_mutex); 120409b1eac2SEvan Yan return (DDI_SUCCESS); 120509b1eac2SEvan Yan } 120609b1eac2SEvan Yan } 120709b1eac2SEvan Yan 120809b1eac2SEvan Yan mutex_exit(&ib_p->ib_ino_lst_mutex); 120909b1eac2SEvan Yan return (DDI_FAILURE); 121009b1eac2SEvan Yan } 121109b1eac2SEvan Yan 121244bb982bSgovinda void 121309b1eac2SEvan Yan px_ib_log_new_cpu(px_ib_t *ib_p, cpuid_t old_cpu_id, cpuid_t new_cpu_id, 121469cd775fSschwartz uint32_t ino) 121569cd775fSschwartz { 1216b0fc0e77Sgovinda px_ino_t *ino_p; 1217b0fc0e77Sgovinda px_ino_pil_t *ipil_p; 1218b0fc0e77Sgovinda px_ih_t *ih_p; 1219b0fc0e77Sgovinda int i; 122069cd775fSschwartz 122169cd775fSschwartz mutex_enter(&ib_p->ib_ino_lst_mutex); 122269cd775fSschwartz 122369cd775fSschwartz /* Log in OS data structures the new CPU. */ 1224b0fc0e77Sgovinda if (ino_p = px_ib_locate_ino(ib_p, ino)) { 122569cd775fSschwartz 122669cd775fSschwartz /* Log in OS data structures the new CPU. */ 122769cd775fSschwartz ino_p->ino_cpuid = new_cpu_id; 122869cd775fSschwartz 1229b0fc0e77Sgovinda for (ipil_p = ino_p->ino_ipil_p; ipil_p; 1230b0fc0e77Sgovinda ipil_p = ipil_p->ipil_next_p) { 1231b0fc0e77Sgovinda for (i = 0, ih_p = ipil_p->ipil_ih_head; 1232b0fc0e77Sgovinda (i < ipil_p->ipil_ih_size); 1233b0fc0e77Sgovinda i++, ih_p = ih_p->ih_next) { 1234b0fc0e77Sgovinda /* 1235b0fc0e77Sgovinda * Account for any residual time 1236b0fc0e77Sgovinda * to be logged for old cpu. 1237b0fc0e77Sgovinda */ 1238b0fc0e77Sgovinda px_ib_cpu_ticks_to_ih_nsec(ib_p, 1239b0fc0e77Sgovinda ih_p, old_cpu_id); 1240b0fc0e77Sgovinda } 1241b0fc0e77Sgovinda } 124269cd775fSschwartz } 124369cd775fSschwartz 124469cd775fSschwartz mutex_exit(&ib_p->ib_ino_lst_mutex); 124569cd775fSschwartz } 1246