diff options
author | Ted Unangst <tedu@cvs.openbsd.org> | 2014-10-17 01:51:40 +0000 |
---|---|---|
committer | Ted Unangst <tedu@cvs.openbsd.org> | 2014-10-17 01:51:40 +0000 |
commit | 408955add921fe3396cd06f800d7d8a066c9ab26 (patch) | |
tree | 91ae4f7a9012abb064041f158840bc8c3420206a | |
parent | 74fe337c10141c7b46dc14a8fc44882a404a47d6 (diff) |
redo the performance throttling in the kernel.
introduce a new sysctl, hw.perfpolicy, that governs the policy.
when set to anything other than manual, hw.setperf then becomes read only.
phessler was heading in this direction, but this is slightly different. :)
-rw-r--r-- | sys/kern/init_main.c | 11 | ||||
-rw-r--r-- | sys/kern/kern_sysctl.c | 29 | ||||
-rw-r--r-- | sys/kern/sched_bsd.c | 152 | ||||
-rw-r--r-- | sys/sys/sched.h | 5 | ||||
-rw-r--r-- | sys/sys/sysctl.h | 6 |
5 files changed, 178 insertions, 25 deletions
diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c index e9e5827fb4b..4d5b672a619 100644 --- a/sys/kern/init_main.c +++ b/sys/kern/init_main.c @@ -1,4 +1,4 @@ -/* $OpenBSD: init_main.c,v 1.221 2014/10/13 22:42:22 deraadt Exp $ */ +/* $OpenBSD: init_main.c,v 1.222 2014/10/17 01:51:39 tedu Exp $ */ /* $NetBSD: init_main.c,v 1.84.4.1 1996/06/02 09:08:06 mrg Exp $ */ /* @@ -116,6 +116,11 @@ struct sigacts sigacts0; struct process *initprocess; struct proc *reaperproc; +#ifndef SMALL_KERNEL +extern struct timeout setperf_to; +void setperf_auto(void *); +#endif + int cmask = CMASK; extern struct user *proc0paddr; @@ -545,6 +550,10 @@ main(void *framep) start_init_exec = 1; wakeup((void *)&start_init_exec); +#ifndef SMALL_KERNEL + timeout_set(&setperf_to, setperf_auto, NULL); +#endif + /* * proc0: nothing to do, back to sleep */ diff --git a/sys/kern/kern_sysctl.c b/sys/kern/kern_sysctl.c index 15c467c23ba..dcf6b861981 100644 --- a/sys/kern/kern_sysctl.c +++ b/sys/kern/kern_sysctl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_sysctl.c,v 1.266 2014/10/11 17:12:30 deraadt Exp $ */ +/* $OpenBSD: kern_sysctl.c,v 1.267 2014/10/17 01:51:39 tedu Exp $ */ /* $NetBSD: kern_sysctl.c,v 1.17 1996/05/20 17:49:05 mrg Exp $ */ /*- @@ -127,8 +127,6 @@ void fill_file(struct kinfo_file *, struct file *, struct filedesc *, void fill_kproc(struct process *, struct kinfo_proc *, struct proc *, int); int (*cpu_cpuspeed)(int *); -void (*cpu_setperf)(int); -int perflevel = 100; /* * Lock to avoid too many processes vslocking a large amount of memory @@ -649,11 +647,6 @@ hw_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, disk_count * sizeof(struct diskstats))); case HW_DISKCOUNT: return (sysctl_rdint(oldp, oldlenp, newp, disk_count)); -#ifndef SMALL_KERNEL - case HW_SENSORS: - return (sysctl_sensors(name + 1, namelen - 1, oldp, oldlenp, - newp, newlen)); -#endif case HW_CPUSPEED: if (!cpu_cpuspeed) return (EOPNOTSUPP); @@ -661,19 +654,15 @@ hw_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, if (err) return err; return (sysctl_rdint(oldp, oldlenp, newp, cpuspeed)); +#ifndef SMALL_KERNEL + case HW_SENSORS: + return (sysctl_sensors(name + 1, namelen - 1, oldp, oldlenp, + newp, newlen)); case HW_SETPERF: - if (!cpu_setperf) - return (EOPNOTSUPP); - err = sysctl_int(oldp, oldlenp, newp, newlen, &perflevel); - if (err) - return err; - if (perflevel > 100) - perflevel = 100; - if (perflevel < 0) - perflevel = 0; - if (newp) - cpu_setperf(perflevel); - return (0); + return (sysctl_hwsetperf(oldp, oldlenp, newp, newlen)); + case HW_PERFPOLICY: + return (sysctl_hwperfpolicy(oldp, oldlenp, newp, newlen)); +#endif /* !SMALL_KERNEL */ case HW_VENDOR: if (hw_vendor) return (sysctl_rdstring(oldp, oldlenp, newp, diff --git a/sys/kern/sched_bsd.c b/sys/kern/sched_bsd.c index 98bb478b8bc..67813da2131 100644 --- a/sys/kern/sched_bsd.c +++ b/sys/kern/sched_bsd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sched_bsd.c,v 1.35 2014/07/04 05:58:31 guenther Exp $ */ +/* $OpenBSD: sched_bsd.c,v 1.36 2014/10/17 01:51:39 tedu Exp $ */ /* $NetBSD: kern_synch.c,v 1.37 1996/04/22 01:38:37 christos Exp $ */ /*- @@ -41,6 +41,7 @@ #include <sys/systm.h> #include <sys/proc.h> #include <sys/kernel.h> +#include <sys/malloc.h> #include <sys/buf.h> #include <sys/signalvar.h> #include <sys/resourcevar.h> @@ -563,3 +564,152 @@ schedclock(struct proc *p) p->p_priority = p->p_usrpri; SCHED_UNLOCK(s); } + +#ifndef SMALL_KERNEL +/* + * The code below handles CPU throttling. + */ +#include <sys/sysctl.h> + +#define PERFPOL_MANUAL 0 +#define PERFPOL_AUTO 1 +#define PERFPOL_HIGH 2 +int perflevel = 100; +int perfpolicy = PERFPOL_MANUAL; + +void (*cpu_setperf)(int); + +struct timeout setperf_to; +void setperf_auto(void *); + +void +setperf_auto(void *v) +{ + static uint64_t *idleticks, *totalticks; + + int i, j; + int speedup; + CPU_INFO_ITERATOR cii; + struct cpu_info *ci; + uint64_t idle, total, allidle, alltotal; + + if (perfpolicy != PERFPOL_AUTO) + return; + + if (!idleticks) + if (!(idleticks = malloc(sizeof(*idleticks) * ncpusfound, + M_DEVBUF, M_NOWAIT | M_ZERO))) + return; + if (!totalticks) + if (!(totalticks = malloc(sizeof(*totalticks) * ncpusfound, + M_DEVBUF, M_NOWAIT | M_ZERO))) { + free(idleticks, M_DEVBUF, 0); + return; + } + + alltotal = allidle = 0; + j = 0; + speedup = 0; + CPU_INFO_FOREACH(cii, ci) { + total = 0; + for (i = 0; i < CPUSTATES; i++) { + total += ci->ci_schedstate.spc_cp_time[i]; + } + total -= totalticks[j]; + idle = ci->ci_schedstate.spc_cp_time[CP_IDLE] - idleticks[j]; + if (idle < total / 3) + speedup = 1; + alltotal += total; + allidle += idle; + idleticks[j] += idle; + totalticks[j] += total; + j++; + } + if (allidle < alltotal / 2) + speedup = 1; + + if (speedup && perflevel != 100) { + perflevel = 100; + cpu_setperf(perflevel); + } else if (!speedup && perflevel != 0) { + perflevel = 0; + cpu_setperf(perflevel); + } + + timeout_add_msec(&setperf_to, 100); +} + +int +sysctl_hwsetperf(void *oldp, size_t *oldlenp, void *newp, size_t newlen) +{ + int err, newperf; + + if (!cpu_setperf) + return EOPNOTSUPP; + + if (perfpolicy != PERFPOL_MANUAL) + return sysctl_rdint(oldp, oldlenp, newp, perflevel); + + newperf = perflevel; + err = sysctl_int(oldp, oldlenp, newp, newlen, &newperf); + if (err) + return err; + if (newperf > 100) + newperf = 100; + if (newperf < 0) + newperf = 0; + perflevel = newperf; + cpu_setperf(perflevel); + + return 0; +} + +int +sysctl_hwperfpolicy(void *oldp, size_t *oldlenp, void *newp, size_t newlen) +{ + char policy[32]; + int err; + + if (!cpu_setperf) + return EOPNOTSUPP; + + switch (perfpolicy) { + case PERFPOL_MANUAL: + strlcpy(policy, "manual", sizeof(policy)); + break; + case PERFPOL_AUTO: + strlcpy(policy, "auto", sizeof(policy)); + break; + case PERFPOL_HIGH: + strlcpy(policy, "high", sizeof(policy)); + break; + default: + strlcpy(policy, "unknown", sizeof(policy)); + break; + } + + if (newp == NULL) + return sysctl_rdstring(oldp, oldlenp, newp, policy); + + err = sysctl_string(oldp, oldlenp, newp, newlen, policy, sizeof(policy)); + if (err) + return err; + if (strcmp(policy, "manual") == 0) + perfpolicy = PERFPOL_MANUAL; + else if (strcmp(policy, "auto") == 0) + perfpolicy = PERFPOL_AUTO; + else if (strcmp(policy, "high") == 0) + perfpolicy = PERFPOL_HIGH; + else + return EINVAL; + + if (perfpolicy == PERFPOL_AUTO) { + timeout_add_msec(&setperf_to, 200); + } else if (perfpolicy == PERFPOL_HIGH) { + perflevel = 100; + cpu_setperf(perflevel); + } + return 0; +} +#endif + diff --git a/sys/sys/sched.h b/sys/sys/sched.h index 731d9dcbfd1..032580f5fe1 100644 --- a/sys/sys/sched.h +++ b/sys/sys/sched.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sched.h,v 1.36 2014/09/09 07:07:39 blambert Exp $ */ +/* $OpenBSD: sched.h,v 1.37 2014/10/17 01:51:39 tedu Exp $ */ /* $NetBSD: sched.h,v 1.2 1999/02/28 18:14:58 ross Exp $ */ /*- @@ -153,6 +153,9 @@ void cpu_idle_cycle(void); void cpu_idle_leave(void); void sched_peg_curproc(struct cpu_info *ci); +int sysctl_hwsetperf(void *, size_t *, void *, size_t); +int sysctl_hwperfpolicy(void *, size_t *, void *, size_t); + #ifdef MULTIPROCESSOR void sched_start_secondary_cpus(void); void sched_stop_secondary_cpus(void); diff --git a/sys/sys/sysctl.h b/sys/sys/sysctl.h index 8bb7f090cf3..665aae654c6 100644 --- a/sys/sys/sysctl.h +++ b/sys/sys/sysctl.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sysctl.h,v 1.149 2014/08/20 06:23:03 mikeb Exp $ */ +/* $OpenBSD: sysctl.h,v 1.150 2014/10/17 01:51:39 tedu Exp $ */ /* $NetBSD: sysctl.h,v 1.16 1996/04/09 20:55:36 cgd Exp $ */ /* @@ -798,7 +798,8 @@ struct kinfo_file { #define HW_USERMEM64 20 /* quad: non-kernel memory */ #define HW_NCPUFOUND 21 /* int: number of cpus found*/ #define HW_ALLOWPOWERDOWN 22 /* allow power button shutdown */ -#define HW_MAXID 23 /* number of valid hw ids */ +#define HW_PERFPOLICY 23 /* set performance policy */ +#define HW_MAXID 24 /* number of valid hw ids */ #define CTL_HW_NAMES { \ { 0, 0 }, \ @@ -824,6 +825,7 @@ struct kinfo_file { { "usermem", CTLTYPE_QUAD }, \ { "ncpufound", CTLTYPE_INT }, \ { "allowpowerdown", CTLTYPE_INT }, \ + { "perfpolicy", CTLTYPE_STRING }, \ } /* |