On Mon, Jul 27, 2026 at 11:53:55AM -0700, Ackerley Tng wrote:
> Hope you also had a chance to look at [1] that David Woodhouse is
> working on :)
>
> [1] https://lore.kernel.org/all/1dc299af6b795a40c8887b3d84f915024f6446a9.camel@…
Heh, that's awfully DMABUF like :)
So, if that happens then sure we can probably create a VFIO exporter
for it as well along side the DMABUF exporter Matt is working on and
that should handle the shared/private steps intel needs
Jason
On Tue, Aug 04, 2026 at 10:19:11AM -0600, Logan Gunthorpe wrote:
> There's a vague convention for this already: the term 'p2pmem' is often
> used for cases where the driver uses the allocator, etc. (I think I had
> this intention when I wrote the code and have since forgotten about
> it).
I've been calling it the genalloc layer and the core layer. p2pmem
would be OK to refer to the genalloc stuff. So if you want to have
CONFIG_PCI_P2PDMA and CONFIG_PCI_P2PMEM that seem sOk
> code into it's own file, potentially renaming some functions. Then, in
> the end, we would probably have a pcim_p2pdma_supported() function and a
> pcim_p2pmem_supported() function, the latter being used by existing use
> cases.
Not quite sure why we need this?
Matt, the mlx5 stuff is the same as VFIO, it just uses the "core"
layer and does not use the genalloc. So there shouldn't be an issue
here, if the genalloc is off then the mlx5 stuff should still
work. There shouldn't be a case where CONFIG_PCI_P2PDMA=y and mlx5 is
broken?
Did some of APIs get mixed into the genalloc family that should not
have?
Jason
On Wed, 2026-08-05 at 10:50 +0200, Andreas Hindborg wrote:
> "Philipp Stanner" <phasta(a)kernel.org> writes:
>
> > One often cannot allocate in the kernel with the desired flags, most
> > notably in atomic context. Pre-allocating the memory is the preferred
> > solution in such situations.
> >
> > Add support for xa_reserve() in the Rust abstractions of xarray. Create
> > a Reservation object similar to a lock-guard, that can be dropped once
> > the reservation is no longer needed or once the index was stored to.
> >
> > Signed-off-by: Philipp Stanner <phasta(a)kernel.org>
> > ---
> > Please regard this more as an RFC.
> >
> > I need pre-allocating in XArray for DmaFence. How exactly we achieve
> > this is open for discussion.
>
> Do you need to actually reserve a key, or do you just need atomic
> allocation?
I would just have needed a position to store to, but the fence sequence
number would be the only reasonable index, so you'd also reserve a key.
Anyways, please forget about this for now, we abstained from using the
XArray in the current revision (v8) of this patch series.
Thx
P.
On Tue, 2026-08-04 at 14:54 -0300, Daniel Almeida wrote:
>
> Hi Phillip :)
>
> Tested-by: Daniel Almeida <daniel.almeida(a)collabora.com>
Hi, thx for the test
I address your major feedback below; minor points like renaming I will
address in the next revision.
>
> > On 31 Jul 2026, at 05:04, Philipp Stanner <phasta(a)kernel.org> wrote:
> >
[…]
> > An additional issue discovered during the review process of this code is
> > that there is (currently) no mechanism in Rust to prevent someone from
> > circumventing the DriverFence's FenceContext-reference's lifetime by
> > "forgetting" the fence, e.g. with core::mem::forget(). Since this
> > apparently can also happen with recfounting cycles, it's quite likely
> > that it would enable UAF bugs on the FenceContext. Print warnings if
> > this or other misuse of the fence API happens.
>
> I think cycles are fine, if anything they make things live longer than intended
> (possibly leaking them forever), but at no point they lead to UAFs. And for the
> mem::forget() point, there seems to be precedent in other patches pointing to
> unsafe constructors, where the safety requirement is basically "don't
> mem::forget() this".
>
> In fact, if you mem::forget() in the previous Arc approach, it seems like you leak
> the context, while mem::forget() on the current solution does seem to allow UAF
> by ending the borrow as you said yourself:
>
> let ctx = .... // FenceContext in some driver queue structure
> let fence = ctx.fence_alloc(...).new_fence() // DriverFence<'_, T> // borrows ctx
> mem::forget(fence); // ends the borrow
> drop(ctx); // DriverFenceData contains a dangling &FenceContext, and that allocation is managed by C
>
> versus:
>
> let ctx: Arc<FenceCtx<...>> = ...; // same as above, assume refcount==1
> let fence = ctx.new_fence_allocation(..).new_fence(); // ctx refcount==2
> mem::forget(fence); // ctx refcount==2
> drop(ctx) //ctx refcount==1
>
> I noticed that you added a counter to catch the first case, but that assumes that
> signaled fences won’t reach into the context anymore, IIUC? More on that below.
Answering on that further down below
>
> >
[…]
>
> SPDX is being used here,
>
> > +
> > +/*
> > + * Copyright (C) 2025, 2026 Red Hat Inc.:
> > + * Author: Philipp Stanner <pstanner(a)redhat.com>
> > + */
>
> So perhaps SPDX-CopyrightText should be used here?
What does that look like? I think I never saw it anywhere.
>
> > +
> >
[…]
>
> > + // happening.
> > + //
> > + // However, we cannot fully guarantee in Rust that `DriverFence`s will not
> > + // be forgotten, e.g., through refcounting. This could circumvent the
> > + // lifetime which intends to enforce that all fences disappear before their
> > + // context.
> > + nr_of_unsignaled_fences: Atomic<u64>,
>
> Why are we tracking the number of unsignaled fences, specifically? Is it not
> possible for the C side (which controls the actual allocation) to reach back
> into the Rust side via the callbacks even after the context drops, regardless
> of their signaled status?
Tracking the number was agreed on as a compromise to move the
abstraction forward. It only exists for a warning-print.
The signaled status is absolutely decisive for preventing that both the
DriverFence::data and FenceContext cannot be reached anymore by both C
*and* Rust code consuming a Fence.
The reason is that the fence backend ops can reach the DriverFence and
the FenceContext. Only signalling the fence decouples them, and then
you still have to wait for an RCU grace period to be sure that all
accessors are gone. This is what the C backend forces on us, but it
also applies for someone doing (future implementations of)
Fence::get_driver_name() or Fence::is_signaled().
>
> And in any case, even side stepping this signaled vs unsignaled dilemma that
> this counter is trying to check, the end result seems to be "sorry, you misused
> the API and now a UAF is possible". In this specific sense, it doesn't sound
> that much better than what we are trying to move away from in C.
>
> The more I think about this borrow design, the more I believe a simple refcount
> would be way safer. I mean, this field would go away to begin with, IIUC, and
> that’s not even considering the point above.
Refcounting does not solve the fundamental issue. A dma_fence must
always correctly represent the state of the associated job on the GPU.
If you forget a fence, you might deadlock. If you signal a fence for a
job that is still running on the GPU, you might get unnoticed memory
corruptions.
Refcounting the fctx was also my solution, but was objected to by
various parties (IIRC at least Boris and Danilo) who argue that the
life time is the proper solution. If I understood correctly the major
objection is that the refcount enables the device resources in
FenceContext::data to arbitrarily outlive its device, which is
something Danilo is very concerned about.
Anyways, as we discussed in the call, and as also my code comment in
FenceContext::drop() highlights, this atomic is only there for printing
a warning, because the proper solution we actually want is to have the
FenceContext signal all forgotten fences when it drops. *Then* you are
really completely safe, because then the forgotten fences get decoupled
from the fence context. So no UAF, although a driver that forgets
fences would still be broken.
The reason why (IIRC) Alice proposed doing the counter + warning and a
TODO entry is to move forward faster with the fence implementation,
which I understood is also in Tyr's interest (regarding this unlikely
bug).
The reason why it is not solved right now is that you need a data
structure in FenceContext that keeps track of unsignaled fences.
Entries in that data structure then have to be pre-allocated. And you
have to remove entries from the data structure *whenever a fence
signals*.
The only good data structure we have in Rust currently is XArray, which
we cannot use because:
* it uses `usize` as an index, but dma_fence seqno is u64, so this
could become a problem for Tyr, supporting 32-bit architectures.
* We'd need to wait for Andreas' reservation mechanism for XArray.
* XArray is not a good data structure for ever-increasing seqnos like
the u64 of dma_fence. It quickly performance-degrades because it
preserves index order, resulting in like a dozen pointer
indirections for large indices.
So I had investigated using a List, but the list would force you then
to shove another refcounting mechanism (ListArc) on top of the stored
fences, and Rust's list implementation is really not trivial to be
used. At least not for me. Getting this right would take time.
Note that with the JobQueue design we're aiming at right now,
DriverFence and FenceContext would all be owned by JobQueue, so the
driver couldn't ever forget a DriverFence whithout also forgetting the
FenceContext. Hence, for the forseeable future, no one will encounter
that bug.
So, long story short, this is a solvable TODO, but we need a good data
structure for it. Preferably a hash table? Or the list implementation
needs to be improved so that it can take an ARef<Fence> instead of a
ListArc<Fence>. But someone needs to do that work.
Please tell me whether you agree with proceeding like this and make
FenceContext::drop() completely waterproof later, or whether you want
to suggest to do that now – but then I'd need help with the data
structures.
>
>
> >
[…]
> > + /// The `data` you pass here must not perform any operations that are illegal
> > + /// in atomic context in its [`Drop`] implementation.
> > + pub fn fence_alloc(&self, data: T::FenceDataType) -> Result<DriverFenceAllocation<'_, T>> {
>
> ^ In the same spirit as the last iteration, can we please rename this?
>
> We don’t need to shorten methods and types IMHO. We can just say
> “new_allocation” or a some other variation without “alloc”.
Well, the name IMO needs to reflect that this method creates some fency
object. So just "new_allocation" is not good I think because you want
to see *what* is being allocated.
new_fence_allocation() maybe?
>
> >
[…]
> > +/// The receiving counterpart of a [`DriverFence`].
> > +///
> > +/// The Rust DMA fence implementation has a dualistic design: [`DriverFence`]s
> > +/// are the producer-side, intended to be always owned by only one party. That
> > +/// party has the monopoly on signalling the fence.
> > +///
> > +/// A [`Fence`] is the counterpart for consumers. Thus, [`Fence`]s are always
> > +/// refcounted and can shared with an arbitrary number of parties, including
> > +/// userspace. Hereby, a [`Fence`] can only be used for actions such as checking
> > +/// the fence's status or for registering callbacks on it.
> > +///
> > +/// Once the associated [`DriverFence`] signals, all
> > +/// [`FenceCallbackRegistration`]s registered on the [`Fence`] will be executed.
> > +///
> > +/// A [`Fence`] can arbitrarily outlive its [`DriverFence`] and the
> > +/// [`FenceContext`]. Signalling a [`DriverFence`] decouples it from its
> > +/// [`Fence`]s.
> > +#[repr(transparent)]
> > +pub struct Fence {
> > + /// The actual dma_fence passed to C.
> > + inner: Opaque<bindings::dma_fence>,
> > +}
> > +
> > +/// Guard helper for locking within this module.
>
> Is this new? Needs a bit more documentation.
It is new, yes. Changelog mentions it.
>
> OTOH I think we shouldn’t come up with a new guard type. IIRC from
> Lyude’s et al previous work, there’s already a way to build a Rust lock
> from a C lock (Lock::from_raw()). In fact, I think this whole implementation
> can boil down to:
>
> pub fn lock(&self) -> SpinLockIrqGuard<‘_, ()> {
> let ptr = unsafe {bindings::dma_fence_spinlock(…)};
> unsafe { SpinLockIrq::<()>::from_raw(ptr)}.lock()
> }
>
> This has a few advantages:
>
> a) doesn’t introduce its own guard type, instead reusing something that was
> previously tested,
>
> b) you get the right behavior with the included NotThreadSafe token.
>
> c) the guard is now borrowed. Your guard is owned, which can easily lead to UB
> because the lifetime is detached from the &self that originated it.
This little helper is only used internally, and where it is used other
code bits have to operate on the rawpointer with other unsafe blocks.
At other places we use the fence rawpointer without taking the lock. So
I'm not sure whether this will ever be clean.
The only reason I added it was that I by now had 3 places where I do
unsafe { } lock and unlock.
Lyude's code is not yet merged.
[…]
>
> > +///
> > +/// let mut fctx = KBox::pin_init(FenceContext::new(0, driver_name, timeline_name, fctx_data), GFP_KERNEL)?;
>
> Missing rustfmt?
I do run rustfmt. It doesn't do anything about this line.
P.
Hi,
On Mon, Oct 23, 2023 at 10:25:50AM -0700, Doug Anderson wrote:
> On Mon, Oct 23, 2023 at 9:31 AM Yuran Pereira <yuran.pereira(a)hotmail.com> wrote:
> >
> > Since "Clean up checks for already prepared/enabled in panels" has
> > already been done and merged [1], I think there is no longer a need
> > for this item to be in the gpu TODO.
> >
> > [1] https://patchwork.freedesktop.org/patch/551421/
> >
> > Signed-off-by: Yuran Pereira <yuran.pereira(a)hotmail.com>
> > ---
> > Documentation/gpu/todo.rst | 25 -------------------------
> > 1 file changed, 25 deletions(-)
>
> It's not actually all done. It's in a bit of a limbo state right now,
> unfortunately. I landed all of the "simple" cases where panels were
> needlessly tracking prepare/enable, but the less simple cases are
> still outstanding.
>
> Specifically the issue is that many panels have code to properly power
> cycle themselves off at shutdown time and in order to do that they
> need to keep track of the prepare/enable state. After a big, long
> discussion [1] it was decided that we could get rid of all the panel
> code handling shutdown if only all relevant DRM KMS drivers would
> properly call drm_atomic_helper_shutdown().
>
> I made an attempt to get DRM KMS drivers to call
> drm_atomic_helper_shutdown() [2] [3] [4]. I was able to land the
> patches that went through drm-misc, but currently many of the
> non-drm-misc ones are blocked waiting for attention.
>
> ...so things that could be done to help out:
>
> a) Could review patches that haven't landed in [4]. Maybe adding a
> Reviewed-by tag would help wake up maintainers?
>
> b) Could see if you can identify panels that are exclusively used w/
> DRM drivers that have already been converted and then we could post
> patches for just those panels. I have no idea how easy this task would
> be. Is it enough to look at upstream dts files by "compatible" string?
I think it is, yes.
Maxime
Hi Praan,
On 30/07/2026 23:55, Pranjal Shrivastava wrote:
> On Wed, Jul 15, 2026 at 06:47:26PM +0100, Matt Evans wrote:
>> Add vfio_pci_dma_buf_find_pfn(), which a VMA fault handler can use to
>> find a PFN.
>>
>> This supports multi-range DMABUFs, which typically would be used to
>> represent scattered spans but might even represent overlapping or
>> aliasing spans of PFNs.
>>
>> Because this is intended to be used in vfio_pci_core.c, we also need
>> to expose the struct vfio_pci_dma_buf in the vfio_pci_priv.h header.
>>
>> Signed-off-by: Matt Evans <matt(a)ozlabs.org>
>> ---
>> drivers/vfio/pci/vfio_pci_dmabuf.c | 153 ++++++++++++++++++++++++++---
>> drivers/vfio/pci/vfio_pci_priv.h | 20 ++++
>> 2 files changed, 160 insertions(+), 13 deletions(-)
>>
>> diff --git a/drivers/vfio/pci/vfio_pci_dmabuf.c b/drivers/vfio/pci/vfio_pci_dmabuf.c
>> index c16f460c01d6..7c047400dfd1 100644
>> --- a/drivers/vfio/pci/vfio_pci_dmabuf.c
>> +++ b/drivers/vfio/pci/vfio_pci_dmabuf.c
>> @@ -9,19 +9,6 @@
>>
>> MODULE_IMPORT_NS("DMA_BUF");
>>
>> -struct vfio_pci_dma_buf {
>> - struct dma_buf *dmabuf;
>> - struct vfio_pci_core_device *vdev;
>> - struct list_head dmabufs_elm;
>> - size_t size;
>> - struct phys_vec *phys_vec;
>> - struct p2pdma_provider *provider;
>> - u32 nr_ranges;
>> - struct kref kref;
>> - struct completion comp;
>> - u8 revoked : 1;
>> -};
>> -
>> static int vfio_pci_dma_buf_attach(struct dma_buf *dmabuf,
>> struct dma_buf_attachment *attachment)
>> {
>> @@ -106,6 +93,146 @@ static const struct dma_buf_ops vfio_pci_dmabuf_ops = {
>> .release = vfio_pci_dma_buf_release,
>> };
>>
>> +int vfio_pci_dma_buf_find_pfn(struct vfio_pci_dma_buf *priv,
>> + struct vm_area_struct *vma,
>> + unsigned long fault_addr,
>> + unsigned int order,
>> + unsigned long *out_pfn)
>> +{
>> + /*
>> + * Given a VMA (start, end, pgoffs) and a fault address,
>> + * search the corresponding DMABUF's phys_vec[] to find the
>> + * range representing the address's offset into the VMA, and
>> + * its PFN.
>> + *
>> + * The phys_vec[] ranges represent contiguous spans of VAs
>> + * upwards from the buffer offset 0; the actual PFNs might be
>> + * in any order, overlap/alias, etc. Calculate an offset of
>> + * the desired page given VMA start/pgoff and address, then
>> + * search upwards from 0 to find which span contains it.
>> + *
>> + * On success, a valid PFN for a page sized by 'order' is
>> + * returned into out_pfn.
>> + *
>> + * Failure occurs if:
>> + * - A hugepage would cross the edge of the VMA,
>> + * - A hugepage isn't entirely contained within a range
>> + * (including where it straddles the boundary between
>> + * ranges),
>> + * - We find a range, but the final PFN isn't aligned to the
>> + * requested order.
>> + *
>> + * Upon failure, -EAGAIN is returned and the caller is
>> + * expected to try again with a smaller order, which will
>> + * eventually succeed (order=0 will always work).
>> + *
>> + * It's suboptimal if DMABUFs are created with neighbouring
>> + * ranges that are physically contiguous, since hugepages
>> + * can't straddle range boundaries. (The construction of the
>> + * ranges should merge them in this case.)
>> + *
>> + * Finally, vma_pgoff_adjust is used with a DMABUF created for
>> + * a VFIO BAR mmap: a BAR mapped with vm_pgoff > 0 creates a
>> + * DMABUF such that byte 0 of the VMA corresponds to byte 0 of
>> + * the DMABUF and byte 'vm_pgoff << PAGE_SHIFT' into the BAR.
>> + * To avoid double-offsetting in this scenario, subtracting
>> + * vma_pgoff_adjust from this (non-zero) vm_pgoff generates
>> + * the effective offset.
>> + */
>> +
>> + const unsigned long pagesize = PAGE_SIZE << order;
>> + unsigned long vma_off = ((vma->vm_pgoff - priv->vma_pgoff_adjust) <<
>> + PAGE_SHIFT) & VFIO_PCI_OFFSET_MASK;
>
> Maybe I'm getting ahead of myself here.. but it seems like this
> restricts us to only mapping DMABUFs at offsets < 1TB due to the
> VFIO_PCI_OFFSET_MASK (since we have HBMs on PCI devices now, hitting 1TB
> may not be a very distant future).
This is a really good question, thanks for raisiing it. It is not too
forward-thinking at all.
> While I understand this mask is needed to drop the BAR encoding in the
> high bits.
>
> My worry is, if in the future a user were to export a massive
> contiguous DMABUF (e.g., >1TB of aggregated HBM) and tried to mmap deep
> into it (passing an offset >= 1TB), this bitwise AND would silently drop
> the high bits, leading to silent data corruption.
One of the big advantages of DMABUF export was that the range could be
huge and unencumbered by the VFIO_PCI_OFFSET_SHIFT of the traditional
mmap() interface. It's a way to mmap huge BARs without having to change
the user-visible shift). So definitely this is a relevant concern.
> I think we should explicitly reject such an mmap with -EINVAL like:
>
> +const unsigned long pagesize = PAGE_SIZE << order;
> +unsigned long vma_off = (vma->vm_pgoff - priv->vma_pgoff_adjust) << PAGE_SHIFT;
>
> +/*
> + * Prevent silent wrap-around if the user mmaps a DMABUF at an
> + * offset greater than the VFIO index mask allows.
> + */
> +if (unlikely(vma_off > VFIO_PCI_OFFSET_MASK))
> + return -EINVAL;
>
> +vma_off &= VFIO_PCI_OFFSET_MASK;
Agreed, for now this absolutely should not silently wrap if the offset
is > 1TB, and we live with the restriction that a DMABUF sized >1TB
can't be mapped with such an offset. (We can still, say, map all of a
16TB DMABUF with offset=0, which is good.). I'll add a check, thanks
for pointing this out.
I suggest as something to revisit later, we flag for a DMABUF (maybe an
evolution of vma_pgoff_adjust) to differentiate whether this masking
needs to be applied (traditional mmap() path) or not (DMABUF mmap()),
and then offsets can be arbitrarily large.
Cheers,
Matt
>> + unsigned long rounded_page_addr = ALIGN_DOWN(fault_addr, pagesize);
>> + unsigned long rounded_page_end = rounded_page_addr + pagesize;
>> + unsigned long fault_offset;
>> + unsigned long fault_offset_end;
>> + unsigned long range_start_offset = 0;
>> + unsigned int i;
>> + int ret;
>> +
>
>
> Thanks,
> Praan