1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * Kstat support for X86 PCI driver 30 */ 31 32 #include <sys/conf.h> 33 #include <sys/mach_intr.h> 34 #include <sys/psm.h> 35 #include <sys/clock.h> 36 #include <sys/apic.h> 37 #include <io/pci/pci_var.h> 38 39 typedef struct pci_kstat_private { 40 ddi_intr_handle_impl_t *hdlp; 41 dev_info_t *rootnex_dip; 42 } pci_kstat_private_t; 43 44 static struct { 45 kstat_named_t ihks_name; 46 kstat_named_t ihks_type; 47 kstat_named_t ihks_cpu; 48 kstat_named_t ihks_pil; 49 kstat_named_t ihks_time; 50 kstat_named_t ihks_ino; 51 kstat_named_t ihks_cookie; 52 kstat_named_t ihks_devpath; 53 kstat_named_t ihks_buspath; 54 } pci_ks_template = { 55 { "name", KSTAT_DATA_CHAR }, 56 { "type", KSTAT_DATA_CHAR }, 57 { "cpu", KSTAT_DATA_UINT64 }, 58 { "pil", KSTAT_DATA_UINT64 }, 59 { "time", KSTAT_DATA_UINT64 }, 60 { "ino", KSTAT_DATA_UINT64 }, 61 { "cookie", KSTAT_DATA_UINT64 }, 62 { "devpath", KSTAT_DATA_STRING }, 63 { "buspath", KSTAT_DATA_STRING }, 64 }; 65 66 static char ih_devpath[MAXPATHLEN]; 67 static char ih_buspath[MAXPATHLEN]; 68 static uint32_t pci_ks_inst; 69 static kmutex_t pci_ks_template_lock; 70 71 /*ARGSUSED*/ 72 static int 73 pci_ih_ks_update(kstat_t *ksp, int rw) 74 { 75 pci_kstat_private_t *private_data = 76 (pci_kstat_private_t *)ksp->ks_private; 77 dev_info_t *rootnex_dip = private_data->rootnex_dip; 78 ddi_intr_handle_impl_t *ih_p = private_data->hdlp; 79 dev_info_t *dip = ih_p->ih_dip; 80 int maxlen = sizeof (pci_ks_template.ihks_name.value.c); 81 apic_get_intr_t intrinfo; 82 83 (void) snprintf(pci_ks_template.ihks_name.value.c, maxlen, "%s%d", 84 ddi_driver_name(dip), ddi_get_instance(dip)); 85 (void) ddi_pathname(dip, ih_devpath); 86 (void) ddi_pathname(rootnex_dip, ih_buspath); 87 kstat_named_setstr(&pci_ks_template.ihks_devpath, ih_devpath); 88 kstat_named_setstr(&pci_ks_template.ihks_buspath, ih_buspath); 89 90 /* 91 * ih_p->ih_vector really has an IRQ. Ask pci_get_intr_from_vecirq to 92 * return a vector since that's what PCItool will require intrd to use. 93 * 94 * PCItool will change the CPU routing of the IRQ that vector maps to. 95 * 96 * Note that although possibly multiple vectors can map to an IRQ, the 97 * vector returned below will always be the same for a given IRQ 98 * specified, and so all kstats for a given IRQ will report the same 99 * value for the ino field. 100 * 101 * --- 102 * 103 * Check for the enabled state, since kstats are set up when the ISR is 104 * added, not enabled. There may be a period where interrupts are not 105 * enabled even though they may have been added. 106 * 107 * It is also possible that the vector is for a dummy interrupt. 108 * pci_get_intr_from_vecirq will return failure in this case. 109 */ 110 intrinfo.avgi_cpu_id = 0; /* In case pci_get_intr_from_vecirq fails */ 111 intrinfo.avgi_req_flags = PSMGI_REQ_CPUID | PSMGI_REQ_VECTOR; 112 if ((ih_p->ih_state != DDI_IHDL_STATE_ENABLE) || 113 (pci_get_intr_from_vecirq(&intrinfo, ih_p->ih_vector, IS_IRQ) != 114 DDI_SUCCESS) || 115 (intrinfo.avgi_cpu_id & PSMGI_CPU_FLAGS)) { 116 117 (void) strcpy(pci_ks_template.ihks_type.value.c, "disabled"); 118 pci_ks_template.ihks_pil.value.ui64 = 0; 119 pci_ks_template.ihks_time.value.ui64 = 0; 120 pci_ks_template.ihks_cookie.value.ui64 = 0; 121 pci_ks_template.ihks_cpu.value.ui64 = 0; 122 pci_ks_template.ihks_ino.value.ui64 = 0; 123 124 /* Interrupt is user-bound. Remove kstat. */ 125 if (intrinfo.avgi_cpu_id & PSMGI_CPU_FLAGS) 126 (void) taskq_dispatch(system_taskq, 127 (void (*)(void *))pci_kstat_delete, ksp, TQ_SLEEP); 128 129 return (0); 130 } 131 132 /* 133 * Interrupt is valid (not a dummy), not user-bound to a specific cpu, 134 * and enabled. Update kstat fields. 135 */ 136 switch (ih_p->ih_type) { 137 case DDI_INTR_TYPE_MSI: 138 (void) strcpy(pci_ks_template.ihks_type.value.c, "msi"); 139 break; 140 case DDI_INTR_TYPE_MSIX: 141 (void) strcpy(pci_ks_template.ihks_type.value.c, "msix"); 142 break; 143 default: 144 (void) strcpy(pci_ks_template.ihks_type.value.c, "fixed"); 145 break; 146 } 147 pci_ks_template.ihks_pil.value.ui64 = ih_p->ih_pri; 148 pci_ks_template.ihks_time.value.ui64 = 149 ((ihdl_plat_t *)ih_p->ih_private)->ip_ticks; 150 tsc_scalehrtime((hrtime_t *)&pci_ks_template.ihks_time.value.ui64); 151 pci_ks_template.ihks_cookie.value.ui64 = ih_p->ih_vector; 152 /* CPU won't be user bound at this point. */ 153 pci_ks_template.ihks_cpu.value.ui64 = intrinfo.avgi_cpu_id; 154 pci_ks_template.ihks_ino.value.ui64 = intrinfo.avgi_vector; 155 156 return (0); 157 } 158 159 160 void pci_kstat_create(kstat_t **kspp, dev_info_t *rootnex_dip, 161 ddi_intr_handle_impl_t *hdlp) 162 { 163 pci_kstat_private_t *private_data; 164 165 *kspp = kstat_create("pci_intrs", atomic_inc_32_nv(&pci_ks_inst), 166 _MODULE_NAME, "interrupts", KSTAT_TYPE_NAMED, 167 sizeof (pci_ks_template) / sizeof (kstat_named_t), 168 KSTAT_FLAG_VIRTUAL); 169 if (*kspp != NULL) { 170 171 private_data = 172 kmem_zalloc(sizeof (pci_kstat_private_t), KM_SLEEP); 173 private_data->hdlp = hdlp; 174 private_data->rootnex_dip = rootnex_dip; 175 176 (*kspp)->ks_private = private_data; 177 (*kspp)->ks_data_size += MAXPATHLEN * 2; 178 (*kspp)->ks_lock = &pci_ks_template_lock; 179 (*kspp)->ks_data = &pci_ks_template; 180 (*kspp)->ks_update = pci_ih_ks_update; 181 kstat_install(*kspp); 182 } 183 } 184 185 186 /* 187 * This function is invoked in two ways: 188 * - Thru taskq thread via pci_ih_ks_update to remove kstats for user-bound 189 * interrupts. 190 * - From the REMISR introp when an interrupt is being removed. 191 */ 192 void 193 pci_kstat_delete(kstat_t *ksp) 194 { 195 pci_kstat_private_t *kstat_private; 196 ddi_intr_handle_impl_t *hdlp; 197 198 if (ksp) { 199 kstat_private = ksp->ks_private; 200 hdlp = kstat_private->hdlp; 201 ((ihdl_plat_t *)hdlp->ih_private)->ip_ksp = NULL; 202 203 /* 204 * Delete the kstat before removing the private pointer, to 205 * prevent a kstat update from coming after private is freed. 206 */ 207 kstat_delete(ksp); 208 209 kmem_free(kstat_private, sizeof (pci_kstat_private_t)); 210 } 211 } 212