diff mbox series

[v1,2/3] dma-buf: add dma buffer release notifier callback

Message ID 835ecf35d2d2d1ea763fe25837f52297c83c511f.1672666311.git.oleksii_moisieiev@epam.com
State New
Headers show
Series Add ioctls to map grant refs on the external backing storage | expand

Commit Message

Oleksii Moisieiev Jan. 2, 2023, 1:41 p.m. UTC
Add posibility to register callback on dma-buffer which is
called before dma_buf->ops->release call.
This helps when external user of the dma buffer should be notified
before buffer releases without changing dma-buf ops. This is needed when
external dma buffer is used as backing storage for gntdev refs export
and grant refs should be unmapped before dma buffer release.

Signed-off-by: Oleksii Moisieiev <oleksii_moisieiev@epam.com>
---
 drivers/dma-buf/dma-buf.c | 44 +++++++++++++++++++++++++++++++++++++++
 include/linux/dma-buf.h   | 15 +++++++++++++
 2 files changed, 59 insertions(+)
diff mbox series

Patch

diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
index efb4990b29e1..3e663ef92e1f 100644
--- a/drivers/dma-buf/dma-buf.c
+++ b/drivers/dma-buf/dma-buf.c
@@ -25,6 +25,7 @@ 
 #include <linux/dma-resv.h>
 #include <linux/mm.h>
 #include <linux/mount.h>
+#include <linux/notifier.h>
 #include <linux/pseudo_fs.h>
 
 #include <uapi/linux/dma-buf.h>
@@ -57,6 +58,46 @@  static char *dmabuffs_dname(struct dentry *dentry, char *buffer, int buflen)
 			     dentry->d_name.name, ret > 0 ? name : "");
 }
 
+int dma_buf_register_release_notifier(struct dma_buf *dmabuf,
+			ext_release_notifier_cb ext_release_cb, void *priv)
+{
+	int ret = 0;
+
+	spin_lock(&dmabuf->ext_release_lock);
+
+	if (dmabuf->ext_release_cb) {
+		ret = -EEXIST;
+		goto unlock;
+	}
+
+	dmabuf->ext_release_cb = ext_release_cb;
+	dmabuf->ext_release_priv = priv;
+ unlock:
+	spin_unlock(&dmabuf->ext_release_lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dma_buf_register_release_notifier);
+
+void dma_buf_unregister_release_notifier(struct dma_buf *dmabuf)
+{
+	spin_lock(&dmabuf->ext_release_lock);
+	dmabuf->ext_release_cb = NULL;
+	spin_unlock(&dmabuf->ext_release_lock);
+}
+EXPORT_SYMBOL_GPL(dma_buf_unregister_release_notifier);
+
+static void dma_buf_call_release_notifier(struct dma_buf *dmabuf)
+{
+	if (!dmabuf->ext_release_cb)
+		return;
+
+	spin_lock(&dmabuf->ext_release_lock);
+	dmabuf->ext_release_cb(dmabuf, dmabuf->ext_release_priv);
+	spin_unlock(&dmabuf->ext_release_lock);
+
+	dma_buf_unregister_release_notifier(dmabuf);
+}
+
 static void dma_buf_release(struct dentry *dentry)
 {
 	struct dma_buf *dmabuf;
@@ -75,6 +116,8 @@  static void dma_buf_release(struct dentry *dentry)
 	BUG_ON(dmabuf->cb_in.active || dmabuf->cb_out.active);
 
 	dma_buf_stats_teardown(dmabuf);
+	dma_buf_call_release_notifier(dmabuf);
+
 	dmabuf->ops->release(dmabuf);
 
 	if (dmabuf->resv == (struct dma_resv *)&dmabuf[1])
@@ -642,6 +685,7 @@  struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
 	init_waitqueue_head(&dmabuf->poll);
 	dmabuf->cb_in.poll = dmabuf->cb_out.poll = &dmabuf->poll;
 	dmabuf->cb_in.active = dmabuf->cb_out.active = 0;
+	spin_lock_init(&dmabuf->ext_release_lock);
 
 	if (!resv) {
 		resv = (struct dma_resv *)&dmabuf[1];
diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h
index 71731796c8c3..6282d56ac040 100644
--- a/include/linux/dma-buf.h
+++ b/include/linux/dma-buf.h
@@ -287,6 +287,8 @@  struct dma_buf_ops {
 	void (*vunmap)(struct dma_buf *dmabuf, struct iosys_map *map);
 };
 
+typedef void (*ext_release_notifier_cb)(struct dma_buf *dmabuf, void *priv);
+
 /**
  * struct dma_buf - shared buffer object
  *
@@ -432,6 +434,15 @@  struct dma_buf {
 	 */
 	struct dma_resv *resv;
 
+	/** @ext_release_cb notififier callback to call on release */
+	ext_release_notifier_cb ext_release_cb;
+
+	/** @ext_release_priv private data for callback */
+	void *ext_release_priv;
+
+	/** @ext_release_lock spinlock for ext_notifier helper */
+	spinlock_t ext_release_lock;
+
 	/** @poll: for userspace poll support */
 	wait_queue_head_t poll;
 
@@ -632,4 +643,8 @@  int dma_buf_mmap(struct dma_buf *, struct vm_area_struct *,
 		 unsigned long);
 int dma_buf_vmap(struct dma_buf *dmabuf, struct iosys_map *map);
 void dma_buf_vunmap(struct dma_buf *dmabuf, struct iosys_map *map);
+
+int dma_buf_register_release_notifier(struct dma_buf *dmabuf,
+		 ext_release_notifier_cb ext_release_cb, void *priv);
+void dma_buf_unregister_release_notifier(struct dma_buf *dmabuf);
 #endif /* __DMA_BUF_H__ */