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

From: Oleksii Moisieiev
Date: Mon Jan 02 2023 - 08:42:24 EST


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@xxxxxxxx>
---
drivers/dma-buf/dma-buf.c | 44 +++++++++++++++++++++++++++++++++++++++
include/linux/dma-buf.h | 15 +++++++++++++
2 files changed, 59 insertions(+)

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__ */
--
2.25.1