root/sys/dev/pci/drm/i915/i915_vma_resource.h
/* SPDX-License-Identifier: MIT */
/*
 * Copyright © 2021 Intel Corporation
 */

#ifndef __I915_VMA_RESOURCE_H__
#define __I915_VMA_RESOURCE_H__

#include <linux/dma-fence.h>
#include <linux/refcount.h>

#include "i915_gem.h"
#include "i915_scatterlist.h"
#include "i915_sw_fence.h"
#include "intel_runtime_pm.h"

struct intel_memory_region;

struct i915_page_sizes {
        /**
         * The sg mask of the pages sg_table. i.e the mask of
         * the lengths for each sg entry.
         */
        unsigned int phys;

        /**
         * The gtt page sizes we are allowed to use given the
         * sg mask and the supported page sizes. This will
         * express the smallest unit we can use for the whole
         * object, as well as the larger sizes we may be able
         * to use opportunistically.
         */
        unsigned int sg;
};

/**
 * struct i915_vma_bindinfo - Information needed for async bind
 * only but that can be dropped after the bind has taken place.
 * Consider making this a separate argument to the bind_vma
 * op, coalescing with other arguments like vm, stash, cache_level
 * and flags
 * @pages: The pages sg-table.
 * @page_sizes: Page sizes of the pages.
 * @pages_rsgt: Refcounted sg-table when delayed object destruction
 * is supported. May be NULL.
 * @readonly: Whether the vma should be bound read-only.
 * @lmem: Whether the vma points to lmem.
 */
struct i915_vma_bindinfo {
        struct sg_table *pages;
        struct i915_page_sizes page_sizes;
        struct i915_refct_sgt *pages_rsgt;
        bool readonly:1;
        bool lmem:1;
};

/**
 * struct i915_vma_resource - Snapshotted unbind information.
 * @unbind_fence: Fence to mark unbinding complete. Note that this fence
 * is not considered published until unbind is scheduled, and as such it
 * is illegal to access this fence before scheduled unbind other than
 * for refcounting.
 * @lock: The @unbind_fence lock.
 * @hold_count: Number of holders blocking the fence from finishing.
 * The vma itself is keeping a hold, which is released when unbind
 * is scheduled.
 * @work: Work struct for deferred unbind work.
 * @chain: Pointer to struct i915_sw_fence used to await dependencies.
 * @rb: Rb node for the vm's pending unbind interval tree.
 * @__subtree_last: Interval tree private member.
 * @wakeref: wakeref.
 * @vm: non-refcounted pointer to the vm. This is for internal use only and
 * this member is cleared after vm_resource unbind.
 * @mr: The memory region of the object pointed to by the vma.
 * @ops: Pointer to the backend i915_vma_ops.
 * @private: Bind backend private info.
 * @start: Offset into the address space of bind range start. Note that
 * this is after any padding that might have been allocated.
 * @node_size: Size of the allocated range manager node with padding
 * subtracted.
 * @vma_size: Bind size.
 * @guard: The size of guard area preceding and trailing the bind.
 * @page_sizes_gtt: Resulting page sizes from the bind operation.
 * @bound_flags: Flags indicating binding status.
 * @allocated: Backend private data. TODO: Should move into @private.
 * @immediate_unbind: Unbind can be done immediately and doesn't need to be
 * deferred to a work item awaiting unsignaled fences. This is a hack.
 * (dma_fence_work uses a fence flag for this, but this seems slightly
 * cleaner).
 * @needs_wakeref: Whether a wakeref is needed during unbind. Since we can't
 * take a wakeref in the dma-fence signalling critical path, it needs to be
 * taken when the unbind is scheduled.
 * @skip_pte_rewrite: During ggtt suspend and vm takedown pte rewriting
 * needs to be skipped for unbind.
 * @tlb: pointer for obj->mm.tlb, if async unbind. Otherwise, NULL
 *
 * The lifetime of a struct i915_vma_resource is from a binding request to
 * the actual possible asynchronous unbind has completed.
 */
struct i915_vma_resource {
        struct dma_fence unbind_fence;
        /* See above for description of the lock. */
        spinlock_t lock;
        refcount_t hold_count;
        struct work_struct work;
        struct i915_sw_fence chain;
        struct rb_node rb;
        u64 __subtree_last;
        struct i915_address_space *vm;
        intel_wakeref_t wakeref;

        /**
         * @bi: Information needed for async bind only but that can be dropped
         * after the bind has taken place.
         *
         * Consider making this a separate argument to the bind_vma op,
         * coalescing with other arguments like vm, stash, cache_level and flags
         */
        struct i915_vma_bindinfo bi;

#if IS_ENABLED(CONFIG_DRM_I915_CAPTURE_ERROR)
        struct intel_memory_region *mr;
#endif
        const struct i915_vma_ops *ops;
        void *private;
        u64 start;
        u64 node_size;
        u64 vma_size;
        u32 guard;
        u32 page_sizes_gtt;

