lockstat(4): Membar audit. - Serialize updates to lockstat_enabled, lockstat_dev_enabled, and lockstat_dtrace_enabled with a new __cpu_simple_lock. - Use xc_barrier to obviate any need for additional membars in lockstat_event. - Use atomic_load/store_* for access that might not be serialized by lockstat_lock or lockstat_enabled_lock.diff -r1.10 -r1.11 src/external/cddl/osnet/dev/lockstat/lockstat.c
(riastradh)
--- src/external/cddl/osnet/dev/lockstat/lockstat.c 2019/02/12 14:31:45 1.10
+++ src/external/cddl/osnet/dev/lockstat/lockstat.c 2022/02/27 14:16:12 1.11
@@ -1,14 +1,14 @@ | @@ -1,14 +1,14 @@ | |||
1 | /* $NetBSD: lockstat.c,v 1.10 2019/02/12 14:31:45 rin Exp $ */ | 1 | /* $NetBSD: lockstat.c,v 1.11 2022/02/27 14:16:12 riastradh Exp $ */ | |
2 | 2 | |||
3 | /* | 3 | /* | |
4 | * CDDL HEADER START | 4 | * CDDL HEADER START | |
5 | * | 5 | * | |
6 | * The contents of this file are subject to the terms of the | 6 | * The contents of this file are subject to the terms of the | |
7 | * Common Development and Distribution License (the "License"). | 7 | * Common Development and Distribution License (the "License"). | |
8 | * You may not use this file except in compliance with the License. | 8 | * You may not use this file except in compliance with the License. | |
9 | * | 9 | * | |
10 | * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE | 10 | * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE | |
11 | * or http://www.opensolaris.org/os/licensing. | 11 | * or http://www.opensolaris.org/os/licensing. | |
12 | * See the License for the specific language governing permissions | 12 | * See the License for the specific language governing permissions | |
13 | * and limitations under the License. | 13 | * and limitations under the License. | |
14 | * | 14 | * | |
@@ -16,27 +16,27 @@ | @@ -16,27 +16,27 @@ | |||
16 | * file and include the License file at usr/src/OPENSOLARIS.LICENSE. | 16 | * file and include the License file at usr/src/OPENSOLARIS.LICENSE. | |
17 | * If applicable, add the following below this CDDL HEADER, with the | 17 | * If applicable, add the following below this CDDL HEADER, with the | |
18 | * fields enclosed by brackets "[]" replaced with your own identifying | 18 | * fields enclosed by brackets "[]" replaced with your own identifying | |
19 | * information: Portions Copyright [yyyy] [name of copyright owner] | 19 | * information: Portions Copyright [yyyy] [name of copyright owner] | |
20 | * | 20 | * | |
21 | * CDDL HEADER END | 21 | * CDDL HEADER END | |
22 | */ | 22 | */ | |
23 | /* | 23 | /* | |
24 | * Copyright 2009 Sun Microsystems, Inc. All rights reserved. | 24 | * Copyright 2009 Sun Microsystems, Inc. All rights reserved. | |
25 | * Use is subject to license terms. | 25 | * Use is subject to license terms. | |
26 | */ | 26 | */ | |
27 | 27 | |||
28 | #include <sys/cdefs.h> | 28 | #include <sys/cdefs.h> | |
29 | __KERNEL_RCSID(0, "$NetBSD: lockstat.c,v 1.10 2019/02/12 14:31:45 rin Exp $"); | 29 | __KERNEL_RCSID(0, "$NetBSD: lockstat.c,v 1.11 2022/02/27 14:16:12 riastradh Exp $"); | |
30 | 30 | |||
31 | #include <sys/types.h> | 31 | #include <sys/types.h> | |
32 | #include <sys/proc.h> | 32 | #include <sys/proc.h> | |
33 | #include <sys/param.h> | 33 | #include <sys/param.h> | |
34 | #include <sys/conf.h> | 34 | #include <sys/conf.h> | |
35 | #include <sys/dtrace.h> | 35 | #include <sys/dtrace.h> | |
36 | #include <sys/module.h> | 36 | #include <sys/module.h> | |
37 | #include <sys/mutex.h> | 37 | #include <sys/mutex.h> | |
38 | #include <sys/systm.h> | 38 | #include <sys/systm.h> | |
39 | #include <sys/xcall.h> | 39 | #include <sys/xcall.h> | |
40 | #include <sys/atomic.h> | 40 | #include <sys/atomic.h> | |
41 | 41 | |||
42 | #define NLOCKSTAT 1 | 42 | #define NLOCKSTAT 1 | |
@@ -62,49 +62,58 @@ lockstat_probe_t lockstat_probes[] = { | @@ -62,49 +62,58 @@ lockstat_probe_t lockstat_probes[] = { | |||
62 | }; | 62 | }; | |
63 | 63 | |||
64 | static dtrace_provider_id_t lockstat_id; | 64 | static dtrace_provider_id_t lockstat_id; | |
65 | static size_t lockstat_dtrace_count; | 65 | static size_t lockstat_dtrace_count; | |
66 | 66 | |||
67 | /*ARGSUSED*/ | 67 | /*ARGSUSED*/ | |
68 | static int | 68 | static int | |
69 | lockstat_enable(void *arg, dtrace_id_t id, void *parg) | 69 | lockstat_enable(void *arg, dtrace_id_t id, void *parg) | |
70 | { | 70 | { | |
71 | lockstat_probe_t *probe = parg; | 71 | lockstat_probe_t *probe = parg; | |
72 | 72 | |||
73 | ASSERT(!lockstat_probemap[LS_COMPRESS(probe->lsp_probe)]); | 73 | ASSERT(!lockstat_probemap[LS_COMPRESS(probe->lsp_probe)]); | |
74 | 74 | |||
75 | lockstat_probemap[LS_COMPRESS(probe->lsp_probe)] = id; | |||
76 | if (lockstat_dtrace_count++ == 0) { | 75 | if (lockstat_dtrace_count++ == 0) { | |
76 | LOCKSTAT_ENABLED_UPDATE_BEGIN(); | |||
77 | lockstat_dtrace_enabled = LB_DTRACE; | 77 | lockstat_dtrace_enabled = LB_DTRACE; | |
78 | LOCKSTAT_ENABLED_UPDATE(); | 78 | LOCKSTAT_ENABLED_UPDATE_END(); | |
79 | } | 79 | } | |
80 | atomic_store_relaxed(&lockstat_probemap[LS_COMPRESS(probe->lsp_probe)], | |||
81 | id); | |||
80 | 82 | |||
81 | return 0; | 83 | return 0; | |
82 | } | 84 | } | |
83 | 85 | |||
84 | /*ARGSUSED*/ | 86 | /*ARGSUSED*/ | |
85 | static void | 87 | static void | |
86 | lockstat_disable(void *arg, dtrace_id_t id __unused, void *parg) | 88 | lockstat_disable(void *arg, dtrace_id_t id __unused, void *parg) | |
87 | { | 89 | { | |
88 | lockstat_probe_t *probe = parg; | 90 | lockstat_probe_t *probe = parg; | |
89 | 91 | |||
90 | ASSERT(lockstat_probemap[LS_COMPRESS(probe->lsp_probe)]); | 92 | ASSERT(lockstat_probemap[LS_COMPRESS(probe->lsp_probe)]); | |
91 | 93 | |||
94 | atomic_store_relaxed(&lockstat_probemap[LS_COMPRESS(probe->lsp_probe)], | |||
95 | 0); | |||
92 | if (--lockstat_dtrace_count == 0) { | 96 | if (--lockstat_dtrace_count == 0) { | |
97 | LOCKSTAT_ENABLED_UPDATE_BEGIN(); | |||
93 | lockstat_dtrace_enabled = 0; | 98 | lockstat_dtrace_enabled = 0; | |
94 | LOCKSTAT_ENABLED_UPDATE(); | 99 | LOCKSTAT_ENABLED_UPDATE_END(); | |
95 | } | |||
96 | 100 | |||
97 | lockstat_probemap[LS_COMPRESS(probe->lsp_probe)] = 0; | 101 | /* | |
102 | * Wait for all lockstat dtrace probe on all CPUs to | |||
103 | * finish, now that they've been disabled. | |||
104 | */ | |||
105 | xc_barrier(0); | |||
106 | } | |||
98 | } | 107 | } | |
99 | 108 | |||
100 | /*ARGSUSED*/ | 109 | /*ARGSUSED*/ | |
101 | static void | 110 | static void | |
102 | lockstat_provide(void *arg, dtrace_probedesc_t *desc) | 111 | lockstat_provide(void *arg, dtrace_probedesc_t *desc) | |
103 | { | 112 | { | |
104 | int i = 0; | 113 | int i = 0; | |
105 | 114 | |||
106 | for (i = 0; lockstat_probes[i].lsp_func != NULL; i++) { | 115 | for (i = 0; lockstat_probes[i].lsp_func != NULL; i++) { | |
107 | lockstat_probe_t *probe = &lockstat_probes[i]; | 116 | lockstat_probe_t *probe = &lockstat_probes[i]; | |
108 | 117 | |||
109 | if (dtrace_probe_lookup(lockstat_id, __UNCONST("kernel"), | 118 | if (dtrace_probe_lookup(lockstat_id, __UNCONST("kernel"), | |
110 | __UNCONST(probe->lsp_func), __UNCONST(probe->lsp_name)) | 119 | __UNCONST(probe->lsp_func), __UNCONST(probe->lsp_name)) | |
@@ -139,48 +148,47 @@ static dtrace_pattr_t lockstat_attr = { | @@ -139,48 +148,47 @@ static dtrace_pattr_t lockstat_attr = { | |||
139 | static dtrace_pops_t lockstat_pops = { | 148 | static dtrace_pops_t lockstat_pops = { | |
140 | lockstat_provide, | 149 | lockstat_provide, | |
141 | NULL, | 150 | NULL, | |
142 | lockstat_enable, | 151 | lockstat_enable, | |
143 | lockstat_disable, | 152 | lockstat_disable, | |
144 | NULL, | 153 | NULL, | |
145 | NULL, | 154 | NULL, | |
146 | NULL, | 155 | NULL, | |
147 | NULL, | 156 | NULL, | |
148 | NULL, | 157 | NULL, | |
149 | lockstat_destroy | 158 | lockstat_destroy | |
150 | }; | 159 | }; | |
151 | 160 | |||
152 | static void | |||
153 | lockstat_barrier_xc(void *arg0 __unused, void *arg1 __unused) | |||
154 | { | |||
155 | ||||
156 | membar_consumer(); | |||
157 | } | |||
158 | ||||
159 | typedef void (*dtrace_probe_func_t)(dtrace_id_t, uintptr_t, uintptr_t, | 161 | typedef void (*dtrace_probe_func_t)(dtrace_id_t, uintptr_t, uintptr_t, | |
160 | uintptr_t, uintptr_t, uintptr_t); | 162 | uintptr_t, uintptr_t, uintptr_t); | |
161 | 163 | |||
162 | static bool | 164 | static bool | |
163 | lockstat_cas_probe(dtrace_probe_func_t old, dtrace_probe_func_t new) | 165 | lockstat_cas_probe(dtrace_probe_func_t old, dtrace_probe_func_t new) | |
164 | { | 166 | { | |
165 | 167 | |||
166 | ASSERT(kernconfig_is_held()); | 168 | ASSERT(kernconfig_is_held()); | |
167 | 169 | |||
168 | if (lockstat_probe_func != old) | 170 | if (lockstat_probe_func != old) | |
169 | return false; | 171 | return false; | |
170 | 172 | |||
171 | lockstat_probe_func = new; | 173 | lockstat_probe_func = new; | |
172 | membar_producer(); | 174 | ||
173 | xc_wait(xc_broadcast(0, lockstat_barrier_xc, NULL, NULL)); | 175 | /* | |
176 | * Make sure that the probe function is initialized on all CPUs | |||
177 | * before we enable the lockstat probe by setting | |||
178 | * lockstat_probemap[...]. | |||
179 | */ | |||
180 | xc_barrier(0); | |||
181 | ||||
174 | return true; | 182 | return true; | |
175 | } | 183 | } | |
176 | 184 | |||
177 | static int | 185 | static int | |
178 | lockstat_init(void) | 186 | lockstat_init(void) | |
179 | { | 187 | { | |
180 | int error; | 188 | int error; | |
181 | bool ok; | 189 | bool ok; | |
182 | 190 | |||
183 | /* Install the probe function. */ | 191 | /* Install the probe function. */ | |
184 | ok = lockstat_cas_probe(lockstat_probe_stub, dtrace_probe); | 192 | ok = lockstat_cas_probe(lockstat_probe_stub, dtrace_probe); | |
185 | if (!ok) { | 193 | if (!ok) { | |
186 | printf("dtrace_lockstat: lockstat probe already installed\n"); | 194 | printf("dtrace_lockstat: lockstat probe already installed\n"); |
--- src/sys/dev/lockstat.c 2020/05/23 23:42:42 1.27
+++ src/sys/dev/lockstat.c 2022/02/27 14:16:12 1.28
@@ -1,14 +1,14 @@ | @@ -1,14 +1,14 @@ | |||
1 | /* $NetBSD: lockstat.c,v 1.27 2020/05/23 23:42:42 ad Exp $ */ | 1 | /* $NetBSD: lockstat.c,v 1.28 2022/02/27 14:16:12 riastradh Exp $ */ | |
2 | 2 | |||
3 | /*- | 3 | /*- | |
4 | * Copyright (c) 2006, 2007, 2019 The NetBSD Foundation, Inc. | 4 | * Copyright (c) 2006, 2007, 2019 The NetBSD Foundation, Inc. | |
5 | * All rights reserved. | 5 | * All rights reserved. | |
6 | * | 6 | * | |
7 | * This code is derived from software contributed to The NetBSD Foundation | 7 | * This code is derived from software contributed to The NetBSD Foundation | |
8 | * by Andrew Doran. | 8 | * by Andrew Doran. | |
9 | * | 9 | * | |
10 | * Redistribution and use in source and binary forms, with or without | 10 | * Redistribution and use in source and binary forms, with or without | |
11 | * modification, are permitted provided that the following conditions | 11 | * modification, are permitted provided that the following conditions | |
12 | * are met: | 12 | * are met: | |
13 | * 1. Redistributions of source code must retain the above copyright | 13 | * 1. Redistributions of source code must retain the above copyright | |
14 | * notice, this list of conditions and the following disclaimer. | 14 | * notice, this list of conditions and the following disclaimer. | |
@@ -31,39 +31,40 @@ | @@ -31,39 +31,40 @@ | |||
31 | 31 | |||
32 | /* | 32 | /* | |
33 | * Lock statistics driver, providing kernel support for the lockstat(8) | 33 | * Lock statistics driver, providing kernel support for the lockstat(8) | |
34 | * command. | 34 | * command. | |
35 | * | 35 | * | |
36 | * We use a global lock word (lockstat_lock) to track device opens. | 36 | * We use a global lock word (lockstat_lock) to track device opens. | |
37 | * Only one thread can hold the device at a time, providing a global lock. | 37 | * Only one thread can hold the device at a time, providing a global lock. | |
38 | * | 38 | * | |
39 | * XXX Timings for contention on sleep locks are currently incorrect. | 39 | * XXX Timings for contention on sleep locks are currently incorrect. | |
40 | * XXX Convert this to use timecounters! | 40 | * XXX Convert this to use timecounters! | |
41 | */ | 41 | */ | |
42 | 42 | |||
43 | #include <sys/cdefs.h> | 43 | #include <sys/cdefs.h> | |
44 | __KERNEL_RCSID(0, "$NetBSD: lockstat.c,v 1.27 2020/05/23 23:42:42 ad Exp $"); | 44 | __KERNEL_RCSID(0, "$NetBSD: lockstat.c,v 1.28 2022/02/27 14:16:12 riastradh Exp $"); | |
45 | 45 | |||
46 | #include <sys/types.h> | 46 | #include <sys/types.h> | |
47 | #include <sys/param.h> | 47 | #include <sys/param.h> | |
48 | #include <sys/proc.h> | 48 | #include <sys/proc.h> | |
49 | #include <sys/resourcevar.h> | 49 | #include <sys/resourcevar.h> | |
50 | #include <sys/systm.h> | 50 | #include <sys/systm.h> | |
51 | #include <sys/kernel.h> | 51 | #include <sys/kernel.h> | |
52 | #include <sys/kmem.h> | 52 | #include <sys/kmem.h> | |
53 | #include <sys/conf.h> | 53 | #include <sys/conf.h> | |
54 | #include <sys/cpu.h> | 54 | #include <sys/cpu.h> | |
55 | #include <sys/syslog.h> | 55 | #include <sys/syslog.h> | |
56 | #include <sys/atomic.h> | 56 | #include <sys/atomic.h> | |
57 | #include <sys/xcall.h> | |||
57 | 58 | |||
58 | #include <dev/lockstat.h> | 59 | #include <dev/lockstat.h> | |
59 | 60 | |||
60 | #include <machine/lock.h> | 61 | #include <machine/lock.h> | |
61 | 62 | |||
62 | #include "ioconf.h" | 63 | #include "ioconf.h" | |
63 | 64 | |||
64 | #ifndef __HAVE_CPU_COUNTER | 65 | #ifndef __HAVE_CPU_COUNTER | |
65 | #error CPU counters not available | 66 | #error CPU counters not available | |
66 | #endif | 67 | #endif | |
67 | 68 | |||
68 | #if LONG_BIT == 64 | 69 | #if LONG_BIT == 64 | |
69 | #define LOCKSTAT_HASH_SHIFT 3 | 70 | #define LOCKSTAT_HASH_SHIFT 3 | |
@@ -91,26 +92,27 @@ typedef struct lslist lslist_t; | @@ -91,26 +92,27 @@ typedef struct lslist lslist_t; | |||
91 | void lockstat_start(lsenable_t *); | 92 | void lockstat_start(lsenable_t *); | |
92 | int lockstat_alloc(lsenable_t *); | 93 | int lockstat_alloc(lsenable_t *); | |
93 | void lockstat_init_tables(lsenable_t *); | 94 | void lockstat_init_tables(lsenable_t *); | |
94 | int lockstat_stop(lsdisable_t *); | 95 | int lockstat_stop(lsdisable_t *); | |
95 | void lockstat_free(void); | 96 | void lockstat_free(void); | |
96 | 97 | |||
97 | dev_type_open(lockstat_open); | 98 | dev_type_open(lockstat_open); | |
98 | dev_type_close(lockstat_close); | 99 | dev_type_close(lockstat_close); | |
99 | dev_type_read(lockstat_read); | 100 | dev_type_read(lockstat_read); | |
100 | dev_type_ioctl(lockstat_ioctl); | 101 | dev_type_ioctl(lockstat_ioctl); | |
101 | 102 | |||
102 | volatile u_int lockstat_enabled; | 103 | volatile u_int lockstat_enabled; | |
103 | volatile u_int lockstat_dev_enabled; | 104 | volatile u_int lockstat_dev_enabled; | |
105 | __cpu_simple_lock_t lockstat_enabled_lock; | |||
104 | uintptr_t lockstat_csstart; | 106 | uintptr_t lockstat_csstart; | |
105 | uintptr_t lockstat_csend; | 107 | uintptr_t lockstat_csend; | |
106 | uintptr_t lockstat_csmask; | 108 | uintptr_t lockstat_csmask; | |
107 | uintptr_t lockstat_lamask; | 109 | uintptr_t lockstat_lamask; | |
108 | uintptr_t lockstat_lockstart; | 110 | uintptr_t lockstat_lockstart; | |
109 | uintptr_t lockstat_lockend; | 111 | uintptr_t lockstat_lockend; | |
110 | __cpu_simple_lock_t lockstat_lock; | 112 | __cpu_simple_lock_t lockstat_lock; | |
111 | lwp_t *lockstat_lwp; | 113 | lwp_t *lockstat_lwp; | |
112 | lsbuf_t *lockstat_baseb; | 114 | lsbuf_t *lockstat_baseb; | |
113 | size_t lockstat_sizeb; | 115 | size_t lockstat_sizeb; | |
114 | int lockstat_busy; | 116 | int lockstat_busy; | |
115 | struct timespec lockstat_stime; | 117 | struct timespec lockstat_stime; | |
116 | 118 | |||
@@ -144,26 +146,27 @@ const struct cdevsw lockstat_cdevsw = { | @@ -144,26 +146,27 @@ const struct cdevsw lockstat_cdevsw = { | |||
144 | .d_flag = D_OTHER | D_MPSAFE | 146 | .d_flag = D_OTHER | D_MPSAFE | |
145 | }; | 147 | }; | |
146 | 148 | |||
147 | /* | 149 | /* | |
148 | * Called when the pseudo-driver is attached. | 150 | * Called when the pseudo-driver is attached. | |
149 | */ | 151 | */ | |
150 | void | 152 | void | |
151 | lockstatattach(int nunits) | 153 | lockstatattach(int nunits) | |
152 | { | 154 | { | |
153 | 155 | |||
154 | (void)nunits; | 156 | (void)nunits; | |
155 | 157 | |||
156 | __cpu_simple_lock_init(&lockstat_lock); | 158 | __cpu_simple_lock_init(&lockstat_lock); | |
159 | __cpu_simple_lock_init(&lockstat_enabled_lock); | |||
157 | } | 160 | } | |
158 | 161 | |||
159 | /* | 162 | /* | |
160 | * Prepare the per-CPU tables for use, or clear down tables when tracing is | 163 | * Prepare the per-CPU tables for use, or clear down tables when tracing is | |
161 | * stopped. | 164 | * stopped. | |
162 | */ | 165 | */ | |
163 | void | 166 | void | |
164 | lockstat_init_tables(lsenable_t *le) | 167 | lockstat_init_tables(lsenable_t *le) | |
165 | { | 168 | { | |
166 | int i, per, slop, cpuno; | 169 | int i, per, slop, cpuno; | |
167 | CPU_INFO_ITERATOR cii; | 170 | CPU_INFO_ITERATOR cii; | |
168 | struct cpu_info *ci; | 171 | struct cpu_info *ci; | |
169 | lscpu_t *lc; | 172 | lscpu_t *lc; | |
@@ -225,56 +228,72 @@ lockstat_start(lsenable_t *le) | @@ -225,56 +228,72 @@ lockstat_start(lsenable_t *le) | |||
225 | else | 228 | else | |
226 | lockstat_csmask = 0; | 229 | lockstat_csmask = 0; | |
227 | 230 | |||
228 | if ((le->le_flags & LE_LOCK) != 0) | 231 | if ((le->le_flags & LE_LOCK) != 0) | |
229 | lockstat_lamask = (uintptr_t)-1LL; | 232 | lockstat_lamask = (uintptr_t)-1LL; | |
230 | else | 233 | else | |
231 | lockstat_lamask = 0; | 234 | lockstat_lamask = 0; | |
232 | 235 | |||
233 | lockstat_csstart = le->le_csstart; | 236 | lockstat_csstart = le->le_csstart; | |
234 | lockstat_csend = le->le_csend; | 237 | lockstat_csend = le->le_csend; | |
235 | lockstat_lockstart = le->le_lockstart; | 238 | lockstat_lockstart = le->le_lockstart; | |
236 | lockstat_lockstart = le->le_lockstart; | 239 | lockstat_lockstart = le->le_lockstart; | |
237 | lockstat_lockend = le->le_lockend; | 240 | lockstat_lockend = le->le_lockend; | |
238 | membar_sync(); | 241 | ||
242 | /* | |||
243 | * Ensure everything is initialized on all CPUs, by issuing a | |||
244 | * null xcall with the side effect of a release barrier on this | |||
245 | * CPU and an acquire barrier on all other CPUs, before they | |||
246 | * can witness any flags set in lockstat_dev_enabled -- this | |||
247 | * way we don't need to add any barriers in lockstat_event. | |||
248 | */ | |||
249 | xc_barrier(0); | |||
250 | ||||
251 | /* | |||
252 | * Start timing after the xcall, so we don't spuriously count | |||
253 | * xcall communication time, but before flipping the switch, so | |||
254 | * we don't dirty sample with locks taken in the timecounter. | |||
255 | */ | |||
239 | getnanotime(&lockstat_stime); | 256 | getnanotime(&lockstat_stime); | |
240 | lockstat_dev_enabled = le->le_mask; | 257 | ||
241 | LOCKSTAT_ENABLED_UPDATE(); | 258 | LOCKSTAT_ENABLED_UPDATE_BEGIN(); | |
259 | atomic_store_relaxed(&lockstat_dev_enabled, le->le_mask); | |||
260 | LOCKSTAT_ENABLED_UPDATE_END(); | |||
242 | } | 261 | } | |
243 | 262 | |||
244 | /* | 263 | /* | |
245 | * Stop collecting lock statistics. | 264 | * Stop collecting lock statistics. | |
246 | */ | 265 | */ | |
247 | int | 266 | int | |
248 | lockstat_stop(lsdisable_t *ld) | 267 | lockstat_stop(lsdisable_t *ld) | |
249 | { | 268 | { | |
250 | CPU_INFO_ITERATOR cii; | 269 | CPU_INFO_ITERATOR cii; | |
251 | struct cpu_info *ci; | 270 | struct cpu_info *ci; | |
252 | u_int cpuno, overflow; | 271 | u_int cpuno, overflow; | |
253 | struct timespec ts; | 272 | struct timespec ts; | |
254 | int error; | 273 | int error; | |
255 | lwp_t *l; | 274 | lwp_t *l; | |
256 | 275 | |||
257 | /* coverity[assert_side_effect] */ | 276 | /* coverity[assert_side_effect] */ | |
258 | KASSERT(lockstat_dev_enabled); | 277 | KASSERT(lockstat_dev_enabled); | |
259 | 278 | |||
260 | /* | 279 | /* | |
261 | * Set enabled false, force a write barrier, and wait for other CPUs | 280 | * Disable and wait for other CPUs to exit lockstat_event(). | |
262 | * to exit lockstat_event(). | |||
263 | */ | 281 | */ | |
264 | lockstat_dev_enabled = 0; | 282 | LOCKSTAT_ENABLED_UPDATE_BEGIN(); | |
265 | LOCKSTAT_ENABLED_UPDATE(); | 283 | atomic_store_relaxed(&lockstat_dev_enabled, 0); | |
284 | LOCKSTAT_ENABLED_UPDATE_END(); | |||
266 | getnanotime(&ts); | 285 | getnanotime(&ts); | |
267 | tsleep(&lockstat_stop, PPAUSE, "lockstat", mstohz(10)); | 286 | xc_barrier(0); | |
268 | 287 | |||
269 | /* | 288 | /* | |
270 | * Did we run out of buffers while tracing? | 289 | * Did we run out of buffers while tracing? | |
271 | */ | 290 | */ | |
272 | overflow = 0; | 291 | overflow = 0; | |
273 | for (CPU_INFO_FOREACH(cii, ci)) | 292 | for (CPU_INFO_FOREACH(cii, ci)) | |
274 | overflow += ((lscpu_t *)ci->ci_lockstat)->lc_overflow; | 293 | overflow += ((lscpu_t *)ci->ci_lockstat)->lc_overflow; | |
275 | 294 | |||
276 | if (overflow != 0) { | 295 | if (overflow != 0) { | |
277 | error = EOVERFLOW; | 296 | error = EOVERFLOW; | |
278 | log(LOG_NOTICE, "lockstat: %d buffer allocations failed\n", | 297 | log(LOG_NOTICE, "lockstat: %d buffer allocations failed\n", | |
279 | overflow); | 298 | overflow); | |
280 | } else | 299 | } else | |
@@ -360,32 +379,34 @@ lockstat_free(void) | @@ -360,32 +379,34 @@ lockstat_free(void) | |||
360 | void | 379 | void | |
361 | lockstat_event(uintptr_t lock, uintptr_t callsite, u_int flags, u_int count, | 380 | lockstat_event(uintptr_t lock, uintptr_t callsite, u_int flags, u_int count, | |
362 | uint64_t cycles) | 381 | uint64_t cycles) | |
363 | { | 382 | { | |
364 | lslist_t *ll; | 383 | lslist_t *ll; | |
365 | lscpu_t *lc; | 384 | lscpu_t *lc; | |
366 | lsbuf_t *lb; | 385 | lsbuf_t *lb; | |
367 | u_int event; | 386 | u_int event; | |
368 | int s; | 387 | int s; | |
369 | 388 | |||
370 | #ifdef KDTRACE_HOOKS | 389 | #ifdef KDTRACE_HOOKS | |
371 | uint32_t id; | 390 | uint32_t id; | |
372 | CTASSERT((LS_NPROBES & (LS_NPROBES - 1)) == 0); | 391 | CTASSERT((LS_NPROBES & (LS_NPROBES - 1)) == 0); | |
373 | if ((id = lockstat_probemap[LS_COMPRESS(flags)]) != 0) | 392 | if ((id = atomic_load_relaxed(&lockstat_probemap[LS_COMPRESS(flags)])) | |
393 | != 0) | |||
374 | (*lockstat_probe_func)(id, lock, callsite, flags, count, | 394 | (*lockstat_probe_func)(id, lock, callsite, flags, count, | |
375 | cycles); | 395 | cycles); | |
376 | #endif | 396 | #endif | |
377 | 397 | |||
378 | if ((flags & lockstat_dev_enabled) != flags || count == 0) | 398 | if ((flags & atomic_load_relaxed(&lockstat_dev_enabled)) != flags || | |
399 | count == 0) | |||
379 | return; | 400 | return; | |
380 | if (lock < lockstat_lockstart || lock > lockstat_lockend) | 401 | if (lock < lockstat_lockstart || lock > lockstat_lockend) | |
381 | return; | 402 | return; | |
382 | if (callsite < lockstat_csstart || callsite > lockstat_csend) | 403 | if (callsite < lockstat_csstart || callsite > lockstat_csend) | |
383 | return; | 404 | return; | |
384 | 405 | |||
385 | callsite &= lockstat_csmask; | 406 | callsite &= lockstat_csmask; | |
386 | lock &= lockstat_lamask; | 407 | lock &= lockstat_lamask; | |
387 | 408 | |||
388 | /* | 409 | /* | |
389 | * Find the table for this lock+callsite pair, and try to locate a | 410 | * Find the table for this lock+callsite pair, and try to locate a | |
390 | * buffer with the same key. | 411 | * buffer with the same key. | |
391 | */ | 412 | */ | |
@@ -477,27 +498,27 @@ lockstat_ioctl(dev_t dev, u_long cmd, vo | @@ -477,27 +498,27 @@ lockstat_ioctl(dev_t dev, u_long cmd, vo | |||
477 | switch (cmd) { | 498 | switch (cmd) { | |
478 | case IOC_LOCKSTAT_GVERSION: | 499 | case IOC_LOCKSTAT_GVERSION: | |
479 | *(int *)data = LS_VERSION; | 500 | *(int *)data = LS_VERSION; | |
480 | error = 0; | 501 | error = 0; | |
481 | break; | 502 | break; | |
482 | 503 | |||
483 | case IOC_LOCKSTAT_ENABLE: | 504 | case IOC_LOCKSTAT_ENABLE: | |
484 | le = (lsenable_t *)data; | 505 | le = (lsenable_t *)data; | |
485 | 506 | |||
486 | if (!cpu_hascounter()) { | 507 | if (!cpu_hascounter()) { | |
487 | error = ENODEV; | 508 | error = ENODEV; | |
488 | break; | 509 | break; | |
489 | } | 510 | } | |
490 | if (lockstat_dev_enabled) { | 511 | if (atomic_load_relaxed(&lockstat_dev_enabled)) { | |
491 | error = EBUSY; | 512 | error = EBUSY; | |
492 | break; | 513 | break; | |
493 | } | 514 | } | |
494 | 515 | |||
495 | /* | 516 | /* | |
496 | * Sanitize the arguments passed in and set up filtering. | 517 | * Sanitize the arguments passed in and set up filtering. | |
497 | */ | 518 | */ | |
498 | if (le->le_nbufs == 0) { | 519 | if (le->le_nbufs == 0) { | |
499 | le->le_nbufs = MIN(LOCKSTAT_DEFBUFS * ncpu, | 520 | le->le_nbufs = MIN(LOCKSTAT_DEFBUFS * ncpu, | |
500 | LOCKSTAT_MAXBUFS); | 521 | LOCKSTAT_MAXBUFS); | |
501 | } else if (le->le_nbufs > LOCKSTAT_MAXBUFS || | 522 | } else if (le->le_nbufs > LOCKSTAT_MAXBUFS || | |
502 | le->le_nbufs < LOCKSTAT_MINBUFS) { | 523 | le->le_nbufs < LOCKSTAT_MINBUFS) { | |
503 | error = EINVAL; | 524 | error = EINVAL; | |
@@ -514,27 +535,27 @@ lockstat_ioctl(dev_t dev, u_long cmd, vo | @@ -514,27 +535,27 @@ lockstat_ioctl(dev_t dev, u_long cmd, vo | |||
514 | if ((le->le_mask & LB_EVENT_MASK) == 0) | 535 | if ((le->le_mask & LB_EVENT_MASK) == 0) | |
515 | return EINVAL; | 536 | return EINVAL; | |
516 | if ((le->le_mask & LB_LOCK_MASK) == 0) | 537 | if ((le->le_mask & LB_LOCK_MASK) == 0) | |
517 | return EINVAL; | 538 | return EINVAL; | |
518 | 539 | |||
519 | /* | 540 | /* | |
520 | * Start tracing. | 541 | * Start tracing. | |
521 | */ | 542 | */ | |
522 | if ((error = lockstat_alloc(le)) == 0) | 543 | if ((error = lockstat_alloc(le)) == 0) | |
523 | lockstat_start(le); | 544 | lockstat_start(le); | |
524 | break; | 545 | break; | |
525 | 546 | |||
526 | case IOC_LOCKSTAT_DISABLE: | 547 | case IOC_LOCKSTAT_DISABLE: | |
527 | if (!lockstat_dev_enabled) | 548 | if (!atomic_load_relaxed(&lockstat_dev_enabled)) | |
528 | error = EINVAL; | 549 | error = EINVAL; | |
529 | else | 550 | else | |
530 | error = lockstat_stop((lsdisable_t *)data); | 551 | error = lockstat_stop((lsdisable_t *)data); | |
531 | break; | 552 | break; | |
532 | 553 | |||
533 | default: | 554 | default: | |
534 | error = ENOTTY; | 555 | error = ENOTTY; | |
535 | break; | 556 | break; | |
536 | } | 557 | } | |
537 | 558 | |||
538 | return error; | 559 | return error; | |
539 | } | 560 | } | |
540 | 561 |
--- src/sys/dev/lockstat.h 2016/01/24 01:01:11 1.14
+++ src/sys/dev/lockstat.h 2022/02/27 14:16:12 1.15
@@ -1,14 +1,14 @@ | @@ -1,14 +1,14 @@ | |||
1 | /* $NetBSD: lockstat.h,v 1.14 2016/01/24 01:01:11 christos Exp $ */ | 1 | /* $NetBSD: lockstat.h,v 1.15 2022/02/27 14:16:12 riastradh Exp $ */ | |
2 | 2 | |||
3 | /*- | 3 | /*- | |
4 | * Copyright (c) 2006 The NetBSD Foundation, Inc. | 4 | * Copyright (c) 2006 The NetBSD Foundation, Inc. | |
5 | * All rights reserved. | 5 | * All rights reserved. | |
6 | * | 6 | * | |
7 | * This code is derived from software contributed to The NetBSD Foundation | 7 | * This code is derived from software contributed to The NetBSD Foundation | |
8 | * by Andrew Doran. | 8 | * by Andrew Doran. | |
9 | * | 9 | * | |
10 | * Redistribution and use in source and binary forms, with or without | 10 | * Redistribution and use in source and binary forms, with or without | |
11 | * modification, are permitted provided that the following conditions | 11 | * modification, are permitted provided that the following conditions | |
12 | * are met: | 12 | * are met: | |
13 | * 1. Redistributions of source code must retain the above copyright | 13 | * 1. Redistributions of source code must retain the above copyright | |
14 | * notice, this list of conditions and the following disclaimer. | 14 | * notice, this list of conditions and the following disclaimer. | |
@@ -28,27 +28,29 @@ | @@ -28,27 +28,29 @@ | |||
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
29 | * POSSIBILITY OF SUCH DAMAGE. | 29 | * POSSIBILITY OF SUCH DAMAGE. | |
30 | */ | 30 | */ | |
31 | 31 | |||
32 | #ifndef _SYS_LOCKSTAT_H_ | 32 | #ifndef _SYS_LOCKSTAT_H_ | |
33 | #define _SYS_LOCKSTAT_H_ | 33 | #define _SYS_LOCKSTAT_H_ | |
34 | 34 | |||
35 | #ifdef _KERNEL_OPT | 35 | #ifdef _KERNEL_OPT | |
36 | #include "opt_dtrace.h" | 36 | #include "opt_dtrace.h" | |
37 | #include <lockstat.h> | 37 | #include <lockstat.h> | |
38 | #endif | 38 | #endif | |
39 | 39 | |||
40 | #include <sys/types.h> | 40 | #include <sys/types.h> | |
41 | ||||
41 | #include <sys/ioccom.h> | 42 | #include <sys/ioccom.h> | |
43 | #include <sys/lock.h> | |||
42 | #include <sys/queue.h> | 44 | #include <sys/queue.h> | |
43 | #include <sys/time.h> | 45 | #include <sys/time.h> | |
44 | 46 | |||
45 | #if defined(_KERNEL) && defined(__HAVE_CPU_COUNTER) | 47 | #if defined(_KERNEL) && defined(__HAVE_CPU_COUNTER) | |
46 | #include <machine/cpu_counter.h> | 48 | #include <machine/cpu_counter.h> | |
47 | #endif | 49 | #endif | |
48 | 50 | |||
49 | /* | 51 | /* | |
50 | * Interface version. The interface is not designed to provide | 52 | * Interface version. The interface is not designed to provide | |
51 | * compatibility across NetBSD releases. | 53 | * compatibility across NetBSD releases. | |
52 | */ | 54 | */ | |
53 | 55 | |||
54 | #define IOC_LOCKSTAT_GVERSION _IOR('L', 0, int) | 56 | #define IOC_LOCKSTAT_GVERSION _IOR('L', 0, int) | |
@@ -149,27 +151,27 @@ do { \ | @@ -149,27 +151,27 @@ do { \ | |||
149 | (type), (count), (time)); \ | 151 | (type), (count), (time)); \ | |
150 | } while (/* CONSTCOND */ 0); | 152 | } while (/* CONSTCOND */ 0); | |
151 | 153 | |||
152 | #define LOCKSTAT_EVENT_RA(flag, lock, type, count, time, ra) \ | 154 | #define LOCKSTAT_EVENT_RA(flag, lock, type, count, time, ra) \ | |
153 | do { \ | 155 | do { \ | |
154 | if (__predict_false(flag)) \ | 156 | if (__predict_false(flag)) \ | |
155 | lockstat_event((uintptr_t)(lock), (uintptr_t)ra, \ | 157 | lockstat_event((uintptr_t)(lock), (uintptr_t)ra, \ | |
156 | (type), (count), (time)); \ | 158 | (type), (count), (time)); \ | |
157 | } while (/* CONSTCOND */ 0); | 159 | } while (/* CONSTCOND */ 0); | |
158 | 160 | |||
159 | #define LOCKSTAT_TIMER(name) uint64_t name = 0 | 161 | #define LOCKSTAT_TIMER(name) uint64_t name = 0 | |
160 | #define LOCKSTAT_COUNTER(name) uint64_t name = 0 | 162 | #define LOCKSTAT_COUNTER(name) uint64_t name = 0 | |
161 | #define LOCKSTAT_FLAG(name) int name | 163 | #define LOCKSTAT_FLAG(name) int name | |
162 | #define LOCKSTAT_ENTER(name) name = lockstat_enabled | 164 | #define LOCKSTAT_ENTER(name) name = atomic_load_relaxed(&lockstat_enabled) | |
163 | #define LOCKSTAT_EXIT(name) | 165 | #define LOCKSTAT_EXIT(name) | |
164 | 166 | |||
165 | #define LOCKSTAT_START_TIMER(flag, name) \ | 167 | #define LOCKSTAT_START_TIMER(flag, name) \ | |
166 | do { \ | 168 | do { \ | |
167 | if (__predict_false(flag)) \ | 169 | if (__predict_false(flag)) \ | |
168 | (name) -= cpu_counter(); \ | 170 | (name) -= cpu_counter(); \ | |
169 | } while (/* CONSTCOND */ 0) | 171 | } while (/* CONSTCOND */ 0) | |
170 | 172 | |||
171 | #define LOCKSTAT_STOP_TIMER(flag, name) \ | 173 | #define LOCKSTAT_STOP_TIMER(flag, name) \ | |
172 | do { \ | 174 | do { \ | |
173 | if (__predict_false(flag)) \ | 175 | if (__predict_false(flag)) \ | |
174 | (name) += cpu_counter(); \ | 176 | (name) += cpu_counter(); \ | |
175 | } while (/* CONSTCOND */ 0) | 177 | } while (/* CONSTCOND */ 0) | |
@@ -204,23 +206,32 @@ extern volatile u_int lockstat_dtrace_en | @@ -204,23 +206,32 @@ extern volatile u_int lockstat_dtrace_en | |||
204 | #define LS_NPROBES 0x20 /* 5 bits */ | 206 | #define LS_NPROBES 0x20 /* 5 bits */ | |
205 | 207 | |||
206 | extern uint32_t lockstat_probemap[]; | 208 | extern uint32_t lockstat_probemap[]; | |
207 | extern void (*lockstat_probe_func)(uint32_t, uintptr_t, uintptr_t, | 209 | extern void (*lockstat_probe_func)(uint32_t, uintptr_t, uintptr_t, | |
208 | uintptr_t, uintptr_t, uintptr_t); | 210 | uintptr_t, uintptr_t, uintptr_t); | |
209 | 211 | |||
210 | void lockstat_probe_stub(uint32_t, uintptr_t, uintptr_t, | 212 | void lockstat_probe_stub(uint32_t, uintptr_t, uintptr_t, | |
211 | uintptr_t, uintptr_t, uintptr_t); | 213 | uintptr_t, uintptr_t, uintptr_t); | |
212 | #else | 214 | #else | |
213 | #define KDTRACE_LOCKSTAT_ENABLED 0 | 215 | #define KDTRACE_LOCKSTAT_ENABLED 0 | |
214 | #endif | 216 | #endif | |
215 | 217 | |||
216 | #if defined(_KERNEL) && NLOCKSTAT > 0 | 218 | #if defined(_KERNEL) && NLOCKSTAT > 0 | |
219 | extern __cpu_simple_lock_t lockstat_enabled_lock; | |||
217 | extern volatile u_int lockstat_enabled; | 220 | extern volatile u_int lockstat_enabled; | |
218 | extern volatile u_int lockstat_dev_enabled; | 221 | extern volatile u_int lockstat_dev_enabled; | |
219 | 222 | |||
220 | #define LOCKSTAT_ENABLED_UPDATE() do { \ | 223 | #define LOCKSTAT_ENABLED_UPDATE_BEGIN() do \ | |
221 | lockstat_enabled = lockstat_dev_enabled | KDTRACE_LOCKSTAT_ENABLED; \ | 224 | { \ | |
222 | membar_producer(); \ | 225 | __cpu_simple_lock(&lockstat_enabled_lock); \ | |
223 | } while (/*CONSTCOND*/0) | 226 | } while (/*CONSTCOND*/0) | |
227 | ||||
228 | #define LOCKSTAT_ENABLED_UPDATE_END() do \ | |||
229 | { \ | |||
230 | atomic_store_relaxed(&lockstat_enabled, \ | |||
231 | lockstat_dev_enabled | KDTRACE_LOCKSTAT_ENABLED); \ | |||
232 | __cpu_simple_unlock(&lockstat_enabled_lock); \ | |||
233 | } while (/*CONSTCOND*/0) | |||
234 | ||||
224 | #endif | 235 | #endif | |
225 | 236 | |||
226 | #endif /* _SYS_LOCKSTAT_H_ */ | 237 | #endif /* _SYS_LOCKSTAT_H_ */ |