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