Skip to content
Navigation Menu
Toggle navigation
Sign in
In this repository
All GitHub Enterprise
↵
Jump to
↵
No suggested jump to results
In this repository
All GitHub Enterprise
↵
Jump to
↵
In this organization
All GitHub Enterprise
↵
Jump to
↵
In this repository
All GitHub Enterprise
↵
Jump to
↵
Sign in
Reseting focus
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
mariux64
/
linux
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
1
Pull requests
0
Actions
Projects
0
Wiki
Security
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Wiki
Security
Insights
Files
77a0594
Documentation
LICENSES
arch
alpha
arc
arm
arm64
c6x
csky
h8300
hexagon
ia64
m68k
microblaze
mips
nds32
nios2
openrisc
parisc
powerpc
riscv
s390
appldata
boot
configs
crypto
hypfs
include
kernel
syscalls
vdso32
vdso64
.gitignore
Makefile
alternative.c
asm-offsets.c
audit.c
audit.h
base.S
cache.c
compat_audit.c
compat_linux.c
compat_linux.h
compat_ptrace.h
compat_signal.c
cpcmd.c
crash_dump.c
debug.c
diag.c
dis.c
dumpstack.c
early.c
early_printk.c
ebcdic.c
entry.S
entry.h
fpu.c
ftrace.c
guarded_storage.c
head64.S
idle.c
ima_arch.c
ipl.c
ipl_vmparm.c
irq.c
jump_label.c
kdebugfs.c
kexec_elf.c
kexec_image.c
kprobes.c
lgr.c
machine_kexec.c
machine_kexec_file.c
machine_kexec_reloc.c
mcount.S
module.c
nmi.c
nospec-branch.c
nospec-sysfs.c
os_info.c
perf_cpum_cf.c
perf_cpum_cf_common.c
perf_cpum_cf_diag.c
perf_cpum_cf_events.c
perf_cpum_sf.c
perf_event.c
perf_regs.c
pgm_check.S
process.c
processor.c
ptrace.c
reipl.S
relocate_kernel.S
runtime_instr.c
setup.c
signal.c
smp.c
stacktrace.c
sthyi.c
suspend.c
swsusp.S
sys_s390.c
sysinfo.c
time.c
topology.c
trace.c
traps.c
unwind_bc.c
uprobes.c
vdso.c
vmlinux.lds.S
vtime.c
kvm
lib
mm
net
numa
oprofile
pci
purgatory
scripts
tools
Kbuild
Kconfig
Kconfig.debug
Makefile
sh
sparc
um
unicore32
x86
xtensa
.gitignore
Kconfig
block
certs
crypto
drivers
fs
include
init
ipc
kernel
lib
mm
net
samples
scripts
security
sound
tools
usr
virt
.clang-format
.cocciconfig
.get_maintainer.ignore
.gitattributes
.gitignore
.mailmap
COPYING
CREDITS
Kbuild
Kconfig
MAINTAINERS
Makefile
README
Breadcrumbs
linux
/
arch
/
s390
/
kernel
/
vtime.c
Copy path
Blame
Blame
Latest commit
History
History
442 lines (384 loc) · 11.5 KB
Breadcrumbs
linux
/
arch
/
s390
/
kernel
/
vtime.c
Top
File metadata and controls
Code
Blame
442 lines (384 loc) · 11.5 KB
Raw
// SPDX-License-Identifier: GPL-2.0 /* * Virtual cpu timer based timer functions. * * Copyright IBM Corp. 2004, 2012 * Author(s): Jan Glauber <jan.glauber@de.ibm.com> */ #include <linux/kernel_stat.h> #include <linux/sched/cputime.h> #include <linux/export.h> #include <linux/kernel.h> #include <linux/timex.h> #include <linux/types.h> #include <linux/time.h> #include <asm/vtimer.h> #include <asm/vtime.h> #include <asm/cpu_mf.h> #include <asm/smp.h> #include "entry.h" static void virt_timer_expire(void); static LIST_HEAD(virt_timer_list); static DEFINE_SPINLOCK(virt_timer_lock); static atomic64_t virt_timer_current; static atomic64_t virt_timer_elapsed; DEFINE_PER_CPU(u64, mt_cycles[8]); static DEFINE_PER_CPU(u64, mt_scaling_mult) = { 1 }; static DEFINE_PER_CPU(u64, mt_scaling_div) = { 1 }; static DEFINE_PER_CPU(u64, mt_scaling_jiffies); static inline u64 get_vtimer(void) { u64 timer; asm volatile("stpt %0" : "=Q" (timer)); return timer; } static inline void set_vtimer(u64 expires) { u64 timer; asm volatile( " stpt %0\n" /* Store current cpu timer value */ " spt %1" /* Set new value imm. afterwards */ : "=Q" (timer) : "Q" (expires)); S390_lowcore.system_timer += S390_lowcore.last_update_timer - timer; S390_lowcore.last_update_timer = expires; } static inline int virt_timer_forward(u64 elapsed) { BUG_ON(!irqs_disabled()); if (list_empty(&virt_timer_list)) return 0; elapsed = atomic64_add_return(elapsed, &virt_timer_elapsed); return elapsed >= atomic64_read(&virt_timer_current); } static void update_mt_scaling(void) { u64 cycles_new[8], *cycles_old; u64 delta, fac, mult, div; int i; stcctm(MT_DIAG, smp_cpu_mtid + 1, cycles_new); cycles_old = this_cpu_ptr(mt_cycles); fac = 1; mult = div = 0; for (i = 0; i <= smp_cpu_mtid; i++) { delta = cycles_new[i] - cycles_old[i]; div += delta; mult *= i + 1; mult += delta * fac; fac *= i + 1; } div *= fac; if (div > 0) { /* Update scaling factor */ __this_cpu_write(mt_scaling_mult, mult); __this_cpu_write(mt_scaling_div, div); memcpy(cycles_old, cycles_new, sizeof(u64) * (smp_cpu_mtid + 1)); } __this_cpu_write(mt_scaling_jiffies, jiffies_64); } static inline u64 update_tsk_timer(unsigned long *tsk_vtime, u64 new) { u64 delta; delta = new - *tsk_vtime; *tsk_vtime = new; return delta; } static inline u64 scale_vtime(u64 vtime) { u64 mult = __this_cpu_read(mt_scaling_mult); u64 div = __this_cpu_read(mt_scaling_div); if (smp_cpu_mtid) return vtime * mult / div; return vtime; } static void account_system_index_scaled(struct task_struct *p, u64 cputime, enum cpu_usage_stat index) { p->stimescaled += cputime_to_nsecs(scale_vtime(cputime)); account_system_index_time(p, cputime_to_nsecs(cputime), index); } /* * Update process times based on virtual cpu times stored by entry.S * to the lowcore fields user_timer, system_timer & steal_clock. */ static int do_account_vtime(struct task_struct *tsk) { u64 timer, clock, user, guest, system, hardirq, softirq; timer = S390_lowcore.last_update_timer; clock = S390_lowcore.last_update_clock; asm volatile( " stpt %0\n" /* Store current cpu timer value */ #ifdef CONFIG_HAVE_MARCH_Z9_109_FEATURES " stckf %1" /* Store current tod clock value */ #else " stck %1" /* Store current tod clock value */ #endif : "=Q" (S390_lowcore.last_update_timer), "=Q" (S390_lowcore.last_update_clock)); clock = S390_lowcore.last_update_clock - clock; timer -= S390_lowcore.last_update_timer; if (hardirq_count()) S390_lowcore.hardirq_timer += timer; else S390_lowcore.system_timer += timer; /* Update MT utilization calculation */ if (smp_cpu_mtid && time_after64(jiffies_64, this_cpu_read(mt_scaling_jiffies))) update_mt_scaling(); /* Calculate cputime delta */ user = update_tsk_timer(&tsk->thread.user_timer, READ_ONCE(S390_lowcore.user_timer)); guest = update_tsk_timer(&tsk->thread.guest_timer, READ_ONCE(S390_lowcore.guest_timer)); system = update_tsk_timer(&tsk->thread.system_timer, READ_ONCE(S390_lowcore.system_timer)); hardirq = update_tsk_timer(&tsk->thread.hardirq_timer, READ_ONCE(S390_lowcore.hardirq_timer)); softirq = update_tsk_timer(&tsk->thread.softirq_timer, READ_ONCE(S390_lowcore.softirq_timer)); S390_lowcore.steal_timer += clock - user - guest - system - hardirq - softirq; /* Push account value */ if (user) { account_user_time(tsk, cputime_to_nsecs(user)); tsk->utimescaled += cputime_to_nsecs(scale_vtime(user)); } if (guest) { account_guest_time(tsk, cputime_to_nsecs(guest)); tsk->utimescaled += cputime_to_nsecs(scale_vtime(guest)); } if (system) account_system_index_scaled(tsk, system, CPUTIME_SYSTEM); if (hardirq) account_system_index_scaled(tsk, hardirq, CPUTIME_IRQ); if (softirq) account_system_index_scaled(tsk, softirq, CPUTIME_SOFTIRQ); return virt_timer_forward(user + guest + system + hardirq + softirq); } void vtime_task_switch(struct task_struct *prev) { do_account_vtime(prev); prev->thread.user_timer = S390_lowcore.user_timer; prev->thread.guest_timer = S390_lowcore.guest_timer; prev->thread.system_timer = S390_lowcore.system_timer; prev->thread.hardirq_timer = S390_lowcore.hardirq_timer; prev->thread.softirq_timer = S390_lowcore.softirq_timer; S390_lowcore.user_timer = current->thread.user_timer; S390_lowcore.guest_timer = current->thread.guest_timer; S390_lowcore.system_timer = current->thread.system_timer; S390_lowcore.hardirq_timer = current->thread.hardirq_timer; S390_lowcore.softirq_timer = current->thread.softirq_timer; } /* * In s390, accounting pending user time also implies * accounting system time in order to correctly compute * the stolen time accounting. */ void vtime_flush(struct task_struct *tsk) { u64 steal, avg_steal; if (do_account_vtime(tsk)) virt_timer_expire(); steal = S390_lowcore.steal_timer; avg_steal = S390_lowcore.avg_steal_timer / 2; if ((s64) steal > 0) { S390_lowcore.steal_timer = 0; account_steal_time(steal); avg_steal += steal; } S390_lowcore.avg_steal_timer = avg_steal; } /* * Update process times based on virtual cpu times stored by entry.S * to the lowcore fields user_timer, system_timer & steal_clock. */ void vtime_account_irq_enter(struct task_struct *tsk) { u64 timer; timer = S390_lowcore.last_update_timer; S390_lowcore.last_update_timer = get_vtimer(); timer -= S390_lowcore.last_update_timer; if ((tsk->flags & PF_VCPU) && (irq_count() == 0)) S390_lowcore.guest_timer += timer; else if (hardirq_count()) S390_lowcore.hardirq_timer += timer; else if (in_serving_softirq()) S390_lowcore.softirq_timer += timer; else S390_lowcore.system_timer += timer; virt_timer_forward(timer); } EXPORT_SYMBOL_GPL(vtime_account_irq_enter); void vtime_account_kernel(struct task_struct *tsk) __attribute__((alias("vtime_account_irq_enter"))); EXPORT_SYMBOL_GPL(vtime_account_kernel); /* * Sorted add to a list. List is linear searched until first bigger * element is found. */ static void list_add_sorted(struct vtimer_list *timer, struct list_head *head) { struct vtimer_list *tmp; list_for_each_entry(tmp, head, entry) { if (tmp->expires > timer->expires) { list_add_tail(&timer->entry, &tmp->entry); return; } } list_add_tail(&timer->entry, head); } /* * Handler for expired virtual CPU timer. */ static void virt_timer_expire(void) { struct vtimer_list *timer, *tmp; unsigned long elapsed; LIST_HEAD(cb_list); /* walk timer list, fire all expired timers */ spin_lock(&virt_timer_lock); elapsed = atomic64_read(&virt_timer_elapsed); list_for_each_entry_safe(timer, tmp, &virt_timer_list, entry) { if (timer->expires < elapsed) /* move expired timer to the callback queue */ list_move_tail(&timer->entry, &cb_list); else timer->expires -= elapsed; } if (!list_empty(&virt_timer_list)) { timer = list_first_entry(&virt_timer_list, struct vtimer_list, entry); atomic64_set(&virt_timer_current, timer->expires); } atomic64_sub(elapsed, &virt_timer_elapsed); spin_unlock(&virt_timer_lock); /* Do callbacks and recharge periodic timers */ list_for_each_entry_safe(timer, tmp, &cb_list, entry) { list_del_init(&timer->entry); timer->function(timer->data); if (timer->interval) { /* Recharge interval timer */ timer->expires = timer->interval + atomic64_read(&virt_timer_elapsed); spin_lock(&virt_timer_lock); list_add_sorted(timer, &virt_timer_list); spin_unlock(&virt_timer_lock); } } } void init_virt_timer(struct vtimer_list *timer) { timer->function = NULL; INIT_LIST_HEAD(&timer->entry); } EXPORT_SYMBOL(init_virt_timer); static inline int vtimer_pending(struct vtimer_list *timer) { return !list_empty(&timer->entry); } static void internal_add_vtimer(struct vtimer_list *timer) { if (list_empty(&virt_timer_list)) { /* First timer, just program it. */ atomic64_set(&virt_timer_current, timer->expires); atomic64_set(&virt_timer_elapsed, 0); list_add(&timer->entry, &virt_timer_list); } else { /* Update timer against current base. */ timer->expires += atomic64_read(&virt_timer_elapsed); if (likely((s64) timer->expires < (s64) atomic64_read(&virt_timer_current))) /* The new timer expires before the current timer. */ atomic64_set(&virt_timer_current, timer->expires); /* Insert new timer into the list. */ list_add_sorted(timer, &virt_timer_list); } } static void __add_vtimer(struct vtimer_list *timer, int periodic) { unsigned long flags; timer->interval = periodic ? timer->expires : 0; spin_lock_irqsave(&virt_timer_lock, flags); internal_add_vtimer(timer); spin_unlock_irqrestore(&virt_timer_lock, flags); } /* * add_virt_timer - add a oneshot virtual CPU timer */ void add_virt_timer(struct vtimer_list *timer) { __add_vtimer(timer, 0); } EXPORT_SYMBOL(add_virt_timer); /* * add_virt_timer_int - add an interval virtual CPU timer */ void add_virt_timer_periodic(struct vtimer_list *timer) { __add_vtimer(timer, 1); } EXPORT_SYMBOL(add_virt_timer_periodic); static int __mod_vtimer(struct vtimer_list *timer, u64 expires, int periodic) { unsigned long flags; int rc; BUG_ON(!timer->function); if (timer->expires == expires && vtimer_pending(timer)) return 1; spin_lock_irqsave(&virt_timer_lock, flags); rc = vtimer_pending(timer); if (rc) list_del_init(&timer->entry); timer->interval = periodic ? expires : 0; timer->expires = expires; internal_add_vtimer(timer); spin_unlock_irqrestore(&virt_timer_lock, flags); return rc; } /* * returns whether it has modified a pending timer (1) or not (0) */ int mod_virt_timer(struct vtimer_list *timer, u64 expires) { return __mod_vtimer(timer, expires, 0); } EXPORT_SYMBOL(mod_virt_timer); /* * returns whether it has modified a pending timer (1) or not (0) */ int mod_virt_timer_periodic(struct vtimer_list *timer, u64 expires) { return __mod_vtimer(timer, expires, 1); } EXPORT_SYMBOL(mod_virt_timer_periodic); /* * Delete a virtual timer. * * returns whether the deleted timer was pending (1) or not (0) */ int del_virt_timer(struct vtimer_list *timer) { unsigned long flags; if (!vtimer_pending(timer)) return 0; spin_lock_irqsave(&virt_timer_lock, flags); list_del_init(&timer->entry); spin_unlock_irqrestore(&virt_timer_lock, flags); return 1; } EXPORT_SYMBOL(del_virt_timer); /* * Start the virtual CPU timer on the current CPU. */ void vtime_init(void) { /* set initial cpu timer */ set_vtimer(VTIMER_MAX_SLICE); /* Setup initial MT scaling values */ if (smp_cpu_mtid) { __this_cpu_write(mt_scaling_jiffies, jiffies); __this_cpu_write(mt_scaling_mult, 1); __this_cpu_write(mt_scaling_div, 1); stcctm(MT_DIAG, smp_cpu_mtid + 1, this_cpu_ptr(mt_cycles)); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
You can’t perform that action at this time.