Commit 969a5197 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 's390-5.4-6' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux

Pull s390 fixes from Vasily Gorbik:

 - Fix cpu idle time accounting

 - Fix stack unwinder case when both pt_regs and sp are specified

 - Fix information leak via cmm timeout proc handler

* tag 's390-5.4-6' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux:
  s390/idle: fix cpu idle time calculation
  s390/unwind: fix mixing regs and sp
  s390/cmm: fix information leak in cmm_timeout_handler()
parents 1204c70d 3d7efa4e
...@@ -35,6 +35,7 @@ struct unwind_state { ...@@ -35,6 +35,7 @@ struct unwind_state {
struct task_struct *task; struct task_struct *task;
struct pt_regs *regs; struct pt_regs *regs;
unsigned long sp, ip; unsigned long sp, ip;
bool reuse_sp;
int graph_idx; int graph_idx;
bool reliable; bool reliable;
bool error; bool error;
......
...@@ -69,18 +69,26 @@ DEVICE_ATTR(idle_count, 0444, show_idle_count, NULL); ...@@ -69,18 +69,26 @@ DEVICE_ATTR(idle_count, 0444, show_idle_count, NULL);
static ssize_t show_idle_time(struct device *dev, static ssize_t show_idle_time(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
{ {
unsigned long long now, idle_time, idle_enter, idle_exit, in_idle;
struct s390_idle_data *idle = &per_cpu(s390_idle, dev->id); struct s390_idle_data *idle = &per_cpu(s390_idle, dev->id);
unsigned long long now, idle_time, idle_enter, idle_exit;
unsigned int seq; unsigned int seq;
do { do {
now = get_tod_clock();
seq = read_seqcount_begin(&idle->seqcount); seq = read_seqcount_begin(&idle->seqcount);
idle_time = READ_ONCE(idle->idle_time); idle_time = READ_ONCE(idle->idle_time);
idle_enter = READ_ONCE(idle->clock_idle_enter); idle_enter = READ_ONCE(idle->clock_idle_enter);
idle_exit = READ_ONCE(idle->clock_idle_exit); idle_exit = READ_ONCE(idle->clock_idle_exit);
} while (read_seqcount_retry(&idle->seqcount, seq)); } while (read_seqcount_retry(&idle->seqcount, seq));
idle_time += idle_enter ? ((idle_exit ? : now) - idle_enter) : 0; in_idle = 0;
now = get_tod_clock();
if (idle_enter) {
if (idle_exit) {
in_idle = idle_exit - idle_enter;
} else if (now > idle_enter) {
in_idle = now - idle_enter;
}
}
idle_time += in_idle;
return sprintf(buf, "%llu\n", idle_time >> 12); return sprintf(buf, "%llu\n", idle_time >> 12);
} }
DEVICE_ATTR(idle_time_us, 0444, show_idle_time, NULL); DEVICE_ATTR(idle_time_us, 0444, show_idle_time, NULL);
...@@ -88,17 +96,24 @@ DEVICE_ATTR(idle_time_us, 0444, show_idle_time, NULL); ...@@ -88,17 +96,24 @@ DEVICE_ATTR(idle_time_us, 0444, show_idle_time, NULL);
u64 arch_cpu_idle_time(int cpu) u64 arch_cpu_idle_time(int cpu)
{ {
struct s390_idle_data *idle = &per_cpu(s390_idle, cpu); struct s390_idle_data *idle = &per_cpu(s390_idle, cpu);
unsigned long long now, idle_enter, idle_exit; unsigned long long now, idle_enter, idle_exit, in_idle;
unsigned int seq; unsigned int seq;
do { do {
now = get_tod_clock();
seq = read_seqcount_begin(&idle->seqcount); seq = read_seqcount_begin(&idle->seqcount);
idle_enter = READ_ONCE(idle->clock_idle_enter); idle_enter = READ_ONCE(idle->clock_idle_enter);
idle_exit = READ_ONCE(idle->clock_idle_exit); idle_exit = READ_ONCE(idle->clock_idle_exit);
} while (read_seqcount_retry(&idle->seqcount, seq)); } while (read_seqcount_retry(&idle->seqcount, seq));
in_idle = 0;
return cputime_to_nsecs(idle_enter ? ((idle_exit ?: now) - idle_enter) : 0); now = get_tod_clock();
if (idle_enter) {
if (idle_exit) {
in_idle = idle_exit - idle_enter;
} else if (now > idle_enter) {
in_idle = now - idle_enter;
}
}
return cputime_to_nsecs(in_idle);
} }
void arch_cpu_idle_enter(void) void arch_cpu_idle_enter(void)
......
...@@ -46,10 +46,15 @@ bool unwind_next_frame(struct unwind_state *state) ...@@ -46,10 +46,15 @@ bool unwind_next_frame(struct unwind_state *state)
regs = state->regs; regs = state->regs;
if (unlikely(regs)) { if (unlikely(regs)) {
sp = READ_ONCE_NOCHECK(regs->gprs[15]); if (state->reuse_sp) {
if (unlikely(outside_of_stack(state, sp))) { sp = state->sp;
if (!update_stack_info(state, sp)) state->reuse_sp = false;
goto out_err; } else {
sp = READ_ONCE_NOCHECK(regs->gprs[15]);
if (unlikely(outside_of_stack(state, sp))) {
if (!update_stack_info(state, sp))
goto out_err;
}
} }
sf = (struct stack_frame *) sp; sf = (struct stack_frame *) sp;
ip = READ_ONCE_NOCHECK(sf->gprs[8]); ip = READ_ONCE_NOCHECK(sf->gprs[8]);
...@@ -107,9 +112,9 @@ void __unwind_start(struct unwind_state *state, struct task_struct *task, ...@@ -107,9 +112,9 @@ void __unwind_start(struct unwind_state *state, struct task_struct *task,
{ {
struct stack_info *info = &state->stack_info; struct stack_info *info = &state->stack_info;
unsigned long *mask = &state->stack_mask; unsigned long *mask = &state->stack_mask;
bool reliable, reuse_sp;
struct stack_frame *sf; struct stack_frame *sf;
unsigned long ip; unsigned long ip;
bool reliable;
memset(state, 0, sizeof(*state)); memset(state, 0, sizeof(*state));
state->task = task; state->task = task;
...@@ -134,10 +139,12 @@ void __unwind_start(struct unwind_state *state, struct task_struct *task, ...@@ -134,10 +139,12 @@ void __unwind_start(struct unwind_state *state, struct task_struct *task,
if (regs) { if (regs) {
ip = READ_ONCE_NOCHECK(regs->psw.addr); ip = READ_ONCE_NOCHECK(regs->psw.addr);
reliable = true; reliable = true;
reuse_sp = true;
} else { } else {
sf = (struct stack_frame *) sp; sf = (struct stack_frame *) sp;
ip = READ_ONCE_NOCHECK(sf->gprs[8]); ip = READ_ONCE_NOCHECK(sf->gprs[8]);
reliable = false; reliable = false;
reuse_sp = false;
} }
#ifdef CONFIG_FUNCTION_GRAPH_TRACER #ifdef CONFIG_FUNCTION_GRAPH_TRACER
...@@ -151,5 +158,6 @@ void __unwind_start(struct unwind_state *state, struct task_struct *task, ...@@ -151,5 +158,6 @@ void __unwind_start(struct unwind_state *state, struct task_struct *task,
state->sp = sp; state->sp = sp;
state->ip = ip; state->ip = ip;
state->reliable = reliable; state->reliable = reliable;
state->reuse_sp = reuse_sp;
} }
EXPORT_SYMBOL_GPL(__unwind_start); EXPORT_SYMBOL_GPL(__unwind_start);
...@@ -298,16 +298,16 @@ static int cmm_timeout_handler(struct ctl_table *ctl, int write, ...@@ -298,16 +298,16 @@ static int cmm_timeout_handler(struct ctl_table *ctl, int write,
} }
if (write) { if (write) {
len = *lenp; len = min(*lenp, sizeof(buf));
if (copy_from_user(buf, buffer, if (copy_from_user(buf, buffer, len))
len > sizeof(buf) ? sizeof(buf) : len))
return -EFAULT; return -EFAULT;
buf[sizeof(buf) - 1] = '\0'; buf[len - 1] = '\0';
cmm_skip_blanks(buf, &p); cmm_skip_blanks(buf, &p);
nr = simple_strtoul(p, &p, 0); nr = simple_strtoul(p, &p, 0);
cmm_skip_blanks(p, &p); cmm_skip_blanks(p, &p);
seconds = simple_strtoul(p, &p, 0); seconds = simple_strtoul(p, &p, 0);
cmm_set_timeout(nr, seconds); cmm_set_timeout(nr, seconds);
*ppos += *lenp;
} else { } else {
len = sprintf(buf, "%ld %ld\n", len = sprintf(buf, "%ld %ld\n",
cmm_timeout_pages, cmm_timeout_seconds); cmm_timeout_pages, cmm_timeout_seconds);
...@@ -315,9 +315,9 @@ static int cmm_timeout_handler(struct ctl_table *ctl, int write, ...@@ -315,9 +315,9 @@ static int cmm_timeout_handler(struct ctl_table *ctl, int write,
len = *lenp; len = *lenp;
if (copy_to_user(buffer, buf, len)) if (copy_to_user(buffer, buf, len))
return -EFAULT; return -EFAULT;
*lenp = len;
*ppos += len;
} }
*lenp = len;
*ppos += len;
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment