Skip to content

Commit e23d136

Browse files
committed
dtrace: Removal of XCalls from dtrace_sync()
Replaces synchronization mechanism in the framework with lock-free algorithm. Orabug: 26671843 Signed-off-by: Tomas Jedlicka <[email protected]> Reviewed-by: Nick Alcock <[email protected]>
1 parent 78ac89e commit e23d136

File tree

8 files changed

+430
-18
lines changed

8 files changed

+430
-18
lines changed

arch/sparc/kernel/fbt_blacklist.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,9 @@ BL_DENTRY(void *, gup_pud_range)
2828
BL_DENTRY(void *, gup_pmd_range)
2929
BL_DENTRY(void *, gup_huge_pmd)
3030
BL_DENTRY(void *, gup_pte_range)
31+
32+
/*
33+
* Functions used in dtrace_sync().
34+
*/
35+
BL_DENTRY(void *, find_next_bit)
36+
BL_DENTRY(void *, _find_next_bit)

arch/x86/kernel/fbt_blacklist.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,9 @@ BL_DENTRY(void *, gup_pmd_range)
6666
BL_DENTRY(void *, gup_huge_pmd)
6767
BL_DENTRY(void *, gup_pte_range)
6868
BL_DENTRY(void *, pte_mfn_to_pfn)
69+
70+
/*
71+
* Functions used in dtrace_sync().
72+
*/
73+
BL_DENTRY(void *, find_next_bit)
74+
BL_DENTRY(void *, _find_next_bit)

dtrace/dtrace_isa.c

Lines changed: 124 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <linux/mm.h>
2121
#include <linux/smp.h>
2222
#include <linux/uaccess.h>
23+
#include <linux/cpumask.h>
2324
#include <asm/cacheflush.h>
2425
#include <asm/ptrace.h>
2526
#include <asm/stacktrace.h>
@@ -36,10 +37,6 @@ int dtrace_getipl(void)
3637
return in_interrupt();
3738
}
3839

