xref: /freebsd/sys/dev/hwpmc/hwpmc_soft.c (revision 4a3690dfa11353418fe85214c06b33cdd3353a74)
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);
127d49302aeSFabien Thomas 	/* Module unload is protected by pmc SX lock. */
128d49302aeSFabien Thomas 	if (ps->ps_alloc != NULL)
129d49302aeSFabien Thomas 		ps->ps_alloc();
130f5f9340bSFabien Thomas 
131f5f9340bSFabien Thomas 	return (0);
132f5f9340bSFabien Thomas }
133f5f9340bSFabien Thomas 
134f5f9340bSFabien Thomas static int
135f5f9340bSFabien Thomas soft_config_pmc(int cpu, int ri, struct pmc *pm)
136f5f9340bSFabien Thomas {
137f5f9340bSFabien Thomas 	struct pmc_hw *phw;
138f5f9340bSFabien Thomas 
139*4a3690dfSJohn Baldwin 	PMCDBG3(MDP,CFG,1, "cpu=%d ri=%d pm=%p", cpu, ri, pm);
140f5f9340bSFabien Thomas 
141f5f9340bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
142f5f9340bSFabien Thomas 	    ("[soft,%d] illegal CPU value %d", __LINE__, cpu));
143f5f9340bSFabien Thomas 	KASSERT(ri >= 0 && ri < SOFT_NPMCS,
144f5f9340bSFabien Thomas 	    ("[soft,%d] illegal row-index %d", __LINE__, ri));
145f5f9340bSFabien Thomas 
146f5f9340bSFabien Thomas 	phw = &soft_pcpu[cpu]->soft_hw[ri];
147f5f9340bSFabien Thomas 
148f5f9340bSFabien Thomas 	KASSERT(pm == NULL || phw->phw_pmc == NULL,
149f5f9340bSFabien Thomas 	    ("[soft,%d] pm=%p phw->pm=%p hwpmc not unconfigured", __LINE__,
150f5f9340bSFabien Thomas 	    pm, phw->phw_pmc));
151f5f9340bSFabien Thomas 
152f5f9340bSFabien Thomas 	phw->phw_pmc = pm;
153f5f9340bSFabien Thomas 
154f5f9340bSFabien Thomas 	return (0);
155f5f9340bSFabien Thomas }
156f5f9340bSFabien Thomas 
157f5f9340bSFabien Thomas static int
158f5f9340bSFabien Thomas soft_describe(int cpu, int ri, struct pmc_info *pi, struct pmc **ppmc)
159f5f9340bSFabien Thomas {
160f5f9340bSFabien Thomas 	int error;
161f5f9340bSFabien Thomas 	size_t copied;
162f5f9340bSFabien Thomas 	const struct soft_descr *pd;
163f5f9340bSFabien Thomas 	struct pmc_hw *phw;
164f5f9340bSFabien Thomas 
165f5f9340bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
166f5f9340bSFabien Thomas 	    ("[soft,%d] illegal CPU %d", __LINE__, cpu));
167f5f9340bSFabien Thomas 	KASSERT(ri >= 0 && ri < SOFT_NPMCS,
168f5f9340bSFabien Thomas 	    ("[soft,%d] illegal row-index %d", __LINE__, ri));
169f5f9340bSFabien Thomas 
170f5f9340bSFabien Thomas 	phw = &soft_pcpu[cpu]->soft_hw[ri];
171f5f9340bSFabien Thomas 	pd  = &soft_pmcdesc[ri];
172f5f9340bSFabien Thomas 
173f5f9340bSFabien Thomas 	if ((error = copystr(pd->pm_descr.pd_name, pi->pm_name,
174f5f9340bSFabien Thomas 	    PMC_NAME_MAX, &copied)) != 0)
175f5f9340bSFabien Thomas 		return (error);
176f5f9340bSFabien Thomas 
177f5f9340bSFabien Thomas 	pi->pm_class = pd->pm_descr.pd_class;
178f5f9340bSFabien Thomas 
179f5f9340bSFabien Thomas 	if (phw->phw_state & PMC_PHW_FLAG_IS_ENABLED) {
180f5f9340bSFabien Thomas 		pi->pm_enabled = TRUE;
181f5f9340bSFabien Thomas 		*ppmc          = phw->phw_pmc;
182f5f9340bSFabien Thomas 	} else {
183f5f9340bSFabien Thomas 		pi->pm_enabled = FALSE;
184f5f9340bSFabien Thomas 		*ppmc          = NULL;
185f5f9340bSFabien Thomas 	}
186f5f9340bSFabien Thomas 
187f5f9340bSFabien Thomas 	return (0);
188f5f9340bSFabien Thomas }
189f5f9340bSFabien Thomas 
190f5f9340bSFabien Thomas static int
191f5f9340bSFabien Thomas soft_get_config(int cpu, int ri, struct pmc **ppm)
192f5f9340bSFabien Thomas {
193f5f9340bSFabien Thomas 	(void) ri;
194f5f9340bSFabien Thomas 
195f5f9340bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
196f5f9340bSFabien Thomas 	    ("[soft,%d] illegal CPU %d", __LINE__, cpu));
197f5f9340bSFabien Thomas 	KASSERT(ri >= 0 && ri < SOFT_NPMCS,
198f5f9340bSFabien Thomas 	    ("[soft,%d] illegal row-index %d", __LINE__, ri));
199f5f9340bSFabien Thomas 
200f5f9340bSFabien Thomas 	*ppm = soft_pcpu[cpu]->soft_hw[ri].phw_pmc;
201f5f9340bSFabien Thomas 	return (0);
202f5f9340bSFabien Thomas }
203f5f9340bSFabien Thomas 
204f5f9340bSFabien Thomas static int
205f5f9340bSFabien Thomas soft_pcpu_fini(struct pmc_mdep *md, int cpu)
206f5f9340bSFabien Thomas {
207f5f9340bSFabien Thomas 	int ri;
208f5f9340bSFabien Thomas 	struct pmc_cpu *pc;
209f5f9340bSFabien Thomas 
210f5f9340bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
211f5f9340bSFabien Thomas 	    ("[soft,%d] illegal cpu %d", __LINE__, cpu));
212f5f9340bSFabien Thomas 	KASSERT(soft_pcpu[cpu] != NULL, ("[soft,%d] null pcpu", __LINE__));
213f5f9340bSFabien Thomas 
214f5f9340bSFabien Thomas 	free(soft_pcpu[cpu], M_PMC);
215f5f9340bSFabien Thomas 	soft_pcpu[cpu] = NULL;
216f5f9340bSFabien Thomas 
217f5f9340bSFabien Thomas 	ri = md->pmd_classdep[PMC_CLASS_INDEX_SOFT].pcd_ri;
218f5f9340bSFabien Thomas 
219f5f9340bSFabien Thomas 	KASSERT(ri >= 0 && ri < SOFT_NPMCS,
220f5f9340bSFabien Thomas 	    ("[soft,%d] ri=%d", __LINE__, ri));
221f5f9340bSFabien Thomas 
222f5f9340bSFabien Thomas 	pc = pmc_pcpu[cpu];
223f5f9340bSFabien Thomas 	pc->pc_hwpmcs[ri] = NULL;
224f5f9340bSFabien Thomas 
225f5f9340bSFabien Thomas 	return (0);
226f5f9340bSFabien Thomas }
227f5f9340bSFabien Thomas 
228f5f9340bSFabien Thomas static int
229f5f9340bSFabien Thomas soft_pcpu_init(struct pmc_mdep *md, int cpu)
230f5f9340bSFabien Thomas {
231f5f9340bSFabien Thomas 	int first_ri, n;
232f5f9340bSFabien Thomas 	struct pmc_cpu *pc;
233f5f9340bSFabien Thomas 	struct soft_cpu *soft_pc;
234f5f9340bSFabien Thomas 	struct pmc_hw *phw;
235f5f9340bSFabien Thomas 
236f5f9340bSFabien Thomas 
237f5f9340bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
238f5f9340bSFabien Thomas 	    ("[soft,%d] illegal cpu %d", __LINE__, cpu));
239f5f9340bSFabien Thomas 	KASSERT(soft_pcpu, ("[soft,%d] null pcpu", __LINE__));
240f5f9340bSFabien Thomas 	KASSERT(soft_pcpu[cpu] == NULL, ("[soft,%d] non-null per-cpu",
241f5f9340bSFabien Thomas 	    __LINE__));
242f5f9340bSFabien Thomas 
243f5f9340bSFabien Thomas 	soft_pc = malloc(sizeof(struct soft_cpu), M_PMC, M_WAITOK|M_ZERO);
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 
279*4a3690dfSJohn Baldwin 	PMCDBG1(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 
303*4a3690dfSJohn Baldwin 	PMCDBG3(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;
314d49302aeSFabien Thomas 	enum pmc_event ev;
315d49302aeSFabien Thomas 	struct pmc_soft *ps;
316f5f9340bSFabien Thomas 
317f5f9340bSFabien Thomas 	(void) pmc;
318f5f9340bSFabien Thomas 
319f5f9340bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
320f5f9340bSFabien Thomas 	    ("[soft,%d] illegal CPU value %d", __LINE__, cpu));
321f5f9340bSFabien Thomas 	KASSERT(ri >= 0 && ri < SOFT_NPMCS,
322f5f9340bSFabien Thomas 	    ("[soft,%d] illegal row-index %d", __LINE__, ri));
323f5f9340bSFabien Thomas 
324f5f9340bSFabien Thomas 	phw = &soft_pcpu[cpu]->soft_hw[ri];
325f5f9340bSFabien Thomas 
326f5f9340bSFabien Thomas 	KASSERT(phw->phw_pmc == NULL,
327f5f9340bSFabien Thomas 	    ("[soft,%d] PHW pmc %p non-NULL", __LINE__, phw->phw_pmc));
328f5f9340bSFabien Thomas 
329d49302aeSFabien Thomas 	ev = pmc->pm_event;
330d49302aeSFabien Thomas 
331d49302aeSFabien Thomas 	/* Check if event is registered. */
332d49302aeSFabien Thomas 	ps = pmc_soft_ev_acquire(ev);
333d49302aeSFabien Thomas 	KASSERT(ps != NULL,
334d49302aeSFabien Thomas 	    ("[soft,%d] unregistered event %d", __LINE__, ev));
335d49302aeSFabien Thomas 	pmc_soft_ev_release(ps);
336d49302aeSFabien Thomas 	/* Module unload is protected by pmc SX lock. */
337d49302aeSFabien Thomas 	if (ps->ps_release != NULL)
338d49302aeSFabien Thomas 		ps->ps_release();
339f5f9340bSFabien Thomas 	return (0);
340f5f9340bSFabien Thomas }
341f5f9340bSFabien Thomas 
342f5f9340bSFabien Thomas static int
343f5f9340bSFabien Thomas soft_start_pmc(int cpu, int ri)
344f5f9340bSFabien Thomas {
345f5f9340bSFabien Thomas 	struct pmc *pm;
346f5f9340bSFabien Thomas 	struct soft_cpu *pc;
347f5f9340bSFabien Thomas 	struct pmc_soft *ps;
348f5f9340bSFabien Thomas 
349f5f9340bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
350f5f9340bSFabien Thomas 	    ("[soft,%d] illegal CPU value %d", __LINE__, cpu));
351f5f9340bSFabien Thomas 	KASSERT(ri >= 0 && ri < SOFT_NPMCS,
352f5f9340bSFabien Thomas 	    ("[soft,%d] illegal row-index %d", __LINE__, ri));
353f5f9340bSFabien Thomas 
354f5f9340bSFabien Thomas 	pc = soft_pcpu[cpu];
355f5f9340bSFabien Thomas 	pm = pc->soft_hw[ri].phw_pmc;
356f5f9340bSFabien Thomas 
357f5f9340bSFabien Thomas 	KASSERT(pm,
358f5f9340bSFabien Thomas 	    ("[soft,%d] cpu %d ri %d pmc not configured", __LINE__, cpu, ri));
359f5f9340bSFabien Thomas 
360f5f9340bSFabien Thomas 	ps = pmc_soft_ev_acquire(pm->pm_event);
361f5f9340bSFabien Thomas 	if (ps == NULL)
362f5f9340bSFabien Thomas 		return (EINVAL);
363f5f9340bSFabien Thomas 	atomic_add_int(&ps->ps_running, 1);
364f5f9340bSFabien Thomas 	pmc_soft_ev_release(ps);
365f5f9340bSFabien Thomas 
366f5f9340bSFabien Thomas 	return (0);
367f5f9340bSFabien Thomas }
368f5f9340bSFabien Thomas 
369f5f9340bSFabien Thomas static int
370f5f9340bSFabien Thomas soft_stop_pmc(int cpu, int ri)
371f5f9340bSFabien Thomas {
372f5f9340bSFabien Thomas 	struct pmc *pm;
373f5f9340bSFabien Thomas 	struct soft_cpu *pc;
374f5f9340bSFabien Thomas 	struct pmc_soft *ps;
375f5f9340bSFabien Thomas 
376f5f9340bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
377f5f9340bSFabien Thomas 	    ("[soft,%d] illegal CPU value %d", __LINE__, cpu));
378f5f9340bSFabien Thomas 	KASSERT(ri >= 0 && ri < SOFT_NPMCS,
379f5f9340bSFabien Thomas 	    ("[soft,%d] illegal row-index %d", __LINE__, ri));
380f5f9340bSFabien Thomas 
381f5f9340bSFabien Thomas 	pc = soft_pcpu[cpu];
382f5f9340bSFabien Thomas 	pm = pc->soft_hw[ri].phw_pmc;
383f5f9340bSFabien Thomas 
384f5f9340bSFabien Thomas 	KASSERT(pm,
385f5f9340bSFabien Thomas 	    ("[soft,%d] cpu %d ri %d pmc not configured", __LINE__, cpu, ri));
386f5f9340bSFabien Thomas 
387f5f9340bSFabien Thomas 	ps = pmc_soft_ev_acquire(pm->pm_event);
388f5f9340bSFabien Thomas 	/* event unregistered ? */
389f5f9340bSFabien Thomas 	if (ps != NULL) {
390f5f9340bSFabien Thomas 		atomic_subtract_int(&ps->ps_running, 1);
391f5f9340bSFabien Thomas 		pmc_soft_ev_release(ps);
392f5f9340bSFabien Thomas 	}
393f5f9340bSFabien Thomas 
394f5f9340bSFabien Thomas 	return (0);
395f5f9340bSFabien Thomas }
396f5f9340bSFabien Thomas 
397f5f9340bSFabien Thomas int
398f5f9340bSFabien Thomas pmc_soft_intr(struct pmckern_soft *ks)
399f5f9340bSFabien Thomas {
400f5f9340bSFabien Thomas 	struct pmc *pm;
401f5f9340bSFabien Thomas 	struct soft_cpu *pc;
402f5f9340bSFabien Thomas 	int ri, processed, error, user_mode;
403f5f9340bSFabien Thomas 
404f5f9340bSFabien Thomas 	KASSERT(ks->pm_cpu >= 0 && ks->pm_cpu < pmc_cpu_max(),
405f5f9340bSFabien Thomas 	    ("[soft,%d] CPU %d out of range", __LINE__, ks->pm_cpu));
406f5f9340bSFabien Thomas 
407f5f9340bSFabien Thomas 	processed = 0;
408f5f9340bSFabien Thomas 	pc = soft_pcpu[ks->pm_cpu];
409f5f9340bSFabien Thomas 
410f5f9340bSFabien Thomas 	for (ri = 0; ri < SOFT_NPMCS; ri++) {
411f5f9340bSFabien Thomas 
412f5f9340bSFabien Thomas 		pm = pc->soft_hw[ri].phw_pmc;
413f5f9340bSFabien Thomas 		if (pm == NULL ||
414f5f9340bSFabien Thomas 		    pm->pm_state != PMC_STATE_RUNNING ||
415f5f9340bSFabien Thomas 		    pm->pm_event != ks->pm_ev) {
416f5f9340bSFabien Thomas 				continue;
417f5f9340bSFabien Thomas 		}
418f5f9340bSFabien Thomas 
419f5f9340bSFabien Thomas 		processed = 1;
420f5f9340bSFabien Thomas 		if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) {
4218a4f65bcSAlexander Motin 			if ((pc->soft_values[ri]--) <= 0)
4228a4f65bcSAlexander Motin 				pc->soft_values[ri] += pm->pm_sc.pm_reloadcount;
4238a4f65bcSAlexander Motin 			else
4248a4f65bcSAlexander Motin 				continue;
425f5f9340bSFabien Thomas 			user_mode = TRAPF_USERMODE(ks->pm_tf);
426f5f9340bSFabien Thomas 			error = pmc_process_interrupt(ks->pm_cpu, PMC_SR, pm,
427f5f9340bSFabien Thomas 			    ks->pm_tf, user_mode);
428f5f9340bSFabien Thomas 			if (error) {
429f5f9340bSFabien Thomas 				soft_stop_pmc(ks->pm_cpu, ri);
430f5f9340bSFabien Thomas 				continue;
431f5f9340bSFabien Thomas 			}
432f5f9340bSFabien Thomas 
433f5f9340bSFabien Thomas 			if (user_mode) {
434f5f9340bSFabien Thomas 				/* If in user mode setup AST to process
435f5f9340bSFabien Thomas 				 * callchain out of interrupt context.
436f5f9340bSFabien Thomas 				 */
437f5f9340bSFabien Thomas 				curthread->td_flags |= TDF_ASTPENDING;
438f5f9340bSFabien Thomas 			}
4398a4f65bcSAlexander Motin 		} else
4408a4f65bcSAlexander Motin 			pc->soft_values[ri]++;
441f5f9340bSFabien Thomas 	}
442f5f9340bSFabien Thomas 
443f5f9340bSFabien Thomas 	atomic_add_int(processed ? &pmc_stats.pm_intr_processed :
444f5f9340bSFabien Thomas 	    &pmc_stats.pm_intr_ignored, 1);
445f5f9340bSFabien Thomas 
446f5f9340bSFabien Thomas 	return (processed);
447f5f9340bSFabien Thomas }
448f5f9340bSFabien Thomas 
449f5f9340bSFabien Thomas void
450f5f9340bSFabien Thomas pmc_soft_initialize(struct pmc_mdep *md)
451f5f9340bSFabien Thomas {
452f5f9340bSFabien Thomas 	struct pmc_classdep *pcd;
453f5f9340bSFabien Thomas 
454f5f9340bSFabien Thomas 	/* Add SOFT PMCs. */
455f5f9340bSFabien Thomas 	soft_pcpu = malloc(sizeof(struct soft_cpu *) * pmc_cpu_max(), M_PMC,
456f5f9340bSFabien Thomas 	    M_ZERO|M_WAITOK);
457f5f9340bSFabien Thomas 
458f5f9340bSFabien Thomas 	pcd = &md->pmd_classdep[PMC_CLASS_INDEX_SOFT];
459f5f9340bSFabien Thomas 
460f5f9340bSFabien Thomas 	pcd->pcd_caps	= SOFT_CAPS;
461f5f9340bSFabien Thomas 	pcd->pcd_class	= PMC_CLASS_SOFT;
462f5f9340bSFabien Thomas 	pcd->pcd_num	= SOFT_NPMCS;
463f5f9340bSFabien Thomas 	pcd->pcd_ri	= md->pmd_npmc;
464f5f9340bSFabien Thomas 	pcd->pcd_width	= 64;
465f5f9340bSFabien Thomas 
466f5f9340bSFabien Thomas 	pcd->pcd_allocate_pmc = soft_allocate_pmc;
467f5f9340bSFabien Thomas 	pcd->pcd_config_pmc   = soft_config_pmc;
468f5f9340bSFabien Thomas 	pcd->pcd_describe     = soft_describe;
469f5f9340bSFabien Thomas 	pcd->pcd_get_config   = soft_get_config;
470f5f9340bSFabien Thomas 	pcd->pcd_get_msr      = NULL;
471f5f9340bSFabien Thomas 	pcd->pcd_pcpu_init    = soft_pcpu_init;
472f5f9340bSFabien Thomas 	pcd->pcd_pcpu_fini    = soft_pcpu_fini;
473f5f9340bSFabien Thomas 	pcd->pcd_read_pmc     = soft_read_pmc;
474f5f9340bSFabien Thomas 	pcd->pcd_write_pmc    = soft_write_pmc;
475f5f9340bSFabien Thomas 	pcd->pcd_release_pmc  = soft_release_pmc;
476f5f9340bSFabien Thomas 	pcd->pcd_start_pmc    = soft_start_pmc;
477f5f9340bSFabien Thomas 	pcd->pcd_stop_pmc     = soft_stop_pmc;
478f5f9340bSFabien Thomas 
479f5f9340bSFabien Thomas 	md->pmd_npmc += SOFT_NPMCS;
480f5f9340bSFabien Thomas }
481f5f9340bSFabien Thomas 
482f5f9340bSFabien Thomas void
483f5f9340bSFabien Thomas pmc_soft_finalize(struct pmc_mdep *md)
484f5f9340bSFabien Thomas {
485f5f9340bSFabien Thomas #ifdef	INVARIANTS
486f5f9340bSFabien Thomas 	int i, ncpus;
487f5f9340bSFabien Thomas 
488f5f9340bSFabien Thomas 	ncpus = pmc_cpu_max();
489f5f9340bSFabien Thomas 	for (i = 0; i < ncpus; i++)
490f5f9340bSFabien Thomas 		KASSERT(soft_pcpu[i] == NULL, ("[soft,%d] non-null pcpu cpu %d",
491f5f9340bSFabien Thomas 		    __LINE__, i));
492f5f9340bSFabien Thomas 
493f5f9340bSFabien Thomas 	KASSERT(md->pmd_classdep[PMC_CLASS_INDEX_SOFT].pcd_class ==
494f5f9340bSFabien Thomas 	    PMC_CLASS_SOFT, ("[soft,%d] class mismatch", __LINE__));
495f5f9340bSFabien Thomas #endif
496f5f9340bSFabien Thomas 	free(soft_pcpu, M_PMC);
497f5f9340bSFabien Thomas 	soft_pcpu = NULL;
498f5f9340bSFabien Thomas }
499