[PATCH] dma-buf: fix and rework dma_buf_poll v6

Daniel Vetter daniel.vetter at ffwll.ch
Tue Jul 20 12:04:27 UTC 2021


On Wed, Jul 14, 2021 at 3:21 PM Christian König
<ckoenig.leichtzumerken at gmail.com> wrote:
> Just a gentle ping. Or have I missed your reply?

Nah just got overwhelmed with dma-resv discussion and took a break. I
still think some igt (or reviewing what we have) would be good. We
could also just merge Jason's import/export series, since he's typed
the igts for that which us dma_buf poll.
-Daniel

>
> Thanks,
> Christian.
>
> Am 09.07.21 um 14:07 schrieb Christian König:
> > Daniel pointed me towards this function and there are multiple obvious problems
> > in the implementation.
> >
> > First of all the retry loop is not working as intended. In general the retry
> > makes only sense if you grab the reference first and then check the sequence
> > values.
> >
> > Then we should always also wait for the exclusive fence.
> >
> > It's also good practice to keep the reference around when installing callbacks
> > to fences you don't own.
> >
> > And last the whole implementation was unnecessary complex and rather hard to
> > understand which could lead to probably unexpected behavior of the IOCTL.
> >
> > Fix all this by reworking the implementation from scratch. Dropping the
> > whole RCU approach and taking the lock instead.
> >
> > Only mildly tested and needs a thoughtful review of the code.
> >
> > v2: fix the reference counting as well
> > v3: keep the excl fence handling as is for stable
> > v4: back to testing all fences, drop RCU
> > v5: handle in and out separately
> > v6: add missing clear of events
> >
> > Signed-off-by: Christian König <christian.koenig at amd.com>
> > CC: stable at vger.kernel.org
> > ---
> >   drivers/dma-buf/dma-buf.c | 156 +++++++++++++++++---------------------
> >   include/linux/dma-buf.h   |   2 +-
> >   2 files changed, 72 insertions(+), 86 deletions(-)
> >
> > diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
> > index eadd1eaa2fb5..39e1ef872829 100644
> > --- a/drivers/dma-buf/dma-buf.c
> > +++ b/drivers/dma-buf/dma-buf.c
> > @@ -72,7 +72,7 @@ static void dma_buf_release(struct dentry *dentry)
> >        * If you hit this BUG() it means someone dropped their ref to the
> >        * dma-buf while still having pending operation to the buffer.
> >        */
> > -     BUG_ON(dmabuf->cb_shared.active || dmabuf->cb_excl.active);
> > +     BUG_ON(dmabuf->cb_in.active || dmabuf->cb_out.active);
> >
> >       dmabuf->ops->release(dmabuf);
> >
> > @@ -202,16 +202,57 @@ static void dma_buf_poll_cb(struct dma_fence *fence, struct dma_fence_cb *cb)
> >       wake_up_locked_poll(dcb->poll, dcb->active);
> >       dcb->active = 0;
> >       spin_unlock_irqrestore(&dcb->poll->lock, flags);
> > +     dma_fence_put(fence);
> > +}
> > +
> > +static bool dma_buf_poll_shared(struct dma_resv *resv,
> > +                             struct dma_buf_poll_cb_t *dcb)
> > +{
> > +     struct dma_resv_list *fobj = dma_resv_get_list(resv);
> > +     struct dma_fence *fence;
> > +     int i, r;
> > +
> > +     if (!fobj)
> > +             return false;
> > +
> > +     for (i = 0; i < fobj->shared_count; ++i) {
> > +             fence = rcu_dereference_protected(fobj->shared[i],
> > +                                               dma_resv_held(resv));
> > +             dma_fence_get(fence);
> > +             r = dma_fence_add_callback(fence, &dcb->cb, dma_buf_poll_cb);
> > +             if (!r)
> > +                     return true;
> > +             dma_fence_put(fence);
> > +     }
> > +
> > +     return false;
> > +}
> > +
> > +static bool dma_buf_poll_excl(struct dma_resv *resv,
> > +                           struct dma_buf_poll_cb_t *dcb)
> > +{
> > +     struct dma_fence *fence = dma_resv_get_excl(resv);
> > +     int r;
> > +
> > +     if (!fence)
> > +             return false;
> > +
> > +     dma_fence_get(fence);
> > +     r = dma_fence_add_callback(fence, &dcb->cb, dma_buf_poll_cb);
> > +     if (!r)
> > +             return true;
> > +     dma_fence_put(fence);
> > +
> > +     return false;
> >   }
> >
> >   static __poll_t dma_buf_poll(struct file *file, poll_table *poll)
> >   {
> >       struct dma_buf *dmabuf;
> >       struct dma_resv *resv;
> > -     struct dma_resv_list *fobj;
> > -     struct dma_fence *fence_excl;
> > +     unsigned shared_count;
> >       __poll_t events;
> > -     unsigned shared_count, seq;
> > +     int r, i;
> >
> >       dmabuf = file->private_data;
> >       if (!dmabuf || !dmabuf->resv)
> > @@ -225,101 +266,46 @@ static __poll_t dma_buf_poll(struct file *file, poll_table *poll)
> >       if (!events)
> >               return 0;
> >
> > -retry:
> > -     seq = read_seqcount_begin(&resv->seq);
> > -     rcu_read_lock();
> > -
> > -     fobj = rcu_dereference(resv->fence);
> > -     if (fobj)
> > -             shared_count = fobj->shared_count;
> > -     else
> > -             shared_count = 0;
> > -     fence_excl = rcu_dereference(resv->fence_excl);
> > -     if (read_seqcount_retry(&resv->seq, seq)) {
> > -             rcu_read_unlock();
> > -             goto retry;
> > -     }
> > +     dma_resv_lock(resv, NULL);
> >
> > -     if (fence_excl && (!(events & EPOLLOUT) || shared_count == 0)) {
> > -             struct dma_buf_poll_cb_t *dcb = &dmabuf->cb_excl;
> > -             __poll_t pevents = EPOLLIN;
> > -
> > -             if (shared_count == 0)
> > -                     pevents |= EPOLLOUT;
> > +     if (events & EPOLLOUT) {
> > +             struct dma_buf_poll_cb_t *dcb = &dmabuf->cb_out;
> >
> > +             /* Check that callback isn't busy */
> >               spin_lock_irq(&dmabuf->poll.lock);
> > -             if (dcb->active) {
> > -                     dcb->active |= pevents;
> > -                     events &= ~pevents;
> > -             } else
> > -                     dcb->active = pevents;
> > +             if (dcb->active)
> > +                     events &= ~EPOLLOUT;
> > +             else
> > +                     dcb->active = EPOLLOUT;
> >               spin_unlock_irq(&dmabuf->poll.lock);
> >
> > -             if (events & pevents) {
> > -                     if (!dma_fence_get_rcu(fence_excl)) {
> > -                             /* force a recheck */
> > -                             events &= ~pevents;
> > -                             dma_buf_poll_cb(NULL, &dcb->cb);
> > -                     } else if (!dma_fence_add_callback(fence_excl, &dcb->cb,
> > -                                                        dma_buf_poll_cb)) {
> > -                             events &= ~pevents;
> > -                             dma_fence_put(fence_excl);
> > -                     } else {
> > -                             /*
> > -                              * No callback queued, wake up any additional
> > -                              * waiters.
> > -                              */
> > -                             dma_fence_put(fence_excl);
> > -                             dma_buf_poll_cb(NULL, &dcb->cb);
> > -                     }
> > -             }
> > +             if (events & EPOLLOUT && !dma_buf_poll_shared(resv, dcb) &&
> > +                 !dma_buf_poll_excl(resv, dcb))
> > +                     /* No callback queued, wake up any other waiters */
> > +                     dma_buf_poll_cb(NULL, &dcb->cb);
> > +             else
> > +                     events &= ~EPOLLOUT;
> >       }
> >
> > -     if ((events & EPOLLOUT) && shared_count > 0) {
> > -             struct dma_buf_poll_cb_t *dcb = &dmabuf->cb_shared;
> > -             int i;
> > +     if (events & EPOLLIN) {
> > +             struct dma_buf_poll_cb_t *dcb = &dmabuf->cb_in;
> >
> > -             /* Only queue a new callback if no event has fired yet */
> > +             /* Check that callback isn't busy */
> >               spin_lock_irq(&dmabuf->poll.lock);
> >               if (dcb->active)
> > -                     events &= ~EPOLLOUT;
> > +                     events &= ~EPOLLIN;
> >               else
> > -                     dcb->active = EPOLLOUT;
> > +                     dcb->active = EPOLLIN;
> >               spin_unlock_irq(&dmabuf->poll.lock);
> >
> > -             if (!(events & EPOLLOUT))
> > -                     goto out;
> > -
> > -             for (i = 0; i < shared_count; ++i) {
> > -                     struct dma_fence *fence = rcu_dereference(fobj->shared[i]);
> > -
> > -                     if (!dma_fence_get_rcu(fence)) {
> > -                             /*
> > -                              * fence refcount dropped to zero, this means
> > -                              * that fobj has been freed
> > -                              *
> > -                              * call dma_buf_poll_cb and force a recheck!
> > -                              */
> > -                             events &= ~EPOLLOUT;
> > -                             dma_buf_poll_cb(NULL, &dcb->cb);
> > -                             break;
> > -                     }
> > -                     if (!dma_fence_add_callback(fence, &dcb->cb,
> > -                                                 dma_buf_poll_cb)) {
> > -                             dma_fence_put(fence);
> > -                             events &= ~EPOLLOUT;
> > -                             break;
> > -                     }
> > -                     dma_fence_put(fence);
> > -             }
> > -
> > -             /* No callback queued, wake up any additional waiters. */
> > -             if (i == shared_count)
> > +             if (events & EPOLLIN && !dma_buf_poll_excl(resv, dcb))
> > +                     /* No callback queued, wake up any other waiters */
> >                       dma_buf_poll_cb(NULL, &dcb->cb);
> > +             else
> > +                     events &= ~EPOLLIN;
> >       }
> >
> > -out:
> > -     rcu_read_unlock();
> > +     dma_resv_unlock(resv);
> >       return events;
> >   }
> >
> > @@ -562,8 +548,8 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
> >       dmabuf->owner = exp_info->owner;
> >       spin_lock_init(&dmabuf->name_lock);
> >       init_waitqueue_head(&dmabuf->poll);
> > -     dmabuf->cb_excl.poll = dmabuf->cb_shared.poll = &dmabuf->poll;
> > -     dmabuf->cb_excl.active = dmabuf->cb_shared.active = 0;
> > +     dmabuf->cb_in.poll = dmabuf->cb_out.poll = &dmabuf->poll;
> > +     dmabuf->cb_in.active = dmabuf->cb_out.active = 0;
> >
> >       if (!resv) {
> >               resv = (struct dma_resv *)&dmabuf[1];
> > diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h
> > index efdc56b9d95f..7e747ad54c81 100644
> > --- a/include/linux/dma-buf.h
> > +++ b/include/linux/dma-buf.h
> > @@ -329,7 +329,7 @@ struct dma_buf {
> >               wait_queue_head_t *poll;
> >
> >               __poll_t active;
> > -     } cb_excl, cb_shared;
> > +     } cb_in, cb_out;
> >   };
> >
> >   /**
>


-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch


More information about the dri-devel mailing list