xref: /freebsd/sys/dev/hwpmc/hwpmc_soft.c (revision d49302aeadec18cf73a9c19e19e19b640451a3ad)
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);
127*d49302aeSFabien Thomas 	/* Module unload is protected by pmc SX lock. */
128*d49302aeSFabien Thomas 	if (ps->ps_alloc != NULL)
129*d49302aeSFabien 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 
139f5f9340bSFabien Thomas 	PMCDBG(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 	if (soft_pc == NULL)
245f5f9340bSFabien Thomas 		return (ENOMEM);
246f5f9340bSFabien Thomas 
247f5f9340bSFabien Thomas 	pc = pmc_pcpu[cpu];
248f5f9340bSFabien Thomas 
249f5f9340bSFabien Thomas 	KASSERT(pc != NULL, ("[soft,%d] cpu %d null per-cpu", __LINE__, cpu));
250f5f9340bSFabien Thomas 
251f5f9340bSFabien Thomas 	soft_pcpu[cpu] = soft_pc;
252f5f9340bSFabien Thomas 	phw = soft_pc->soft_hw;
253f5f9340bSFabien Thomas 	first_ri = md->pmd_classdep[PMC_CLASS_INDEX_SOFT].pcd_ri;
254f5f9340bSFabien Thomas 
255f5f9340bSFabien Thomas 	for (n = 0; n < SOFT_NPMCS; n++, phw++) {
256f5f9340bSFabien Thomas 		phw->phw_state = PMC_PHW_FLAG_IS_ENABLED |
257f5f9340bSFabien Thomas 		    PMC_PHW_CPU_TO_STATE(cpu) | PMC_PHW_INDEX_TO_STATE(n);
258f5f9340bSFabien Thomas 		phw->phw_pmc = NULL;
259f5f9340bSFabien Thomas 		pc->pc_hwpmcs[n + first_ri] = phw;
260f5f9340bSFabien Thomas 	}
261f5f9340bSFabien Thomas 
262f5f9340bSFabien Thomas 	return (0);
263f5f9340bSFabien Thomas }
264f5f9340bSFabien Thomas 
265f5f9340bSFabien Thomas static int
266f5f9340bSFabien Thomas soft_read_pmc(int cpu, int ri, pmc_value_t *v)
267f5f9340bSFabien Thomas {
268f5f9340bSFabien Thomas 	struct pmc *pm;
269f5f9340bSFabien Thomas 	const struct pmc_hw *phw;
270f5f9340bSFabien Thomas 
271f5f9340bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
272f5f9340bSFabien Thomas 	    ("[soft,%d] illegal CPU value %d", __LINE__, cpu));
273f5f9340bSFabien Thomas 	KASSERT(ri >= 0 && ri < SOFT_NPMCS,
274f5f9340bSFabien Thomas 	    ("[soft,%d] illegal row-index %d", __LINE__, ri));
275f5f9340bSFabien Thomas 
276f5f9340bSFabien Thomas 	phw = &soft_pcpu[cpu]->soft_hw[ri];
277f5f9340bSFabien Thomas 	pm  = phw->phw_pmc;
278f5f9340bSFabien Thomas 
279f5f9340bSFabien Thomas 	KASSERT(pm != NULL,
280f5f9340bSFabien Thomas 	    ("[soft,%d] no owner for PHW [cpu%d,pmc%d]", __LINE__, cpu, ri));
281f5f9340bSFabien Thomas 
282f5f9340bSFabien Thomas 	PMCDBG(MDP,REA,1,"soft-read id=%d", ri);
283f5f9340bSFabien Thomas 
284f5f9340bSFabien Thomas 	*v = soft_pcpu[cpu]->soft_values[ri];
285f5f9340bSFabien Thomas 
286f5f9340bSFabien Thomas 	return (0);
287f5f9340bSFabien Thomas }
288f5f9340bSFabien Thomas 
289f5f9340bSFabien Thomas static int
290f5f9340bSFabien Thomas soft_write_pmc(int cpu, int ri, pmc_value_t v)
291f5f9340bSFabien Thomas {
292f5f9340bSFabien Thomas 	struct pmc *pm;
293f5f9340bSFabien Thomas 	const struct soft_descr *pd;
294f5f9340bSFabien Thomas 
295f5f9340bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
296f5f9340bSFabien Thomas 	    ("[soft,%d] illegal cpu value %d", __LINE__, cpu));
297f5f9340bSFabien Thomas 	KASSERT(ri >= 0 && ri < SOFT_NPMCS,
298f5f9340bSFabien Thomas 	    ("[soft,%d] illegal row-index %d", __LINE__, ri));
299f5f9340bSFabien Thomas 
300f5f9340bSFabien Thomas 	pm = soft_pcpu[cpu]->soft_hw[ri].phw_pmc;
301f5f9340bSFabien Thomas 	pd = &soft_pmcdesc[ri];
302f5f9340bSFabien Thomas 
303f5f9340bSFabien Thomas 	KASSERT(pm,
304f5f9340bSFabien Thomas 	    ("[soft,%d] cpu %d ri %d pmc not configured", __LINE__, cpu, ri));
305f5f9340bSFabien Thomas 
306f5f9340bSFabien Thomas 	PMCDBG(MDP,WRI,1, "soft-write cpu=%d ri=%d v=%jx", cpu, ri, v);
307f5f9340bSFabien Thomas 
308f5f9340bSFabien Thomas 	soft_pcpu[cpu]->soft_values[ri] = v;
309f5f9340bSFabien Thomas 
310f5f9340bSFabien Thomas 	return (0);
311f5f9340bSFabien Thomas }
312f5f9340bSFabien Thomas 
313f5f9340bSFabien Thomas static int
314f5f9340bSFabien Thomas soft_release_pmc(int cpu, int ri, struct pmc *pmc)
315f5f9340bSFabien Thomas {
316f5f9340bSFabien Thomas 	struct pmc_hw *phw;
317*d49302aeSFabien Thomas 	enum pmc_event ev;
318*d49302aeSFabien Thomas 	struct pmc_soft *ps;
319f5f9340bSFabien Thomas 
320f5f9340bSFabien Thomas 	(void) pmc;
321f5f9340bSFabien Thomas 
322f5f9340bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
323f5f9340bSFabien Thomas 	    ("[soft,%d] illegal CPU value %d", __LINE__, cpu));
324f5f9340bSFabien Thomas 	KASSERT(ri >= 0 && ri < SOFT_NPMCS,
325f5f9340bSFabien Thomas 	    ("[soft,%d] illegal row-index %d", __LINE__, ri));
326f5f9340bSFabien Thomas 
327f5f9340bSFabien Thomas 	phw = &soft_pcpu[cpu]->soft_hw[ri];
328f5f9340bSFabien Thomas 
329f5f9340bSFabien Thomas 	KASSERT(phw->phw_pmc == NULL,
330f5f9340bSFabien Thomas 	    ("[soft,%d] PHW pmc %p non-NULL", __LINE__, phw->phw_pmc));
331f5f9340bSFabien Thomas 
332*d49302aeSFabien Thomas 	ev = pmc->pm_event;
333*d49302aeSFabien Thomas 
334*d49302aeSFabien Thomas 	/* Check if event is registered. */
335*d49302aeSFabien Thomas 	ps = pmc_soft_ev_acquire(ev);
336*d49302aeSFabien Thomas 	KASSERT(ps != NULL,
337*d49302aeSFabien Thomas 	    ("[soft,%d] unregistered event %d", __LINE__, ev));
338*d49302aeSFabien Thomas 	pmc_soft_ev_release(ps);
339*d49302aeSFabien Thomas 	/* Module unload is protected by pmc SX lock. */
340*d49302aeSFabien Thomas 	if (ps->ps_release != NULL)
341*d49302aeSFabien Thomas 		ps->ps_release();
342f5f9340bSFabien Thomas 	return (0);
343f5f9340bSFabien Thomas }
344f5f9340bSFabien Thomas 
345f5f9340bSFabien Thomas static int
346f5f9340bSFabien Thomas soft_start_pmc(int cpu, int ri)
347f5f9340bSFabien Thomas {
348f5f9340bSFabien Thomas 	struct pmc *pm;
349f5f9340bSFabien Thomas 	struct soft_cpu *pc;
350f5f9340bSFabien Thomas 	struct pmc_soft *ps;
351f5f9340bSFabien Thomas 
352f5f9340bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
353f5f9340bSFabien Thomas 	    ("[soft,%d] illegal CPU value %d", __LINE__, cpu));
354f5f9340bSFabien Thomas 	KASSERT(ri >= 0 && ri < SOFT_NPMCS,
355f5f9340bSFabien Thomas 	    ("[soft,%d] illegal row-index %d", __LINE__, ri));
356f5f9340bSFabien Thomas 
357f5f9340bSFabien Thomas 	pc = soft_pcpu[cpu];
358f5f9340bSFabien Thomas 	pm = pc->soft_hw[ri].phw_pmc;
359f5f9340bSFabien Thomas 
360f5f9340bSFabien Thomas 	KASSERT(pm,
361f5f9340bSFabien Thomas 	    ("[soft,%d] cpu %d ri %d pmc not configured", __LINE__, cpu, ri));
362f5f9340bSFabien Thomas 
363f5f9340bSFabien Thomas 	ps = pmc_soft_ev_acquire(pm->pm_event);
364f5f9340bSFabien Thomas 	if (ps == NULL)
365f5f9340bSFabien Thomas 		return (EINVAL);
366f5f9340bSFabien Thomas 	atomic_add_int(&ps->ps_running, 1);
367f5f9340bSFabien Thomas 	pmc_soft_ev_release(ps);
368f5f9340bSFabien Thomas 
369f5f9340bSFabien Thomas 	return (0);
370f5f9340bSFabien Thomas }
371f5f9340bSFabien Thomas 
372f5f9340bSFabien Thomas static int
373f5f9340bSFabien Thomas soft_stop_pmc(int cpu, int ri)
374f5f9340bSFabien Thomas {
375f5f9340bSFabien Thomas 	struct pmc *pm;
376f5f9340bSFabien Thomas 	struct soft_cpu *pc;
377f5f9340bSFabien Thomas 	struct pmc_soft *ps;
378f5f9340bSFabien Thomas 
379f5f9340bSFabien Thomas 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
380f5f9340bSFabien Thomas 	    ("[soft,%d] illegal CPU value %d", __LINE__, cpu));
381f5f9340bSFabien Thomas 	KASSERT(ri >= 0 && ri < SOFT_NPMCS,
382f5f9340bSFabien Thomas 	    ("[soft,%d] illegal row-index %d", __LINE__, ri));
383f5f9340bSFabien Thomas 
384f5f9340bSFabien Thomas 	pc = soft_pcpu[cpu];
385f5f9340bSFabien Thomas 	pm = pc->soft_hw[ri].phw_pmc;
386f5f9340bSFabien Thomas 
387f5f9340bSFabien Thomas 	KASSERT(pm,
388f5f9340bSFabien Thomas 	    ("[soft,%d] cpu %d ri %d pmc not configured", __LINE__, cpu, ri));
389f5f9340bSFabien Thomas 
390f5f9340bSFabien Thomas 	ps = pmc_soft_ev_acquire(pm->pm_event);
391f5f9340bSFabien Thomas 	/* event unregistered ? */
392f5f9340bSFabien Thomas 	if (ps != NULL) {
393f5f9340bSFabien Thomas 		atomic_subtract_int(&ps->ps_running, 1);
394f5f9340bSFabien Thomas 		pmc_soft_ev_release(ps);
395f5f9340bSFabien Thomas 	}
396f5f9340bSFabien Thomas 
397f5f9340bSFabien Thomas 	return (0);
398f5f9340bSFabien Thomas }
399f5f9340bSFabien Thomas 
400f5f9340bSFabien Thomas int
401f5f9340bSFabien Thomas pmc_soft_intr(struct pmckern_soft *ks)
402f5f9340bSFabien Thomas {
403f5f9340bSFabien Thomas 	struct pmc *pm;
404f5f9340bSFabien Thomas 	struct soft_cpu *pc;
405f5f9340bSFabien Thomas 	int ri, processed, error, user_mode;
406f5f9340bSFabien Thomas 
407f5f9340bSFabien Thomas 	KASSERT(ks->pm_cpu >= 0 && ks->pm_cpu < pmc_cpu_max(),
408f5f9340bSFabien Thomas 	    ("[soft,%d] CPU %d out of range", __LINE__, ks->pm_cpu));
409f5f9340bSFabien Thomas 
410f5f9340bSFabien Thomas 	processed = 0;
411f5f9340bSFabien Thomas 	pc = soft_pcpu[ks->pm_cpu];
412f5f9340bSFabien Thomas 
413f5f9340bSFabien Thomas 	for (ri = 0; ri < SOFT_NPMCS; ri++) {
414f5f9340bSFabien Thomas 
415f5f9340bSFabien Thomas 		pm = pc->soft_hw[ri].phw_pmc;
416f5f9340bSFabien Thomas 		if (pm == NULL ||
417f5f9340bSFabien Thomas 		    pm->pm_state != PMC_STATE_RUNNING ||
418f5f9340bSFabien Thomas 		    pm->pm_event != ks->pm_ev) {
419f5f9340bSFabien Thomas 				continue;
420f5f9340bSFabien Thomas 		}
421f5f9340bSFabien Thomas 
422f5f9340bSFabien Thomas 		processed = 1;
423f5f9340bSFabien Thomas 		if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) {
4248a4f65bcSAlexander Motin 			if ((pc->soft_values[ri]--) <= 0)
4258a4f65bcSAlexander Motin 				pc->soft_values[ri] += pm->pm_sc.pm_reloadcount;
4268a4f65bcSAlexander Motin 			else
4278a4f65bcSAlexander Motin 				continue;
428f5f9340bSFabien Thomas 			user_mode = TRAPF_USERMODE(ks->pm_tf);
429f5f9340bSFabien Thomas 			error = pmc_process_interrupt(ks->pm_cpu, PMC_SR, pm,
430f5f9340bSFabien Thomas 			    ks->pm_tf, user_mode);
431f5f9340bSFabien Thomas 			if (error) {
432f5f9340bSFabien Thomas 				soft_stop_pmc(ks->pm_cpu, ri);
433f5f9340bSFabien Thomas 				continue;
434f5f9340bSFabien Thomas 			}
435f5f9340bSFabien Thomas 
436f5f9340bSFabien Thomas 			if (user_mode) {
437f5f9340bSFabien Thomas 				/* If in user mode setup AST to process
438f5f9340bSFabien Thomas 				 * callchain out of interrupt context.
439f5f9340bSFabien Thomas 				 */
440f5f9340bSFabien Thomas 				curthread->td_flags |= TDF_ASTPENDING;
441f5f9340bSFabien Thomas 			}
4428a4f65bcSAlexander Motin 		} else
4438a4f65bcSAlexander Motin 			pc->soft_values[ri]++;
444f5f9340bSFabien Thomas 	}
445f5f9340bSFabien Thomas 
446f5f9340bSFabien Thomas 	atomic_add_int(processed ? &pmc_stats.pm_intr_processed :
447f5f9340bSFabien Thomas 	    &pmc_stats.pm_intr_ignored, 1);
448f5f9340bSFabien Thomas 
449f5f9340bSFabien Thomas 	return (processed);
450f5f9340bSFabien Thomas }
451f5f9340bSFabien Thomas 
452f5f9340bSFabien Thomas void
453f5f9340bSFabien Thomas pmc_soft_initialize(struct pmc_mdep *md)
454f5f9340bSFabien Thomas {
455f5f9340bSFabien Thomas 	struct pmc_classdep *pcd;
456f5f9340bSFabien Thomas 
457f5f9340bSFabien Thomas 	/* Add SOFT PMCs. */
458f5f9340bSFabien Thomas 	soft_pcpu = malloc(sizeof(struct soft_cpu *) * pmc_cpu_max(), M_PMC,
459f5f9340bSFabien Thomas 	    M_ZERO|M_WAITOK);
460f5f9340bSFabien Thomas 
461f5f9340bSFabien Thomas 	pcd = &md->pmd_classdep[PMC_CLASS_INDEX_SOFT];
462f5f9340bSFabien Thomas 
463f5f9340bSFabien Thomas 	pcd->pcd_caps	= SOFT_CAPS;
464f5f9340bSFabien Thomas 	pcd->pcd_class	= PMC_CLASS_SOFT;
465f5f9340bSFabien Thomas 	pcd->pcd_num	= SOFT_NPMCS;
466f5f9340bSFabien Thomas 	pcd->pcd_ri	= md->pmd_npmc;
467f5f9340bSFabien Thomas 	pcd->pcd_width	= 64;
468f5f9340bSFabien Thomas 
469f5f9340bSFabien Thomas 	pcd->pcd_allocate_pmc = soft_allocate_pmc;
470f5f9340bSFabien Thomas 	pcd->pcd_config_pmc   = soft_config_pmc;
471f5f9340bSFabien Thomas 	pcd->pcd_describe     = soft_describe;
472f5f9340bSFabien Thomas 	pcd->pcd_get_config   = soft_get_config;
473f5f9340bSFabien Thomas 	pcd->pcd_get_msr      = NULL;
474f5f9340bSFabien Thomas 	pcd->pcd_pcpu_init    = soft_pcpu_init;
475f5f9340bSFabien Thomas 	pcd->pcd_pcpu_fini    = soft_pcpu_fini;
476f5f9340bSFabien Thomas 	pcd->pcd_read_pmc     = soft_read_pmc;
477f5f9340bSFabien Thomas 	pcd->pcd_write_pmc    = soft_write_pmc;
478f5f9340bSFabien Thomas 	pcd->pcd_release_pmc  = soft_release_pmc;
479f5f9340bSFabien Thomas 	pcd->pcd_start_pmc    = soft_start_pmc;
480f5f9340bSFabien Thomas 	pcd->pcd_stop_pmc     = soft_stop_pmc;
481f5f9340bSFabien Thomas 
482f5f9340bSFabien Thomas 	md->pmd_npmc += SOFT_NPMCS;
483f5f9340bSFabien Thomas }
484f5f9340bSFabien Thomas 
485f5f9340bSFabien Thomas void
486f5f9340bSFabien Thomas pmc_soft_finalize(struct pmc_mdep *md)
487f5f9340bSFabien Thomas {
488f5f9340bSFabien Thomas #ifdef	INVARIANTS
489f5f9340bSFabien Thomas 	int i, ncpus;
490f5f9340bSFabien Thomas 
491f5f9340bSFabien Thomas 	ncpus = pmc_cpu_max();
492f5f9340bSFabien Thomas 	for (i = 0; i < ncpus; i++)
493f5f9340bSFabien Thomas 		KASSERT(soft_pcpu[i] == NULL, ("[soft,%d] non-null pcpu cpu %d",
494f5f9340bSFabien Thomas 		    __LINE__, i));
495f5f9340bSFabien Thomas 
496f5f9340bSFabien Thomas 	KASSERT(md->pmd_classdep[PMC_CLASS_INDEX_SOFT].pcd_class ==
497f5f9340bSFabien Thomas 	    PMC_CLASS_SOFT, ("[soft,%d] class mismatch", __LINE__));
498f5f9340bSFabien Thomas #endif
499f5f9340bSFabien Thomas 	free(soft_pcpu, M_PMC);
500f5f9340bSFabien Thomas 	soft_pcpu = NULL;
501f5f9340bSFabien Thomas }
502