xref: /titanic_51/usr/src/uts/sun4v/io/iospc/iospc.c (revision 4df55fde49134f9735f84011f23a767c75e393c7)
1*4df55fdeSJanie Lu /*
2*4df55fdeSJanie Lu  * CDDL HEADER START
3*4df55fdeSJanie Lu  *
4*4df55fdeSJanie Lu  * The contents of this file are subject to the terms of the
5*4df55fdeSJanie Lu  * Common Development and Distribution License (the "License").
6*4df55fdeSJanie Lu  * You may not use this file except in compliance with the License.
7*4df55fdeSJanie Lu  *
8*4df55fdeSJanie Lu  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*4df55fdeSJanie Lu  * or http://www.opensolaris.org/os/licensing.
10*4df55fdeSJanie Lu  * See the License for the specific language governing permissions
11*4df55fdeSJanie Lu  * and limitations under the License.
12*4df55fdeSJanie Lu  *
13*4df55fdeSJanie Lu  * When distributing Covered Code, include this CDDL HEADER in each
14*4df55fdeSJanie Lu  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*4df55fdeSJanie Lu  * If applicable, add the following below this CDDL HEADER, with the
16*4df55fdeSJanie Lu  * fields enclosed by brackets "[]" replaced with your own identifying
17*4df55fdeSJanie Lu  * information: Portions Copyright [yyyy] [name of copyright owner]
18*4df55fdeSJanie Lu  *
19*4df55fdeSJanie Lu  * CDDL HEADER END
20*4df55fdeSJanie Lu  */
21*4df55fdeSJanie Lu 
22*4df55fdeSJanie Lu /*
23*4df55fdeSJanie Lu  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24*4df55fdeSJanie Lu  * Use is subject to license terms.
25*4df55fdeSJanie Lu  */
26*4df55fdeSJanie Lu 
27*4df55fdeSJanie Lu /*
28*4df55fdeSJanie Lu  * IO Performance Counter Driver
29*4df55fdeSJanie Lu  */
30*4df55fdeSJanie Lu 
31*4df55fdeSJanie Lu #include <sys/types.h>
32*4df55fdeSJanie Lu #include <sys/ddi.h>
33*4df55fdeSJanie Lu #include <sys/modctl.h>
34*4df55fdeSJanie Lu #include "iospc.h"
35*4df55fdeSJanie Lu 
36*4df55fdeSJanie Lu /* Debugging level. */
37*4df55fdeSJanie Lu #ifdef DEBUG
38*4df55fdeSJanie Lu int iospc_debug = 0;
39*4df55fdeSJanie Lu #endif /* DEBUG */
40*4df55fdeSJanie Lu 
41*4df55fdeSJanie Lu /* State structure anchor. */
42*4df55fdeSJanie Lu void *iospc_state_p;
43*4df55fdeSJanie Lu 
44*4df55fdeSJanie Lu static int iospc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
45*4df55fdeSJanie Lu static int iospc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
46*4df55fdeSJanie Lu static int iospc_create_name_kstat(iospc_grp_t *grp);
47*4df55fdeSJanie Lu static void iospc_delete_name_kstats(kstat_t **name_kstats_pp,
48*4df55fdeSJanie Lu     int num_kstats);
49*4df55fdeSJanie Lu static kstat_t *iospc_create_cntr_kstat(char *name, int dev_inst,
50*4df55fdeSJanie Lu     int (*update)(kstat_t *, int), iospc_ksinfo_t *ksinfop, int num_pics);
51*4df55fdeSJanie Lu static int iospc_kstat_update(kstat_t *ksp, int rw);
52*4df55fdeSJanie Lu static kstat_t *iospc_create_picN_kstat(char *mod_name, int pic,
53*4df55fdeSJanie Lu     uint64_t mask, int num_ev, iospc_event_t *ev_array);
54*4df55fdeSJanie Lu 
55*4df55fdeSJanie Lu iospc_grp_t **iospc_leaf_grps = NULL;
56*4df55fdeSJanie Lu int iospc_kstat_inited = 0;
57*4df55fdeSJanie Lu kmutex_t iospc_mutex;
58*4df55fdeSJanie Lu 
59*4df55fdeSJanie Lu static struct dev_ops iospc_ops = {
60*4df55fdeSJanie Lu 	DEVO_REV,
61*4df55fdeSJanie Lu 	0,
62*4df55fdeSJanie Lu 	nulldev,
63*4df55fdeSJanie Lu 	nulldev,
64*4df55fdeSJanie Lu 	nulldev,
65*4df55fdeSJanie Lu 	iospc_attach,
66*4df55fdeSJanie Lu 	iospc_detach,
67*4df55fdeSJanie Lu 	nodev,
68*4df55fdeSJanie Lu 	NULL,
69*4df55fdeSJanie Lu 	NULL,
70*4df55fdeSJanie Lu 	nodev
71*4df55fdeSJanie Lu };
72*4df55fdeSJanie Lu 
73*4df55fdeSJanie Lu extern struct mod_ops mod_driverops;
74*4df55fdeSJanie Lu 
75*4df55fdeSJanie Lu static struct modldrv md = {
76*4df55fdeSJanie Lu 	&mod_driverops,
77*4df55fdeSJanie Lu 	"IO Perf Counter Driver",
78*4df55fdeSJanie Lu 	&iospc_ops,
79*4df55fdeSJanie Lu };
80*4df55fdeSJanie Lu 
81*4df55fdeSJanie Lu static struct modlinkage ml = {
82*4df55fdeSJanie Lu 	MODREV_1,
83*4df55fdeSJanie Lu 	(void *)&md,
84*4df55fdeSJanie Lu 	NULL
85*4df55fdeSJanie Lu };
86*4df55fdeSJanie Lu 
87*4df55fdeSJanie Lu /*
88*4df55fdeSJanie Lu  * One-time module-wide initialization.
89*4df55fdeSJanie Lu  */
90*4df55fdeSJanie Lu int
91*4df55fdeSJanie Lu _init(void)
92*4df55fdeSJanie Lu {
93*4df55fdeSJanie Lu 	int rval;
94*4df55fdeSJanie Lu 
95*4df55fdeSJanie Lu 	/* Initialize per-leaf soft state pointer. */
96*4df55fdeSJanie Lu 	if ((rval = ddi_soft_state_init(&iospc_state_p,
97*4df55fdeSJanie Lu 	    sizeof (iospc_t), 1)) != DDI_SUCCESS)
98*4df55fdeSJanie Lu 		return (rval);
99*4df55fdeSJanie Lu 
100*4df55fdeSJanie Lu 	/* If all checks out, install the module. */
101*4df55fdeSJanie Lu 	if ((rval = mod_install(&ml)) != DDI_SUCCESS) {
102*4df55fdeSJanie Lu 		ddi_soft_state_fini(&iospc_state_p);
103*4df55fdeSJanie Lu 		return (rval);
104*4df55fdeSJanie Lu 	}
105*4df55fdeSJanie Lu 	mutex_init(&iospc_mutex, NULL, MUTEX_DRIVER, NULL);
106*4df55fdeSJanie Lu 	return (DDI_SUCCESS);
107*4df55fdeSJanie Lu }
108*4df55fdeSJanie Lu 
109*4df55fdeSJanie Lu /*
110*4df55fdeSJanie Lu  * One-time module-wide cleanup, after last detach is done.
111*4df55fdeSJanie Lu  */
112*4df55fdeSJanie Lu int
113*4df55fdeSJanie Lu _fini(void)
114*4df55fdeSJanie Lu {
115*4df55fdeSJanie Lu 	int rval;
116*4df55fdeSJanie Lu 
117*4df55fdeSJanie Lu 	/*
118*4df55fdeSJanie Lu 	 * Remove the module first as this operation is the only thing here
119*4df55fdeSJanie Lu 	 * which can fail.
120*4df55fdeSJanie Lu 	 */
121*4df55fdeSJanie Lu 	rval = mod_remove(&ml);
122*4df55fdeSJanie Lu 	if (rval != DDI_SUCCESS)
123*4df55fdeSJanie Lu 		return (rval);
124*4df55fdeSJanie Lu 
125*4df55fdeSJanie Lu 	if (iospc_leaf_grps != NULL) {
126*4df55fdeSJanie Lu 		iospc_kstat_fini();
127*4df55fdeSJanie Lu 		mutex_enter(&iospc_mutex);
128*4df55fdeSJanie Lu 		iospc_kstat_inited = 0;
129*4df55fdeSJanie Lu 		(void) rfios_unbind_group();
130*4df55fdeSJanie Lu 		iospc_leaf_grps = NULL;
131*4df55fdeSJanie Lu 		mutex_exit(&iospc_mutex);
132*4df55fdeSJanie Lu 	}
133*4df55fdeSJanie Lu 
134*4df55fdeSJanie Lu 	mutex_destroy(&iospc_mutex);
135*4df55fdeSJanie Lu 
136*4df55fdeSJanie Lu 	/* Free px soft state */
137*4df55fdeSJanie Lu 	ddi_soft_state_fini(&iospc_state_p);
138*4df55fdeSJanie Lu 
139*4df55fdeSJanie Lu 	return (DDI_SUCCESS);
140*4df55fdeSJanie Lu }
141*4df55fdeSJanie Lu 
142*4df55fdeSJanie Lu int
143*4df55fdeSJanie Lu _info(struct modinfo *modinfop)
144*4df55fdeSJanie Lu {
145*4df55fdeSJanie Lu 	return (mod_info(&ml, modinfop));
146*4df55fdeSJanie Lu }
147*4df55fdeSJanie Lu 
148*4df55fdeSJanie Lu /*
149*4df55fdeSJanie Lu  * Per-instance initialization.  Suspend/resume not supported.
150*4df55fdeSJanie Lu  */
151*4df55fdeSJanie Lu static int
152*4df55fdeSJanie Lu iospc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
153*4df55fdeSJanie Lu {
154*4df55fdeSJanie Lu 	iospc_t *iospc_p;
155*4df55fdeSJanie Lu 	int instance = ddi_get_instance(dip);
156*4df55fdeSJanie Lu 	char *ptr;
157*4df55fdeSJanie Lu 
158*4df55fdeSJanie Lu 	IOSPC_DBG2("iospc: iospc_attach: enter\n");
159*4df55fdeSJanie Lu 	switch (cmd) {
160*4df55fdeSJanie Lu 	case DDI_RESUME:
161*4df55fdeSJanie Lu 	case DDI_ATTACH:
162*4df55fdeSJanie Lu 		/* Initialize one-time kstat structures. */
163*4df55fdeSJanie Lu 		mutex_enter(&iospc_mutex);
164*4df55fdeSJanie Lu 		if (!iospc_kstat_inited) {
165*4df55fdeSJanie Lu 			if ((ddi_prop_lookup_string(DDI_DEV_T_ANY, dip,
166*4df55fdeSJanie Lu 			    0, "compatible", &ptr)) != DDI_PROP_SUCCESS)
167*4df55fdeSJanie Lu 				goto bad_property;
168*4df55fdeSJanie Lu 
169*4df55fdeSJanie Lu 			if ((strcmp(ptr, "SUNW,ktios-pr") == 0) ||
170*4df55fdeSJanie Lu 			    (strcmp(ptr, "SUNW,rfios-pr") == 0)) {
171*4df55fdeSJanie Lu 				iospc_leaf_grps = rfios_bind_group();
172*4df55fdeSJanie Lu 			} else {
173*4df55fdeSJanie Lu 				ddi_prop_free(ptr);
174*4df55fdeSJanie Lu 				goto bad_property;
175*4df55fdeSJanie Lu 			}
176*4df55fdeSJanie Lu 
177*4df55fdeSJanie Lu 			ddi_prop_free(ptr);
178*4df55fdeSJanie Lu 
179*4df55fdeSJanie Lu 			if (iospc_kstat_init() != DDI_SUCCESS)
180*4df55fdeSJanie Lu 				goto bad_kstat_init;
181*4df55fdeSJanie Lu 
182*4df55fdeSJanie Lu 			iospc_kstat_inited++;
183*4df55fdeSJanie Lu 		}
184*4df55fdeSJanie Lu 		mutex_exit(&iospc_mutex);
185*4df55fdeSJanie Lu 
186*4df55fdeSJanie Lu 		if (ddi_soft_state_zalloc(iospc_state_p, instance) !=
187*4df55fdeSJanie Lu 		    DDI_SUCCESS) {
188*4df55fdeSJanie Lu 			goto bad_softstate;
189*4df55fdeSJanie Lu 		}
190*4df55fdeSJanie Lu 
191*4df55fdeSJanie Lu 		iospc_p = (iospc_t *)ddi_get_soft_state(iospc_state_p,
192*4df55fdeSJanie Lu 		    instance);
193*4df55fdeSJanie Lu 
194*4df55fdeSJanie Lu 		iospc_p->iospc_dip = dip;
195*4df55fdeSJanie Lu 
196*4df55fdeSJanie Lu 		/* Set up kstats. */
197*4df55fdeSJanie Lu 
198*4df55fdeSJanie Lu 		if (iospc_kstat_attach(iospc_p) != DDI_SUCCESS)
199*4df55fdeSJanie Lu 			goto bad_kstat_attach;
200*4df55fdeSJanie Lu 
201*4df55fdeSJanie Lu 		IOSPC_DBG2("iospc: iospc_attach: exit SUCCESS\n");
202*4df55fdeSJanie Lu 
203*4df55fdeSJanie Lu 		return (DDI_SUCCESS);
204*4df55fdeSJanie Lu 
205*4df55fdeSJanie Lu bad_kstat_attach:
206*4df55fdeSJanie Lu 		(void) ddi_soft_state_free(iospc_state_p, instance);
207*4df55fdeSJanie Lu bad_softstate:
208*4df55fdeSJanie Lu 		iospc_kstat_fini();
209*4df55fdeSJanie Lu bad_kstat_init:
210*4df55fdeSJanie Lu bad_property:
211*4df55fdeSJanie Lu 		mutex_enter(&iospc_mutex);
212*4df55fdeSJanie Lu 		IOSPC_DBG2("iospc: iospc_attach: exit FAILURE\n");
213*4df55fdeSJanie Lu 		return (DDI_FAILURE);
214*4df55fdeSJanie Lu 
215*4df55fdeSJanie Lu 	default:
216*4df55fdeSJanie Lu 		return (DDI_FAILURE);
217*4df55fdeSJanie Lu 	}
218*4df55fdeSJanie Lu }
219*4df55fdeSJanie Lu 
220*4df55fdeSJanie Lu /*
221*4df55fdeSJanie Lu  * Per-instance cleanup.  Suspend/resume not supported.
222*4df55fdeSJanie Lu  */
223*4df55fdeSJanie Lu static int
224*4df55fdeSJanie Lu iospc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
225*4df55fdeSJanie Lu {
226*4df55fdeSJanie Lu 	int instance = ddi_get_instance(dip);
227*4df55fdeSJanie Lu 
228*4df55fdeSJanie Lu 	IOSPC_DBG2("iospc: iospc_detach: enter\n");
229*4df55fdeSJanie Lu 	iospc_t *iospc_p = (iospc_t *)ddi_get_soft_state(
230*4df55fdeSJanie Lu 	    iospc_state_p, instance);
231*4df55fdeSJanie Lu 
232*4df55fdeSJanie Lu 	switch (cmd) {
233*4df55fdeSJanie Lu 	case DDI_SUSPEND:
234*4df55fdeSJanie Lu 	case DDI_DETACH:
235*4df55fdeSJanie Lu 		iospc_kstat_detach(iospc_p);
236*4df55fdeSJanie Lu 		(void) ddi_soft_state_free(iospc_state_p, instance);
237*4df55fdeSJanie Lu 
238*4df55fdeSJanie Lu 		IOSPC_DBG2("iospc: iospc_detach: exit - SUCCESS\n");
239*4df55fdeSJanie Lu 		return (DDI_SUCCESS);
240*4df55fdeSJanie Lu 
241*4df55fdeSJanie Lu 	default:
242*4df55fdeSJanie Lu 		IOSPC_DBG2("iospc: iospc_detach: exit - FAILURE\n");
243*4df55fdeSJanie Lu 		return (DDI_FAILURE);
244*4df55fdeSJanie Lu 	}
245*4df55fdeSJanie Lu }
246*4df55fdeSJanie Lu 
247*4df55fdeSJanie Lu #define	PIC_STR_LEN	5	/* Size of a PICx name string. */
248*4df55fdeSJanie Lu 
249*4df55fdeSJanie Lu /*
250*4df55fdeSJanie Lu  * One-time initialization for this module.
251*4df55fdeSJanie Lu  */
252*4df55fdeSJanie Lu int
253*4df55fdeSJanie Lu iospc_kstat_init()
254*4df55fdeSJanie Lu {
255*4df55fdeSJanie Lu 	iospc_grp_t **grp_pp;
256*4df55fdeSJanie Lu 	iospc_grp_t *grp_p;
257*4df55fdeSJanie Lu 
258*4df55fdeSJanie Lu 	IOSPC_DBG2("iospc: kstat_init: enter\n");
259*4df55fdeSJanie Lu 
260*4df55fdeSJanie Lu 	/*
261*4df55fdeSJanie Lu 	 * Initialize the name kstats for each group, drawing upon the table
262*4df55fdeSJanie Lu 	 * for values.
263*4df55fdeSJanie Lu 	 */
264*4df55fdeSJanie Lu 	for (grp_pp = iospc_leaf_grps; *grp_pp != NULL; grp_pp++) {
265*4df55fdeSJanie Lu 
266*4df55fdeSJanie Lu 		grp_p = *grp_pp;
267*4df55fdeSJanie Lu 
268*4df55fdeSJanie Lu 		IOSPC_DBG2("Setting up group for %s\n", grp_p->grp_name);
269*4df55fdeSJanie Lu 
270*4df55fdeSJanie Lu 		/* Create basic pic event-type pair. */
271*4df55fdeSJanie Lu 		grp_p->name_kstats_pp = kmem_zalloc((grp_p->num_counters *
272*4df55fdeSJanie Lu 		    sizeof (kstat_t)), KM_SLEEP);
273*4df55fdeSJanie Lu 		if (iospc_create_name_kstat(grp_p) != DDI_SUCCESS) {
274*4df55fdeSJanie Lu 			iospc_kstat_fini();
275*4df55fdeSJanie Lu 			IOSPC_DBG1("iospc: init: failure exit\n");
276*4df55fdeSJanie Lu 			return (DDI_FAILURE);
277*4df55fdeSJanie Lu 		}
278*4df55fdeSJanie Lu 	}
279*4df55fdeSJanie Lu 
280*4df55fdeSJanie Lu 	IOSPC_DBG2("iospc: kstat_init: success exit\n");
281*4df55fdeSJanie Lu 
282*4df55fdeSJanie Lu 	return (DDI_SUCCESS);
283*4df55fdeSJanie Lu }
284*4df55fdeSJanie Lu 
285*4df55fdeSJanie Lu /*
286*4df55fdeSJanie Lu  * Per-instance initialization for this module.
287*4df55fdeSJanie Lu  */
288*4df55fdeSJanie Lu int
289*4df55fdeSJanie Lu iospc_kstat_attach(iospc_t *iospc_p)
290*4df55fdeSJanie Lu {
291*4df55fdeSJanie Lu 	iospc_grp_t **grp_pp;
292*4df55fdeSJanie Lu 	iospc_grp_t *grp_p;
293*4df55fdeSJanie Lu 	iospc_ksinfo_t *ksinfo_p;
294*4df55fdeSJanie Lu 
295*4df55fdeSJanie Lu 	int i;
296*4df55fdeSJanie Lu 
297*4df55fdeSJanie Lu 	IOSPC_DBG2("iospc: kstat_attach %d: enter\n",
298*4df55fdeSJanie Lu 	    ddi_get_instance(iospc_p->iospc_dip));
299*4df55fdeSJanie Lu 
300*4df55fdeSJanie Lu 	/* Set up kstats for each group. */
301*4df55fdeSJanie Lu 	for (i = 0, grp_pp = iospc_leaf_grps; *grp_pp != NULL; i++, grp_pp++) {
302*4df55fdeSJanie Lu 
303*4df55fdeSJanie Lu 		if (i >= IOSPC_MAX_NUM_GRPS)
304*4df55fdeSJanie Lu 			goto err;
305*4df55fdeSJanie Lu 
306*4df55fdeSJanie Lu 		grp_p = *grp_pp;
307*4df55fdeSJanie Lu 
308*4df55fdeSJanie Lu 		/*
309*4df55fdeSJanie Lu 		 * ksinfo_p keeps all info needed by iospc_kstat_update,
310*4df55fdeSJanie Lu 		 * which is fired off asynchronously on demand by the kstat
311*4df55fdeSJanie Lu 		 * framework.
312*4df55fdeSJanie Lu 		 */
313*4df55fdeSJanie Lu 		ksinfo_p = (iospc_ksinfo_t *)kmem_zalloc(
314*4df55fdeSJanie Lu 		    sizeof (iospc_ksinfo_t), KM_SLEEP);
315*4df55fdeSJanie Lu 
316*4df55fdeSJanie Lu 		ksinfo_p->iospc_p = iospc_p;
317*4df55fdeSJanie Lu 		ksinfo_p->grp_p  = grp_p;
318*4df55fdeSJanie Lu 
319*4df55fdeSJanie Lu 		/* Also save in state structure, for later cleanup. */
320*4df55fdeSJanie Lu 		iospc_p->iospc_ksinfo_p[i] = ksinfo_p;
321*4df55fdeSJanie Lu 
322*4df55fdeSJanie Lu 		/* Create counter kstats */
323*4df55fdeSJanie Lu 		ksinfo_p->cntr_ksp = iospc_create_cntr_kstat(grp_p->grp_name,
324*4df55fdeSJanie Lu 		    ddi_get_instance(iospc_p->iospc_dip),
325*4df55fdeSJanie Lu 		    iospc_kstat_update, ksinfo_p, grp_p->num_counters);
326*4df55fdeSJanie Lu 
327*4df55fdeSJanie Lu 		if (ksinfo_p->cntr_ksp == NULL)
328*4df55fdeSJanie Lu 			goto err;
329*4df55fdeSJanie Lu 
330*4df55fdeSJanie Lu 		if (grp_p->access_init(iospc_p, ksinfo_p) != SUCCESS)
331*4df55fdeSJanie Lu 			goto err;
332*4df55fdeSJanie Lu 	}
333*4df55fdeSJanie Lu 
334*4df55fdeSJanie Lu 	IOSPC_DBG2("iospc: kstat_attach: success exit\n");
335*4df55fdeSJanie Lu 	return (DDI_SUCCESS);
336*4df55fdeSJanie Lu err:
337*4df55fdeSJanie Lu 	iospc_kstat_detach(iospc_p);
338*4df55fdeSJanie Lu 	IOSPC_DBG2("iospc: kstat_attach: failure exit\n");
339*4df55fdeSJanie Lu 	return (DDI_FAILURE);
340*4df55fdeSJanie Lu }
341*4df55fdeSJanie Lu 
342*4df55fdeSJanie Lu /*
343*4df55fdeSJanie Lu  * Create the name kstats for each group.
344*4df55fdeSJanie Lu  */
345*4df55fdeSJanie Lu static int
346*4df55fdeSJanie Lu iospc_create_name_kstat(iospc_grp_t *grp_p)
347*4df55fdeSJanie Lu {
348*4df55fdeSJanie Lu 	int i;
349*4df55fdeSJanie Lu 
350*4df55fdeSJanie Lu 	for (i = 0; i < grp_p->num_counters; i++) {
351*4df55fdeSJanie Lu 		grp_p->name_kstats_pp[i] = iospc_create_picN_kstat(
352*4df55fdeSJanie Lu 		    grp_p->grp_name, i,
353*4df55fdeSJanie Lu 		    grp_p->regsel_p->fields_p[i].event_offset,
354*4df55fdeSJanie Lu 		    grp_p->regsel_p->fields_p[i].num_events,
355*4df55fdeSJanie Lu 		    grp_p->regsel_p->fields_p[i].events_p);
356*4df55fdeSJanie Lu 
357*4df55fdeSJanie Lu 		if (grp_p->name_kstats_pp[i] == NULL)
358*4df55fdeSJanie Lu 			return (DDI_FAILURE);
359*4df55fdeSJanie Lu 	}
360*4df55fdeSJanie Lu 	return (DDI_SUCCESS);
361*4df55fdeSJanie Lu }
362*4df55fdeSJanie Lu 
363*4df55fdeSJanie Lu /*
364*4df55fdeSJanie Lu  * Create the picN kstat. Returns a pointer to the
365*4df55fdeSJanie Lu  * kstat which the driver must store to allow it
366*4df55fdeSJanie Lu  * to be deleted when necessary.
367*4df55fdeSJanie Lu  */
368*4df55fdeSJanie Lu static kstat_t *
369*4df55fdeSJanie Lu iospc_create_picN_kstat(char *mod_name, int pic, uint64_t ev_offset,
370*4df55fdeSJanie Lu     int num_ev, iospc_event_t *ev_array)
371*4df55fdeSJanie Lu {
372*4df55fdeSJanie Lu 	int event;
373*4df55fdeSJanie Lu 	char pic_name[PIC_STR_LEN];
374*4df55fdeSJanie Lu 	kstat_t	*picN_ksp = NULL;
375*4df55fdeSJanie Lu 	struct kstat_named *pic_named_data;
376*4df55fdeSJanie Lu 
377*4df55fdeSJanie Lu 	(void) snprintf(pic_name, PIC_STR_LEN, "pic%1d", pic);
378*4df55fdeSJanie Lu 
379*4df55fdeSJanie Lu 	if ((picN_ksp = kstat_create(mod_name, 0, pic_name,
380*4df55fdeSJanie Lu 	    "bus", KSTAT_TYPE_NAMED, num_ev, NULL)) == NULL) {
381*4df55fdeSJanie Lu 		return (NULL);
382*4df55fdeSJanie Lu 	}
383*4df55fdeSJanie Lu 
384*4df55fdeSJanie Lu 	/* NOTE: Number of events is assumed to always be non-zero. */
385*4df55fdeSJanie Lu 
386*4df55fdeSJanie Lu 	pic_named_data = (struct kstat_named *)picN_ksp->ks_data;
387*4df55fdeSJanie Lu 
388*4df55fdeSJanie Lu 	/*
389*4df55fdeSJanie Lu 	 * Fill up data section of the kstat
390*4df55fdeSJanie Lu 	 * Write event names and their associated pcr masks.
391*4df55fdeSJanie Lu 	 * num_ev - 1 is because CLEAR_PIC is added separately.
392*4df55fdeSJanie Lu 	 */
393*4df55fdeSJanie Lu 	for (event = 0; event < num_ev - 1; event++) {
394*4df55fdeSJanie Lu 		pic_named_data[event].value.ui64 =
395*4df55fdeSJanie Lu 		    ev_array[event].value << ev_offset;
396*4df55fdeSJanie Lu 
397*4df55fdeSJanie Lu 		kstat_named_init(&pic_named_data[event],
398*4df55fdeSJanie Lu 		    ev_array[event].name, KSTAT_DATA_UINT64);
399*4df55fdeSJanie Lu 	}
400*4df55fdeSJanie Lu 
401*4df55fdeSJanie Lu 	/*
402*4df55fdeSJanie Lu 	 * add the clear_pic entry
403*4df55fdeSJanie Lu 	 */
404*4df55fdeSJanie Lu 	pic_named_data[event].value.ui64 =
405*4df55fdeSJanie Lu 	    (uint64_t)~(ev_array[event].value << ev_offset);
406*4df55fdeSJanie Lu 
407*4df55fdeSJanie Lu 	kstat_named_init(&pic_named_data[event], ev_array[event].name,
408*4df55fdeSJanie Lu 	    KSTAT_DATA_UINT64);
409*4df55fdeSJanie Lu 
410*4df55fdeSJanie Lu 	kstat_install(picN_ksp);
411*4df55fdeSJanie Lu 
412*4df55fdeSJanie Lu 	return (picN_ksp);
413*4df55fdeSJanie Lu }
414*4df55fdeSJanie Lu 
415*4df55fdeSJanie Lu /*
416*4df55fdeSJanie Lu  * Create the "counters" kstat.
417*4df55fdeSJanie Lu  */
418*4df55fdeSJanie Lu static kstat_t *
419*4df55fdeSJanie Lu iospc_create_cntr_kstat(char *name, int dev_inst,
420*4df55fdeSJanie Lu     int (*update)(kstat_t *, int), iospc_ksinfo_t *ksinfop, int num_pics)
421*4df55fdeSJanie Lu {
422*4df55fdeSJanie Lu 	int i;
423*4df55fdeSJanie Lu 	char pic_str[PIC_STR_LEN];
424*4df55fdeSJanie Lu 	struct kstat *counters_ksp;
425*4df55fdeSJanie Lu 	struct kstat_named *counters_named_data;
426*4df55fdeSJanie Lu 
427*4df55fdeSJanie Lu 	IOSPC_DBG2("iospc_create_cntr_kstat: name: %s instance: %d\n",
428*4df55fdeSJanie Lu 	    name, dev_inst);
429*4df55fdeSJanie Lu 
430*4df55fdeSJanie Lu 	/*
431*4df55fdeSJanie Lu 	 * Size of kstat is num_pics + 1. extra one for pcr.
432*4df55fdeSJanie Lu 	 */
433*4df55fdeSJanie Lu 
434*4df55fdeSJanie Lu 	if ((counters_ksp = kstat_create(name, dev_inst, "counters", "bus",
435*4df55fdeSJanie Lu 	    KSTAT_TYPE_NAMED, num_pics + 1, KSTAT_FLAG_WRITABLE)) == NULL) {
436*4df55fdeSJanie Lu 		return (NULL);
437*4df55fdeSJanie Lu 	}
438*4df55fdeSJanie Lu 
439*4df55fdeSJanie Lu 	counters_named_data = (struct kstat_named *)(counters_ksp->ks_data);
440*4df55fdeSJanie Lu 	kstat_named_init(&counters_named_data[0], "pcr", KSTAT_DATA_UINT64);
441*4df55fdeSJanie Lu 
442*4df55fdeSJanie Lu 	for (i = 0; i < num_pics; i++) {
443*4df55fdeSJanie Lu 		(void) snprintf(pic_str, PIC_STR_LEN, "pic%1d", i);
444*4df55fdeSJanie Lu 
445*4df55fdeSJanie Lu 		kstat_named_init(&counters_named_data[i+1], pic_str,
446*4df55fdeSJanie Lu 		    KSTAT_DATA_UINT64);
447*4df55fdeSJanie Lu 	}
448*4df55fdeSJanie Lu 
449*4df55fdeSJanie Lu 	/*
450*4df55fdeSJanie Lu 	 * Store the reg type and other info. in the kstat's private field
451*4df55fdeSJanie Lu 	 * so that they are available to the update function.
452*4df55fdeSJanie Lu 	 */
453*4df55fdeSJanie Lu 	counters_ksp->ks_private = (void *)ksinfop;
454*4df55fdeSJanie Lu 	counters_ksp->ks_update = update;
455*4df55fdeSJanie Lu 
456*4df55fdeSJanie Lu 	kstat_install(counters_ksp);
457*4df55fdeSJanie Lu 
458*4df55fdeSJanie Lu 	return (counters_ksp);
459*4df55fdeSJanie Lu }
460*4df55fdeSJanie Lu 
461*4df55fdeSJanie Lu /*
462*4df55fdeSJanie Lu  * Program a performance counter.
463*4df55fdeSJanie Lu  *
464*4df55fdeSJanie Lu  * reggroup is which type of counter.
465*4df55fdeSJanie Lu  * counter is the counter number.
466*4df55fdeSJanie Lu  * event is the event to program for that counter.
467*4df55fdeSJanie Lu  */
468*4df55fdeSJanie Lu static int
469*4df55fdeSJanie Lu iospc_perfcnt_program(iospc_t *iospc_p, iospc_grp_t *grp_p,
470*4df55fdeSJanie Lu 	iospc_ksinfo_t *ksinfo_p, uint64_t new_events)
471*4df55fdeSJanie Lu {
472*4df55fdeSJanie Lu 	uint64_t old_events;
473*4df55fdeSJanie Lu 	int rval = SUCCESS;
474*4df55fdeSJanie Lu 	uint64_t event_mask;
475*4df55fdeSJanie Lu 	int counter;
476*4df55fdeSJanie Lu 
477*4df55fdeSJanie Lu 	IOSPC_DBG1(
478*4df55fdeSJanie Lu 	    "iospc_perfcnt_program enter: new_events:0x%" PRIx64 "\n",
479*4df55fdeSJanie Lu 	    new_events);
480*4df55fdeSJanie Lu 
481*4df55fdeSJanie Lu 	if ((rval = grp_p->access(iospc_p, ksinfo_p->arg, IOSPC_REG_READ,
482*4df55fdeSJanie Lu 	    grp_p->regsel_p->regoff, &old_events)) != SUCCESS)
483*4df55fdeSJanie Lu 		goto done_pgm;
484*4df55fdeSJanie Lu 
485*4df55fdeSJanie Lu 	IOSPC_DBG1("  old_events:0x%" PRIx64 "\n", old_events);
486*4df55fdeSJanie Lu 
487*4df55fdeSJanie Lu 	for (counter = 0; counter < grp_p->num_counters; counter++) {
488*4df55fdeSJanie Lu 
489*4df55fdeSJanie Lu 		if (grp_p->counters_p[counter].zero_regoff == NO_REGISTER)
490*4df55fdeSJanie Lu 			continue;
491*4df55fdeSJanie Lu 
492*4df55fdeSJanie Lu 		event_mask = grp_p->regsel_p->fields_p[counter].event_mask <<
493*4df55fdeSJanie Lu 		    grp_p->regsel_p->fields_p[counter].event_offset;
494*4df55fdeSJanie Lu 
495*4df55fdeSJanie Lu 		IOSPC_DBG1(
496*4df55fdeSJanie Lu 		    "grp:%s, counter:%d, zero_regoff:0x%lx, "
497*4df55fdeSJanie Lu 		    "event_mask:0x%" PRIx64 ", old&mask:0x%lx, "
498*4df55fdeSJanie Lu 		    "new&mask:0x%lx\n",
499*4df55fdeSJanie Lu 		    grp_p->grp_name, counter,
500*4df55fdeSJanie Lu 		    grp_p->counters_p[counter].zero_regoff,
501*4df55fdeSJanie Lu 		    event_mask, old_events & event_mask,
502*4df55fdeSJanie Lu 		    new_events & event_mask);
503*4df55fdeSJanie Lu 
504*4df55fdeSJanie Lu 		if ((old_events & event_mask) ==
505*4df55fdeSJanie Lu 		    (new_events & event_mask))
506*4df55fdeSJanie Lu 			continue;
507*4df55fdeSJanie Lu 
508*4df55fdeSJanie Lu 		IOSPC_DBG1("Zeroing counter %d\n", counter);
509*4df55fdeSJanie Lu 
510*4df55fdeSJanie Lu 		if ((rval = grp_p->access(iospc_p, ksinfo_p->arg,
511*4df55fdeSJanie Lu 		    IOSPC_REG_WRITE, grp_p->counters_p[counter].zero_regoff,
512*4df55fdeSJanie Lu 		    &grp_p->counters_p[counter].zero_value)) != SUCCESS)
513*4df55fdeSJanie Lu 			goto done_pgm;
514*4df55fdeSJanie Lu 	}
515*4df55fdeSJanie Lu 
516*4df55fdeSJanie Lu 	if (old_events != new_events) {
517*4df55fdeSJanie Lu 
518*4df55fdeSJanie Lu 		IOSPC_DBG1("old != new, setting event reg %ld to 0x%lx\n",
519*4df55fdeSJanie Lu 		    grp_p->regsel_p->regoff, new_events);
520*4df55fdeSJanie Lu 
521*4df55fdeSJanie Lu 		if ((rval = grp_p->access(iospc_p, ksinfo_p->arg,
522*4df55fdeSJanie Lu 		    IOSPC_REG_WRITE, grp_p->regsel_p->regoff, &new_events))
523*4df55fdeSJanie Lu 		    != SUCCESS) {
524*4df55fdeSJanie Lu 			IOSPC_DBG1(
525*4df55fdeSJanie Lu 			    "Write of new event data failed, "
526*4df55fdeSJanie Lu 			    "select reg offset: %ld\n",
527*4df55fdeSJanie Lu 			    grp_p->regsel_p->regoff);
528*4df55fdeSJanie Lu 			goto done_pgm;
529*4df55fdeSJanie Lu 		}
530*4df55fdeSJanie Lu 	}
531*4df55fdeSJanie Lu done_pgm:
532*4df55fdeSJanie Lu 	IOSPC_DBG1("iospc_perfcnt_program: returning status %d.\n", rval);
533*4df55fdeSJanie Lu 	return (rval);
534*4df55fdeSJanie Lu }
535*4df55fdeSJanie Lu 
536*4df55fdeSJanie Lu /*
537*4df55fdeSJanie Lu  * kstat update function. Handles reads/writes
538*4df55fdeSJanie Lu  * from/to kstat.
539*4df55fdeSJanie Lu  */
540*4df55fdeSJanie Lu static int
541*4df55fdeSJanie Lu iospc_kstat_update(kstat_t *ksp, int rw)
542*4df55fdeSJanie Lu {
543*4df55fdeSJanie Lu 	struct kstat_named *data_p;
544*4df55fdeSJanie Lu 	int counter;
545*4df55fdeSJanie Lu 	iospc_ksinfo_t *ksinfop = ksp->ks_private;
546*4df55fdeSJanie Lu 	iospc_grp_t *grp_p = ksinfop->grp_p;
547*4df55fdeSJanie Lu 	iospc_t *iospc_p = ksinfop->iospc_p;
548*4df55fdeSJanie Lu 
549*4df55fdeSJanie Lu 	data_p = (struct kstat_named *)ksp->ks_data;
550*4df55fdeSJanie Lu 
551*4df55fdeSJanie Lu 	if (rw == KSTAT_WRITE) {
552*4df55fdeSJanie Lu 
553*4df55fdeSJanie Lu 		IOSPC_DBG2("iospc_kstat_update: wr %ld\n",
554*4df55fdeSJanie Lu 		    data_p[0].value.ui64);
555*4df55fdeSJanie Lu 
556*4df55fdeSJanie Lu 		/*
557*4df55fdeSJanie Lu 		 * Fields without programmable events won't be zeroed as
558*4df55fdeSJanie Lu 		 * iospc_perfcnt_program is what zeros them.
559*4df55fdeSJanie Lu 		 */
560*4df55fdeSJanie Lu 
561*4df55fdeSJanie Lu 		/* This group has programmable events. */
562*4df55fdeSJanie Lu 		if (grp_p->regsel_p->regoff != NO_REGISTER) {
563*4df55fdeSJanie Lu 
564*4df55fdeSJanie Lu 			IOSPC_DBG2("write: regoff has valid register\n");
565*4df55fdeSJanie Lu 			if (iospc_perfcnt_program(iospc_p, grp_p, ksinfop,
566*4df55fdeSJanie Lu 			    data_p[0].value.ui64) != SUCCESS)
567*4df55fdeSJanie Lu 				return (EIO);
568*4df55fdeSJanie Lu 		}
569*4df55fdeSJanie Lu 
570*4df55fdeSJanie Lu 	} else {	/* Read the event register and all of the counters. */
571*4df55fdeSJanie Lu 
572*4df55fdeSJanie Lu 		/* This group has programmable events. */
573*4df55fdeSJanie Lu 		if (grp_p->regsel_p->regoff != NO_REGISTER) {
574*4df55fdeSJanie Lu 
575*4df55fdeSJanie Lu 			IOSPC_DBG2("read: regoff has valid register\n");
576*4df55fdeSJanie Lu 
577*4df55fdeSJanie Lu 			if (grp_p->access(iospc_p, ksinfop->arg, IOSPC_REG_READ,
578*4df55fdeSJanie Lu 			    grp_p->regsel_p->regoff, &data_p[0].value.ui64)
579*4df55fdeSJanie Lu 			    != SUCCESS)
580*4df55fdeSJanie Lu 				return (EIO);
581*4df55fdeSJanie Lu 		} else
582*4df55fdeSJanie Lu 			data_p[0].value.ui64 = 0ull;
583*4df55fdeSJanie Lu 
584*4df55fdeSJanie Lu 		IOSPC_DBG2("iospc_kstat_update: rd event %lx\n",
585*4df55fdeSJanie Lu 		    data_p[0].value.ui64);
586*4df55fdeSJanie Lu 
587*4df55fdeSJanie Lu 		for (counter = 0; counter < grp_p->num_counters; counter++) {
588*4df55fdeSJanie Lu 
589*4df55fdeSJanie Lu 			if (grp_p->access(iospc_p, ksinfop->arg, IOSPC_REG_READ,
590*4df55fdeSJanie Lu 			    grp_p->counters_p[counter].regoff,
591*4df55fdeSJanie Lu 			    &data_p[counter + 1].value.ui64) != SUCCESS)
592*4df55fdeSJanie Lu 				return (EIO);
593*4df55fdeSJanie Lu 
594*4df55fdeSJanie Lu 			IOSPC_DBG2("cntr%d, off:0x%lx, val:0x%lx\n", counter,
595*4df55fdeSJanie Lu 			    grp_p->counters_p[counter].regoff,
596*4df55fdeSJanie Lu 			    data_p[counter + 1].value.ui64);
597*4df55fdeSJanie Lu 		}
598*4df55fdeSJanie Lu 	}
599*4df55fdeSJanie Lu 	return (SUCCESS);
600*4df55fdeSJanie Lu }
601*4df55fdeSJanie Lu 
602*4df55fdeSJanie Lu void
603*4df55fdeSJanie Lu iospc_kstat_fini()
604*4df55fdeSJanie Lu {
605*4df55fdeSJanie Lu 	iospc_grp_t **grp_pp;
606*4df55fdeSJanie Lu 	iospc_grp_t *grp_p;
607*4df55fdeSJanie Lu 	int j;
608*4df55fdeSJanie Lu 
609*4df55fdeSJanie Lu 	IOSPC_DBG2("iospc_kstat_fini called\n");
610*4df55fdeSJanie Lu 
611*4df55fdeSJanie Lu 	for (j = 0, grp_pp = iospc_leaf_grps; *grp_pp != NULL; j++, grp_pp++) {
612*4df55fdeSJanie Lu 		grp_p = *grp_pp;
613*4df55fdeSJanie Lu 		if (grp_p->name_kstats_pp != NULL) {
614*4df55fdeSJanie Lu 			iospc_delete_name_kstats(grp_p->name_kstats_pp,
615*4df55fdeSJanie Lu 			    grp_p->num_counters);
616*4df55fdeSJanie Lu 			kmem_free(grp_p->name_kstats_pp,
617*4df55fdeSJanie Lu 			    grp_p->num_counters * sizeof (kstat_t));
618*4df55fdeSJanie Lu 			grp_p->name_kstats_pp = NULL;
619*4df55fdeSJanie Lu 		}
620*4df55fdeSJanie Lu 	}
621*4df55fdeSJanie Lu }
622*4df55fdeSJanie Lu 
623*4df55fdeSJanie Lu static void
624*4df55fdeSJanie Lu iospc_delete_name_kstats(kstat_t **name_kstats_pp, int num_kstats)
625*4df55fdeSJanie Lu {
626*4df55fdeSJanie Lu 	int i;
627*4df55fdeSJanie Lu 
628*4df55fdeSJanie Lu 	if (name_kstats_pp != NULL) {
629*4df55fdeSJanie Lu 		for (i = 0; i < num_kstats; i++) {
630*4df55fdeSJanie Lu 			if (name_kstats_pp[i] != NULL)
631*4df55fdeSJanie Lu 				kstat_delete(name_kstats_pp[i]);
632*4df55fdeSJanie Lu 		}
633*4df55fdeSJanie Lu 	}
634*4df55fdeSJanie Lu }
635*4df55fdeSJanie Lu 
636*4df55fdeSJanie Lu void
637*4df55fdeSJanie Lu iospc_kstat_detach(iospc_t *iospc_p)
638*4df55fdeSJanie Lu {
639*4df55fdeSJanie Lu 	iospc_grp_t **grp_pp;
640*4df55fdeSJanie Lu 	iospc_grp_t *grp_p;
641*4df55fdeSJanie Lu 	int i;
642*4df55fdeSJanie Lu 
643*4df55fdeSJanie Lu 	IOSPC_DBG2("iospc_kstat_detach called\n");
644*4df55fdeSJanie Lu 
645*4df55fdeSJanie Lu 	for (i = 0, grp_pp = iospc_leaf_grps; *grp_pp != NULL; i++, grp_pp++) {
646*4df55fdeSJanie Lu 
647*4df55fdeSJanie Lu 		if (i >= IOSPC_MAX_NUM_GRPS)
648*4df55fdeSJanie Lu 			return;
649*4df55fdeSJanie Lu 
650*4df55fdeSJanie Lu 		grp_p = *grp_pp;
651*4df55fdeSJanie Lu 		if (iospc_p->iospc_ksinfo_p[i] != NULL) {
652*4df55fdeSJanie Lu 
653*4df55fdeSJanie Lu 			grp_p->access_fini(iospc_p, iospc_p->iospc_ksinfo_p[i]);
654*4df55fdeSJanie Lu 
655*4df55fdeSJanie Lu 			if (iospc_p->iospc_ksinfo_p[i]->cntr_ksp != NULL)
656*4df55fdeSJanie Lu 				kstat_delete(
657*4df55fdeSJanie Lu 				    iospc_p->iospc_ksinfo_p[i]->cntr_ksp);
658*4df55fdeSJanie Lu 			kmem_free(iospc_p->iospc_ksinfo_p[i],
659*4df55fdeSJanie Lu 			    sizeof (iospc_ksinfo_t));
660*4df55fdeSJanie Lu 		}
661*4df55fdeSJanie Lu 
662*4df55fdeSJanie Lu 	}
663*4df55fdeSJanie Lu }
664