On Mon, Nov 28, 2016 at 04:01:14PM +0100, Martin Pieuchot wrote:

> @@ -313,7 +319,13 @@ bpf_detachd(struct bpf_d *d)
>               int error;
>  
>               d->bd_promisc = 0;
> +
> +             bpf_get(d);
> +             mtx_leave(&d->bd_mtx);
>               error = ifpromisc(bp->bif_ifp, 0);
> +             mtx_enter(&d->bd_mtx);
> +             bpf_put(d);
> +

If I see something like this, I ask myself wether bp has to be
protected by the mutex like this:

                bpf_get(d);
                ifp = bp->bif_ifp;
                mtx_leave(&d->bd_mtx);
                error = ifpromisc(ifp, 0);
                mtx_enter(&d->bd_mtx);
                bpf_put(d);

But I think your code is safe as bd_bif is only modified with kernel
lock.

> @@ -542,6 +568,7 @@ bpf_wakeup_cb(void *xd)
>  
>       selwakeup(&d->bd_sel);
>       bpf_put(d);
> +
>  }
>  
>  int

This new line is wrong.

> @@ -717,7 +753,9 @@ bpfioctl(dev_t dev, u_long cmd, caddr_t 
>                               *(u_int *)addr = size = bpf_maxbufsize;
>                       else if (size < BPF_MINBUFSIZE)
>                               *(u_int *)addr = size = BPF_MINBUFSIZE;
> +                     mtx_enter(&d->bd_mtx);
>                       d->bd_bufsize = size;
> +                     mtx_leave(&d->bd_mtx);
>               }
>               break;
>  

bd_bufsize needs protection, but you only add it in BIOCSBLEN,
you should also put a mutex in BIOCGBLEN.

> @@ -778,30 +815,36 @@ bpfioctl(dev_t dev, u_long cmd, caddr_t 
>        * Get device parameters.
>        */
>       case BIOCGDLT:
> +             mtx_enter(&d->bd_mtx);
>               if (d->bd_bif == NULL)
>                       error = EINVAL;
>               else
>                       *(u_int *)addr = d->bd_bif->bif_dlt;
> +             mtx_leave(&d->bd_mtx);
>               break;

bd_bif is not protected elsewhere.  bif_dlt is only set during attach.
So I think you need no mutex here.

>  
>       /*
>        * Set device parameters.
>        */
>       case BIOCSDLT:
> +             mtx_enter(&d->bd_mtx);
>               if (d->bd_bif == NULL)
>                       error = EINVAL;
>               else
>                       error = bpf_setdlt(d, *(u_int *)addr);
> +             mtx_leave(&d->bd_mtx);
>               break;

bd_bif needs not protection, so put the mutex only in the else path.
Then it is obvious why it is there.

>  
>       /*
>        * Set interface name.
>        */
>       case BIOCGETIF:
> +             mtx_enter(&d->bd_mtx);
>               if (d->bd_bif == NULL)
>                       error = EINVAL;
>               else
>                       bpf_ifname(d->bd_bif->bif_ifp, (struct ifreq *)addr);
> +             mtx_leave(&d->bd_mtx);
>               break;

The interface name is not used by bpf without kernel lock.  You do
not need a mutex here.

> @@ -1151,6 +1197,8 @@ filt_bpfread(struct knote *kn, long hint
>  {
>       struct bpf_d *d = kn->kn_hook;
>  
> +     KERNEL_ASSERT_LOCKED();
> +
>       kn->kn_data = d->bd_hlen;
>       if (d->bd_immediate)
>               kn->kn_data += d->bd_slen;

You need the mutex here to access bd_slen.

> @@ -1232,12 +1279,10 @@ _bpf_mtap(caddr_t arg, const struct mbuf
>                       if (!gottime++)
>                               microtime(&tv);
>  
> -                     KERNEL_LOCK();
> -                     s = splnet();
> +                     mtx_enter(&d->bd_mtx);
>                       bpf_catchpacket(d, (u_char *)m, pktlen, slen, cpfn,
>                           &tv);
> -                     splx(s);
> -                     KERNEL_UNLOCK();
> +                     mtx_leave(&d->bd_mtx);
>  
>                       if (d->bd_fildrop)
>                               drop = 1;

bd_fildrop can be changed by ioctl.  Can we expect to read the
correct value without a memory barrier?

Same question applies to bd_dirfilt.

bd_rcount is a long value incremented atomically in _bpf_mtap().
It is read from an other CPU with kernel lock during an ioctl.  Is
this sufficient to get a consisten value?  Do we also need an atomic
operation when reading it?

> --- net/bpfdesc.h     22 Aug 2016 10:40:36 -0000      1.31
> +++ net/bpfdesc.h     28 Nov 2016 14:51:06 -0000
> @@ -56,15 +56,17 @@ struct bpf_d {
>        *   fbuf (free) - When read is done, put cluster here.
>        * On receiving, if sbuf is full and fbuf is 0, packet is dropped.
>        */
> +     struct mutex    bd_mtx;         /* protect buffer slots below */
>       caddr_t         bd_sbuf;        /* store slot */
>       caddr_t         bd_hbuf;        /* hold slot */
>       caddr_t         bd_fbuf;        /* free slot */
>       int             bd_slen;        /* current length of store buffer */
>       int             bd_hlen;        /* current length of hold buffer */
> -
>       int             bd_bufsize;     /* absolute length of buffers */

So bd_mtx is protecting the fields above.  Everything below must
either be not accessed without kernel lock, or only be set during
attach.  Is that correct or do I have a wrong understanding of the
SMP model?

>  
> -     struct bpf_if * bd_bif;         /* interface descriptor */
> +     int             __in_uiomove;

Why do you prefix it with __ ?  bd_uiomove would be more consistent.

bluhm

Reply via email to