Merge tag 'compat-ioctl-5.5' of git://git.kernel.org:/pub/scm/linux/kernel/git/arnd...
[linux-2.6-microblaze.git] / drivers / dma-buf / dma-buf.c
index 433d91d..ce41cd9 100644 (file)
@@ -45,10 +45,10 @@ static char *dmabuffs_dname(struct dentry *dentry, char *buffer, int buflen)
        size_t ret = 0;
 
        dmabuf = dentry->d_fsdata;
-       mutex_lock(&dmabuf->lock);
+       dma_resv_lock(dmabuf->resv, NULL);
        if (dmabuf->name)
                ret = strlcpy(name, dmabuf->name, DMA_BUF_NAME_LEN);
-       mutex_unlock(&dmabuf->lock);
+       dma_resv_unlock(dmabuf->resv);
 
        return dynamic_dname(dentry, buffer, buflen, "/%s:%s",
                             dentry->d_name.name, ret > 0 ? name : "");
@@ -334,7 +334,7 @@ static long dma_buf_set_name(struct dma_buf *dmabuf, const char __user *buf)
        if (IS_ERR(name))
                return PTR_ERR(name);
 
-       mutex_lock(&dmabuf->lock);
+       dma_resv_lock(dmabuf->resv, NULL);
        if (!list_empty(&dmabuf->attachments)) {
                ret = -EBUSY;
                kfree(name);
@@ -344,7 +344,7 @@ static long dma_buf_set_name(struct dma_buf *dmabuf, const char __user *buf)
        dmabuf->name = name;
 
 out_unlock:
-       mutex_unlock(&dmabuf->lock);
+       dma_resv_unlock(dmabuf->resv);
        return ret;
 }
 
@@ -403,10 +403,10 @@ static void dma_buf_show_fdinfo(struct seq_file *m, struct file *file)
        /* Don't count the temporary reference taken inside procfs seq_show */
        seq_printf(m, "count:\t%ld\n", file_count(dmabuf->file) - 1);
        seq_printf(m, "exp_name:\t%s\n", dmabuf->exp_name);
-       mutex_lock(&dmabuf->lock);
+       dma_resv_lock(dmabuf->resv, NULL);
        if (dmabuf->name)
                seq_printf(m, "name:\t%s\n", dmabuf->name);
-       mutex_unlock(&dmabuf->lock);
+       dma_resv_unlock(dmabuf->resv);
 }
 
 static const struct file_operations dma_buf_fops = {
@@ -415,9 +415,7 @@ static const struct file_operations dma_buf_fops = {
        .llseek         = dma_buf_llseek,
        .poll           = dma_buf_poll,
        .unlocked_ioctl = dma_buf_ioctl,
-#ifdef CONFIG_COMPAT
-       .compat_ioctl   = dma_buf_ioctl,
-#endif
+       .compat_ioctl   = compat_ptr_ioctl,
        .show_fdinfo    = dma_buf_show_fdinfo,
 };
 
@@ -525,6 +523,10 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
                return ERR_PTR(-EINVAL);
        }
 
+       if (WARN_ON(exp_info->ops->cache_sgt_mapping &&
+                   exp_info->ops->dynamic_mapping))
+               return ERR_PTR(-EINVAL);
+
        if (!try_module_get(exp_info->owner))
                return ERR_PTR(-ENOENT);
 
@@ -645,10 +647,11 @@ void dma_buf_put(struct dma_buf *dmabuf)
 EXPORT_SYMBOL_GPL(dma_buf_put);
 
 /**
- * dma_buf_attach - Add the device to dma_buf's attachments list; optionally,
+ * dma_buf_dynamic_attach - Add the device to dma_buf's attachments list; optionally,
  * calls attach() of dma_buf_ops to allow device-specific attach functionality
- * @dmabuf:    [in]    buffer to attach device to.
- * @dev:       [in]    device to be attached.
+ * @dmabuf:            [in]    buffer to attach device to.
+ * @dev:               [in]    device to be attached.
+ * @dynamic_mapping:   [in]    calling convention for map/unmap
  *
  * Returns struct dma_buf_attachment pointer for this attachment. Attachments
  * must be cleaned up by calling dma_buf_detach().
@@ -662,8 +665,9 @@ EXPORT_SYMBOL_GPL(dma_buf_put);
  * accessible to @dev, and cannot be moved to a more suitable place. This is
  * indicated with the error code -EBUSY.
  */
