xref: /netbsd-src/sys/dev/tprof/tprof.c (revision 183ce831afd8041589560bf846730d6193878392)
1*183ce831Smsaitoh /*	$NetBSD: tprof.c,v 1.23 2023/04/11 10:07:12 msaitoh Exp $	*/
248a1e4c2Syamt 
348a1e4c2Syamt /*-
489e330a7Syamt  * Copyright (c)2008,2009,2010 YAMAMOTO Takashi,
548a1e4c2Syamt  * All rights reserved.
648a1e4c2Syamt  *
748a1e4c2Syamt  * Redistribution and use in source and binary forms, with or without
848a1e4c2Syamt  * modification, are permitted provided that the following conditions
948a1e4c2Syamt  * are met:
1048a1e4c2Syamt  * 1. Redistributions of source code must retain the above copyright
1148a1e4c2Syamt  *    notice, this list of conditions and the following disclaimer.
1248a1e4c2Syamt  * 2. Redistributions in binary form must reproduce the above copyright
1348a1e4c2Syamt  *    notice, this list of conditions and the following disclaimer in the
1448a1e4c2Syamt  *    documentation and/or other materials provided with the distribution.
1548a1e4c2Syamt  *
1648a1e4c2Syamt  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1748a1e4c2Syamt  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1848a1e4c2Syamt  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1948a1e4c2Syamt  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2048a1e4c2Syamt  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2148a1e4c2Syamt  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2248a1e4c2Syamt  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2348a1e4c2Syamt  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2448a1e4c2Syamt  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2548a1e4c2Syamt  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2648a1e4c2Syamt  * SUCH DAMAGE.
2748a1e4c2Syamt  */
2848a1e4c2Syamt 
2948a1e4c2Syamt #include <sys/cdefs.h>
30*183ce831Smsaitoh __KERNEL_RCSID(0, "$NetBSD: tprof.c,v 1.23 2023/04/11 10:07:12 msaitoh Exp $");
3148a1e4c2Syamt 
3248a1e4c2Syamt #include <sys/param.h>
3348a1e4c2Syamt #include <sys/systm.h>
3448a1e4c2Syamt #include <sys/kernel.h>
3548a1e4c2Syamt 
3648a1e4c2Syamt #include <sys/callout.h>
37003f8943Sriastradh #include <sys/conf.h>
38003f8943Sriastradh #include <sys/cpu.h>
3948a1e4c2Syamt #include <sys/kmem.h>
40e1b625b4Syamt #include <sys/module.h>
41003f8943Sriastradh #include <sys/percpu.h>
422b54f35cSryo #include <sys/poll.h>
4389e330a7Syamt #include <sys/proc.h>
4448a1e4c2Syamt #include <sys/queue.h>
452b54f35cSryo #include <sys/select.h>
46003f8943Sriastradh #include <sys/workqueue.h>
47caba1a6fSryo #include <sys/xcall.h>
4848a1e4c2Syamt 
4948a1e4c2Syamt #include <dev/tprof/tprof.h>
5048a1e4c2Syamt #include <dev/tprof/tprof_ioctl.h>
5148a1e4c2Syamt 
52e7ae23fdSchristos #include "ioconf.h"
53e7ae23fdSchristos 
54caba1a6fSryo #ifndef TPROF_HZ
55caba1a6fSryo #define TPROF_HZ	10000
56caba1a6fSryo #endif
57caba1a6fSryo 
58e1b625b4Syamt /*
59e1b625b4Syamt  * locking order:
60e1b625b4Syamt  *	tprof_reader_lock -> tprof_lock
61e1b625b4Syamt  *	tprof_startstop_lock -> tprof_lock
62e1b625b4Syamt  */
63e1b625b4Syamt 
64e1b625b4Syamt /*
65e1b625b4Syamt  * protected by:
66e1b625b4Syamt  *	L: tprof_lock
67e1b625b4Syamt  *	R: tprof_reader_lock
68e1b625b4Syamt  *	S: tprof_startstop_lock
6989e330a7Syamt  *	s: writer should hold tprof_startstop_lock and tprof_lock
7089e330a7Syamt  *	   reader should hold tprof_startstop_lock or tprof_lock
71e1b625b4Syamt  */
72e1b625b4Syamt 
7348a1e4c2Syamt typedef struct tprof_buf {
7448a1e4c2Syamt 	u_int b_used;
7548a1e4c2Syamt 	u_int b_size;
7648a1e4c2Syamt 	u_int b_overflow;
7748a1e4c2Syamt 	u_int b_unused;
7848a1e4c2Syamt 	STAILQ_ENTRY(tprof_buf) b_list;
7948a1e4c2Syamt 	tprof_sample_t b_data[];
8048a1e4c2Syamt } tprof_buf_t;
8148a1e4c2Syamt #define	TPROF_BUF_BYTESIZE(sz) \
8248a1e4c2Syamt 	(sizeof(tprof_buf_t) + (sz) * sizeof(tprof_sample_t))
832b54f35cSryo #define	TPROF_MAX_SAMPLES_PER_BUF	TPROF_HZ
8448a1e4c2Syamt 
8548a1e4c2Syamt typedef struct {
8648a1e4c2Syamt 	tprof_buf_t *c_buf;
8783fb606cSyamt 	uint32_t c_cpuid;
8848a1e4c2Syamt 	struct work c_work;
8948a1e4c2Syamt 	callout_t c_callout;
9048a1e4c2Syamt } __aligned(CACHE_LINE_SIZE) tprof_cpu_t;
9148a1e4c2Syamt 
92e1b625b4Syamt typedef struct tprof_backend {
93caba1a6fSryo 	/*
94caba1a6fSryo 	 * tprof_backend_softc_t must be passed as an argument to the interrupt
95caba1a6fSryo 	 * handler, but since this is difficult to implement in armv7/v8. Then,
96caba1a6fSryo 	 * tprof_backend is exposed. Additionally, softc must be placed at the
97caba1a6fSryo 	 * beginning of struct tprof_backend.
98caba1a6fSryo 	 */
99caba1a6fSryo 	tprof_backend_softc_t tb_softc;
100caba1a6fSryo 
101e1b625b4Syamt 	const char *tb_name;
102e1b625b4Syamt 	const tprof_backend_ops_t *tb_ops;
103e1b625b4Syamt 	LIST_ENTRY(tprof_backend) tb_list;
104e1b625b4Syamt } tprof_backend_t;
10570a4009aSyamt 
10648a1e4c2Syamt static kmutex_t tprof_lock;
107e1b625b4Syamt static u_int tprof_nworker;		/* L: # of running worker LWPs */
10848a1e4c2Syamt static lwp_t *tprof_owner;
109e1b625b4Syamt static STAILQ_HEAD(, tprof_buf) tprof_list; /* L: global buffer list */
110e1b625b4Syamt static u_int tprof_nbuf_on_list;	/* L: # of buffers on tprof_list */
11148a1e4c2Syamt static struct workqueue *tprof_wq;
112003f8943Sriastradh static struct percpu *tprof_cpus __read_mostly;	/* tprof_cpu_t * */
11348a1e4c2Syamt static u_int tprof_samples_per_buf;
1142b54f35cSryo static u_int tprof_max_buf;
11548a1e4c2Syamt 
116caba1a6fSryo tprof_backend_t *tprof_backend;	/* S: */
117e1b625b4Syamt static LIST_HEAD(, tprof_backend) tprof_backends =
118e1b625b4Syamt     LIST_HEAD_INITIALIZER(tprof_backend); /* S: */
119e1b625b4Syamt 
12048a1e4c2Syamt static kmutex_t tprof_reader_lock;
121e1b625b4Syamt static kcondvar_t tprof_reader_cv;	/* L: */
122e1b625b4Syamt static off_t tprof_reader_offset;	/* R: */
12348a1e4c2Syamt 
12448a1e4c2Syamt static kmutex_t tprof_startstop_lock;
125e1b625b4Syamt static kcondvar_t tprof_cv;		/* L: */
1262b54f35cSryo static struct selinfo tprof_selp;	/* L: */
12748a1e4c2Syamt 
128e1b625b4Syamt static struct tprof_stat tprof_stat;	/* L: */
12948a1e4c2Syamt 
13048a1e4c2Syamt static tprof_cpu_t *
tprof_cpu_direct(struct cpu_info * ci)1317c9aa013Sryo tprof_cpu_direct(struct cpu_info *ci)
1327c9aa013Sryo {
1337c9aa013Sryo 	tprof_cpu_t **cp;
1347c9aa013Sryo 
1357c9aa013Sryo 	cp = percpu_getptr_remote(tprof_cpus, ci);
1367c9aa013Sryo 	return *cp;
1377c9aa013Sryo }
1387c9aa013Sryo 
1397c9aa013Sryo static tprof_cpu_t *
tprof_cpu(struct cpu_info * ci)14048a1e4c2Syamt tprof_cpu(struct cpu_info *ci)
14148a1e4c2Syamt {
1427c9aa013Sryo 	tprof_cpu_t *c;
14348a1e4c2Syamt 
144003f8943Sriastradh 	/*
145003f8943Sriastradh 	 * As long as xcalls are blocked -- e.g., by kpreempt_disable
146003f8943Sriastradh 	 * -- the percpu object will not be swapped and destroyed.  We
147003f8943Sriastradh 	 * can't write to it, because the data may have already been
148003f8943Sriastradh 	 * moved to a new buffer, but we can safely read from it.
149003f8943Sriastradh 	 */
150003f8943Sriastradh 	kpreempt_disable();
1517c9aa013Sryo 	c = tprof_cpu_direct(ci);
152003f8943Sriastradh 	kpreempt_enable();
153003f8943Sriastradh 
154003f8943Sriastradh 	return c;
15548a1e4c2Syamt }
15648a1e4c2Syamt 
15748a1e4c2Syamt static tprof_cpu_t *
tprof_curcpu(void)15848a1e4c2Syamt tprof_curcpu(void)
15948a1e4c2Syamt {
16048a1e4c2Syamt 
16148a1e4c2Syamt 	return tprof_cpu(curcpu());
16248a1e4c2Syamt }
16348a1e4c2Syamt 
16448a1e4c2Syamt static tprof_buf_t *
tprof_buf_alloc(void)16548a1e4c2Syamt tprof_buf_alloc(void)
16648a1e4c2Syamt {
16748a1e4c2Syamt 	tprof_buf_t *new;
16848a1e4c2Syamt 	u_int size = tprof_samples_per_buf;
16948a1e4c2Syamt 
17048a1e4c2Syamt 	new = kmem_alloc(TPROF_BUF_BYTESIZE(size), KM_SLEEP);
17148a1e4c2Syamt 	new->b_used = 0;
17248a1e4c2Syamt 	new->b_size = size;
17348a1e4c2Syamt 	new->b_overflow = 0;
17448a1e4c2Syamt 	return new;
17548a1e4c2Syamt }
17648a1e4c2Syamt 
17748a1e4c2Syamt static void
tprof_buf_free(tprof_buf_t * buf)17848a1e4c2Syamt tprof_buf_free(tprof_buf_t *buf)
17948a1e4c2Syamt {
18048a1e4c2Syamt 
18148a1e4c2Syamt 	kmem_free(buf, TPROF_BUF_BYTESIZE(buf->b_size));
18248a1e4c2Syamt }
18348a1e4c2Syamt 
18448a1e4c2Syamt static tprof_buf_t *
tprof_buf_switch(tprof_cpu_t * c,tprof_buf_t * new)18548a1e4c2Syamt tprof_buf_switch(tprof_cpu_t *c, tprof_buf_t *new)
18648a1e4c2Syamt {
18748a1e4c2Syamt 	tprof_buf_t *old;
18848a1e4c2Syamt 
18948a1e4c2Syamt 	old = c->c_buf;
19048a1e4c2Syamt 	c->c_buf = new;
19148a1e4c2Syamt 	return old;
19248a1e4c2Syamt }
19348a1e4c2Syamt 
19448a1e4c2Syamt static tprof_buf_t *
tprof_buf_refresh(void)19548a1e4c2Syamt tprof_buf_refresh(void)
19648a1e4c2Syamt {
19748a1e4c2Syamt 	tprof_cpu_t * const c = tprof_curcpu();
19848a1e4c2Syamt 	tprof_buf_t *new;
19948a1e4c2Syamt 
20048a1e4c2Syamt 	new = tprof_buf_alloc();
20148a1e4c2Syamt 	return tprof_buf_switch(c, new);
20248a1e4c2Syamt }
20348a1e4c2Syamt 
20448a1e4c2Syamt static void
tprof_worker(struct work * wk,void * dummy)20548a1e4c2Syamt tprof_worker(struct work *wk, void *dummy)
20648a1e4c2Syamt {
20748a1e4c2Syamt 	tprof_cpu_t * const c = tprof_curcpu();
20848a1e4c2Syamt 	tprof_buf_t *buf;
209caba1a6fSryo 	tprof_backend_t *tb;
21048a1e4c2Syamt 	bool shouldstop;
21148a1e4c2Syamt 
21248a1e4c2Syamt 	KASSERT(wk == &c->c_work);
21348a1e4c2Syamt 	KASSERT(dummy == NULL);
21448a1e4c2Syamt 
21548a1e4c2Syamt 	/*
216*183ce831Smsaitoh 	 * Get a per cpu buffer.
21748a1e4c2Syamt 	 */
21848a1e4c2Syamt 	buf = tprof_buf_refresh();
21948a1e4c2Syamt 
22048a1e4c2Syamt 	/*
22148a1e4c2Syamt 	 * and put it on the global list for read(2).
22248a1e4c2Syamt 	 */
22348a1e4c2Syamt 	mutex_enter(&tprof_lock);
224caba1a6fSryo 	tb = tprof_backend;
225caba1a6fSryo 	shouldstop = (tb == NULL || tb->tb_softc.sc_ctr_running_mask == 0);
22648a1e4c2Syamt 	if (shouldstop) {
22748a1e4c2Syamt 		KASSERT(tprof_nworker > 0);
22848a1e4c2Syamt 		tprof_nworker--;
22948a1e4c2Syamt 		cv_broadcast(&tprof_cv);
23048a1e4c2Syamt 		cv_broadcast(&tprof_reader_cv);
23148a1e4c2Syamt 	}
23248a1e4c2Syamt 	if (buf->b_used == 0) {
23348a1e4c2Syamt 		tprof_stat.ts_emptybuf++;
2342b54f35cSryo 	} else if (tprof_nbuf_on_list < tprof_max_buf) {
23548a1e4c2Syamt 		tprof_stat.ts_sample += buf->b_used;
23648a1e4c2Syamt 		tprof_stat.ts_overflow += buf->b_overflow;
23748a1e4c2Syamt 		tprof_stat.ts_buf++;
23848a1e4c2Syamt 		STAILQ_INSERT_TAIL(&tprof_list, buf, b_list);
23948a1e4c2Syamt 		tprof_nbuf_on_list++;
24048a1e4c2Syamt 		buf = NULL;
2412b54f35cSryo 		selnotify(&tprof_selp, 0, NOTE_SUBMIT);
24248a1e4c2Syamt 		cv_broadcast(&tprof_reader_cv);
24348a1e4c2Syamt 	} else {
24448a1e4c2Syamt 		tprof_stat.ts_dropbuf_sample += buf->b_used;
24548a1e4c2Syamt 		tprof_stat.ts_dropbuf++;
24648a1e4c2Syamt 	}
24748a1e4c2Syamt 	mutex_exit(&tprof_lock);
248*183ce831Smsaitoh 	if (buf)
24948a1e4c2Syamt 		tprof_buf_free(buf);
250*183ce831Smsaitoh 
251*183ce831Smsaitoh 	if (!shouldstop)
2522b54f35cSryo 		callout_schedule(&c->c_callout, hz / 8);
25348a1e4c2Syamt }
25448a1e4c2Syamt 
25548a1e4c2Syamt static void
tprof_kick(void * vp)25648a1e4c2Syamt tprof_kick(void *vp)
25748a1e4c2Syamt {
25848a1e4c2Syamt 	struct cpu_info * const ci = vp;
25948a1e4c2Syamt 	tprof_cpu_t * const c = tprof_cpu(ci);
26048a1e4c2Syamt 
26148a1e4c2Syamt 	workqueue_enqueue(tprof_wq, &c->c_work, ci);
26248a1e4c2Syamt }
26348a1e4c2Syamt 
26448a1e4c2Syamt static void
tprof_stop1(void)26548a1e4c2Syamt tprof_stop1(void)
26648a1e4c2Syamt {
26748a1e4c2Syamt 	CPU_INFO_ITERATOR cii;
26848a1e4c2Syamt 	struct cpu_info *ci;
26948a1e4c2Syamt 
27048a1e4c2Syamt 	KASSERT(mutex_owned(&tprof_startstop_lock));
27134ccaa3dSyamt 	KASSERT(tprof_nworker == 0);
27248a1e4c2Syamt 
27348a1e4c2Syamt 	for (CPU_INFO_FOREACH(cii, ci)) {
27448a1e4c2Syamt 		tprof_cpu_t * const c = tprof_cpu(ci);
27548a1e4c2Syamt 		tprof_buf_t *old;
27648a1e4c2Syamt 
27748a1e4c2Syamt 		old = tprof_buf_switch(c, NULL);
278*183ce831Smsaitoh 		if (old != NULL)
27948a1e4c2Syamt 			tprof_buf_free(old);
280*183ce831Smsaitoh 
28148a1e4c2Syamt 		callout_destroy(&c->c_callout);
28248a1e4c2Syamt 	}
28348a1e4c2Syamt 	workqueue_destroy(tprof_wq);
28448a1e4c2Syamt }
28548a1e4c2Syamt 
286a087cb3cSmaxv static void
tprof_getinfo(struct tprof_info * info)287a087cb3cSmaxv tprof_getinfo(struct tprof_info *info)
288a087cb3cSmaxv {
289a087cb3cSmaxv 	tprof_backend_t *tb;
290a087cb3cSmaxv 
291a087cb3cSmaxv 	KASSERT(mutex_owned(&tprof_startstop_lock));
292a087cb3cSmaxv 
293a087cb3cSmaxv 	memset(info, 0, sizeof(*info));
294a087cb3cSmaxv 	info->ti_version = TPROF_VERSION;
295*183ce831Smsaitoh 	if ((tb = tprof_backend) != NULL)
296a087cb3cSmaxv 		info->ti_ident = tb->tb_ops->tbo_ident();
297a087cb3cSmaxv }
298a087cb3cSmaxv 
29948a1e4c2Syamt static int
tprof_getncounters(u_int * ncounters)300caba1a6fSryo tprof_getncounters(u_int *ncounters)
301caba1a6fSryo {
302caba1a6fSryo 	tprof_backend_t *tb;
303caba1a6fSryo 
304caba1a6fSryo 	tb = tprof_backend;
305caba1a6fSryo 	if (tb == NULL)
306caba1a6fSryo 		return ENOENT;
307caba1a6fSryo 
308caba1a6fSryo 	*ncounters = tb->tb_ops->tbo_ncounters();
309caba1a6fSryo 	return 0;
310caba1a6fSryo }
311caba1a6fSryo 
312caba1a6fSryo static void
tprof_start_cpu(void * arg1,void * arg2)313caba1a6fSryo tprof_start_cpu(void *arg1, void *arg2)
314caba1a6fSryo {
315caba1a6fSryo 	tprof_backend_t *tb = arg1;
316caba1a6fSryo 	tprof_countermask_t runmask = (uintptr_t)arg2;
317caba1a6fSryo 
318caba1a6fSryo 	tb->tb_ops->tbo_start(runmask);
319caba1a6fSryo }
320caba1a6fSryo 
321caba1a6fSryo static void
tprof_stop_cpu(void * arg1,void * arg2)322caba1a6fSryo tprof_stop_cpu(void *arg1, void *arg2)
323caba1a6fSryo {
324caba1a6fSryo 	tprof_backend_t *tb = arg1;
325caba1a6fSryo 	tprof_countermask_t stopmask = (uintptr_t)arg2;
326caba1a6fSryo 
327caba1a6fSryo 	tb->tb_ops->tbo_stop(stopmask);
328caba1a6fSryo }
329caba1a6fSryo 
330caba1a6fSryo static int
tprof_start(tprof_countermask_t runmask)331caba1a6fSryo tprof_start(tprof_countermask_t runmask)
33248a1e4c2Syamt {
33348a1e4c2Syamt 	CPU_INFO_ITERATOR cii;
33448a1e4c2Syamt 	struct cpu_info *ci;
335e1b625b4Syamt 	tprof_backend_t *tb;
336caba1a6fSryo 	uint64_t xc;
337caba1a6fSryo 	int error;
338caba1a6fSryo 	bool firstrun;
33948a1e4c2Syamt 
34048a1e4c2Syamt 	KASSERT(mutex_owned(&tprof_startstop_lock));
34148a1e4c2Syamt 
342e1b625b4Syamt 	tb = tprof_backend;
343e1b625b4Syamt 	if (tb == NULL) {
344e1b625b4Syamt 		error = ENOENT;
345e1b625b4Syamt 		goto done;
346e1b625b4Syamt 	}
347caba1a6fSryo 
348caba1a6fSryo 	runmask &= ~tb->tb_softc.sc_ctr_running_mask;
349caba1a6fSryo 	runmask &= tb->tb_softc.sc_ctr_configured_mask;
350caba1a6fSryo 	if (runmask == 0) {
351caba1a6fSryo 		/*
352*183ce831Smsaitoh 		 * Targets are already running.
353*183ce831Smsaitoh 		 * Unconfigured counters are ignored.
354caba1a6fSryo 		 */
355caba1a6fSryo 		error = 0;
356e1b625b4Syamt 		goto done;
357e1b625b4Syamt 	}
358e1b625b4Syamt 
359caba1a6fSryo 	firstrun = (tb->tb_softc.sc_ctr_running_mask == 0);
360caba1a6fSryo 	if (firstrun) {
361caba1a6fSryo 		if (tb->tb_ops->tbo_establish != NULL) {
362caba1a6fSryo 			error = tb->tb_ops->tbo_establish(&tb->tb_softc);
363caba1a6fSryo 			if (error != 0)
364caba1a6fSryo 				goto done;
365caba1a6fSryo 		}
36648a1e4c2Syamt 
367caba1a6fSryo 		tprof_samples_per_buf = TPROF_MAX_SAMPLES_PER_BUF;
3682b54f35cSryo 		tprof_max_buf = ncpu * 3;
369caba1a6fSryo 		error = workqueue_create(&tprof_wq, "tprofmv", tprof_worker,
370caba1a6fSryo 		    NULL, PRI_NONE, IPL_SOFTCLOCK, WQ_MPSAFE | WQ_PERCPU);
37148a1e4c2Syamt 		if (error != 0) {
372caba1a6fSryo 			if (tb->tb_ops->tbo_disestablish != NULL)
373caba1a6fSryo 				tb->tb_ops->tbo_disestablish(&tb->tb_softc);
37448a1e4c2Syamt 			goto done;
37548a1e4c2Syamt 		}
37648a1e4c2Syamt 
37748a1e4c2Syamt 		for (CPU_INFO_FOREACH(cii, ci)) {
37848a1e4c2Syamt 			tprof_cpu_t * const c = tprof_cpu(ci);
37948a1e4c2Syamt 			tprof_buf_t *new;
38048a1e4c2Syamt 			tprof_buf_t *old;
38148a1e4c2Syamt 
38248a1e4c2Syamt 			new = tprof_buf_alloc();
38348a1e4c2Syamt 			old = tprof_buf_switch(c, new);
38448a1e4c2Syamt 			if (old != NULL) {
38548a1e4c2Syamt 				tprof_buf_free(old);
38648a1e4c2Syamt 			}
38748a1e4c2Syamt 			callout_init(&c->c_callout, CALLOUT_MPSAFE);
38848a1e4c2Syamt 			callout_setfunc(&c->c_callout, tprof_kick, ci);
38948a1e4c2Syamt 		}
39048a1e4c2Syamt 	}
39148a1e4c2Syamt 
392caba1a6fSryo 	runmask &= tb->tb_softc.sc_ctr_configured_mask;
393caba1a6fSryo 	xc = xc_broadcast(0, tprof_start_cpu, tb, (void *)(uintptr_t)runmask);
394caba1a6fSryo 	xc_wait(xc);
39548a1e4c2Syamt 	mutex_enter(&tprof_lock);
396caba1a6fSryo 	tb->tb_softc.sc_ctr_running_mask |= runmask;
39748a1e4c2Syamt 	mutex_exit(&tprof_lock);
398caba1a6fSryo 
399caba1a6fSryo 	if (firstrun) {
40048a1e4c2Syamt 		for (CPU_INFO_FOREACH(cii, ci)) {
40148a1e4c2Syamt 			tprof_cpu_t * const c = tprof_cpu(ci);
40248a1e4c2Syamt 
40348a1e4c2Syamt 			mutex_enter(&tprof_lock);
40448a1e4c2Syamt 			tprof_nworker++;
40548a1e4c2Syamt 			mutex_exit(&tprof_lock);
40648a1e4c2Syamt 			workqueue_enqueue(tprof_wq, &c->c_work, ci);
40748a1e4c2Syamt 		}
408caba1a6fSryo 	}
409366581c7Schs 	error = 0;
410366581c7Schs 
41148a1e4c2Syamt done:
41248a1e4c2Syamt 	return error;
41348a1e4c2Syamt }
41448a1e4c2Syamt 
41548a1e4c2Syamt static void
tprof_stop(tprof_countermask_t stopmask)416caba1a6fSryo tprof_stop(tprof_countermask_t stopmask)
41748a1e4c2Syamt {
418e1b625b4Syamt 	tprof_backend_t *tb;
419caba1a6fSryo 	uint64_t xc;
420caba1a6fSryo 
421caba1a6fSryo 	tb = tprof_backend;
422caba1a6fSryo 	if (tb == NULL)
423caba1a6fSryo 		return;
42448a1e4c2Syamt 
42548a1e4c2Syamt 	KASSERT(mutex_owned(&tprof_startstop_lock));
426caba1a6fSryo 	stopmask &= tb->tb_softc.sc_ctr_running_mask;
427caba1a6fSryo 	if (stopmask == 0) {
428*183ce831Smsaitoh 		/* Targets are not running */
42948a1e4c2Syamt 		goto done;
43048a1e4c2Syamt 	}
43148a1e4c2Syamt 
432caba1a6fSryo 	xc = xc_broadcast(0, tprof_stop_cpu, tb, (void *)(uintptr_t)stopmask);
433caba1a6fSryo 	xc_wait(xc);
43448a1e4c2Syamt 	mutex_enter(&tprof_lock);
435caba1a6fSryo 	tb->tb_softc.sc_ctr_running_mask &= ~stopmask;
436caba1a6fSryo 	mutex_exit(&tprof_lock);
437caba1a6fSryo 
438*183ce831Smsaitoh 	/* All counters have stopped? */
439caba1a6fSryo 	if (tb->tb_softc.sc_ctr_running_mask == 0) {
440caba1a6fSryo 		mutex_enter(&tprof_lock);
44148a1e4c2Syamt 		cv_broadcast(&tprof_reader_cv);
442*183ce831Smsaitoh 		while (tprof_nworker > 0)
44348a1e4c2Syamt 			cv_wait(&tprof_cv, &tprof_lock);
444*183ce831Smsaitoh 
44548a1e4c2Syamt 		mutex_exit(&tprof_lock);
44648a1e4c2Syamt 
44748a1e4c2Syamt 		tprof_stop1();
448caba1a6fSryo 		if (tb->tb_ops->tbo_disestablish != NULL)
449caba1a6fSryo 			tb->tb_ops->tbo_disestablish(&tb->tb_softc);
450caba1a6fSryo 	}
45148a1e4c2Syamt done:
45248a1e4c2Syamt 	;
45348a1e4c2Syamt }
45448a1e4c2Syamt 
455caba1a6fSryo static void
tprof_init_percpu_counters_offset(void * vp,void * vp2,struct cpu_info * ci)456caba1a6fSryo tprof_init_percpu_counters_offset(void *vp, void *vp2, struct cpu_info *ci)
457caba1a6fSryo {
458caba1a6fSryo 	uint64_t *counters_offset = vp;
459caba1a6fSryo 	u_int counter = (uintptr_t)vp2;
460caba1a6fSryo 
461caba1a6fSryo 	tprof_backend_t *tb = tprof_backend;
462caba1a6fSryo 	tprof_param_t *param = &tb->tb_softc.sc_count[counter].ctr_param;
463caba1a6fSryo 	counters_offset[counter] = param->p_value;
464caba1a6fSryo }
465caba1a6fSryo 
466caba1a6fSryo static void
tprof_configure_event_cpu(void * arg1,void * arg2)467caba1a6fSryo tprof_configure_event_cpu(void *arg1, void *arg2)
468caba1a6fSryo {
469caba1a6fSryo 	tprof_backend_t *tb = arg1;
470caba1a6fSryo 	u_int counter = (uintptr_t)arg2;
471caba1a6fSryo 	tprof_param_t *param = &tb->tb_softc.sc_count[counter].ctr_param;
472caba1a6fSryo 
473caba1a6fSryo 	tb->tb_ops->tbo_configure_event(counter, param);
474caba1a6fSryo }
475caba1a6fSryo 
476caba1a6fSryo static int
tprof_configure_event(const tprof_param_t * param)477caba1a6fSryo tprof_configure_event(const tprof_param_t *param)
478caba1a6fSryo {
479caba1a6fSryo 	tprof_backend_t *tb;
480caba1a6fSryo 	tprof_backend_softc_t *sc;
481caba1a6fSryo 	tprof_param_t *sc_param;
482caba1a6fSryo 	uint64_t xc;
483caba1a6fSryo 	int c, error;
484caba1a6fSryo 
485caba1a6fSryo 	if ((param->p_flags & (TPROF_PARAM_USER | TPROF_PARAM_KERN)) == 0) {
486caba1a6fSryo 		error = EINVAL;
487caba1a6fSryo 		goto done;
488caba1a6fSryo 	}
489caba1a6fSryo 
490caba1a6fSryo 	tb = tprof_backend;
491caba1a6fSryo 	if (tb == NULL) {
492caba1a6fSryo 		error = ENOENT;
493caba1a6fSryo 		goto done;
494caba1a6fSryo 	}
495caba1a6fSryo 	sc = &tb->tb_softc;
496caba1a6fSryo 
497caba1a6fSryo 	c = param->p_counter;
498caba1a6fSryo 	if (c >= tb->tb_softc.sc_ncounters) {
499caba1a6fSryo 		error = EINVAL;
500caba1a6fSryo 		goto done;
501caba1a6fSryo 	}
502caba1a6fSryo 
503caba1a6fSryo 	if (tb->tb_ops->tbo_valid_event != NULL) {
504caba1a6fSryo 		error = tb->tb_ops->tbo_valid_event(param->p_counter, param);
505caba1a6fSryo 		if (error != 0)
506caba1a6fSryo 			goto done;
507caba1a6fSryo 	}
508caba1a6fSryo 
509caba1a6fSryo 	/* if already running, stop the counter */
510caba1a6fSryo 	if (ISSET(c, tb->tb_softc.sc_ctr_running_mask))
511caba1a6fSryo 		tprof_stop(__BIT(c));
512caba1a6fSryo 
513caba1a6fSryo 	sc->sc_count[c].ctr_bitwidth =
514caba1a6fSryo 	    tb->tb_ops->tbo_counter_bitwidth(param->p_counter);
515caba1a6fSryo 
516caba1a6fSryo 	sc_param = &sc->sc_count[c].ctr_param;
517caba1a6fSryo 	memcpy(sc_param, param, sizeof(*sc_param)); /* save copy of param */
518caba1a6fSryo 
519caba1a6fSryo 	if (ISSET(param->p_flags, TPROF_PARAM_PROFILE)) {
520caba1a6fSryo 		uint64_t freq, inum, dnum;
521caba1a6fSryo 
522caba1a6fSryo 		freq = tb->tb_ops->tbo_counter_estimate_freq(c);
523caba1a6fSryo 		sc->sc_count[c].ctr_counter_val = freq / TPROF_HZ;
524caba1a6fSryo 		if (sc->sc_count[c].ctr_counter_val == 0) {
525caba1a6fSryo 			printf("%s: counter#%d frequency (%"PRIu64") is"
526caba1a6fSryo 			    " very low relative to TPROF_HZ (%u)\n", __func__,
527caba1a6fSryo 			    c, freq, TPROF_HZ);
528caba1a6fSryo 			sc->sc_count[c].ctr_counter_val =
529caba1a6fSryo 			    4000000000ULL / TPROF_HZ;
530caba1a6fSryo 		}
531caba1a6fSryo 
532caba1a6fSryo 		switch (param->p_flags & TPROF_PARAM_VALUE2_MASK) {
533caba1a6fSryo 		case TPROF_PARAM_VALUE2_SCALE:
534caba1a6fSryo 			if (sc_param->p_value2 == 0)
535caba1a6fSryo 				break;
536caba1a6fSryo 			/*
537caba1a6fSryo 			 * p_value2 is 64-bit fixed-point
538caba1a6fSryo 			 * upper 32 bits are the integer part
539caba1a6fSryo 			 * lower 32 bits are the decimal part
540caba1a6fSryo 			 */
541caba1a6fSryo 			inum = sc_param->p_value2 >> 32;
542caba1a6fSryo 			dnum = sc_param->p_value2 & __BITS(31, 0);
543caba1a6fSryo 			sc->sc_count[c].ctr_counter_val =
544caba1a6fSryo 			    sc->sc_count[c].ctr_counter_val * inum +
545caba1a6fSryo 			    (sc->sc_count[c].ctr_counter_val * dnum >> 32);
546caba1a6fSryo 			if (sc->sc_count[c].ctr_counter_val == 0)
547caba1a6fSryo 				sc->sc_count[c].ctr_counter_val = 1;
548caba1a6fSryo 			break;
549caba1a6fSryo 		case TPROF_PARAM_VALUE2_TRIGGERCOUNT:
550caba1a6fSryo 			if (sc_param->p_value2 == 0)
551caba1a6fSryo 				sc_param->p_value2 = 1;
552caba1a6fSryo 			if (sc_param->p_value2 >
553caba1a6fSryo 			    __BITS(sc->sc_count[c].ctr_bitwidth - 1, 0)) {
554caba1a6fSryo 				sc_param->p_value2 =
555caba1a6fSryo 				    __BITS(sc->sc_count[c].ctr_bitwidth - 1, 0);
556caba1a6fSryo 			}
557caba1a6fSryo 			sc->sc_count[c].ctr_counter_val = sc_param->p_value2;
558caba1a6fSryo 			break;
559caba1a6fSryo 		default:
560caba1a6fSryo 			break;
561caba1a6fSryo 		}
562caba1a6fSryo 		sc->sc_count[c].ctr_counter_reset_val =
563caba1a6fSryo 		    -sc->sc_count[c].ctr_counter_val;
564caba1a6fSryo 		sc->sc_count[c].ctr_counter_reset_val &=
565caba1a6fSryo 		    __BITS(sc->sc_count[c].ctr_bitwidth - 1, 0);
566caba1a6fSryo 	} else {
567caba1a6fSryo 		sc->sc_count[c].ctr_counter_val = 0;
568caba1a6fSryo 		sc->sc_count[c].ctr_counter_reset_val = 0;
569caba1a6fSryo 	}
570caba1a6fSryo 
571caba1a6fSryo 	/* At this point, p_value is used as an initial value */
572caba1a6fSryo 	percpu_foreach(tb->tb_softc.sc_ctr_offset_percpu,
573caba1a6fSryo 	    tprof_init_percpu_counters_offset, (void *)(uintptr_t)c);
574caba1a6fSryo 	/* On the backend side, p_value is used as the reset value */
575caba1a6fSryo 	sc_param->p_value = tb->tb_softc.sc_count[c].ctr_counter_reset_val;
576caba1a6fSryo 
577caba1a6fSryo 	xc = xc_broadcast(0, tprof_configure_event_cpu,
578caba1a6fSryo 	    tb, (void *)(uintptr_t)c);
579caba1a6fSryo 	xc_wait(xc);
580caba1a6fSryo 
581caba1a6fSryo 	mutex_enter(&tprof_lock);
582caba1a6fSryo 	/* update counters bitmasks */
583caba1a6fSryo 	SET(tb->tb_softc.sc_ctr_configured_mask, __BIT(c));
584caba1a6fSryo 	CLR(tb->tb_softc.sc_ctr_prof_mask, __BIT(c));
585caba1a6fSryo 	CLR(tb->tb_softc.sc_ctr_ovf_mask, __BIT(c));
586caba1a6fSryo 	/* profiled counter requires overflow handling */
587caba1a6fSryo 	if (ISSET(param->p_flags, TPROF_PARAM_PROFILE)) {
588caba1a6fSryo 		SET(tb->tb_softc.sc_ctr_prof_mask, __BIT(c));
589caba1a6fSryo 		SET(tb->tb_softc.sc_ctr_ovf_mask, __BIT(c));
590caba1a6fSryo 	}
591caba1a6fSryo 	/* counters with less than 64bits also require overflow handling */
592caba1a6fSryo 	if (sc->sc_count[c].ctr_bitwidth != 64)
593caba1a6fSryo 		SET(tb->tb_softc.sc_ctr_ovf_mask, __BIT(c));
594caba1a6fSryo 	mutex_exit(&tprof_lock);
595caba1a6fSryo 
596caba1a6fSryo 	error = 0;
597caba1a6fSryo 
598caba1a6fSryo  done:
599caba1a6fSryo 	return error;
600caba1a6fSryo }
601caba1a6fSryo 
602caba1a6fSryo static void
tprof_getcounts_cpu(void * arg1,void * arg2)603caba1a6fSryo tprof_getcounts_cpu(void *arg1, void *arg2)
604caba1a6fSryo {
605caba1a6fSryo 	tprof_backend_t *tb = arg1;
606caba1a6fSryo 	tprof_backend_softc_t *sc = &tb->tb_softc;
607caba1a6fSryo 	uint64_t *counters = arg2;
608caba1a6fSryo 	uint64_t *counters_offset;
609caba1a6fSryo 	unsigned int c;
610caba1a6fSryo 
611caba1a6fSryo 	tprof_countermask_t configmask = sc->sc_ctr_configured_mask;
612caba1a6fSryo 	counters_offset = percpu_getref(sc->sc_ctr_offset_percpu);
613caba1a6fSryo 	for (c = 0; c < sc->sc_ncounters; c++) {
614caba1a6fSryo 		if (ISSET(configmask, __BIT(c))) {
615caba1a6fSryo 			uint64_t ctr = tb->tb_ops->tbo_counter_read(c);
616caba1a6fSryo 			counters[c] = counters_offset[c] +
617caba1a6fSryo 			    ((ctr - sc->sc_count[c].ctr_counter_reset_val) &
618caba1a6fSryo 			    __BITS(sc->sc_count[c].ctr_bitwidth - 1, 0));
619*183ce831Smsaitoh 		} else
620caba1a6fSryo 			counters[c] = 0;
621caba1a6fSryo 	}
622caba1a6fSryo 	percpu_putref(sc->sc_ctr_offset_percpu);
623caba1a6fSryo }
624caba1a6fSryo 
625caba1a6fSryo static int
tprof_getcounts(tprof_counts_t * counts)626caba1a6fSryo tprof_getcounts(tprof_counts_t *counts)
627caba1a6fSryo {
628caba1a6fSryo 	struct cpu_info *ci;
629caba1a6fSryo 	tprof_backend_t *tb;
630caba1a6fSryo 	uint64_t xc;
631caba1a6fSryo 
632caba1a6fSryo 	tb = tprof_backend;
633caba1a6fSryo 	if (tb == NULL)
634caba1a6fSryo 		return ENOENT;
635caba1a6fSryo 
636caba1a6fSryo 	if (counts->c_cpu >= ncpu)
637caba1a6fSryo 		return ESRCH;
638caba1a6fSryo 	ci = cpu_lookup(counts->c_cpu);
639caba1a6fSryo 	if (ci == NULL)
640caba1a6fSryo 		return ESRCH;
641caba1a6fSryo 
642caba1a6fSryo 	xc = xc_unicast(0, tprof_getcounts_cpu, tb, counts->c_count, ci);
643caba1a6fSryo 	xc_wait(xc);
644caba1a6fSryo 
645caba1a6fSryo 	counts->c_ncounters = tb->tb_softc.sc_ncounters;
646caba1a6fSryo 	counts->c_runningmask = tb->tb_softc.sc_ctr_running_mask;
647caba1a6fSryo 	return 0;
648caba1a6fSryo }
649caba1a6fSryo 
650e1b625b4Syamt /*
651e1b625b4Syamt  * tprof_clear: drain unread samples.
652e1b625b4Syamt  */
653e1b625b4Syamt 
65448a1e4c2Syamt static void
tprof_clear(void)65548a1e4c2Syamt tprof_clear(void)
65648a1e4c2Syamt {
65748a1e4c2Syamt 	tprof_buf_t *buf;
65848a1e4c2Syamt 
65948a1e4c2Syamt 	mutex_enter(&tprof_reader_lock);
66048a1e4c2Syamt 	mutex_enter(&tprof_lock);
66148a1e4c2Syamt 	while ((buf = STAILQ_FIRST(&tprof_list)) != NULL) {
66248a1e4c2Syamt 		if (buf != NULL) {
66348a1e4c2Syamt 			STAILQ_REMOVE_HEAD(&tprof_list, b_list);
66448a1e4c2Syamt 			KASSERT(tprof_nbuf_on_list > 0);
66548a1e4c2Syamt 			tprof_nbuf_on_list--;
66648a1e4c2Syamt 			mutex_exit(&tprof_lock);
66748a1e4c2Syamt 			tprof_buf_free(buf);
66848a1e4c2Syamt 			mutex_enter(&tprof_lock);
66948a1e4c2Syamt 		}
67048a1e4c2Syamt 	}
67148a1e4c2Syamt 	KASSERT(tprof_nbuf_on_list == 0);
67248a1e4c2Syamt 	mutex_exit(&tprof_lock);
67348a1e4c2Syamt 	tprof_reader_offset = 0;
67448a1e4c2Syamt 	mutex_exit(&tprof_reader_lock);
67548a1e4c2Syamt 
67648a1e4c2Syamt 	memset(&tprof_stat, 0, sizeof(tprof_stat));
67748a1e4c2Syamt }
67848a1e4c2Syamt 
679e1b625b4Syamt static tprof_backend_t *
tprof_backend_lookup(const char * name)680e1b625b4Syamt tprof_backend_lookup(const char *name)
681e1b625b4Syamt {
682e1b625b4Syamt 	tprof_backend_t *tb;
683e1b625b4Syamt 
684e1b625b4Syamt 	KASSERT(mutex_owned(&tprof_startstop_lock));
685e1b625b4Syamt 
686e1b625b4Syamt 	LIST_FOREACH(tb, &tprof_backends, tb_list) {
687e1b625b4Syamt 		if (!strcmp(tb->tb_name, name)) {
688e1b625b4Syamt 			return tb;
689e1b625b4Syamt 		}
690e1b625b4Syamt 	}
691e1b625b4Syamt 	return NULL;
692e1b625b4Syamt }
693e1b625b4Syamt 
69448a1e4c2Syamt /* -------------------- backend interfaces */
69548a1e4c2Syamt 
69648a1e4c2Syamt /*
69748a1e4c2Syamt  * tprof_sample: record a sample on the per-cpu buffer.
69848a1e4c2Syamt  *
69948a1e4c2Syamt  * be careful; can be called in NMI context.
70083fb606cSyamt  * we are bluntly assuming the followings are safe.
70183fb606cSyamt  *	curcpu()
70283fb606cSyamt  *	curlwp->l_lid
70383fb606cSyamt  *	curlwp->l_proc->p_pid
70448a1e4c2Syamt  */
70548a1e4c2Syamt 
70648a1e4c2Syamt void
tprof_sample(void * unused,const tprof_frame_info_t * tfi)707a087cb3cSmaxv tprof_sample(void *unused, const tprof_frame_info_t *tfi)
70848a1e4c2Syamt {
7097c9aa013Sryo 	tprof_cpu_t * const c = tprof_cpu_direct(curcpu());
71048a1e4c2Syamt 	tprof_buf_t * const buf = c->c_buf;
71189e330a7Syamt 	tprof_sample_t *sp;
7120bbefb72Syamt 	const uintptr_t pc = tfi->tfi_pc;
71383fb606cSyamt 	const lwp_t * const l = curlwp;
71448a1e4c2Syamt 	u_int idx;
71548a1e4c2Syamt 
71648a1e4c2Syamt 	idx = buf->b_used;
71748a1e4c2Syamt 	if (__predict_false(idx >= buf->b_size)) {
71848a1e4c2Syamt 		buf->b_overflow++;
71948a1e4c2Syamt 		return;
72048a1e4c2Syamt 	}
72189e330a7Syamt 	sp = &buf->b_data[idx];
72283fb606cSyamt 	sp->s_pid = l->l_proc->p_pid;
72383fb606cSyamt 	sp->s_lwpid = l->l_lid;
72483fb606cSyamt 	sp->s_cpuid = c->c_cpuid;
725caba1a6fSryo 	sp->s_flags = ((tfi->tfi_inkernel) ? TPROF_SAMPLE_INKERNEL : 0) |
726caba1a6fSryo 	    __SHIFTIN(tfi->tfi_counter, TPROF_SAMPLE_COUNTER_MASK);
72789e330a7Syamt 	sp->s_pc = pc;
72848a1e4c2Syamt 	buf->b_used = idx + 1;
72948a1e4c2Syamt }
73048a1e4c2Syamt 
731e1b625b4Syamt /*
732e1b625b4Syamt  * tprof_backend_register:
733e1b625b4Syamt  */
734e1b625b4Syamt 
735e1b625b4Syamt int
tprof_backend_register(const char * name,const tprof_backend_ops_t * ops,int vers)736e1b625b4Syamt tprof_backend_register(const char *name, const tprof_backend_ops_t *ops,
737e1b625b4Syamt     int vers)
738e1b625b4Syamt {
739e1b625b4Syamt 	tprof_backend_t *tb;
740e1b625b4Syamt 
741*183ce831Smsaitoh 	if (vers != TPROF_BACKEND_VERSION)
742e1b625b4Syamt 		return EINVAL;
743e1b625b4Syamt 
744e1b625b4Syamt 	mutex_enter(&tprof_startstop_lock);
745e1b625b4Syamt 	tb = tprof_backend_lookup(name);
746e1b625b4Syamt 	if (tb != NULL) {
747e1b625b4Syamt 		mutex_exit(&tprof_startstop_lock);
748e1b625b4Syamt 		return EEXIST;
749e1b625b4Syamt 	}
750e1b625b4Syamt #if 1 /* XXX for now */
751e1b625b4Syamt 	if (!LIST_EMPTY(&tprof_backends)) {
752e1b625b4Syamt 		mutex_exit(&tprof_startstop_lock);
753e1b625b4Syamt 		return ENOTSUP;
754e1b625b4Syamt 	}
755e1b625b4Syamt #endif
756caba1a6fSryo 	tb = kmem_zalloc(sizeof(*tb), KM_SLEEP);
757e1b625b4Syamt 	tb->tb_name = name;
758e1b625b4Syamt 	tb->tb_ops = ops;
759e1b625b4Syamt 	LIST_INSERT_HEAD(&tprof_backends, tb, tb_list);
760e1b625b4Syamt #if 1 /* XXX for now */
761e1b625b4Syamt 	if (tprof_backend == NULL) {
762e1b625b4Syamt 		tprof_backend = tb;
763e1b625b4Syamt 	}
764e1b625b4Syamt #endif
765e1b625b4Syamt 	mutex_exit(&tprof_startstop_lock);
766e1b625b4Syamt 
767*183ce831Smsaitoh 	/* Init backend softc */
768caba1a6fSryo 	tb->tb_softc.sc_ncounters = tb->tb_ops->tbo_ncounters();
769caba1a6fSryo 	tb->tb_softc.sc_ctr_offset_percpu_size =
770caba1a6fSryo 	    sizeof(uint64_t) * tb->tb_softc.sc_ncounters;
771caba1a6fSryo 	tb->tb_softc.sc_ctr_offset_percpu =
772caba1a6fSryo 	    percpu_alloc(tb->tb_softc.sc_ctr_offset_percpu_size);
773caba1a6fSryo 
774e1b625b4Syamt 	return 0;
775e1b625b4Syamt }
776e1b625b4Syamt 
777e1b625b4Syamt /*
778e1b625b4Syamt  * tprof_backend_unregister:
779e1b625b4Syamt  */
780e1b625b4Syamt 
781e1b625b4Syamt int
tprof_backend_unregister(const char * name)782e1b625b4Syamt tprof_backend_unregister(const char *name)
783e1b625b4Syamt {
784e1b625b4Syamt 	tprof_backend_t *tb;
785e1b625b4Syamt 
786e1b625b4Syamt 	mutex_enter(&tprof_startstop_lock);
787e1b625b4Syamt 	tb = tprof_backend_lookup(name);
788e1b625b4Syamt #if defined(DIAGNOSTIC)
789e1b625b4Syamt 	if (tb == NULL) {
790e1b625b4Syamt 		mutex_exit(&tprof_startstop_lock);
791e1b625b4Syamt 		panic("%s: not found '%s'", __func__, name);
792e1b625b4Syamt 	}
793e1b625b4Syamt #endif /* defined(DIAGNOSTIC) */
794caba1a6fSryo 	if (tb->tb_softc.sc_ctr_running_mask != 0) {
795e1b625b4Syamt 		mutex_exit(&tprof_startstop_lock);
796e1b625b4Syamt 		return EBUSY;
797e1b625b4Syamt 	}
798e1b625b4Syamt #if 1 /* XXX for now */
799*183ce831Smsaitoh 	if (tprof_backend == tb)
800e1b625b4Syamt 		tprof_backend = NULL;
801e1b625b4Syamt #endif
802e1b625b4Syamt 	LIST_REMOVE(tb, tb_list);
803e1b625b4Syamt 	mutex_exit(&tprof_startstop_lock);
804e1b625b4Syamt 
805caba1a6fSryo 	/* fini backend softc */
806caba1a6fSryo 	percpu_free(tb->tb_softc.sc_ctr_offset_percpu,
807caba1a6fSryo 	    tb->tb_softc.sc_ctr_offset_percpu_size);
808caba1a6fSryo 
809*183ce831Smsaitoh 	/* Free backend */
810e1b625b4Syamt 	kmem_free(tb, sizeof(*tb));
811e1b625b4Syamt 
812e1b625b4Syamt 	return 0;
813e1b625b4Syamt }
814e1b625b4Syamt 
81548a1e4c2Syamt /* -------------------- cdevsw interfaces */
81648a1e4c2Syamt 
81748a1e4c2Syamt static int
tprof_open(dev_t dev,int flags,int type,struct lwp * l)81848a1e4c2Syamt tprof_open(dev_t dev, int flags, int type, struct lwp *l)
81948a1e4c2Syamt {
82048a1e4c2Syamt 
821*183ce831Smsaitoh 	if (minor(dev) != 0)
82248a1e4c2Syamt 		return EXDEV;
823*183ce831Smsaitoh 
82448a1e4c2Syamt 	mutex_enter(&tprof_lock);
82548a1e4c2Syamt 	if (tprof_owner != NULL) {
82648a1e4c2Syamt 		mutex_exit(&tprof_lock);
82748a1e4c2Syamt 		return  EBUSY;
82848a1e4c2Syamt 	}
82948a1e4c2Syamt 	tprof_owner = curlwp;
83048a1e4c2Syamt 	mutex_exit(&tprof_lock);
83148a1e4c2Syamt 
83248a1e4c2Syamt 	return 0;
83348a1e4c2Syamt }
83448a1e4c2Syamt 
83548a1e4c2Syamt static int
tprof_close(dev_t dev,int flags,int type,struct lwp * l)83648a1e4c2Syamt tprof_close(dev_t dev, int flags, int type, struct lwp *l)
83748a1e4c2Syamt {
83848a1e4c2Syamt 
83948a1e4c2Syamt 	KASSERT(minor(dev) == 0);
84048a1e4c2Syamt 
84148a1e4c2Syamt 	mutex_enter(&tprof_startstop_lock);
84248a1e4c2Syamt 	mutex_enter(&tprof_lock);
84348a1e4c2Syamt 	tprof_owner = NULL;
84448a1e4c2Syamt 	mutex_exit(&tprof_lock);
845caba1a6fSryo 	tprof_stop(TPROF_COUNTERMASK_ALL);
84648a1e4c2Syamt 	tprof_clear();
847caba1a6fSryo 
848caba1a6fSryo 	tprof_backend_t *tb = tprof_backend;
849caba1a6fSryo 	if (tb != NULL) {
850caba1a6fSryo 		KASSERT(tb->tb_softc.sc_ctr_running_mask == 0);
851caba1a6fSryo 		tb->tb_softc.sc_ctr_configured_mask = 0;
852caba1a6fSryo 		tb->tb_softc.sc_ctr_prof_mask = 0;
853caba1a6fSryo 		tb->tb_softc.sc_ctr_ovf_mask = 0;
854caba1a6fSryo 	}
855caba1a6fSryo 
85648a1e4c2Syamt 	mutex_exit(&tprof_startstop_lock);
85748a1e4c2Syamt 
85848a1e4c2Syamt 	return 0;
85948a1e4c2Syamt }
86048a1e4c2Syamt 
86148a1e4c2Syamt static int
tprof_poll(dev_t dev,int events,struct lwp * l)8622b54f35cSryo tprof_poll(dev_t dev, int events, struct lwp *l)
8632b54f35cSryo {
8642b54f35cSryo 	int revents;
8652b54f35cSryo 
8662b54f35cSryo 	revents = events & (POLLIN | POLLRDNORM);
8672b54f35cSryo 	if (revents == 0)
8682b54f35cSryo 		return 0;
8692b54f35cSryo 
8702b54f35cSryo 	mutex_enter(&tprof_lock);
8712b54f35cSryo 	if (STAILQ_EMPTY(&tprof_list)) {
8722b54f35cSryo 		revents = 0;
8732b54f35cSryo 		selrecord(l, &tprof_selp);
8742b54f35cSryo 	}
8752b54f35cSryo 	mutex_exit(&tprof_lock);
8762b54f35cSryo 
8772b54f35cSryo 	return revents;
8782b54f35cSryo }
8792b54f35cSryo 
8802b54f35cSryo static void
filt_tprof_read_detach(struct knote * kn)8812b54f35cSryo filt_tprof_read_detach(struct knote *kn)
8822b54f35cSryo {
883be4c756bSryo 	mutex_enter(&tprof_lock);
8842b54f35cSryo 	selremove_knote(&tprof_selp, kn);
885be4c756bSryo 	mutex_exit(&tprof_lock);
8862b54f35cSryo }
8872b54f35cSryo 
8882b54f35cSryo static int
filt_tprof_read_event(struct knote * kn,long hint)8892b54f35cSryo filt_tprof_read_event(struct knote *kn, long hint)
8902b54f35cSryo {
8912b54f35cSryo 	int rv = 0;
8922b54f35cSryo 
8932b54f35cSryo 	if ((hint & NOTE_SUBMIT) == 0)
894be4c756bSryo 		mutex_enter(&tprof_lock);
8952b54f35cSryo 
8962b54f35cSryo 	if (!STAILQ_EMPTY(&tprof_list)) {
8972b54f35cSryo 		tprof_buf_t *buf;
8982b54f35cSryo 		int64_t n = 0;
8992b54f35cSryo 
9002b54f35cSryo 		STAILQ_FOREACH(buf, &tprof_list, b_list) {
9012b54f35cSryo 			n += buf->b_used;
9022b54f35cSryo 		}
9032b54f35cSryo 		kn->kn_data = n * sizeof(tprof_sample_t);
9042b54f35cSryo 
9052b54f35cSryo 		rv = 1;
9062b54f35cSryo 	}
9072b54f35cSryo 
9082b54f35cSryo 	if ((hint & NOTE_SUBMIT) == 0)
909be4c756bSryo 		mutex_exit(&tprof_lock);
9102b54f35cSryo 
9112b54f35cSryo 	return rv;
9122b54f35cSryo }
9132b54f35cSryo 
9142b54f35cSryo static const struct filterops tprof_read_filtops = {
9152b54f35cSryo 	.f_flags = FILTEROP_ISFD | FILTEROP_MPSAFE,
9162b54f35cSryo 	.f_attach = NULL,
9172b54f35cSryo 	.f_detach = filt_tprof_read_detach,
9182b54f35cSryo 	.f_event = filt_tprof_read_event,
9192b54f35cSryo };
9202b54f35cSryo 
9212b54f35cSryo static int
tprof_kqfilter(dev_t dev,struct knote * kn)9222b54f35cSryo tprof_kqfilter(dev_t dev, struct knote *kn)
9232b54f35cSryo {
9242b54f35cSryo 	switch (kn->kn_filter) {
9252b54f35cSryo 	case EVFILT_READ:
9262b54f35cSryo 		kn->kn_fop = &tprof_read_filtops;
927be4c756bSryo 		mutex_enter(&tprof_lock);
9282b54f35cSryo 		selrecord_knote(&tprof_selp, kn);
929be4c756bSryo 		mutex_exit(&tprof_lock);
9302b54f35cSryo 		break;
9312b54f35cSryo 	default:
9322b54f35cSryo 		return EINVAL;
9332b54f35cSryo 	}
9342b54f35cSryo 
9352b54f35cSryo 	return 0;
9362b54f35cSryo }
9372b54f35cSryo 
9382b54f35cSryo static int
tprof_read(dev_t dev,struct uio * uio,int flags)93948a1e4c2Syamt tprof_read(dev_t dev, struct uio *uio, int flags)
94048a1e4c2Syamt {
94148a1e4c2Syamt 	tprof_buf_t *buf;
94248a1e4c2Syamt 	size_t bytes;
94348a1e4c2Syamt 	size_t resid;
9442b54f35cSryo 	size_t done = 0;
94548a1e4c2Syamt 	int error = 0;
94648a1e4c2Syamt 
94748a1e4c2Syamt 	KASSERT(minor(dev) == 0);
94848a1e4c2Syamt 	mutex_enter(&tprof_reader_lock);
94948a1e4c2Syamt 	while (uio->uio_resid > 0 && error == 0) {
95048a1e4c2Syamt 		/*
951*183ce831Smsaitoh 		 * Take the first buffer from the list.
95248a1e4c2Syamt 		 */
95348a1e4c2Syamt 		mutex_enter(&tprof_lock);
95448a1e4c2Syamt 		buf = STAILQ_FIRST(&tprof_list);
95548a1e4c2Syamt 		if (buf == NULL) {
9562b54f35cSryo 			if (tprof_nworker == 0 || done != 0) {
95748a1e4c2Syamt 				mutex_exit(&tprof_lock);
95848a1e4c2Syamt 				error = 0;
95948a1e4c2Syamt 				break;
96048a1e4c2Syamt 			}
96148a1e4c2Syamt 			mutex_exit(&tprof_reader_lock);
96248a1e4c2Syamt 			error = cv_wait_sig(&tprof_reader_cv, &tprof_lock);
96348a1e4c2Syamt 			mutex_exit(&tprof_lock);
96448a1e4c2Syamt 			mutex_enter(&tprof_reader_lock);
96548a1e4c2Syamt 			continue;
96648a1e4c2Syamt 		}
96748a1e4c2Syamt 		STAILQ_REMOVE_HEAD(&tprof_list, b_list);
96848a1e4c2Syamt 		KASSERT(tprof_nbuf_on_list > 0);
96948a1e4c2Syamt 		tprof_nbuf_on_list--;
97048a1e4c2Syamt 		mutex_exit(&tprof_lock);
97148a1e4c2Syamt 
97248a1e4c2Syamt 		/*
973*183ce831Smsaitoh 		 * Copy it out.
97448a1e4c2Syamt 		 */
97548a1e4c2Syamt 		bytes = MIN(buf->b_used * sizeof(tprof_sample_t) -
97648a1e4c2Syamt 		    tprof_reader_offset, uio->uio_resid);
97748a1e4c2Syamt 		resid = uio->uio_resid;
97848a1e4c2Syamt 		error = uiomove((char *)buf->b_data + tprof_reader_offset,
97948a1e4c2Syamt 		    bytes, uio);
98048a1e4c2Syamt 		done = resid - uio->uio_resid;
98148a1e4c2Syamt 		tprof_reader_offset += done;
98248a1e4c2Syamt 
98348a1e4c2Syamt 		/*
984*183ce831Smsaitoh 		 * If we didn't consume the whole buffer,
98548a1e4c2Syamt 		 * put it back to the list.
98648a1e4c2Syamt 		 */
98748a1e4c2Syamt 		if (tprof_reader_offset <
98848a1e4c2Syamt 		    buf->b_used * sizeof(tprof_sample_t)) {
98948a1e4c2Syamt 			mutex_enter(&tprof_lock);
99048a1e4c2Syamt 			STAILQ_INSERT_HEAD(&tprof_list, buf, b_list);
99148a1e4c2Syamt 			tprof_nbuf_on_list++;
99248a1e4c2Syamt 			cv_broadcast(&tprof_reader_cv);
99348a1e4c2Syamt 			mutex_exit(&tprof_lock);
99448a1e4c2Syamt 		} else {
99548a1e4c2Syamt 			tprof_buf_free(buf);
99648a1e4c2Syamt 			tprof_reader_offset = 0;
99748a1e4c2Syamt 		}
99848a1e4c2Syamt 	}
99948a1e4c2Syamt 	mutex_exit(&tprof_reader_lock);
100048a1e4c2Syamt 
100148a1e4c2Syamt 	return error;
100248a1e4c2Syamt }
100348a1e4c2Syamt 
100448a1e4c2Syamt static int
tprof_ioctl(dev_t dev,u_long cmd,void * data,int flags,struct lwp * l)100548a1e4c2Syamt tprof_ioctl(dev_t dev, u_long cmd, void *data, int flags, struct lwp *l)
100648a1e4c2Syamt {
1007a087cb3cSmaxv 	const tprof_param_t *param;
1008caba1a6fSryo 	tprof_counts_t *counts;
100948a1e4c2Syamt 	int error = 0;
101048a1e4c2Syamt 
101148a1e4c2Syamt 	KASSERT(minor(dev) == 0);
101248a1e4c2Syamt 
101348a1e4c2Syamt 	switch (cmd) {
1014a087cb3cSmaxv 	case TPROF_IOC_GETINFO:
1015a087cb3cSmaxv 		mutex_enter(&tprof_startstop_lock);
1016a087cb3cSmaxv 		tprof_getinfo(data);
1017a087cb3cSmaxv 		mutex_exit(&tprof_startstop_lock);
101848a1e4c2Syamt 		break;
1019caba1a6fSryo 	case TPROF_IOC_GETNCOUNTERS:
1020caba1a6fSryo 		mutex_enter(&tprof_lock);
1021caba1a6fSryo 		error = tprof_getncounters((u_int *)data);
1022caba1a6fSryo 		mutex_exit(&tprof_lock);
1023caba1a6fSryo 		break;
102448a1e4c2Syamt 	case TPROF_IOC_START:
102548a1e4c2Syamt 		mutex_enter(&tprof_startstop_lock);
1026caba1a6fSryo 		error = tprof_start(*(tprof_countermask_t *)data);
102748a1e4c2Syamt 		mutex_exit(&tprof_startstop_lock);
102848a1e4c2Syamt 		break;
102948a1e4c2Syamt 	case TPROF_IOC_STOP:
103048a1e4c2Syamt 		mutex_enter(&tprof_startstop_lock);
1031caba1a6fSryo 		tprof_stop(*(tprof_countermask_t *)data);
103248a1e4c2Syamt 		mutex_exit(&tprof_startstop_lock);
103348a1e4c2Syamt 		break;
103448a1e4c2Syamt 	case TPROF_IOC_GETSTAT:
103548a1e4c2Syamt 		mutex_enter(&tprof_lock);
103648a1e4c2Syamt 		memcpy(data, &tprof_stat, sizeof(tprof_stat));
103748a1e4c2Syamt 		mutex_exit(&tprof_lock);
103848a1e4c2Syamt 		break;
1039caba1a6fSryo 	case TPROF_IOC_CONFIGURE_EVENT:
1040caba1a6fSryo 		param = data;
1041caba1a6fSryo 		mutex_enter(&tprof_startstop_lock);
1042caba1a6fSryo 		error = tprof_configure_event(param);
1043caba1a6fSryo 		mutex_exit(&tprof_startstop_lock);
1044caba1a6fSryo 		break;
1045caba1a6fSryo 	case TPROF_IOC_GETCOUNTS:
1046caba1a6fSryo 		counts = data;
1047caba1a6fSryo 		mutex_enter(&tprof_startstop_lock);
1048caba1a6fSryo 		error = tprof_getcounts(counts);
1049caba1a6fSryo 		mutex_exit(&tprof_startstop_lock);
1050caba1a6fSryo 		break;
105148a1e4c2Syamt 	default:
105248a1e4c2Syamt 		error = EINVAL;
105348a1e4c2Syamt 		break;
105448a1e4c2Syamt 	}
105548a1e4c2Syamt 
105648a1e4c2Syamt 	return error;
105748a1e4c2Syamt }
105848a1e4c2Syamt 
105948a1e4c2Syamt const struct cdevsw tprof_cdevsw = {
106048a1e4c2Syamt 	.d_open = tprof_open,
106148a1e4c2Syamt 	.d_close = tprof_close,
106248a1e4c2Syamt 	.d_read = tprof_read,
106348a1e4c2Syamt 	.d_write = nowrite,
106448a1e4c2Syamt 	.d_ioctl = tprof_ioctl,
106548a1e4c2Syamt 	.d_stop = nostop,
106648a1e4c2Syamt 	.d_tty = notty,
10672b54f35cSryo 	.d_poll = tprof_poll,
106848a1e4c2Syamt 	.d_mmap = nommap,
10692b54f35cSryo 	.d_kqfilter = tprof_kqfilter,
1070f9228f42Sdholland 	.d_discard = nodiscard,
1071a68f9396Sdholland 	.d_flag = D_OTHER | D_MPSAFE
107248a1e4c2Syamt };
107348a1e4c2Syamt 
107448a1e4c2Syamt void
tprofattach(int nunits)107548a1e4c2Syamt tprofattach(int nunits)
107648a1e4c2Syamt {
107748a1e4c2Syamt 
1078*183ce831Smsaitoh 	/* Nothing */
1079e1b625b4Syamt }
1080e1b625b4Syamt 
1081e1b625b4Syamt MODULE(MODULE_CLASS_DRIVER, tprof, NULL);
1082e1b625b4Syamt 
1083e1b625b4Syamt static void
tprof_cpu_init(void * vcp,void * vcookie,struct cpu_info * ci)1084003f8943Sriastradh tprof_cpu_init(void *vcp, void *vcookie, struct cpu_info *ci)
1085003f8943Sriastradh {
1086003f8943Sriastradh 	tprof_cpu_t **cp = vcp, *c;
1087003f8943Sriastradh 
1088003f8943Sriastradh 	c = kmem_zalloc(sizeof(*c), KM_SLEEP);
1089003f8943Sriastradh 	c->c_buf = NULL;
1090003f8943Sriastradh 	c->c_cpuid = cpu_index(ci);
1091003f8943Sriastradh 	*cp = c;
1092003f8943Sriastradh }
1093003f8943Sriastradh 
1094003f8943Sriastradh static void
tprof_cpu_fini(void * vcp,void * vcookie,struct cpu_info * ci)1095003f8943Sriastradh tprof_cpu_fini(void *vcp, void *vcookie, struct cpu_info *ci)
1096003f8943Sriastradh {
1097003f8943Sriastradh 	tprof_cpu_t **cp = vcp, *c;
1098003f8943Sriastradh 
1099003f8943Sriastradh 	c = *cp;
1100003f8943Sriastradh 	KASSERT(c->c_cpuid == cpu_index(ci));
1101003f8943Sriastradh 	KASSERT(c->c_buf == NULL);
1102003f8943Sriastradh 	kmem_free(c, sizeof(*c));
1103003f8943Sriastradh 	*cp = NULL;
1104003f8943Sriastradh }
1105003f8943Sriastradh 
1106003f8943Sriastradh static void
tprof_driver_init(void)1107e1b625b4Syamt tprof_driver_init(void)
1108e1b625b4Syamt {
1109e1b625b4Syamt 
111048a1e4c2Syamt 	mutex_init(&tprof_lock, MUTEX_DEFAULT, IPL_NONE);
111148a1e4c2Syamt 	mutex_init(&tprof_reader_lock, MUTEX_DEFAULT, IPL_NONE);
111248a1e4c2Syamt 	mutex_init(&tprof_startstop_lock, MUTEX_DEFAULT, IPL_NONE);
11132b54f35cSryo 	selinit(&tprof_selp);
111448a1e4c2Syamt 	cv_init(&tprof_cv, "tprof");
1115aa30923aSpgoyette 	cv_init(&tprof_reader_cv, "tprof_rd");
111648a1e4c2Syamt 	STAILQ_INIT(&tprof_list);
1117003f8943Sriastradh 	tprof_cpus = percpu_create(sizeof(tprof_cpu_t *),
1118003f8943Sriastradh 	    tprof_cpu_init, tprof_cpu_fini, NULL);
111948a1e4c2Syamt }
1120e1b625b4Syamt 
1121e1b625b4Syamt static void
tprof_driver_fini(void)1122e1b625b4Syamt tprof_driver_fini(void)
1123e1b625b4Syamt {
1124e1b625b4Syamt 
1125003f8943Sriastradh 	percpu_free(tprof_cpus, sizeof(tprof_cpu_t *));
1126e1b625b4Syamt 	mutex_destroy(&tprof_lock);
1127e1b625b4Syamt 	mutex_destroy(&tprof_reader_lock);
1128e1b625b4Syamt 	mutex_destroy(&tprof_startstop_lock);
11292b54f35cSryo 	seldestroy(&tprof_selp);
1130e1b625b4Syamt 	cv_destroy(&tprof_cv);
1131e1b625b4Syamt 	cv_destroy(&tprof_reader_cv);
1132e1b625b4Syamt }
1133e1b625b4Syamt 
1134e1b625b4Syamt static int
tprof_modcmd(modcmd_t cmd,void * arg)1135e1b625b4Syamt tprof_modcmd(modcmd_t cmd, void *arg)
1136e1b625b4Syamt {
1137e1b625b4Syamt 
1138e1b625b4Syamt 	switch (cmd) {
1139e1b625b4Syamt 	case MODULE_CMD_INIT:
1140e1b625b4Syamt 		tprof_driver_init();
1141e1b625b4Syamt #if defined(_MODULE)
1142e1b625b4Syamt 		{
1143e1b625b4Syamt 			devmajor_t bmajor = NODEVMAJOR;
1144e1b625b4Syamt 			devmajor_t cmajor = NODEVMAJOR;
1145e1b625b4Syamt 			int error;
1146e1b625b4Syamt 
1147e1b625b4Syamt 			error = devsw_attach("tprof", NULL, &bmajor,
1148e1b625b4Syamt 			    &tprof_cdevsw, &cmajor);
1149e1b625b4Syamt 			if (error) {
1150e1b625b4Syamt 				tprof_driver_fini();
1151e1b625b4Syamt 				return error;
1152e1b625b4Syamt 			}
1153e1b625b4Syamt 		}
1154e1b625b4Syamt #endif /* defined(_MODULE) */
1155e1b625b4Syamt 		return 0;
1156e1b625b4Syamt 
1157e1b625b4Syamt 	case MODULE_CMD_FINI:
1158e1b625b4Syamt #if defined(_MODULE)
1159e7bed289Sriastradh 		devsw_detach(NULL, &tprof_cdevsw);
1160e1b625b4Syamt #endif /* defined(_MODULE) */
1161e1b625b4Syamt 		tprof_driver_fini();
1162e1b625b4Syamt 		return 0;
1163e1b625b4Syamt 
1164e1b625b4Syamt 	default:
1165e1b625b4Syamt 		return ENOTTY;
1166e1b625b4Syamt 	}
1167e1b625b4Syamt }
1168