xref: /illumos-gate/usr/src/uts/i86pc/io/pci/pci_kstats.c (revision 89b2a9fbeabf42fa54594df0e5927bcc50a07cc9)
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 	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