Hi Frederick,

On Sun, May 06, 2018 at 09:19:50PM +0200, Frederic Weisbecker wrote:
> The breakpoint code 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 struct.
> 
> Prepare fox fixing this misdesign and separate both logics.

Could you elaborate on what the problem is? I would have expected that
when arch_build_bp_info() returns an error code, we wouldn't
subsequently use the arch_hw_breakpoint information. Where does that
happen?

I understand that there was a problem on x86 -- I'm just having
difficulty figuring it out.

I also see that the check and commit hooks have to duplicate a
reasonable amount of logic, e.g. the switch on bp->attr.type. Can we
instead refactor the existing arch_build_bp_info() hooks to use a
temporary arch_hw_breakpoint, and then struct assign it after all the
error cases, e.g.

static int arch_build_bp_info(struct perf_event *bp)
{
        struct arch_hw_breakpoint hbp;
        
        if (some_condition(bp))
                hbp->field = 0xf00;

        switch (bp->attr.type) {
        case FOO:
                return -EINVAL;
        case BAR:
                hbp->other_field = 7;
                break;
        };

        if (failure_case(foo))
                return err;
        
        *counter_arch_bp(bp) = hbp;
}

... or is that also problematic?

Thanks,
Mark.

> Signed-off-by: Frederic Weisbecker <frede...@kernel.org>
> Cc: Linus Torvalds <torva...@linux-foundation.org>
> Cc: Andy Lutomirski <l...@kernel.org>
> Cc: Yoshinori Sato <ys...@users.sourceforge.jp>
> Cc: Rich Felker <dal...@libc.org>
> Cc: Ingo Molnar <mi...@kernel.org>
> Cc: Thomas Gleixner <t...@linutronix.de>
> Cc: Will Deacon <will.dea...@arm.com>
> Cc: Mark Rutland <mark.rutl...@arm.com>
> Cc: Max Filippov <jcmvb...@gmail.com>
> Cc: Chris Zankel <ch...@zankel.net>
> Cc: Catalin Marinas <catalin.mari...@arm.com>
> Cc: Benjamin Herrenschmidt <b...@kernel.crashing.org>
> Cc: Paul Mackerras <pau...@samba.org>
> Cc: Michael Ellerman <m...@ellerman.id.au>
> Cc: Peter Zijlstra <pet...@infradead.org>
> Cc: Arnaldo Carvalho de Melo <a...@kernel.org>
> Cc: Alexander Shishkin <alexander.shish...@linux.intel.com>
> Cc: Jiri Olsa <jo...@redhat.com>
> Cc: Namhyung Kim <namhy...@kernel.org>
> ---
>  arch/arm/kernel/hw_breakpoint.c | 176 
> +++++++++++++++++++++++-----------------
>  1 file changed, 103 insertions(+), 73 deletions(-)
> 
> diff --git a/arch/arm/kernel/hw_breakpoint.c b/arch/arm/kernel/hw_breakpoint.c
> index 629e251..1769d3a 100644
> --- a/arch/arm/kernel/hw_breakpoint.c
> +++ b/arch/arm/kernel/hw_breakpoint.c
> @@ -515,45 +515,33 @@ int arch_bp_generic_fields(struct 
> arch_hw_breakpoint_ctrl ctrl,
>       return 0;
>  }
>  
> -/*
> - * Construct an arch_hw_breakpoint from a perf_event.
> - */
> -static int arch_build_bp_info(struct perf_event *bp)
> +static int hw_breakpoint_arch_check(struct perf_event *bp,
> +                                 const struct perf_event_attr *attr)
>  {
> -     struct arch_hw_breakpoint *info = counter_arch_bp(bp);
> +     u32 offset, alignment_mask = 0x3;
> +
> +     /* Ensure that we are in monitor debug mode. */
> +     if (!monitor_mode_enabled())
> +             return -ENODEV;
>  
>       /* Type */
> -     switch (bp->attr.bp_type) {
> +     switch (attr->bp_type) {
>       case HW_BREAKPOINT_X:
> -             info->ctrl.type = ARM_BREAKPOINT_EXECUTE;
> -             break;
>       case HW_BREAKPOINT_R:
> -             info->ctrl.type = ARM_BREAKPOINT_LOAD;
> -             break;
>       case HW_BREAKPOINT_W:
> -             info->ctrl.type = ARM_BREAKPOINT_STORE;
> -             break;
>       case HW_BREAKPOINT_RW:
> -             info->ctrl.type = ARM_BREAKPOINT_LOAD | ARM_BREAKPOINT_STORE;
>               break;
>       default:
>               return -EINVAL;
>       }
>  
>       /* Len */
> -     switch (bp->attr.bp_len) {
> +     switch (attr->bp_len) {
>       case HW_BREAKPOINT_LEN_1:
> -             info->ctrl.len = ARM_BREAKPOINT_LEN_1;
> -             break;
>       case HW_BREAKPOINT_LEN_2:
> -             info->ctrl.len = ARM_BREAKPOINT_LEN_2;
> -             break;
>       case HW_BREAKPOINT_LEN_4:
> -             info->ctrl.len = ARM_BREAKPOINT_LEN_4;
> -             break;
>       case HW_BREAKPOINT_LEN_8:
> -             info->ctrl.len = ARM_BREAKPOINT_LEN_8;
> -             if ((info->ctrl.type != ARM_BREAKPOINT_EXECUTE)
> +             if ((attr->bp_type != HW_BREAKPOINT_X)
>                       && max_watchpoint_len >= 8)
>                       break;
>       default:
> @@ -566,50 +554,17 @@ static int arch_build_bp_info(struct perf_event *bp)
>        * by the hardware and must be aligned to the appropriate number of
>        * bytes.
>        */
> -     if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE &&
> -         info->ctrl.len != ARM_BREAKPOINT_LEN_2 &&
> -         info->ctrl.len != ARM_BREAKPOINT_LEN_4)
> +     if (attr->bp_type == HW_BREAKPOINT_X &&
> +         attr->bp_len != HW_BREAKPOINT_LEN_2 &&
> +         attr->bp_len != HW_BREAKPOINT_LEN_4)
>               return -EINVAL;
>  
> -     /* Address */
> -     info->address = bp->attr.bp_addr;
> -
> -     /* Privilege */
> -     info->ctrl.privilege = ARM_BREAKPOINT_USER;
> -     if (arch_check_bp_in_kernelspace(bp))
> -             info->ctrl.privilege |= ARM_BREAKPOINT_PRIV;
> -
> -     /* Enabled? */
> -     info->ctrl.enabled = !bp->attr.disabled;
> -
> -     /* Mismatch */
> -     info->ctrl.mismatch = 0;
> -
> -     return 0;
> -}
> -
> -/*
> - * Validate the arch-specific HW Breakpoint register settings.
> - */
> -int arch_validate_hwbkpt_settings(struct perf_event *bp)
> -{
> -     struct arch_hw_breakpoint *info = counter_arch_bp(bp);
> -     int ret = 0;
> -     u32 offset, alignment_mask = 0x3;
> -
> -     /* Ensure that we are in monitor debug mode. */
> -     if (!monitor_mode_enabled())
> -             return -ENODEV;
> -
> -     /* Build the arch_hw_breakpoint. */
> -     ret = arch_build_bp_info(bp);
> -     if (ret)
> -             goto out;
> -
>       /* Check address alignment. */
> -     if (info->ctrl.len == ARM_BREAKPOINT_LEN_8)
> +     if (attr->bp_len == HW_BREAKPOINT_LEN_8)
>               alignment_mask = 0x7;
> -     offset = info->address & alignment_mask;
> +
> +     offset = attr->bp_addr & alignment_mask;
> +
>       switch (offset) {
>       case 0:
>               /* Aligned */
> @@ -617,20 +572,16 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp)
>       case 1:
>       case 2:
>               /* Allow halfword watchpoints and breakpoints. */
> -             if (info->ctrl.len == ARM_BREAKPOINT_LEN_2)
> +             if (attr->bp_len == HW_BREAKPOINT_LEN_2)
>                       break;
>       case 3:
>               /* Allow single byte watchpoint. */
> -             if (info->ctrl.len == ARM_BREAKPOINT_LEN_1)
> +             if (attr->bp_len == HW_BREAKPOINT_LEN_1)
>                       break;
>       default:
> -             ret = -EINVAL;
> -             goto out;
> +             return -EINVAL;
>       }
>  
> -     info->address &= ~alignment_mask;
> -     info->ctrl.len <<= offset;
> -
>       if (is_default_overflow_handler(bp)) {
>               /*
>                * Mismatch breakpoints are required for single-stepping
> @@ -655,13 +606,92 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp)
>                * reports them.
>                */
>               if (!debug_exception_updates_fsr() &&
> -                 (info->ctrl.type == ARM_BREAKPOINT_LOAD ||
> -                  info->ctrl.type == ARM_BREAKPOINT_STORE))
> +                 (attr->bp_type == HW_BREAKPOINT_R ||
> +                  attr->bp_type == HW_BREAKPOINT_W))
>                       return -EINVAL;
>       }
>  
> -out:
> -     return ret;
> +     return 0;
> +}
> +
> +static void hw_breakpoint_arch_commit(struct perf_event *bp)
> +{
> +     struct arch_hw_breakpoint *info = counter_arch_bp(bp);
> +     struct perf_event_attr *attr = &bp->attr;
> +     u32 offset, alignment_mask = 0x3;
> +
> +     /* Type */
> +     switch (attr->bp_type) {
> +     case HW_BREAKPOINT_X:
> +             info->ctrl.type = ARM_BREAKPOINT_EXECUTE;
> +             break;
> +     case HW_BREAKPOINT_R:
> +             info->ctrl.type = ARM_BREAKPOINT_LOAD;
> +             break;
> +     case HW_BREAKPOINT_W:
> +             info->ctrl.type = ARM_BREAKPOINT_STORE;
> +             break;
> +     case HW_BREAKPOINT_RW:
> +             info->ctrl.type = ARM_BREAKPOINT_LOAD | ARM_BREAKPOINT_STORE;
> +             break;
> +     default:
> +             WARN_ON_ONCE(1);
> +     }
> +
> +     /* Len */
> +     switch (attr->bp_len) {
> +     case HW_BREAKPOINT_LEN_1:
> +             info->ctrl.len = ARM_BREAKPOINT_LEN_1;
> +             break;
> +     case HW_BREAKPOINT_LEN_2:
> +             info->ctrl.len = ARM_BREAKPOINT_LEN_2;
> +             break;
> +     case HW_BREAKPOINT_LEN_4:
> +             info->ctrl.len = ARM_BREAKPOINT_LEN_4;
> +             break;
> +     case HW_BREAKPOINT_LEN_8:
> +             info->ctrl.len = ARM_BREAKPOINT_LEN_8;
> +             break;
> +     default:
> +             WARN_ON_ONCE(1);
> +     }
> +
> +     /* Address */
> +     info->address = attr->bp_addr;
> +
> +     /* Privilege */
> +     info->ctrl.privilege = ARM_BREAKPOINT_USER;
> +     if (arch_check_bp_in_kernelspace(bp))
> +             info->ctrl.privilege |= ARM_BREAKPOINT_PRIV;
> +
> +     /* Enabled? */
> +     info->ctrl.enabled = !attr->disabled;
> +
> +     /* Mismatch */
> +     info->ctrl.mismatch = 0;
> +
> +     if (info->ctrl.len == ARM_BREAKPOINT_LEN_8)
> +             alignment_mask = 0x7;
> +
> +     offset = info->address & alignment_mask;
> +     info->address &= ~alignment_mask;
> +     info->ctrl.len <<= offset;
> +}
> +
> +/*
> + * Validate the arch-specific HW Breakpoint register settings.
> + */
> +int arch_validate_hwbkpt_settings(struct perf_event *bp)
> +{
> +     int err;
> +
> +     err = hw_breakpoint_arch_check(bp, &bp->attr);
> +     if (err)
> +             return err;
> +
> +     hw_breakpoint_arch_commit(bp);
> +
> +     return 0;
>  }
>  
>  /*
> -- 
> 2.7.4
> 

Reply via email to