Re: [PATCH bpf-next 08/10] s390/bpf: Support arena atomics

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Jun 27, 2024 at 2:09 AM Ilya Leoshkevich <iii@xxxxxxxxxxxxx> wrote:
>
> s390x supports most BPF atomics using single instructions, which
> makes implementing arena support a matter of adding arena address to
> the base register (unfortunately atomics do not support index
> registers), and wrapping the respective native instruction in probing
> sequences.
>
> An exception is BPF_XCHG, which is implemented using two different
> memory accesses and a loop. Make sure there is enough extable entries
> for both instructions. Compute the base address once for both memory
> accesses. Since on exception we need to land after the loop, emit the
> nops manually.
>
> Signed-off-by: Ilya Leoshkevich <iii@xxxxxxxxxxxxx>
> ---
>  arch/s390/net/bpf_jit_comp.c | 100 +++++++++++++++++++++++++++++++----
>  1 file changed, 91 insertions(+), 9 deletions(-)
>
> diff --git a/arch/s390/net/bpf_jit_comp.c b/arch/s390/net/bpf_jit_comp.c
> index 1dd359c25ada..12293689ad60 100644
> --- a/arch/s390/net/bpf_jit_comp.c
> +++ b/arch/s390/net/bpf_jit_comp.c
> @@ -704,6 +704,10 @@ static void bpf_jit_probe_init(struct bpf_jit_probe *probe)
>  static void bpf_jit_probe_emit_nop(struct bpf_jit *jit,
>                                    struct bpf_jit_probe *probe)
>  {
> +       if (probe->prg == -1 || probe->nop_prg != -1)
> +               /* The probe is not armed or nop is already emitted. */
> +               return;
> +
>         probe->nop_prg = jit->prg;
>         /* bcr 0,%0 */
>         _EMIT2(0x0700);
> @@ -738,6 +742,21 @@ static void bpf_jit_probe_store_pre(struct bpf_jit *jit, struct bpf_insn *insn,
>         probe->prg = jit->prg;
>  }
>
> +static void bpf_jit_probe_atomic_pre(struct bpf_jit *jit,
> +                                    struct bpf_insn *insn,
> +                                    struct bpf_jit_probe *probe)
> +{
> +       if (BPF_MODE(insn->code) != BPF_PROBE_ATOMIC)
> +               return;
> +
> +       /* lgrl %r1,kern_arena */
> +       EMIT6_PCREL_RILB(0xc4080000, REG_W1, jit->kern_arena);
> +       /* agr %r1,%dst */
> +       EMIT4(0xb9080000, REG_W1, insn->dst_reg);
> +       probe->arena_reg = REG_W1;
> +       probe->prg = jit->prg;
> +}
> +
>  static int bpf_jit_probe_post(struct bpf_jit *jit, struct bpf_prog *fp,
>                               struct bpf_jit_probe *probe)
>  {
> @@ -1523,15 +1542,30 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
>          */
>         case BPF_STX | BPF_ATOMIC | BPF_DW:
>         case BPF_STX | BPF_ATOMIC | BPF_W:
> +       case BPF_STX | BPF_PROBE_ATOMIC | BPF_DW:
> +       case BPF_STX | BPF_PROBE_ATOMIC | BPF_W:
>         {
>                 bool is32 = BPF_SIZE(insn->code) == BPF_W;
>
> +               /*
> +                * Unlike loads and stores, atomics have only a base register,
> +                * but no index register. For the non-arena case, simply use
> +                * %dst as a base. For the arena case, use the work register
> +                * %r1: first, load the arena base into it, and then add %dst
> +                * to it.
> +                */
> +               probe.arena_reg = dst_reg;
> +
>                 switch (insn->imm) {
> -/* {op32|op64} {%w0|%src},%src,off(%dst) */
>  #define EMIT_ATOMIC(op32, op64) do {                                   \
> +       bpf_jit_probe_atomic_pre(jit, insn, &probe);                    \
> +       /* {op32|op64} {%w0|%src},%src,off(%arena) */                   \
>         EMIT6_DISP_LH(0xeb000000, is32 ? (op32) : (op64),               \
>                       (insn->imm & BPF_FETCH) ? src_reg : REG_W0,       \
> -                     src_reg, dst_reg, off);                           \
> +                     src_reg, probe.arena_reg, off);                   \
> +       err = bpf_jit_probe_post(jit, fp, &probe);                      \
> +       if (err < 0)                                                    \
> +               return err;                                             \
>         if (insn->imm & BPF_FETCH) {                                    \
>                 /* bcr 14,0 - see atomic_fetch_{add,and,or,xor}() */    \
>                 _EMIT2(0x07e0);                                         \
> @@ -1560,25 +1594,48 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
>                         EMIT_ATOMIC(0x00f7, 0x00e7);
>                         break;
>  #undef EMIT_ATOMIC
> -               case BPF_XCHG:
> -                       /* {ly|lg} %w0,off(%dst) */
> +               case BPF_XCHG: {
> +                       struct bpf_jit_probe load_probe = probe;
> +
> +                       bpf_jit_probe_atomic_pre(jit, insn, &load_probe);
> +                       /* {ly|lg} %w0,off(%arena) */
>                         EMIT6_DISP_LH(0xe3000000,
>                                       is32 ? 0x0058 : 0x0004, REG_W0, REG_0,
> -                                     dst_reg, off);
> -                       /* 0: {csy|csg} %w0,%src,off(%dst) */
> +                                     load_probe.arena_reg, off);
> +                       bpf_jit_probe_emit_nop(jit, &load_probe);
> +                       /* Reuse {ly|lg}'s arena_reg for {csy|csg}. */
> +                       if (load_probe.prg != -1) {
> +                               probe.prg = jit->prg;
> +                               probe.arena_reg = load_probe.arena_reg;
> +                       }
> +                       /* 0: {csy|csg} %w0,%src,off(%arena) */
>                         EMIT6_DISP_LH(0xeb000000, is32 ? 0x0014 : 0x0030,
> -                                     REG_W0, src_reg, dst_reg, off);
> +                                     REG_W0, src_reg, probe.arena_reg, off);
> +                       bpf_jit_probe_emit_nop(jit, &probe);
>                         /* brc 4,0b */
>                         EMIT4_PCREL_RIC(0xa7040000, 4, jit->prg - 6);
>                         /* {llgfr|lgr} %src,%w0 */
>                         EMIT4(is32 ? 0xb9160000 : 0xb9040000, src_reg, REG_W0);
> +                       /* Both probes should land here on exception. */
> +                       err = bpf_jit_probe_post(jit, fp, &load_probe);
> +                       if (err < 0)
> +                               return err;
> +                       err = bpf_jit_probe_post(jit, fp, &probe);
> +                       if (err < 0)
> +                               return err;
>                         if (is32 && insn_is_zext(&insn[1]))
>                                 insn_count = 2;
>                         break;
> +               }
>                 case BPF_CMPXCHG:
> -                       /* 0: {csy|csg} %b0,%src,off(%dst) */
> +                       bpf_jit_probe_atomic_pre(jit, insn, &probe);
> +                       /* 0: {csy|csg} %b0,%src,off(%arena) */
>                         EMIT6_DISP_LH(0xeb000000, is32 ? 0x0014 : 0x0030,
> -                                     BPF_REG_0, src_reg, dst_reg, off);
> +                                     BPF_REG_0, src_reg,
> +                                     probe.arena_reg, off);
> +                       err = bpf_jit_probe_post(jit, fp, &probe);
> +                       if (err < 0)
> +                               return err;
>                         break;
>                 default:
>                         pr_err("Unknown atomic operation %02x\n", insn->imm);
> @@ -2142,9 +2199,25 @@ static struct bpf_binary_header *bpf_jit_alloc(struct bpf_jit *jit,
>                                                struct bpf_prog *fp)
>  {
>         struct bpf_binary_header *header;
> +       struct bpf_insn *insn;
>         u32 extable_size;
>         u32 code_size;
> +       int i;
>
> +       for (i = 0; i < fp->len; i++) {
> +               insn = &fp->insnsi[i];
> +
> +               if (BPF_CLASS(insn->code) == BPF_STX &&
> +                   BPF_MODE(insn->code) == BPF_PROBE_ATOMIC &&
> +                   (BPF_SIZE(insn->code) == BPF_DW ||
> +                    BPF_SIZE(insn->code) == BPF_W) &&
> +                   insn->imm == BPF_XCHG)
> +                       /*
> +                        * bpf_jit_insn() emits a load and a compare-and-swap,
> +                        * both of which need to be probed.
> +                        */
> +                       fp->aux->num_exentries += 1;
> +       }
>         /* We need two entries per insn. */
>         fp->aux->num_exentries *= 2;
>
> @@ -2825,3 +2898,12 @@ bool bpf_jit_supports_arena(void)
>  {
>         return true;
>  }
> +
> +bool bpf_jit_supports_insn(struct bpf_insn *insn, bool in_arena)
> +{
> +       /*
> +        * Currently the verifier uses this function only to check which
> +        * atomic stores to arena are supported, and they all are.
> +        */
> +       return true;

Including all the multi insn instructions that are implemented as loops?
On x86 I left out atomic+fetch+[and|or|xor],
because they're tricky with looping.
Just checking that when an exception happens
the loop is not going to become infinite ?
If I'm reading the code correctly the exception handling will not only
skip one insn, but will skip the whole loop?





[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux