xref: /illumos-gate/usr/src/uts/sun4v/io/iospc/iospc.c (revision 8b92a81b8f3ff3d4fe904caad0969cfd13b7efa6)
14df55fdeSJanie Lu /*
24df55fdeSJanie Lu  * CDDL HEADER START
34df55fdeSJanie Lu  *
44df55fdeSJanie Lu  * The contents of this file are subject to the terms of the
54df55fdeSJanie Lu  * Common Development and Distribution License (the "License").
64df55fdeSJanie Lu  * You may not use this file except in compliance with the License.
74df55fdeSJanie Lu  *
84df55fdeSJanie Lu  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94df55fdeSJanie Lu  * or http://www.opensolaris.org/os/licensing.
104df55fdeSJanie Lu  * See the License for the specific language governing permissions
114df55fdeSJanie Lu  * and limitations under the License.
124df55fdeSJanie Lu  *
134df55fdeSJanie Lu  * When distributing Covered Code, include this CDDL HEADER in each
144df55fdeSJanie Lu  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154df55fdeSJanie Lu  * If applicable, add the following below this CDDL HEADER, with the
164df55fdeSJanie Lu  * fields enclosed by brackets "[]" replaced with your own identifying
174df55fdeSJanie Lu  * information: Portions Copyright [yyyy] [name of copyright owner]
184df55fdeSJanie Lu  *
194df55fdeSJanie Lu  * CDDL HEADER END
204df55fdeSJanie Lu  */
214df55fdeSJanie Lu 
224df55fdeSJanie Lu /*
234df55fdeSJanie Lu  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
244df55fdeSJanie Lu  * Use is subject to license terms.
254df55fdeSJanie Lu  */
264df55fdeSJanie Lu 
274df55fdeSJanie Lu /*
284df55fdeSJanie Lu  * IO Performance Counter Driver
294df55fdeSJanie Lu  */
304df55fdeSJanie Lu 
314df55fdeSJanie Lu #include <sys/types.h>
324df55fdeSJanie Lu #include <sys/ddi.h>
334df55fdeSJanie Lu #include <sys/modctl.h>
344df55fdeSJanie Lu #include "iospc.h"
354df55fdeSJanie Lu 
364df55fdeSJanie Lu /* Debugging level. */
374df55fdeSJanie Lu #ifdef DEBUG
384df55fdeSJanie Lu int iospc_debug = 0;
394df55fdeSJanie Lu #endif /* DEBUG */
404df55fdeSJanie Lu 
414df55fdeSJanie Lu /* State structure anchor. */
424df55fdeSJanie Lu void *iospc_state_p;
434df55fdeSJanie Lu 
444df55fdeSJanie Lu static int iospc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
454df55fdeSJanie Lu static int iospc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
464df55fdeSJanie Lu static int iospc_create_name_kstat(iospc_grp_t *grp);
474df55fdeSJanie Lu static void iospc_delete_name_kstats(kstat_t **name_kstats_pp,
484df55fdeSJanie Lu     int num_kstats);
494df55fdeSJanie Lu static kstat_t *iospc_create_cntr_kstat(char *name, int dev_inst,
504df55fdeSJanie Lu     int (*update)(kstat_t *, int), iospc_ksinfo_t *ksinfop, int num_pics);
514df55fdeSJanie Lu static int iospc_kstat_update(kstat_t *ksp, int rw);
524df55fdeSJanie Lu static kstat_t *iospc_create_picN_kstat(char *mod_name, int pic,
534df55fdeSJanie Lu     uint64_t mask, int num_ev, iospc_event_t *ev_array);
544df55fdeSJanie Lu 
554df55fdeSJanie Lu iospc_grp_t **iospc_leaf_grps = NULL;
564df55fdeSJanie Lu int iospc_kstat_inited = 0;
574df55fdeSJanie Lu kmutex_t iospc_mutex;
584df55fdeSJanie Lu 
594df55fdeSJanie Lu static struct dev_ops iospc_ops = {
604df55fdeSJanie Lu 	DEVO_REV,
614df55fdeSJanie Lu 	0,
624df55fdeSJanie Lu 	nulldev,
634df55fdeSJanie Lu 	nulldev,
644df55fdeSJanie Lu 	nulldev,
654df55fdeSJanie Lu 	iospc_attach,
664df55fdeSJanie Lu 	iospc_detach,
674df55fdeSJanie Lu 	nodev,
684df55fdeSJanie Lu 	NULL,
694df55fdeSJanie Lu 	NULL,
704df55fdeSJanie Lu 	nodev
714df55fdeSJanie Lu };
724df55fdeSJanie Lu 
734df55fdeSJanie Lu extern struct mod_ops mod_driverops;
744df55fdeSJanie Lu 
754df55fdeSJanie Lu static struct modldrv md = {
764df55fdeSJanie Lu 	&mod_driverops,
774df55fdeSJanie Lu 	"IO Perf Counter Driver",
784df55fdeSJanie Lu 	&iospc_ops,
794df55fdeSJanie Lu };
804df55fdeSJanie Lu 
814df55fdeSJanie Lu static struct modlinkage ml = {
824df55fdeSJanie Lu 	MODREV_1,
834df55fdeSJanie Lu 	(void *)&md,
844df55fdeSJanie Lu 	NULL
854df55fdeSJanie Lu };
864df55fdeSJanie Lu 
874df55fdeSJanie Lu /*
884df55fdeSJanie Lu  * One-time module-wide initialization.
894df55fdeSJanie Lu  */
904df55fdeSJanie Lu int
_init(void)914df55fdeSJanie Lu _init(void)
924df55fdeSJanie Lu {
934df55fdeSJanie Lu 	int rval;
944df55fdeSJanie Lu 
954df55fdeSJanie Lu 	/* Initialize per-leaf soft state pointer. */
964df55fdeSJanie Lu 	if ((rval = ddi_soft_state_init(&iospc_state_p,
974df55fdeSJanie Lu 	    sizeof (iospc_t), 1)) != DDI_SUCCESS)
984df55fdeSJanie Lu 		return (rval);
994df55fdeSJanie Lu 
1004df55fdeSJanie Lu 	/* If all checks out, install the module. */
1014df55fdeSJanie Lu 	if ((rval = mod_install(&ml)) != DDI_SUCCESS) {
1024df55fdeSJanie Lu 		ddi_soft_state_fini(&iospc_state_p);
1034df55fdeSJanie Lu 		return (rval);
1044df55fdeSJanie Lu 	}
1054df55fdeSJanie Lu 	mutex_init(&iospc_mutex, NULL, MUTEX_DRIVER, NULL);
1064df55fdeSJanie Lu 	return (DDI_SUCCESS);
1074df55fdeSJanie Lu }
1084df55fdeSJanie Lu 
1094df55fdeSJanie Lu /*
1104df55fdeSJanie Lu  * One-time module-wide cleanup, after last detach is done.
1114df55fdeSJanie Lu  */
1124df55fdeSJanie Lu int
_fini(void)1134df55fdeSJanie Lu _fini(void)
1144df55fdeSJanie Lu {
1154df55fdeSJanie Lu 	int rval;
1164df55fdeSJanie Lu 
1174df55fdeSJanie Lu 	/*
1184df55fdeSJanie Lu 	 * Remove the module first as this operation is the only thing here
1194df55fdeSJanie Lu 	 * which can fail.
1204df55fdeSJanie Lu 	 */
1214df55fdeSJanie Lu 	rval = mod_remove(&ml);
1224df55fdeSJanie Lu 	if (rval != DDI_SUCCESS)
1234df55fdeSJanie Lu 		return (rval);
1244df55fdeSJanie Lu 
1254df55fdeSJanie Lu 	if (iospc_leaf_grps != NULL) {
1264df55fdeSJanie Lu 		iospc_kstat_fini();
1274df55fdeSJanie Lu 		mutex_enter(&iospc_mutex);
1284df55fdeSJanie Lu 		iospc_kstat_inited = 0;
1294df55fdeSJanie Lu 		(void) rfios_unbind_group();
1304df55fdeSJanie Lu 		iospc_leaf_grps = NULL;
1314df55fdeSJanie Lu 		mutex_exit(&iospc_mutex);
1324df55fdeSJanie Lu 	}
1334df55fdeSJanie Lu 
1344df55fdeSJanie Lu 	mutex_destroy(&iospc_mutex);
1354df55fdeSJanie Lu 
1364df55fdeSJanie Lu 	/* Free px soft state */
1374df55fdeSJanie Lu 	ddi_soft_state_fini(&iospc_state_p);
1384df55fdeSJanie Lu 
1394df55fdeSJanie Lu 	return (DDI_SUCCESS);
1404df55fdeSJanie Lu }
1414df55fdeSJanie Lu 
1424df55fdeSJanie Lu int
_info(struct modinfo * modinfop)1434df55fdeSJanie Lu _info(struct modinfo *modinfop)
1444df55fdeSJanie Lu {
1454df55fdeSJanie Lu 	return (mod_info(&ml, modinfop));
1464df55fdeSJanie Lu }
1474df55fdeSJanie Lu 
1484df55fdeSJanie Lu /*
1494df55fdeSJanie Lu  * Per-instance initialization.  Suspend/resume not supported.
1504df55fdeSJanie Lu  */
1514df55fdeSJanie Lu static int
iospc_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)1524df55fdeSJanie Lu iospc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
1534df55fdeSJanie Lu {
1544df55fdeSJanie Lu 	iospc_t *iospc_p;
1554df55fdeSJanie Lu 	int instance = ddi_get_instance(dip);
1564df55fdeSJanie Lu 	char *ptr;
1574df55fdeSJanie Lu 
1584df55fdeSJanie Lu 	IOSPC_DBG2("iospc: iospc_attach: enter\n");
1594df55fdeSJanie Lu 	switch (cmd) {
1604df55fdeSJanie Lu 	case DDI_RESUME:
1614df55fdeSJanie Lu 	case DDI_ATTACH:
1624df55fdeSJanie Lu 		/* Initialize one-time kstat structures. */
1634df55fdeSJanie Lu 		mutex_enter(&iospc_mutex);
1644df55fdeSJanie Lu 		if (!iospc_kstat_inited) {
1654df55fdeSJanie Lu 			if ((ddi_prop_lookup_string(DDI_DEV_T_ANY, dip,
1664df55fdeSJanie Lu 			    0, "compatible", &ptr)) != DDI_PROP_SUCCESS)
1674df55fdeSJanie Lu 				goto bad_property;
1684df55fdeSJanie Lu 
1694df55fdeSJanie Lu 			if ((strcmp(ptr, "SUNW,ktios-pr") == 0) ||
1704df55fdeSJanie Lu 			    (strcmp(ptr, "SUNW,rfios-pr") == 0)) {
1714df55fdeSJanie Lu 				iospc_leaf_grps = rfios_bind_group();
1724df55fdeSJanie Lu 			} else {
1734df55fdeSJanie Lu 				ddi_prop_free(ptr);
1744df55fdeSJanie Lu 				goto bad_property;
1754df55fdeSJanie Lu 			}
1764df55fdeSJanie Lu 
1774df55fdeSJanie Lu 			ddi_prop_free(ptr);
1784df55fdeSJanie Lu 
1794df55fdeSJanie Lu 			if (iospc_kstat_init() != DDI_SUCCESS)
1804df55fdeSJanie Lu 				goto bad_kstat_init;
1814df55fdeSJanie Lu 
1824df55fdeSJanie Lu 			iospc_kstat_inited++;
1834df55fdeSJanie Lu 		}
1844df55fdeSJanie Lu 		mutex_exit(&iospc_mutex);
1854df55fdeSJanie Lu 
1864df55fdeSJanie Lu 		if (ddi_soft_state_zalloc(iospc_state_p, instance) !=
1874df55fdeSJanie Lu 		    DDI_SUCCESS) {
1884df55fdeSJanie Lu 			goto bad_softstate;
1894df55fdeSJanie Lu 		}
1904df55fdeSJanie Lu 
1914df55fdeSJanie Lu 		iospc_p = (iospc_t *)ddi_get_soft_state(iospc_state_p,
1924df55fdeSJanie Lu 		    instance);
1934df55fdeSJanie Lu 
1944df55fdeSJanie Lu 		iospc_p->iospc_dip = dip;
1954df55fdeSJanie Lu 
1964df55fdeSJanie Lu 		/* Set up kstats. */
1974df55fdeSJanie Lu 
1984df55fdeSJanie Lu 		if (iospc_kstat_attach(iospc_p) != DDI_SUCCESS)
1994df55fdeSJanie Lu 			goto bad_kstat_attach;
2004df55fdeSJanie Lu 
2014df55fdeSJanie Lu 		IOSPC_DBG2("iospc: iospc_attach: exit SUCCESS\n");
2024df55fdeSJanie Lu 
2034df55fdeSJanie Lu 		return (DDI_SUCCESS);
2044df55fdeSJanie Lu 
2054df55fdeSJanie Lu bad_kstat_attach:
2064df55fdeSJanie Lu 		(void) ddi_soft_state_free(iospc_state_p, instance);
2074df55fdeSJanie Lu bad_softstate:
2084df55fdeSJanie Lu 		iospc_kstat_fini();
2094df55fdeSJanie Lu bad_kstat_init:
2104df55fdeSJanie Lu bad_property:
2114df55fdeSJanie Lu 		mutex_enter(&iospc_mutex);
2124df55fdeSJanie Lu 		IOSPC_DBG2("iospc: iospc_attach: exit FAILURE\n");
2134df55fdeSJanie Lu 		return (DDI_FAILURE);
2144df55fdeSJanie Lu 
2154df55fdeSJanie Lu 	default:
2164df55fdeSJanie Lu 		return (DDI_FAILURE);
2174df55fdeSJanie Lu 	}
2184df55fdeSJanie Lu }
2194df55fdeSJanie Lu 
2204df55fdeSJanie Lu /*
2214df55fdeSJanie Lu  * Per-instance cleanup.  Suspend/resume not supported.
2224df55fdeSJanie Lu  */
2234df55fdeSJanie Lu static int
iospc_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)2244df55fdeSJanie Lu iospc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
2254df55fdeSJanie Lu {
2264df55fdeSJanie Lu 	int instance = ddi_get_instance(dip);
2274df55fdeSJanie Lu 
2284df55fdeSJanie Lu 	IOSPC_DBG2("iospc: iospc_detach: enter\n");
2294df55fdeSJanie Lu 	iospc_t *iospc_p = (iospc_t *)ddi_get_soft_state(
2304df55fdeSJanie Lu 	    iospc_state_p, instance);
2314df55fdeSJanie Lu 
2324df55fdeSJanie Lu 	switch (cmd) {
2334df55fdeSJanie Lu 	case DDI_SUSPEND:
2344df55fdeSJanie Lu 	case DDI_DETACH:
2354df55fdeSJanie Lu 		iospc_kstat_detach(iospc_p);
2364df55fdeSJanie Lu 		(void) ddi_soft_state_free(iospc_state_p, instance);
2374df55fdeSJanie Lu 
2384df55fdeSJanie Lu 		IOSPC_DBG2("iospc: iospc_detach: exit - SUCCESS\n");
2394df55fdeSJanie Lu 		return (DDI_SUCCESS);
2404df55fdeSJanie Lu 
2414df55fdeSJanie Lu 	default:
2424df55fdeSJanie Lu 		IOSPC_DBG2("iospc: iospc_detach: exit - FAILURE\n");
2434df55fdeSJanie Lu 		return (DDI_FAILURE);
2444df55fdeSJanie Lu 	}
2454df55fdeSJanie Lu }
2464df55fdeSJanie Lu 
2474df55fdeSJanie Lu #define	PIC_STR_LEN	5	/* Size of a PICx name string. */
2484df55fdeSJanie Lu 
2494df55fdeSJanie Lu /*
2504df55fdeSJanie Lu  * One-time initialization for this module.
2514df55fdeSJanie Lu  */
2524df55fdeSJanie Lu int
iospc_kstat_init()2534df55fdeSJanie Lu iospc_kstat_init()
2544df55fdeSJanie Lu {
2554df55fdeSJanie Lu 	iospc_grp_t **grp_pp;
2564df55fdeSJanie Lu 	iospc_grp_t *grp_p;
2574df55fdeSJanie Lu 
2584df55fdeSJanie Lu 	IOSPC_DBG2("iospc: kstat_init: enter\n");
2594df55fdeSJanie Lu 
2604df55fdeSJanie Lu 	/*
2614df55fdeSJanie Lu 	 * Initialize the name kstats for each group, drawing upon the table
2624df55fdeSJanie Lu 	 * for values.
2634df55fdeSJanie Lu 	 */
2644df55fdeSJanie Lu 	for (grp_pp = iospc_leaf_grps; *grp_pp != NULL; grp_pp++) {
2654df55fdeSJanie Lu 
2664df55fdeSJanie Lu 		grp_p = *grp_pp;
2674df55fdeSJanie Lu 
2684df55fdeSJanie Lu 		IOSPC_DBG2("Setting up group for %s\n", grp_p->grp_name);
2694df55fdeSJanie Lu 
2704df55fdeSJanie Lu 		/* Create basic pic event-type pair. */
2714df55fdeSJanie Lu 		grp_p->name_kstats_pp = kmem_zalloc((grp_p->num_counters *
2724df55fdeSJanie Lu 		    sizeof (kstat_t)), KM_SLEEP);
2734df55fdeSJanie Lu 		if (iospc_create_name_kstat(grp_p) != DDI_SUCCESS) {
2744df55fdeSJanie Lu 			iospc_kstat_fini();
2754df55fdeSJanie Lu 			IOSPC_DBG1("iospc: init: failure exit\n");
2764df55fdeSJanie Lu 			return (DDI_FAILURE);
2774df55fdeSJanie Lu 		}
2784df55fdeSJanie Lu 	}
2794df55fdeSJanie Lu 
2804df55fdeSJanie Lu 	IOSPC_DBG2("iospc: kstat_init: success exit\n");
2814df55fdeSJanie Lu 
2824df55fdeSJanie Lu 	return (DDI_SUCCESS);
2834df55fdeSJanie Lu }
2844df55fdeSJanie Lu 
2854df55fdeSJanie Lu /*
2864df55fdeSJanie Lu  * Per-instance initialization for this module.
2874df55fdeSJanie Lu  */
2884df55fdeSJanie Lu int
iospc_kstat_attach(iospc_t * iospc_p)2894df55fdeSJanie Lu iospc_kstat_attach(iospc_t *iospc_p)
2904df55fdeSJanie Lu {
2914df55fdeSJanie Lu 	iospc_grp_t **grp_pp;
2924df55fdeSJanie Lu 	iospc_grp_t *grp_p;
2934df55fdeSJanie Lu 	iospc_ksinfo_t *ksinfo_p;
2944df55fdeSJanie Lu 
2954df55fdeSJanie Lu 	int i;
2964df55fdeSJanie Lu 
2974df55fdeSJanie Lu 	IOSPC_DBG2("iospc: kstat_attach %d: enter\n",
2984df55fdeSJanie Lu 	    ddi_get_instance(iospc_p->iospc_dip));
2994df55fdeSJanie Lu 
3004df55fdeSJanie Lu 	/* Set up kstats for each group. */
3014df55fdeSJanie Lu 	for (i = 0, grp_pp = iospc_leaf_grps; *grp_pp != NULL; i++, grp_pp++) {
3024df55fdeSJanie Lu 
3034df55fdeSJanie Lu 		if (i >= IOSPC_MAX_NUM_GRPS)
3044df55fdeSJanie Lu 			goto err;
3054df55fdeSJanie Lu 
3064df55fdeSJanie Lu 		grp_p = *grp_pp;
3074df55fdeSJanie Lu 
3084df55fdeSJanie Lu 		/*
3094df55fdeSJanie Lu 		 * ksinfo_p keeps all info needed by iospc_kstat_update,
3104df55fdeSJanie Lu 		 * which is fired off asynchronously on demand by the kstat
3114df55fdeSJanie Lu 		 * framework.
3124df55fdeSJanie Lu 		 */
3134df55fdeSJanie Lu 		ksinfo_p = (iospc_ksinfo_t *)kmem_zalloc(
3144df55fdeSJanie Lu 		    sizeof (iospc_ksinfo_t), KM_SLEEP);
3154df55fdeSJanie Lu 
3164df55fdeSJanie Lu 		ksinfo_p->iospc_p = iospc_p;
3174df55fdeSJanie Lu 		ksinfo_p->grp_p  = grp_p;
3184df55fdeSJanie Lu 
3194df55fdeSJanie Lu 		/* Also save in state structure, for later cleanup. */
3204df55fdeSJanie Lu 		iospc_p->iospc_ksinfo_p[i] = ksinfo_p;
3214df55fdeSJanie Lu 
3224df55fdeSJanie Lu 		/* Create counter kstats */
3234df55fdeSJanie Lu 		ksinfo_p->cntr_ksp = iospc_create_cntr_kstat(grp_p->grp_name,
3244df55fdeSJanie Lu 		    ddi_get_instance(iospc_p->iospc_dip),
3254df55fdeSJanie Lu 		    iospc_kstat_update, ksinfo_p, grp_p->num_counters);
3264df55fdeSJanie Lu 
3274df55fdeSJanie Lu 		if (ksinfo_p->cntr_ksp == NULL)
3284df55fdeSJanie Lu 			goto err;
3294df55fdeSJanie Lu 
3304df55fdeSJanie Lu 		if (grp_p->access_init(iospc_p, ksinfo_p) != SUCCESS)
3314df55fdeSJanie Lu 			goto err;
3324df55fdeSJanie Lu 	}
3334df55fdeSJanie Lu 
3344df55fdeSJanie Lu 	IOSPC_DBG2("iospc: kstat_attach: success exit\n");
3354df55fdeSJanie Lu 	return (DDI_SUCCESS);
3364df55fdeSJanie Lu err:
3374df55fdeSJanie Lu 	iospc_kstat_detach(iospc_p);
3384df55fdeSJanie Lu 	IOSPC_DBG2("iospc: kstat_attach: failure exit\n");
3394df55fdeSJanie Lu 	return (DDI_FAILURE);
3404df55fdeSJanie Lu }
3414df55fdeSJanie Lu 
3424df55fdeSJanie Lu /*
3434df55fdeSJanie Lu  * Create the name kstats for each group.
3444df55fdeSJanie Lu  */
3454df55fdeSJanie Lu static int
iospc_create_name_kstat(iospc_grp_t * grp_p)3464df55fdeSJanie Lu iospc_create_name_kstat(iospc_grp_t *grp_p)
3474df55fdeSJanie Lu {
3484df55fdeSJanie Lu 	int i;
3494df55fdeSJanie Lu 
3504df55fdeSJanie Lu 	for (i = 0; i < grp_p->num_counters; i++) {
3514df55fdeSJanie Lu 		grp_p->name_kstats_pp[i] = iospc_create_picN_kstat(
3524df55fdeSJanie Lu 		    grp_p->grp_name, i,
3534df55fdeSJanie Lu 		    grp_p->regsel_p->fields_p[i].event_offset,
3544df55fdeSJanie Lu 		    grp_p->regsel_p->fields_p[i].num_events,
3554df55fdeSJanie Lu 		    grp_p->regsel_p->fields_p[i].events_p);
3564df55fdeSJanie Lu 
3574df55fdeSJanie Lu 		if (grp_p->name_kstats_pp[i] == NULL)
3584df55fdeSJanie Lu 			return (DDI_FAILURE);
3594df55fdeSJanie Lu 	}
3604df55fdeSJanie Lu 	return (DDI_SUCCESS);
3614df55fdeSJanie Lu }
3624df55fdeSJanie Lu 
3634df55fdeSJanie Lu /*
3644df55fdeSJanie Lu  * Create the picN kstat. Returns a pointer to the
3654df55fdeSJanie Lu  * kstat which the driver must store to allow it
3664df55fdeSJanie Lu  * to be deleted when necessary.
3674df55fdeSJanie Lu  */
3684df55fdeSJanie Lu static kstat_t *
iospc_create_picN_kstat(char * mod_name,int pic,uint64_t ev_offset,int num_ev,iospc_event_t * ev_array)3694df55fdeSJanie Lu iospc_create_picN_kstat(char *mod_name, int pic, uint64_t ev_offset,
3704df55fdeSJanie Lu     int num_ev, iospc_event_t *ev_array)
3714df55fdeSJanie Lu {
3724df55fdeSJanie Lu 	int event;
3734df55fdeSJanie Lu 	char pic_name[PIC_STR_LEN];
3744df55fdeSJanie Lu 	kstat_t	*picN_ksp = NULL;
3754df55fdeSJanie Lu 	struct kstat_named *pic_named_data;
3764df55fdeSJanie Lu 
3774df55fdeSJanie Lu 	(void) snprintf(pic_name, PIC_STR_LEN, "pic%1d", pic);
3784df55fdeSJanie Lu 
3794df55fdeSJanie Lu 	if ((picN_ksp = kstat_create(mod_name, 0, pic_name,
380*8b92a81bSToomas Soome 	    "bus", KSTAT_TYPE_NAMED, num_ev, 0)) == NULL) {
3814df55fdeSJanie Lu 		return (NULL);
3824df55fdeSJanie Lu 	}
3834df55fdeSJanie Lu 
3844df55fdeSJanie Lu 	/* NOTE: Number of events is assumed to always be non-zero. */
3854df55fdeSJanie Lu 
3864df55fdeSJanie Lu 	pic_named_data = (struct kstat_named *)picN_ksp->ks_data;
3874df55fdeSJanie Lu 
3884df55fdeSJanie Lu 	/*
3894df55fdeSJanie Lu 	 * Fill up data section of the kstat
3904df55fdeSJanie Lu 	 * Write event names and their associated pcr masks.
3914df55fdeSJanie Lu 	 * num_ev - 1 is because CLEAR_PIC is added separately.
3924df55fdeSJanie Lu 	 */
3934df55fdeSJanie Lu 	for (event = 0; event < num_ev - 1; event++) {
3944df55fdeSJanie Lu 		pic_named_data[event].value.ui64 =
3954df55fdeSJanie Lu 		    ev_array[event].value << ev_offset;
3964df55fdeSJanie Lu 
3974df55fdeSJanie Lu 		kstat_named_init(&pic_named_data[event],
3984df55fdeSJanie Lu 		    ev_array[event].name, KSTAT_DATA_UINT64);
3994df55fdeSJanie Lu 	}
4004df55fdeSJanie Lu 
4014df55fdeSJanie Lu 	/*
4024df55fdeSJanie Lu 	 * add the clear_pic entry
4034df55fdeSJanie Lu 	 */
4044df55fdeSJanie Lu 	pic_named_data[event].value.ui64 =
4054df55fdeSJanie Lu 	    (uint64_t)~(ev_array[event].value << ev_offset);
4064df55fdeSJanie Lu 
4074df55fdeSJanie Lu 	kstat_named_init(&pic_named_data[event], ev_array[event].name,
4084df55fdeSJanie Lu 	    KSTAT_DATA_UINT64);
4094df55fdeSJanie Lu 
4104df55fdeSJanie Lu 	kstat_install(picN_ksp);
4114df55fdeSJanie Lu 
4124df55fdeSJanie Lu 	return (picN_ksp);
4134df55fdeSJanie Lu }
4144df55fdeSJanie Lu 
4154df55fdeSJanie Lu /*
4164df55fdeSJanie Lu  * Create the "counters" kstat.
4174df55fdeSJanie Lu  */
4184df55fdeSJanie Lu static kstat_t *
iospc_create_cntr_kstat(char * name,int dev_inst,int (* update)(kstat_t *,int),iospc_ksinfo_t * ksinfop,int num_pics)4194df55fdeSJanie Lu iospc_create_cntr_kstat(char *name, int dev_inst,
4204df55fdeSJanie Lu     int (*update)(kstat_t *, int), iospc_ksinfo_t *ksinfop, int num_pics)
4214df55fdeSJanie Lu {
4224df55fdeSJanie Lu 	int i;
4234df55fdeSJanie Lu 	char pic_str[PIC_STR_LEN];
4244df55fdeSJanie Lu 	struct kstat *counters_ksp;
4254df55fdeSJanie Lu 	struct kstat_named *counters_named_data;
4264df55fdeSJanie Lu 
4274df55fdeSJanie Lu 	IOSPC_DBG2("iospc_create_cntr_kstat: name: %s instance: %d\n",
4284df55fdeSJanie Lu 	    name, dev_inst);
4294df55fdeSJanie Lu 
4304df55fdeSJanie Lu 	/*
4314df55fdeSJanie Lu 	 * Size of kstat is num_pics + 1. extra one for pcr.
4324df55fdeSJanie Lu 	 */
4334df55fdeSJanie Lu 
4344df55fdeSJanie Lu 	if ((counters_ksp = kstat_create(name, dev_inst, "counters", "bus",
4354df55fdeSJanie Lu 	    KSTAT_TYPE_NAMED, num_pics + 1, KSTAT_FLAG_WRITABLE)) == NULL) {
4364df55fdeSJanie Lu 		return (NULL);
4374df55fdeSJanie Lu 	}
4384df55fdeSJanie Lu 
4394df55fdeSJanie Lu 	counters_named_data = (struct kstat_named *)(counters_ksp->ks_data);
4404df55fdeSJanie Lu 	kstat_named_init(&counters_named_data[0], "pcr", KSTAT_DATA_UINT64);
4414df55fdeSJanie Lu 
4424df55fdeSJanie Lu 	for (i = 0; i < num_pics; i++) {
4434df55fdeSJanie Lu 		(void) snprintf(pic_str, PIC_STR_LEN, "pic%1d", i);
4444df55fdeSJanie Lu 
4454df55fdeSJanie Lu 		kstat_named_init(&counters_named_data[i+1], pic_str,
4464df55fdeSJanie Lu 		    KSTAT_DATA_UINT64);
4474df55fdeSJanie Lu 	}
4484df55fdeSJanie Lu 
4494df55fdeSJanie Lu 	/*
4504df55fdeSJanie Lu 	 * Store the reg type and other info. in the kstat's private field
4514df55fdeSJanie Lu 	 * so that they are available to the update function.
4524df55fdeSJanie Lu 	 */
4534df55fdeSJanie Lu 	counters_ksp->ks_private = (void *)ksinfop;
4544df55fdeSJanie Lu 	counters_ksp->ks_update = update;
4554df55fdeSJanie Lu 
4564df55fdeSJanie Lu 	kstat_install(counters_ksp);
4574df55fdeSJanie Lu 
4584df55fdeSJanie Lu 	return (counters_ksp);
4594df55fdeSJanie Lu }
4604df55fdeSJanie Lu 
4614df55fdeSJanie Lu /*
4624df55fdeSJanie Lu  * Program a performance counter.
4634df55fdeSJanie Lu  *
4644df55fdeSJanie Lu  * reggroup is which type of counter.
4654df55fdeSJanie Lu  * counter is the counter number.
4664df55fdeSJanie Lu  * event is the event to program for that counter.
4674df55fdeSJanie Lu  */
4684df55fdeSJanie Lu static int
iospc_perfcnt_program(iospc_t * iospc_p,iospc_grp_t * grp_p,iospc_ksinfo_t * ksinfo_p,uint64_t new_events)4694df55fdeSJanie Lu iospc_perfcnt_program(iospc_t *iospc_p, iospc_grp_t *grp_p,
4704df55fdeSJanie Lu     iospc_ksinfo_t *ksinfo_p, uint64_t new_events)
4714df55fdeSJanie Lu {
4724df55fdeSJanie Lu 	uint64_t old_events;
4734df55fdeSJanie Lu 	int rval = SUCCESS;
4744df55fdeSJanie Lu 	uint64_t event_mask;
4754df55fdeSJanie Lu 	int counter;
4764df55fdeSJanie Lu 
4774df55fdeSJanie Lu 	IOSPC_DBG1(
4784df55fdeSJanie Lu 	    "iospc_perfcnt_program enter: new_events:0x%" PRIx64 "\n",
4794df55fdeSJanie Lu 	    new_events);
4804df55fdeSJanie Lu 
4814df55fdeSJanie Lu 	if ((rval = grp_p->access(iospc_p, ksinfo_p->arg, IOSPC_REG_READ,
4824df55fdeSJanie Lu 	    grp_p->regsel_p->regoff, &old_events)) != SUCCESS)
4834df55fdeSJanie Lu 		goto done_pgm;
4844df55fdeSJanie Lu 
4854df55fdeSJanie Lu 	IOSPC_DBG1("  old_events:0x%" PRIx64 "\n", old_events);
4864df55fdeSJanie Lu 
4874df55fdeSJanie Lu 	for (counter = 0; counter < grp_p->num_counters; counter++) {
4884df55fdeSJanie Lu 
4894df55fdeSJanie Lu 		if (grp_p->counters_p[counter].zero_regoff == NO_REGISTER)
4904df55fdeSJanie Lu 			continue;
4914df55fdeSJanie Lu 
4924df55fdeSJanie Lu 		event_mask = grp_p->regsel_p->fields_p[counter].event_mask <<
4934df55fdeSJanie Lu 		    grp_p->regsel_p->fields_p[counter].event_offset;
4944df55fdeSJanie Lu 
4954df55fdeSJanie Lu 		IOSPC_DBG1(
4964df55fdeSJanie Lu 		    "grp:%s, counter:%d, zero_regoff:0x%lx, "
4974df55fdeSJanie Lu 		    "event_mask:0x%" PRIx64 ", old&mask:0x%lx, "
4984df55fdeSJanie Lu 		    "new&mask:0x%lx\n",
4994df55fdeSJanie Lu 		    grp_p->grp_name, counter,
5004df55fdeSJanie Lu 		    grp_p->counters_p[counter].zero_regoff,
5014df55fdeSJanie Lu 		    event_mask, old_events & event_mask,
5024df55fdeSJanie Lu 		    new_events & event_mask);
5034df55fdeSJanie Lu 
5044df55fdeSJanie Lu 		if ((old_events & event_mask) ==
5054df55fdeSJanie Lu 		    (new_events & event_mask))
5064df55fdeSJanie Lu 			continue;
5074df55fdeSJanie Lu 
5084df55fdeSJanie Lu 		IOSPC_DBG1("Zeroing counter %d\n", counter);
5094df55fdeSJanie Lu 
5104df55fdeSJanie Lu 		if ((rval = grp_p->access(iospc_p, ksinfo_p->arg,
5114df55fdeSJanie Lu 		    IOSPC_REG_WRITE, grp_p->counters_p[counter].zero_regoff,
5124df55fdeSJanie Lu 		    &grp_p->counters_p[counter].zero_value)) != SUCCESS)
5134df55fdeSJanie Lu 			goto done_pgm;
5144df55fdeSJanie Lu 	}
5154df55fdeSJanie Lu 
5164df55fdeSJanie Lu 	if (old_events != new_events) {
5174df55fdeSJanie Lu 
5184df55fdeSJanie Lu 		IOSPC_DBG1("old != new, setting event reg %ld to 0x%lx\n",
5194df55fdeSJanie Lu 		    grp_p->regsel_p->regoff, new_events);
5204df55fdeSJanie Lu 
5214df55fdeSJanie Lu 		if ((rval = grp_p->access(iospc_p, ksinfo_p->arg,
5224df55fdeSJanie Lu 		    IOSPC_REG_WRITE, grp_p->regsel_p->regoff, &new_events))
5234df55fdeSJanie Lu 		    != SUCCESS) {
5244df55fdeSJanie Lu 			IOSPC_DBG1(
5254df55fdeSJanie Lu 			    "Write of new event data failed, "
5264df55fdeSJanie Lu 			    "select reg offset: %ld\n",
5274df55fdeSJanie Lu 			    grp_p->regsel_p->regoff);
5284df55fdeSJanie Lu 			goto done_pgm;
5294df55fdeSJanie Lu 		}
5304df55fdeSJanie Lu 	}
5314df55fdeSJanie Lu done_pgm:
5324df55fdeSJanie Lu 	IOSPC_DBG1("iospc_perfcnt_program: returning status %d.\n", rval);
5334df55fdeSJanie Lu 	return (rval);
5344df55fdeSJanie Lu }
5354df55fdeSJanie Lu 
5364df55fdeSJanie Lu /*
5374df55fdeSJanie Lu  * kstat update function. Handles reads/writes
5384df55fdeSJanie Lu  * from/to kstat.
5394df55fdeSJanie Lu  */
5404df55fdeSJanie Lu static int
iospc_kstat_update(kstat_t * ksp,int rw)5414df55fdeSJanie Lu iospc_kstat_update(kstat_t *ksp, int rw)
5424df55fdeSJanie Lu {
5434df55fdeSJanie Lu 	struct kstat_named *data_p;
5444df55fdeSJanie Lu 	int counter;
5454df55fdeSJanie Lu 	iospc_ksinfo_t *ksinfop = ksp->ks_private;
5464df55fdeSJanie Lu 	iospc_grp_t *grp_p = ksinfop->grp_p;
5474df55fdeSJanie Lu 	iospc_t *iospc_p = ksinfop->iospc_p;
5484df55fdeSJanie Lu 
5494df55fdeSJanie Lu 	data_p = (struct kstat_named *)ksp->ks_data;
5504df55fdeSJanie Lu 
5514df55fdeSJanie Lu 	if (rw == KSTAT_WRITE) {
5524df55fdeSJanie Lu 
5534df55fdeSJanie Lu 		IOSPC_DBG2("iospc_kstat_update: wr %ld\n",
5544df55fdeSJanie Lu 		    data_p[0].value.ui64);
5554df55fdeSJanie Lu 
5564df55fdeSJanie Lu 		/*
5574df55fdeSJanie Lu 		 * Fields without programmable events won't be zeroed as
5584df55fdeSJanie Lu 		 * iospc_perfcnt_program is what zeros them.
5594df55fdeSJanie Lu 		 */
5604df55fdeSJanie Lu 
5614df55fdeSJanie Lu 		/* This group has programmable events. */
5624df55fdeSJanie Lu 		if (grp_p->regsel_p->regoff != NO_REGISTER) {
5634df55fdeSJanie Lu 
5644df55fdeSJanie Lu 			IOSPC_DBG2("write: regoff has valid register\n");
5654df55fdeSJanie Lu 			if (iospc_perfcnt_program(iospc_p, grp_p, ksinfop,
5664df55fdeSJanie Lu 			    data_p[0].value.ui64) != SUCCESS)
5674df55fdeSJanie Lu 				return (EIO);
5684df55fdeSJanie Lu 		}
5694df55fdeSJanie Lu 
5704df55fdeSJanie Lu 	} else {	/* Read the event register and all of the counters. */
5714df55fdeSJanie Lu 
5724df55fdeSJanie Lu 		/* This group has programmable events. */
5734df55fdeSJanie Lu 		if (grp_p->regsel_p->regoff != NO_REGISTER) {
5744df55fdeSJanie Lu 
5754df55fdeSJanie Lu 			IOSPC_DBG2("read: regoff has valid register\n");
5764df55fdeSJanie Lu 
5774df55fdeSJanie Lu 			if (grp_p->access(iospc_p, ksinfop->arg, IOSPC_REG_READ,
5784df55fdeSJanie Lu 			    grp_p->regsel_p->regoff, &data_p[0].value.ui64)
5794df55fdeSJanie Lu 			    != SUCCESS)
5804df55fdeSJanie Lu 				return (EIO);
5814df55fdeSJanie Lu 		} else
5824df55fdeSJanie Lu 			data_p[0].value.ui64 = 0ull;
5834df55fdeSJanie Lu 
5844df55fdeSJanie Lu 		IOSPC_DBG2("iospc_kstat_update: rd event %lx\n",
5854df55fdeSJanie Lu 		    data_p[0].value.ui64);
5864df55fdeSJanie Lu 
5874df55fdeSJanie Lu 		for (counter = 0; counter < grp_p->num_counters; counter++) {
5884df55fdeSJanie Lu 
5894df55fdeSJanie Lu 			if (grp_p->access(iospc_p, ksinfop->arg, IOSPC_REG_READ,
5904df55fdeSJanie Lu 			    grp_p->counters_p[counter].regoff,
5914df55fdeSJanie Lu 			    &data_p[counter + 1].value.ui64) != SUCCESS)
5924df55fdeSJanie Lu 				return (EIO);
5934df55fdeSJanie Lu 
5944df55fdeSJanie Lu 			IOSPC_DBG2("cntr%d, off:0x%lx, val:0x%lx\n", counter,
5954df55fdeSJanie Lu 			    grp_p->counters_p[counter].regoff,
5964df55fdeSJanie Lu 			    data_p[counter + 1].value.ui64);
5974df55fdeSJanie Lu 		}
5984df55fdeSJanie Lu 	}
5994df55fdeSJanie Lu 	return (SUCCESS);
6004df55fdeSJanie Lu }
6014df55fdeSJanie Lu 
6024df55fdeSJanie Lu void
iospc_kstat_fini()6034df55fdeSJanie Lu iospc_kstat_fini()
6044df55fdeSJanie Lu {
6054df55fdeSJanie Lu 	iospc_grp_t **grp_pp;
6064df55fdeSJanie Lu 	iospc_grp_t *grp_p;
6074df55fdeSJanie Lu 	int j;
6084df55fdeSJanie Lu 
6094df55fdeSJanie Lu 	IOSPC_DBG2("iospc_kstat_fini called\n");
6104df55fdeSJanie Lu 
6114df55fdeSJanie Lu 	for (j = 0, grp_pp = iospc_leaf_grps; *grp_pp != NULL; j++, grp_pp++) {
6124df55fdeSJanie Lu 		grp_p = *grp_pp;
6134df55fdeSJanie Lu 		if (grp_p->name_kstats_pp != NULL) {
6144df55fdeSJanie Lu 			iospc_delete_name_kstats(grp_p->name_kstats_pp,
6154df55fdeSJanie Lu 			    grp_p->num_counters);
6164df55fdeSJanie Lu 			kmem_free(grp_p->name_kstats_pp,
6174df55fdeSJanie Lu 			    grp_p->num_counters * sizeof (kstat_t));
6184df55fdeSJanie Lu 			grp_p->name_kstats_pp = NULL;
6194df55fdeSJanie Lu 		}
6204df55fdeSJanie Lu 	}
6214df55fdeSJanie Lu }
6224df55fdeSJanie Lu 
6234df55fdeSJanie Lu static void
iospc_delete_name_kstats(kstat_t ** name_kstats_pp,int num_kstats)6244df55fdeSJanie Lu iospc_delete_name_kstats(kstat_t **name_kstats_pp, int num_kstats)
6254df55fdeSJanie Lu {
6264df55fdeSJanie Lu 	int i;
6274df55fdeSJanie Lu 
6284df55fdeSJanie Lu 	if (name_kstats_pp != NULL) {
6294df55fdeSJanie Lu 		for (i = 0; i < num_kstats; i++) {
6304df55fdeSJanie Lu 			if (name_kstats_pp[i] != NULL)
6314df55fdeSJanie Lu 				kstat_delete(name_kstats_pp[i]);
6324df55fdeSJanie Lu 		}
6334df55fdeSJanie Lu 	}
6344df55fdeSJanie Lu }
6354df55fdeSJanie Lu 
6364df55fdeSJanie Lu void
iospc_kstat_detach(iospc_t * iospc_p)6374df55fdeSJanie Lu iospc_kstat_detach(iospc_t *iospc_p)
6384df55fdeSJanie Lu {
6394df55fdeSJanie Lu 	iospc_grp_t **grp_pp;
6404df55fdeSJanie Lu 	iospc_grp_t *grp_p;
6414df55fdeSJanie Lu 	int i;
6424df55fdeSJanie Lu 
6434df55fdeSJanie Lu 	IOSPC_DBG2("iospc_kstat_detach called\n");
6444df55fdeSJanie Lu 
6454df55fdeSJanie Lu 	for (i = 0, grp_pp = iospc_leaf_grps; *grp_pp != NULL; i++, grp_pp++) {
6464df55fdeSJanie Lu 
6474df55fdeSJanie Lu 		if (i >= IOSPC_MAX_NUM_GRPS)
6484df55fdeSJanie Lu 			return;
6494df55fdeSJanie Lu 
6504df55fdeSJanie Lu 		grp_p = *grp_pp;
6514df55fdeSJanie Lu 		if (iospc_p->iospc_ksinfo_p[i] != NULL) {
6524df55fdeSJanie Lu 
6534df55fdeSJanie Lu 			grp_p->access_fini(iospc_p, iospc_p->iospc_ksinfo_p[i]);
6544df55fdeSJanie Lu 
6554df55fdeSJanie Lu 			if (iospc_p->iospc_ksinfo_p[i]->cntr_ksp != NULL)
6564df55fdeSJanie Lu 				kstat_delete(
6574df55fdeSJanie Lu 				    iospc_p->iospc_ksinfo_p[i]->cntr_ksp);
6584df55fdeSJanie Lu 			kmem_free(iospc_p->iospc_ksinfo_p[i],
6594df55fdeSJanie Lu 			    sizeof (iospc_ksinfo_t));
6604df55fdeSJanie Lu 		}
6614df55fdeSJanie Lu 
6624df55fdeSJanie Lu 	}
6634df55fdeSJanie Lu }
664