History

[PATCH] Add the vgetcpu vsyscall

This patch adds a vgetcpu vsyscall, which depending on the CPU RDTSCP capability uses either the RDTSCP or CPUID to obtain a CPU and node numbers and pass them to the program. AK: Lots of changes over Vojtech's original code: Better prototype for vgetcpu() It's better to pass the cpu / node numbers as separate arguments to avoid mistakes when going from SMP to NUMA. Also add a fast time stamp based cache using a user supplied argument to speed things more up. Use fast method from Chuck Ebbert to retrieve node/cpu from GDT limit instead of CPUID Made sure RDTSCP init is always executed after node is known. Drop printk

Vojtech Pavlik committed 20 years ago

Commit c08c8201 parent a670fad8 files · +130 −14Browse files

Unified · Split

8 files
  1. arch/x86_64/kernel/head.S +1 −1
  2. arch/x86_64/kernel/time.c +7 −6
  3. arch/x86_64/kernel/vmlinux.lds.S +3 −0
  4. arch/x86_64/kernel/vsyscall.c +82 −2
  5. include/asm-x86_64/segment.h +3 −2
  6. include/asm-x86_64/smp.h +9 −3
  7. include/asm-x86_64/vsyscall.h +9 −0
  8. include/linux/getcpu.h +16 −0
