xref: /freebsd/sys/dev/hwpmc/hwpmc_soft.c (revision f5f9340b9807d44d200658ba1bbbbbb57ab72e07)
1*f5f9340bSFabien Thomas /*-
2*f5f9340bSFabien Thomas  * Copyright (c) 2012 Fabien Thomas
3*f5f9340bSFabien Thomas  * All rights reserved.
4*f5f9340bSFabien Thomas  *
5*f5f9340bSFabien Thomas  * Redistribution and use in source and binary forms, with or without
6*f5f9340bSFabien Thomas  * modification, are permitted provided that the following conditions
7*f5f9340bSFabien Thomas  * are met:
8*f5f9340bSFabien Thomas  * 1. Redistributions of source code must retain the above copyright
9*f5f9340bSFabien Thomas  *    notice, this list of conditions and the following disclaimer.
10*f5f9340bSFabien Thomas  * 2. Redistributions in binary form must reproduce the above copyright
11*f5f9340bSFabien Thomas  *    notice, this list of conditions and the following disclaimer in the
12*f5f9340bSFabien Thomas  *    documentation and/or other materials provided with the distribution.
13*f5f9340bSFabien Thomas  *
14*f5f9340bSFabien Thomas  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15*f5f9340bSFabien Thomas  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16*f5f9340bSFabien Thomas  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17*f5f9340bSFabien Thomas  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18*f5f9340bSFabien Thomas  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19*f5f9340bSFabien Thomas  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20*f5f9340bSFabien Thomas  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21*f5f9340bSFabien Thomas  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22*f5f9340bSFabien Thomas  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23*f5f9340bSFabien Thomas  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24*f5f9340bSFabien Thomas  * SUCH DAMAGE.
25*f5f9340bSFabien Thomas  */
26*f5f9340bSFabien Thomas 
27*f5f9340bSFabien Thomas #include <sys/cdefs.h>
28*f5f9340bSFabien Thomas __FBSDID("$FreeBSD$");
29*f5f9340bSFabien Thomas 
30*f5f9340bSFabien Thomas #include <sys/param.h>
31*f5f9340bSFabien Thomas #include <sys/pmc.h>
32*f5f9340bSFabien Thomas #include <sys/pmckern.h>
33*f5f9340bSFabien Thomas #include <sys/systm.h>
34*f5f9340bSFabien Thomas #include <sys/mutex.h>
35*f5f9340bSFabien Thomas 
36*f5f9340bSFabien Thomas #include <machine/cpu.h>
37*f5f9340bSFabien Thomas #include <machine/cpufunc.h>
38*f5f9340bSFabien Thomas 
39*f5f9340bSFabien Thomas #include "hwpmc_soft.h"
40*f5f9340bSFabien Thomas 
41*f5f9340bSFabien Thomas /*
42*f5f9340bSFabien Thomas  * Software PMC support.
43*f5f9340bSFabien Thomas  */
44*f5f9340bSFabien Thomas 
45*f5f9340bSFabien Thomas #define	SOFT_CAPS (PMC_CAP_READ | PMC_CAP_WRITE | PMC_CAP_INTERRUPT | \
46*f5f9340bSFabien Thomas     PMC_CAP_USER | PMC_CAP_SYSTEM)
47*f5f9340bSFabien Thomas 
48*f5f9340bSFabien Thomas struct soft_descr {
49*f5f9340bSFabien Thomas 	struct pmc_descr pm_descr;  /* "base class" */
50*f5f9340bSFabien Thomas };
51*f5f9340bSFabien Thomas 
52*f5f9340bSFabien Thomas static struct soft_descr soft_pmcdesc[SOFT_NPMCS] =
53*f5f9340bSFabien Thomas {
54*f5f9340bSFabien Thomas #define	SOFT_PMCDESCR(N)				\
55*f5f9340bSFabien Thomas 	{						\
56*f5f9340bSFabien Thomas 		.pm_descr =				\
57*f5f9340bSFabien Thomas 		{					\
58*f5f9340bSFabien Thomas 			.pd_name = #N,			\
59*f5f9340bSFabien Thomas 			.pd_class = PMC_CLASS_SOFT,	\
60*f5f9340bSFabien Thomas 			.pd_caps = SOFT_CAPS,		\
61*f5f9340bSFabien Thomas 			.pd_width = 64			\
62*f5f9340bSFabien Thomas 		},					\
63*f5f9340bSFabien Thomas 	}
64*f5f9340bSFabien Thomas 
65*f5f9340bSFabien Thomas 	SOFT_PMCDESCR(SOFT0),
66*f5f9340bSFabien Thomas 	SOFT_PMCDESCR(SOFT1),
67*f5f9340bSFabien Thomas 	SOFT_PMCDESCR(SOFT2),
68*f5f9340bSFabien Thomas 	SOFT_PMCDESCR(SOFT3),
69*f5f9340bSFabien Thomas 	SOFT_PMCDESCR(SOFT4),
70*f5f9340bSFabien Thomas 	SOFT_PMCDESCR(SOFT5),
71*f5f9340bSFabien Thomas 	SOFT_PMCDESCR(SOFT6),
72*f5f9340bSFabien Thomas 	SOFT_PMCDESCR(SOFT7),
73*f5f9340bSFabien Thomas 	SOFT_PMCDESCR(SOFT8),
74*f5f9340bSFabien Thomas 	SOFT_PMCDESCR(SOFT9),
75*f5f9340bSFabien Thomas 	SOFT_PMCDESCR(SOFT10),
76*f5f9340bSFabien Thomas 	SOFT_PMCDESCR(SOFT11),
77*f5f9340bSFabien Thomas 	SOFT_PMCDESCR(SOFT12),
78*f5f9340bSFabien Thomas 	SOFT_PMCDESCR(SOFT13),
79*f5f9340bSFabien Thomas 	SOFT_PMCDESCR(SOFT14),
80*f5f9340bSFabien Thomas 	SOFT_PMCDESCR(SOFT15)
81*f5f9340bSFabien Thomas };
82*f5f9340bSFabien Thomas 
83*f5f9340bSFabien Thomas /*
84*f5f9340bSFabien Thomas  * Per-CPU data structure.
85*f5f9340bSFabien Thomas  */
86*f5f9340bSFabien Thomas 
87*f5f9340bSFabien Thomas struct soft_cpu {
88*f5f9340bSFabien Thomas 	struct pmc_hw	soft_hw[SOFT_NPMCS];
89*f5f9340bSFabien Thomas 	pmc_value_t	soft_values[SOFT_NPMCS];
90*f5f9340bSFabien Thomas };
91*f5f9340bSFabien Thomas 
92*f5f9340bSFabien Thomas 
93*f5f9340bSFabien Thomas static struct soft_cpu **soft_pcpu;
94*f5f9340bSFabien Thomas 
95*f5f9340bSFabien Thomas static int
96*f5f9340bSFabien Thomas soft_allocate_pmc(int cpu, int ri, struct pmc *pm,
97*f5f9340bSFabien Thomas     const struct pmc_op_pmcallocate *a)
98*f5f9340bSFabien Thomas {
99*f5f9340bSFabien Thomas 	enum pmc_event ev;
100*f5f9340bSFabien Thomas 	struct pmc_soft *ps;
101*f5f9340bSFabien Thomas 
102*f5f9340bSFabien Thomas 	(void) cpu;
103*f5f9340bSFabien Thomas 
104*f5f9340bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
105*f5f9340bSFabien Thomas 	    ("[soft,%d] illegal CPU value %d", __LINE__, cpu));
106*f5f9340bSFabien Thomas 	KASSERT(ri >= 0 && ri < SOFT_NPMCS,
107*f5f9340bSFabien Thomas 	    ("[soft,%d] illegal row-index %d", __LINE__, ri));
108*f5f9340bSFabien Thomas 
109*f5f9340bSFabien Thomas 	if (a->pm_class != PMC_CLASS_SOFT)
110*f5f9340bSFabien Thomas 		return (EINVAL);
111*f5f9340bSFabien Thomas 
112*f5f9340bSFabien Thomas 	if ((pm->pm_caps & SOFT_CAPS) == 0)
113*f5f9340bSFabien Thomas 		return (EINVAL);
114*f5f9340bSFabien Thomas 
115*f5f9340bSFabien Thomas 	if ((pm->pm_caps & ~SOFT_CAPS) != 0)
116*f5f9340bSFabien Thomas 		return (EPERM);
117*f5f9340bSFabien Thomas 
118*f5f9340bSFabien Thomas 	ev = pm->pm_event;
119*f5f9340bSFabien Thomas 	if (ev < PMC_EV_SOFT_FIRST || ev > PMC_EV_SOFT_LAST)
120*f5f9340bSFabien Thomas 		return (EINVAL);
121*f5f9340bSFabien Thomas 
122*f5f9340bSFabien Thomas 	/* Check if event is registered. */
123*f5f9340bSFabien Thomas 	ps = pmc_soft_ev_acquire(ev);
124*f5f9340bSFabien Thomas 	if (ps == NULL)
125*f5f9340bSFabien Thomas 		return (EINVAL);
126*f5f9340bSFabien Thomas 	pmc_soft_ev_release(ps);
127*f5f9340bSFabien Thomas 
128*f5f9340bSFabien Thomas 	return (0);
129*f5f9340bSFabien Thomas }
130*f5f9340bSFabien Thomas 
131*f5f9340bSFabien Thomas static int
132*f5f9340bSFabien Thomas soft_config_pmc(int cpu, int ri, struct pmc *pm)
133*f5f9340bSFabien Thomas {
134*f5f9340bSFabien Thomas 	struct pmc_hw *phw;
135*f5f9340bSFabien Thomas 
136*f5f9340bSFabien Thomas 	PMCDBG(MDP,CFG,1, "cpu=%d ri=%d pm=%p", cpu, ri, pm);
137*f5f9340bSFabien Thomas 
138*f5f9340bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
139*f5f9340bSFabien Thomas 	    ("[soft,%d] illegal CPU value %d", __LINE__, cpu));
140*f5f9340bSFabien Thomas 	KASSERT(ri >= 0 && ri < SOFT_NPMCS,
141*f5f9340bSFabien Thomas 	    ("[soft,%d] illegal row-index %d", __LINE__, ri));
142*f5f9340bSFabien Thomas 
143*f5f9340bSFabien Thomas 	phw = &soft_pcpu[cpu]->soft_hw[ri];
144*f5f9340bSFabien Thomas 
145*f5f9340bSFabien Thomas 	KASSERT(pm == NULL || phw->phw_pmc == NULL,
146*f5f9340bSFabien Thomas 	    ("[soft,%d] pm=%p phw->pm=%p hwpmc not unconfigured", __LINE__,
147*f5f9340bSFabien Thomas 	    pm, phw->phw_pmc));
148*f5f9340bSFabien Thomas 
149*f5f9340bSFabien Thomas 	phw->phw_pmc = pm;
150*f5f9340bSFabien Thomas 
151*f5f9340bSFabien Thomas 	return (0);
152*f5f9340bSFabien Thomas }
153*f5f9340bSFabien Thomas 
154*f5f9340bSFabien Thomas static int
155*f5f9340bSFabien Thomas soft_describe(int cpu, int ri, struct pmc_info *pi, struct pmc **ppmc)
156*f5f9340bSFabien Thomas {
157*f5f9340bSFabien Thomas 	int error;
158*f5f9340bSFabien Thomas 	size_t copied;
159*f5f9340bSFabien Thomas 	const struct soft_descr *pd;
160*f5f9340bSFabien Thomas 	struct pmc_hw *phw;
161*f5f9340bSFabien Thomas 
162*f5f9340bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
163*f5f9340bSFabien Thomas 	    ("[soft,%d] illegal CPU %d", __LINE__, cpu));
164*f5f9340bSFabien Thomas 	KASSERT(ri >= 0 && ri < SOFT_NPMCS,
165*f5f9340bSFabien Thomas 	    ("[soft,%d] illegal row-index %d", __LINE__, ri));
166*f5f9340bSFabien Thomas 
167*f5f9340bSFabien Thomas 	phw = &soft_pcpu[cpu]->soft_hw[ri];
168*f5f9340bSFabien Thomas 	pd  = &soft_pmcdesc[ri];
169*f5f9340bSFabien Thomas 
170*f5f9340bSFabien Thomas 	if ((error = copystr(pd->pm_descr.pd_name, pi->pm_name,
171*f5f9340bSFabien Thomas 	    PMC_NAME_MAX, &copied)) != 0)
172*f5f9340bSFabien Thomas 		return (error);
173*f5f9340bSFabien Thomas 
174*f5f9340bSFabien Thomas 	pi->pm_class = pd->pm_descr.pd_class;
175*f5f9340bSFabien Thomas 
176*f5f9340bSFabien Thomas 	if (phw->phw_state & PMC_PHW_FLAG_IS_ENABLED) {
177*f5f9340bSFabien Thomas 		pi->pm_enabled = TRUE;
178*f5f9340bSFabien Thomas 		*ppmc          = phw->phw_pmc;
179*f5f9340bSFabien Thomas 	} else {
180*f5f9340bSFabien Thomas 		pi->pm_enabled = FALSE;
181*f5f9340bSFabien Thomas 		*ppmc          = NULL;
182*f5f9340bSFabien Thomas 	}
183*f5f9340bSFabien Thomas 
184*f5f9340bSFabien Thomas 	return (0);
185*f5f9340bSFabien Thomas }
186*f5f9340bSFabien Thomas 
187*f5f9340bSFabien Thomas static int
188*f5f9340bSFabien Thomas soft_get_config(int cpu, int ri, struct pmc **ppm)
189*f5f9340bSFabien Thomas {
190*f5f9340bSFabien Thomas 	(void) ri;
191*f5f9340bSFabien Thomas 
192*f5f9340bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
193*f5f9340bSFabien Thomas 	    ("[soft,%d] illegal CPU %d", __LINE__, cpu));
194*f5f9340bSFabien Thomas 	KASSERT(ri >= 0 && ri < SOFT_NPMCS,
195*f5f9340bSFabien Thomas 	    ("[soft,%d] illegal row-index %d", __LINE__, ri));
196*f5f9340bSFabien Thomas 
197*f5f9340bSFabien Thomas 	*ppm = soft_pcpu[cpu]->soft_hw[ri].phw_pmc;
198*f5f9340bSFabien Thomas 	return (0);
199*f5f9340bSFabien Thomas }
200*f5f9340bSFabien Thomas 
201*f5f9340bSFabien Thomas static int
202*f5f9340bSFabien Thomas soft_pcpu_fini(struct pmc_mdep *md, int cpu)
203*f5f9340bSFabien Thomas {
204*f5f9340bSFabien Thomas 	int ri;
205*f5f9340bSFabien Thomas 	struct pmc_cpu *pc;
206*f5f9340bSFabien Thomas 
207*f5f9340bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
208*f5f9340bSFabien Thomas 	    ("[soft,%d] illegal cpu %d", __LINE__, cpu));
209*f5f9340bSFabien Thomas 	KASSERT(soft_pcpu[cpu] != NULL, ("[soft,%d] null pcpu", __LINE__));
210*f5f9340bSFabien Thomas 
211*f5f9340bSFabien Thomas 	free(soft_pcpu[cpu], M_PMC);
212*f5f9340bSFabien Thomas 	soft_pcpu[cpu] = NULL;
213*f5f9340bSFabien Thomas 
214*f5f9340bSFabien Thomas 	ri = md->pmd_classdep[PMC_CLASS_INDEX_SOFT].pcd_ri;
215*f5f9340bSFabien Thomas 
216*f5f9340bSFabien Thomas 	KASSERT(ri >= 0 && ri < SOFT_NPMCS,
217*f5f9340bSFabien Thomas 	    ("[soft,%d] ri=%d", __LINE__, ri));
218*f5f9340bSFabien Thomas 
219*f5f9340bSFabien Thomas 	pc = pmc_pcpu[cpu];
220*f5f9340bSFabien Thomas 	pc->pc_hwpmcs[ri] = NULL;
221*f5f9340bSFabien Thomas 
222*f5f9340bSFabien Thomas 	return (0);
223*f5f9340bSFabien Thomas }
224*f5f9340bSFabien Thomas 
225*f5f9340bSFabien Thomas static int
226*f5f9340bSFabien Thomas soft_pcpu_init(struct pmc_mdep *md, int cpu)
227*f5f9340bSFabien Thomas {
228*f5f9340bSFabien Thomas 	int first_ri, n;
229*f5f9340bSFabien Thomas 	struct pmc_cpu *pc;
230*f5f9340bSFabien Thomas 	struct soft_cpu *soft_pc;
231*f5f9340bSFabien Thomas 	struct pmc_hw *phw;
232*f5f9340bSFabien Thomas 
233*f5f9340bSFabien Thomas 
234*f5f9340bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
235*f5f9340bSFabien Thomas 	    ("[soft,%d] illegal cpu %d", __LINE__, cpu));
236*f5f9340bSFabien Thomas 	KASSERT(soft_pcpu, ("[soft,%d] null pcpu", __LINE__));
237*f5f9340bSFabien Thomas 	KASSERT(soft_pcpu[cpu] == NULL, ("[soft,%d] non-null per-cpu",
238*f5f9340bSFabien Thomas 	    __LINE__));
239*f5f9340bSFabien Thomas 
240*f5f9340bSFabien Thomas 	soft_pc = malloc(sizeof(struct soft_cpu), M_PMC, M_WAITOK|M_ZERO);
241*f5f9340bSFabien Thomas 	if (soft_pc == NULL)
242*f5f9340bSFabien Thomas 		return (ENOMEM);
243*f5f9340bSFabien Thomas 
244*f5f9340bSFabien Thomas 	pc = pmc_pcpu[cpu];
245*f5f9340bSFabien Thomas 
246*f5f9340bSFabien Thomas 	KASSERT(pc != NULL, ("[soft,%d] cpu %d null per-cpu", __LINE__, cpu));
247*f5f9340bSFabien Thomas 
248*f5f9340bSFabien Thomas 	soft_pcpu[cpu] = soft_pc;
249*f5f9340bSFabien Thomas 	phw = soft_pc->soft_hw;
250*f5f9340bSFabien Thomas 	first_ri = md->pmd_classdep[PMC_CLASS_INDEX_SOFT].pcd_ri;
251*f5f9340bSFabien Thomas 
252*f5f9340bSFabien Thomas 	for (n = 0; n < SOFT_NPMCS; n++, phw++) {
253*f5f9340bSFabien Thomas 		phw->phw_state = PMC_PHW_FLAG_IS_ENABLED |
254*f5f9340bSFabien Thomas 		    PMC_PHW_CPU_TO_STATE(cpu) | PMC_PHW_INDEX_TO_STATE(n);
255*f5f9340bSFabien Thomas 		phw->phw_pmc = NULL;
256*f5f9340bSFabien Thomas 		pc->pc_hwpmcs[n + first_ri] = phw;
257*f5f9340bSFabien Thomas 	}
258*f5f9340bSFabien Thomas 
259*f5f9340bSFabien Thomas 	return (0);
260*f5f9340bSFabien Thomas }
261*f5f9340bSFabien Thomas 
262*f5f9340bSFabien Thomas static int
263*f5f9340bSFabien Thomas soft_read_pmc(int cpu, int ri, pmc_value_t *v)
264*f5f9340bSFabien Thomas {
265*f5f9340bSFabien Thomas 	struct pmc *pm;
266*f5f9340bSFabien Thomas 	const struct pmc_hw *phw;
267*f5f9340bSFabien Thomas 
268*f5f9340bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
269*f5f9340bSFabien Thomas 	    ("[soft,%d] illegal CPU value %d", __LINE__, cpu));
270*f5f9340bSFabien Thomas 	KASSERT(ri >= 0 && ri < SOFT_NPMCS,
271*f5f9340bSFabien Thomas 	    ("[soft,%d] illegal row-index %d", __LINE__, ri));
272*f5f9340bSFabien Thomas 
273*f5f9340bSFabien Thomas 	phw = &soft_pcpu[cpu]->soft_hw[ri];
274*f5f9340bSFabien Thomas 	pm  = phw->phw_pmc;
275*f5f9340bSFabien Thomas 
276*f5f9340bSFabien Thomas 	KASSERT(pm != NULL,
277*f5f9340bSFabien Thomas 	    ("[soft,%d] no owner for PHW [cpu%d,pmc%d]", __LINE__, cpu, ri));
278*f5f9340bSFabien Thomas 
279*f5f9340bSFabien Thomas 	PMCDBG(MDP,REA,1,"soft-read id=%d", ri);
280*f5f9340bSFabien Thomas 
281*f5f9340bSFabien Thomas 	*v = soft_pcpu[cpu]->soft_values[ri];
282*f5f9340bSFabien Thomas 
283*f5f9340bSFabien Thomas 	return (0);
284*f5f9340bSFabien Thomas }
285*f5f9340bSFabien Thomas 
286*f5f9340bSFabien Thomas static int
287*f5f9340bSFabien Thomas soft_write_pmc(int cpu, int ri, pmc_value_t v)
288*f5f9340bSFabien Thomas {
289*f5f9340bSFabien Thomas 	struct pmc *pm;
290*f5f9340bSFabien Thomas 	const struct soft_descr *pd;
291*f5f9340bSFabien Thomas 
292*f5f9340bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
293*f5f9340bSFabien Thomas 	    ("[soft,%d] illegal cpu value %d", __LINE__, cpu));
294*f5f9340bSFabien Thomas 	KASSERT(ri >= 0 && ri < SOFT_NPMCS,
295*f5f9340bSFabien Thomas 	    ("[soft,%d] illegal row-index %d", __LINE__, ri));
296*f5f9340bSFabien Thomas 
297*f5f9340bSFabien Thomas 	pm = soft_pcpu[cpu]->soft_hw[ri].phw_pmc;
298*f5f9340bSFabien Thomas 	pd = &soft_pmcdesc[ri];
299*f5f9340bSFabien Thomas 
300*f5f9340bSFabien Thomas 	KASSERT(pm,
301*f5f9340bSFabien Thomas 	    ("[soft,%d] cpu %d ri %d pmc not configured", __LINE__, cpu, ri));
302*f5f9340bSFabien Thomas 
303*f5f9340bSFabien Thomas 	PMCDBG(MDP,WRI,1, "soft-write cpu=%d ri=%d v=%jx", cpu, ri, v);
304*f5f9340bSFabien Thomas 
305*f5f9340bSFabien Thomas 	soft_pcpu[cpu]->soft_values[ri] = v;
306*f5f9340bSFabien Thomas 
307*f5f9340bSFabien Thomas 	return (0);
308*f5f9340bSFabien Thomas }
309*f5f9340bSFabien Thomas 
310*f5f9340bSFabien Thomas static int
311*f5f9340bSFabien Thomas soft_release_pmc(int cpu, int ri, struct pmc *pmc)
312*f5f9340bSFabien Thomas {
313*f5f9340bSFabien Thomas 	struct pmc_hw *phw;
314*f5f9340bSFabien Thomas 
315*f5f9340bSFabien Thomas 	(void) pmc;
316*f5f9340bSFabien Thomas 
317*f5f9340bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
318*f5f9340bSFabien Thomas 	    ("[soft,%d] illegal CPU value %d", __LINE__, cpu));
319*f5f9340bSFabien Thomas 	KASSERT(ri >= 0 && ri < SOFT_NPMCS,
320*f5f9340bSFabien Thomas 	    ("[soft,%d] illegal row-index %d", __LINE__, ri));
321*f5f9340bSFabien Thomas 
322*f5f9340bSFabien Thomas 	phw = &soft_pcpu[cpu]->soft_hw[ri];
323*f5f9340bSFabien Thomas 
324*f5f9340bSFabien Thomas 	KASSERT(phw->phw_pmc == NULL,
325*f5f9340bSFabien Thomas 	    ("[soft,%d] PHW pmc %p non-NULL", __LINE__, phw->phw_pmc));
326*f5f9340bSFabien Thomas 
327*f5f9340bSFabien Thomas 	/*
328*f5f9340bSFabien Thomas 	 * Nothing to do.
329*f5f9340bSFabien Thomas 	 */
330*f5f9340bSFabien Thomas 	return (0);
331*f5f9340bSFabien Thomas }
332*f5f9340bSFabien Thomas 
333*f5f9340bSFabien Thomas static int
334*f5f9340bSFabien Thomas soft_start_pmc(int cpu, int ri)
335*f5f9340bSFabien Thomas {
336*f5f9340bSFabien Thomas 	struct pmc *pm;
337*f5f9340bSFabien Thomas 	struct soft_cpu *pc;
338*f5f9340bSFabien Thomas 	struct pmc_soft *ps;
339*f5f9340bSFabien Thomas 
340*f5f9340bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
341*f5f9340bSFabien Thomas 	    ("[soft,%d] illegal CPU value %d", __LINE__, cpu));
342*f5f9340bSFabien Thomas 	KASSERT(ri >= 0 && ri < SOFT_NPMCS,
343*f5f9340bSFabien Thomas 	    ("[soft,%d] illegal row-index %d", __LINE__, ri));
344*f5f9340bSFabien Thomas 
345*f5f9340bSFabien Thomas 	pc = soft_pcpu[cpu];
346*f5f9340bSFabien Thomas 	pm = pc->soft_hw[ri].phw_pmc;
347*f5f9340bSFabien Thomas 
348*f5f9340bSFabien Thomas 	KASSERT(pm,
349*f5f9340bSFabien Thomas 	    ("[soft,%d] cpu %d ri %d pmc not configured", __LINE__, cpu, ri));
350*f5f9340bSFabien Thomas 
351*f5f9340bSFabien Thomas 	ps = pmc_soft_ev_acquire(pm->pm_event);
352*f5f9340bSFabien Thomas 	if (ps == NULL)
353*f5f9340bSFabien Thomas 		return (EINVAL);
354*f5f9340bSFabien Thomas 	atomic_add_int(&ps->ps_running, 1);
355*f5f9340bSFabien Thomas 	pmc_soft_ev_release(ps);
356*f5f9340bSFabien Thomas 
357*f5f9340bSFabien Thomas 	return (0);
358*f5f9340bSFabien Thomas }
359*f5f9340bSFabien Thomas 
360*f5f9340bSFabien Thomas static int
361*f5f9340bSFabien Thomas soft_stop_pmc(int cpu, int ri)
362*f5f9340bSFabien Thomas {
363*f5f9340bSFabien Thomas 	struct pmc *pm;
364*f5f9340bSFabien Thomas 	struct soft_cpu *pc;
365*f5f9340bSFabien Thomas 	struct pmc_soft *ps;
366*f5f9340bSFabien Thomas 
367*f5f9340bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
368*f5f9340bSFabien Thomas 	    ("[soft,%d] illegal CPU value %d", __LINE__, cpu));
369*f5f9340bSFabien Thomas 	KASSERT(ri >= 0 && ri < SOFT_NPMCS,
370*f5f9340bSFabien Thomas 	    ("[soft,%d] illegal row-index %d", __LINE__, ri));
371*f5f9340bSFabien Thomas 
372*f5f9340bSFabien Thomas 	pc = soft_pcpu[cpu];
373*f5f9340bSFabien Thomas 	pm = pc->soft_hw[ri].phw_pmc;
374*f5f9340bSFabien Thomas 
375*f5f9340bSFabien Thomas 	KASSERT(pm,
376*f5f9340bSFabien Thomas 	    ("[soft,%d] cpu %d ri %d pmc not configured", __LINE__, cpu, ri));
377*f5f9340bSFabien Thomas 
378*f5f9340bSFabien Thomas 	ps = pmc_soft_ev_acquire(pm->pm_event);
379*f5f9340bSFabien Thomas 	/* event unregistered ? */
380*f5f9340bSFabien Thomas 	if (ps != NULL) {
381*f5f9340bSFabien Thomas 		atomic_subtract_int(&ps->ps_running, 1);
382*f5f9340bSFabien Thomas 		pmc_soft_ev_release(ps);
383*f5f9340bSFabien Thomas 	}
384*f5f9340bSFabien Thomas 
385*f5f9340bSFabien Thomas 	return (0);
386*f5f9340bSFabien Thomas }
387*f5f9340bSFabien Thomas 
388*f5f9340bSFabien Thomas int
389*f5f9340bSFabien Thomas pmc_soft_intr(struct pmckern_soft *ks)
390*f5f9340bSFabien Thomas {
391*f5f9340bSFabien Thomas 	struct pmc *pm;
392*f5f9340bSFabien Thomas 	struct soft_cpu *pc;
393*f5f9340bSFabien Thomas 	int ri, processed, error, user_mode;
394*f5f9340bSFabien Thomas 
395*f5f9340bSFabien Thomas 	KASSERT(ks->pm_cpu >= 0 && ks->pm_cpu < pmc_cpu_max(),
396*f5f9340bSFabien Thomas 	    ("[soft,%d] CPU %d out of range", __LINE__, ks->pm_cpu));
397*f5f9340bSFabien Thomas 
398*f5f9340bSFabien Thomas 	processed = 0;
399*f5f9340bSFabien Thomas 	pc = soft_pcpu[ks->pm_cpu];
400*f5f9340bSFabien Thomas 
401*f5f9340bSFabien Thomas 	for (ri = 0; ri < SOFT_NPMCS; ri++) {
402*f5f9340bSFabien Thomas 
403*f5f9340bSFabien Thomas 		pm = pc->soft_hw[ri].phw_pmc;
404*f5f9340bSFabien Thomas 		if (pm == NULL ||
405*f5f9340bSFabien Thomas 		    pm->pm_state != PMC_STATE_RUNNING ||
406*f5f9340bSFabien Thomas 		    pm->pm_event != ks->pm_ev) {
407*f5f9340bSFabien Thomas 				continue;
408*f5f9340bSFabien Thomas 		}
409*f5f9340bSFabien Thomas 
410*f5f9340bSFabien Thomas 		processed = 1;
411*f5f9340bSFabien Thomas 		pc->soft_values[ri]++;
412*f5f9340bSFabien Thomas 		if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) {
413*f5f9340bSFabien Thomas 			user_mode = TRAPF_USERMODE(ks->pm_tf);
414*f5f9340bSFabien Thomas 			error = pmc_process_interrupt(ks->pm_cpu, PMC_SR, pm,
415*f5f9340bSFabien Thomas 			    ks->pm_tf, user_mode);
416*f5f9340bSFabien Thomas 			if (error) {
417*f5f9340bSFabien Thomas 				soft_stop_pmc(ks->pm_cpu, ri);
418*f5f9340bSFabien Thomas 				continue;
419*f5f9340bSFabien Thomas 			}
420*f5f9340bSFabien Thomas 
421*f5f9340bSFabien Thomas 			if (user_mode) {
422*f5f9340bSFabien Thomas 				/* If in user mode setup AST to process
423*f5f9340bSFabien Thomas 				 * callchain out of interrupt context.
424*f5f9340bSFabien Thomas 				 */
425*f5f9340bSFabien Thomas 				curthread->td_flags |= TDF_ASTPENDING;
426*f5f9340bSFabien Thomas 			}
427*f5f9340bSFabien Thomas 		}
428*f5f9340bSFabien Thomas 	}
429*f5f9340bSFabien Thomas 
430*f5f9340bSFabien Thomas 	atomic_add_int(processed ? &pmc_stats.pm_intr_processed :
431*f5f9340bSFabien Thomas 	    &pmc_stats.pm_intr_ignored, 1);
432*f5f9340bSFabien Thomas 
433*f5f9340bSFabien Thomas 	return (processed);
434*f5f9340bSFabien Thomas }
435*f5f9340bSFabien Thomas 
436*f5f9340bSFabien Thomas void
437*f5f9340bSFabien Thomas pmc_soft_initialize(struct pmc_mdep *md)
438*f5f9340bSFabien Thomas {
439*f5f9340bSFabien Thomas 	struct pmc_classdep *pcd;
440*f5f9340bSFabien Thomas 
441*f5f9340bSFabien Thomas 	/* Add SOFT PMCs. */
442*f5f9340bSFabien Thomas 	soft_pcpu = malloc(sizeof(struct soft_cpu *) * pmc_cpu_max(), M_PMC,
443*f5f9340bSFabien Thomas 	    M_ZERO|M_WAITOK);
444*f5f9340bSFabien Thomas 
445*f5f9340bSFabien Thomas 	pcd = &md->pmd_classdep[PMC_CLASS_INDEX_SOFT];
446*f5f9340bSFabien Thomas 
447*f5f9340bSFabien Thomas 	pcd->pcd_caps	= SOFT_CAPS;
448*f5f9340bSFabien Thomas 	pcd->pcd_class	= PMC_CLASS_SOFT;
449*f5f9340bSFabien Thomas 	pcd->pcd_num	= SOFT_NPMCS;
450*f5f9340bSFabien Thomas 	pcd->pcd_ri	= md->pmd_npmc;
451*f5f9340bSFabien Thomas 	pcd->pcd_width	= 64;
452*f5f9340bSFabien Thomas 
453*f5f9340bSFabien Thomas 	pcd->pcd_allocate_pmc = soft_allocate_pmc;
454*f5f9340bSFabien Thomas 	pcd->pcd_config_pmc   = soft_config_pmc;
455*f5f9340bSFabien Thomas 	pcd->pcd_describe     = soft_describe;
456*f5f9340bSFabien Thomas 	pcd->pcd_get_config   = soft_get_config;
457*f5f9340bSFabien Thomas 	pcd->pcd_get_msr      = NULL;
458*f5f9340bSFabien Thomas 	pcd->pcd_pcpu_init    = soft_pcpu_init;
459*f5f9340bSFabien Thomas 	pcd->pcd_pcpu_fini    = soft_pcpu_fini;
460*f5f9340bSFabien Thomas 	pcd->pcd_read_pmc     = soft_read_pmc;
461*f5f9340bSFabien Thomas 	pcd->pcd_write_pmc    = soft_write_pmc;
462*f5f9340bSFabien Thomas 	pcd->pcd_release_pmc  = soft_release_pmc;
463*f5f9340bSFabien Thomas 	pcd->pcd_start_pmc    = soft_start_pmc;
464*f5f9340bSFabien Thomas 	pcd->pcd_stop_pmc     = soft_stop_pmc;
465*f5f9340bSFabien Thomas 
466*f5f9340bSFabien Thomas 	md->pmd_npmc += SOFT_NPMCS;
467*f5f9340bSFabien Thomas }
468*f5f9340bSFabien Thomas 
469*f5f9340bSFabien Thomas void
470*f5f9340bSFabien Thomas pmc_soft_finalize(struct pmc_mdep *md)
471*f5f9340bSFabien Thomas {
472*f5f9340bSFabien Thomas #ifdef	INVARIANTS
473*f5f9340bSFabien Thomas 	int i, ncpus;
474*f5f9340bSFabien Thomas 
475*f5f9340bSFabien Thomas 	ncpus = pmc_cpu_max();
476*f5f9340bSFabien Thomas 	for (i = 0; i < ncpus; i++)
477*f5f9340bSFabien Thomas 		KASSERT(soft_pcpu[i] == NULL, ("[soft,%d] non-null pcpu cpu %d",
478*f5f9340bSFabien Thomas 		    __LINE__, i));
479*f5f9340bSFabien Thomas 
480*f5f9340bSFabien Thomas 	KASSERT(md->pmd_classdep[PMC_CLASS_INDEX_SOFT].pcd_class ==
481*f5f9340bSFabien Thomas 	    PMC_CLASS_SOFT, ("[soft,%d] class mismatch", __LINE__));
482*f5f9340bSFabien Thomas #endif
483*f5f9340bSFabien Thomas 	free(soft_pcpu, M_PMC);
484*f5f9340bSFabien Thomas 	soft_pcpu = NULL;
485*f5f9340bSFabien Thomas }
486