39-
static void dtrace_sync_func(void)
40-
{
41-
}
42-
4340
void dtrace_xcall(processorid_t cpu, dtrace_xcall_t func, void *arg)
4441
{
4542
if (cpu == DTRACE_CPUALL) {
@@ -48,14 +45,133 @@ void dtrace_xcall(processorid_t cpu, dtrace_xcall_t func, void *arg)
4845
smp_call_function_single(cpu, func, arg, 1);
4946
}
5047

51-
void dtrace_sync(void)
48+
void dtrace_toxic_ranges(void (*func)(uintptr_t, uintptr_t))
5249
{
53-
dtrace_xcall(DTRACE_CPUALL, (dtrace_xcall_t)dtrace_sync_func, NULL);
50+
/* FIXME */
5451
}
5552

56-
void dtrace_toxic_ranges(void (*func)(uintptr_t, uintptr_t))
53+
/*
54+
* Note: not called from probe context. This function is called
55+
* asynchronously (and at a regular interval) from outside of probe context
56+
* by the DTrace framework to sync shared data which DTrace probe context
57+
* may access without locks.
58+
*
59+
* Whenever the framework updates data which can be accessed from probe context,
60+
* the framework then calls dtrace_sync(). dtrace_sync() guarantees all probes
61+
* are using the new data before returning.
62+
*
63+
* See the comment in dtrace_impl.h which describes this algorithm.
64+
* The cpuc_in_probe_ctxt flag is an increasing 16-bit count. It is odd when
65+
* in DTrace probe context and even when not in DTrace probe context.
66+
* The upper 15 bits are a counter which are incremented when exiting DTrace
67+
* probe context. These upper 15 bits are used to detect "sample aliasing":
68+
* i.e. the target CPU is not in DTrace probe context between samples but
69+
* continually enters probe context just before being sampled.
70+
*
71+
* dtrace_sync() loops over NCPUs. CPUs which are not in DTrace probe context
72+
* (cpuc_in_probe_ctxt is even) are removed from the list. This is repeated
73+
* until there are no CPUs left in the sync list.
74+
*
75+
* In the rare cases where dtrace_sync() loops over all NCPUs more than
76+
* dtrace_sync_sample_count times, dtrace_sync() then spins on one CPU's
77+
* cpuc_in_probe_ctxt count until the count increments. This is intended to
78+
* avoid sample aliasing.
79+
*/
80+
void dtrace_sync(void)
5781
{
58-
/* FIXME */
82+
/*
83+
* sync_cpus is a bitmap of CPUs that need to be synced with.
84+
*/
85+
cpumask_t sync_cpus;
86+
uint64_t sample_count = 0;
87+
int cpuid, sample_cpuid;
88+
int outstanding;
89+
90+
/*
91+
* Create bitmap of CPUs that need to be synced with.
92+
*/
93+
cpumask_copy(&sync_cpus, cpu_online_mask);
94+
outstanding = 0;
95+
for_each_cpu(cpuid, &sync_cpus) {
96+
++outstanding;
97+
98+
/*
99+
* Set a flag to let the CPU know we are syncing with it.
100+
*/
101+
DTRACE_SYNC_START(cpuid);
102+
}
103+
104+
/*
105+
* The preceding stores by DTRACE_SYNC_START() must complete before
106+
* subsequent loads or stores. No membar is needed because the
107+
* atomic-add operation in DTRACE_SYNC_START is a memory barrier on
108+
* SPARC and X86.
109+
*/
110+
111+
while (outstanding > 0) {
112+
/*
113+
* Loop over the map of CPUs that need to be synced with.
114+
*/
115+
for_each_cpu(cpuid, &sync_cpus) {
116+
if (!DTRACE_SYNC_IN_CRITICAL(cpuid)) {
117+
118+
/* Clear the CPU's sync request flag */
119+
DTRACE_SYNC_END(cpuid);
120+
121+
/*
122+
* remove cpuid from list of CPUs that
123+
* still need to be synced with.
124+
*/
125+
DTRACE_SYNC_DONE(cpuid, &sync_cpus);
126+
--outstanding;
127+
} else {
128+
/*
129+
* Remember one of the outstanding CPUs to spin
130+
* on once we reach the sampling limit.
131+
*/
132+
sample_cpuid = cpuid;
133+
}
134+
}
135+
136+
/*
137+
* dtrace_probe may be running in sibling threads in this core.
138+
*/
139+
if (outstanding > 0) {
140+
dtrace_safe_smt_pause();
141+
142+
/*
143+
* After sample_count loops, spin on one CPU's count
144+
* instead of just checking for odd/even.
145+
*/
146+
if (++sample_count > dtrace_sync_sample_count) {
147+
uint64_t count =
148+
DTRACE_SYNC_CRITICAL_COUNT(sample_cpuid);
149+
150+
/*
151+
* Spin until critical section count increments.
152+
*/
153+
if (DTRACE_SYNC_IN_CRITICAL(sample_cpuid)) {
154+
while (count ==
155+
DTRACE_SYNC_CRITICAL_COUNT(
156+
sample_cpuid)) {
157+
158+
dtrace_safe_smt_pause();
159+
}
160+
}
161+
162+
DTRACE_SYNC_END(sample_cpuid);
163+
DTRACE_SYNC_DONE(sample_cpuid, &sync_cpus);
164+
--outstanding;
165+
}
166+
}
167+
}
168+
169+
/*
170+
* All preceding loads by DTRACE_SYNC_IN_CRITICAL() and
171+
* DTRACE_SYNC_CRITICAL_COUNT() must complete before subsequent loads
172+
* or stores. No membar is needed because the atomic-add operation in
173+
* DTRACE_SYNC_END() is a memory barrier on SPARC and X86.
174+
*/
59175
}
60176

61177
/*

dtrace/dtrace_probe.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,7 @@ void dtrace_probe(dtrace_id_t id, uintptr_t arg0, uintptr_t arg1,
515515
int onintr;
516516
volatile uint16_t *flags;
517517
int pflag = 0;
518+
uint32_t re_entry;
518519

519520
#ifdef FIXME
520521
/*
@@ -526,12 +527,13 @@ void dtrace_probe(dtrace_id_t id, uintptr_t arg0, uintptr_t arg1,
526527
return;
527528
#endif
528529

530+
DTRACE_SYNC_ENTER_CRITICAL(cookie, re_entry);
531+
529532
/*
530533
* If preemption has already been disabled before we get here, we
531534
* accept it as a free gift. We just need to make sure that we don't
532535
* re-enable preemption on the way out...
533536
*/
534-
local_irq_save(cookie);
535537
if ((pflag = dtrace_is_preemptive()))
536538
dtrace_preempt_off();
537539

@@ -547,7 +549,7 @@ void dtrace_probe(dtrace_id_t id, uintptr_t arg0, uintptr_t arg1,
547549
*/
548550
if (pflag)
549551
dtrace_preempt_on();
550-
local_irq_restore(cookie);
552+
DTRACE_SYNC_EXIT_CRITICAL(cookie, re_entry);
551553
return;
552554
}
553555

@@ -557,7 +559,7 @@ void dtrace_probe(dtrace_id_t id, uintptr_t arg0, uintptr_t arg1,
557559
*/
558560
if (pflag)
559561
dtrace_preempt_on();
560-
local_irq_restore(cookie);
562+
DTRACE_SYNC_EXIT_CRITICAL(cookie, re_entry);
561563
return;
562564
}
563565

@@ -574,7 +576,7 @@ void dtrace_probe(dtrace_id_t id, uintptr_t arg0, uintptr_t arg1,
574576
pflag);
575577
if (pflag)
576578
dtrace_preempt_on();
577-
local_irq_restore(cookie);
579+
DTRACE_SYNC_EXIT_CRITICAL(cookie, re_entry);
578580
return;
579581
}
580582