arch/x86_64/kernel/head.S+1−1
@@ -371,7 +371,7 @@ ENTRY(cpu_gdt_table) · 370 unmodified lines
371371 .quad 0,0 /* TSS */
372372 .quad 0,0 /* LDT */
373373 .quad 0,0,0 /* three TLS descriptors */
374− .quad 0 /* unused */
374+ .quad 0x0000f40000000000 /* node/CPU stored in limit */
375375 gdt_end:
376376 /* asm/segment.h:GDT_ENTRIES must match this */
377377 /* This should be a multiple of the cache line size */
arch/x86_64/kernel/time.c+7−6
@@ -899,12 +899,8 @@ static int __cpuinit · 898 unmodified lines
899899 time_cpu_notifier(struct notifier_block *nb, unsigned long action, void *hcpu)
900900 {
901901 unsigned cpu = (unsigned long) hcpu;
902− if (action == CPU_ONLINE &&
903− cpu_has(&cpu_data[cpu], X86_FEATURE_RDTSCP)) {
904− unsigned p;
905− p = smp_processor_id() | (cpu_to_node(smp_processor_id())<<12);
906− write_rdtscp_aux(p);
907− }
902+ if (action == CPU_ONLINE)
903+ vsyscall_set_cpu(cpu);
908904 return NOTIFY_DONE;
909905 }
910906
@@ -993,6 +989,11 @@ void time_init_gtod(void) · 82 unmodified lines
993989 if (unsynchronized_tsc())
994990 notsc = 1;
995991
992+ if (cpu_has(&boot_cpu_data, X86_FEATURE_RDTSCP))
993+ vgetcpu_mode = VGETCPU_RDTSCP;
994+ else
995+ vgetcpu_mode = VGETCPU_LSL;
996+
996997 if (vxtime.hpet_address && notsc) {
997998 timetype = hpet_use_timer ? "HPET" : "PIT/HPET";
998999 if (hpet_use_timer)
arch/x86_64/kernel/vmlinux.lds.S+3−0
@@ -99,6 +99,9 @@ SECTIONS · 98 unmodified lines
9999 .vxtime : AT(VLOAD(.vxtime)) { *(.vxtime) }
100100 vxtime = VVIRT(.vxtime);
101101
102+ .vgetcpu_mode : AT(VLOAD(.vgetcpu_mode)) { *(.vgetcpu_mode) }
103+ vgetcpu_mode = VVIRT(.vgetcpu_mode);
104+
102105 .wall_jiffies : AT(VLOAD(.wall_jiffies)) { *(.wall_jiffies) }
103106 wall_jiffies = VVIRT(.wall_jiffies);
104107
arch/x86_64/kernel/vsyscall.c+82−2
@@ -26,6 +26,7 @@ · 25 unmodified lines
2626 #include <linux/seqlock.h>
2727 #include <linux/jiffies.h>
2828 #include <linux/sysctl.h>
29+#include <linux/getcpu.h>
2930
3031 #include <asm/vsyscall.h>
3132 #include <asm/pgtable.h>
@@ -33,11 +34,15 @@ · 1 unmodified line
3334 #include <asm/fixmap.h>
3435 #include <asm/errno.h>
3536 #include <asm/io.h>
37+#include <asm/segment.h>
38+#include <asm/desc.h>
39+#include <asm/topology.h>
3640
3741 #define __vsyscall(nr) __attribute__ ((unused,__section__(".vsyscall_" #nr)))
3842
3943 int __sysctl_vsyscall __section_sysctl_vsyscall = 1;
4044 seqlock_t __xtime_lock __section_xtime_lock = SEQLOCK_UNLOCKED;
45+int __vgetcpu_mode __section_vgetcpu_mode;
4146
4247 #include <asm/unistd.h>
4348
@@ -127,9 +132,46 @@ time_t __vsyscall(1) vtime(time_t *t) · 83 unmodified lines
127132 return __xtime.tv_sec;
128133 }
129134
130−long __vsyscall(2) venosys_0(void)
135+/* Fast way to get current CPU and node.
136+ This helps to do per node and per CPU caches in user space.
137+ The result is not guaranteed without CPU affinity, but usually
138+ works out because the scheduler tries to keep a thread on the same
139+ CPU.
140+
141+ tcache must point to a two element sized long array.
142+ All arguments can be NULL. */
143+long __vsyscall(2)
144+vgetcpu(unsigned *cpu, unsigned *node, struct getcpu_cache *tcache)
131145 {
132− return -ENOSYS;
146+ unsigned int dummy, p;
147+ unsigned long j = 0;
148+
149+ /* Fast cache - only recompute value once per jiffies and avoid
150+ relatively costly rdtscp/cpuid otherwise.
151+ This works because the scheduler usually keeps the process
152+ on the same CPU and this syscall doesn't guarantee its
153+ results anyways.
154+ We do this here because otherwise user space would do it on
155+ its own in a likely inferior way (no access to jiffies).
156+ If you don't like it pass NULL. */
157+ if (tcache && tcache->t0 == (j = __jiffies)) {
158+ p = tcache->t1;
159+ } else if (__vgetcpu_mode == VGETCPU_RDTSCP) {
160+ /* Load per CPU data from RDTSCP */
161+ rdtscp(dummy, dummy, p);
162+ } else {
163+ /* Load per CPU data from GDT */
164+ asm("lsl %1,%0" : "=r" (p) : "r" (__PER_CPU_SEG));
165+ }
166+ if (tcache) {
167+ tcache->t0 = j;
168+ tcache->t1 = p;
169+ }
170+ if (cpu)
171+ *cpu = p & 0xfff;
172+ if (node)
173+ *node = p >> 12;
174+ return 0;
133175 }
134176
135177 long __vsyscall(3) venosys_1(void)
@@ -200,6 +242,43 @@ static ctl_table kernel_root_table2[] = { · 64 unmodified lines
200242
201243 #endif
202244
245+static void __cpuinit write_rdtscp_cb(void *info)
246+{
247+ write_rdtscp_aux((unsigned long)info);
248+}
249+
250+void __cpuinit vsyscall_set_cpu(int cpu)
251+{
252+ unsigned long *d;
253+ unsigned long node = 0;
254+#ifdef CONFIG_NUMA
255+ node = cpu_to_node[cpu];
256+#endif
257+ if (cpu_has(&cpu_data[cpu], X86_FEATURE_RDTSCP)) {
258+ void *info = (void *)((node << 12) | cpu);
259+ /* Can happen on preemptive kernel */
260+ if (get_cpu() == cpu)
261+ write_rdtscp_cb(info);
262+#ifdef CONFIG_SMP
263+ else {
264+ /* the notifier is unfortunately not executed on the
265+ target CPU */
266+ smp_call_function_single(cpu,write_rdtscp_cb,info,0,1);
267+ }
268+#endif
269+ put_cpu();
270+ }
271+
272+ /* Store cpu number in limit so that it can be loaded quickly
273+ in user space in vgetcpu.
274+ 12 bits for the CPU and 8 bits for the node. */
275+ d = (unsigned long *)(cpu_gdt(cpu) + GDT_ENTRY_PER_CPU);
276+ *d = 0x0f40000000000ULL;
277+ *d |= cpu;
278+ *d |= (node & 0xf) << 12;
279+ *d |= (node >> 4) << 48;
280+}
281+
203282 static void __init map_vsyscall(void)
204283 {
205284 extern char __vsyscall_0;
@@ -214,6 +293,7 @@ static int __init vsyscall_init(void) · 8 unmodified lines
214293 VSYSCALL_ADDR(__NR_vgettimeofday)));
215294 BUG_ON((unsigned long) &vtime != VSYSCALL_ADDR(__NR_vtime));
216295 BUG_ON((VSYSCALL_ADDR(0) != __fix_to_virt(VSYSCALL_FIRST_PAGE)));
296+ BUG_ON((unsigned long) &vgetcpu != VSYSCALL_ADDR(__NR_vgetcpu));
217297 map_vsyscall();
218298 #ifdef CONFIG_SYSCTL
219299 register_sysctl_table(kernel_root_table2, 0);
include/asm-x86_64/segment.h+3−2
@@ -20,15 +20,16 @@ · 19 unmodified lines
2020 #define __USER_CS 0x33 /* 6*8+3 */
2121 #define __USER32_DS __USER_DS
2222
23−#define GDT_ENTRY_TLS 1
2423 #define GDT_ENTRY_TSS 8 /* needs two entries */
2524 #define GDT_ENTRY_LDT 10 /* needs two entries */
2625 #define GDT_ENTRY_TLS_MIN 12
2726 #define GDT_ENTRY_TLS_MAX 14
28−/* 15 free */
2927
3028 #define GDT_ENTRY_TLS_ENTRIES 3
3129
30+#define GDT_ENTRY_PER_CPU 15 /* Abused to load per CPU data from limit */
31+#define __PER_CPU_SEG (GDT_ENTRY_PER_CPU * 8 + 3)
32+
3233 /* TLS indexes for 64bit - hardcoded in arch_prctl */
3334 #define FS_TLS 0
3435 #define GS_TLS 1
include/asm-x86_64/smp.h+9−3
@@ -133,13 +133,19 @@ static __inline int logical_smp_processor_id(void) · 132 unmodified lines
133133 /* we don't want to mark this access volatile - bad code generation */
134134 return GET_APIC_LOGICAL_ID(*(unsigned long *)(APIC_BASE+APIC_LDR));
135135 }
136−#endif
137136
138137 #ifdef CONFIG_SMP
139138 #define cpu_physical_id(cpu) x86_cpu_to_apicid[cpu]
140139 #else
141140 #define cpu_physical_id(cpu) boot_cpu_id
142−#endif
143−
141+static inline int smp_call_function_single(int cpuid, void (*func) (void *info),
142+ void *info, int retry, int wait)
143+{
144+ /* Disable interrupts here? */
145+ func(info);
146+ return 0;
147+}
148+#endif /* !CONFIG_SMP */
149+#endif /* !__ASSEMBLY */
144150 #endif
145151
include/asm-x86_64/vsyscall.h+9−0
@@ -4,6 +4,7 @@ · 3 unmodified lines
44 enum vsyscall_num {
55 __NR_vgettimeofday,
66 __NR_vtime,
7+ __NR_vgetcpu,
78 };
89
910 #define VSYSCALL_START (-10UL << 20)
@@ -15,6 +16,7 @@ enum vsyscall_num { · 5 unmodified lines
1516 #include <linux/seqlock.h>
1617
1718 #define __section_vxtime __attribute__ ((unused, __section__ (".vxtime"), aligned(16)))
19+#define __section_vgetcpu_mode __attribute__ ((unused, __section__ (".vgetcpu_mode"), aligned(16)))
1820 #define __section_wall_jiffies __attribute__ ((unused, __section__ (".wall_jiffies"), aligned(16)))
1921 #define __section_jiffies __attribute__ ((unused, __section__ (".jiffies"), aligned(16)))
2022 #define __section_sys_tz __attribute__ ((unused, __section__ (".sys_tz"), aligned(16)))
@@ -26,6 +28,9 @@ enum vsyscall_num { · 5 unmodified lines
2628 #define VXTIME_HPET 2
2729 #define VXTIME_PMTMR 3
2830
31+#define VGETCPU_RDTSCP 1
32+#define VGETCPU_LSL 2
33+
2934 struct vxtime_data {
3035 long hpet_address; /* HPET base address */
3136 int last;
@@ -40,6 +45,7 @@ struct vxtime_data { · 8 unmodified lines
4045
4146 /* vsyscall space (readonly) */
4247 extern struct vxtime_data __vxtime;
48+extern int __vgetcpu_mode;
4349 extern struct timespec __xtime;
4450 extern volatile unsigned long __jiffies;
4551 extern unsigned long __wall_jiffies;
@@ -48,6 +54,7 @@ extern seqlock_t __xtime_lock; · 2 unmodified lines
4854
4955 /* kernel space (writeable) */
5056 extern struct vxtime_data vxtime;
57+extern int vgetcpu_mode;
5158 extern unsigned long wall_jiffies;
5259 extern struct timezone sys_tz;
5360 extern int sysctl_vsyscall;
@@ -55,6 +62,8 @@ extern seqlock_t xtime_lock; · 1 unmodified line
5562
5663 extern int sysctl_vsyscall;
5764
65+extern void vsyscall_set_cpu(int cpu);
66+
5867 #define ARCH_HAVE_XTIME_LOCK 1
5968
6069 #endif /* __KERNEL__ */
include/linux/getcpu.h+16−0
@@ -0,0 +1,16 @@
1+#ifndef _LINUX_GETCPU_H
2+#define _LINUX_GETCPU_H 1
3+
4+/* Cache for getcpu() to speed it up. Results might be upto a jiffie
5+ out of date, but will be faster.
6+ User programs should not refer to the contents of this structure.
7+ It is only a cache for vgetcpu(). It might change in future kernels.
8+ The user program must store this information per thread (__thread)
9+ If you want 100% accurate information pass NULL instead. */
10+struct getcpu_cache {
11+ unsigned long t0;
12+ unsigned long t1;
13+ unsigned long res[4];
14+};
15+
16+#endif

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t