1*18b19e82Smpi /* $OpenBSD: dt_dev.c,v 1.40 2024/11/05 08:11:54 mpi Exp $ */
291b2ecf6Smpi
391b2ecf6Smpi /*
491b2ecf6Smpi * Copyright (c) 2019 Martin Pieuchot <mpi@openbsd.org>
591b2ecf6Smpi *
691b2ecf6Smpi * Permission to use, copy, modify, and distribute this software for any
791b2ecf6Smpi * purpose with or without fee is hereby granted, provided that the above
891b2ecf6Smpi * copyright notice and this permission notice appear in all copies.
991b2ecf6Smpi *
1091b2ecf6Smpi * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1191b2ecf6Smpi * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1291b2ecf6Smpi * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1391b2ecf6Smpi * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1491b2ecf6Smpi * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1591b2ecf6Smpi * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1691b2ecf6Smpi * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1791b2ecf6Smpi */
1891b2ecf6Smpi
1991b2ecf6Smpi #include <sys/types.h>
2091b2ecf6Smpi #include <sys/systm.h>
2191b2ecf6Smpi #include <sys/param.h>
22c56c27b6Scheloha #include <sys/clockintr.h>
2391b2ecf6Smpi #include <sys/device.h>
24a28bb56fSclaudio #include <sys/exec_elf.h>
2591b2ecf6Smpi #include <sys/malloc.h>
2691b2ecf6Smpi #include <sys/proc.h>
27a28bb56fSclaudio #include <sys/ptrace.h>
2891b2ecf6Smpi
299d5d5c61Smpi #include <machine/intr.h>
309d5d5c61Smpi
3191b2ecf6Smpi #include <dev/dt/dtvar.h>
3291b2ecf6Smpi
3391b2ecf6Smpi /*
34ecfa01e0Smpi * Number of frames to skip in stack traces.
3591b2ecf6Smpi *
36ecfa01e0Smpi * The number of frames required to execute dt(4) profiling code
37ecfa01e0Smpi * depends on the probe, context, architecture and possibly the
38ecfa01e0Smpi * compiler.
39ecfa01e0Smpi *
40ecfa01e0Smpi * Static probes (tracepoints) are executed in the context of the
41ecfa01e0Smpi * current thread and only need to skip frames up to the recording
42ecfa01e0Smpi * function. For example the syscall provider:
4391b2ecf6Smpi *
4491b2ecf6Smpi * dt_prov_syscall_entry+0x141
4591b2ecf6Smpi * syscall+0x205 <--- start here
4691b2ecf6Smpi * Xsyscall+0x128
4791b2ecf6Smpi *
48ecfa01e0Smpi * Probes executed in their own context, like the profile provider,
49ecfa01e0Smpi * need to skip the frames of that context which are different for
50ecfa01e0Smpi * every architecture. For example the profile provider executed
51ecfa01e0Smpi * from hardclock(9) on amd64:
5291b2ecf6Smpi *
5391b2ecf6Smpi * dt_prov_profile_enter+0x6e
54ecfa01e0Smpi * hardclock+0x1a9
55ecfa01e0Smpi * lapic_clockintr+0x3f
56ecfa01e0Smpi * Xresume_lapic_ltimer+0x26
57ecfa01e0Smpi * acpicpu_idle+0x1d2 <---- start here.
58ecfa01e0Smpi * sched_idle+0x225
5991b2ecf6Smpi * proc_trampoline+0x1c
6091b2ecf6Smpi */
61ecfa01e0Smpi #if defined(__amd64__)
62c56c27b6Scheloha #define DT_FA_PROFILE 5
63f6782744Sclaudio #define DT_FA_STATIC 2
64f6782744Sclaudio #elif defined(__i386__)
65c56c27b6Scheloha #define DT_FA_PROFILE 5
66ecfa01e0Smpi #define DT_FA_STATIC 2
67c5d97e14Sclaudio #elif defined(__macppc__)
68c56c27b6Scheloha #define DT_FA_PROFILE 5
69c5d97e14Sclaudio #define DT_FA_STATIC 2
70d6e57b0dSvisa #elif defined(__octeon__)
71d6e57b0dSvisa #define DT_FA_PROFILE 6
72d6e57b0dSvisa #define DT_FA_STATIC 2
73f1ec5211Skettenis #elif defined(__powerpc64__)
74f1ec5211Skettenis #define DT_FA_PROFILE 6
75f1ec5211Skettenis #define DT_FA_STATIC 2
76ecfa01e0Smpi #elif defined(__sparc64__)
77f6782744Sclaudio #define DT_FA_PROFILE 7
78ecfa01e0Smpi #define DT_FA_STATIC 1
7991b2ecf6Smpi #else
80ecfa01e0Smpi #define DT_FA_STATIC 0
81ecfa01e0Smpi #define DT_FA_PROFILE 0
8291b2ecf6Smpi #endif
8391b2ecf6Smpi
8491b2ecf6Smpi #define DT_EVTRING_SIZE 16 /* # of slots in per PCB event ring */
8591b2ecf6Smpi
8691b2ecf6Smpi #define DPRINTF(x...) /* nothing */
8791b2ecf6Smpi
8891b2ecf6Smpi /*
89f5199088Smpi * Per-CPU Event States
90f5199088Smpi *
91f5199088Smpi * Locks used to protect struct members:
92cc4b6028Smpi * r owned by thread doing read(2)
93f5199088Smpi * c owned by CPU
94f5199088Smpi * s sliced ownership, based on read/write indexes
95cc4b6028Smpi * p written by CPU, read by thread doing read(2)
96f5199088Smpi */
97f5199088Smpi struct dt_cpubuf {
98f5199088Smpi unsigned int dc_prod; /* [r] read index */
99f5199088Smpi unsigned int dc_cons; /* [c] write index */
100f5199088Smpi struct dt_evt *dc_ring; /* [s] ring of event states */
101f5199088Smpi unsigned int dc_inevt; /* [c] in event already? */
102f5199088Smpi
103f5199088Smpi /* Counters */
104cc4b6028Smpi unsigned int dc_dropevt; /* [p] # of events dropped */
105f5199088Smpi unsigned int dc_readevt; /* [r] # of events read */
106f5199088Smpi };
107f5199088Smpi
108f5199088Smpi /*
10991b2ecf6Smpi * Descriptor associated with each program opening /dev/dt. It is used
11091b2ecf6Smpi * to keep track of enabled PCBs.
11191b2ecf6Smpi *
11291b2ecf6Smpi * Locks used to protect struct members in this file:
11311c54b09Smvs * a atomic
114b609c616Santon * K kernel lock
115cc4b6028Smpi * r owned by thread doing read(2)
116f5199088Smpi * I invariant after initialization
11791b2ecf6Smpi */
11891b2ecf6Smpi struct dt_softc {
119b609c616Santon SLIST_ENTRY(dt_softc) ds_next; /* [K] descriptor list */
12091b2ecf6Smpi int ds_unit; /* [I] D_CLONE unique unit */
12191b2ecf6Smpi pid_t ds_pid; /* [I] PID of tracing program */
1229d5d5c61Smpi void *ds_si; /* [I] to defer wakeup(9) */
12391b2ecf6Smpi
124b609c616Santon struct dt_pcb_list ds_pcbs; /* [K] list of enabled PCBs */
125b609c616Santon int ds_recording; /* [K] currently recording? */
126f5199088Smpi unsigned int ds_evtcnt; /* [a] # of readable evts */
12791b2ecf6Smpi
128f5199088Smpi struct dt_cpubuf ds_cpu[MAXCPUS]; /* [I] Per-cpu event states */
129f5199088Smpi unsigned int ds_lastcpu; /* [r] last CPU ring read(2). */
13091b2ecf6Smpi };
13191b2ecf6Smpi
132b609c616Santon SLIST_HEAD(, dt_softc) dtdev_list; /* [K] list of open /dev/dt nodes */
13391b2ecf6Smpi
13491b2ecf6Smpi /*
13591b2ecf6Smpi * Probes are created during dt_attach() and never modified/freed during
13691b2ecf6Smpi * the lifetime of the system. That's why we consider them as [I]mmutable.
13791b2ecf6Smpi */
13891b2ecf6Smpi unsigned int dt_nprobes; /* [I] # of probes available */
13991b2ecf6Smpi SIMPLEQ_HEAD(, dt_probe) dt_probe_list; /* [I] list of probes */
14091b2ecf6Smpi
14191b2ecf6Smpi struct rwlock dt_lock = RWLOCK_INITIALIZER("dtlk");
142b609c616Santon volatile uint32_t dt_tracing = 0; /* [K] # of processes tracing */
14391b2ecf6Smpi
14411c54b09Smvs int allowdt; /* [a] */
145a5bcf5abSbluhm
14691b2ecf6Smpi void dtattach(struct device *, struct device *, void *);
14791b2ecf6Smpi int dtopen(dev_t, int, int, struct proc *);
14891b2ecf6Smpi int dtclose(dev_t, int, int, struct proc *);
14991b2ecf6Smpi int dtread(dev_t, struct uio *, int);
15091b2ecf6Smpi int dtioctl(dev_t, u_long, caddr_t, int, struct proc *);
15191b2ecf6Smpi
15291b2ecf6Smpi struct dt_softc *dtlookup(int);
153f5199088Smpi struct dt_softc *dtalloc(void);
154f5199088Smpi void dtfree(struct dt_softc *);
15591b2ecf6Smpi
15691b2ecf6Smpi int dt_ioctl_list_probes(struct dt_softc *, struct dtioc_probe *);
157cd5e5223Sbluhm int dt_ioctl_get_args(struct dt_softc *, struct dtioc_arg *);
15891b2ecf6Smpi int dt_ioctl_get_stats(struct dt_softc *, struct dtioc_stat *);
15991b2ecf6Smpi int dt_ioctl_record_start(struct dt_softc *);
16091b2ecf6Smpi void dt_ioctl_record_stop(struct dt_softc *);
16191b2ecf6Smpi int dt_ioctl_probe_enable(struct dt_softc *, struct dtioc_req *);
162840df46fSjasper int dt_ioctl_probe_disable(struct dt_softc *, struct dtioc_req *);
163a28bb56fSclaudio int dt_ioctl_get_auxbase(struct dt_softc *, struct dtioc_getaux *);
16491b2ecf6Smpi
165f5199088Smpi int dt_ring_copy(struct dt_cpubuf *, struct uio *, size_t, size_t *);
16691b2ecf6Smpi
1679d5d5c61Smpi void dt_wakeup(struct dt_softc *);
1689d5d5c61Smpi void dt_deferred_wakeup(void *);
1699d5d5c61Smpi
17091b2ecf6Smpi void
dtattach(struct device * parent,struct device * self,void * aux)17191b2ecf6Smpi dtattach(struct device *parent, struct device *self, void *aux)
17291b2ecf6Smpi {
17391b2ecf6Smpi SLIST_INIT(&dtdev_list);
17491b2ecf6Smpi SIMPLEQ_INIT(&dt_probe_list);
17591b2ecf6Smpi
17691b2ecf6Smpi /* Init providers */
17791b2ecf6Smpi dt_nprobes += dt_prov_profile_init();
17891b2ecf6Smpi dt_nprobes += dt_prov_syscall_init();
17991b2ecf6Smpi dt_nprobes += dt_prov_static_init();
180840df46fSjasper #ifdef DDBPROF
181840df46fSjasper dt_nprobes += dt_prov_kprobe_init();
182840df46fSjasper #endif
18391b2ecf6Smpi }
18491b2ecf6Smpi
18591b2ecf6Smpi int
dtopen(dev_t dev,int flags,int mode,struct proc * p)18691b2ecf6Smpi dtopen(dev_t dev, int flags, int mode, struct proc *p)
18791b2ecf6Smpi {
18891b2ecf6Smpi struct dt_softc *sc;
18991b2ecf6Smpi int unit = minor(dev);
190c4318b75Smpi
19111c54b09Smvs if (atomic_load_int(&allowdt) == 0)
192c4318b75Smpi return EPERM;
19391b2ecf6Smpi
194f5199088Smpi sc = dtalloc();
19591b2ecf6Smpi if (sc == NULL)
19691b2ecf6Smpi return ENOMEM;
19791b2ecf6Smpi
198c57c5c68Sbluhm /* no sleep after this point */
199c57c5c68Sbluhm if (dtlookup(unit) != NULL) {
200f5199088Smpi dtfree(sc);
201c57c5c68Sbluhm return EBUSY;
202c57c5c68Sbluhm }
20391b2ecf6Smpi
20491b2ecf6Smpi sc->ds_unit = unit;
20591b2ecf6Smpi sc->ds_pid = p->p_p->ps_pid;
20691b2ecf6Smpi TAILQ_INIT(&sc->ds_pcbs);
207f5199088Smpi sc->ds_lastcpu = 0;
20891b2ecf6Smpi sc->ds_evtcnt = 0;
20991b2ecf6Smpi
21091b2ecf6Smpi SLIST_INSERT_HEAD(&dtdev_list, sc, ds_next);
21191b2ecf6Smpi
21291b2ecf6Smpi DPRINTF("dt%d: pid %d open\n", sc->ds_unit, sc->ds_pid);
21391b2ecf6Smpi
21491b2ecf6Smpi return 0;
21591b2ecf6Smpi }
21691b2ecf6Smpi
21791b2ecf6Smpi int
dtclose(dev_t dev,int flags,int mode,struct proc * p)21891b2ecf6Smpi dtclose(dev_t dev, int flags, int mode, struct proc *p)
21991b2ecf6Smpi {
22091b2ecf6Smpi struct dt_softc *sc;
22191b2ecf6Smpi int unit = minor(dev);
22291b2ecf6Smpi
22391b2ecf6Smpi sc = dtlookup(unit);
22491b2ecf6Smpi KASSERT(sc != NULL);
22591b2ecf6Smpi
22691b2ecf6Smpi DPRINTF("dt%d: pid %d close\n", sc->ds_unit, sc->ds_pid);
22791b2ecf6Smpi
22891b2ecf6Smpi SLIST_REMOVE(&dtdev_list, sc, dt_softc, ds_next);
22991b2ecf6Smpi dt_ioctl_record_stop(sc);
23091b2ecf6Smpi dt_pcb_purge(&sc->ds_pcbs);
231f5199088Smpi dtfree(sc);
23291b2ecf6Smpi
23391b2ecf6Smpi return 0;
23491b2ecf6Smpi }
23591b2ecf6Smpi
23691b2ecf6Smpi int
dtread(dev_t dev,struct uio * uio,int flags)23791b2ecf6Smpi dtread(dev_t dev, struct uio *uio, int flags)
23891b2ecf6Smpi {
23991b2ecf6Smpi struct dt_softc *sc;
240f5199088Smpi struct dt_cpubuf *dc;
241f5199088Smpi int i, error = 0, unit = minor(dev);
2429ff65b2bSmpi size_t count, max, read = 0;
24391b2ecf6Smpi
24491b2ecf6Smpi sc = dtlookup(unit);
24591b2ecf6Smpi KASSERT(sc != NULL);
24691b2ecf6Smpi
2479ff65b2bSmpi max = howmany(uio->uio_resid, sizeof(struct dt_evt));
2489ff65b2bSmpi if (max < 1)
24991b2ecf6Smpi return (EMSGSIZE);
25091b2ecf6Smpi
251f5199088Smpi while (!atomic_load_int(&sc->ds_evtcnt)) {
252f2e7dc09Sclaudio sleep_setup(sc, PWAIT | PCATCH, "dtread");
253f5199088Smpi error = sleep_finish(0, !atomic_load_int(&sc->ds_evtcnt));
25491b2ecf6Smpi if (error == EINTR || error == ERESTART)
25591b2ecf6Smpi break;
25691b2ecf6Smpi }
25791b2ecf6Smpi if (error)
25891b2ecf6Smpi return error;
25991b2ecf6Smpi
26091b2ecf6Smpi KERNEL_ASSERT_LOCKED();
261f5199088Smpi for (i = 0; i < ncpusfound; i++) {
2629ff65b2bSmpi count = 0;
263f5199088Smpi dc = &sc->ds_cpu[(sc->ds_lastcpu + i) % ncpusfound];
264f5199088Smpi error = dt_ring_copy(dc, uio, max, &count);
265baa78d7bSmpi if (error && count == 0)
2669ff65b2bSmpi break;
2679ff65b2bSmpi
26891b2ecf6Smpi read += count;
2699ff65b2bSmpi max -= count;
2709ff65b2bSmpi if (max == 0)
27191b2ecf6Smpi break;
27291b2ecf6Smpi }
273f5199088Smpi sc->ds_lastcpu += i % ncpusfound;
27491b2ecf6Smpi
275f5199088Smpi atomic_sub_int(&sc->ds_evtcnt, read);
27691b2ecf6Smpi
2779ff65b2bSmpi return error;
27891b2ecf6Smpi }
27991b2ecf6Smpi
28091b2ecf6Smpi int
dtioctl(dev_t dev,u_long cmd,caddr_t addr,int flag,struct proc * p)28191b2ecf6Smpi dtioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
28291b2ecf6Smpi {
28391b2ecf6Smpi struct dt_softc *sc;
28491b2ecf6Smpi int unit = minor(dev);
28591b2ecf6Smpi int on, error = 0;
28691b2ecf6Smpi
28791b2ecf6Smpi sc = dtlookup(unit);
28891b2ecf6Smpi KASSERT(sc != NULL);
28991b2ecf6Smpi
29091b2ecf6Smpi switch (cmd) {
29191b2ecf6Smpi case DTIOCGPLIST:
29291b2ecf6Smpi return dt_ioctl_list_probes(sc, (struct dtioc_probe *)addr);
293cd5e5223Sbluhm case DTIOCGARGS:
294cd5e5223Sbluhm return dt_ioctl_get_args(sc, (struct dtioc_arg *)addr);
29591b2ecf6Smpi case DTIOCGSTATS:
29691b2ecf6Smpi return dt_ioctl_get_stats(sc, (struct dtioc_stat *)addr);
29791b2ecf6Smpi case DTIOCRECORD:
29891b2ecf6Smpi case DTIOCPRBENABLE:
299840df46fSjasper case DTIOCPRBDISABLE:
300a28bb56fSclaudio case DTIOCGETAUXBASE:
30191b2ecf6Smpi /* root only ioctl(2) */
30291b2ecf6Smpi break;
30391b2ecf6Smpi default:
30491b2ecf6Smpi return ENOTTY;
30591b2ecf6Smpi }
30691b2ecf6Smpi
30791b2ecf6Smpi if ((error = suser(p)) != 0)
30891b2ecf6Smpi return error;
30991b2ecf6Smpi
31091b2ecf6Smpi switch (cmd) {
31191b2ecf6Smpi case DTIOCRECORD:
31291b2ecf6Smpi on = *(int *)addr;
31391b2ecf6Smpi if (on)
31491b2ecf6Smpi error = dt_ioctl_record_start(sc);
31591b2ecf6Smpi else
31691b2ecf6Smpi dt_ioctl_record_stop(sc);
31791b2ecf6Smpi break;
31891b2ecf6Smpi case DTIOCPRBENABLE:
31991b2ecf6Smpi error = dt_ioctl_probe_enable(sc, (struct dtioc_req *)addr);
32091b2ecf6Smpi break;
321840df46fSjasper case DTIOCPRBDISABLE:
322840df46fSjasper error = dt_ioctl_probe_disable(sc, (struct dtioc_req *)addr);
323840df46fSjasper break;
324a28bb56fSclaudio case DTIOCGETAUXBASE:
325a28bb56fSclaudio error = dt_ioctl_get_auxbase(sc, (struct dtioc_getaux *)addr);
326a28bb56fSclaudio break;
32791b2ecf6Smpi default:
32891b2ecf6Smpi KASSERT(0);
32991b2ecf6Smpi }
33091b2ecf6Smpi
33191b2ecf6Smpi return error;
33291b2ecf6Smpi }
33391b2ecf6Smpi
33491b2ecf6Smpi struct dt_softc *
dtlookup(int unit)33591b2ecf6Smpi dtlookup(int unit)
33691b2ecf6Smpi {
33791b2ecf6Smpi struct dt_softc *sc;
33891b2ecf6Smpi
33991b2ecf6Smpi KERNEL_ASSERT_LOCKED();
34091b2ecf6Smpi
34191b2ecf6Smpi SLIST_FOREACH(sc, &dtdev_list, ds_next) {
34291b2ecf6Smpi if (sc->ds_unit == unit)
34391b2ecf6Smpi break;
34491b2ecf6Smpi }
34591b2ecf6Smpi
34691b2ecf6Smpi return sc;
34791b2ecf6Smpi }
34891b2ecf6Smpi
349f5199088Smpi struct dt_softc *
dtalloc(void)350f5199088Smpi dtalloc(void)
351f5199088Smpi {
352f5199088Smpi struct dt_softc *sc;
353f5199088Smpi struct dt_evt *dtev;
354f5199088Smpi int i;
355f5199088Smpi
356f5199088Smpi sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_CANFAIL|M_ZERO);
357f5199088Smpi if (sc == NULL)
358f5199088Smpi return NULL;
359f5199088Smpi
360f5199088Smpi for (i = 0; i < ncpusfound; i++) {
361*18b19e82Smpi dtev = mallocarray(DT_EVTRING_SIZE, sizeof(*dtev), M_DEVBUF,
362f5199088Smpi M_WAITOK|M_CANFAIL|M_ZERO);
363f5199088Smpi if (dtev == NULL)
364f5199088Smpi break;
365f5199088Smpi sc->ds_cpu[i].dc_ring = dtev;
366f5199088Smpi }
367f5199088Smpi if (i < ncpusfound) {
368f5199088Smpi dtfree(sc);
369f5199088Smpi return NULL;
370f5199088Smpi }
371f5199088Smpi
372*18b19e82Smpi sc->ds_si = softintr_establish(IPL_SOFTCLOCK, dt_deferred_wakeup, sc);
373*18b19e82Smpi if (sc->ds_si == NULL) {
374*18b19e82Smpi dtfree(sc);
375*18b19e82Smpi return NULL;
376*18b19e82Smpi }
377*18b19e82Smpi
378f5199088Smpi return sc;
379f5199088Smpi }
380f5199088Smpi
381f5199088Smpi void
dtfree(struct dt_softc * sc)382f5199088Smpi dtfree(struct dt_softc *sc)
383f5199088Smpi {
384f5199088Smpi struct dt_evt *dtev;
385f5199088Smpi int i;
386f5199088Smpi
387*18b19e82Smpi if (sc->ds_si != NULL)
388*18b19e82Smpi softintr_disestablish(sc->ds_si);
389*18b19e82Smpi
390f5199088Smpi for (i = 0; i < ncpusfound; i++) {
391f5199088Smpi dtev = sc->ds_cpu[i].dc_ring;
392*18b19e82Smpi free(dtev, M_DEVBUF, DT_EVTRING_SIZE * sizeof(*dtev));
393f5199088Smpi }
394f5199088Smpi free(sc, M_DEVBUF, sizeof(*sc));
395f5199088Smpi }
396f5199088Smpi
39791b2ecf6Smpi int
dt_ioctl_list_probes(struct dt_softc * sc,struct dtioc_probe * dtpr)39891b2ecf6Smpi dt_ioctl_list_probes(struct dt_softc *sc, struct dtioc_probe *dtpr)
39991b2ecf6Smpi {
40091b2ecf6Smpi struct dtioc_probe_info info, *dtpi;
40191b2ecf6Smpi struct dt_probe *dtp;
40291b2ecf6Smpi size_t size;
40391b2ecf6Smpi int error = 0;
40491b2ecf6Smpi
40591b2ecf6Smpi size = dtpr->dtpr_size;
406915014bfSmpi dtpr->dtpr_size = dt_nprobes * sizeof(*dtpi);
407915014bfSmpi if (size == 0)
408915014bfSmpi return 0;
409915014bfSmpi
41091b2ecf6Smpi dtpi = dtpr->dtpr_probes;
41191b2ecf6Smpi SIMPLEQ_FOREACH(dtp, &dt_probe_list, dtp_next) {
41291b2ecf6Smpi if (size < sizeof(*dtpi)) {
41391b2ecf6Smpi error = ENOSPC;
41491b2ecf6Smpi break;
41591b2ecf6Smpi }
416cd5e5223Sbluhm memset(&info, 0, sizeof(info));
41791b2ecf6Smpi info.dtpi_pbn = dtp->dtp_pbn;
41823829576Smpi info.dtpi_nargs = dtp->dtp_nargs;
41991b2ecf6Smpi strlcpy(info.dtpi_prov, dtp->dtp_prov->dtpv_name,
42091b2ecf6Smpi sizeof(info.dtpi_prov));
42191b2ecf6Smpi strlcpy(info.dtpi_func, dtp->dtp_func, sizeof(info.dtpi_func));
42291b2ecf6Smpi strlcpy(info.dtpi_name, dtp->dtp_name, sizeof(info.dtpi_name));
42391b2ecf6Smpi error = copyout(&info, dtpi, sizeof(*dtpi));
42491b2ecf6Smpi if (error)
42591b2ecf6Smpi break;
42691b2ecf6Smpi size -= sizeof(*dtpi);
42791b2ecf6Smpi dtpi++;
428cd5e5223Sbluhm }
429cd5e5223Sbluhm
430cd5e5223Sbluhm return error;
431cd5e5223Sbluhm }
432cd5e5223Sbluhm
433cd5e5223Sbluhm int
dt_ioctl_get_args(struct dt_softc * sc,struct dtioc_arg * dtar)434cd5e5223Sbluhm dt_ioctl_get_args(struct dt_softc *sc, struct dtioc_arg *dtar)
435cd5e5223Sbluhm {
436cd5e5223Sbluhm struct dtioc_arg_info info, *dtai;
437cd5e5223Sbluhm struct dt_probe *dtp;
438cd5e5223Sbluhm size_t size, n, t;
439cd5e5223Sbluhm uint32_t pbn;
440cd5e5223Sbluhm int error = 0;
441cd5e5223Sbluhm
442cd5e5223Sbluhm pbn = dtar->dtar_pbn;
443cd5e5223Sbluhm if (pbn == 0 || pbn > dt_nprobes)
444cd5e5223Sbluhm return EINVAL;
445cd5e5223Sbluhm
446cd5e5223Sbluhm SIMPLEQ_FOREACH(dtp, &dt_probe_list, dtp_next) {
447cd5e5223Sbluhm if (pbn == dtp->dtp_pbn)
448cd5e5223Sbluhm break;
449cd5e5223Sbluhm }
450cd5e5223Sbluhm if (dtp == NULL)
451cd5e5223Sbluhm return EINVAL;
452cd5e5223Sbluhm
453cd5e5223Sbluhm if (dtp->dtp_sysnum != 0) {
454cd5e5223Sbluhm /* currently not supported for system calls */
455cd5e5223Sbluhm dtar->dtar_size = 0;
456cd5e5223Sbluhm return 0;
457cd5e5223Sbluhm }
458cd5e5223Sbluhm
459cd5e5223Sbluhm size = dtar->dtar_size;
460cd5e5223Sbluhm dtar->dtar_size = dtp->dtp_nargs * sizeof(*dtar);
461cd5e5223Sbluhm if (size == 0)
462cd5e5223Sbluhm return 0;
463cd5e5223Sbluhm
464cd5e5223Sbluhm t = 0;
465cd5e5223Sbluhm dtai = dtar->dtar_args;
466cd5e5223Sbluhm for (n = 0; n < dtp->dtp_nargs; n++) {
467cd5e5223Sbluhm if (size < sizeof(*dtai)) {
468cd5e5223Sbluhm error = ENOSPC;
469cd5e5223Sbluhm break;
470cd5e5223Sbluhm }
471cd5e5223Sbluhm if (n >= DTMAXARGTYPES || dtp->dtp_argtype[n] == NULL)
472cd5e5223Sbluhm continue;
473cd5e5223Sbluhm memset(&info, 0, sizeof(info));
474cd5e5223Sbluhm info.dtai_pbn = dtp->dtp_pbn;
475cd5e5223Sbluhm info.dtai_argn = t++;
476cd5e5223Sbluhm strlcpy(info.dtai_argtype, dtp->dtp_argtype[n],
477cd5e5223Sbluhm sizeof(info.dtai_argtype));
478cd5e5223Sbluhm error = copyout(&info, dtai, sizeof(*dtai));
479cd5e5223Sbluhm if (error)
480cd5e5223Sbluhm break;
481cd5e5223Sbluhm size -= sizeof(*dtai);
482cd5e5223Sbluhm dtai++;
483cd5e5223Sbluhm }
484cd5e5223Sbluhm dtar->dtar_size = t * sizeof(*dtar);
48591b2ecf6Smpi
48691b2ecf6Smpi return error;
48791b2ecf6Smpi }
48891b2ecf6Smpi
48991b2ecf6Smpi int
dt_ioctl_get_stats(struct dt_softc * sc,struct dtioc_stat * dtst)49091b2ecf6Smpi dt_ioctl_get_stats(struct dt_softc *sc, struct dtioc_stat *dtst)
49191b2ecf6Smpi {
492f5199088Smpi struct dt_cpubuf *dc;
493f5199088Smpi uint64_t readevt = 0, dropevt = 0;
494f5199088Smpi int i;
49591b2ecf6Smpi
496f5199088Smpi for (i = 0; i < ncpusfound; i++) {
497f5199088Smpi dc = &sc->ds_cpu[i];
498f5199088Smpi
499f5199088Smpi membar_consumer();
500f5199088Smpi dropevt += dc->dc_dropevt;
501f5199088Smpi readevt += dc->dc_readevt;
502f5199088Smpi }
503f5199088Smpi
504f5199088Smpi dtst->dtst_readevt = readevt;
505f5199088Smpi dtst->dtst_dropevt = dropevt;
50691b2ecf6Smpi return 0;
50791b2ecf6Smpi }
50891b2ecf6Smpi
50991b2ecf6Smpi int
dt_ioctl_record_start(struct dt_softc * sc)51091b2ecf6Smpi dt_ioctl_record_start(struct dt_softc *sc)
51191b2ecf6Smpi {
512a79ed6b1Scheloha uint64_t now;
51391b2ecf6Smpi struct dt_pcb *dp;
51491b2ecf6Smpi
51591b2ecf6Smpi if (sc->ds_recording)
51691b2ecf6Smpi return EBUSY;
51791b2ecf6Smpi
51891b2ecf6Smpi KERNEL_ASSERT_LOCKED();
51991b2ecf6Smpi if (TAILQ_EMPTY(&sc->ds_pcbs))
52091b2ecf6Smpi return ENOENT;
52191b2ecf6Smpi
5220e146e1bSvisa rw_enter_write(&dt_lock);
523a79ed6b1Scheloha now = nsecuptime();
52491b2ecf6Smpi TAILQ_FOREACH(dp, &sc->ds_pcbs, dp_snext) {
52591b2ecf6Smpi struct dt_probe *dtp = dp->dp_dtp;
52691b2ecf6Smpi
52791b2ecf6Smpi SMR_SLIST_INSERT_HEAD_LOCKED(&dtp->dtp_pcbs, dp, dp_pnext);
52891b2ecf6Smpi dtp->dtp_recording++;
52991b2ecf6Smpi dtp->dtp_prov->dtpv_recording++;
530c56c27b6Scheloha
531c56c27b6Scheloha if (dp->dp_nsecs != 0) {
532c56c27b6Scheloha clockintr_bind(&dp->dp_clockintr, dp->dp_cpu, dt_clock,
533c56c27b6Scheloha dp);
534a79ed6b1Scheloha clockintr_schedule(&dp->dp_clockintr,
535a79ed6b1Scheloha now + dp->dp_nsecs);
536c56c27b6Scheloha }
53791b2ecf6Smpi }
5380e146e1bSvisa rw_exit_write(&dt_lock);
53991b2ecf6Smpi
54091b2ecf6Smpi sc->ds_recording = 1;
54191b2ecf6Smpi dt_tracing++;
54291b2ecf6Smpi
54391b2ecf6Smpi return 0;
54491b2ecf6Smpi }
54591b2ecf6Smpi
54691b2ecf6Smpi void
dt_ioctl_record_stop(struct dt_softc * sc)54791b2ecf6Smpi dt_ioctl_record_stop(struct dt_softc *sc)
54891b2ecf6Smpi {
54991b2ecf6Smpi struct dt_pcb *dp;
55091b2ecf6Smpi
55191b2ecf6Smpi if (!sc->ds_recording)
55291b2ecf6Smpi return;
55391b2ecf6Smpi
55491b2ecf6Smpi DPRINTF("dt%d: pid %d disable\n", sc->ds_unit, sc->ds_pid);
55591b2ecf6Smpi
55691b2ecf6Smpi dt_tracing--;
55791b2ecf6Smpi sc->ds_recording = 0;
55891b2ecf6Smpi
5590e146e1bSvisa rw_enter_write(&dt_lock);
56091b2ecf6Smpi TAILQ_FOREACH(dp, &sc->ds_pcbs, dp_snext) {
56191b2ecf6Smpi struct dt_probe *dtp = dp->dp_dtp;
56291b2ecf6Smpi
563c56c27b6Scheloha /*
564c56c27b6Scheloha * Set an execution barrier to ensure the shared
565c56c27b6Scheloha * reference to dp is inactive.
566c56c27b6Scheloha */
567c56c27b6Scheloha if (dp->dp_nsecs != 0)
568c56c27b6Scheloha clockintr_unbind(&dp->dp_clockintr, CL_BARRIER);
569c56c27b6Scheloha
57091b2ecf6Smpi dtp->dtp_recording--;
57191b2ecf6Smpi dtp->dtp_prov->dtpv_recording--;
57291b2ecf6Smpi SMR_SLIST_REMOVE_LOCKED(&dtp->dtp_pcbs, dp, dt_pcb, dp_pnext);
57391b2ecf6Smpi }
5740e146e1bSvisa rw_exit_write(&dt_lock);
5750e146e1bSvisa
5760e146e1bSvisa /* Wait until readers cannot access the PCBs. */
5770e146e1bSvisa smr_barrier();
57891b2ecf6Smpi }
57991b2ecf6Smpi
58091b2ecf6Smpi int
dt_ioctl_probe_enable(struct dt_softc * sc,struct dtioc_req * dtrq)58191b2ecf6Smpi dt_ioctl_probe_enable(struct dt_softc *sc, struct dtioc_req *dtrq)
58291b2ecf6Smpi {
58391b2ecf6Smpi struct dt_pcb_list plist;
58491b2ecf6Smpi struct dt_probe *dtp;
58591b2ecf6Smpi int error;
58691b2ecf6Smpi
58791b2ecf6Smpi SIMPLEQ_FOREACH(dtp, &dt_probe_list, dtp_next) {
58891b2ecf6Smpi if (dtp->dtp_pbn == dtrq->dtrq_pbn)
58991b2ecf6Smpi break;
59091b2ecf6Smpi }
59191b2ecf6Smpi if (dtp == NULL)
59291b2ecf6Smpi return ENOENT;
59391b2ecf6Smpi
59491b2ecf6Smpi TAILQ_INIT(&plist);
59591b2ecf6Smpi error = dtp->dtp_prov->dtpv_alloc(dtp, sc, &plist, dtrq);
59691b2ecf6Smpi if (error)
59791b2ecf6Smpi return error;
59891b2ecf6Smpi
59991b2ecf6Smpi DPRINTF("dt%d: pid %d enable %u : %b\n", sc->ds_unit, sc->ds_pid,
60091b2ecf6Smpi dtrq->dtrq_pbn, (unsigned int)dtrq->dtrq_evtflags, DTEVT_FLAG_BITS);
60191b2ecf6Smpi
60291b2ecf6Smpi /* Append all PCBs to this instance */
6031e9785c2Sbket TAILQ_CONCAT(&sc->ds_pcbs, &plist, dp_snext);
60491b2ecf6Smpi
60591b2ecf6Smpi return 0;
60691b2ecf6Smpi }
60791b2ecf6Smpi
608840df46fSjasper int
dt_ioctl_probe_disable(struct dt_softc * sc,struct dtioc_req * dtrq)609840df46fSjasper dt_ioctl_probe_disable(struct dt_softc *sc, struct dtioc_req *dtrq)
610840df46fSjasper {
611840df46fSjasper struct dt_probe *dtp;
612840df46fSjasper int error;
613840df46fSjasper
614840df46fSjasper SIMPLEQ_FOREACH(dtp, &dt_probe_list, dtp_next) {
615840df46fSjasper if (dtp->dtp_pbn == dtrq->dtrq_pbn)
616840df46fSjasper break;
617840df46fSjasper }
618840df46fSjasper if (dtp == NULL)
619840df46fSjasper return ENOENT;
620840df46fSjasper
621840df46fSjasper if (dtp->dtp_prov->dtpv_dealloc) {
622840df46fSjasper error = dtp->dtp_prov->dtpv_dealloc(dtp, sc, dtrq);
623840df46fSjasper if (error)
624840df46fSjasper return error;
625840df46fSjasper }
626840df46fSjasper
627840df46fSjasper DPRINTF("dt%d: pid %d dealloc\n", sc->ds_unit, sc->ds_pid,
628840df46fSjasper dtrq->dtrq_pbn);
629840df46fSjasper
630840df46fSjasper return 0;
631840df46fSjasper }
632840df46fSjasper
633a28bb56fSclaudio int
dt_ioctl_get_auxbase(struct dt_softc * sc,struct dtioc_getaux * dtga)634a28bb56fSclaudio dt_ioctl_get_auxbase(struct dt_softc *sc, struct dtioc_getaux *dtga)
635a28bb56fSclaudio {
636a28bb56fSclaudio struct uio uio;
637a28bb56fSclaudio struct iovec iov;
638a28bb56fSclaudio struct process *pr;
639a28bb56fSclaudio struct proc *p = curproc;
640a28bb56fSclaudio AuxInfo auxv[ELF_AUX_ENTRIES];
641a28bb56fSclaudio int i, error;
642a28bb56fSclaudio
643a28bb56fSclaudio dtga->dtga_auxbase = 0;
644a28bb56fSclaudio
645a28bb56fSclaudio if ((pr = prfind(dtga->dtga_pid)) == NULL)
646a28bb56fSclaudio return ESRCH;
647a28bb56fSclaudio
648a28bb56fSclaudio iov.iov_base = auxv;
649a28bb56fSclaudio iov.iov_len = sizeof(auxv);
650a28bb56fSclaudio uio.uio_iov = &iov;
651a28bb56fSclaudio uio.uio_iovcnt = 1;
652a28bb56fSclaudio uio.uio_offset = pr->ps_auxinfo;
653a28bb56fSclaudio uio.uio_resid = sizeof(auxv);
654a28bb56fSclaudio uio.uio_segflg = UIO_SYSSPACE;
655a28bb56fSclaudio uio.uio_procp = p;
656a28bb56fSclaudio uio.uio_rw = UIO_READ;
657a28bb56fSclaudio
658a28bb56fSclaudio error = process_domem(p, pr, &uio, PT_READ_D);
659a28bb56fSclaudio if (error)
660a28bb56fSclaudio return error;
661a28bb56fSclaudio
662a28bb56fSclaudio for (i = 0; i < ELF_AUX_ENTRIES; i++)
663a28bb56fSclaudio if (auxv[i].au_id == AUX_base)
664a28bb56fSclaudio dtga->dtga_auxbase = auxv[i].au_v;
665a28bb56fSclaudio
666a28bb56fSclaudio return 0;
667a28bb56fSclaudio }
668a28bb56fSclaudio
66991b2ecf6Smpi struct dt_probe *
dt_dev_alloc_probe(const char * func,const char * name,struct dt_provider * dtpv)67091b2ecf6Smpi dt_dev_alloc_probe(const char *func, const char *name, struct dt_provider *dtpv)
67191b2ecf6Smpi {
67291b2ecf6Smpi struct dt_probe *dtp;
67391b2ecf6Smpi
67491b2ecf6Smpi dtp = malloc(sizeof(*dtp), M_DT, M_NOWAIT|M_ZERO);
67591b2ecf6Smpi if (dtp == NULL)
67691b2ecf6Smpi return NULL;
67791b2ecf6Smpi
67891b2ecf6Smpi SMR_SLIST_INIT(&dtp->dtp_pcbs);
67991b2ecf6Smpi dtp->dtp_prov = dtpv;
68091b2ecf6Smpi dtp->dtp_func = func;
68191b2ecf6Smpi dtp->dtp_name = name;
68291b2ecf6Smpi dtp->dtp_sysnum = -1;
683840df46fSjasper dtp->dtp_ref = 0;
684840df46fSjasper
68591b2ecf6Smpi return dtp;
68691b2ecf6Smpi }
68791b2ecf6Smpi
68891b2ecf6Smpi void
dt_dev_register_probe(struct dt_probe * dtp)68991b2ecf6Smpi dt_dev_register_probe(struct dt_probe *dtp)
69091b2ecf6Smpi {
69191b2ecf6Smpi static uint64_t probe_nb;
69291b2ecf6Smpi
69391b2ecf6Smpi dtp->dtp_pbn = ++probe_nb;
69491b2ecf6Smpi SIMPLEQ_INSERT_TAIL(&dt_probe_list, dtp, dtp_next);
69591b2ecf6Smpi }
69691b2ecf6Smpi
69791b2ecf6Smpi struct dt_pcb *
dt_pcb_alloc(struct dt_probe * dtp,struct dt_softc * sc)69891b2ecf6Smpi dt_pcb_alloc(struct dt_probe *dtp, struct dt_softc *sc)
69991b2ecf6Smpi {
70091b2ecf6Smpi struct dt_pcb *dp;
70191b2ecf6Smpi
70291b2ecf6Smpi dp = malloc(sizeof(*dp), M_DT, M_WAITOK|M_CANFAIL|M_ZERO);
70391b2ecf6Smpi if (dp == NULL)
704f5199088Smpi return NULL;
70591b2ecf6Smpi
70691b2ecf6Smpi dp->dp_sc = sc;
70791b2ecf6Smpi dp->dp_dtp = dtp;
70891b2ecf6Smpi return dp;
70991b2ecf6Smpi }
71091b2ecf6Smpi
71191b2ecf6Smpi void
dt_pcb_free(struct dt_pcb * dp)71291b2ecf6Smpi dt_pcb_free(struct dt_pcb *dp)
71391b2ecf6Smpi {
71491b2ecf6Smpi free(dp, M_DT, sizeof(*dp));
71591b2ecf6Smpi }
71691b2ecf6Smpi
71791b2ecf6Smpi void
dt_pcb_purge(struct dt_pcb_list * plist)71891b2ecf6Smpi dt_pcb_purge(struct dt_pcb_list *plist)
71991b2ecf6Smpi {
72091b2ecf6Smpi struct dt_pcb *dp;
72191b2ecf6Smpi
72291b2ecf6Smpi while ((dp = TAILQ_FIRST(plist)) != NULL) {
72391b2ecf6Smpi TAILQ_REMOVE(plist, dp, dp_snext);
72491b2ecf6Smpi dt_pcb_free(dp);
72591b2ecf6Smpi }
72691b2ecf6Smpi }
72791b2ecf6Smpi
72891b2ecf6Smpi /*
72991b2ecf6Smpi * Get a reference to the next free event state from the ring.
73091b2ecf6Smpi */
73191b2ecf6Smpi struct dt_evt *
dt_pcb_ring_get(struct dt_pcb * dp,int profiling)732ecfa01e0Smpi dt_pcb_ring_get(struct dt_pcb *dp, int profiling)
73391b2ecf6Smpi {
73491b2ecf6Smpi struct proc *p = curproc;
73591b2ecf6Smpi struct dt_evt *dtev;
736f5199088Smpi int prod, cons, distance;
737f5199088Smpi struct dt_cpubuf *dc = &dp->dp_sc->ds_cpu[cpu_number()];
73891b2ecf6Smpi
739f5199088Smpi if (dc->dc_inevt == 1)
740f5199088Smpi return NULL;
741f5199088Smpi
742f5199088Smpi dc->dc_inevt = 1;
743f5199088Smpi
744f5199088Smpi membar_consumer();
745f5199088Smpi prod = dc->dc_prod;
746f5199088Smpi cons = dc->dc_cons;
747f5199088Smpi distance = prod - cons;
74891b2ecf6Smpi if (distance == 1 || distance == (1 - DT_EVTRING_SIZE)) {
74991b2ecf6Smpi /* read(2) isn't finished */
750f5199088Smpi dc->dc_dropevt++;
751f5199088Smpi membar_producer();
752f5199088Smpi
753f5199088Smpi dc->dc_inevt = 0;
75491b2ecf6Smpi return NULL;
75591b2ecf6Smpi }
75691b2ecf6Smpi
75791b2ecf6Smpi /*
75891b2ecf6Smpi * Save states in next free event slot.
75991b2ecf6Smpi */
760f5199088Smpi dtev = &dc->dc_ring[cons];
76191b2ecf6Smpi memset(dtev, 0, sizeof(*dtev));
76291b2ecf6Smpi
76391b2ecf6Smpi dtev->dtev_pbn = dp->dp_dtp->dtp_pbn;
76491b2ecf6Smpi dtev->dtev_cpu = cpu_number();
76591b2ecf6Smpi dtev->dtev_pid = p->p_p->ps_pid;
76621e92265Sbluhm dtev->dtev_tid = p->p_tid + THREAD_PID_OFFSET;
76791b2ecf6Smpi nanotime(&dtev->dtev_tsp);
76891b2ecf6Smpi
76991b2ecf6Smpi if (ISSET(dp->dp_evtflags, DTEVT_EXECNAME))
77014cb6e73Sderaadt strlcpy(dtev->dtev_comm, p->p_p->ps_comm, sizeof(dtev->dtev_comm));
77191b2ecf6Smpi
772a28bb56fSclaudio if (ISSET(dp->dp_evtflags, DTEVT_KSTACK)) {
773ecfa01e0Smpi if (profiling)
774ecfa01e0Smpi stacktrace_save_at(&dtev->dtev_kstack, DT_FA_PROFILE);
775ecfa01e0Smpi else
776ecfa01e0Smpi stacktrace_save_at(&dtev->dtev_kstack, DT_FA_STATIC);
77791b2ecf6Smpi }
778a28bb56fSclaudio if (ISSET(dp->dp_evtflags, DTEVT_USTACK))
779a28bb56fSclaudio stacktrace_save_utrace(&dtev->dtev_ustack);
78091b2ecf6Smpi
78191b2ecf6Smpi return dtev;
78291b2ecf6Smpi }
78391b2ecf6Smpi
78491b2ecf6Smpi void
dt_pcb_ring_consume(struct dt_pcb * dp,struct dt_evt * dtev)78591b2ecf6Smpi dt_pcb_ring_consume(struct dt_pcb *dp, struct dt_evt *dtev)
78691b2ecf6Smpi {
787f5199088Smpi struct dt_cpubuf *dc = &dp->dp_sc->ds_cpu[cpu_number()];
78891b2ecf6Smpi
789f5199088Smpi KASSERT(dtev == &dc->dc_ring[dc->dc_cons]);
79091b2ecf6Smpi
791f5199088Smpi dc->dc_cons = (dc->dc_cons + 1) % DT_EVTRING_SIZE;
792f5199088Smpi membar_producer();
793f5199088Smpi
794f5199088Smpi atomic_inc_int(&dp->dp_sc->ds_evtcnt);
795f5199088Smpi dc->dc_inevt = 0;
796f5199088Smpi
7979d5d5c61Smpi dt_wakeup(dp->dp_sc);
79891b2ecf6Smpi }
79991b2ecf6Smpi
80091b2ecf6Smpi /*
801f5199088Smpi * Copy at most `max' events from `dc', producing the same amount
80291b2ecf6Smpi * of free slots.
80391b2ecf6Smpi */
80491b2ecf6Smpi int
dt_ring_copy(struct dt_cpubuf * dc,struct uio * uio,size_t max,size_t * rcvd)805f5199088Smpi dt_ring_copy(struct dt_cpubuf *dc, struct uio *uio, size_t max, size_t *rcvd)
80691b2ecf6Smpi {
80791b2ecf6Smpi size_t count, copied = 0;
80891b2ecf6Smpi unsigned int cons, prod;
8099ff65b2bSmpi int error = 0;
81091b2ecf6Smpi
8119ff65b2bSmpi KASSERT(max > 0);
81291b2ecf6Smpi
813f5199088Smpi membar_consumer();
814f5199088Smpi cons = dc->dc_cons;
815f5199088Smpi prod = dc->dc_prod;
81691b2ecf6Smpi
81791b2ecf6Smpi if (cons < prod)
81891b2ecf6Smpi count = DT_EVTRING_SIZE - prod;
81991b2ecf6Smpi else
82091b2ecf6Smpi count = cons - prod;
82191b2ecf6Smpi
82291b2ecf6Smpi if (count == 0)
8239ff65b2bSmpi return 0;
82491b2ecf6Smpi
8259ff65b2bSmpi count = MIN(count, max);
826f5199088Smpi error = uiomove(&dc->dc_ring[prod], count * sizeof(struct dt_evt), uio);
8279ff65b2bSmpi if (error)
8289ff65b2bSmpi return error;
82991b2ecf6Smpi copied += count;
83091b2ecf6Smpi
83191b2ecf6Smpi /* Produce */
83291b2ecf6Smpi prod = (prod + count) % DT_EVTRING_SIZE;
83391b2ecf6Smpi
8349ff65b2bSmpi /* If the ring didn't wrap, stop here. */
8359ff65b2bSmpi if (max == copied || prod != 0 || cons == 0)
83691b2ecf6Smpi goto out;
83791b2ecf6Smpi
8389ff65b2bSmpi count = MIN(cons, (max - copied));
839f5199088Smpi error = uiomove(&dc->dc_ring[0], count * sizeof(struct dt_evt), uio);
8409ff65b2bSmpi if (error)
8419ff65b2bSmpi goto out;
8429ff65b2bSmpi
84391b2ecf6Smpi copied += count;
84491b2ecf6Smpi prod += count;
84591b2ecf6Smpi
84691b2ecf6Smpi out:
847f5199088Smpi dc->dc_readevt += copied;
848f5199088Smpi dc->dc_prod = prod;
849f5199088Smpi membar_producer();
8509ff65b2bSmpi
8519ff65b2bSmpi *rcvd = copied;
8529ff65b2bSmpi return error;
85391b2ecf6Smpi }
8549d5d5c61Smpi
8559d5d5c61Smpi void
dt_wakeup(struct dt_softc * sc)8569d5d5c61Smpi dt_wakeup(struct dt_softc *sc)
8579d5d5c61Smpi {
8589d5d5c61Smpi /*
8599d5d5c61Smpi * It is not always safe or possible to call wakeup(9) and grab
8609d5d5c61Smpi * the SCHED_LOCK() from a given tracepoint. This is true for
8619d5d5c61Smpi * any tracepoint that might trigger inside the scheduler or at
8629d5d5c61Smpi * any IPL higher than IPL_SCHED. For this reason use a soft-
8639d5d5c61Smpi * interrupt to defer the wakeup.
8649d5d5c61Smpi */
8659d5d5c61Smpi softintr_schedule(sc->ds_si);
8669d5d5c61Smpi }
8679d5d5c61Smpi
8689d5d5c61Smpi void
dt_deferred_wakeup(void * arg)8699d5d5c61Smpi dt_deferred_wakeup(void *arg)
8709d5d5c61Smpi {
8719d5d5c61Smpi struct dt_softc *sc = arg;
8729d5d5c61Smpi
8739d5d5c61Smpi wakeup(sc);
8749d5d5c61Smpi }
875