<br><br><div class="gmail_quote">On Wed, Mar 21, 2012 at 8:45 AM, Rob Clark <span dir="ltr">&lt;<a href="mailto:rob.clark@linaro.org">rob.clark@linaro.org</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div class="im">On Tue, Mar 20, 2012 at 3:53 PM, Daniel Vetter &lt;<a href="mailto:daniel.vetter@ffwll.ch">daniel.vetter@ffwll.ch</a>&gt; wrote:<br>
&gt; Let&#39;s have some competition here for dma_buf mmap support ;-)<br>
&gt;<br>
&gt; Compared to Rob Clarke&#39;s RFC I&#39;ve ditched the prepare/finish hooks<br>
&gt; and corresponding ioctls on the dma_buf file. The major reason for<br>
&gt; that is that many people seem to be under the impression that this is<br>
&gt; also for synchronization with outstanding asynchronous processsing.<br>
&gt; I&#39;m pretty massively opposed to this because:<br>
&gt;<br>
&gt; - It boils down reinventing a new rather general-purpose userspace<br>
&gt;  synchronization interface. If we look at things like futexes, this<br>
&gt;  is hard to get right.<br>
&gt; - Furthermore a lot of kernel code has to interact with this<br>
&gt;  synchronization primitive. This smells a look like the dri1 hw_lock,<br>
&gt;  a horror show I prefer not to reinvent.<br>
&gt; - Even more fun is that multiple different subsystems would interact<br>
&gt;  here, so we have plenty of opportunities to create funny deadlock<br>
&gt;  scenarios.<br>
&gt;<br>
&gt; I think synchronization is a wholesale different problem from data<br>
&gt; sharing and should be tackled as an orthogonal problem.<br>
<br>
</div>fwiw, I was debating about two approaches before I sent initial RFC..<br>
with or without the ioctl&#39;s.<br>
<br>
I do agree that trying to hide synchronization behind those is likely<br>
to be asking for trouble.  Although I think it is partially a symptom<br>
of missing a synchronization primitive that we could use.  (Ie. when<br>
all you have is a hammer, everything looks like a nail.)<br></blockquote><div><br></div><div>I&#39;m in 100% agreement with you both on synchronization being a separate problem.  The android team is working on a generic solution for that as well, expect to see some of it coming across this list in the next few weeks.  I do hate the idea that an implementer might abuse this api for that purpose.  </div>
<div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
On the other hand, it does significantly ease dealing with cached<br>
buffers, and seems useful for other debugging purposes (verifying<br>
userspace isn&#39;t accessing buffers when they shouldn&#39;t.  And adding<br>
required ioctls later is an API change.  Which was why I was leaning<br>
towards the approach of including ioctls but just reviewing closely<br>
the patches that add driver support.<br>
<br>
But, I have one idea.  What about including the ioctls, but just<br>
leaving them stubbed out (ie. not including the dmabuf ops).  It at<br>
least prevents drivers from abusing it, while leaving the API in place<br>
for userspace to avoid changing API to kernel later in a way that<br>
might break userspace.<br>
<br>
Other than that, the patch looks good.. including the extra<br>
error/sanity checking and documentation, which I really skimped on for<br>
the first version.  (I mainly just wanted to get the flame-war over<br>
ioctls going with the first version :-P)<br>
<br>
BR,<br>
-R<br>
<div><div class="h5"><br>
&gt; Now we could demand that prepare/finish may only ensure cache<br>
&gt; coherency (as Rob intended), but that runs up into the next problem:<br>
&gt; We not only need mmap support to facilitate sw-only processing nodes<br>
&gt; in a pipeline (without jumping through hoops by importing the dma_buf<br>
&gt; into some sw-access only importer), which allows for a nicer<br>
&gt; ION-&gt;dma-buf upgrade path for existing Android userspace. We also need<br>
&gt; mmap support for existing importing subsystems to support existing<br>
&gt; userspace libraries. And a loot of these subsystems are expected to<br>
&gt; export coherent userspace mappings.<br>
&gt;<br>
&gt; So prepare/finish can only ever be optional and the exporter /needs/<br>
&gt; to support coherent mappings. Given that mmap access is always<br>
&gt; somewhat fallback-y in nature I&#39;ve decided to drop this optimization,<br>
&gt; instead of just making it optional. If we demonstrate a clear need for<br>
&gt; this, supported by benchmark results, we can always add it in again<br>
&gt; later as an optional extension.<br>
&gt;<br>
&gt; Other differences compared to Rob&#39;s RFC is the above mentioned support<br>
&gt; for mapping a dma-buf through facilities provided by the importer.<br>
&gt; Which results in mmap support no longer being optional.<br>
&gt;<br>
&gt; Note taht this dma-buf mmap patch does _not_ support every possible<br>
&gt; insanity an existing subsystem could pull of with mmap: Because it<br>
&gt; does not allow to intercept pagefaults and shoot down ptes importing<br>
&gt; subsystems can&#39;t add some magic of their own at these points (e.g. to<br>
&gt; automatically synchronize with outstanding rendering or set up some<br>
&gt; special resources). I&#39;ve done a cursory read through a few mmap<br>
&gt; implementions of various subsytems and I&#39;m hopeful that we can avoid<br>
&gt; this (and the complexity it&#39;d bring with it).<br>
&gt;<br>
&gt; Additonally I&#39;ve extended the documentation a bit to explain the hows<br>
&gt; and whys of this mmap extension.<br>
&gt;<br>
&gt; Comments, reviews and flames highly welcome.<br>
&gt;<br>
&gt; Cheers, Daniel<br>
&gt; ---<br>
&gt;  Documentation/dma-buf-sharing.txt |   84 +++++++++++++++++++++++++++++++++---<br>
&gt;  drivers/base/dma-buf.c            |   64 +++++++++++++++++++++++++++-<br>
&gt;  include/linux/dma-buf.h           |   16 +++++++<br>
&gt;  3 files changed, 156 insertions(+), 8 deletions(-)<br>
&gt;<br>
&gt; diff --git a/Documentation/dma-buf-sharing.txt b/Documentation/dma-buf-sharing.txt<br>
&gt; index a6d4c37..c42a4a5 100644<br>
&gt; --- a/Documentation/dma-buf-sharing.txt<br>
&gt; +++ b/Documentation/dma-buf-sharing.txt<br>
&gt; @@ -29,13 +29,6 @@ The buffer-user<br>
&gt;    in memory, mapped into its own address space, so it can access the same area<br>
&gt;    of memory.<br>
&gt;<br>
&gt; -*IMPORTANT*: [see <a href="https://lkml.org/lkml/2011/12/20/211" target="_blank">https://lkml.org/lkml/2011/12/20/211</a> for more details]<br>
&gt; -For this first version, A buffer shared using the dma_buf sharing API:<br>
&gt; -- *may* be exported to user space using &quot;mmap&quot; *ONLY* by exporter, outside of<br>
&gt; -  this framework.<br>
&gt; -- with this new iteration of the dma-buf api cpu access from the kernel has been<br>
&gt; -  enable, see below for the details.<br>
&gt; -<br>
&gt;  dma-buf operations for device dma only<br>
&gt;  --------------------------------------<br>
&gt;<br>
&gt; @@ -313,6 +306,83 @@ Access to a dma_buf from the kernel context involves three steps:<br>
&gt;                                  enum dma_data_direction dir);<br>
&gt;<br>
&gt;<br>
&gt; +Direct Userspace Access/mmap Support<br>
&gt; +------------------------------------<br>
&gt; +<br>
&gt; +Being able to mmap an export dma-buf buffer object has 2 main use-cases:<br>
&gt; +- CPU fallback processing in a pipeline and<br>
&gt; +- supporting existing mmap interfaces in importers.<br>
&gt; +<br>
&gt; +1. CPU fallback processing in a pipeline<br>
&gt; +<br>
&gt; +   In many processing pipelines it is sometimes required that the cpu can access<br>
&gt; +   the data in a dma-buf (e.g. for thumbnail creation, snapshots, ...). To avoid<br>
&gt; +   the need to handle this specially in userspace frameworks for buffer sharing<br>
&gt; +   it&#39;s ideal if the dma_buf fd itself can be used to access the backing storage<br>
&gt; +   from userspace using mmap.<br>
&gt; +<br>
&gt; +   Furthermore Android&#39;s ION framework already supports this (and is otherwise<br>
&gt; +   rather similar to dma-buf from a userspace consumer side with using fds as<br>
&gt; +   handles, too). So it&#39;s beneficial to support this in a similar fashion on<br>
&gt; +   dma-buf to have a good transition path for existing Android userspace.<br>
&gt; +<br>
&gt; +   No special interfaces, userspace simply calls mmap on the dma-buf fd.<br>
&gt; +<br>
&gt; +2. Supporting existing mmap interfaces in exporters<br>
&gt; +<br>
&gt; +   Similar to the motivation for kernel cpu access it is again important that<br>
&gt; +   the userspace code of a given importing subsystem can use the same interfaces<br>
&gt; +   with a imported dma-buf buffer object as with a native buffer object. This is<br>
&gt; +   especially important for drm where the userspace part of contemporary OpenGL,<br>
&gt; +   X, and other drivers is huge, and reworking them to use a different way to<br>
&gt; +   mmap a buffer rather invasive.<br>
&gt; +<br>
&gt; +   The assumption in the current dma-buf interfaces is that redirecting the<br>
&gt; +   initial mmap is all that&#39;s needed. A survey of some of the existing<br>
&gt; +   subsystems shows that no driver seems to do any nefarious thing like syncing<br>
&gt; +   up with outstanding asynchronous processing on the device or allocating<br>
&gt; +   special resources at fault time. So hopefully this is good enough, since<br>
&gt; +   adding interfaces to intercept pagefaults and allow pte shootdowns would<br>
&gt; +   increase the complexity quite a bit.<br>
&gt; +<br>
&gt; +   Interface:<br>
&gt; +      int dma_buf_mmap(struct dma_buf *, struct vm_area_struct *,<br>
&gt; +                      unsigned long);<br>
&gt; +<br>
&gt; +   If the importing subsystem simply provides a special-purpose mmap call to set<br>
&gt; +   up a mapping in userspace, calling do_mmap with dma_buf-&gt;file will equally<br>
&gt; +   achieve that for a dma-buf object.<br>
&gt; +<br>
&gt; +3. Implementation notes for exporters<br>
&gt; +<br>
&gt; +   Because dma-buf buffers have invariant size over their lifetime, the dma-buf<br>
&gt; +   core checks whether a vma is too large and rejects such mappings. The<br>
&gt; +   exporter hence does not need to duplicate this check.<br>
&gt; +<br>
&gt; +   Because existing importing subsystems might presume coherent mappings for<br>
&gt; +   userspace, the exporter needs to set up a coherent mapping. If that&#39;s not<br>
&gt; +   possible, it needs to fake coherency by manually shooting down ptes when<br>
&gt; +   leaving the cpu domain and flushing caches at fault time. Note that all the<br>
&gt; +   dma_buf files share the same anon inode, hence the exporter needs to replace<br>
&gt; +   the dma_buf file stored in vma-&gt;vm_file with it&#39;s own if pte shootdown is<br>
&gt; +   requred. This is because the kernel uses the underlying inode&#39;s address_space<br>
&gt; +   for vma tracking (and hence pte tracking at shootdown time with<br>
&gt; +   unmap_mapping_range).<br></div></div></blockquote><div><br></div><div>I want to make sure I understand how this would work.  I&#39;ve been planning on making cache maintenance implicit, and most of the corresponding userspace components I&#39;ve seen for android expect to do implicit cache maintenance on these buffers if they need cached mappings.  The android framework has a logical place for this maintenance to take place.  I assume that you&#39;d detect a buffer leaving the cpu domain by using the dma_data_direction passed to dma_buf_map_attachment?  We&#39;re definitely pushing a bunch of complexity into the exporter, that at least on android could easily be covered by an explicit api.  I&#39;m not dead set against it, I just want to make sure I get it right if we go down this road.  </div>
<div><br></div><div>Rebecca</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div><div class="h5">
&gt; +<br>
&gt; +   If the above shootdown dance turns out to be too expensive in certain<br>
&gt; +   scenarios, we can extend dma-buf with a more explicit cache tracking scheme<br>
&gt; +   for userspace mappings. But the current assumption is that using mmap is<br>
&gt; +   always a slower path, so some inefficiencies should be acceptable.<br>
&gt; +<br>
&gt; +   Exporters that shoot down mappings (for any reasons) shall not do any<br>
&gt; +   synchronization at fault time with outstanding device operations.<br>
&gt; +   Synchronization is an orthogonal issue to sharing the backing storage of a<br>
&gt; +   buffer and hence should not be handled by dma-buf itself. This is explictly<br>
&gt; +   mentioned here because many people seem to want something like this, but if<br>
&gt; +   different exporters handle this differently, buffer sharing can fail in<br>
&gt; +   interesting ways depending upong the exporter (if userspace starts depending<br>
&gt; +   upon this implicit synchronization).<br>
&gt; +<br>
&gt;  Miscellaneous notes<br>
&gt;  -------------------<br>
&gt;<br>
&gt; diff --git a/drivers/base/dma-buf.c b/drivers/base/dma-buf.c<br>
&gt; index 07cbbc6..f3b923c 100644<br>
&gt; --- a/drivers/base/dma-buf.c<br>
&gt; +++ b/drivers/base/dma-buf.c<br>
&gt; @@ -44,8 +44,26 @@ static int dma_buf_release(struct inode *inode, struct file *file)<br>
&gt;        return 0;<br>
&gt;  }<br>
&gt;<br>
&gt; +static int dma_buf_mmap_internal(struct file *file, struct vm_areat_struct *vma)<br>
&gt; +{<br>
&gt; +       struct dma_buf *dmabuf;<br>
&gt; +<br>
&gt; +       if (!is_dma_buf_file(file))<br>
&gt; +               return -EINVAL;<br>
&gt; +<br>
&gt; +       /* check for overflowing the buffer&#39;s size */<br>
&gt; +       if (vma-&gt;vm_pgoff + ((vma-&gt;vm_end - vma-&gt;vm_start) &gt;&gt; PAGE_SHIFT) &gt;<br>
&gt; +           dmabuf-&gt;size &gt;&gt; PAGE_SHIFT)<br>
&gt; +               return -EINVAL;<br>
&gt; +<br>
&gt; +       dmabuf = file-&gt;private_data;<br>
&gt; +<br>
&gt; +       return dmabuf-&gt;ops-&gt;mmap(dmabuf, vma);<br>
&gt; +}<br>
&gt; +<br>
&gt;  static const struct file_operations dma_buf_fops = {<br>
&gt;        .release        = dma_buf_release,<br>
&gt; +       .mmap           = dma_buf_mmap_internal,<br>
&gt;  };<br>
&gt;<br>
&gt;  /*<br>
&gt; @@ -82,7 +100,8 @@ struct dma_buf *dma_buf_export(void *priv, const struct dma_buf_ops *ops,<br>
&gt;                          || !ops-&gt;unmap_dma_buf<br>
&gt;                          || !ops-&gt;release<br>
&gt;                          || !ops-&gt;kmap_atomic<br>
&gt; -                         || !ops-&gt;kmap)) {<br>
&gt; +                         || !ops-&gt;kmap<br>
&gt; +                         || !ops-&gt;mmap)) {<br>
&gt;                return ERR_PTR(-EINVAL);<br>
&gt;        }<br>
&gt;<br>
&gt; @@ -406,3 +425,46 @@ void dma_buf_kunmap(struct dma_buf *dmabuf, unsigned long page_num,<br>
&gt;                dmabuf-&gt;ops-&gt;kunmap(dmabuf, page_num, vaddr);<br>
&gt;  }<br>
&gt;  EXPORT_SYMBOL_GPL(dma_buf_kunmap);<br>
&gt; +<br>
&gt; +<br>
&gt; +/**<br>
&gt; + * dma_buf_mmap - Setup up a userspace mmap with the given vma<br>
&gt; + * @dma_buf:   [in]    buffer that should back the vma<br>
&gt; + * @vma:       [in]    vma for the mmap<br>
&gt; + * @pgoff:     [in]    offset in pages where this mmap should start within the<br>
&gt; + *                     dma-buf buffer.<br>
&gt; + *<br>
&gt; + * This function adjusts the passed in vma so that it points at the file of the<br>
&gt; + * dma_buf operation. It alsog adjusts the starting pgoff and does bounds<br>
&gt; + * checking on the size of the vma. Then it calls the exporters mmap function to<br>
&gt; + * set up the mapping.<br>
&gt; + *<br>
&gt; + * Can return negative error values, returns 0 on success.<br>
&gt; + */<br>
&gt; +int dma_buf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma,<br>
&gt; +                unsigned long pgoff)<br>
&gt; +{<br>
&gt; +       if (WARN_ON(!dmabuf || !vma))<br>
&gt; +               return -EINVAL;<br>
&gt; +<br>
&gt; +       /* check for offset overflow */<br>
&gt; +       if (pgoff + ((vma-&gt;vm_end - vma-&gt;vm_start) &gt;&gt; PAGE_SHIFT) &lt; pgoff)<br>
&gt; +               return -EOVERFLOW;<br>
&gt; +<br>
&gt; +       /* check for overflowing the buffer&#39;s size */<br>
&gt; +       if (pgoff + ((vma-&gt;vm_end - vma-&gt;vm_start) &gt;&gt; PAGE_SHIFT) &gt;<br>
&gt; +           dmabuf-&gt;size &gt;&gt; PAGE_SHIFT)<br>
&gt; +               return -EINVAL;<br>
&gt; +<br>
&gt; +       /* readjust the vma */<br>
&gt; +       if (vma-&gt;vm_file)<br>
&gt; +               fput(vma-&gt;vm_file);<br>
&gt; +<br>
&gt; +       vma-&gt;vm_file = dmabuf-&gt;file;<br>
&gt; +       get_file(vma-&gt;vm_file);<br>
&gt; +<br>
&gt; +       vma-&gt;vm_pgoff = pgoff;<br>
&gt; +<br>
&gt; +       return dmabuf-&gt;ops-&gt;mmap(dmabuf, vma);<br>
&gt; +}<br>
&gt; +EXPORT_SYMBOL_GPL(dma_buf_mmap);<br>
&gt; diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h<br>
&gt; index ee7ef99..d254630 100644<br>
&gt; --- a/include/linux/dma-buf.h<br>
&gt; +++ b/include/linux/dma-buf.h<br>
&gt; @@ -61,6 +61,10 @@ struct dma_buf_attachment;<br>
&gt;  *                This Callback must not sleep.<br>
&gt;  * @kmap: maps a page from the buffer into kernel address space.<br>
&gt;  * @kunmap: [optional] unmaps a page from the buffer.<br>
&gt; + * @mmap: used to expose the backing storage to userspace. Note that the<br>
&gt; + *       mapping needs to be coherent - if the exporter doesn&#39;t directly<br>
&gt; + *       support this, it needs to fake coherency by shooting down any ptes<br>
&gt; + *       when transitioning away from the cpu domain.<br>
&gt;  */<br>
&gt;  struct dma_buf_ops {<br>
&gt;        int (*attach)(struct dma_buf *, struct device *,<br>
&gt; @@ -92,6 +96,8 @@ struct dma_buf_ops {<br>
&gt;        void (*kunmap_atomic)(struct dma_buf *, unsigned long, void *);<br>
&gt;        void *(*kmap)(struct dma_buf *, unsigned long);<br>
&gt;        void (*kunmap)(struct dma_buf *, unsigned long, void *);<br>
&gt; +<br>
&gt; +       int (*mmap)(struct dma_buf *, struct vm_area_struct *vma);<br>
&gt;  };<br>
&gt;<br>
&gt;  /**<br>
&gt; @@ -167,6 +173,9 @@ void *dma_buf_kmap_atomic(struct dma_buf *, unsigned long);<br>
&gt;  void dma_buf_kunmap_atomic(struct dma_buf *, unsigned long, void *);<br>
&gt;  void *dma_buf_kmap(struct dma_buf *, unsigned long);<br>
&gt;  void dma_buf_kunmap(struct dma_buf *, unsigned long, void *);<br>
&gt; +<br>
&gt; +int dma_buf_mmap(struct dma_buf *, struct vm_area_struct *,<br>
&gt; +                unsigned long);<br>
&gt;  #else<br>
&gt;<br>
&gt;  static inline struct dma_buf_attachment *dma_buf_attach(struct dma_buf *dmabuf,<br>
&gt; @@ -247,6 +256,13 @@ static inline void dma_buf_kunmap(struct dma_buf *, unsigned long,<br>
&gt;                                  void *)<br>
&gt;  {<br>
&gt;  }<br>
&gt; +<br>
&gt; +static inline int dma_buf_mmap(struct dma_buf *,<br>
&gt; +                              struct vm_area_struct *vma,<br>
&gt; +                              unsigned long pgoff)<br>
&gt; +{<br>
&gt; +       return -ENODEV;<br>
&gt; +}<br>
&gt;  #endif /* CONFIG_DMA_SHARED_BUFFER */<br>
&gt;<br>
&gt;  #endif /* __DMA_BUF_H__ */<br>
&gt; --<br>
&gt; 1.7.7.5<br>
&gt;<br>
&gt; _______________________________________________<br>
</div></div>&gt; dri-devel mailing list<br>
&gt; <a href="mailto:dri-devel@lists.freedesktop.org">dri-devel@lists.freedesktop.org</a><br>
&gt; <a href="http://lists.freedesktop.org/mailman/listinfo/dri-devel" target="_blank">http://lists.freedesktop.org/mailman/listinfo/dri-devel</a><br>
<div class="HOEnZb"><div class="h5"><br>
_______________________________________________<br>
Linaro-mm-sig mailing list<br>
<a href="mailto:Linaro-mm-sig@lists.linaro.org">Linaro-mm-sig@lists.linaro.org</a><br>
<a href="http://lists.linaro.org/mailman/listinfo/linaro-mm-sig" target="_blank">http://lists.linaro.org/mailman/listinfo/linaro-mm-sig</a><br>
</div></div></blockquote></div><br>