-struct dma_buf_attachment *dma_buf_attach(struct dma_buf *dmabuf,
-                                         struct device *dev)
+struct dma_buf_attachment *
+dma_buf_dynamic_attach(struct dma_buf *dmabuf, struct device *dev,
+                      bool dynamic_mapping)
 {
        struct dma_buf_attachment *attach;
        int ret;
@@ -677,24 +681,68 @@ struct dma_buf_attachment *dma_buf_attach(struct dma_buf *dmabuf,
 
        attach->dev = dev;
        attach->dmabuf = dmabuf;
-
-       mutex_lock(&dmabuf->lock);
+       attach->dynamic_mapping = dynamic_mapping;
 
        if (dmabuf->ops->attach) {
                ret = dmabuf->ops->attach(dmabuf, attach);
                if (ret)
                        goto err_attach;
        }
+       dma_resv_lock(dmabuf->resv, NULL);
        list_add(&attach->node, &dmabuf->attachments);
+       dma_resv_unlock(dmabuf->resv);
 
-       mutex_unlock(&dmabuf->lock);
+       /* When either the importer or the exporter can't handle dynamic
+        * mappings we cache the mapping here to avoid issues with the
+        * reservation object lock.
+        */
+       if (dma_buf_attachment_is_dynamic(attach) !=
+           dma_buf_is_dynamic(dmabuf)) {
+               struct sg_table *sgt;
+
+               if (dma_buf_is_dynamic(attach->dmabuf))
+                       dma_resv_lock(attach->dmabuf->resv, NULL);
+
+               sgt = dmabuf->ops->map_dma_buf(attach, DMA_BIDIRECTIONAL);
+               if (!sgt)
+                       sgt = ERR_PTR(-ENOMEM);
+               if (IS_ERR(sgt)) {
+                       ret = PTR_ERR(sgt);
+                       goto err_unlock;
+               }
+               if (dma_buf_is_dynamic(attach->dmabuf))
+                       dma_resv_unlock(attach->dmabuf->resv);
+               attach->sgt = sgt;
+               attach->dir = DMA_BIDIRECTIONAL;
+       }
 
        return attach;
 
 err_attach:
        kfree(attach);
-       mutex_unlock(&dmabuf->lock);
        return ERR_PTR(ret);
+
+err_unlock:
+       if (dma_buf_is_dynamic(attach->dmabuf))
+               dma_resv_unlock(attach->dmabuf->resv);
+
+       dma_buf_detach(dmabuf, attach);
+       return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(dma_buf_dynamic_attach);
+
+/**
+ * dma_buf_attach - Wrapper for dma_buf_dynamic_attach
+ * @dmabuf:    [in]    buffer to attach device to.
+ * @dev:       [in]    device to be attached.
+ *
+ * Wrapper to call dma_buf_dynamic_attach() for drivers which still use a static
+ * mapping.
+ */
+struct dma_buf_attachment *dma_buf_attach(struct dma_buf *dmabuf,
+                                         struct device *dev)
+{
+       return dma_buf_dynamic_attach(dmabuf, dev, false);
 }
 EXPORT_SYMBOL_GPL(dma_buf_attach);
 
@@ -711,15 +759,22 @@ void dma_buf_detach(struct dma_buf *dmabuf, struct dma_buf_attachment *attach)
        if (WARN_ON(!dmabuf || !attach))
                return;
 
-       if (attach->sgt)
+       if (attach->sgt) {
+               if (dma_buf_is_dynamic(attach->dmabuf))
+                       dma_resv_lock(attach->dmabuf->resv, NULL);
+
                dmabuf->ops->unmap_dma_buf(attach, attach->sgt, attach->dir);
 
-       mutex_lock(&dmabuf->lock);
+               if (dma_buf_is_dynamic(attach->dmabuf))
+                       dma_resv_unlock(attach->dmabuf->resv);
+       }
+
+       dma_resv_lock(dmabuf->resv, NULL);
        list_del(&attach->node);
+       dma_resv_unlock(dmabuf->resv);
        if (dmabuf->ops->detach)
                dmabuf->ops->detach(dmabuf, attach);
 
-       mutex_unlock(&dmabuf->lock);
        kfree(attach);
 }
 EXPORT_SYMBOL_GPL(dma_buf_detach);
@@ -749,6 +804,9 @@ struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *attach,
        if (WARN_ON(!attach || !attach->dmabuf))
                return ERR_PTR(-EINVAL);
 
+       if (dma_buf_attachment_is_dynamic(attach))
+               dma_resv_assert_held(attach->dmabuf->resv);
+
        if (attach->sgt) {
                /*
                 * Two mappings with different directions for the same
@@ -761,6 +819,9 @@ struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *attach,
                return attach->sgt;
        }
 
+       if (dma_buf_is_dynamic(attach->dmabuf))
+               dma_resv_assert_held(attach->dmabuf->resv);
+
        sg_table = attach->dmabuf->ops->map_dma_buf(attach, direction);
        if (!sg_table)
                sg_table = ERR_PTR(-ENOMEM);
@@ -793,9 +854,15 @@ void dma_buf_unmap_attachment(struct dma_buf_attachment *attach,
        if (WARN_ON(!attach || !attach->dmabuf || !sg_table))
                return;
 
+       if (dma_buf_attachment_is_dynamic(attach))
+               dma_resv_assert_held(attach->dmabuf->resv);
+
        if (attach->sgt == sg_table)
                return;
 
+       if (dma_buf_is_dynamic(attach->dmabuf))
+               dma_resv_assert_held(attach->dmabuf->resv);
+
        attach->dmabuf->ops->unmap_dma_buf(attach, sg_table, direction);
 }
 EXPORT_SYMBOL_GPL(dma_buf_unmap_attachment);
@@ -1171,13 +1238,10 @@ static int dma_buf_debug_show(struct seq_file *s, void *unused)
                   "size", "flags", "mode", "count", "ino");
 
        list_for_each_entry(buf_obj, &db_list.head, list_node) {
-               ret = mutex_lock_interruptible(&buf_obj->lock);
 
-               if (ret) {
-                       seq_puts(s,
-                                "\tERROR locking buffer object: skipping\n");
-                       continue;
-               }
+               ret = dma_resv_lock_interruptible(buf_obj->resv, NULL);
+               if (ret)
+                       goto error_unlock;
 
                seq_printf(s, "%08zu\t%08x\t%08x\t%08ld\t%s\t%08lu\t%s\n",
                                buf_obj->size,
@@ -1223,19 +1287,23 @@ static int dma_buf_debug_show(struct seq_file *s, void *unused)
                        seq_printf(s, "\t%s\n", dev_name(attach_obj->dev));
                        attach_count++;
                }
+               dma_resv_unlock(buf_obj->resv);
 
                seq_printf(s, "Total %d devices attached\n\n",
                                attach_count);
 
                count++;
                size += buf_obj->size;
-               mutex_unlock(&buf_obj->lock);
        }
 
        seq_printf(s, "\nTotal %d objects, %zu bytes\n", count, size);
 
        mutex_unlock(&db_list.lock);
        return 0;
+
+error_unlock:
+       mutex_unlock(&db_list.lock);
+       return ret;
 }
 
 DEFINE_SHOW_ATTRIBUTE(dma_buf_debug);