Re: [PATCH bpf-next v7 12/26] bpf: Allow locking bpf_spin_lock global variables

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

 



On Tue, Nov 15, 2022 at 11:03:02PM IST, Dave Marchevsky wrote:
> On 11/14/22 2:15 PM, Kumar Kartikeya Dwivedi wrote:
> > Global variables reside in maps accessible using direct_value_addr
> > callbacks, so giving each load instruction's rewrite a unique reg->id
> > disallows us from holding locks which are global.
> >
> > The reason for preserving reg->id as a unique value for registers that
> > may point to spin lock is that two separate lookups are treated as two
> > separate memory regions, and any possible aliasing is ignored for the
> > purposes of spin lock correctness.
> >
> > This is not great especially for the global variable case, which are
> > served from maps that have max_entries == 1, i.e. they always lead to
> > map values pointing into the same map value.
> >
> > So refactor the active_spin_lock into a 'active_lock' structure which
> > represents the lock identity, and instead of the reg->id, remember two
> > fields, a pointer and the reg->id. The pointer will store reg->map_ptr
> > or reg->btf. It's only necessary to distinguish for the id == 0 case of
> > global variables, but always setting the pointer to a non-NULL value and
> > using the pointer to check whether the lock is held simplifies code in
> > the verifier.
> >
> > This is generic enough to allow it for global variables, map lookups,
> > and allocated objects at the same time.
> >
> > Note that while whether a lock is held can be answered by just comparing
> > active_lock.ptr to NULL, to determine whether the register is pointing
> > to the same held lock requires comparing _both_ ptr and id.
> >
> > Finally, as a result of this refactoring, pseudo load instructions are
> > not given a unique reg->id, as they are doing lookup for the same map
> > value (max_entries is never greater than 1).
> >
> > Essentially, we consider that the tuple of (ptr, id) will always be
> > unique for any kind of argument to bpf_spin_{lock,unlock}.
> >
> > Note that this can be extended in the future to also remember offset
> > used for locking, so that we can introduce multiple bpf_spin_lock fields
> > in the same allocation.
> >
> > Signed-off-by: Kumar Kartikeya Dwivedi <memxor@xxxxxxxxx>
> > ---
> >  include/linux/bpf_verifier.h | 10 ++++++++-
> >  kernel/bpf/verifier.c        | 41 ++++++++++++++++++++++++------------
> >  2 files changed, 37 insertions(+), 14 deletions(-)
> >
> > diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
> > index 1a32baa78ce2..fa738abea267 100644
> > --- a/include/linux/bpf_verifier.h
> > +++ b/include/linux/bpf_verifier.h
> > @@ -323,7 +323,15 @@ struct bpf_verifier_state {
> >  	u32 branches;
> >  	u32 insn_idx;
> >  	u32 curframe;
> > -	u32 active_spin_lock;
> > +	struct {
> > +		/* This can either be reg->map_ptr or reg->btf, but it is only
> > +		 * used to check whether the lock is held or not by comparing to
> > +		 * NULL.
> > +		 */
> > +		void *ptr;
> > +		/* This will be reg->id */
> > +		u32 id;
> > +	} active_lock;
>
> I didn't get back to you re: naming here, but I think these names are clear,
> especially with comments elaborating on the details. The first comment can
> be clarified a bit, though. Sounds like "is only used to check whether lock is
> held or not by comparing to NULL" is saying that active_lock.ptr is only
> compared to NULL in this patch, but changes to process_spin_lock check
> which results in verbose(env, "bpf_spin_unlock of different lock\n") are
> comparing it to another ptr.
>
> Maybe you're trying to say "if active_lock.ptr
> is NULL, there's no active lock and other fields in this struct don't hold
> anything valid. If non-NULL, there is an active lock held"?
>

Makes sense, I'll improve the comment.

> Separately, the line in patch summary with "we consider that the tuple of
> (ptr, id) will always be unique" would help with clarity if it was in the
> comments here.
>

Ack.

> >  	bool speculative;
> >
> >  	/* first and last insn idx of this verifier state */
> > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > index 070d003a99f0..99b5edb56978 100644
> > --- a/kernel/bpf/verifier.c
> > +++ b/kernel/bpf/verifier.c
> > @@ -1215,7 +1215,8 @@ static int copy_verifier_state(struct bpf_verifier_state *dst_state,
> >  	}
> >  	dst_state->speculative = src->speculative;
> >  	dst_state->curframe = src->curframe;
> > -	dst_state->active_spin_lock = src->active_spin_lock;
> > +	dst_state->active_lock.ptr = src->active_lock.ptr;
> > +	dst_state->active_lock.id = src->active_lock.id;
> >  	dst_state->branches = src->branches;
> >  	dst_state->parent = src->parent;
> >  	dst_state->first_insn_idx = src->first_insn_idx;
> > @@ -5587,7 +5588,7 @@ int check_kfunc_mem_size_reg(struct bpf_verifier_env *env, struct bpf_reg_state
> >   * Since only one bpf_spin_lock is allowed the checks are simpler than
> >   * reg_is_refcounted() logic. The verifier needs to remember only
> >   * one spin_lock instead of array of acquired_refs.
> > - * cur_state->active_spin_lock remembers which map value element got locked
> > + * cur_state->active_lock remembers which map value element got locked
>
> Maybe remove "map value" here? Since previous patch adds support for allocated
> object. "remembers which element got locked" or "remembers which map value
> or allocated object" better reflect current state of spin_lock support after
> these patches.
>

Ack, I should update other discrepancies wrt the previous update in this whole
comment block.

Thanks for the review.



[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