summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/xe
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/xe')
-rw-r--r--drivers/gpu/drm/xe/instructions/xe_gpu_commands.h1
-rw-r--r--drivers/gpu/drm/xe/xe_i2c.c3
-rw-r--r--drivers/gpu/drm/xe/xe_mmio_gem.c108
-rw-r--r--drivers/gpu/drm/xe/xe_mmio_gem.h2
-rw-r--r--drivers/gpu/drm/xe/xe_pagefault.c10
-rw-r--r--drivers/gpu/drm/xe/xe_pagefault_types.h3
-rw-r--r--drivers/gpu/drm/xe/xe_ring_ops.c11
-rw-r--r--drivers/gpu/drm/xe/xe_shrinker.c124
8 files changed, 170 insertions, 92 deletions
diff --git a/drivers/gpu/drm/xe/instructions/xe_gpu_commands.h b/drivers/gpu/drm/xe/instructions/xe_gpu_commands.h
index 18d0fde8c98f..faf8d7e2c5c1 100644
--- a/drivers/gpu/drm/xe/instructions/xe_gpu_commands.h
+++ b/drivers/gpu/drm/xe/instructions/xe_gpu_commands.h
@@ -46,6 +46,7 @@
#define GFX_OP_PIPE_CONTROL(len) ((0x3<<29)|(0x3<<27)|(0x2<<24)|((len)-2))
#define PIPE_CONTROL0_QUEUE_DRAIN_MODE BIT(12)
+#define PIPE_CONTROL0_UNTYPED_DATAPORT_CACHE_FLUSH BIT(11) /* gen12 */
#define PIPE_CONTROL0_L3_READ_ONLY_CACHE_INVALIDATE BIT(10) /* gen12 */
#define PIPE_CONTROL0_HDC_PIPELINE_FLUSH BIT(9) /* gen12 */
diff --git a/drivers/gpu/drm/xe/xe_i2c.c b/drivers/gpu/drm/xe/xe_i2c.c
index 399099ff0dbc..f4f381988289 100644
--- a/drivers/gpu/drm/xe/xe_i2c.c
+++ b/drivers/gpu/drm/xe/xe_i2c.c
@@ -318,8 +318,10 @@ void xe_i2c_pm_resume(struct xe_device *xe, bool d3cold)
static void xe_i2c_remove(void *data)
{
struct xe_i2c *i2c = data;
+ struct xe_device *xe = tile_to_xe(i2c->mmio->tile);
unsigned int i;
+ xe_i2c_irq_reset(xe);
xe_amc_exit(i2c);
for (i = 0; i < XE_I2C_MAX_CLIENTS; i++) {
@@ -329,6 +331,7 @@ static void xe_i2c_remove(void *data)
bus_unregister_notifier(&i2c_bus_type, &i2c->bus_notifier);
xe_i2c_unregister_adapter(i2c);
+ xe->i2c = NULL;
}
/**
diff --git a/drivers/gpu/drm/xe/xe_mmio_gem.c b/drivers/gpu/drm/xe/xe_mmio_gem.c
index 3741ae60f532..5ffe03d36190 100644
--- a/drivers/gpu/drm/xe/xe_mmio_gem.c
+++ b/drivers/gpu/drm/xe/xe_mmio_gem.c
@@ -5,9 +5,9 @@
#include "xe_mmio_gem.h"
+#include <linux/dma-resv.h>
#include <drm/drm_drv.h>
#include <drm/drm_gem.h>
-#include <drm/drm_managed.h>
#include "xe_device_types.h"
@@ -37,12 +37,24 @@ static vm_fault_t xe_mmio_gem_vm_fault(struct vm_fault *);
struct xe_mmio_gem {
struct drm_gem_object base;
phys_addr_t phys_addr;
+ struct page *dummy_page; /* protected by the GEM's dma_resv */
+ bool destroyed; /* protected by the GEM's dma_resv */
};
+static int xe_mmio_gem_vm_may_split(struct vm_area_struct *area, unsigned long addr)
+{
+ /*
+ * Forbid splitting. Together with VM_DONTEXPAND, this keeps the VMA
+ * matching the GEM object exactly.
+ */
+ return -EINVAL;
+}
+
static const struct vm_operations_struct vm_ops = {
.open = drm_gem_vm_open,
.close = drm_gem_vm_close,
.fault = xe_mmio_gem_vm_fault,
+ .may_split = xe_mmio_gem_vm_may_split,
};
static const struct drm_gem_object_funcs xe_mmio_gem_funcs = {
@@ -121,6 +133,8 @@ static void xe_mmio_gem_free(struct drm_gem_object *base)
{
struct xe_mmio_gem *obj = to_xe_mmio_gem(base);
+ if (obj->dummy_page)
+ __free_page(obj->dummy_page);
drm_gem_object_release(base);
kfree(obj);
}
@@ -128,15 +142,31 @@ static void xe_mmio_gem_free(struct drm_gem_object *base)
/**
* xe_mmio_gem_destroy - Destroy the GEM object that exposes an MMIO region
* @gem: the GEM object to destroy
+ * @file: DRM file descriptor previously passed to xe_mmio_gem_create()
*
* This function releases resources associated with the GEM object created by
* xe_mmio_gem_create().
*
* See: "Exposing MMIO regions to userspace"
*/
-void xe_mmio_gem_destroy(struct xe_mmio_gem *gem)
+void xe_mmio_gem_destroy(struct xe_mmio_gem *gem, struct drm_file *file)
{
- xe_mmio_gem_free(&gem->base);
+ struct drm_gem_object *base = &gem->base;
+ struct drm_device *dev = base->dev;
+
+ drm_vma_node_revoke(&base->vma_node, file);
+
+ dma_resv_lock(base->resv, NULL);
+ gem->destroyed = true;
+ dma_resv_unlock(base->resv);
+ /*
+ * Setting 'destroyed' under lock takes care of the subsequent faults.
+ * Zap the existing PTEs to cut off access to the real MMIO through
+ * currently mapped pages.
+ */
+ drm_vma_node_unmap(&base->vma_node, dev->anon_inode->i_mapping);
+
+ drm_gem_object_put(base);
}
static int xe_mmio_gem_mmap(struct drm_gem_object *base, struct vm_area_struct *vma)
@@ -147,8 +177,6 @@ static int xe_mmio_gem_mmap(struct drm_gem_object *base, struct vm_area_struct *
if ((vma->vm_flags & VM_SHARED) == 0)
return -EINVAL;
- /* Set vm_pgoff (used as a fake buffer offset by DRM) to 0 */
- vma->vm_pgoff = 0;
vma->vm_page_prot = pgprot_noncached(vma_get_page_prot(vma));
vm_flags_set(vma, VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP |
VM_DONTCOPY | VM_NORESERVE);
@@ -157,51 +185,47 @@ static int xe_mmio_gem_mmap(struct drm_gem_object *base, struct vm_area_struct *
return 0;
}
-static void xe_mmio_gem_release_dummy_page(struct drm_device *dev, void *res)
+static int alloc_dummy_page_if_needed(struct drm_gem_object *base)
{
- __free_page((struct page *)res);
+ struct xe_mmio_gem *obj = to_xe_mmio_gem(base);
+
+ dma_resv_assert_held(base->resv);
+ if (!obj->dummy_page)
+ obj->dummy_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
+
+ return obj->dummy_page ? 0 : -ENOMEM;
}
-static vm_fault_t xe_mmio_gem_vm_fault_dummy_page(struct vm_area_struct *vma)
+static vm_fault_t xe_mmio_gem_vm_fault_dummy_page(struct vm_fault *vmf)
{
+ struct vm_area_struct *vma = vmf->vma;
struct drm_gem_object *base = vma->vm_private_data;
- struct drm_device *dev = base->dev;
- vm_fault_t ret = VM_FAULT_NOPAGE;
- struct page *page;
+ struct xe_mmio_gem *obj = to_xe_mmio_gem(base);
unsigned long pfn;
- unsigned long i;
- page = alloc_page(GFP_KERNEL | __GFP_ZERO);
- if (!page)
+ if (alloc_dummy_page_if_needed(base))
return VM_FAULT_OOM;
- if (drmm_add_action_or_reset(dev, xe_mmio_gem_release_dummy_page, page))
- return VM_FAULT_OOM;
-
- pfn = page_to_pfn(page);
-
- /* Map the entire VMA to the same dummy page */
- for (i = 0; i < base->size; i += PAGE_SIZE) {
- unsigned long addr = vma->vm_start + i;
-
- ret = vmf_insert_pfn(vma, addr, pfn);
- if (ret & VM_FAULT_ERROR)
- break;
- }
+ pfn = page_to_pfn(obj->dummy_page);
- return ret;
+ return vmf_insert_pfn_prot(vma, vmf->address, pfn,
+ vm_get_page_prot(vma->vm_flags));
}
-static vm_fault_t xe_mmio_gem_vm_fault(struct vm_fault *vmf)
+static vm_fault_t xe_mmio_gem_vm_fault_locked(struct vm_fault *vmf)
{
struct vm_area_struct *vma = vmf->vma;
struct drm_gem_object *base = vma->vm_private_data;
struct xe_mmio_gem *obj = to_xe_mmio_gem(base);
struct drm_device *dev = base->dev;
vm_fault_t ret = VM_FAULT_NOPAGE;
- unsigned long i;
+ unsigned long addr, pfn;
int idx;
+ dma_resv_assert_held(base->resv);
+ if (obj->destroyed)
+ return VM_FAULT_SIGBUS;
+
if (!drm_dev_enter(dev, &idx)) {
/*
* Provide a dummy page to avoid SIGBUS for events such as hot-unplug.
@@ -209,18 +233,30 @@ static vm_fault_t xe_mmio_gem_vm_fault(struct vm_fault *vmf)
* It is assumed the userspace will receive the notification via some
* other channel (e.g. drm uevent).
*/
- return xe_mmio_gem_vm_fault_dummy_page(vma);
+ return xe_mmio_gem_vm_fault_dummy_page(vmf);
}
- for (i = 0; i < base->size; i += PAGE_SIZE) {
- unsigned long addr = vma->vm_start + i;
- unsigned long phys_addr = obj->phys_addr + i;
-
- ret = vmf_insert_pfn(vma, addr, PHYS_PFN(phys_addr));
+ pfn = PHYS_PFN(obj->phys_addr);
+ for (addr = vma->vm_start; addr < vma->vm_end; addr += PAGE_SIZE) {
+ ret = vmf_insert_pfn(vma, addr, pfn);
if (ret & VM_FAULT_ERROR)
break;
+
+ pfn++;
}
drm_dev_exit(idx);
return ret;
}
+
+static vm_fault_t xe_mmio_gem_vm_fault(struct vm_fault *vmf)
+{
+ struct vm_area_struct *vma = vmf->vma;
+ struct drm_gem_object *base = vma->vm_private_data;
+ vm_fault_t ret;
+
+ dma_resv_lock(base->resv, NULL);
+ ret = xe_mmio_gem_vm_fault_locked(vmf);
+ dma_resv_unlock(base->resv);
+ return ret;
+}
diff --git a/drivers/gpu/drm/xe/xe_mmio_gem.h b/drivers/gpu/drm/xe/xe_mmio_gem.h
index 4b76d5586ebb..80d7795f07c8 100644
--- a/drivers/gpu/drm/xe/xe_mmio_gem.h
+++ b/drivers/gpu/drm/xe/xe_mmio_gem.h
@@ -15,6 +15,6 @@ struct xe_mmio_gem;
struct xe_mmio_gem *xe_mmio_gem_create(struct xe_device *xe, struct drm_file *file,
phys_addr_t phys_addr, size_t size);
u64 xe_mmio_gem_mmap_offset(struct xe_mmio_gem *gem);
-void xe_mmio_gem_destroy(struct xe_mmio_gem *gem);
+void xe_mmio_gem_destroy(struct xe_mmio_gem *gem, struct drm_file *file);
#endif /* _XE_MMIO_GEM_H_ */
diff --git a/drivers/gpu/drm/xe/xe_pagefault.c b/drivers/gpu/drm/xe/xe_pagefault.c
index dbf8f71d3328..a4986df8328d 100644
--- a/drivers/gpu/drm/xe/xe_pagefault.c
+++ b/drivers/gpu/drm/xe/xe_pagefault.c
@@ -16,6 +16,7 @@
#include "xe_hw_engine.h"
#include "xe_pagefault.h"
#include "xe_pagefault_types.h"
+#include "xe_pm.h"
#include "xe_svm.h"
#include "xe_trace_bo.h"
#include "xe_vm.h"
@@ -292,9 +293,17 @@ static void xe_pagefault_queue_work(struct work_struct *w)
{
struct xe_pagefault_queue *pf_queue =
container_of(w, typeof(*pf_queue), worker);
+ struct xe_device *xe = pf_queue->xe;
struct xe_pagefault pf;
unsigned long threshold;
+ /*
+ * A live VM holds a PM reference, but a torn-down VM does not.
+ * Guard the entire worker loop to safely drain stale faults and
+ * prevent autosuspends from desyncing batched CT flushes.
+ */
+ guard(xe_pm_runtime)(xe);
+
#define USM_QUEUE_MAX_RUNTIME_MS 20
threshold = jiffies + msecs_to_jiffies(USM_QUEUE_MAX_RUNTIME_MS);
@@ -365,6 +374,7 @@ static int xe_pagefault_queue_init(struct xe_device *xe,
drm_dbg(&xe->drm, "xe_pagefault_entry_size=%d, total_num_eus=%d, pf_queue->size=%u",
xe_pagefault_entry_size(), total_num_eus, pf_queue->size);
+ pf_queue->xe = xe;
spin_lock_init(&pf_queue->lock);
INIT_WORK(&pf_queue->worker, xe_pagefault_queue_work);
diff --git a/drivers/gpu/drm/xe/xe_pagefault_types.h b/drivers/gpu/drm/xe/xe_pagefault_types.h
index c4ee625b93dd..f63a12aa0d4f 100644
--- a/drivers/gpu/drm/xe/xe_pagefault_types.h
+++ b/drivers/gpu/drm/xe/xe_pagefault_types.h
@@ -8,6 +8,7 @@
#include <linux/workqueue.h>
+struct xe_device;
struct xe_gt;
struct xe_pagefault;
@@ -118,6 +119,8 @@ struct xe_pagefault {
* queue to absorb the device’s worst-case number of outstanding faults.
*/
struct xe_pagefault_queue {
+ /** @xe: Back-pointer to the Xe device */
+ struct xe_device *xe;
/**
* @data: Data in queue containing struct xe_pagefault, protected by
* @lock
diff --git a/drivers/gpu/drm/xe/xe_ring_ops.c b/drivers/gpu/drm/xe/xe_ring_ops.c
index 39a670e91ba7..08b4a4283e96 100644
--- a/drivers/gpu/drm/xe/xe_ring_ops.c
+++ b/drivers/gpu/drm/xe/xe_ring_ops.c
@@ -212,6 +212,7 @@ static int emit_render_cache_flush(struct xe_sched_job *job, u32 *dw, int i)
{
struct xe_exec_queue *q = job->q;
struct xe_gt *gt = q->gt;
+ struct xe_device *xe = gt_to_xe(gt);
bool lacks_render = !(gt->info.engine_mask & XE_HW_ENGINE_RCS_MASK);
u32 flags0, flags1;
@@ -220,6 +221,16 @@ static int emit_render_cache_flush(struct xe_sched_job *job, u32 *dw, int i)
LRC_PPHWSP_FLUSH_INVAL_SCRATCH_ADDR, 0);
flags0 = PIPE_CONTROL0_HDC_PIPELINE_FLUSH;
+ /*
+ * Prior to MTL, HDC Pipeline Flush reliably also flushes the LSC
+ * untyped L1 dataport cache, provided HDC_CHICKEN0 is programmed
+ * correctly. Starting with MTL that coupling no longer holds
+ * regardless of how HDC_CHICKEN0 is programmed, but explicitly
+ * requesting the flush via PIPE_CONTROL is itself only reliable
+ * from Xe2 onward, so only gate it in on Xe2+.
+ */
+ if (GRAPHICS_VERx100(xe) >= 2000)
+ flags0 |= PIPE_CONTROL0_UNTYPED_DATAPORT_CACHE_FLUSH;
flags1 = (PIPE_CONTROL_TILE_CACHE_FLUSH |
PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH |
PIPE_CONTROL_DEPTH_CACHE_FLUSH |
diff --git a/drivers/gpu/drm/xe/xe_shrinker.c b/drivers/gpu/drm/xe/xe_shrinker.c
index 83374cd57660..deb4378c1ec1 100644
--- a/drivers/gpu/drm/xe/xe_shrinker.c
+++ b/drivers/gpu/drm/xe/xe_shrinker.c
@@ -54,13 +54,40 @@ xe_shrinker_mod_pages(struct xe_shrinker *shrinker, long shrinkable, long purgea
write_unlock(&shrinker->lock);
}
-static s64 __xe_shrinker_walk(struct xe_device *xe,
+static bool __xe_shrinker_runtime_pm_get(struct xe_shrinker *shrinker)
+{
+ struct xe_device *xe = shrinker->xe;
+
+ if (xe_pm_runtime_get_if_active(xe))
+ return true;
+
+ if (xe_rpm_reclaim_safe(xe) && !ttm_bo_shrink_avoid_wait()) {
+ xe_pm_runtime_get(xe);
+ return true;
+ }
+
+ queue_work(xe->unordered_wq, &shrinker->pm_worker);
+
+ return false;
+}
+
+static void xe_shrinker_runtime_pm_put(struct xe_shrinker *shrinker, bool runtime_pm)
+{
+ if (runtime_pm)
+ xe_pm_runtime_put(shrinker->xe);
+}
+
+static int __xe_shrinker_walk(struct xe_shrinker *shrinker,
struct ttm_operation_ctx *ctx,
const struct xe_bo_shrink_flags flags,
- unsigned long to_scan, unsigned long *scanned)
+ unsigned long to_scan, unsigned long *scanned,
+ unsigned long *freed)
{
+ struct xe_device *xe = shrinker->xe;
unsigned int mem_type;
- s64 freed = 0, lret;
+ bool rpm = false;
+ int ret = 0;
+ s64 lret;
for (mem_type = XE_PL_SYSTEM; mem_type <= XE_PL_TT; ++mem_type) {
struct ttm_resource_manager *man = ttm_manager_type(&xe->ttm, mem_type);
@@ -74,23 +101,35 @@ static s64 __xe_shrinker_walk(struct xe_device *xe,
if (!man || !man->use_tt)
continue;
+ if (mem_type != XE_PL_SYSTEM && !rpm &&
+ xe_device_is_l2_flush_optimized(xe)) {
+ if (!__xe_shrinker_runtime_pm_get(shrinker))
+ break;
+ rpm = true;
+ }
+
ttm_bo_lru_for_each_reserved_guarded(&curs, man, &arg, ttm_bo) {
if (!ttm_bo_shrink_suitable(ttm_bo, ctx))
continue;
lret = xe_bo_shrink(ctx, ttm_bo, flags, scanned);
- if (lret < 0)
- return lret;
+ if (lret < 0) {
+ ret = lret;
+ goto out;
+ }
- freed += lret;
+ *freed += lret;
if (*scanned >= to_scan)
- break;
+ goto out;
}
/* Trylocks should never error, just fail. */
xe_assert(xe, !IS_ERR(ttm_bo));
}
- return freed;
+out:
+ xe_shrinker_runtime_pm_put(shrinker, rpm);
+
+ return ret;
}
/*
@@ -99,40 +138,36 @@ static s64 __xe_shrinker_walk(struct xe_device *xe,
* add writeback. This avoids stalls and explicit writebacks with light or
* moderate memory pressure.
*/
-static s64 xe_shrinker_walk(struct xe_device *xe,
+static int xe_shrinker_walk(struct xe_shrinker *shrinker,
struct ttm_operation_ctx *ctx,
const struct xe_bo_shrink_flags flags,
- unsigned long to_scan, unsigned long *scanned)
+ unsigned long to_scan, unsigned long *scanned,
+ unsigned long *freed)
{
bool no_wait_gpu = true;
struct xe_bo_shrink_flags save_flags = flags;
- s64 lret, freed;
+ int ret;
swap(no_wait_gpu, ctx->no_wait_gpu);
save_flags.writeback = false;
- lret = __xe_shrinker_walk(xe, ctx, save_flags, to_scan, scanned);
+ ret = __xe_shrinker_walk(shrinker, ctx, save_flags, to_scan, scanned,
+ freed);
swap(no_wait_gpu, ctx->no_wait_gpu);
- if (lret < 0 || *scanned >= to_scan)
- return lret;
+ if (ret || *scanned >= to_scan)
+ return ret;
- freed = lret;
if (!ctx->no_wait_gpu) {
- lret = __xe_shrinker_walk(xe, ctx, save_flags, to_scan, scanned);
- if (lret < 0)
- return lret;
- freed += lret;
- if (*scanned >= to_scan)
- return freed;
+ ret = __xe_shrinker_walk(shrinker, ctx, save_flags, to_scan, scanned,
+ freed);
+ if (ret || *scanned >= to_scan)
+ return ret;
}
- if (flags.writeback) {
- lret = __xe_shrinker_walk(xe, ctx, flags, to_scan, scanned);
- if (lret < 0)
- return lret;
- freed += lret;
- }
+ if (flags.writeback)
+ ret = __xe_shrinker_walk(shrinker, ctx, flags, to_scan, scanned,
+ freed);
- return freed;
+ return ret;
}
static unsigned long
@@ -180,22 +215,7 @@ static bool xe_shrinker_runtime_pm_get(struct xe_shrinker *shrinker, bool force,
return false;
}
- if (!xe_pm_runtime_get_if_active(xe)) {
- if (xe_rpm_reclaim_safe(xe) && !ttm_bo_shrink_avoid_wait()) {
- xe_pm_runtime_get(xe);
- return true;
- }
- queue_work(xe->unordered_wq, &shrinker->pm_worker);
- return false;
- }
-
- return true;
-}
-
-static void xe_shrinker_runtime_pm_put(struct xe_shrinker *shrinker, bool runtime_pm)
-{
- if (runtime_pm)
- xe_pm_runtime_put(shrinker->xe);
+ return __xe_shrinker_runtime_pm_get(shrinker);
}
static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_control *sc)
@@ -214,7 +234,6 @@ static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_con
bool runtime_pm;
bool purgeable;
bool can_backup = !!(sc->gfp_mask & __GFP_FS);
- s64 lret;
nr_to_scan = sc->nr_to_scan;
@@ -225,12 +244,9 @@ static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_con
/* Might need runtime PM. Try to wake early if it looks like it. */
runtime_pm = xe_shrinker_runtime_pm_get(shrinker, false, nr_to_scan, can_backup);
- if (purgeable && nr_scanned < nr_to_scan) {
- lret = xe_shrinker_walk(shrinker->xe, &ctx, shrink_flags,
- nr_to_scan, &nr_scanned);
- if (lret >= 0)
- freed += lret;
- }
+ if (purgeable && nr_scanned < nr_to_scan)
+ xe_shrinker_walk(shrinker, &ctx, shrink_flags,
+ nr_to_scan, &nr_scanned, &freed);
sc->nr_scanned = nr_scanned;
if (nr_scanned >= nr_to_scan || !can_backup)
@@ -242,10 +258,8 @@ static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_con
shrink_flags.purge = false;
- lret = xe_shrinker_walk(shrinker->xe, &ctx, shrink_flags,
- nr_to_scan, &nr_scanned);
- if (lret >= 0)
- freed += lret;
+ xe_shrinker_walk(shrinker, &ctx, shrink_flags,
+ nr_to_scan, &nr_scanned, &freed);
sc->nr_scanned = nr_scanned;
out: