[RFC PATCH 4/7] dma-buf: uapi: Mechanism to revoke DMABUFs via ioctl()

Matt Evans posted 7 patches 1 month, 1 week ago
There is a newer version of this series
[RFC PATCH 4/7] dma-buf: uapi: Mechanism to revoke DMABUFs via ioctl()
Posted by Matt Evans 1 month, 1 week ago
Add a new dma-buf ioctl() op, DMA_BUF_IOCTL_REVOKE, connected to a new
(optional) dma_buf_ops callback, revoke().  An exporter receiving this
will _permanently_ revoke the DMABUF, meaning it can no longer be
mapped/attached/mmap()ed.  It also guarantees that existing
importers have been detached (e.g. via move_notify) and all mappings
made inaccessible.

This is useful for lifecycle management in scenarios where a process
has created a DMABUF representing a resource, then delegated it to
a client process; access to the resource is revoked when the client is
deemed "done", and the resource can be safely re-used elsewhere.

Signed-off-by: Matt Evans <mattev@meta.com>
---
 drivers/dma-buf/dma-buf.c    |  5 +++++
 include/linux/dma-buf.h      | 22 ++++++++++++++++++++++
 include/uapi/linux/dma-buf.h |  1 +
 3 files changed, 28 insertions(+)

diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
index edaa9e4ee4ae..b9b315317f2d 100644
--- a/drivers/dma-buf/dma-buf.c
+++ b/drivers/dma-buf/dma-buf.c
@@ -561,6 +561,11 @@ static long dma_buf_ioctl(struct file *file,
 	case DMA_BUF_IOCTL_IMPORT_SYNC_FILE:
 		return dma_buf_import_sync_file(dmabuf, (const void __user *)arg);
 #endif
+	case DMA_BUF_IOCTL_REVOKE:
+		if (dmabuf->ops->revoke)
+			return dmabuf->ops->revoke(dmabuf);
+		else
+			return -EINVAL;
 
 	default:
 		return -ENOTTY;
diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h
index 0bc492090237..a68c9ad7aebd 100644
--- a/include/linux/dma-buf.h
+++ b/include/linux/dma-buf.h
@@ -277,6 +277,28 @@ struct dma_buf_ops {
 
 	int (*vmap)(struct dma_buf *dmabuf, struct iosys_map *map);
 	void (*vunmap)(struct dma_buf *dmabuf, struct iosys_map *map);
+
+	/**
+	 * @revoke:
+	 *
+	 * This callback is invoked from a userspace
+	 * DMA_BUF_IOCTL_REVOKE operation, and requests that access to
+	 * the buffer is immediately and permanently revoked.  On
+	 * successful return, the buffer is not accessible through any
+	 * mmap() or dma-buf import.  The request fails if the buffer
+	 * is pinned; otherwise, the exporter marks the buffer as
+	 * inaccessible and uses the move_notify callback to inform
+	 * importers of the change.  The buffer is permanently
+	 * disabled, and the exporter must refuse all map, mmap,
+	 * attach, etc. requests.
+	 *
+	 * Returns:
+	 *
+	 * 0 on success, or a negative error code on failure:
+	 * -ENODEV if the associated device no longer exists/is closed.
+	 * -EBADFD if the buffer has already been revoked.
+	 */
+	int (*revoke)(struct dma_buf *dmabuf);
 };
 
 /**
diff --git a/include/uapi/linux/dma-buf.h b/include/uapi/linux/dma-buf.h
index 5a6fda66d9ad..84bf2dd2d0f3 100644
--- a/include/uapi/linux/dma-buf.h
+++ b/include/uapi/linux/dma-buf.h
@@ -178,5 +178,6 @@ struct dma_buf_import_sync_file {
 #define DMA_BUF_SET_NAME_B	_IOW(DMA_BUF_BASE, 1, __u64)
 #define DMA_BUF_IOCTL_EXPORT_SYNC_FILE	_IOWR(DMA_BUF_BASE, 2, struct dma_buf_export_sync_file)
 #define DMA_BUF_IOCTL_IMPORT_SYNC_FILE	_IOW(DMA_BUF_BASE, 3, struct dma_buf_import_sync_file)
+#define DMA_BUF_IOCTL_REVOKE	_IO(DMA_BUF_BASE, 4)
 
 #endif
-- 
2.47.3
Re: [RFC PATCH 4/7] dma-buf: uapi: Mechanism to revoke DMABUFs via ioctl()
Posted by Christian König 1 month, 1 week ago
On 2/26/26 21:22, Matt Evans wrote:
> Add a new dma-buf ioctl() op, DMA_BUF_IOCTL_REVOKE, connected to a new
> (optional) dma_buf_ops callback, revoke().  An exporter receiving this
> will _permanently_ revoke the DMABUF, meaning it can no longer be
> mapped/attached/mmap()ed.  It also guarantees that existing
> importers have been detached (e.g. via move_notify) and all mappings
> made inaccessible.
> 
> This is useful for lifecycle management in scenarios where a process
> has created a DMABUF representing a resource, then delegated it to
> a client process; access to the resource is revoked when the client is
> deemed "done", and the resource can be safely re-used elsewhere.

Well that means revoking from the importer side. That absolutely doesn't make sense to me.

Why would you do that?

Regards,
Christian.

> 
> Signed-off-by: Matt Evans <mattev@meta.com>
> ---
>  drivers/dma-buf/dma-buf.c    |  5 +++++
>  include/linux/dma-buf.h      | 22 ++++++++++++++++++++++
>  include/uapi/linux/dma-buf.h |  1 +
>  3 files changed, 28 insertions(+)
> 
> diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
> index edaa9e4ee4ae..b9b315317f2d 100644
> --- a/drivers/dma-buf/dma-buf.c
> +++ b/drivers/dma-buf/dma-buf.c
> @@ -561,6 +561,11 @@ static long dma_buf_ioctl(struct file *file,
>         case DMA_BUF_IOCTL_IMPORT_SYNC_FILE:
>                 return dma_buf_import_sync_file(dmabuf, (const void __user *)arg);
>  #endif
> +       case DMA_BUF_IOCTL_REVOKE:
> +               if (dmabuf->ops->revoke)
> +                       return dmabuf->ops->revoke(dmabuf);
> +               else
> +                       return -EINVAL;
> 
>         default:
>                 return -ENOTTY;
> diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h
> index 0bc492090237..a68c9ad7aebd 100644
> --- a/include/linux/dma-buf.h
> +++ b/include/linux/dma-buf.h
> @@ -277,6 +277,28 @@ struct dma_buf_ops {
> 
>         int (*vmap)(struct dma_buf *dmabuf, struct iosys_map *map);
>         void (*vunmap)(struct dma_buf *dmabuf, struct iosys_map *map);
> +
> +       /**
> +        * @revoke:
> +        *
> +        * This callback is invoked from a userspace
> +        * DMA_BUF_IOCTL_REVOKE operation, and requests that access to
> +        * the buffer is immediately and permanently revoked.  On
> +        * successful return, the buffer is not accessible through any
> +        * mmap() or dma-buf import.  The request fails if the buffer
> +        * is pinned; otherwise, the exporter marks the buffer as
> +        * inaccessible and uses the move_notify callback to inform
> +        * importers of the change.  The buffer is permanently
> +        * disabled, and the exporter must refuse all map, mmap,
> +        * attach, etc. requests.
> +        *
> +        * Returns:
> +        *
> +        * 0 on success, or a negative error code on failure:
> +        * -ENODEV if the associated device no longer exists/is closed.
> +        * -EBADFD if the buffer has already been revoked.
> +        */
> +       int (*revoke)(struct dma_buf *dmabuf);
>  };
> 
>  /**
> diff --git a/include/uapi/linux/dma-buf.h b/include/uapi/linux/dma-buf.h
> index 5a6fda66d9ad..84bf2dd2d0f3 100644
> --- a/include/uapi/linux/dma-buf.h
> +++ b/include/uapi/linux/dma-buf.h
> @@ -178,5 +178,6 @@ struct dma_buf_import_sync_file {
>  #define DMA_BUF_SET_NAME_B     _IOW(DMA_BUF_BASE, 1, __u64)
>  #define DMA_BUF_IOCTL_EXPORT_SYNC_FILE _IOWR(DMA_BUF_BASE, 2, struct dma_buf_export_sync_file)
>  #define DMA_BUF_IOCTL_IMPORT_SYNC_FILE _IOW(DMA_BUF_BASE, 3, struct dma_buf_import_sync_file)
> +#define DMA_BUF_IOCTL_REVOKE   _IO(DMA_BUF_BASE, 4)
> 
>  #endif
> --
> 2.47.3
>
Re: [RFC PATCH 4/7] dma-buf: uapi: Mechanism to revoke DMABUFs via ioctl()
Posted by Matt Evans 1 month, 1 week ago
Hi Christian,

On 27/02/2026 10:05, Christian König wrote:
> On 2/26/26 21:22, Matt Evans wrote:
>> Add a new dma-buf ioctl() op, DMA_BUF_IOCTL_REVOKE, connected to a new
>> (optional) dma_buf_ops callback, revoke().  An exporter receiving this
>> will _permanently_ revoke the DMABUF, meaning it can no longer be
>> mapped/attached/mmap()ed.  It also guarantees that existing
>> importers have been detached (e.g. via move_notify) and all mappings
>> made inaccessible.
>>
>> This is useful for lifecycle management in scenarios where a process
>> has created a DMABUF representing a resource, then delegated it to
>> a client process; access to the resource is revoked when the client is
>> deemed "done", and the resource can be safely re-used elsewhere.
> 
> Well that means revoking from the importer side. That absolutely doesn't make sense to me.
> 
> Why would you do that?

Well, it's for cleanup, but directed to a specific buffer.

Elaborating on the original example, a userspace driver creates a DMABUF
for parts of a BAR and then sends its fd to some other client process
via SCM_RIGHTS.  The client might then do all of:

- Process mappings of the buffer
- iommufd IO-mappings of it
- other unrelated drivers import it
- share the fd with more processes!

i.e. poking a programming interface and orchestrating P2P DMA to it.
Eventually the client completes and messages the driver to say goodbye,
except the client is buggy: it hangs before it munmaps or request other
drivers to shut down/detach their imports.

Now the original driver can't reuse any BAR ranges it shared out, as
there might still be active mappings or even ongoing P2P DMA to them.

The goal is to guarantee a point in time where resources corresponding
to a previously-shared DMABUF fd _cannot_ be accessed anymore:  CPUs,
or other drivers/importers, or any other kind of P2P DMA.  So yes, a
revoke must detach importers, using the synchronous revocation flow
Leon added in [0] ("dma-buf: Use revoke mechanism to invalidate shared
buffers").

(Apologies, I should really have just built this on top of a tree
containing that series to make this need clearer.)

But, it ultimately seems to have the same downstream effects as if one
were to, say, shut down VFIO device fds and therefore trigger
vfio_pci_dma_buf_cleanup().  It's just the reason to trigger revocation
is different:  a selective userspace-triggered revocation of a given
buffer, instead of an exporter cleanup-triggered revocation of all
buffers.  In both cases the goals are identical too, of a synchronised
point after which no more DMA/CPU access can happen.

(If I've misunderstood your question please clarify, but I hope that
answers it!)

Cheers,


Matt

[0] https://lore.kernel.org/linux-iommu/20260205-nocturnal-poetic-chamois-f566ad@houat/T/#m310cd07011e3a1461b6fda45e3f9b886ba76571a

> 
> Regards,
> Christian.
> 
>>
>> Signed-off-by: Matt Evans <mattev@meta.com>
>> ---
>>  drivers/dma-buf/dma-buf.c    |  5 +++++
>>  include/linux/dma-buf.h      | 22 ++++++++++++++++++++++
>>  include/uapi/linux/dma-buf.h |  1 +
>>  3 files changed, 28 insertions(+)
>>
>> diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
>> index edaa9e4ee4ae..b9b315317f2d 100644
>> --- a/drivers/dma-buf/dma-buf.c
>> +++ b/drivers/dma-buf/dma-buf.c
>> @@ -561,6 +561,11 @@ static long dma_buf_ioctl(struct file *file,
>>         case DMA_BUF_IOCTL_IMPORT_SYNC_FILE:
>>                 return dma_buf_import_sync_file(dmabuf, (const void __user *)arg);
>>  #endif
>> +       case DMA_BUF_IOCTL_REVOKE:
>> +               if (dmabuf->ops->revoke)
>> +                       return dmabuf->ops->revoke(dmabuf);
>> +               else
>> +                       return -EINVAL;
>>
>>         default:
>>                 return -ENOTTY;
>> diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h
>> index 0bc492090237..a68c9ad7aebd 100644
>> --- a/include/linux/dma-buf.h
>> +++ b/include/linux/dma-buf.h
>> @@ -277,6 +277,28 @@ struct dma_buf_ops {
>>
>>         int (*vmap)(struct dma_buf *dmabuf, struct iosys_map *map);
>>         void (*vunmap)(struct dma_buf *dmabuf, struct iosys_map *map);
>> +
>> +       /**
>> +        * @revoke:
>> +        *
>> +        * This callback is invoked from a userspace
>> +        * DMA_BUF_IOCTL_REVOKE operation, and requests that access to
>> +        * the buffer is immediately and permanently revoked.  On
>> +        * successful return, the buffer is not accessible through any
>> +        * mmap() or dma-buf import.  The request fails if the buffer
>> +        * is pinned; otherwise, the exporter marks the buffer as
>> +        * inaccessible and uses the move_notify callback to inform
>> +        * importers of the change.  The buffer is permanently
>> +        * disabled, and the exporter must refuse all map, mmap,
>> +        * attach, etc. requests.
>> +        *
>> +        * Returns:
>> +        *
>> +        * 0 on success, or a negative error code on failure:
>> +        * -ENODEV if the associated device no longer exists/is closed.
>> +        * -EBADFD if the buffer has already been revoked.
>> +        */
>> +       int (*revoke)(struct dma_buf *dmabuf);
>>  };
>>
>>  /**
>> diff --git a/include/uapi/linux/dma-buf.h b/include/uapi/linux/dma-buf.h
>> index 5a6fda66d9ad..84bf2dd2d0f3 100644
>> --- a/include/uapi/linux/dma-buf.h
>> +++ b/include/uapi/linux/dma-buf.h
>> @@ -178,5 +178,6 @@ struct dma_buf_import_sync_file {
>>  #define DMA_BUF_SET_NAME_B     _IOW(DMA_BUF_BASE, 1, __u64)
>>  #define DMA_BUF_IOCTL_EXPORT_SYNC_FILE _IOWR(DMA_BUF_BASE, 2, struct dma_buf_export_sync_file)
>>  #define DMA_BUF_IOCTL_IMPORT_SYNC_FILE _IOW(DMA_BUF_BASE, 3, struct dma_buf_import_sync_file)
>> +#define DMA_BUF_IOCTL_REVOKE   _IO(DMA_BUF_BASE, 4)
>>
>>  #endif
>> --
>> 2.47.3
>>
> 

Re: [RFC PATCH 4/7] dma-buf: uapi: Mechanism to revoke DMABUFs via ioctl()
Posted by Christian König 1 month, 1 week ago
Hi Matt,

On 2/27/26 14:02, Matt Evans wrote:
> Hi Christian,
> 
> On 27/02/2026 10:05, Christian König wrote:
>> On 2/26/26 21:22, Matt Evans wrote:
>>> Add a new dma-buf ioctl() op, DMA_BUF_IOCTL_REVOKE, connected to a new
>>> (optional) dma_buf_ops callback, revoke().  An exporter receiving this
>>> will _permanently_ revoke the DMABUF, meaning it can no longer be
>>> mapped/attached/mmap()ed.  It also guarantees that existing
>>> importers have been detached (e.g. via move_notify) and all mappings
>>> made inaccessible.
>>>
>>> This is useful for lifecycle management in scenarios where a process
>>> has created a DMABUF representing a resource, then delegated it to
>>> a client process; access to the resource is revoked when the client is
>>> deemed "done", and the resource can be safely re-used elsewhere.
>>
>> Well that means revoking from the importer side. That absolutely doesn't make sense to me.
>>
>> Why would you do that?
> 
> Well, it's for cleanup, but directed to a specific buffer.
> 
> Elaborating on the original example, a userspace driver creates a DMABUF
> for parts of a BAR and then sends its fd to some other client process
> via SCM_RIGHTS.  The client might then do all of:
> 
> - Process mappings of the buffer
> - iommufd IO-mappings of it
> - other unrelated drivers import it
> - share the fd with more processes!
> 
> i.e. poking a programming interface and orchestrating P2P DMA to it.
> Eventually the client completes and messages the driver to say goodbye,
> except the client is buggy: it hangs before it munmaps or request other
> drivers to shut down/detach their imports.
> 
> Now the original driver can't reuse any BAR ranges it shared out, as
> there might still be active mappings or even ongoing P2P DMA to them.
> 
> The goal is to guarantee a point in time where resources corresponding
> to a previously-shared DMABUF fd _cannot_ be accessed anymore:  CPUs,
> or other drivers/importers, or any other kind of P2P DMA.  So yes, a
> revoke must detach importers, using the synchronous revocation flow
> Leon added in [0] ("dma-buf: Use revoke mechanism to invalidate shared
> buffers").
> 
> (Apologies, I should really have just built this on top of a tree
> containing that series to make this need clearer.)
> 
> But, it ultimately seems to have the same downstream effects as if one
> were to, say, shut down VFIO device fds and therefore trigger
> vfio_pci_dma_buf_cleanup().  It's just the reason to trigger revocation
> is different:  a selective userspace-triggered revocation of a given
> buffer, instead of an exporter cleanup-triggered revocation of all
> buffers.  In both cases the goals are identical too, of a synchronised
> point after which no more DMA/CPU access can happen.
> 
> (If I've misunderstood your question please clarify, but I hope that
> answers it!)

Yeah that makes it clear, Jasons answer also helped quite a bit to understand what you want to do here.

First of all your requirements sound reasonable, but absolutely clear NAK to the way those patches approach of implementing them. You completely mixed up the different DMA-buf roles and which is used for what.

See the IOCTLs on the DMA-buf file descriptor are for the importer side to communicate with the exporter side. E.g. thinks like "I'm done writing with the CPU, please make that visible to yourself and other importers".....

But what you want to do here is just the other way around, the exporter side wants to signal to all importers that it can't use the buffer any more, correct?

If I understood that correctly then my suggestion is that you have a new IOCTL on the VFIO fd you originally used to export the DMA-buf fd. This IOCTL takes the DMA-buf fd and after double checking that it indeed is the exporter of that fd revokes all importer access to it.

I'm certainly open on suggestions on how to improve the DMA-buf documentation to make that more clearer in the future.

Regards,
Christian.

> 
> Cheers,
> 
> 
> Matt
> 
> [0] https://lore.kernel.org/linux-iommu/20260205-nocturnal-poetic-chamois-f566ad@houat/T/#m310cd07011e3a1461b6fda45e3f9b886ba76571a
> 
>>
>> Regards,
>> Christian.
>>
>>>
>>> Signed-off-by: Matt Evans <mattev@meta.com>
>>> ---
>>>  drivers/dma-buf/dma-buf.c    |  5 +++++
>>>  include/linux/dma-buf.h      | 22 ++++++++++++++++++++++
>>>  include/uapi/linux/dma-buf.h |  1 +
>>>  3 files changed, 28 insertions(+)
>>>
>>> diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
>>> index edaa9e4ee4ae..b9b315317f2d 100644
>>> --- a/drivers/dma-buf/dma-buf.c
>>> +++ b/drivers/dma-buf/dma-buf.c
>>> @@ -561,6 +561,11 @@ static long dma_buf_ioctl(struct file *file,
>>>         case DMA_BUF_IOCTL_IMPORT_SYNC_FILE:
>>>                 return dma_buf_import_sync_file(dmabuf, (const void __user *)arg);
>>>  #endif
>>> +       case DMA_BUF_IOCTL_REVOKE:
>>> +               if (dmabuf->ops->revoke)
>>> +                       return dmabuf->ops->revoke(dmabuf);
>>> +               else
>>> +                       return -EINVAL;
>>>
>>>         default:
>>>                 return -ENOTTY;
>>> diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h
>>> index 0bc492090237..a68c9ad7aebd 100644
>>> --- a/include/linux/dma-buf.h
>>> +++ b/include/linux/dma-buf.h
>>> @@ -277,6 +277,28 @@ struct dma_buf_ops {
>>>
>>>         int (*vmap)(struct dma_buf *dmabuf, struct iosys_map *map);
>>>         void (*vunmap)(struct dma_buf *dmabuf, struct iosys_map *map);
>>> +
>>> +       /**
>>> +        * @revoke:
>>> +        *
>>> +        * This callback is invoked from a userspace
>>> +        * DMA_BUF_IOCTL_REVOKE operation, and requests that access to
>>> +        * the buffer is immediately and permanently revoked.  On
>>> +        * successful return, the buffer is not accessible through any
>>> +        * mmap() or dma-buf import.  The request fails if the buffer
>>> +        * is pinned; otherwise, the exporter marks the buffer as
>>> +        * inaccessible and uses the move_notify callback to inform
>>> +        * importers of the change.  The buffer is permanently
>>> +        * disabled, and the exporter must refuse all map, mmap,
>>> +        * attach, etc. requests.
>>> +        *
>>> +        * Returns:
>>> +        *
>>> +        * 0 on success, or a negative error code on failure:
>>> +        * -ENODEV if the associated device no longer exists/is closed.
>>> +        * -EBADFD if the buffer has already been revoked.
>>> +        */
>>> +       int (*revoke)(struct dma_buf *dmabuf);
>>>  };
>>>
>>>  /**
>>> diff --git a/include/uapi/linux/dma-buf.h b/include/uapi/linux/dma-buf.h
>>> index 5a6fda66d9ad..84bf2dd2d0f3 100644
>>> --- a/include/uapi/linux/dma-buf.h
>>> +++ b/include/uapi/linux/dma-buf.h
>>> @@ -178,5 +178,6 @@ struct dma_buf_import_sync_file {
>>>  #define DMA_BUF_SET_NAME_B     _IOW(DMA_BUF_BASE, 1, __u64)
>>>  #define DMA_BUF_IOCTL_EXPORT_SYNC_FILE _IOWR(DMA_BUF_BASE, 2, struct dma_buf_export_sync_file)
>>>  #define DMA_BUF_IOCTL_IMPORT_SYNC_FILE _IOW(DMA_BUF_BASE, 3, struct dma_buf_import_sync_file)
>>> +#define DMA_BUF_IOCTL_REVOKE   _IO(DMA_BUF_BASE, 4)
>>>
>>>  #endif
>>> --
>>> 2.47.3
>>>
>>
> 

Re: [RFC PATCH 4/7] dma-buf: uapi: Mechanism to revoke DMABUFs via ioctl()
Posted by Matt Evans 1 month, 1 week ago
Hi Christian,

On 27/02/2026 15:20, Christian König wrote:
> Hi Matt,
> 
> On 2/27/26 14:02, Matt Evans wrote:
>> Hi Christian,
>>
>> On 27/02/2026 10:05, Christian König wrote:
>>> On 2/26/26 21:22, Matt Evans wrote:
>>>> Add a new dma-buf ioctl() op, DMA_BUF_IOCTL_REVOKE, connected to a new
>>>> (optional) dma_buf_ops callback, revoke().  An exporter receiving this
>>>> will _permanently_ revoke the DMABUF, meaning it can no longer be
>>>> mapped/attached/mmap()ed.  It also guarantees that existing
>>>> importers have been detached (e.g. via move_notify) and all mappings
>>>> made inaccessible.
>>>>
>>>> This is useful for lifecycle management in scenarios where a process
>>>> has created a DMABUF representing a resource, then delegated it to
>>>> a client process; access to the resource is revoked when the client is
>>>> deemed "done", and the resource can be safely re-used elsewhere.
>>>
>>> Well that means revoking from the importer side. That absolutely doesn't make sense to me.
>>>
>>> Why would you do that?
>>
>> Well, it's for cleanup, but directed to a specific buffer.
>>
>> Elaborating on the original example, a userspace driver creates a DMABUF
>> for parts of a BAR and then sends its fd to some other client process
>> via SCM_RIGHTS.  The client might then do all of:
>>
>> - Process mappings of the buffer
>> - iommufd IO-mappings of it
>> - other unrelated drivers import it
>> - share the fd with more processes!
>>
>> i.e. poking a programming interface and orchestrating P2P DMA to it.
>> Eventually the client completes and messages the driver to say goodbye,
>> except the client is buggy: it hangs before it munmaps or request other
>> drivers to shut down/detach their imports.
>>
>> Now the original driver can't reuse any BAR ranges it shared out, as
>> there might still be active mappings or even ongoing P2P DMA to them.
>>
>> The goal is to guarantee a point in time where resources corresponding
>> to a previously-shared DMABUF fd _cannot_ be accessed anymore:  CPUs,
>> or other drivers/importers, or any other kind of P2P DMA.  So yes, a
>> revoke must detach importers, using the synchronous revocation flow
>> Leon added in [0] ("dma-buf: Use revoke mechanism to invalidate shared
>> buffers").
>>
>> (Apologies, I should really have just built this on top of a tree
>> containing that series to make this need clearer.)
>>
>> But, it ultimately seems to have the same downstream effects as if one
>> were to, say, shut down VFIO device fds and therefore trigger
>> vfio_pci_dma_buf_cleanup().  It's just the reason to trigger revocation
>> is different:  a selective userspace-triggered revocation of a given
>> buffer, instead of an exporter cleanup-triggered revocation of all
>> buffers.  In both cases the goals are identical too, of a synchronised
>> point after which no more DMA/CPU access can happen.
>>
>> (If I've misunderstood your question please clarify, but I hope that
>> answers it!)
> 
> Yeah that makes it clear, Jasons answer also helped quite a bit to understand what you want to do here.
> 
> First of all your requirements sound reasonable, but absolutely clear NAK to the way those patches approach of implementing them. You completely mixed up the different DMA-buf roles and which is used for what.

Yep, no worries -- this is just an RFC in order to get such feedback.

> See the IOCTLs on the DMA-buf file descriptor are for the importer side to communicate with the exporter side. E.g. thinks like "I'm done writing with the CPU, please make that visible to yourself and other importers".....
> 
> But what you want to do here is just the other way around, the exporter side wants to signal to all importers that it can't use the buffer any more, correct?

Yes, that's right, it would be the role of the exporter (VFIO in the
first instance) being triggered by userspace to do this revoke.

I see this doesn't really fit with the other ioctls being
importer-centric; thanks, I do agree that an exporter revocation op
would stick out here.

(I tried in the cover letter to flag that better ways probably exist,
and the PoC intended to set context for how it'd be triggered in end-to-
end usage.  At any rate, I'm glad we're aligning on the overall concept/
goals (modulo implementation :) ).)

> If I understood that correctly then my suggestion is that you have a new IOCTL on the VFIO fd you originally used to export the DMA-buf fd. This IOCTL takes the DMA-buf fd and after double checking that it indeed is the exporter of that fd revokes all importer access to it.

This was Jason's suggestion in the other mail too, and it seems like a
much nicer way to do it.  Thank you for the suggestions, I'll redo it
like that.

> I'm certainly open on suggestions on how to improve the DMA-buf documentation to make that more clearer in the future.

I'll re-read it and see if any ideas for clarification occur, as I did
indeed miss that the fd implies the importer role, apologies.


Thanks,


Matt


> 
> Regards,
> Christian.
> 
>>
>> Cheers,
>>
>>
>> Matt
>>
>> [0] https://lore.kernel.org/linux-iommu/20260205-nocturnal-poetic-chamois-f566ad@houat/T/#m310cd07011e3a1461b6fda45e3f9b886ba76571a 
>>
>>>
>>> Regards,
>>> Christian.
>>>
>>>>
>>>> Signed-off-by: Matt Evans <mattev@meta.com>
>>>> ---
>>>>  drivers/dma-buf/dma-buf.c    |  5 +++++
>>>>  include/linux/dma-buf.h      | 22 ++++++++++++++++++++++
>>>>  include/uapi/linux/dma-buf.h |  1 +
>>>>  3 files changed, 28 insertions(+)
>>>>
>>>> diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
>>>> index edaa9e4ee4ae..b9b315317f2d 100644
>>>> --- a/drivers/dma-buf/dma-buf.c
>>>> +++ b/drivers/dma-buf/dma-buf.c
>>>> @@ -561,6 +561,11 @@ static long dma_buf_ioctl(struct file *file,
>>>>         case DMA_BUF_IOCTL_IMPORT_SYNC_FILE:
>>>>                 return dma_buf_import_sync_file(dmabuf, (const void __user *)arg);
>>>>  #endif
>>>> +       case DMA_BUF_IOCTL_REVOKE:
>>>> +               if (dmabuf->ops->revoke)
>>>> +                       return dmabuf->ops->revoke(dmabuf);
>>>> +               else
>>>> +                       return -EINVAL;
>>>>
>>>>         default:
>>>>                 return -ENOTTY;
>>>> diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h
>>>> index 0bc492090237..a68c9ad7aebd 100644
>>>> --- a/include/linux/dma-buf.h
>>>> +++ b/include/linux/dma-buf.h
>>>> @@ -277,6 +277,28 @@ struct dma_buf_ops {
>>>>
>>>>         int (*vmap)(struct dma_buf *dmabuf, struct iosys_map *map);
>>>>         void (*vunmap)(struct dma_buf *dmabuf, struct iosys_map *map);
>>>> +
>>>> +       /**
>>>> +        * @revoke:
>>>> +        *
>>>> +        * This callback is invoked from a userspace
>>>> +        * DMA_BUF_IOCTL_REVOKE operation, and requests that access to
>>>> +        * the buffer is immediately and permanently revoked.  On
>>>> +        * successful return, the buffer is not accessible through any
>>>> +        * mmap() or dma-buf import.  The request fails if the buffer
>>>> +        * is pinned; otherwise, the exporter marks the buffer as
>>>> +        * inaccessible and uses the move_notify callback to inform
>>>> +        * importers of the change.  The buffer is permanently
>>>> +        * disabled, and the exporter must refuse all map, mmap,
>>>> +        * attach, etc. requests.
>>>> +        *
>>>> +        * Returns:
>>>> +        *
>>>> +        * 0 on success, or a negative error code on failure:
>>>> +        * -ENODEV if the associated device no longer exists/is closed.
>>>> +        * -EBADFD if the buffer has already been revoked.
>>>> +        */
>>>> +       int (*revoke)(struct dma_buf *dmabuf);
>>>>  };
>>>>
>>>>  /**
>>>> diff --git a/include/uapi/linux/dma-buf.h b/include/uapi/linux/dma-buf.h
>>>> index 5a6fda66d9ad..84bf2dd2d0f3 100644
>>>> --- a/include/uapi/linux/dma-buf.h
>>>> +++ b/include/uapi/linux/dma-buf.h
>>>> @@ -178,5 +178,6 @@ struct dma_buf_import_sync_file {
>>>>  #define DMA_BUF_SET_NAME_B     _IOW(DMA_BUF_BASE, 1, __u64)
>>>>  #define DMA_BUF_IOCTL_EXPORT_SYNC_FILE _IOWR(DMA_BUF_BASE, 2, struct dma_buf_export_sync_file)
>>>>  #define DMA_BUF_IOCTL_IMPORT_SYNC_FILE _IOW(DMA_BUF_BASE, 3, struct dma_buf_import_sync_file)
>>>> +#define DMA_BUF_IOCTL_REVOKE   _IO(DMA_BUF_BASE, 4)
>>>>
>>>>  #endif
>>>> --
>>>> 2.47.3
>>>>
>>>
>>
> 

Re: [RFC PATCH 4/7] dma-buf: uapi: Mechanism to revoke DMABUFs via ioctl()
Posted by Jason Gunthorpe 1 month, 1 week ago
On Fri, Feb 27, 2026 at 11:05:24AM +0100, Christian König wrote:
> On 2/26/26 21:22, Matt Evans wrote:
> > Add a new dma-buf ioctl() op, DMA_BUF_IOCTL_REVOKE, connected to a new
> > (optional) dma_buf_ops callback, revoke().  An exporter receiving this
> > will _permanently_ revoke the DMABUF, meaning it can no longer be
> > mapped/attached/mmap()ed.  It also guarantees that existing
> > importers have been detached (e.g. via move_notify) and all mappings
> > made inaccessible.
> > 
> > This is useful for lifecycle management in scenarios where a process
> > has created a DMABUF representing a resource, then delegated it to
> > a client process; access to the resource is revoked when the client is
> > deemed "done", and the resource can be safely re-used elsewhere.
> 
> Well that means revoking from the importer side. That absolutely
> doesn't make sense to me.
> 
> Why would you do that?

They are building a "vending process" to wrapper VFIO. They want to
send a little bit of MMIO space wrapped in a DMABUF to some other
process over a unix FD. At some later point the vending process will
want to revoke the MMIO so it will issue this IOCTL to the DMABUF FD
it held on to. That will render the FD unusable wherever else it
happened to go.

I had a similar discussion about other iommu features where they want
to insert security protocols into this vending sequence.

IDK if this should be generic DMABUF or not. Another option is to add
a new VFIO ioctl that does the revoke and takes in a DMABUF FD. If it
is a VFIO DMABUF FD then it will revoke it as desired here using the
VFIO machinery.

Jason