On Mon, Oct 14, 2024 at 07:55:36PM +0000, Matthew Brost wrote: > On Mon, Oct 14, 2024 at 07:32:24PM +0200, Michal Wajdeczko wrote: > > > > > > On 13.10.2024 19:30, Matthew Brost wrote: > > > On Sun, Oct 13, 2024 at 01:55:45PM +0200, Michal Wajdeczko wrote: > > >> > > >> > > >> On 12.10.2024 03:50, Matthew Brost wrote: > > >>> On Wed, Oct 09, 2024 at 07:21:21PM +0200, Michal Wajdeczko wrote: > > >>>> The purpose of the GuC Buffer Cache is to prepare a cached buffer > > >>>> that could be used by some of the CTB based communication actions > > >>>> which require an indirect data to be passed in a separate location > > >>>> than CT message buffer. > > >>>> > > >>>> Signed-off-by: Michal Wajdeczko <michal.wajdeczko@xxxxxxxxx> > > >>> > > >>> Quick reaction without to much thought, this looks like reinventing > > >>> suballocation which we already have in the DRM layer / Xe. > > >>> > > >>> See - xe_sa.c, drm_suballoc.c > > >>> > > >>> So I'd say build this layer on top one of those or ditch this layer > > >>> entirely and directly use xe_sa.c. I'm pretty sure you could allocate > > >>> from the existing pool of tile->mem.kernel_bb_pool as the locking in > > >>> that layers make that safe. Maybe rename 'tile->mem.kernel_bb_pool' to > > >>> something more generic if that works. > > >> > > >> TBH reuse of the xe_sa was my first approach but then I found that every > > >> new GPU sub-allocation actually still allocates some host memory: > > >> > > > > > > I was wondering the purpose of this patch was to remove memory > > > allocations from PF H2G function. This makes more sense what you are > > > trying to do. > > > > > >> struct drm_suballoc * drm_suballoc_new(...) > > >> { > > >> ... > > >> sa = kmalloc(sizeof(*sa), gfp); > > > > > > Can we not just wire GFP_ATOMIC here? I suppose this is failure point > > > albiet a very unlikely one. > > > > > > Another option might be update SA layers to take drm_suballoc on the > > > stack and just initialize it and then after use fini it. Of course this > > > only works if the init / fini are called within a single function. This > > > might be useful in other places / drivers too. > > > > > > I guess all these options need to be weighed against each other. > > > > > > 1. GFP atomic in drm_suballoc_new - easiest but failure point > > > 2. Add drm_suballoc_init / fini - seems like this would work > > > 3. This new layer - works but quite a bit of new code > > > > > > I think I'd personally lean towards start with #1 to get this fixed > > > > #1 still looks like a cheating to me > > > > Agree this is a less than ideal longterm fix but GFP_ATOMIC is used > fairly often in the core MM to avoid reclaim - e.g. dma mapping API > for IOVA allocation quickly comes to mind. > > > > quickly and then post #2 shortly afterwards to see something like this > > > could get accepted. > > > > for #2 likely it's not just drm_suballoc_init|fini but also another set > > of functions at drm_suballoc_manager and xe_sa_manager level to handle > > alternate flow, that would blur original usage, so I'm not sure that > > it's worth investment just to cover a niche GuC scenario > > > > I do think support generic support for sub-allocations in a reclaim safe > manner will likely prove to be useful across the subsystem. It seems > like a use case that can easily arrise. +dri-devel, Sima and Dave. Orig patch: https://lore.kernel.org/intel-xe/20241009172125.1539-2-michal.wajdeczko@xxxxxxxxx/ I do believe this could be useful at the drm_suballoc_manager side. And easier to start from there instead of creating the guc solution one and moving there. And +1 on 'avoiding the i915 traps'. > > > and #3 it's not that large as you may think as series also includes test > > code and there few kernel-doc, and IMO it's more tailored for GuC (as it > > also supports reading data filled by the GuC from that sub-allocation - > > there is VF H2G action that uses indirect buffer in such way) > > > > Agree it is not huge considering the kernel doc + selftest but also this > is more of avoiding the i915 traps we fell into of reinventing things / > handrolling solutions rather using DRM level solutions. > > The use case you mention should be able to built on top DRM / Xe > sub-allocations though, right? > > To be clear, this isn't strong push back on my end to merge what you > have it is more of just making sure we make the right choice here for > longterm health of the driver. > > Let's maybe get a maintainer opinion here as it is ultimately up to them > for things like this. > > Lucas, Rodrigo, Thomas? > > Matt > > > > > > >> so it didn't match my requirement to avoid any memory allocations since > > >> I want to use it while sending H2G with VFs re-provisioning during a > > >> reset - as attempt to resolve issue mentioned in [1] > > >> > > >> [1] > > >> https://lore.kernel.org/intel-xe/3e13401972fd49240f486fd7d47580e576794c78.camel@xxxxxxxxx/ > > >> > > > > > > Thanks for the ref. > > > > > > Matt > > > > > >> > > >>> > > >>> Matt > > >>> > > >>>> --- > > >>>> drivers/gpu/drm/xe/Makefile | 1 + > > >>>> drivers/gpu/drm/xe/xe_guc_buf.c | 387 ++++++++++++++++++++++++++ > > >>>> drivers/gpu/drm/xe/xe_guc_buf.h | 48 ++++ > > >>>> drivers/gpu/drm/xe/xe_guc_buf_types.h | 40 +++ > > >>>> 4 files changed, 476 insertions(+) > > >>>> create mode 100644 drivers/gpu/drm/xe/xe_guc_buf.c > > >>>> create mode 100644 drivers/gpu/drm/xe/xe_guc_buf.h > > >>>> create mode 100644 drivers/gpu/drm/xe/xe_guc_buf_types.h > > >>>> > > >>>> diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile > > >>>> index da80c29aa363..0aed652dc806 100644 > > >>>> --- a/drivers/gpu/drm/xe/Makefile > > >>>> +++ b/drivers/gpu/drm/xe/Makefile > > >>>> @@ -56,6 +56,7 @@ xe-y += xe_bb.o \ > > >>>> xe_gt_topology.o \ > > >>>> xe_guc.o \ > > >>>> xe_guc_ads.o \ > > >>>> + xe_guc_buf.o \ > > >>>> xe_guc_capture.o \ > > >>>> xe_guc_ct.o \ > > >>>> xe_guc_db_mgr.o \ > > >>>> diff --git a/drivers/gpu/drm/xe/xe_guc_buf.c b/drivers/gpu/drm/xe/xe_guc_buf.c > > >>>> new file mode 100644 > > >>>> index 000000000000..a49be711ea86 > > >>>> --- /dev/null > > >>>> +++ b/drivers/gpu/drm/xe/xe_guc_buf.c > > >>>> @@ -0,0 +1,387 @@ > > >>>> +// SPDX-License-Identifier: MIT > > >>>> +/* > > >>>> + * Copyright © 2024 Intel Corporation > > >>>> + */ > > >>>> + > > >>>> +#include <linux/bitmap.h> > > >>>> +#include <linux/cleanup.h> > > >>>> +#include <linux/mutex.h> > > >>>> + > > >>>> +#include <drm/drm_managed.h> > > >>>> + > > >>>> +#include "xe_assert.h" > > >>>> +#include "xe_bo.h" > > >>>> +#include "xe_gt_printk.h" > > >>>> +#include "xe_guc.h" > > >>>> +#include "xe_guc_buf.h" > > >>>> + > > >>>> +/** > > >>>> + * DOC: GuC Buffer Cache > > >>>> + * > > >>>> + * The purpose of the `GuC Buffer Cache`_ is to prepare a cached buffer for use > > >>>> + * by the GuC `CTB based communication` actions that require an indirect data to > > >>>> + * be passed in a separate GPU memory location, that needs to be available only > > >>>> + * during processing of that GuC action. > > >>>> + * > > >>>> + * The xe_guc_buf_cache_init() will allocate and initialize the cache object. > > >>>> + * The object is drm managed and will be allocated with GFP_KERNEL flag. > > >>>> + * The size of the underlying GPU memory buffer will be aligned to SZ_4K. > > >>>> + * The cache will then support up to BITS_PER_LONG a sub-allocations from that > > >>>> + * data buffer. Each sub-allocation will be at least aligned to SZ_64. > > >>>> + * > > >>>> + * :: > > >>>> + * > > >>>> + * <------> chunk (n * 64) > > >>>> + * <------------- CPU mirror (n * 4K) --------------------------------> > > >>>> + * +--------+--------+--------+--------+-----------------------+--------+ > > >>>> + * | 0 | 1 | 2 | 3 | | m | > > >>>> + * +--------+--------+--------+--------+-----------------------+--------+ > > >>>> + * || /\ > > >>>> + * flush || > > >>>> + * || sync > > >>>> + * \/ || > > >>>> + * +--------+--------+--------+--------+-----------------------+--------+ > > >>>> + * | 0 | 1 | 2 | 3 | | m | > > >>>> + * +--------+--------+--------+--------+-----------------------+--------+ > > >>>> + * <--------- GPU allocation (n * 4K) --------------------------------> > > >>>> + * <------> chunk (n * 64) > > >>>> + * > > >>>> + * The xe_guc_buf_reserve() will return a reference to a new sub-allocation. > > >>>> + * The xe_guc_buf_release() shall be used to release a such sub-allocation. > > >>>> + * > > >>>> + * The xe_guc_buf_cpu_ptr() will provide access to the sub-allocation. > > >>>> + * The xe_guc_buf_flush() shall be used to flush data from any mirror buffer to > > >>>> + * the underlying GPU memory. > > >>>> + * > > >>>> + * The xe_guc_buf_gpu_addr() will provide a GPU address of the sub-allocation. > > >>>> + * The xe_guc_buf_sync() might be used to copy the content of the sub-allocation > > >>>> + * from the GPU memory to the local mirror buffer. > > >>>> + */ > > >>>> + > > >>>> +static struct xe_guc *cache_to_guc(struct xe_guc_buf_cache *cache) > > >>>> +{ > > >>>> + return cache->guc; > > >>>> +} > > >>>> + > > >>>> +static struct xe_gt *cache_to_gt(struct xe_guc_buf_cache *cache) > > >>>> +{ > > >>>> + return guc_to_gt(cache_to_guc(cache)); > > >>>> +} > > >>>> + > > >>>> +static struct xe_device *cache_to_xe(struct xe_guc_buf_cache *cache) > > >>>> +{ > > >>>> + return gt_to_xe(cache_to_gt(cache)); > > >>>> +} > > >>>> + > > >>>> +static struct mutex *cache_mutex(struct xe_guc_buf_cache *cache) > > >>>> +{ > > >>>> + return &cache_to_guc(cache)->ct.lock; > > >>>> +} > > >>>> + > > >>>> +static void __fini_cache(void *arg) > > >>>> +{ > > >>>> + struct xe_guc_buf_cache *cache = arg; > > >>>> + struct xe_gt *gt = cache_to_gt(cache); > > >>>> + > > >>>> + if (cache->used) > > >>>> + xe_gt_dbg(gt, "buffer cache unclean: %#lx = %u * %u bytes\n", > > >>>> + cache->used, bitmap_weight(&cache->used, BITS_PER_LONG), cache->chunk); > > >>>> + > > >>>> + kvfree(cache->mirror); > > >>>> + cache->mirror = NULL; > > >>>> + cache->bo = NULL; > > >>>> + cache->used = 0; > > >>>> +} > > >>>> + > > >>>> +/** > > >>>> + * xe_guc_buf_cache_init() - Allocate and initialize a GuC Buffer Cache. > > >>>> + * @guc: the &xe_guc where this cache will be used > > >>>> + * @size: minimum size of the cache > > >>>> + * > > >>>> + * See `GuC Buffer Cache`_ for details. > > >>>> + * > > >>>> + * Return: pointer to the &xe_guc_buf_cache on success or a ERR_PTR() on failure. > > >>>> + */ > > >>>> +struct xe_guc_buf_cache *xe_guc_buf_cache_init(struct xe_guc *guc, u32 size) > > >>>> +{ > > >>>> + struct xe_gt *gt = guc_to_gt(guc); > > >>>> + struct xe_tile *tile = gt_to_tile(gt); > > >>>> + struct xe_device *xe = tile_to_xe(tile); > > >>>> + struct xe_guc_buf_cache *cache; > > >>>> + u32 chunk_size; > > >>>> + u32 cache_size; > > >>>> + int ret; > > >>>> + > > >>>> + cache_size = ALIGN(size, SZ_4K); > > >>>> + chunk_size = cache_size / BITS_PER_LONG; > > >>>> + > > >>>> + xe_gt_assert(gt, size); > > >>>> + xe_gt_assert(gt, IS_ALIGNED(chunk_size, SZ_64)); > > >>>> + > > >>>> + cache = drmm_kzalloc(&xe->drm, sizeof(*cache), GFP_KERNEL); > > >>>> + if (!cache) > > >>>> + return ERR_PTR(-ENOMEM); > > >>>> + > > >>>> + cache->bo = xe_managed_bo_create_pin_map(xe, tile, cache_size, > > >>>> + XE_BO_FLAG_VRAM_IF_DGFX(tile) | > > >>>> + XE_BO_FLAG_GGTT | > > >>>> + XE_BO_FLAG_GGTT_INVALIDATE); > > >>>> + if (IS_ERR(cache->bo)) > > >>>> + return ERR_CAST(cache->bo); > > >>>> + > > >>>> + cache->guc = guc; > > >>>> + cache->chunk = chunk_size; > > >>>> + cache->mirror = kvzalloc(cache_size, GFP_KERNEL); > > >>>> + if (!cache->mirror) > > >>>> + return ERR_PTR(-ENOMEM); > > >>>> + > > >>>> + ret = devm_add_action_or_reset(xe->drm.dev, __fini_cache, cache); > > >>>> + if (ret) > > >>>> + return ERR_PTR(ret); > > >>>> + > > >>>> + xe_gt_dbg(gt, "buffer cache at %#x (%uKiB = %u x %zu dwords) for %ps\n", > > >>>> + xe_bo_ggtt_addr(cache->bo), cache_size / SZ_1K, > > >>>> + BITS_PER_LONG, chunk_size / sizeof(u32), __builtin_return_address(0)); > > >>>> + return cache; > > >>>> +} > > >>>> + > > >>>> +static bool cache_is_ref_active(struct xe_guc_buf_cache *cache, unsigned long ref) > > >>>> +{ > > >>>> + lockdep_assert_held(cache_mutex(cache)); > > >>>> + return bitmap_subset(&ref, &cache->used, BITS_PER_LONG); > > >>>> +} > > >>>> + > > >>>> +static bool ref_is_valid(unsigned long ref) > > >>>> +{ > > >>>> + return ref && find_next_bit(&ref, BITS_PER_LONG, > > >>>> + find_first_bit(&ref, BITS_PER_LONG) + > > >>>> + bitmap_weight(&ref, BITS_PER_LONG)) == BITS_PER_LONG; > > >>>> +} > > >>>> + > > >>>> +static void cache_assert_ref(struct xe_guc_buf_cache *cache, unsigned long ref) > > >>>> +{ > > >>>> + xe_gt_assert_msg(cache_to_gt(cache), ref_is_valid(ref), > > >>>> + "# malformed ref %#lx %*pbl", ref, (int)BITS_PER_LONG, &ref); > > >>>> + xe_gt_assert_msg(cache_to_gt(cache), cache_is_ref_active(cache, ref), > > >>>> + "# stale ref %#lx %*pbl vs used %#lx %*pbl", > > >>>> + ref, (int)BITS_PER_LONG, &ref, > > >>>> + cache->used, (int)BITS_PER_LONG, &cache->used); > > >>>> +} > > >>>> + > > >>>> +static unsigned long cache_reserve(struct xe_guc_buf_cache *cache, u32 size) > > >>>> +{ > > >>>> + unsigned long index; > > >>>> + unsigned int nbits; > > >>>> + > > >>>> + lockdep_assert_held(cache_mutex(cache)); > > >>>> + xe_gt_assert(cache_to_gt(cache), size); > > >>>> + xe_gt_assert(cache_to_gt(cache), size <= BITS_PER_LONG * cache->chunk); > > >>>> + > > >>>> + nbits = DIV_ROUND_UP(size, cache->chunk); > > >>>> + index = bitmap_find_next_zero_area(&cache->used, BITS_PER_LONG, 0, nbits, 0); > > >>>> + if (index >= BITS_PER_LONG) { > > >>>> + xe_gt_dbg(cache_to_gt(cache), "no space for %u byte%s in cache at %#x used %*pbl\n", > > >>>> + size, str_plural(size), xe_bo_ggtt_addr(cache->bo), > > >>>> + (int)BITS_PER_LONG, &cache->used); > > >>>> + return 0; > > >>>> + } > > >>>> + > > >>>> + bitmap_set(&cache->used, index, nbits); > > >>>> + > > >>>> + return GENMASK(index + nbits - 1, index); > > >>>> +} > > >>>> + > > >>>> +static u64 cache_ref_offset(struct xe_guc_buf_cache *cache, unsigned long ref) > > >>>> +{ > > >>>> + cache_assert_ref(cache, ref); > > >>>> + return __ffs(ref) * cache->chunk; > > >>>> +} > > >>>> + > > >>>> +static u32 cache_ref_size(struct xe_guc_buf_cache *cache, unsigned long ref) > > >>>> +{ > > >>>> + cache_assert_ref(cache, ref); > > >>>> + return hweight_long(ref) * cache->chunk; > > >>>> +} > > >>>> + > > >>>> +static u64 cache_ref_gpu_addr(struct xe_guc_buf_cache *cache, unsigned long ref) > > >>>> +{ > > >>>> + return xe_bo_ggtt_addr(cache->bo) + cache_ref_offset(cache, ref); > > >>>> +} > > >>>> + > > >>>> +static void *cache_ref_cpu_ptr(struct xe_guc_buf_cache *cache, unsigned long ref) > > >>>> +{ > > >>>> + return cache->mirror + cache_ref_offset(cache, ref); > > >>>> +} > > >>>> + > > >>>> +/** > > >>>> + * xe_guc_buf_reserve() - Reserve a new sub-allocation. > > >>>> + * @cache: the &xe_guc_buf_cache where reserve sub-allocation > > >>>> + * @size: the requested size of the buffer > > >>>> + * > > >>>> + * Use xe_guc_buf_is_valid() to check if returned buffer reference is valid. > > >>>> + * Must use xe_guc_buf_release() to release a sub-allocation. > > >>>> + * > > >>>> + * Return: a &xe_guc_buf of new sub-allocation. > > >>>> + */ > > >>>> +struct xe_guc_buf xe_guc_buf_reserve(struct xe_guc_buf_cache *cache, u32 size) > > >>>> +{ > > >>>> + guard(mutex)(cache_mutex(cache)); > > >>>> + unsigned long ref; > > >>>> + > > >>>> + ref = cache_reserve(cache, size); > > >>>> + > > >>>> + return (struct xe_guc_buf){ .cache = cache, .ref = ref }; > > >>>> +} > > >>>> + > > >>>> +/** > > >>>> + * xe_guc_buf_from_data() - Reserve a new sub-allocation using data. > > >>>> + * @cache: the &xe_guc_buf_cache where reserve sub-allocation > > >>>> + * @data: the data to flush the sub-allocation > > >>>> + * @size: the size of the data > > >>>> + * > > >>>> + * Similar to xe_guc_buf_reserve() but flushes @data to the GPU memory. > > >>>> + * > > >>>> + * Return: a &xe_guc_buf of new sub-allocation. > > >>>> + */ > > >>>> +struct xe_guc_buf xe_guc_buf_from_data(struct xe_guc_buf_cache *cache, > > >>>> + const void *data, size_t size) > > >>>> +{ > > >>>> + guard(mutex)(cache_mutex(cache)); > > >>>> + unsigned long ref; > > >>>> + > > >>>> + ref = cache_reserve(cache, size); > > >>>> + if (ref) { > > >>>> + u32 offset = cache_ref_offset(cache, ref); > > >>>> + > > >>>> + xe_map_memcpy_to(cache_to_xe(cache), &cache->bo->vmap, > > >>>> + offset, data, size); > > >>>> + } > > >>>> + > > >>>> + return (struct xe_guc_buf){ .cache = cache, .ref = ref }; > > >>>> +} > > >>>> + > > >>>> +static void cache_release_ref(struct xe_guc_buf_cache *cache, unsigned long ref) > > >>>> +{ > > >>>> + cache_assert_ref(cache, ref); > > >>>> + cache->used &= ~ref; > > >>>> +} > > >>>> + > > >>>> +/** > > >>>> + * xe_guc_buf_release() - Release a sub-allocation. > > >>>> + * @buf: the &xe_guc_buf to release > > >>>> + * > > >>>> + * Releases a sub-allocation reserved by xe_guc_buf_reserve(). > > >>>> + */ > > >>>> +void xe_guc_buf_release(const struct xe_guc_buf buf) > > >>>> +{ > > >>>> + guard(mutex)(cache_mutex(buf.cache)); > > >>>> + > > >>>> + if (!buf.ref) > > >>>> + return; > > >>>> + > > >>>> + cache_release_ref(buf.cache, buf.ref); > > >>>> +} > > >>>> + > > >>>> +static u64 cache_flush_ref(struct xe_guc_buf_cache *cache, unsigned long ref) > > >>>> +{ > > >>>> + u32 offset = cache_ref_offset(cache, ref); > > >>>> + u32 size = cache_ref_size(cache, ref); > > >>>> + > > >>>> + xe_map_memcpy_to(cache_to_xe(cache), &cache->bo->vmap, > > >>>> + offset, cache->mirror + offset, size); > > >>>> + > > >>>> + return cache_ref_gpu_addr(cache, ref); > > >>>> +} > > >>>> + > > >>>> +/** > > >>>> + * xe_guc_buf_flush() - Copy the data from the sub-allocation to the GPU memory. > > >>>> + * @buf: the &xe_guc_buf to flush > > >>>> + * > > >>>> + * Return: a GPU address of the sub-allocation. > > >>>> + */ > > >>>> +u64 xe_guc_buf_flush(const struct xe_guc_buf buf) > > >>>> +{ > > >>>> + guard(mutex)(cache_mutex(buf.cache)); > > >>>> + > > >>>> + return cache_flush_ref(buf.cache, buf.ref); > > >>>> +} > > >>>> + > > >>>> +static void *cache_sync_ref(struct xe_guc_buf_cache *cache, unsigned long ref) > > >>>> +{ > > >>>> + u32 offset = cache_ref_offset(cache, ref); > > >>>> + u32 size = cache_ref_size(cache, ref); > > >>>> + > > >>>> + xe_map_memcpy_from(cache_to_xe(cache), cache->mirror + offset, > > >>>> + &cache->bo->vmap, offset, size); > > >>>> + > > >>>> + return cache_ref_cpu_ptr(cache, ref); > > >>>> +} > > >>>> + > > >>>> +/** > > >>>> + * xe_guc_buf_sync() - Copy the data from the GPU memory to the sub-allocation. > > >>>> + * @buf: the &xe_guc_buf to sync > > >>>> + * > > >>>> + * Return: the CPU pointer to the sub-allocation. > > >>>> + */ > > >>>> +void *xe_guc_buf_sync(const struct xe_guc_buf buf) > > >>>> +{ > > >>>> + guard(mutex)(cache_mutex(buf.cache)); > > >>>> + > > >>>> + return cache_sync_ref(buf.cache, buf.ref); > > >>>> +} > > >>>> + > > >>>> +/** > > >>>> + * xe_guc_buf_cpu_ptr() - Obtain a CPU pointer to the sub-allocation. > > >>>> + * @buf: the &xe_guc_buf to query > > >>>> + * > > >>>> + * Return: the CPU pointer of the sub-allocation. > > >>>> + */ > > >>>> +void *xe_guc_buf_cpu_ptr(const struct xe_guc_buf buf) > > >>>> +{ > > >>>> + guard(mutex)(cache_mutex(buf.cache)); > > >>>> + > > >>>> + return cache_ref_cpu_ptr(buf.cache, buf.ref); > > >>>> +} > > >>>> + > > >>>> +/** > > >>>> + * xe_guc_buf_gpu_addr() - Obtain a GPU address of the sub-allocation. > > >>>> + * @buf: the &xe_guc_buf to query > > >>>> + * > > >>>> + * Return: the GPU address of the sub-allocation. > > >>>> + */ > > >>>> +u64 xe_guc_buf_gpu_addr(const struct xe_guc_buf buf) > > >>>> +{ > > >>>> + guard(mutex)(cache_mutex(buf.cache)); > > >>>> + > > >>>> + return cache_ref_gpu_addr(buf.cache, buf.ref); > > >>>> +} > > >>>> + > > >>>> +/** > > >>>> + * xe_guc_cache_gpu_addr_from_ptr() - Lookup a GPU address using the pointer. > > >>>> + * @cache: the &xe_guc_buf_cache with sub-allocations > > >>>> + * @ptr: the CPU pointer to the data from a sub-allocation > > >>>> + * @size: the size of the data at @ptr > > >>>> + * > > >>>> + * Return: the GPU address on success or 0 on failure. > > >>>> + */ > > >>>> +u64 xe_guc_cache_gpu_addr_from_ptr(struct xe_guc_buf_cache *cache, const void *ptr, u32 size) > > >>>> +{ > > >>>> + guard(mutex)(cache_mutex(cache)); > > >>>> + ptrdiff_t offset = ptr - cache->mirror; > > >>>> + unsigned long ref; > > >>>> + int first, last; > > >>>> + > > >>>> + if (offset < 0) > > >>>> + return 0; > > >>>> + > > >>>> + first = div_u64(offset, cache->chunk); > > >>>> + last = DIV_ROUND_UP(offset + max(1, size), cache->chunk) - 1; > > >>>> + > > >>>> + if (last >= BITS_PER_LONG) > > >>>> + return 0; > > >>>> + > > >>>> + ref = GENMASK(last, first); > > >>>> + cache_assert_ref(cache, ref); > > >>>> + > > >>>> + return xe_bo_ggtt_addr(cache->bo) + offset; > > >>>> +} > > >>>> diff --git a/drivers/gpu/drm/xe/xe_guc_buf.h b/drivers/gpu/drm/xe/xe_guc_buf.h > > >>>> new file mode 100644 > > >>>> index 000000000000..700e7b06c149 > > >>>> --- /dev/null > > >>>> +++ b/drivers/gpu/drm/xe/xe_guc_buf.h > > >>>> @@ -0,0 +1,48 @@ > > >>>> +/* SPDX-License-Identifier: MIT */ > > >>>> +/* > > >>>> + * Copyright © 2024 Intel Corporation > > >>>> + */ > > >>>> + > > >>>> +#ifndef _XE_GUC_BUF_H_ > > >>>> +#define _XE_GUC_BUF_H_ > > >>>> + > > >>>> +#include <linux/cleanup.h> > > >>>> + > > >>>> +#include "xe_guc_buf_types.h" > > >>>> + > > >>>> +struct xe_guc_buf_cache *xe_guc_buf_cache_init(struct xe_guc *guc, u32 size); > > >>>> + > > >>>> +struct xe_guc_buf xe_guc_buf_reserve(struct xe_guc_buf_cache *cache, u32 size); > > >>>> +struct xe_guc_buf xe_guc_buf_from_data(struct xe_guc_buf_cache *cache, > > >>>> + const void *data, size_t size); > > >>>> +void xe_guc_buf_release(const struct xe_guc_buf buf); > > >>>> + > > >>>> +/** > > >>>> + * xe_guc_buf_is_valid() - Check if the GuC Buffer Cache sub-allocation is valid. > > >>>> + * @buf: the &xe_guc_buf reference to check > > >>>> + * > > >>>> + * Return: true if @buf represents a valid sub-allocation. > > >>>> + */ > > >>>> +static inline bool xe_guc_buf_is_valid(const struct xe_guc_buf buf) > > >>>> +{ > > >>>> + return buf.ref; > > >>>> +} > > >>>> + > > >>>> +void *xe_guc_buf_sync(const struct xe_guc_buf buf); > > >>>> +void *xe_guc_buf_cpu_ptr(const struct xe_guc_buf buf); > > >>>> +u64 xe_guc_buf_flush(const struct xe_guc_buf buf); > > >>>> +u64 xe_guc_buf_gpu_addr(const struct xe_guc_buf buf); > > >>>> + > > >>>> +u64 xe_guc_cache_gpu_addr_from_ptr(struct xe_guc_buf_cache *cache, const void *ptr, u32 size); > > >>>> + > > >>>> +DEFINE_CLASS(xe_guc_buf, struct xe_guc_buf, > > >>>> + xe_guc_buf_release(_T), > > >>>> + xe_guc_buf_reserve(cache, size), > > >>>> + struct xe_guc_buf_cache *cache, u32 size); > > >>>> + > > >>>> +DEFINE_CLASS(xe_guc_buf_from_data, struct xe_guc_buf, > > >>>> + xe_guc_buf_release(_T), > > >>>> + xe_guc_buf_from_data(cache, data, size), > > >>>> + struct xe_guc_buf_cache *cache, const void *data, u32 size); > > >>>> + > > >>>> +#endif > > >>>> diff --git a/drivers/gpu/drm/xe/xe_guc_buf_types.h b/drivers/gpu/drm/xe/xe_guc_buf_types.h > > >>>> new file mode 100644 > > >>>> index 000000000000..fe93b32e97f8 > > >>>> --- /dev/null > > >>>> +++ b/drivers/gpu/drm/xe/xe_guc_buf_types.h > > >>>> @@ -0,0 +1,40 @@ > > >>>> +/* SPDX-License-Identifier: MIT */ > > >>>> +/* > > >>>> + * Copyright © 2024 Intel Corporation > > >>>> + */ > > >>>> + > > >>>> +#ifndef _XE_GUC_BUF_TYPES_H_ > > >>>> +#define _XE_GUC_BUF_TYPES_H_ > > >>>> + > > >>>> +#include <linux/types.h> > > >>>> + > > >>>> +struct xe_bo; > > >>>> +struct xe_guc; > > >>>> + > > >>>> +/** > > >>>> + * struct xe_guc_buf_cache - GuC Data Buffer Cache. > > >>>> + */ > > >>>> +struct xe_guc_buf_cache { > > >>>> + /** @guc: the parent GuC where buffers are used */ > > >>>> + struct xe_guc *guc; > > >>>> + /** @bo: the main cache buffer object with GPU allocation */ > > >>>> + struct xe_bo *bo; > > >>>> + /** @mirror: the CPU pointer to the data buffer */ > > >>>> + void *mirror; > > >>>> + /** @used: the bitmap used to track allocated chunks */ > > >>>> + unsigned long used; > > >>>> + /** @chunk: the size of the smallest sub-allocation */ > > >>>> + u32 chunk; > > >>>> +}; > > >>>> + > > >>>> +/** > > >>>> + * struct xe_guc_buf - GuC Data Buffer Reference. > > >>>> + */ > > >>>> +struct xe_guc_buf { > > >>>> + /** @cache: the cache where this allocation belongs */ > > >>>> + struct xe_guc_buf_cache *cache; > > >>>> + /** @ref: the internal reference */ > > >>>> + unsigned long ref; > > >>>> +}; > > >>>> + > > >>>> +#endif > > >>>> -- > > >>>> 2.43.0 > > >>>> > > >> > >