[PATCH 1/3] drm/suballoc: Introduce a generic suballocation manager

Thomas Hellström thomas.hellstrom at linux.intel.com
Fri Feb 17 12:24:42 UTC 2023


On 2/17/23 12:28, Christian König wrote:
> Am 17.02.23 um 12:21 schrieb Thomas Hellström:
>>
>> On 2/17/23 12:00, Christian König wrote:
>>> Am 16.02.23 um 15:48 schrieb Thomas Hellström:
>>>> Initially we tried to leverage the amdgpu suballocation manager.
>>>> It turnes out, however, that it tries extremely hard not to enable
>>>> signalling on the fences that hold the memory up for freeing, which 
>>>> makes
>>>> it hard to understand and to fix potential issues with it.
>>>>
>>>> So in a simplification effort, introduce a drm suballocation 
>>>> manager as a
>>>> wrapper around an existing allocator (drm_mm) and to avoid using 
>>>> queues
>>>> for freeing, thus avoiding throttling on free which is an undesired
>>>> feature as typically the throttling needs to be done uninterruptible.
>>>>
>>>> This variant is probably more cpu-hungry but can be improved at the 
>>>> cost
>>>> of additional complexity. Ideas for that are documented in the
>>>> drm_suballoc.c file.
>>>>
>>>> Signed-off-by: Thomas Hellström <thomas.hellstrom at linux.intel.com>
>>>> Co-developed-by: Maarten Lankhorst <maarten.lankhorst at linux.intel.com>
>>>> Signed-off-by: Maarten Lankhorst <maarten.lankhorst at linux.intel.com>
>>>> ---
>>>>   drivers/gpu/drm/Kconfig        |   4 +
>>>>   drivers/gpu/drm/Makefile       |   3 +
>>>>   drivers/gpu/drm/drm_suballoc.c | 301 
>>>> +++++++++++++++++++++++++++++++++
>>>>   include/drm/drm_suballoc.h     | 112 ++++++++++++
>>>>   4 files changed, 420 insertions(+)
>>>>   create mode 100644 drivers/gpu/drm/drm_suballoc.c
>>>>   create mode 100644 include/drm/drm_suballoc.h
>>>>
>>>> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
>>>> index dc0f94f02a82..8fbe57407c60 100644
>>>> --- a/drivers/gpu/drm/Kconfig
>>>> +++ b/drivers/gpu/drm/Kconfig
>>>> @@ -232,6 +232,10 @@ config DRM_GEM_SHMEM_HELPER
>>>>       help
>>>>         Choose this if you need the GEM shmem helper functions
>>>>   +config DRM_SUBALLOC_HELPER
>>>> +    tristate
>>>> +    depends on DRM
>>>> +
>>>>   config DRM_SCHED
>>>>       tristate
>>>>       depends on DRM
>>>> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
>>>> index ab4460fcd63f..1e04d135e866 100644
>>>> --- a/drivers/gpu/drm/Makefile
>>>> +++ b/drivers/gpu/drm/Makefile
>>>> @@ -88,6 +88,9 @@ obj-$(CONFIG_DRM_GEM_DMA_HELPER) += drm_dma_helper.o
>>>>   drm_shmem_helper-y := drm_gem_shmem_helper.o
>>>>   obj-$(CONFIG_DRM_GEM_SHMEM_HELPER) += drm_shmem_helper.o
>>>>   +drm_suballoc_helper-y := drm_suballoc.o
>>>> +obj-$(CONFIG_DRM_SUBALLOC_HELPER) += drm_suballoc_helper.o
>>>> +
>>>>   drm_vram_helper-y := drm_gem_vram_helper.o
>>>>   obj-$(CONFIG_DRM_VRAM_HELPER) += drm_vram_helper.o
>>>>   diff --git a/drivers/gpu/drm/drm_suballoc.c 
>>>> b/drivers/gpu/drm/drm_suballoc.c
>>>> new file mode 100644
>>>> index 000000000000..6e0292dea548
>>>> --- /dev/null
>>>> +++ b/drivers/gpu/drm/drm_suballoc.c
>>>> @@ -0,0 +1,301 @@
>>>> +// SPDX-License-Identifier: MIT
>>>> +/*
>>>> + * Copyright © 2022 Intel Corporation
>>>> + */
>>>> +
>>>> +#include <drm/drm_suballoc.h>
>>>> +
>>>> +/**
>>>> + * DOC:
>>>> + * This suballocator intends to be a wrapper around a range allocator
>>>> + * that is aware also of deferred range freeing with fences. 
>>>> Currently
>>>> + * we hard-code the drm_mm as the range allocator.
>>>> + * The approach, while rather simple, suffers from three performance
>>>> + * issues that can all be fixed if needed at the tradeoff of more 
>>>> and / or
>>>> + * more complex code:
>>>> + *
>>>> + * 1) It's cpu-hungry, the drm_mm allocator is overkill. Either 
>>>> code a
>>>> + * much simpler range allocator, or let the caller decide by 
>>>> providing
>>>> + * ops that wrap any range allocator. Also could avoid waking up 
>>>> unless
>>>> + * there is a reasonable chance of enough space in the range manager.
>>>
>>> That's most likely highly problematic.
>>>
>>> The suballocator in radeon/amdgpu was designed so that it resembles 
>>> a ring buffer and is therefor rather CPU efficient.
>>>
>>> We could make the allocator much more trivial, but using drm_mm for 
>>> this is a sledgehammer and therefore a pretty clear no-go.
>>>
>> I don't think the ring vs non-ring is the big problem here, because 
>> (at least with the original implementation), if allocations are 
>> actually made and released in a ring-like fashion, the drm_mm 
>> free-list would consist of one or two blocks and therefore pretty 
>> efficient even for that case, and if slightly longer that would still 
>> not be an issue compared to the fence lists maintained in the older 
>> allocator.
>>
>> The problem is more all the other stuff that was added and built on 
>> top like the interval / rb tree.
>>
>> I still like the idea (originating from Gallium's helpers) to 
>> separate whatever is allocating from the fence delayed free.
>
> That's actually a bad idea. See the ring like approach works because 
> the fences used in amdgpu/radeon are used in a ring like fashion. E.g. 
> the sub allocator mainly provides the temporary space for page table 
> updates. Those in turn are then used by commands written into a ring 
> buffer.

Well, what I'm saying is that *even* if you have a ring-like allocation 
algorithm, given a simpler drm_mm, I think the suggested code would be 
performing just as well as the one in amdgpu / radeon, on top of 
avoiding throttling on free, or do you have a particular scenario in 
mind that you think would be particularly pathological on this allocator?

>
>>
>> Any chance you could do a quick performance comparison? If not, 
>> anything against merging this without the amd / radeon changes until 
>> we can land a simpler allocator?
>
> Only if you can stick the allocator inside Xe and not drm, cause this 
> seems to be for a different use case than the allocators inside 
> radeon/amdgpu.

Hmm. No It's allocating in a ring-like fashion as well.  Let me put 
together a unit test for benchmaking. I think it would be a failure for 
the community to end up with three separate suballocators doing the 
exact same thing for the same problem, really.

/Thomas

>
> Regards,
> Christian.
>
>>
>> Thanks,
>> Thomas
>>
>>
>> Thomas
>>
>>
>>> Regards,
>>> Christian.
>>>
>>>> + *
>>>> + * 2) We unnecessarily install the fence callbacks too early, forcing
>>>> + * enable_signaling() too early causing extra driver effort. This 
>>>> is likely
>>>> + * not an issue if used with the drm_scheduler since it calls
>>>> + * enable_signaling() early anyway.
>>>> + *
>>>> + * 3) Long processing in irq (disabled) context. We've mostly 
>>>> worked around
>>>> + * that already by using the idle_list. If that workaround is 
>>>> deemed to
>>>> + * complex for little gain, we can remove it and use spin_lock_irq()
>>>> + * throughout the manager. If we want to shorten processing in irq 
>>>> context
>>>> + * even further, we can skip the spin_trylock in 
>>>> __drm_suballoc_free() and
>>>> + * avoid freeing allocations from irq context altogeher. However 
>>>> drm_mm
>>>> + * should be quite fast at freeing ranges.
>>>> + *
>>>> + * 4) Shrinker that starts processing the list items in 2) and 3) 
>>>> to play
>>>> + * better with the system.
>>>> + */
>>>> +
>>>> +static void drm_suballoc_process_idle(struct drm_suballoc_manager 
>>>> *sa_manager);
>>>> +
>>>> +/**
>>>> + * drm_suballoc_manager_init() - Initialise the drm_suballoc_manager
>>>> + * @sa_manager: pointer to the sa_manager
>>>> + * @size: number of bytes we want to suballocate
>>>> + * @align: alignment for each suballocated chunk
>>>> + *
>>>> + * Prepares the suballocation manager for suballocations.
>>>> + */
>>>> +void drm_suballoc_manager_init(struct drm_suballoc_manager 
>>>> *sa_manager,
>>>> +                   u64 size, u64 align)
>>>> +{
>>>> +    spin_lock_init(&sa_manager->lock);
>>>> +    spin_lock_init(&sa_manager->idle_list_lock);
>>>> +    mutex_init(&sa_manager->alloc_mutex);
>>>> +    drm_mm_init(&sa_manager->mm, 0, size);
>>>> +    init_waitqueue_head(&sa_manager->wq);
>>>> +    sa_manager->range_size = size;
>>>> +    sa_manager->alignment = align;
>>>> +    INIT_LIST_HEAD(&sa_manager->idle_list);
>>>> +}
>>>> +EXPORT_SYMBOL(drm_suballoc_manager_init);
>>>> +
>>>> +/**
>>>> + * drm_suballoc_manager_fini() - Destroy the drm_suballoc_manager
>>>> + * @sa_manager: pointer to the sa_manager
>>>> + *
>>>> + * Cleans up the suballocation manager after use. All fences added
>>>> + * with drm_suballoc_free() must be signaled, or we cannot clean up
>>>> + * the entire manager.
>>>> + */
>>>> +void drm_suballoc_manager_fini(struct drm_suballoc_manager 
>>>> *sa_manager)
>>>> +{
>>>> +    drm_suballoc_process_idle(sa_manager);
>>>> +    drm_mm_takedown(&sa_manager->mm);
>>>> +    mutex_destroy(&sa_manager->alloc_mutex);
>>>> +}
>>>> +EXPORT_SYMBOL(drm_suballoc_manager_fini);
>>>> +
>>>> +static void __drm_suballoc_free(struct drm_suballoc *sa)
>>>> +{
>>>> +    struct drm_suballoc_manager *sa_manager = sa->manager;
>>>> +    struct dma_fence *fence;
>>>> +
>>>> +    /*
>>>> +     * In order to avoid protecting the potentially lengthy drm_mm 
>>>> manager
>>>> +     * *allocation* processing with an irq-disabling lock,
>>>> +     * defer touching the drm_mm for freeing until we're in task 
>>>> context,
>>>> +     * with no irqs disabled, or happen to succeed in taking the 
>>>> manager
>>>> +     * lock.
>>>> +     */
>>>> +    if (!in_task() || irqs_disabled()) {
>>>> +        unsigned long irqflags;
>>>> +
>>>> +        if (spin_trylock(&sa_manager->lock))
>>>> +            goto locked;
>>>> +
>>>> + spin_lock_irqsave(&sa_manager->idle_list_lock, irqflags);
>>>> +        list_add_tail(&sa->idle_link, &sa_manager->idle_list);
>>>> + spin_unlock_irqrestore(&sa_manager->idle_list_lock, irqflags);
>>>> +        wake_up(&sa_manager->wq);
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    spin_lock(&sa_manager->lock);
>>>> +locked:
>>>> +    drm_mm_remove_node(&sa->node);
>>>> +
>>>> +    fence = sa->fence;
>>>> +    sa->fence = NULL;
>>>> +    spin_unlock(&sa_manager->lock);
>>>> +    /* Maybe only wake if first mm hole is sufficiently large? */
>>>> +    wake_up(&sa_manager->wq);
>>>> +    dma_fence_put(fence);
>>>> +    kfree(sa);
>>>> +}
>>>> +
>>>> +/* Free all deferred idle allocations */
>>>> +static void drm_suballoc_process_idle(struct drm_suballoc_manager 
>>>> *sa_manager)
>>>> +{
>>>> +    /*
>>>> +     * prepare_to_wait() / wake_up() semantics ensure that any list
>>>> +     * addition that was done before wake_up() is visible when
>>>> +     * this code is called from the wait loop.
>>>> +     */
>>>> +    if (!list_empty_careful(&sa_manager->idle_list)) {
>>>> +        struct drm_suballoc *sa, *next;
>>>> +        unsigned long irqflags;
>>>> +        LIST_HEAD(list);
>>>> +
>>>> + spin_lock_irqsave(&sa_manager->idle_list_lock, irqflags);
>>>> +        list_splice_init(&sa_manager->idle_list, &list);
>>>> + spin_unlock_irqrestore(&sa_manager->idle_list_lock, irqflags);
>>>> +
>>>> +        list_for_each_entry_safe(sa, next, &list, idle_link)
>>>> +            __drm_suballoc_free(sa);
>>>> +    }
>>>> +}
>>>> +
>>>> +static void
>>>> +drm_suballoc_fence_signaled(struct dma_fence *fence, struct 
>>>> dma_fence_cb *cb)
>>>> +{
>>>> +    struct drm_suballoc *sa = container_of(cb, typeof(*sa), cb);
>>>> +
>>>> +    __drm_suballoc_free(sa);
>>>> +}
>>>> +
>>>> +static int drm_suballoc_tryalloc(struct drm_suballoc *sa, u64 size)
>>>> +{
>>>> +    struct drm_suballoc_manager *sa_manager = sa->manager;
>>>> +    int err;
>>>> +
>>>> +    drm_suballoc_process_idle(sa_manager);
>>>> +    spin_lock(&sa_manager->lock);
>>>> +    err = drm_mm_insert_node_generic(&sa_manager->mm, &sa->node, 
>>>> size,
>>>> +                     sa_manager->alignment, 0,
>>>> +                     DRM_MM_INSERT_EVICT);
>>>> +    spin_unlock(&sa_manager->lock);
>>>> +    return err;
>>>> +}
>>>> +
>>>> +/**
>>>> + * drm_suballoc_new() - Make a suballocation.
>>>> + * @sa_manager: pointer to the sa_manager
>>>> + * @size: number of bytes we want to suballocate.
>>>> + * @gfp: Allocation context.
>>>> + * @intr: Whether to sleep interruptibly if sleeping.
>>>> + *
>>>> + * Try to make a suballocation of size @size, which will be rounded
>>>> + * up to the alignment specified in specified in 
>>>> drm_suballoc_manager_init().
>>>> + *
>>>> + * Returns a new suballocated bo, or an ERR_PTR.
>>>> + */
>>>> +struct drm_suballoc*
>>>> +drm_suballoc_new(struct drm_suballoc_manager *sa_manager, u64 size,
>>>> +         gfp_t gfp, bool intr)
>>>> +{
>>>> +    struct drm_suballoc *sa;
>>>> +    DEFINE_WAIT(wait);
>>>> +    int err = 0;
>>>> +
>>>> +    if (size > sa_manager->range_size)
>>>> +        return ERR_PTR(-ENOSPC);
>>>> +
>>>> +    sa = kzalloc(sizeof(*sa), gfp);
>>>> +    if (!sa)
>>>> +        return ERR_PTR(-ENOMEM);
>>>> +
>>>> +    /* Avoid starvation using the alloc_mutex */
>>>> +    if (intr)
>>>> +        err = mutex_lock_interruptible(&sa_manager->alloc_mutex);
>>>> +    else
>>>> +        mutex_lock(&sa_manager->alloc_mutex);
>>>> +    if (err) {
>>>> +        kfree(sa);
>>>> +        return ERR_PTR(err);
>>>> +    }
>>>> +
>>>> +    sa->manager = sa_manager;
>>>> +    err = drm_suballoc_tryalloc(sa, size);
>>>> +    if (err != -ENOSPC)
>>>> +        goto out;
>>>> +
>>>> +    for (;;) {
>>>> +        prepare_to_wait(&sa_manager->wq, &wait,
>>>> +                intr ? TASK_INTERRUPTIBLE :
>>>> +                TASK_UNINTERRUPTIBLE);
>>>> +
>>>> +        err = drm_suballoc_tryalloc(sa, size);
>>>> +        if (err != -ENOSPC)
>>>> +            break;
>>>> +
>>>> +        if (intr && signal_pending(current)) {
>>>> +            err = -ERESTARTSYS;
>>>> +            break;
>>>> +        }
>>>> +
>>>> +        io_schedule();
>>>> +    }
>>>> +    finish_wait(&sa_manager->wq, &wait);
>>>> +
>>>> +out:
>>>> +    mutex_unlock(&sa_manager->alloc_mutex);
>>>> +    if (!sa->node.size) {
>>>> +        kfree(sa);
>>>> +        WARN_ON(!err);
>>>> +        sa = ERR_PTR(err);
>>>> +    }
>>>> +
>>>> +    return sa;
>>>> +}
>>>> +EXPORT_SYMBOL(drm_suballoc_new);
>>>> +
>>>> +/**
>>>> + * drm_suballoc_free() - Free a suballocation
>>>> + * @suballoc: pointer to the suballocation
>>>> + * @fence: fence that signals when suballocation is idle
>>>> + * @queue: the index to which queue the suballocation will be 
>>>> placed on the free list.
>>>> + *
>>>> + * Free the suballocation. The suballocation can be re-used after 
>>>> @fence
>>>> + * signals.
>>>> + */
>>>> +void
>>>> +drm_suballoc_free(struct drm_suballoc *sa, struct dma_fence *fence)
>>>> +{
>>>> +    if (!sa)
>>>> +        return;
>>>> +
>>>> +    if (!fence || dma_fence_is_signaled(fence)) {
>>>> +        __drm_suballoc_free(sa);
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    sa->fence = dma_fence_get(fence);
>>>> +    if (dma_fence_add_callback(fence, &sa->cb, 
>>>> drm_suballoc_fence_signaled))
>>>> +        __drm_suballoc_free(sa);
>>>> +}
>>>> +EXPORT_SYMBOL(drm_suballoc_free);
>>>> +
>>>> +#ifdef CONFIG_DEBUG_FS
>>>> +
>>>> +/**
>>>> + * drm_suballoc_dump_debug_info() - Dump the suballocator state
>>>> + * @sa_manager: The suballoc manager.
>>>> + * @p: Pointer to a drm printer for output.
>>>> + * @suballoc_base: Constant to add to the suballocated offsets on 
>>>> printout.
>>>> + *
>>>> + * This function dumps the suballocator state. Note that the 
>>>> caller has
>>>> + * to explicitly order frees and calls to this function in order 
>>>> for the
>>>> + * freed node to show up as protected by a fence.
>>>> + */
>>>> +void drm_suballoc_dump_debug_info(struct drm_suballoc_manager 
>>>> *sa_manager,
>>>> +                  struct drm_printer *p, u64 suballoc_base)
>>>> +{
>>>> +    const struct drm_mm_node *entry;
>>>> +
>>>> +    spin_lock(&sa_manager->lock);
>>>> +    drm_mm_for_each_node(entry, &sa_manager->mm) {
>>>> +        struct drm_suballoc *sa =
>>>> +            container_of(entry, typeof(*sa), node);
>>>> +
>>>> +        drm_printf(p, " ");
>>>> +        drm_printf(p, "[0x%010llx 0x%010llx] size %8lld",
>>>> +               (unsigned long long)suballoc_base + entry->start,
>>>> +               (unsigned long long)suballoc_base + entry->start +
>>>> +               entry->size, (unsigned long long)entry->size);
>>>> +
>>>> +        if (sa->fence)
>>>> +            drm_printf(p, " protected by 0x%016llx on context %llu",
>>>> +                   (unsigned long long)sa->fence->seqno,
>>>> +                   (unsigned long long)sa->fence->context);
>>>> +
>>>> +        drm_printf(p, "\n");
>>>> +    }
>>>> +    spin_unlock(&sa_manager->lock);
>>>> +}
>>>> +EXPORT_SYMBOL(drm_suballoc_dump_debug_info);
>>>> +#endif
>>>> +
>>>> +MODULE_AUTHOR("Intel Corporation");
>>>> +MODULE_DESCRIPTION("Simple range suballocator helper");
>>>> +MODULE_LICENSE("GPL and additional rights");
>>>> diff --git a/include/drm/drm_suballoc.h b/include/drm/drm_suballoc.h
>>>> new file mode 100644
>>>> index 000000000000..910952b3383b
>>>> --- /dev/null
>>>> +++ b/include/drm/drm_suballoc.h
>>>> @@ -0,0 +1,112 @@
>>>> +/* SPDX-License-Identifier: MIT */
>>>> +/*
>>>> + * Copyright © 2022 Intel Corporation
>>>> + */
>>>> +#ifndef _DRM_SUBALLOC_H_
>>>> +#define _DRM_SUBALLOC_H_
>>>> +
>>>> +#include <drm/drm_mm.h>
>>>> +
>>>> +#include <linux/dma-fence.h>
>>>> +#include <linux/types.h>
>>>> +
>>>> +/**
>>>> + * struct drm_suballoc_manager - Wrapper for fenced range allocations
>>>> + * @mm: The range manager. Protected by @lock.
>>>> + * @range_size: The total size of the range.
>>>> + * @alignment: Range alignment.
>>>> + * @wq: Wait queue for sleeping allocations on contention.
>>>> + * @idle_list: List of idle but not yet freed allocations. 
>>>> Protected by
>>>> + * @idle_list_lock.
>>>> + * @task: Task waiting for allocation. Protected by @lock.
>>>> + */
>>>> +struct drm_suballoc_manager {
>>>> +    /** @lock: Manager lock. Protects @mm. */
>>>> +    spinlock_t lock;
>>>> +    /**
>>>> +     * @idle_list_lock: Lock to protect the idle_list.
>>>> +     * Disable irqs when locking.
>>>> +     */
>>>> +    spinlock_t idle_list_lock;
>>>> +    /** @alloc_mutex: Mutex to protect against stavation. */
>>>> +    struct mutex alloc_mutex;
>>>> +    struct drm_mm mm;
>>>> +    u64 range_size;
>>>> +    u64 alignment;
>>>> +    wait_queue_head_t wq;
>>>> +    struct list_head idle_list;
>>>> +};
>>>> +
>>>> +/**
>>>> + * struct drm_suballoc: Suballocated range.
>>>> + * @node: The drm_mm representation of the range.
>>>> + * @fence: dma-fence indicating whether allocation is active or idle.
>>>> + * Assigned on call to free the allocation so doesn't need 
>>>> protection.
>>>> + * @cb: dma-fence callback structure. Used for callbacks when the 
>>>> fence signals.
>>>> + * @manager: The struct drm_suballoc_manager the range belongs to. 
>>>> Immutable.
>>>> + * @idle_link: Link for the manager idle_list. Progected by the
>>>> + * drm_suballoc_manager::idle_lock.
>>>> + */
>>>> +struct drm_suballoc {
>>>> +    struct drm_mm_node node;
>>>> +    struct dma_fence *fence;
>>>> +    struct dma_fence_cb cb;
>>>> +    struct drm_suballoc_manager *manager;
>>>> +    struct list_head idle_link;
>>>> +};
>>>> +
>>>> +void drm_suballoc_manager_init(struct drm_suballoc_manager 
>>>> *sa_manager,
>>>> +                   u64 size, u64 align);
>>>> +
>>>> +void drm_suballoc_manager_fini(struct drm_suballoc_manager 
>>>> *sa_manager);
>>>> +
>>>> +struct drm_suballoc *drm_suballoc_new(struct drm_suballoc_manager 
>>>> *sa_manager,
>>>> +                      u64 size, gfp_t gfp, bool intr);
>>>> +
>>>> +void drm_suballoc_free(struct drm_suballoc *sa, struct dma_fence 
>>>> *fence);
>>>> +
>>>> +/**
>>>> + * drm_suballoc_soffset - Range start.
>>>> + * @sa: The struct drm_suballoc.
>>>> + *
>>>> + * Return: The start of the allocated range.
>>>> + */
>>>> +static inline u64 drm_suballoc_soffset(struct drm_suballoc *sa)
>>>> +{
>>>> +    return sa->node.start;
>>>> +}
>>>> +
>>>> +/**
>>>> + * drm_suballoc_eoffset - Range end.
>>>> + * @sa: The struct drm_suballoc.
>>>> + *
>>>> + * Return: The end of the allocated range + 1.
>>>> + */
>>>> +static inline u64 drm_suballoc_eoffset(struct drm_suballoc *sa)
>>>> +{
>>>> +    return sa->node.start + sa->node.size;
>>>> +}
>>>> +
>>>> +/**
>>>> + * drm_suballoc_size - Range size.
>>>> + * @sa: The struct drm_suballoc.
>>>> + *
>>>> + * Return: The size of the allocated range.
>>>> + */
>>>> +static inline u64 drm_suballoc_size(struct drm_suballoc *sa)
>>>> +{
>>>> +    return sa->node.size;
>>>> +}
>>>> +
>>>> +#ifdef CONFIG_DEBUG_FS
>>>> +void drm_suballoc_dump_debug_info(struct drm_suballoc_manager 
>>>> *sa_manager,
>>>> +                  struct drm_printer *p, u64 suballoc_base);
>>>> +#else
>>>> +static inline void
>>>> +drm_suballoc_dump_debug_info(struct drm_suballoc_manager *sa_manager,
>>>> +                 struct drm_printer *p, u64 suballoc_base)
>>>> +{ }
>>>> +
>>>> +#endif
>>>> +
>>>> +#endif /* _DRM_SUBALLOC_H_ */
>>>
>


More information about the dri-devel mailing list