        u32 bound_flags;
        bool allocated:1;
        bool immediate_unbind:1;
        bool needs_wakeref:1;
        bool skip_pte_rewrite:1;

        u32 *tlb;
};

bool i915_vma_resource_hold(struct i915_vma_resource *vma_res,
                            bool *lockdep_cookie);

void i915_vma_resource_unhold(struct i915_vma_resource *vma_res,
                              bool lockdep_cookie);

struct i915_vma_resource *i915_vma_resource_alloc(void);

void i915_vma_resource_free(struct i915_vma_resource *vma_res);

struct dma_fence *i915_vma_resource_unbind(struct i915_vma_resource *vma_res,
                                           u32 *tlb);

void __i915_vma_resource_init(struct i915_vma_resource *vma_res);

/**
 * i915_vma_resource_get - Take a reference on a vma resource
 * @vma_res: The vma resource on which to take a reference.
 *
 * Return: The @vma_res pointer
 */
static inline struct i915_vma_resource
*i915_vma_resource_get(struct i915_vma_resource *vma_res)
{
        dma_fence_get(&vma_res->unbind_fence);
        return vma_res;
}

/**
 * i915_vma_resource_put - Release a reference to a struct i915_vma_resource
 * @vma_res: The resource
 */
static inline void i915_vma_resource_put(struct i915_vma_resource *vma_res)
{
        dma_fence_put(&vma_res->unbind_fence);
}

/**
 * i915_vma_resource_init - Initialize a vma resource.
 * @vma_res: The vma resource to initialize
 * @vm: Pointer to the vm.
 * @pages: The pages sg-table.
 * @page_sizes: Page sizes of the pages.
 * @pages_rsgt: Pointer to a struct i915_refct_sgt of an object with
 * delayed destruction.
 * @readonly: Whether the vma should be bound read-only.
 * @lmem: Whether the vma points to lmem.
 * @mr: The memory region of the object the vma points to.
 * @ops: The backend ops.
 * @private: Bind backend private info.
 * @start: Offset into the address space of bind range start after padding.
 * @node_size: Size of the allocated range manager node minus padding.
 * @size: Bind size.
 * @guard: The size of the guard area preceding and trailing the bind.
 *
 * Initializes a vma resource allocated using i915_vma_resource_alloc().
 * The reason for having separate allocate and initialize function is that
 * initialization may need to be performed from under a lock where
 * allocation is not allowed.
 */
static inline void i915_vma_resource_init(struct i915_vma_resource *vma_res,
                                          struct i915_address_space *vm,
                                          struct sg_table *pages,
                                          const struct i915_page_sizes *page_sizes,
                                          struct i915_refct_sgt *pages_rsgt,
                                          bool readonly,
                                          bool lmem,
                                          struct intel_memory_region *mr,
                                          const struct i915_vma_ops *ops,
                                          void *private,
                                          u64 start,
                                          u64 node_size,
                                          u64 size,
                                          u32 guard)
{
        __i915_vma_resource_init(vma_res);
        vma_res->vm = vm;
        vma_res->bi.pages = pages;
        vma_res->bi.page_sizes = *page_sizes;
        if (pages_rsgt)
                vma_res->bi.pages_rsgt = i915_refct_sgt_get(pages_rsgt);
        vma_res->bi.readonly = readonly;
        vma_res->bi.lmem = lmem;
#if IS_ENABLED(CONFIG_DRM_I915_CAPTURE_ERROR)
        vma_res->mr = mr;
#endif
        vma_res->ops = ops;
        vma_res->private = private;
        vma_res->start = start;
        vma_res->node_size = node_size;
        vma_res->vma_size = size;
        vma_res->guard = guard;
}

static inline void i915_vma_resource_fini(struct i915_vma_resource *vma_res)
{
        GEM_BUG_ON(refcount_read(&vma_res->hold_count) != 1);
        if (vma_res->bi.pages_rsgt)
                i915_refct_sgt_put(vma_res->bi.pages_rsgt);
        i915_sw_fence_fini(&vma_res->chain);
}

int i915_vma_resource_bind_dep_sync(struct i915_address_space *vm,
                                    u64 first,
                                    u64 last,
                                    bool intr);

int i915_vma_resource_bind_dep_await(struct i915_address_space *vm,
                                     struct i915_sw_fence *sw_fence,
                                     u64 first,
                                     u64 last,
                                     bool intr,
                                     gfp_t gfp);

void i915_vma_resource_bind_dep_sync_all(struct i915_address_space *vm);

void i915_vma_resource_module_exit(void);

int i915_vma_resource_module_init(void);

#endif