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