17a364d25Sschwartz /* 27a364d25Sschwartz * CDDL HEADER START 37a364d25Sschwartz * 47a364d25Sschwartz * The contents of this file are subject to the terms of the 5d48713b8Sesolom * Common Development and Distribution License (the "License"). 6d48713b8Sesolom * You may not use this file except in compliance with the License. 77a364d25Sschwartz * 87a364d25Sschwartz * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97a364d25Sschwartz * or http://www.opensolaris.org/os/licensing. 107a364d25Sschwartz * See the License for the specific language governing permissions 117a364d25Sschwartz * and limitations under the License. 127a364d25Sschwartz * 137a364d25Sschwartz * When distributing Covered Code, include this CDDL HEADER in each 147a364d25Sschwartz * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157a364d25Sschwartz * If applicable, add the following below this CDDL HEADER, with the 167a364d25Sschwartz * fields enclosed by brackets "[]" replaced with your own identifying 177a364d25Sschwartz * information: Portions Copyright [yyyy] [name of copyright owner] 187a364d25Sschwartz * 197a364d25Sschwartz * CDDL HEADER END 207a364d25Sschwartz */ 217a364d25Sschwartz /* 22*5cd376e8SJimmy Vetayases * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 237a364d25Sschwartz */ 247a364d25Sschwartz /* 257a364d25Sschwartz * Kstat support for X86 PCI driver 267a364d25Sschwartz */ 277a364d25Sschwartz 287a364d25Sschwartz #include <sys/conf.h> 297a364d25Sschwartz #include <sys/mach_intr.h> 307a364d25Sschwartz #include <sys/psm.h> 317a364d25Sschwartz #include <sys/clock.h> 32ae115bc7Smrj #include <sys/apic.h> 337a364d25Sschwartz #include <io/pci/pci_var.h> 347a364d25Sschwartz 357a364d25Sschwartz typedef struct pci_kstat_private { 367a364d25Sschwartz ddi_intr_handle_impl_t *hdlp; 377a364d25Sschwartz dev_info_t *rootnex_dip; 387a364d25Sschwartz } pci_kstat_private_t; 397a364d25Sschwartz 407a364d25Sschwartz static struct { 417a364d25Sschwartz kstat_named_t ihks_name; 427a364d25Sschwartz kstat_named_t ihks_type; 437a364d25Sschwartz kstat_named_t ihks_cpu; 447a364d25Sschwartz kstat_named_t ihks_pil; 457a364d25Sschwartz kstat_named_t ihks_time; 467a364d25Sschwartz kstat_named_t ihks_ino; 477a364d25Sschwartz kstat_named_t ihks_cookie; 487a364d25Sschwartz kstat_named_t ihks_devpath; 497a364d25Sschwartz kstat_named_t ihks_buspath; 507a364d25Sschwartz } pci_ks_template = { 517a364d25Sschwartz { "name", KSTAT_DATA_CHAR }, 527a364d25Sschwartz { "type", KSTAT_DATA_CHAR }, 537a364d25Sschwartz { "cpu", KSTAT_DATA_UINT64 }, 547a364d25Sschwartz { "pil", KSTAT_DATA_UINT64 }, 557a364d25Sschwartz { "time", KSTAT_DATA_UINT64 }, 567a364d25Sschwartz { "ino", KSTAT_DATA_UINT64 }, 577a364d25Sschwartz { "cookie", KSTAT_DATA_UINT64 }, 587a364d25Sschwartz { "devpath", KSTAT_DATA_STRING }, 597a364d25Sschwartz { "buspath", KSTAT_DATA_STRING }, 607a364d25Sschwartz }; 617a364d25Sschwartz 62d48713b8Sesolom static char ih_devpath[MAXPATHLEN]; 63d48713b8Sesolom static char ih_buspath[MAXPATHLEN]; 647a364d25Sschwartz static uint32_t pci_ks_inst; 657a364d25Sschwartz static kmutex_t pci_ks_template_lock; 667a364d25Sschwartz 677ff178cdSJimmy Vetayases extern int (*psm_intr_ops)(dev_info_t *, ddi_intr_handle_impl_t *, 687ff178cdSJimmy Vetayases psm_intr_op_t, int *); 697ff178cdSJimmy Vetayases 707a364d25Sschwartz /*ARGSUSED*/ 717a364d25Sschwartz static int 727a364d25Sschwartz pci_ih_ks_update(kstat_t *ksp, int rw) 737a364d25Sschwartz { 747a364d25Sschwartz pci_kstat_private_t *private_data = 757a364d25Sschwartz (pci_kstat_private_t *)ksp->ks_private; 767a364d25Sschwartz dev_info_t *rootnex_dip = private_data->rootnex_dip; 777ff178cdSJimmy Vetayases ddi_intr_handle_impl_t tmp_hdl, *ih_p = private_data->hdlp; 787a364d25Sschwartz dev_info_t *dip = ih_p->ih_dip; 792917a9c9Sschwartz int maxlen = sizeof (pci_ks_template.ihks_name.value.c); 807a364d25Sschwartz apic_get_intr_t intrinfo; 817a364d25Sschwartz 827a364d25Sschwartz (void) snprintf(pci_ks_template.ihks_name.value.c, maxlen, "%s%d", 837a364d25Sschwartz ddi_driver_name(dip), ddi_get_instance(dip)); 84e1d9f4e6Sschwartz (void) ddi_pathname(dip, ih_devpath); 85e1d9f4e6Sschwartz (void) ddi_pathname(rootnex_dip, ih_buspath); 86e1d9f4e6Sschwartz kstat_named_setstr(&pci_ks_template.ihks_devpath, ih_devpath); 87e1d9f4e6Sschwartz kstat_named_setstr(&pci_ks_template.ihks_buspath, ih_buspath); 887a364d25Sschwartz 897a364d25Sschwartz /* 907a364d25Sschwartz * Note that although possibly multiple vectors can map to an IRQ, the 917a364d25Sschwartz * vector returned below will always be the same for a given IRQ 927a364d25Sschwartz * specified, and so all kstats for a given IRQ will report the same 937a364d25Sschwartz * value for the ino field. 94e1d9f4e6Sschwartz * 95e1d9f4e6Sschwartz * --- 96e1d9f4e6Sschwartz * 97e1d9f4e6Sschwartz * Check for the enabled state, since kstats are set up when the ISR is 98e1d9f4e6Sschwartz * added, not enabled. There may be a period where interrupts are not 99e1d9f4e6Sschwartz * enabled even though they may have been added. 100e1d9f4e6Sschwartz * 101e1d9f4e6Sschwartz * It is also possible that the vector is for a dummy interrupt. 102e1d9f4e6Sschwartz * pci_get_intr_from_vecirq will return failure in this case. 1037a364d25Sschwartz */ 1047ff178cdSJimmy Vetayases bcopy(ih_p, &tmp_hdl, sizeof (ddi_intr_handle_impl_t)); 1057ff178cdSJimmy Vetayases tmp_hdl.ih_private = (void *)&intrinfo; 1067ff178cdSJimmy Vetayases intrinfo.avgi_cpu_id = 0; /* In case psm_intr_ops fails */ 1077a364d25Sschwartz intrinfo.avgi_req_flags = PSMGI_REQ_CPUID | PSMGI_REQ_VECTOR; 1087ff178cdSJimmy Vetayases intrinfo.avgi_req_flags |= PSMGI_INTRBY_DEFAULT; 1097ff178cdSJimmy Vetayases 110e1d9f4e6Sschwartz if ((ih_p->ih_state != DDI_IHDL_STATE_ENABLE) || 1117ff178cdSJimmy Vetayases ((*psm_intr_ops)(NULL, &tmp_hdl, PSM_INTR_OP_GET_INTR, NULL) != 112e1d9f4e6Sschwartz DDI_SUCCESS) || 113e1d9f4e6Sschwartz (intrinfo.avgi_cpu_id & PSMGI_CPU_FLAGS)) { 114e1d9f4e6Sschwartz 115e1d9f4e6Sschwartz (void) strcpy(pci_ks_template.ihks_type.value.c, "disabled"); 116e1d9f4e6Sschwartz pci_ks_template.ihks_pil.value.ui64 = 0; 117e1d9f4e6Sschwartz pci_ks_template.ihks_time.value.ui64 = 0; 118e1d9f4e6Sschwartz pci_ks_template.ihks_cookie.value.ui64 = 0; 1197a364d25Sschwartz pci_ks_template.ihks_cpu.value.ui64 = 0; 120e1d9f4e6Sschwartz pci_ks_template.ihks_ino.value.ui64 = 0; 121e1d9f4e6Sschwartz 122e1d9f4e6Sschwartz /* Interrupt is user-bound. Remove kstat. */ 123e1d9f4e6Sschwartz if (intrinfo.avgi_cpu_id & PSMGI_CPU_FLAGS) 124e1d9f4e6Sschwartz (void) taskq_dispatch(system_taskq, 125e1d9f4e6Sschwartz (void (*)(void *))pci_kstat_delete, ksp, TQ_SLEEP); 126e1d9f4e6Sschwartz 127e1d9f4e6Sschwartz return (0); 1287a364d25Sschwartz } 1297a364d25Sschwartz 130e1d9f4e6Sschwartz /* 131e1d9f4e6Sschwartz * Interrupt is valid (not a dummy), not user-bound to a specific cpu, 132e1d9f4e6Sschwartz * and enabled. Update kstat fields. 133e1d9f4e6Sschwartz */ 1342917a9c9Sschwartz switch (ih_p->ih_type) { 1352917a9c9Sschwartz case DDI_INTR_TYPE_MSI: 1362917a9c9Sschwartz (void) strcpy(pci_ks_template.ihks_type.value.c, "msi"); 1372917a9c9Sschwartz break; 1382917a9c9Sschwartz case DDI_INTR_TYPE_MSIX: 1392917a9c9Sschwartz (void) strcpy(pci_ks_template.ihks_type.value.c, "msix"); 1402917a9c9Sschwartz break; 1412917a9c9Sschwartz default: 1422917a9c9Sschwartz (void) strcpy(pci_ks_template.ihks_type.value.c, "fixed"); 1432917a9c9Sschwartz break; 1442917a9c9Sschwartz } 145e1d9f4e6Sschwartz pci_ks_template.ihks_pil.value.ui64 = ih_p->ih_pri; 146e1d9f4e6Sschwartz pci_ks_template.ihks_time.value.ui64 = 147e1d9f4e6Sschwartz ((ihdl_plat_t *)ih_p->ih_private)->ip_ticks; 148843e1988Sjohnlev scalehrtime((hrtime_t *)&pci_ks_template.ihks_time.value.ui64); 149e1d9f4e6Sschwartz pci_ks_template.ihks_cookie.value.ui64 = ih_p->ih_vector; 150e1d9f4e6Sschwartz /* CPU won't be user bound at this point. */ 151e1d9f4e6Sschwartz pci_ks_template.ihks_cpu.value.ui64 = intrinfo.avgi_cpu_id; 152e1d9f4e6Sschwartz pci_ks_template.ihks_ino.value.ui64 = intrinfo.avgi_vector; 1537a364d25Sschwartz 1547a364d25Sschwartz return (0); 1557a364d25Sschwartz } 1567a364d25Sschwartz 1577a364d25Sschwartz 1587a364d25Sschwartz void pci_kstat_create(kstat_t **kspp, dev_info_t *rootnex_dip, 1597a364d25Sschwartz ddi_intr_handle_impl_t *hdlp) 1607a364d25Sschwartz { 1617a364d25Sschwartz pci_kstat_private_t *private_data; 1627a364d25Sschwartz 1637a364d25Sschwartz *kspp = kstat_create("pci_intrs", atomic_inc_32_nv(&pci_ks_inst), 164d89fccd8Sschwartz _MODULE_NAME, "interrupts", KSTAT_TYPE_NAMED, 1657a364d25Sschwartz sizeof (pci_ks_template) / sizeof (kstat_named_t), 1667a364d25Sschwartz KSTAT_FLAG_VIRTUAL); 1677a364d25Sschwartz if (*kspp != NULL) { 1687a364d25Sschwartz 1697a364d25Sschwartz private_data = 1707a364d25Sschwartz kmem_zalloc(sizeof (pci_kstat_private_t), KM_SLEEP); 1717a364d25Sschwartz private_data->hdlp = hdlp; 1727a364d25Sschwartz private_data->rootnex_dip = rootnex_dip; 1737a364d25Sschwartz 1747a364d25Sschwartz (*kspp)->ks_private = private_data; 1757a364d25Sschwartz (*kspp)->ks_data_size += MAXPATHLEN * 2; 1767a364d25Sschwartz (*kspp)->ks_lock = &pci_ks_template_lock; 1777a364d25Sschwartz (*kspp)->ks_data = &pci_ks_template; 1787a364d25Sschwartz (*kspp)->ks_update = pci_ih_ks_update; 1797a364d25Sschwartz kstat_install(*kspp); 1807a364d25Sschwartz } 1817a364d25Sschwartz } 1827a364d25Sschwartz 183e1d9f4e6Sschwartz 184e1d9f4e6Sschwartz /* 185e1d9f4e6Sschwartz * This function is invoked in two ways: 186e1d9f4e6Sschwartz * - Thru taskq thread via pci_ih_ks_update to remove kstats for user-bound 187e1d9f4e6Sschwartz * interrupts. 188e1d9f4e6Sschwartz * - From the REMISR introp when an interrupt is being removed. 189e1d9f4e6Sschwartz */ 1907a364d25Sschwartz void 1917a364d25Sschwartz pci_kstat_delete(kstat_t *ksp) 1927a364d25Sschwartz { 1937a364d25Sschwartz pci_kstat_private_t *kstat_private; 194e1d9f4e6Sschwartz ddi_intr_handle_impl_t *hdlp; 1957a364d25Sschwartz 1967a364d25Sschwartz if (ksp) { 1977a364d25Sschwartz kstat_private = ksp->ks_private; 198e1d9f4e6Sschwartz hdlp = kstat_private->hdlp; 199e1d9f4e6Sschwartz ((ihdl_plat_t *)hdlp->ih_private)->ip_ksp = NULL; 2007a364d25Sschwartz 2017a364d25Sschwartz /* 2027a364d25Sschwartz * Delete the kstat before removing the private pointer, to 2037a364d25Sschwartz * prevent a kstat update from coming after private is freed. 2047a364d25Sschwartz */ 2057a364d25Sschwartz kstat_delete(ksp); 2067a364d25Sschwartz 2077a364d25Sschwartz kmem_free(kstat_private, sizeof (pci_kstat_private_t)); 2087a364d25Sschwartz } 2097a364d25Sschwartz } 210