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
pci_ih_ks_update(kstat_t * ksp,int rw)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
pci_kstat_create(kstat_t ** kspp,dev_info_t * rootnex_dip,ddi_intr_handle_impl_t * hdlp)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
pci_kstat_delete(kstat_t * ksp)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