1 /*        $NetBSD: lockstat.c,v 1.12 2022/02/27 14:16:32 riastradh Exp $        */
2 
3 /*
4  * CDDL HEADER START
5  *
6  * The contents of this file are subject to the terms of the
7  * Common Development and Distribution License (the "License").
8  * You may not use this file except in compliance with the License.
9  *
10  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
11  * or http://www.opensolaris.org/os/licensing.
12  * See the License for the specific language governing permissions
13  * and limitations under the License.
14  *
15  * When distributing Covered Code, include this CDDL HEADER in each
16  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17  * If applicable, add the following below this CDDL HEADER, with the
18  * fields enclosed by brackets "[]" replaced with your own identifying
19  * information: Portions Copyright [yyyy] [name of copyright owner]
20  *
21  * CDDL HEADER END
22  */
23 /*
24  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 #include <sys/cdefs.h>
29 __KERNEL_RCSID(0, "$NetBSD: lockstat.c,v 1.12 2022/02/27 14:16:32 riastradh Exp $");
30 
31 #include <sys/types.h>
32 #include <sys/param.h>
33 
34 #include <sys/atomic.h>
35 #include <sys/conf.h>
36 #include <sys/dtrace.h>
37 #include <sys/module.h>
38 #include <sys/mutex.h>
39 #include <sys/proc.h>
40 #include <sys/systm.h>
41 #include <sys/xcall.h>
42 
43 #define NLOCKSTAT 1
44 #include <dev/lockstat.h>
45 
46 typedef struct lockstat_probe {
47           const char          *lsp_func;
48           const char          *lsp_name;
49           int                 lsp_probe;
50           dtrace_id_t         lsp_id;
51 } lockstat_probe_t;
52 
53 lockstat_probe_t lockstat_probes[] = {
54           { "mutex_spin", "spin",                 LB_SPIN_MUTEX       | LB_SPIN,          0 },
55           { "mutex_adaptive", "sleep",  LB_ADAPTIVE_MUTEX | LB_SLEEP1,          0 },
56           { "mutex_adaptive", "spin",   LB_ADAPTIVE_MUTEX | LB_SPIN,  0 },
57           { "rwlock", "sleep_writer",   LB_RWLOCK | LB_SLEEP1,        0 },
58           { "rwlock", "sleep_reader",   LB_RWLOCK | LB_SLEEP2,        0 },
59           { "rwlock", "spin",           LB_RWLOCK | LB_SPIN,          0 },
60           { "kernel", "spin",           LB_KERNEL_LOCK      | LB_SPIN,          0 },
61           { "lwp", "spin",              LB_NOPREEMPT        | LB_SPIN,          0 },
62           { NULL, NULL,                           0,                                      0 },
63 };
64 
65 static dtrace_provider_id_t lockstat_id;
66 static size_t lockstat_dtrace_count;
67 
68 /*ARGSUSED*/
69 static int
lockstat_enable(void * arg,dtrace_id_t id,void * parg)70 lockstat_enable(void *arg, dtrace_id_t id, void *parg)
71 {
72           lockstat_probe_t *probe = parg;
73 
74           ASSERT(!lockstat_probemap[LS_COMPRESS(probe->lsp_probe)]);
75 
76           if (lockstat_dtrace_count++ == 0) {
77                     LOCKSTAT_ENABLED_UPDATE_BEGIN();
78                     lockstat_dtrace_enabled = LB_DTRACE;
79                     LOCKSTAT_ENABLED_UPDATE_END();
80           }
81           atomic_store_relaxed(&lockstat_probemap[LS_COMPRESS(probe->lsp_probe)],
82               id);
83 
84           return 0;
85 }
86 
87 /*ARGSUSED*/
88 static void
lockstat_disable(void * arg,dtrace_id_t id __unused,void * parg)89 lockstat_disable(void *arg, dtrace_id_t id __unused, void *parg)
90 {
91           lockstat_probe_t *probe = parg;
92 
93           ASSERT(lockstat_probemap[LS_COMPRESS(probe->lsp_probe)]);
94 
95           atomic_store_relaxed(&lockstat_probemap[LS_COMPRESS(probe->lsp_probe)],
96               0);
97           if (--lockstat_dtrace_count == 0) {
98                     LOCKSTAT_ENABLED_UPDATE_BEGIN();
99                     lockstat_dtrace_enabled = 0;
100                     LOCKSTAT_ENABLED_UPDATE_END();
101 
102                     /*
103                      * Wait for all lockstat dtrace probe on all CPUs to
104                      * finish, now that they've been disabled.
105                      */
106                     xc_barrier(0);
107           }
108 }
109 
110 /*ARGSUSED*/
111 static void
lockstat_provide(void * arg,dtrace_probedesc_t * desc)112 lockstat_provide(void *arg, dtrace_probedesc_t *desc)
113 {
114           int i = 0;
115 
116           for (i = 0; lockstat_probes[i].lsp_func != NULL; i++) {
117                     lockstat_probe_t *probe = &lockstat_probes[i];
118 
119                     if (dtrace_probe_lookup(lockstat_id, __UNCONST("kernel"),
120                               __UNCONST(probe->lsp_func), __UNCONST(probe->lsp_name))
121                         != 0)
122                               continue;
123 
124                     ASSERT(!probe->lsp_id);
125                     probe->lsp_id = dtrace_probe_create(lockstat_id,
126                         __UNCONST("kernel"), __UNCONST(probe->lsp_func),
127                         __UNCONST(probe->lsp_name), 1, probe);
128           }
129 }
130 
131 /*ARGSUSED*/
132 static void
lockstat_destroy(void * arg,dtrace_id_t id,void * parg)133 lockstat_destroy(void *arg, dtrace_id_t id, void *parg)
134 {
135           lockstat_probe_t *probe = parg;
136 
137           ASSERT(!lockstat_probemap[probe->lsp_probe]);
138           probe->lsp_id = 0;
139 }
140 
141 static dtrace_pattr_t lockstat_attr = {
142 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
143 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
144 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
145 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
146 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
147 };
148 
149 static dtrace_pops_t lockstat_pops = {
150           lockstat_provide,
151           NULL,
152           lockstat_enable,
153           lockstat_disable,
154           NULL,
155           NULL,
156           NULL,
157           NULL,
158           NULL,
159           lockstat_destroy
160 };
161 
162 typedef void (*dtrace_probe_func_t)(dtrace_id_t, uintptr_t, uintptr_t,
163     uintptr_t, uintptr_t, uintptr_t);
164 
165 static bool
lockstat_cas_probe(dtrace_probe_func_t old,dtrace_probe_func_t new)166 lockstat_cas_probe(dtrace_probe_func_t old, dtrace_probe_func_t new)
167 {
168 
169           ASSERT(kernconfig_is_held());
170 
171           if (lockstat_probe_func != old)
172                     return false;
173 
174           lockstat_probe_func = new;
175 
176           /*
177            * Make sure that the probe function is initialized on all CPUs
178            * before we enable the lockstat probe by setting
179            * lockstat_probemap[...].
180            */
181           xc_barrier(0);
182 
183           return true;
184 }
185 
186 static int
lockstat_init(void)187 lockstat_init(void)
188 {
189           int error;
190           bool ok;
191 
192           /* Install the probe function.  */
193           ok = lockstat_cas_probe(lockstat_probe_stub, dtrace_probe);
194           if (!ok) {
195                     printf("dtrace_lockstat: lockstat probe already installed\n");
196                     error = EEXIST;
197                     goto fail0;
198           }
199 
200           /* Everything is in place.  Register a dtrace provider.  */
201           ASSERT(lockstat_id == 0);
202           error = dtrace_register("lockstat", &lockstat_attr, DTRACE_PRIV_USER,
203               NULL, &lockstat_pops, NULL, &lockstat_id);
204           if (error) {
205                     printf("dtrace_lockstat: failed to register dtrace provider"
206                         ": %d\n", error);
207                     goto fail1;
208           }
209           /* Success!  */
210           return 0;
211 
212 fail1:    ok = lockstat_cas_probe(dtrace_probe, lockstat_probe_stub);
213           ASSERT(ok);
214 fail0:    ASSERT(error);
215           return error;
216 }
217 
218 static int
lockstat_fini(void)219 lockstat_fini(void)
220 {
221           int error;
222           bool ok __debugused;
223 
224           error = dtrace_unregister(lockstat_id);
225           if (error) {
226                     printf("dtrace_lockstat: failed to unregister dtrace provider"
227                         ": %d\n",
228                         error);
229                     return error;
230           }
231 
232           /* Unhook the probe.  */
233           ok = lockstat_cas_probe(dtrace_probe, lockstat_probe_stub);
234           ASSERT(ok);
235 
236           /* Success!  */
237           return 0;
238 }
239 
240 static int
dtrace_lockstat_modcmd(modcmd_t cmd,void * data)241 dtrace_lockstat_modcmd(modcmd_t cmd, void *data)
242 {
243 
244           switch (cmd) {
245           case MODULE_CMD_INIT:
246                     return lockstat_init();
247           case MODULE_CMD_FINI:
248                     return lockstat_fini();
249           default:
250                     return ENOTTY;
251           }
252 }
253 
254 MODULE(MODULE_CLASS_MISC, dtrace_lockstat, "dtrace");
255