xref: /onnv-gate/usr/src/uts/i86xpv/cpu/generic_cpu/gcpu_poll_xpv.c (revision 12004:93f274d4a367)
17532SSean.Ye@Sun.COM /*
27532SSean.Ye@Sun.COM  * CDDL HEADER START
37532SSean.Ye@Sun.COM  *
47532SSean.Ye@Sun.COM  * The contents of this file are subject to the terms of the
57532SSean.Ye@Sun.COM  * Common Development and Distribution License (the "License").
67532SSean.Ye@Sun.COM  * You may not use this file except in compliance with the License.
77532SSean.Ye@Sun.COM  *
87532SSean.Ye@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97532SSean.Ye@Sun.COM  * or http://www.opensolaris.org/os/licensing.
107532SSean.Ye@Sun.COM  * See the License for the specific language governing permissions
117532SSean.Ye@Sun.COM  * and limitations under the License.
127532SSean.Ye@Sun.COM  *
137532SSean.Ye@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
147532SSean.Ye@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157532SSean.Ye@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
167532SSean.Ye@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
177532SSean.Ye@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
187532SSean.Ye@Sun.COM  *
197532SSean.Ye@Sun.COM  * CDDL HEADER END
207532SSean.Ye@Sun.COM  */
217532SSean.Ye@Sun.COM 
227532SSean.Ye@Sun.COM /*
2310175SStuart.Maybee@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
247532SSean.Ye@Sun.COM  * Use is subject to license terms.
257532SSean.Ye@Sun.COM  */
267532SSean.Ye@Sun.COM 
277532SSean.Ye@Sun.COM /*
287532SSean.Ye@Sun.COM  * "Polled" MCA events in an i86xpv dom0.  A timeout runs in the hypervisor
297532SSean.Ye@Sun.COM  * and checks MCA state.  If it observes valid MCA state in a bank and if
307532SSean.Ye@Sun.COM  * it sees that dom0 has registered a handler for the VIRQ_MCA then it
317532SSean.Ye@Sun.COM  * raises that VIRQ to dom0.  The interrupt handler performs a
327532SSean.Ye@Sun.COM  * hypercall to retrieve the polled telemetry and then pushes that telemetry
337532SSean.Ye@Sun.COM  * into the MSR interpose hash and calls the generic logout code which
347532SSean.Ye@Sun.COM  * will then find the provided interposed MSR values when it performs
357532SSean.Ye@Sun.COM  * cmi_hdl_rdmsr so logout code works unchanged for native or i86xpv dom0.
367532SSean.Ye@Sun.COM  */
377532SSean.Ye@Sun.COM 
387532SSean.Ye@Sun.COM #include <sys/types.h>
397532SSean.Ye@Sun.COM #include <sys/conf.h>
407532SSean.Ye@Sun.COM #include <sys/x86_archext.h>
417532SSean.Ye@Sun.COM #include <sys/mca_x86.h>
427532SSean.Ye@Sun.COM #include <sys/ddi.h>
437532SSean.Ye@Sun.COM #include <sys/spl.h>
447532SSean.Ye@Sun.COM #include <sys/sunddi.h>
457532SSean.Ye@Sun.COM #include <sys/evtchn_impl.h>
467532SSean.Ye@Sun.COM #include <sys/hypervisor.h>
477532SSean.Ye@Sun.COM 
487532SSean.Ye@Sun.COM #include "../../i86pc/cpu/generic_cpu/gcpu.h"
497532SSean.Ye@Sun.COM 
507532SSean.Ye@Sun.COM extern int *gcpu_xpv_telem_read(mc_info_t *, int, uint64_t *);
517532SSean.Ye@Sun.COM extern void gcpu_xpv_telem_ack(int, uint64_t);
527532SSean.Ye@Sun.COM extern void gcpu_xpv_mci_process(mc_info_t *, int, cmi_mca_regs_t *, size_t);
537532SSean.Ye@Sun.COM 
547532SSean.Ye@Sun.COM int gcpu_xpv_mch_poll_interval_secs = 10;
557532SSean.Ye@Sun.COM int gcpu_xpv_virq_level = 3;
567532SSean.Ye@Sun.COM 
577532SSean.Ye@Sun.COM static timeout_id_t gcpu_xpv_mch_poll_timeoutid;
587532SSean.Ye@Sun.COM 
597532SSean.Ye@Sun.COM static int gcpu_xpv_virq_vect = -1;
607532SSean.Ye@Sun.COM 
617532SSean.Ye@Sun.COM static mc_info_t gcpu_xpv_polldata;
627532SSean.Ye@Sun.COM static kmutex_t gcpu_xpv_polldata_lock;
637532SSean.Ye@Sun.COM 
647532SSean.Ye@Sun.COM static cmi_mca_regs_t *gcpu_xpv_poll_bankregs;
657532SSean.Ye@Sun.COM static size_t gcpu_xpv_poll_bankregs_sz;
667532SSean.Ye@Sun.COM 
677532SSean.Ye@Sun.COM static uint32_t gcpu_xpv_intr_unclaimed;
687532SSean.Ye@Sun.COM static uint32_t gcpu_xpv_mca_hcall_busy;
697532SSean.Ye@Sun.COM 
707532SSean.Ye@Sun.COM static gcpu_poll_trace_ctl_t gcpu_xpv_poll_trace_ctl;
717532SSean.Ye@Sun.COM 
727532SSean.Ye@Sun.COM #define	GCPU_XPV_ARCH_NREGS		3
737532SSean.Ye@Sun.COM #define	GCPU_XPV_MCH_POLL_REARM		((void *)1)
747532SSean.Ye@Sun.COM #define	GCPU_XPV_MCH_POLL_NO_REARM	NULL
757532SSean.Ye@Sun.COM 
767532SSean.Ye@Sun.COM static uint_t
gcpu_xpv_virq_intr(void)777532SSean.Ye@Sun.COM gcpu_xpv_virq_intr(void)
787532SSean.Ye@Sun.COM {
7910175SStuart.Maybee@Sun.COM 	int types[] = { XEN_MC_URGENT, XEN_MC_NONURGENT };
807532SSean.Ye@Sun.COM 	uint64_t fetch_id;
817532SSean.Ye@Sun.COM 	int count = 0;
827532SSean.Ye@Sun.COM 	int i;
837532SSean.Ye@Sun.COM 
847532SSean.Ye@Sun.COM 	if (gcpu_xpv_virq_vect == -1 || gcpu_xpv_poll_bankregs_sz == 0) {
857532SSean.Ye@Sun.COM 		gcpu_xpv_intr_unclaimed++;
867532SSean.Ye@Sun.COM 		return (DDI_INTR_UNCLAIMED);
877532SSean.Ye@Sun.COM 	}
887532SSean.Ye@Sun.COM 
897532SSean.Ye@Sun.COM 	if (!mutex_tryenter(&gcpu_xpv_polldata_lock)) {
907532SSean.Ye@Sun.COM 		gcpu_xpv_mca_hcall_busy++;
917532SSean.Ye@Sun.COM 		return (DDI_INTR_CLAIMED);
927532SSean.Ye@Sun.COM 	}
937532SSean.Ye@Sun.COM 
947532SSean.Ye@Sun.COM 	for (i = 0; i < sizeof (types) / sizeof (types[0]); i++) {
957532SSean.Ye@Sun.COM 		while (gcpu_xpv_telem_read(&gcpu_xpv_polldata, types[i],
967532SSean.Ye@Sun.COM 		    &fetch_id)) {
977532SSean.Ye@Sun.COM 			gcpu_poll_trace(&gcpu_xpv_poll_trace_ctl,
987532SSean.Ye@Sun.COM 			    GCPU_MPT_WHAT_XPV_VIRQ,
997532SSean.Ye@Sun.COM 			    x86_mcinfo_nentries(&gcpu_xpv_polldata));
1007532SSean.Ye@Sun.COM 			gcpu_xpv_mci_process(&gcpu_xpv_polldata, types[i],
1017532SSean.Ye@Sun.COM 			    gcpu_xpv_poll_bankregs, gcpu_xpv_poll_bankregs_sz);
1027532SSean.Ye@Sun.COM 			gcpu_xpv_telem_ack(types[i], fetch_id);
1037532SSean.Ye@Sun.COM 			count++;
1047532SSean.Ye@Sun.COM 		}
1057532SSean.Ye@Sun.COM 	}
1067532SSean.Ye@Sun.COM 
1077532SSean.Ye@Sun.COM 	mutex_exit(&gcpu_xpv_polldata_lock);
1087532SSean.Ye@Sun.COM 
1097532SSean.Ye@Sun.COM 	return (DDI_INTR_CLAIMED);
1107532SSean.Ye@Sun.COM }
1117532SSean.Ye@Sun.COM 
1127532SSean.Ye@Sun.COM static void
gcpu_xpv_mch_poll(void * arg)1137532SSean.Ye@Sun.COM gcpu_xpv_mch_poll(void *arg)
1147532SSean.Ye@Sun.COM {
1157532SSean.Ye@Sun.COM 	cmi_hdl_t hdl = cmi_hdl_any();
1167532SSean.Ye@Sun.COM 
1177532SSean.Ye@Sun.COM 	if (hdl != NULL) {
1187532SSean.Ye@Sun.COM 		cmi_mc_logout(hdl, 0, 0);
1197532SSean.Ye@Sun.COM 		cmi_hdl_rele(hdl);
1207532SSean.Ye@Sun.COM 	}
1217532SSean.Ye@Sun.COM 
1227532SSean.Ye@Sun.COM 	if (arg == GCPU_XPV_MCH_POLL_REARM &&
1237532SSean.Ye@Sun.COM 	    gcpu_xpv_mch_poll_interval_secs != 0) {
1247532SSean.Ye@Sun.COM 		gcpu_xpv_mch_poll_timeoutid = timeout(gcpu_xpv_mch_poll,
1257532SSean.Ye@Sun.COM 		    GCPU_XPV_MCH_POLL_REARM,
1267532SSean.Ye@Sun.COM 		    drv_usectohz(gcpu_xpv_mch_poll_interval_secs * MICROSEC));
1277532SSean.Ye@Sun.COM 	}
1287532SSean.Ye@Sun.COM }
1297532SSean.Ye@Sun.COM 
1307532SSean.Ye@Sun.COM /*
1317532SSean.Ye@Sun.COM  * gcpu_mca_poll_init is called from gcpu_mca_init for each cpu handle
1327532SSean.Ye@Sun.COM  * that we initialize for.  It should prepare for polling by allocating
1337532SSean.Ye@Sun.COM  * control structures and the like, but must not kick polling off yet.
1347532SSean.Ye@Sun.COM  *
1357532SSean.Ye@Sun.COM  * Since we initialize all cpus in a serialized loop there is no race
1367532SSean.Ye@Sun.COM  * on allocating the bankregs structure, nor in free'ing and enlarging
1377532SSean.Ye@Sun.COM  * it if we find the number of MCA banks is not uniform in the system
1387532SSean.Ye@Sun.COM  * (unlikely) since polling is only started post mp startup.
1397532SSean.Ye@Sun.COM  */
1407532SSean.Ye@Sun.COM 
1417532SSean.Ye@Sun.COM void
gcpu_mca_poll_init(cmi_hdl_t hdl)1427532SSean.Ye@Sun.COM gcpu_mca_poll_init(cmi_hdl_t hdl)
1437532SSean.Ye@Sun.COM {
1447532SSean.Ye@Sun.COM 	gcpu_data_t *gcpu = cmi_hdl_getcmidata(hdl);
1457532SSean.Ye@Sun.COM 	int nbanks = gcpu->gcpu_mca.gcpu_mca_nbanks;
1467532SSean.Ye@Sun.COM 	size_t sz = nbanks * GCPU_XPV_ARCH_NREGS * sizeof (cmi_mca_regs_t);
1477532SSean.Ye@Sun.COM 
1487532SSean.Ye@Sun.COM 	ASSERT(cmi_hdl_class(hdl) == CMI_HDL_SOLARIS_xVM_MCA);
1497532SSean.Ye@Sun.COM 
1507532SSean.Ye@Sun.COM 	if (gcpu_xpv_poll_bankregs == NULL || sz > gcpu_xpv_poll_bankregs_sz) {
1517532SSean.Ye@Sun.COM 		if (gcpu_xpv_poll_bankregs != NULL) {
1527532SSean.Ye@Sun.COM 			kmem_free(gcpu_xpv_poll_bankregs,
1537532SSean.Ye@Sun.COM 			    gcpu_xpv_poll_bankregs_sz);
1547532SSean.Ye@Sun.COM 		} else {
1557532SSean.Ye@Sun.COM 			gcpu_poll_trace_init(&gcpu_xpv_poll_trace_ctl);
1567532SSean.Ye@Sun.COM 		}
1577532SSean.Ye@Sun.COM 
1587532SSean.Ye@Sun.COM 		gcpu_xpv_poll_bankregs_sz = sz;
1597532SSean.Ye@Sun.COM 		gcpu_xpv_poll_bankregs = kmem_zalloc(sz, KM_SLEEP);
1607532SSean.Ye@Sun.COM 
1617532SSean.Ye@Sun.COM 	}
1627532SSean.Ye@Sun.COM }
1637532SSean.Ye@Sun.COM 
164*12004Sjiang.liu@intel.com /* deconfigure gcpu_mca_poll_init() */
165*12004Sjiang.liu@intel.com void
gcpu_mca_poll_fini(cmi_hdl_t hdl)166*12004Sjiang.liu@intel.com gcpu_mca_poll_fini(cmi_hdl_t hdl)
167*12004Sjiang.liu@intel.com {
168*12004Sjiang.liu@intel.com }
169*12004Sjiang.liu@intel.com 
1707532SSean.Ye@Sun.COM void
gcpu_mca_poll_start(cmi_hdl_t hdl)1717532SSean.Ye@Sun.COM gcpu_mca_poll_start(cmi_hdl_t hdl)
1727532SSean.Ye@Sun.COM {
1737532SSean.Ye@Sun.COM 	ASSERT(cmi_hdl_class(hdl) == CMI_HDL_SOLARIS_xVM_MCA);
1747532SSean.Ye@Sun.COM 	/*
1757532SSean.Ye@Sun.COM 	 * We are on the boot cpu (cpu 0), called at the end of its
1767532SSean.Ye@Sun.COM 	 * multiprocessor startup.
1777532SSean.Ye@Sun.COM 	 */
1787532SSean.Ye@Sun.COM 	if (gcpu_xpv_poll_bankregs_sz != 0 && gcpu_xpv_virq_vect == -1) {
1797532SSean.Ye@Sun.COM 		/*
1807532SSean.Ye@Sun.COM 		 * The hypervisor will poll MCA state for us, but it cannot
1817532SSean.Ye@Sun.COM 		 * poll MCH state so we do that via a timeout.
1827532SSean.Ye@Sun.COM 		 */
1837532SSean.Ye@Sun.COM 		if (gcpu_xpv_mch_poll_interval_secs != 0) {
1847532SSean.Ye@Sun.COM 			gcpu_xpv_mch_poll_timeoutid =
1857532SSean.Ye@Sun.COM 			    timeout(gcpu_xpv_mch_poll, GCPU_XPV_MCH_POLL_REARM,
1867532SSean.Ye@Sun.COM 			    drv_usectohz(gcpu_xpv_mch_poll_interval_secs *
1877532SSean.Ye@Sun.COM 			    MICROSEC));
1887532SSean.Ye@Sun.COM 		}
1897532SSean.Ye@Sun.COM 
1907532SSean.Ye@Sun.COM 		/*
1917532SSean.Ye@Sun.COM 		 * Register handler for VIRQ_MCA; once this is in place
1927532SSean.Ye@Sun.COM 		 * the hypervisor will begin to forward polled MCA observations
1937532SSean.Ye@Sun.COM 		 * to us.
1947532SSean.Ye@Sun.COM 		 */
1957532SSean.Ye@Sun.COM 		gcpu_xpv_virq_vect = ec_bind_virq_to_irq(VIRQ_MCA, 0);
1967532SSean.Ye@Sun.COM 		(void) add_avintr(NULL, gcpu_xpv_virq_level,
1977532SSean.Ye@Sun.COM 		    (avfunc)gcpu_xpv_virq_intr, "MCA", gcpu_xpv_virq_vect,
1987532SSean.Ye@Sun.COM 		    NULL, NULL, NULL, NULL);
1997532SSean.Ye@Sun.COM 	}
2007532SSean.Ye@Sun.COM }
201