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