xref: /illumos-gate/usr/src/uts/i86pc/io/pci/pci_kstats.c (revision 1979231e)
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 2006 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 <io/pcplusmp/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 =
81 				    sizeof (pci_ks_template.ihks_name.value.c);
82 	apic_get_intr_t	intrinfo;
83 
84 	(void) snprintf(pci_ks_template.ihks_name.value.c, maxlen, "%s%d",
85 	    ddi_driver_name(dip), ddi_get_instance(dip));
86 	(void) ddi_pathname(dip, ih_devpath);
87 	(void) ddi_pathname(rootnex_dip, ih_buspath);
88 	kstat_named_setstr(&pci_ks_template.ihks_devpath, ih_devpath);
89 	kstat_named_setstr(&pci_ks_template.ihks_buspath, ih_buspath);
90 
91 	/*
92 	 * ih_p->ih_vector really has an IRQ.  Ask pci_get_intr_from_vecirq to
93 	 * return a vector since that's what PCItool will require intrd to use.
94 	 *
95 	 * PCItool will change the CPU routing of the IRQ that vector maps to.
96 	 *
97 	 * Note that although possibly multiple vectors can map to an IRQ, the
98 	 * vector returned below will always be the same for a given IRQ
99 	 * specified, and so all kstats for a given IRQ will report the same
100 	 * value for the ino field.
101 	 *
102 	 * ---
103 	 *
104 	 * Check for the enabled state, since kstats are set up when the ISR is
105 	 * added, not enabled.  There may be a period where interrupts are not
106 	 * enabled even though they may have been added.
107 	 *
108 	 * It is also possible that the vector is for a dummy interrupt.
109 	 * pci_get_intr_from_vecirq will return failure in this case.
110 	 */
111 	intrinfo.avgi_cpu_id = 0; /* In case pci_get_intr_from_vecirq fails */
112 	intrinfo.avgi_req_flags = PSMGI_REQ_CPUID | PSMGI_REQ_VECTOR;
113 	if ((ih_p->ih_state != DDI_IHDL_STATE_ENABLE) ||
114 	    (pci_get_intr_from_vecirq(&intrinfo,  ih_p->ih_vector, IS_IRQ) !=
115 		DDI_SUCCESS) ||
116 	    (intrinfo.avgi_cpu_id & PSMGI_CPU_FLAGS)) {
117 
118 		(void) strcpy(pci_ks_template.ihks_type.value.c, "disabled");
119 		pci_ks_template.ihks_pil.value.ui64 = 0;
120 		pci_ks_template.ihks_time.value.ui64 = 0;
121 		pci_ks_template.ihks_cookie.value.ui64 = 0;
122 		pci_ks_template.ihks_cpu.value.ui64 = 0;
123 		pci_ks_template.ihks_ino.value.ui64 = 0;
124 
125 		/* Interrupt is user-bound.  Remove kstat. */
126 		if (intrinfo.avgi_cpu_id & PSMGI_CPU_FLAGS)
127 			(void) taskq_dispatch(system_taskq,
128 			    (void (*)(void *))pci_kstat_delete, ksp, TQ_SLEEP);
129 
130 		return (0);
131 	}
132 
133 	/*
134 	 * Interrupt is valid (not a dummy), not user-bound to a specific cpu,
135 	 * and enabled.  Update kstat fields.
136 	 */
137 	(void) strcpy(pci_ks_template.ihks_type.value.c,
138 	    DDI_INTR_IS_MSI_OR_MSIX(ih_p->ih_type) ? "msi" : "fixed");
139 	pci_ks_template.ihks_pil.value.ui64 = ih_p->ih_pri;
140 	pci_ks_template.ihks_time.value.ui64 =
141 	    ((ihdl_plat_t *)ih_p->ih_private)->ip_ticks;
142 	tsc_scalehrtime((hrtime_t *)&pci_ks_template.ihks_time.value.ui64);
143 	pci_ks_template.ihks_cookie.value.ui64 = ih_p->ih_vector;
144 	/* CPU won't be user bound at this point. */
145 	pci_ks_template.ihks_cpu.value.ui64 = intrinfo.avgi_cpu_id;
146 	pci_ks_template.ihks_ino.value.ui64 = intrinfo.avgi_vector;
147 
148 	return (0);
149 }
150 
151 
152 void pci_kstat_create(kstat_t **kspp, dev_info_t *rootnex_dip,
153     ddi_intr_handle_impl_t *hdlp)
154 {
155 	pci_kstat_private_t *private_data;
156 
157 	*kspp = kstat_create("pci_intrs", atomic_inc_32_nv(&pci_ks_inst),
158 	    _MODULE_NAME, "interrupts", KSTAT_TYPE_NAMED,
159 	    sizeof (pci_ks_template) / sizeof (kstat_named_t),
160 	    KSTAT_FLAG_VIRTUAL);
161 	if (*kspp != NULL) {
162 
163 		private_data =
164 		    kmem_zalloc(sizeof (pci_kstat_private_t), KM_SLEEP);
165 		private_data->hdlp = hdlp;
166 		private_data->rootnex_dip = rootnex_dip;
167 
168 		(*kspp)->ks_private = private_data;
169 		(*kspp)->ks_data_size += MAXPATHLEN * 2;
170 		(*kspp)->ks_lock = &pci_ks_template_lock;
171 		(*kspp)->ks_data = &pci_ks_template;
172 		(*kspp)->ks_update = pci_ih_ks_update;
173 		kstat_install(*kspp);
174 	}
175 }
176 
177 
178 /*
179  * This function is invoked in two ways:
180  * - Thru taskq thread via pci_ih_ks_update to remove kstats for user-bound
181  *   interrupts.
182  * - From the REMISR introp when an interrupt is being removed.
183  */
184 void
185 pci_kstat_delete(kstat_t *ksp)
186 {
187 	pci_kstat_private_t	*kstat_private;
188 	ddi_intr_handle_impl_t	*hdlp;
189 
190 	if (ksp) {
191 		kstat_private = ksp->ks_private;
192 		hdlp = kstat_private->hdlp;
193 		((ihdl_plat_t *)hdlp->ih_private)->ip_ksp = NULL;
194 
195 		/*
196 		 * Delete the kstat before removing the private pointer, to
197 		 * prevent a kstat update from coming after private is freed.
198 		 */
199 		kstat_delete(ksp);
200 
201 		kmem_free(kstat_private, sizeof (pci_kstat_private_t));
202 	}
203 }
204