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