| From foo@baz Sat Sep 29 04:24:28 PDT 2018 |
| From: Frederic Weisbecker <frederic@kernel.org> |
| Date: Tue, 26 Jun 2018 04:58:48 +0200 |
| Subject: perf/hw_breakpoint: Split attribute parse and commit |
| |
| From: Frederic Weisbecker <frederic@kernel.org> |
| |
| [ Upstream commit 9a4903dde2c8633c5fcf887b98c4e047a6154a54 ] |
| |
| arch_validate_hwbkpt_settings() mixes up attribute check and commit into |
| a single code entity. Therefore the validation may return an error due to |
| incorrect atributes while still leaving halfway modified architecture |
| breakpoint data. |
| |
| This is harmless when we deal with a new breakpoint but it becomes a |
| problem when we modify an existing breakpoint. |
| |
| Split attribute parse and commit to fix that. The architecture is |
| passed a "struct arch_hw_breakpoint" to fill on top of the new attr |
| and the core takes care about copying the backend data once it's fully |
| validated. The architectures then need to implement the new API. |
| |
| Original-patch-by: Andy Lutomirski <luto@kernel.org> |
| Reported-by: Linus Torvalds <torvalds@linux-foundation.org> |
| Signed-off-by: Frederic Weisbecker <frederic@kernel.org> |
| Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com> |
| Cc: Andy Lutomirski <luto@kernel.org> |
| Cc: Arnaldo Carvalho de Melo <acme@kernel.org> |
| Cc: Arnaldo Carvalho de Melo <acme@redhat.com> |
| Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org> |
| Cc: Catalin Marinas <catalin.marinas@arm.com> |
| Cc: Chris Zankel <chris@zankel.net> |
| Cc: Jiri Olsa <jolsa@redhat.com> |
| Cc: Joel Fernandes <joel.opensrc@gmail.com> |
| Cc: Mark Rutland <mark.rutland@arm.com> |
| Cc: Max Filippov <jcmvbkbc@gmail.com> |
| Cc: Michael Ellerman <mpe@ellerman.id.au> |
| Cc: Namhyung Kim <namhyung@kernel.org> |
| Cc: Paul Mackerras <paulus@samba.org> |
| Cc: Peter Zijlstra <peterz@infradead.org> |
| Cc: Rich Felker <dalias@libc.org> |
| Cc: Thomas Gleixner <tglx@linutronix.de> |
| Cc: Will Deacon <will.deacon@arm.com> |
| Cc: Yoshinori Sato <ysato@users.sourceforge.jp> |
| Link: http://lkml.kernel.org/r/1529981939-8231-2-git-send-email-frederic@kernel.org |
| Signed-off-by: Ingo Molnar <mingo@kernel.org> |
| Signed-off-by: Sasha Levin <alexander.levin@microsoft.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| kernel/events/hw_breakpoint.c | 57 ++++++++++++++++++++++++++++++------------ |
| 1 file changed, 41 insertions(+), 16 deletions(-) |
| |
| --- a/kernel/events/hw_breakpoint.c |
| +++ b/kernel/events/hw_breakpoint.c |
| @@ -400,16 +400,35 @@ int dbg_release_bp_slot(struct perf_even |
| return 0; |
| } |
| |
| -static int validate_hw_breakpoint(struct perf_event *bp) |
| +#ifndef hw_breakpoint_arch_parse |
| +int hw_breakpoint_arch_parse(struct perf_event *bp, |
| + const struct perf_event_attr *attr, |
| + struct arch_hw_breakpoint *hw) |
| { |
| - int ret; |
| + int err; |
| |
| - ret = arch_validate_hwbkpt_settings(bp); |
| - if (ret) |
| - return ret; |
| + err = arch_validate_hwbkpt_settings(bp); |
| + if (err) |
| + return err; |
| + |
| + *hw = bp->hw.info; |
| + |
| + return 0; |
| +} |
| +#endif |
| + |
| +static int hw_breakpoint_parse(struct perf_event *bp, |
| + const struct perf_event_attr *attr, |
| + struct arch_hw_breakpoint *hw) |
| +{ |
| + int err; |
| + |
| + err = hw_breakpoint_arch_parse(bp, attr, hw); |
| + if (err) |
| + return err; |
| |
| if (arch_check_bp_in_kernelspace(bp)) { |
| - if (bp->attr.exclude_kernel) |
| + if (attr->exclude_kernel) |
| return -EINVAL; |
| /* |
| * Don't let unprivileged users set a breakpoint in the trap |
| @@ -424,19 +443,22 @@ static int validate_hw_breakpoint(struct |
| |
| int register_perf_hw_breakpoint(struct perf_event *bp) |
| { |
| - int ret; |
| + struct arch_hw_breakpoint hw; |
| + int err; |
| |
| - ret = reserve_bp_slot(bp); |
| - if (ret) |
| - return ret; |
| - |
| - ret = validate_hw_breakpoint(bp); |
| + err = reserve_bp_slot(bp); |
| + if (err) |
| + return err; |
| |
| - /* if arch_validate_hwbkpt_settings() fails then release bp slot */ |
| - if (ret) |
| + err = hw_breakpoint_parse(bp, &bp->attr, &hw); |
| + if (err) { |
| release_bp_slot(bp); |
| + return err; |
| + } |
| + |
| + bp->hw.info = hw; |
| |
| - return ret; |
| + return 0; |
| } |
| |
| /** |
| @@ -464,6 +486,7 @@ modify_user_hw_breakpoint_check(struct p |
| u64 old_len = bp->attr.bp_len; |
| int old_type = bp->attr.bp_type; |
| bool modify = attr->bp_type != old_type; |
| + struct arch_hw_breakpoint hw; |
| int err = 0; |
| |
| bp->attr.bp_addr = attr->bp_addr; |
| @@ -473,7 +496,7 @@ modify_user_hw_breakpoint_check(struct p |
| if (check && memcmp(&bp->attr, attr, sizeof(*attr))) |
| return -EINVAL; |
| |
| - err = validate_hw_breakpoint(bp); |
| + err = hw_breakpoint_parse(bp, attr, &hw); |
| if (!err && modify) |
| err = modify_bp_slot(bp, old_type); |
| |
| @@ -484,7 +507,9 @@ modify_user_hw_breakpoint_check(struct p |
| return err; |
| } |
| |
| + bp->hw.info = hw; |
| bp->attr.disabled = attr->disabled; |
| + |
| return 0; |
| } |
| |