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