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