Am 03.04.25 um 15:15 schrieb Danilo Krummrich:
> On Thu, Apr 03, 2025 at 02:58:13PM +0200, Philipp Stanner wrote:
>> On Thu, 2025-04-03 at 14:08 +0200, Christian König wrote:
>>> Am 03.04.25 um 12:13 schrieb Philipp Stanner:
>>> BTW: nouveau_fence_no_signaling() looks completely broken as well. It
>>> calls nouveau_fence_is_signaled() and then list_del() on the fence
>>> head.
>> I can assure you that a great many things in Nouveau look completely
>> broken.
>>
>> The question for us is always the cost-benefit-ratio when fixing bugs.
>> There are fixes that solve the bug with reasonable effort, and there
>> are great reworks towards an ideal state.
> That's just an additional thing that Christian noticed. It isn't really directly
> related to what you want to fix with your patch.
Well there is some relation. From nouveau_fence_no_signaling():
if (nouveau_fence_is_signaled(f)) {
list_del(&fence->head);
dma_fence_put(&fence->base);
return false;
}
That looks like somebody realized that the fence needs to be removed from the pending list and the reference dropped.
It's just that this was added to the wrong function, e.g. those lines need to be in nouveau_fence_is_signaled() and not here.
Regards,
Christian.
>
> I think the function can simply be dropped.
Am 03.04.25 um 14:58 schrieb Philipp Stanner:
> On Thu, 2025-04-03 at 14:08 +0200, Christian König wrote:
>> Am 03.04.25 um 12:13 schrieb Philipp Stanner:
>>> Nouveau currently relies on the assumption that dma_fences will
>>> only
>>> ever get signalled through nouveau_fence_signal(), which takes care
>>> of
>>> removing a signalled fence from the list
>>> nouveau_fence_chan.pending.
>>>
>>> This self-imposed rule is violated in nouveau_fence_done(), where
>>> dma_fence_is_signaled() can signal the fence without removing it
>>> from
>>> the list. This enables accesses to already signalled fences through
>>> the
>>> list, which is a bug.
>>>
>>> Furthermore, it must always be possible to use standard dma_fence
>>> methods an a dma_fence and observe valid behavior. The canonical
>>> way of
>>> ensuring that signalling a fence has additional effects is to add
>>> those
>>> effects to a callback and register it on that fence.
>>>
>>> Move the code from nouveau_fence_signal() into a dma_fence
>>> callback.
>>> Register that callback when creating the fence.
>>>
>>> Cc: <stable(a)vger.kernel.org> # 4.10+
>>> Signed-off-by: Philipp Stanner <phasta(a)kernel.org>
>>> ---
>>> Changes in v2:
>>> - Remove Fixes: tag. (Danilo)
>>> - Remove integer "drop" and call nvif_event_block() in the fence
>>> callback. (Danilo)
>>> ---
>>> drivers/gpu/drm/nouveau/nouveau_fence.c | 52 +++++++++++++--------
>>> ----
>>> drivers/gpu/drm/nouveau/nouveau_fence.h | 1 +
>>> 2 files changed, 29 insertions(+), 24 deletions(-)
>>>
>>> diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c
>>> b/drivers/gpu/drm/nouveau/nouveau_fence.c
>>> index 7cc84472cece..cf510ef9641a 100644
>>> --- a/drivers/gpu/drm/nouveau/nouveau_fence.c
>>> +++ b/drivers/gpu/drm/nouveau/nouveau_fence.c
>>> @@ -50,24 +50,24 @@ nouveau_fctx(struct nouveau_fence *fence)
>>> return container_of(fence->base.lock, struct
>>> nouveau_fence_chan, lock);
>>> }
>>>
>>> -static int
>>> -nouveau_fence_signal(struct nouveau_fence *fence)
>>> +static void
>>> +nouveau_fence_cleanup_cb(struct dma_fence *dfence, struct
>>> dma_fence_cb *cb)
>>> {
>>> - int drop = 0;
>>> + struct nouveau_fence_chan *fctx;
>>> + struct nouveau_fence *fence;
>>> +
>>> + fence = container_of(dfence, struct nouveau_fence, base);
>>> + fctx = nouveau_fctx(fence);
>>>
>>> - dma_fence_signal_locked(&fence->base);
>>> list_del(&fence->head);
>>> rcu_assign_pointer(fence->channel, NULL);
>>>
>>> if (test_bit(DMA_FENCE_FLAG_USER_BITS, &fence-
>>>> base.flags)) {
>>> - struct nouveau_fence_chan *fctx =
>>> nouveau_fctx(fence);
>>> -
>>> if (!--fctx->notify_ref)
>>> - drop = 1;
>>> + nvif_event_block(&fctx->event);
>>> }
>>>
>>> dma_fence_put(&fence->base);
>>> - return drop;
>>> }
>>>
>>> static struct nouveau_fence *
>>> @@ -93,8 +93,7 @@ nouveau_fence_context_kill(struct
>>> nouveau_fence_chan *fctx, int error)
>>> if (error)
>>> dma_fence_set_error(&fence->base, error);
>>>
>>> - if (nouveau_fence_signal(fence))
>>> - nvif_event_block(&fctx->event);
>>> + dma_fence_signal_locked(&fence->base);
>>> }
>>> fctx->killed = 1;
>>> spin_unlock_irqrestore(&fctx->lock, flags);
>>> @@ -127,11 +126,10 @@ nouveau_fence_context_free(struct
>>> nouveau_fence_chan *fctx)
>>> kref_put(&fctx->fence_ref, nouveau_fence_context_put);
>>> }
>>>
>>> -static int
>>> +static void
>>> nouveau_fence_update(struct nouveau_channel *chan, struct
>>> nouveau_fence_chan *fctx)
>>> {
>>> struct nouveau_fence *fence;
>>> - int drop = 0;
>>> u32 seq = fctx->read(chan);
>>>
>>> while (!list_empty(&fctx->pending)) {
>>> @@ -140,10 +138,8 @@ nouveau_fence_update(struct nouveau_channel
>>> *chan, struct nouveau_fence_chan *fc
>>> if ((int)(seq - fence->base.seqno) < 0)
>>> break;
>>>
>>> - drop |= nouveau_fence_signal(fence);
>>> + dma_fence_signal_locked(&fence->base);
>>> }
>>> -
>>> - return drop;
>>> }
>>>
>>> static void
>>> @@ -152,7 +148,6 @@ nouveau_fence_uevent_work(struct work_struct
>>> *work)
>>> struct nouveau_fence_chan *fctx = container_of(work,
>>> struct nouveau_fence_chan,
>>>
>>> uevent_work);
>>> unsigned long flags;
>>> - int drop = 0;
>>>
>>> spin_lock_irqsave(&fctx->lock, flags);
>>> if (!list_empty(&fctx->pending)) {
>>> @@ -161,11 +156,8 @@ nouveau_fence_uevent_work(struct work_struct
>>> *work)
>>>
>>> fence = list_entry(fctx->pending.next,
>>> typeof(*fence), head);
>>> chan = rcu_dereference_protected(fence->channel,
>>> lockdep_is_held(&fctx->lock));
>>> - if (nouveau_fence_update(chan, fctx))
>>> - drop = 1;
>>> + nouveau_fence_update(chan, fctx);
>>> }
>>> - if (drop)
>>> - nvif_event_block(&fctx->event);
>>>
>>> spin_unlock_irqrestore(&fctx->lock, flags);
>>> }
>>> @@ -235,6 +227,19 @@ nouveau_fence_emit(struct nouveau_fence
>>> *fence)
>>> &fctx->lock, fctx->context, ++fctx-
>>>> sequence);
>>> kref_get(&fctx->fence_ref);
>>>
>>> + fence->cb.func = nouveau_fence_cleanup_cb;
>>> + /* Adding a callback runs into
>>> __dma_fence_enable_signaling(), which will
>>> + * ultimately run into nouveau_fence_no_signaling(), where
>>> a WARN_ON
>>> + * would fire because the refcount can be dropped there.
>>> + *
>>> + * Increment the refcount here temporarily to work around
>>> that.
>>> + */
>>> + dma_fence_get(&fence->base);
>>> + ret = dma_fence_add_callback(&fence->base, &fence->cb,
>>> nouveau_fence_cleanup_cb);
>> That looks like a really really awkward approach. The driver
>> basically uses a the DMA fence infrastructure as middle layer and
>> callbacks into itself to cleanup it's own structures.
> What else are callbacks good for, if not to do something automatically
> when the fence gets signaled?
Well if you add a callback for a signal you issued yourself then that's kind of awkward.
E.g. you call into the DMA fence code, just for the DMA fence code to call yourself back again.
>> Additional to that we don't guarantee any callback order for the DMA
>> fence and so it can be that mix cleaning up the callback with other
>> work which is certainly not good when you want to guarantee that the
>> cleanup happens under the same lock.
> Isn't my perception correct that the primary issue you have with this
> approach is that dma_fence_put() is called from within the callback? Or
> do you also take issue with deleting from the list?
Well kind of both. The issue is that the caller of dma_fence_signal() or dma_fence_signal_locked() must hold the reference until the function returns.
When you do the list cleanup and the drop inside the callback it is perfectly possible that the fence pointer becomes stale before you return and that's really not a good idea.
>
>> Instead the call to dma_fence_signal_locked() should probably be
>> removed from nouveau_fence_signal() and into
>> nouveau_fence_context_kill() and nouveau_fence_update().
>>
>> This way nouveau_fence_is_signaled() can call this function as well.
> Which "this function"? dma_fence_signal_locked()
No the cleanup function for the list entry. Whatever you call that then, the name nouveau_fence_signal() is probably not appropriate any more.
>
>> BTW: nouveau_fence_no_signaling() looks completely broken as well. It
>> calls nouveau_fence_is_signaled() and then list_del() on the fence
>> head.
> I can assure you that a great many things in Nouveau look completely
> broken.
>
> The question for us is always the cost-benefit-ratio when fixing bugs.
> There are fixes that solve the bug with reasonable effort, and there
> are great reworks towards an ideal state.
I would just simply drop that function. As far as I can see it severs no purpose other than doing exactly what the common DMA fence code does anyway.
Just one less thing which could fail.
Christian.
>
> P.
>
>
>> As far as I can see that is completely superfluous and should
>> probably be dropped. IIRC I once had a patch to clean that up but it
>> was dropped for some reason.
>>
>> Regards,
>> Christian.
>>
>>
>>> + dma_fence_put(&fence->base);
>>> + if (ret)
>>> + return ret;
>>> +
>>> ret = fctx->emit(fence);
>>> if (!ret) {
>>> dma_fence_get(&fence->base);
>>> @@ -246,8 +251,7 @@ nouveau_fence_emit(struct nouveau_fence *fence)
>>> return -ENODEV;
>>> }
>>>
>>> - if (nouveau_fence_update(chan, fctx))
>>> - nvif_event_block(&fctx->event);
>>> + nouveau_fence_update(chan, fctx);
>>>
>>> list_add_tail(&fence->head, &fctx->pending);
>>> spin_unlock_irq(&fctx->lock);
>>> @@ -270,8 +274,8 @@ nouveau_fence_done(struct nouveau_fence *fence)
>>>
>>> spin_lock_irqsave(&fctx->lock, flags);
>>> chan = rcu_dereference_protected(fence->channel,
>>> lockdep_is_held(&fctx->lock));
>>> - if (chan && nouveau_fence_update(chan, fctx))
>>> - nvif_event_block(&fctx->event);
>>> + if (chan)
>>> + nouveau_fence_update(chan, fctx);
>>> spin_unlock_irqrestore(&fctx->lock, flags);
>>> }
>>> return dma_fence_is_signaled(&fence->base);
>>> diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.h
>>> b/drivers/gpu/drm/nouveau/nouveau_fence.h
>>> index 8bc065acfe35..e6b2df7fdc42 100644
>>> --- a/drivers/gpu/drm/nouveau/nouveau_fence.h
>>> +++ b/drivers/gpu/drm/nouveau/nouveau_fence.h
>>> @@ -10,6 +10,7 @@ struct nouveau_bo;
>>>
>>> struct nouveau_fence {
>>> struct dma_fence base;
>>> + struct dma_fence_cb cb;
>>>
>>> struct list_head head;
>>>
Am 03.04.25 um 12:25 schrieb Danilo Krummrich:
> On Thu, Apr 03, 2025 at 12:17:29PM +0200, Philipp Stanner wrote:
>> On Thu, 2025-04-03 at 12:13 +0200, Philipp Stanner wrote:
>>> -static int
>>> -nouveau_fence_signal(struct nouveau_fence *fence)
>>> +static void
>>> +nouveau_fence_cleanup_cb(struct dma_fence *dfence, struct
>>> dma_fence_cb *cb)
>>> {
>>> - int drop = 0;
>>> + struct nouveau_fence_chan *fctx;
>>> + struct nouveau_fence *fence;
>>> +
>>> + fence = container_of(dfence, struct nouveau_fence, base);
>>> + fctx = nouveau_fctx(fence);
>>>
>>> - dma_fence_signal_locked(&fence->base);
>>> list_del(&fence->head);
>>> rcu_assign_pointer(fence->channel, NULL);
>>>
>>> if (test_bit(DMA_FENCE_FLAG_USER_BITS, &fence->base.flags))
>>> {
>>> - struct nouveau_fence_chan *fctx =
>>> nouveau_fctx(fence);
>>> -
>>> if (!--fctx->notify_ref)
>>> - drop = 1;
>>> + nvif_event_block(&fctx->event);
>>> }
>>>
>>> dma_fence_put(&fence->base);
>> What I realized while coding this v2 is that we might want to think
>> about whether we really want the dma_fence_put() in the fence callback?
>>
>> It should work fine, since it's exactly identical to the previous
>> code's behavior – but effectively it means that the driver's reference
>> will be dropped whenever it signals that fence.
> Not quite, it's the reference of the fence context's pending list.
>
> When the fence is emitted, dma_fence_init() is called, which initializes the
> reference count to 1. Subsequently, another reference is taken, when the fence
> is added to the pending list. Once the fence is signaled and hence removed from
> the pending list, we can (and have to) drop this reference.
The general idea is that the caller must hold the reference until the signaling is completed.
So for signaling from the interrupt handler it means that you need to call dma_fence_put() for the list reference *after* you called dma_fence_signal_locked().
For signaling from the .enable_signaling or .signaled callback you need to remove the fence from the linked list and call dma_fence_put() *before* you return (because the caller is holding the potential last reference).
That's why I'm pretty sure that the approach with installing the callback won't work. As far as I know no other DMA fence implementation is doing that.
Regards,
Christian.
Am 03.04.25 um 12:13 schrieb Philipp Stanner:
> Nouveau currently relies on the assumption that dma_fences will only
> ever get signalled through nouveau_fence_signal(), which takes care of
> removing a signalled fence from the list nouveau_fence_chan.pending.
>
> This self-imposed rule is violated in nouveau_fence_done(), where
> dma_fence_is_signaled() can signal the fence without removing it from
> the list. This enables accesses to already signalled fences through the
> list, which is a bug.
>
> Furthermore, it must always be possible to use standard dma_fence
> methods an a dma_fence and observe valid behavior. The canonical way of
> ensuring that signalling a fence has additional effects is to add those
> effects to a callback and register it on that fence.
>
> Move the code from nouveau_fence_signal() into a dma_fence callback.
> Register that callback when creating the fence.
>
> Cc: <stable(a)vger.kernel.org> # 4.10+
> Signed-off-by: Philipp Stanner <phasta(a)kernel.org>
> ---
> Changes in v2:
> - Remove Fixes: tag. (Danilo)
> - Remove integer "drop" and call nvif_event_block() in the fence
> callback. (Danilo)
> ---
> drivers/gpu/drm/nouveau/nouveau_fence.c | 52 +++++++++++++------------
> drivers/gpu/drm/nouveau/nouveau_fence.h | 1 +
> 2 files changed, 29 insertions(+), 24 deletions(-)
>
> diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c
> index 7cc84472cece..cf510ef9641a 100644
> --- a/drivers/gpu/drm/nouveau/nouveau_fence.c
> +++ b/drivers/gpu/drm/nouveau/nouveau_fence.c
> @@ -50,24 +50,24 @@ nouveau_fctx(struct nouveau_fence *fence)
> return container_of(fence->base.lock, struct nouveau_fence_chan, lock);
> }
>
> -static int
> -nouveau_fence_signal(struct nouveau_fence *fence)
> +static void
> +nouveau_fence_cleanup_cb(struct dma_fence *dfence, struct dma_fence_cb *cb)
> {
> - int drop = 0;
> + struct nouveau_fence_chan *fctx;
> + struct nouveau_fence *fence;
> +
> + fence = container_of(dfence, struct nouveau_fence, base);
> + fctx = nouveau_fctx(fence);
>
> - dma_fence_signal_locked(&fence->base);
> list_del(&fence->head);
> rcu_assign_pointer(fence->channel, NULL);
>
> if (test_bit(DMA_FENCE_FLAG_USER_BITS, &fence->base.flags)) {
> - struct nouveau_fence_chan *fctx = nouveau_fctx(fence);
> -
> if (!--fctx->notify_ref)
> - drop = 1;
> + nvif_event_block(&fctx->event);
> }
>
> dma_fence_put(&fence->base);
> - return drop;
> }
>
> static struct nouveau_fence *
> @@ -93,8 +93,7 @@ nouveau_fence_context_kill(struct nouveau_fence_chan *fctx, int error)
> if (error)
> dma_fence_set_error(&fence->base, error);
>
> - if (nouveau_fence_signal(fence))
> - nvif_event_block(&fctx->event);
> + dma_fence_signal_locked(&fence->base);
> }
> fctx->killed = 1;
> spin_unlock_irqrestore(&fctx->lock, flags);
> @@ -127,11 +126,10 @@ nouveau_fence_context_free(struct nouveau_fence_chan *fctx)
> kref_put(&fctx->fence_ref, nouveau_fence_context_put);
> }
>
> -static int
> +static void
> nouveau_fence_update(struct nouveau_channel *chan, struct nouveau_fence_chan *fctx)
> {
> struct nouveau_fence *fence;
> - int drop = 0;
> u32 seq = fctx->read(chan);
>
> while (!list_empty(&fctx->pending)) {
> @@ -140,10 +138,8 @@ nouveau_fence_update(struct nouveau_channel *chan, struct nouveau_fence_chan *fc
> if ((int)(seq - fence->base.seqno) < 0)
> break;
>
> - drop |= nouveau_fence_signal(fence);
> + dma_fence_signal_locked(&fence->base);
> }
> -
> - return drop;
> }
>
> static void
> @@ -152,7 +148,6 @@ nouveau_fence_uevent_work(struct work_struct *work)
> struct nouveau_fence_chan *fctx = container_of(work, struct nouveau_fence_chan,
> uevent_work);
> unsigned long flags;
> - int drop = 0;
>
> spin_lock_irqsave(&fctx->lock, flags);
> if (!list_empty(&fctx->pending)) {
> @@ -161,11 +156,8 @@ nouveau_fence_uevent_work(struct work_struct *work)
>
> fence = list_entry(fctx->pending.next, typeof(*fence), head);
> chan = rcu_dereference_protected(fence->channel, lockdep_is_held(&fctx->lock));
> - if (nouveau_fence_update(chan, fctx))
> - drop = 1;
> + nouveau_fence_update(chan, fctx);
> }
> - if (drop)
> - nvif_event_block(&fctx->event);
>
> spin_unlock_irqrestore(&fctx->lock, flags);
> }
> @@ -235,6 +227,19 @@ nouveau_fence_emit(struct nouveau_fence *fence)
> &fctx->lock, fctx->context, ++fctx->sequence);
> kref_get(&fctx->fence_ref);
>
> + fence->cb.func = nouveau_fence_cleanup_cb;
> + /* Adding a callback runs into __dma_fence_enable_signaling(), which will
> + * ultimately run into nouveau_fence_no_signaling(), where a WARN_ON
> + * would fire because the refcount can be dropped there.
> + *
> + * Increment the refcount here temporarily to work around that.
> + */
> + dma_fence_get(&fence->base);
> + ret = dma_fence_add_callback(&fence->base, &fence->cb, nouveau_fence_cleanup_cb);
That looks like a really really awkward approach. The driver basically uses a the DMA fence infrastructure as middle layer and callbacks into itself to cleanup it's own structures.
Additional to that we don't guarantee any callback order for the DMA fence and so it can be that mix cleaning up the callback with other work which is certainly not good when you want to guarantee that the cleanup happens under the same lock.
Instead the call to dma_fence_signal_locked() should probably be removed from nouveau_fence_signal() and into nouveau_fence_context_kill() and nouveau_fence_update().
This way nouveau_fence_is_signaled() can call this function as well.
BTW: nouveau_fence_no_signaling() looks completely broken as well. It calls nouveau_fence_is_signaled() and then list_del() on the fence head.
As far as I can see that is completely superfluous and should probably be dropped. IIRC I once had a patch to clean that up but it was dropped for some reason.
Regards,
Christian.
> + dma_fence_put(&fence->base);
> + if (ret)
> + return ret;
> +
> ret = fctx->emit(fence);
> if (!ret) {
> dma_fence_get(&fence->base);
> @@ -246,8 +251,7 @@ nouveau_fence_emit(struct nouveau_fence *fence)
> return -ENODEV;
> }
>
> - if (nouveau_fence_update(chan, fctx))
> - nvif_event_block(&fctx->event);
> + nouveau_fence_update(chan, fctx);
>
> list_add_tail(&fence->head, &fctx->pending);
> spin_unlock_irq(&fctx->lock);
> @@ -270,8 +274,8 @@ nouveau_fence_done(struct nouveau_fence *fence)
>
> spin_lock_irqsave(&fctx->lock, flags);
> chan = rcu_dereference_protected(fence->channel, lockdep_is_held(&fctx->lock));
> - if (chan && nouveau_fence_update(chan, fctx))
> - nvif_event_block(&fctx->event);
> + if (chan)
> + nouveau_fence_update(chan, fctx);
> spin_unlock_irqrestore(&fctx->lock, flags);
> }
> return dma_fence_is_signaled(&fence->base);
> diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.h b/drivers/gpu/drm/nouveau/nouveau_fence.h
> index 8bc065acfe35..e6b2df7fdc42 100644
> --- a/drivers/gpu/drm/nouveau/nouveau_fence.h
> +++ b/drivers/gpu/drm/nouveau/nouveau_fence.h
> @@ -10,6 +10,7 @@ struct nouveau_bo;
>
> struct nouveau_fence {
> struct dma_fence base;
> + struct dma_fence_cb cb;
>
> struct list_head head;
>
Hi,
This series is the follow-up of the discussion that John and I had some
time ago here:
https://lore.kernel.org/all/CANDhNCquJn6bH3KxKf65BWiTYLVqSd9892-xtFDHHqqyrr…
The initial problem we were discussing was that I'm currently working on
a platform which has a memory layout with ECC enabled. However, enabling
the ECC has a number of drawbacks on that platform: lower performance,
increased memory usage, etc. So for things like framebuffers, the
trade-off isn't great and thus there's a memory region with ECC disabled
to allocate from for such use cases.
After a suggestion from John, I chose to first start using heap
allocations flags to allow for userspace to ask for a particular ECC
setup. This is then backed by a new heap type that runs from reserved
memory chunks flagged as such, and the existing DT properties to specify
the ECC properties.
After further discussion, it was considered that flags were not the
right solution, and relying on the names of the heaps would be enough to
let userspace know the kind of buffer it deals with.
Thus, even though the uAPI part of it has been dropped in this second
version, we still need a driver to create heaps out of carved-out memory
regions. In addition to the original usecase, a similar driver can be
found in BSPs from most vendors, so I believe it would be a useful
addition to the kernel.
I submitted a draft PR to the DT schema for the bindings used in this
PR:
https://github.com/devicetree-org/dt-schema/pull/138
Let me know what you think,
Maxime
Signed-off-by: Maxime Ripard <mripard(a)kernel.org>
---
Changes in v2:
- Add vmap/vunmap operations
- Drop ECC flags uapi
- Rebase on top of 6.14
- Link to v1: https://lore.kernel.org/r/20240515-dma-buf-ecc-heap-v1-0-54cbbd049511@kerne…
---
Maxime Ripard (2):
dma-buf: heaps: system: Remove global variable
dma-buf: heaps: Introduce a new heap for reserved memory
drivers/dma-buf/heaps/Kconfig | 8 +
drivers/dma-buf/heaps/Makefile | 1 +
drivers/dma-buf/heaps/carveout_heap.c | 360 ++++++++++++++++++++++++++++++++++
drivers/dma-buf/heaps/system_heap.c | 17 +-
4 files changed, 381 insertions(+), 5 deletions(-)
---
base-commit: fcbf30774e82a441890b722bf0c26542fb82150f
change-id: 20240515-dma-buf-ecc-heap-28a311d2c94e
Best regards,
--
Maxime Ripard <mripard(a)kernel.org>
Hi Amirreza,
kernel test robot noticed the following build warnings:
[auto build test WARNING on db8da9da41bced445077925f8a886c776a47440c]
url: https://github.com/intel-lab-lkp/linux/commits/Amirreza-Zarrabi/tee-allow-a…
base: db8da9da41bced445077925f8a886c776a47440c
patch link: https://lore.kernel.org/r/20250327-qcom-tee-using-tee-ss-without-mem-obj-v3…
patch subject: [PATCH v3 08/11] tee: add Qualcomm TEE driver
config: s390-allyesconfig (https://download.01.org/0day-ci/archive/20250329/202503290620.2KJEcZM6-lkp@…)
compiler: s390-linux-gcc (GCC) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250329/202503290620.2KJEcZM6-lkp@…)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp(a)intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202503290620.2KJEcZM6-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> drivers/tee/qcomtee/core.c:310: warning: Function parameter or struct member 'oic' not described in 'qcomtee_object_qtee_init'
vim +310 drivers/tee/qcomtee/core.c
298
299 /**
300 * qcomtee_object_qtee_init() - Initialize an object for QTEE.
301 * @object: object returned.
302 * @object_id: object ID received from QTEE.
303 *
304 * Return: On failure, returns < 0 and sets @object to %NULL_QCOMTEE_OBJECT.
305 * On success, returns 0
306 */
307 static int qcomtee_object_qtee_init(struct qcomtee_object_invoke_ctx *oic,
308 struct qcomtee_object **object,
309 unsigned int object_id)
> 310 {
311 int ret = 0;
312
313 switch (qcomtee_object_type(object_id)) {
314 case QCOMTEE_OBJECT_TYPE_NULL:
315 *object = NULL_QCOMTEE_OBJECT;
316
317 break;
318 case QCOMTEE_OBJECT_TYPE_CB:
319 *object = qcomtee_local_object_get(object_id);
320 if (*object == NULL_QCOMTEE_OBJECT)
321 ret = -EINVAL;
322
323 break;
324
325 default: /* QCOMTEE_OBJECT_TYPE_TEE */
326 *object = qcomtee_qtee_object_alloc(oic, object_id);
327 if (*object == NULL_QCOMTEE_OBJECT)
328 ret = -ENOMEM;
329
330 break;
331 }
332
333 return ret;
334 }
335
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
From: Rob Clark <robdclark(a)chromium.org>
Add support for exporting a dma_fence fd for a specific point on a
timeline. This is needed for vtest/vpipe[1][2] to implement timeline
syncobj support, as it needs a way to turn a point on a timeline back
into a dma_fence fd. It also closes an odd omission from the syncobj
UAPI.
[1] https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/33433
[2] https://gitlab.freedesktop.org/virgl/virglrenderer/-/merge_requests/805
v2: Add DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_TIMELINE
Signed-off-by: Rob Clark <robdclark(a)chromium.org>
---
drivers/gpu/drm/drm_syncobj.c | 18 +++++++++++++-----
include/uapi/drm/drm.h | 2 ++
2 files changed, 15 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/drm_syncobj.c b/drivers/gpu/drm/drm_syncobj.c
index 4f2ab8a7b50f..bc57d6f1a22e 100644
--- a/drivers/gpu/drm/drm_syncobj.c
+++ b/drivers/gpu/drm/drm_syncobj.c
@@ -762,7 +762,7 @@ static int drm_syncobj_import_sync_file_fence(struct drm_file *file_private,
}
static int drm_syncobj_export_sync_file(struct drm_file *file_private,
- int handle, int *p_fd)
+ int handle, u64 point, int *p_fd)
{
int ret;
struct dma_fence *fence;
@@ -772,7 +772,7 @@ static int drm_syncobj_export_sync_file(struct drm_file *file_private,
if (fd < 0)
return fd;
- ret = drm_syncobj_find_fence(file_private, handle, 0, 0, &fence);
+ ret = drm_syncobj_find_fence(file_private, handle, point, 0, &fence);
if (ret)
goto err_put_fd;
@@ -869,6 +869,9 @@ drm_syncobj_handle_to_fd_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_private)
{
struct drm_syncobj_handle *args = data;
+ unsigned valid_flags = DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_TIMELINE |
+ DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE;
+ u64 point = 0;
if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
return -EOPNOTSUPP;
@@ -876,13 +879,18 @@ drm_syncobj_handle_to_fd_ioctl(struct drm_device *dev, void *data,
if (args->pad)
return -EINVAL;
- if (args->flags != 0 &&
- args->flags != DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE)
+ if (args->flags != 0 && (args->flags & ~valid_flags))
return -EINVAL;
+ if (args->flags & DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_TIMELINE)
+ point = args->point;
+
if (args->flags & DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE)
return drm_syncobj_export_sync_file(file_private, args->handle,
- &args->fd);
+ point, &args->fd);
+
+ if (args->point)
+ return -EINVAL;
return drm_syncobj_handle_to_fd(file_private, args->handle,
&args->fd);
diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h
index 7fba37b94401..c71a8f4439f2 100644
--- a/include/uapi/drm/drm.h
+++ b/include/uapi/drm/drm.h
@@ -912,6 +912,8 @@ struct drm_syncobj_handle {
__s32 fd;
__u32 pad;
+
+ __u64 point;
};
struct drm_syncobj_transfer {
--
2.49.0
Am 27.03.25 um 09:42 schrieb Philipp Stanner:
> Nouveau currently relies on the assumption that dma_fences will only
> ever get signalled through nouveau_fence_signal(), which takes care of
> removing a signalled fence from the list nouveau_fence_chan.pending.
>
> This self-imposed rule is violated in nouveau_fence_done(), where
> dma_fence_is_signaled() can signal the fence without removing it from
> the list. This enables accesses to already signalled fences through the
> list, which is a bug.
>
> Furthermore, it must always be possible to use standard dma_fence
> methods an a dma_fence and observe valid behavior. The canonical way of
> ensuring that signalling a fence has additional effects is to add those
> effects to a callback and register it on the fence.
Good catch.
>
> Move the code from nouveau_fence_signal() into a dma_fence callback.
> Register that callback when creating the fence.
But that's a really ugly approach.
Either nouveau shouldn't implement the signaled callback or make sure that when returning true from the signaled callback the fence is also removed from the pending list.
>
> Cc: <stable(a)vger.kernel.org> # 4.10+
> Fixes: f54d1867005c ("dma-buf: Rename struct fence to dma_fence")
> Signed-off-by: Philipp Stanner <phasta(a)kernel.org>
> ---
> I'm not entirely sure what Fixes-Tag is appropriate. The last time the
> line causing the signalled fence in the list was touched is the commit
> listed above.
Yeah, that's most likely not correct. My educated guess is that the bug was always there just never discovered.
Regards,
Christian.
> ---
> drivers/gpu/drm/nouveau/nouveau_fence.c | 41 ++++++++++++++++---------
> drivers/gpu/drm/nouveau/nouveau_fence.h | 1 +
> 2 files changed, 27 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c
> index 7cc84472cece..b2c2241a8803 100644
> --- a/drivers/gpu/drm/nouveau/nouveau_fence.c
> +++ b/drivers/gpu/drm/nouveau/nouveau_fence.c
> @@ -50,24 +50,22 @@ nouveau_fctx(struct nouveau_fence *fence)
> return container_of(fence->base.lock, struct nouveau_fence_chan, lock);
> }
>
> -static int
> -nouveau_fence_signal(struct nouveau_fence *fence)
> +static void
> +nouveau_fence_cleanup_cb(struct dma_fence *dfence, struct dma_fence_cb *cb)
> {
> - int drop = 0;
> + struct nouveau_fence_chan *fctx;
> + struct nouveau_fence *fence;
> +
> + fence = container_of(dfence, struct nouveau_fence, base);
> + fctx = nouveau_fctx(fence);
>
> - dma_fence_signal_locked(&fence->base);
> list_del(&fence->head);
> rcu_assign_pointer(fence->channel, NULL);
>
> - if (test_bit(DMA_FENCE_FLAG_USER_BITS, &fence->base.flags)) {
> - struct nouveau_fence_chan *fctx = nouveau_fctx(fence);
> -
> - if (!--fctx->notify_ref)
> - drop = 1;
> - }
> + if (test_bit(DMA_FENCE_FLAG_USER_BITS, &fence->base.flags))
> + --fctx->notify_ref;
>
> dma_fence_put(&fence->base);
> - return drop;
> }
>
> static struct nouveau_fence *
> @@ -93,7 +91,8 @@ nouveau_fence_context_kill(struct nouveau_fence_chan *fctx, int error)
> if (error)
> dma_fence_set_error(&fence->base, error);
>
> - if (nouveau_fence_signal(fence))
> + dma_fence_signal_locked(&fence->base);
> + if (fctx->notify_ref == 0)
> nvif_event_block(&fctx->event);
> }
> fctx->killed = 1;
> @@ -131,7 +130,6 @@ static int
> nouveau_fence_update(struct nouveau_channel *chan, struct nouveau_fence_chan *fctx)
> {
> struct nouveau_fence *fence;
> - int drop = 0;
> u32 seq = fctx->read(chan);
>
> while (!list_empty(&fctx->pending)) {
> @@ -140,10 +138,10 @@ nouveau_fence_update(struct nouveau_channel *chan, struct nouveau_fence_chan *fc
> if ((int)(seq - fence->base.seqno) < 0)
> break;
>
> - drop |= nouveau_fence_signal(fence);
> + dma_fence_signal_locked(&fence->base);
> }
>
> - return drop;
> + return fctx->notify_ref == 0 ? 1 : 0;
> }
>
> static void
> @@ -235,6 +233,19 @@ nouveau_fence_emit(struct nouveau_fence *fence)
> &fctx->lock, fctx->context, ++fctx->sequence);
> kref_get(&fctx->fence_ref);
>
> + fence->cb.func = nouveau_fence_cleanup_cb;
> + /* Adding a callback runs into __dma_fence_enable_signaling(), which will
> + * ultimately run into nouveau_fence_no_signaling(), where a WARN_ON
> + * would fire because the refcount can be dropped there.
> + *
> + * Increment the refcount here temporarily to work around that.
> + */
> + dma_fence_get(&fence->base);
> + ret = dma_fence_add_callback(&fence->base, &fence->cb, nouveau_fence_cleanup_cb);
> + dma_fence_put(&fence->base);
> + if (ret)
> + return ret;
> +
> ret = fctx->emit(fence);
> if (!ret) {
> dma_fence_get(&fence->base);
> diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.h b/drivers/gpu/drm/nouveau/nouveau_fence.h
> index 8bc065acfe35..e6b2df7fdc42 100644
> --- a/drivers/gpu/drm/nouveau/nouveau_fence.h
> +++ b/drivers/gpu/drm/nouveau/nouveau_fence.h
> @@ -10,6 +10,7 @@ struct nouveau_bo;
>
> struct nouveau_fence {
> struct dma_fence base;
> + struct dma_fence_cb cb;
>
> struct list_head head;
>
Hi,
This patch set allocates the restricted DMA-bufs from a DMA-heap
instantiated from the TEE subsystem.
The TEE subsystem handles the DMA-buf allocations since it is the TEE
(OP-TEE, AMD-TEE, TS-TEE, or perhaps a future QTEE) which sets up the
restrictions for the memory used for the DMA-bufs.
The DMA-heap uses a restricted memory pool provided by the backend TEE
driver, allowing it to choose how to allocate the restricted physical
memory.
The allocated DMA-bufs must be imported with a new TEE_IOC_SHM_REGISTER_FD
before they can be passed as arguments when requesting services from the
secure world.
Three use-cases (Secure Video Playback, Trusted UI, and Secure Video
Recording) has been identified so far to serve as examples of what can be
expected. The use-cases has predefined DMA-heap names,
"restricted,secure-video", "restricted,trusted-ui", and
"restricted,secure-video-record". The backend driver registers restricted
memory pools for the use-cases it supports.
Each use-case has it's own restricted memory pool since different use-cases
requires isolation from different parts of the system. A restricted memory
pool can be based on a static carveout instantiated while probing the TEE
backend driver, or dynamically allocated from CMA and made restricted as
needed by the TEE.
This can be tested on a RockPi 4B+ with the following steps:
repo init -u https://github.com/jenswi-linaro/manifest.git -m rockpi4.xml \
-b prototype/sdp-v6
repo sync -j8
cd build
make toolchains -j$(nproc)
make all -j$(nproc)
# Copy ../out/rockpi4.img to an SD card and boot the RockPi from that
# Connect a monitor to the RockPi
# login and at the prompt:
gst-launch-1.0 videotestsrc ! \
aesenc key=1f9423681beb9a79215820f6bda73d0f \
iv=e9aa8e834d8d70b7e0d254ff670dd718 serialize-iv=true ! \
aesdec key=1f9423681beb9a79215820f6bda73d0f ! \
kmssink
The aesdec module has been hacked to use an OP-TEE TA to decrypt the stream
into restricted DMA-bufs which are consumed by the kmssink.
The primitive QEMU tests from previous patch set can be tested on RockPi
in the same way with:
xtest --sdp-basic
The primitive test are tested on QEMU with the following steps:
repo init -u https://github.com/jenswi-linaro/manifest.git -m qemu_v8.xml \
-b prototype/sdp-v6
repo sync -j8
cd build
make toolchains -j$(nproc)
make SPMC_AT_EL=1 all -j$(nproc)
make SPMC_AT_EL=1 run-only
# login and at the prompt:
xtest --sdp-basic
The SPMC_AT_EL=1 parameter configures the build with FF-A and an SPMC at
S-EL1 inside OP-TEE. The parameter can be changed into SPMC_AT_EL=n to test
without FF-A using the original SMC ABI instead. Please remember to do
%rm -rf ../trusted-firmware-a/build/qemu
for TF-A to be rebuilt properly using the new configuration.
https://optee.readthedocs.io/en/latest/building/prerequisites.html
list dependencies needed to build the above.
The tests are pretty basic, mostly checking that a Trusted Application in
the secure world can access and manipulate the memory. There are also some
negative tests for out of bounds buffers etc.
Thanks,
Jens
Changes since V5:
* Removing "tee: add restricted memory allocation" and
"tee: add TEE_IOC_RSTMEM_FD_INFO"
* Adding "tee: implement restricted DMA-heap",
"tee: new ioctl to a register tee_shm from a dmabuf file descriptor",
"tee: add tee_shm_alloc_cma_phys_mem()",
"optee: pass parent device to tee_device_alloc()", and
"tee: tee_device_alloc(): copy dma_mask from parent device"
* The two TEE driver OPs "rstmem_alloc()" and "rstmem_free()" are replaced
with a struct tee_rstmem_pool abstraction.
* Replaced the the TEE_IOC_RSTMEM_ALLOC user space API with the DMA-heap API
Changes since V4:
* Adding the patch "tee: add TEE_IOC_RSTMEM_FD_INFO" needed by the
GStreamer demo
* Removing the dummy CPU access and mmap functions from the dma_buf_ops
* Fixing a compile error in "optee: FF-A: dynamic restricted memory allocation"
reported by kernel test robot <lkp(a)intel.com>
Changes since V3:
* Make the use_case and flags field in struct tee_shm u32's instead of
u16's
* Add more description for TEE_IOC_RSTMEM_ALLOC in the header file
* Import namespace DMA_BUF in module tee, reported by lkp(a)intel.com
* Added a note in the commit message for "optee: account for direction
while converting parameters" why it's needed
* Factor out dynamic restricted memory allocation from
"optee: support restricted memory allocation" into two new commits
"optee: FF-A: dynamic restricted memory allocation" and
"optee: smc abi: dynamic restricted memory allocation"
* Guard CMA usage with #ifdef CONFIG_CMA, effectively disabling dynamic
restricted memory allocate if CMA isn't configured
Changes since the V2 RFC:
* Based on v6.12
* Replaced the flags for SVP and Trusted UID memory with a u32 field with
unique id for each use case
* Added dynamic allocation of restricted memory pools
* Added OP-TEE ABI both with and without FF-A for dynamic restricted memory
* Added support for FF-A with FFA_LEND
Changes since the V1 RFC:
* Based on v6.11
* Complete rewrite, replacing the restricted heap with TEE_IOC_RSTMEM_ALLOC
Changes since Olivier's post [2]:
* Based on Yong Wu's post [1] where much of dma-buf handling is done in
the generic restricted heap
* Simplifications and cleanup
* New commit message for "dma-buf: heaps: add Linaro restricted dmabuf heap
support"
* Replaced the word "secure" with "restricted" where applicable
Etienne Carriere (1):
tee: new ioctl to a register tee_shm from a dmabuf file descriptor
Jens Wiklander (9):
tee: tee_device_alloc(): copy dma_mask from parent device
optee: pass parent device to tee_device_alloc()
optee: account for direction while converting parameters
optee: sync secure world ABI headers
tee: implement restricted DMA-heap
tee: add tee_shm_alloc_cma_phys_mem()
optee: support restricted memory allocation
optee: FF-A: dynamic restricted memory allocation
optee: smc abi: dynamic restricted memory allocation
drivers/tee/Makefile | 1 +
drivers/tee/optee/Makefile | 1 +
drivers/tee/optee/call.c | 10 +-
drivers/tee/optee/core.c | 1 +
drivers/tee/optee/ffa_abi.c | 194 +++++++++++-
drivers/tee/optee/optee_ffa.h | 27 +-
drivers/tee/optee/optee_msg.h | 65 ++++-
drivers/tee/optee/optee_private.h | 55 +++-
drivers/tee/optee/optee_smc.h | 71 ++++-
drivers/tee/optee/rpc.c | 31 +-
drivers/tee/optee/rstmem.c | 329 +++++++++++++++++++++
drivers/tee/optee/smc_abi.c | 190 ++++++++++--
drivers/tee/tee_core.c | 147 +++++++---
drivers/tee/tee_heap.c | 470 ++++++++++++++++++++++++++++++
drivers/tee/tee_private.h | 7 +
drivers/tee/tee_shm.c | 199 ++++++++++++-
include/linux/tee_core.h | 67 +++++
include/linux/tee_drv.h | 10 +
include/uapi/linux/tee.h | 29 ++
19 files changed, 1781 insertions(+), 123 deletions(-)
create mode 100644 drivers/tee/optee/rstmem.c
create mode 100644 drivers/tee/tee_heap.c
base-commit: 7eb172143d5508b4da468ed59ee857c6e5e01da6
--
2.43.0
Am 26.03.25 um 03:59 schrieb Kasireddy, Vivek:
> Hi Christian,
>
>> Subject: Re: [PATCH] udmabuf: fix a buf size overflow issue during udmabuf
>> creation
>>
>> Am 25.03.25 um 07:23 schrieb Kasireddy, Vivek:
>>> Hi Christian,
>>>
>>>> Am 21.03.25 um 17:41 schrieb Xiaogang.Chen:
>>>>> From: Xiaogang Chen <xiaogang.chen(a)amd.com>
>>>>>
>>>>> by casting size_limit_mb to u64 when calculate pglimit.
>>>>>
>>>>> Signed-off-by: Xiaogang Chen<Xiaogang.Chen(a)amd.com>
>>>> Reviewed-by: Christian König <christian.koenig(a)amd.com>
>>>>
>>>> If nobody objects I'm going to push that to drm-misc-fixes.
>>> No objection but I wish the author would have added more details in the
>> commit
>>> message particularly the value they have used to trigger the overflow. I
>> guess
>>> Xiaogang can still comment here and briefly describe the exact use-
>> case/test-case
>>> they are running where they encountered this issue.
>> Isn't that obvious? At least it was for me.
>>
>> As soon as you have a value larger than 4095 the 32bit multiplication
>> overflows, resulting in incorrectly limiting the buffer size.
> Right, that part makes sense. I was mostly curious about why or how they
> were using such a large buffer (use-case details).
Well I suggested that we use udmabuf to implement shareable dma-bufs which can be allocated from a specific NUMA node and are also accounted in memcg.
But to be honest I have absolutely no idea what's the use case for a buffer larger than 4GiB.
Regards,
Christian.
>
>
> Thanks,
> Vivek
>
>> Regards,
>> Christian.
>>
>>> Thanks,
>>> Vivek
>>>
>>>> Regards,
>>>> Christian.
>>>>
>>>>> ---
>>>>> drivers/dma-buf/udmabuf.c | 2 +-
>>>>> 1 file changed, 1 insertion(+), 1 deletion(-)
>>>>>
>>>>> diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c
>>>>> index 8ce1f074c2d3..e99e3a65a470 100644
>>>>> --- a/drivers/dma-buf/udmabuf.c
>>>>> +++ b/drivers/dma-buf/udmabuf.c
>>>>> @@ -398,7 +398,7 @@ static long udmabuf_create(struct miscdevice
>>>> *device,
>>>>> if (!ubuf)
>>>>> return -ENOMEM;
>>>>>
>>>>> - pglimit = (size_limit_mb * 1024 * 1024) >> PAGE_SHIFT;
>>>>> + pglimit = ((u64)size_limit_mb * 1024 * 1024) >> PAGE_SHIFT;
>>>>> for (i = 0; i < head->count; i++) {
>>>>> pgoff_t subpgcnt;
>>>>>