@@ -1250,7 +1252,7 @@ void dtrace_probe(dtrace_id_t id, uintptr_t arg0, uintptr_t arg1,
12501252

12511253
if (pflag)
12521254
dtrace_preempt_on();
1253-
local_irq_restore(cookie);
1255+
DTRACE_SYNC_EXIT_CRITICAL(cookie, re_entry);
12541256

12551257
if (current->dtrace_sig != 0) {
12561258
int sig = current->dtrace_sig;

dtrace/dtrace_spec.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -281,11 +281,12 @@ void dtrace_speculation_clean_here(dtrace_state_t *state)
281281
processorid_t cpu = smp_processor_id();
282282
dtrace_buffer_t *dest = &state->dts_buffer[cpu];
283283
dtrace_specid_t i;
284+
uint32_t re_entry;
284285

285-
local_irq_save(cookie);
286+
DTRACE_SYNC_ENTER_CRITICAL(cookie, re_entry);
286287

287288
if (dest->dtb_tomax == NULL) {
288-
local_irq_restore(cookie);
289+
DTRACE_SYNC_EXIT_CRITICAL(cookie, re_entry);
289290
return;
290291
}
291292

@@ -310,7 +311,7 @@ void dtrace_speculation_clean_here(dtrace_state_t *state)
310311
dtrace_speculation_commit(state, cpu, i + 1);
311312
}
312313

313-
local_irq_restore(cookie);
314+
DTRACE_SYNC_EXIT_CRITICAL(cookie, re_entry);
314315
}
315316

316317
void dtrace_speculation_clean(dtrace_state_t *state)

dtrace/dtrace_state.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ dtrace_optval_t dtrace_jstackstrsize_default = 512;
4444
ktime_t dtrace_deadman_interval = KTIME_INIT(1, 0);
4545
ktime_t dtrace_deadman_timeout = KTIME_INIT(10, 0);
4646
uint64_t dtrace_deadman_user = SECS_TO_JIFFIES(30);
47+
uint64_t dtrace_sync_sample_count = 100; /* Sampling before counting */
4748

4849
dtrace_id_t dtrace_probeid_begin;
4950
dtrace_id_t dtrace_probeid_end;

0 commit comments

Comments
 (0)