summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2026-10-02 07:43:30 +1000
committerDave Airlie <airlied@redhat.com>2026-10-02 07:44:44 +1000
commit845c5cc3697c5520fe3d7fc8597d393d172cc5fd (patch)
treea4e0c16d3eff7330337c13f4022023b882135b21
parentcd9d5e4be968041fd811f5bb6d27d05921877e27 (diff)
parent70dab9ee7d620e6018a64798aed065d14e9baed1 (diff)
Merge tag 'drm-xe-next-2026-10-01' of https://gitlab.freedesktop.org/drm/xe/kernel into drm-next
UAPI Changes: - New sysfs to expose device UID (Badal) Driver Changes: - Nuke PTs only after unlinking contested VMAs (Auld) - Limit sg segment size to PAGE_SIZE on Xen PV (Szymon) - Move locking to xe_sriov_pf_reprovision_default (Michal) - New workarounds (Tilak, Tales, Daniel) - Keep VF LMEM BAR size low if no VFs enabled (Marcin) - Drop ratelimiting from dev_unhold_work debug trace (Varun) - System Controller's Maibox response improvement (Anoop) - General clean-ups (Roper, Lin) - Revert "drm/xe: Set mask bits for CCS_MODE register" (Roper) - CPU binds and ULLS on migration queue (Brost) - Re-apply an i2c patch silently removed by a merge (Bartosz) - New PTL PCI ID (Atwood) - Enable PXP for NVL-S (Alan) - Let xe_guc_ct_send_locked() callers reserve G2H credit (Sujaritha) - Implement print vfunc for page fault info logging (Brian) - Do not dma_fence_put zero fences in err_out path (Jonathan) - Add focused VM teardown and page fault tracing (Daniel) - Fix ULLS chained job loss and GT reset replay (Varun) - Refresh TTM LRU on SVM prefetch (Himal) - Fix xe_mmio_wait32() to honor delay/sleep maximums (Alan) Signed-off-by: Dave Airlie <airlied@redhat.com> From: Rodrigo Vivi <rodrigo.vivi@intel.com> Link: https://patch.msgid.link/ar5YsqAK3bS4Ta6Z@intel.com
-rw-r--r--Documentation/ABI/testing/sysfs-driver-intel-xe-gpu10
-rw-r--r--Documentation/gpu/xe/xe_migrate.rst3
-rw-r--r--drivers/gpu/drm/drm_gpusvm.c105
-rw-r--r--drivers/gpu/drm/drm_pagemap.c17
-rw-r--r--drivers/gpu/drm/xe/Makefile1
-rw-r--r--drivers/gpu/drm/xe/abi/guc_klvs_abi.h1
-rw-r--r--drivers/gpu/drm/xe/regs/xe_gt_regs.h3
-rw-r--r--drivers/gpu/drm/xe/regs/xe_regs.h2
-rw-r--r--drivers/gpu/drm/xe/xe_bo.c4
-rw-r--r--drivers/gpu/drm/xe/xe_bo.h29
-rw-r--r--drivers/gpu/drm/xe/xe_bo_types.h2
-rw-r--r--drivers/gpu/drm/xe/xe_configfs.c67
-rw-r--r--drivers/gpu/drm/xe/xe_configfs.h2
-rw-r--r--drivers/gpu/drm/xe/xe_cpu_bind.c296
-rw-r--r--drivers/gpu/drm/xe/xe_cpu_bind.h111
-rw-r--r--drivers/gpu/drm/xe/xe_devcoredump.c46
-rw-r--r--drivers/gpu/drm/xe/xe_devcoredump.h15
-rw-r--r--drivers/gpu/drm/xe/xe_device.c14
-rw-r--r--drivers/gpu/drm/xe/xe_device_sysfs.c36
-rw-r--r--drivers/gpu/drm/xe/xe_device_types.h15
-rw-r--r--drivers/gpu/drm/xe/xe_drm_client.c2
-rw-r--r--drivers/gpu/drm/xe/xe_exec_queue.c163
-rw-r--r--drivers/gpu/drm/xe/xe_exec_queue.h16
-rw-r--r--drivers/gpu/drm/xe/xe_exec_queue_types.h20
-rw-r--r--drivers/gpu/drm/xe/xe_gt_ccs_mode.c6
-rw-r--r--drivers/gpu/drm/xe/xe_guc.c16
-rw-r--r--drivers/gpu/drm/xe/xe_guc.h2
-rw-r--r--drivers/gpu/drm/xe/xe_guc_ads.c11
-rw-r--r--drivers/gpu/drm/xe/xe_guc_ct.c23
-rw-r--r--drivers/gpu/drm/xe/xe_guc_ct.h2
-rw-r--r--drivers/gpu/drm/xe/xe_guc_engine_activity.c6
-rw-r--r--drivers/gpu/drm/xe/xe_guc_hwconfig.c2
-rw-r--r--drivers/gpu/drm/xe/xe_guc_log.c7
-rw-r--r--drivers/gpu/drm/xe/xe_guc_pagefault.c35
-rw-r--r--drivers/gpu/drm/xe/xe_guc_pc.c3
-rw-r--r--drivers/gpu/drm/xe/xe_guc_submit.c90
-rw-r--r--drivers/gpu/drm/xe/xe_guc_submit_types.h2
-rw-r--r--drivers/gpu/drm/xe/xe_hw_engine.c96
-rw-r--r--drivers/gpu/drm/xe/xe_i2c.c22
-rw-r--r--drivers/gpu/drm/xe/xe_i2c.h1
-rw-r--r--drivers/gpu/drm/xe/xe_lrc.c73
-rw-r--r--drivers/gpu/drm/xe/xe_lrc.h4
-rw-r--r--drivers/gpu/drm/xe/xe_lrc_types.h4
-rw-r--r--drivers/gpu/drm/xe/xe_migrate.c771
-rw-r--r--drivers/gpu/drm/xe/xe_migrate.h95
-rw-r--r--drivers/gpu/drm/xe/xe_mmio.c5
-rw-r--r--drivers/gpu/drm/xe/xe_pagefault.c55
-rw-r--r--drivers/gpu/drm/xe/xe_pagefault_types.h10
-rw-r--r--drivers/gpu/drm/xe/xe_pci.c5
-rw-r--r--drivers/gpu/drm/xe/xe_pci_sriov.c3
-rw-r--r--drivers/gpu/drm/xe/xe_pci_types.h4
-rw-r--r--drivers/gpu/drm/xe/xe_pt.c781
-rw-r--r--drivers/gpu/drm/xe/xe_pt.h12
-rw-r--r--drivers/gpu/drm/xe/xe_pt_types.h49
-rw-r--r--drivers/gpu/drm/xe/xe_ring_ops.c86
-rw-r--r--drivers/gpu/drm/xe/xe_ring_ops_types.h24
-rw-r--r--drivers/gpu/drm/xe/xe_sched_job.c103
-rw-r--r--drivers/gpu/drm/xe/xe_sched_job.h56
-rw-r--r--drivers/gpu/drm/xe/xe_sched_job_types.h49
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_provision.c4
-rw-r--r--drivers/gpu/drm/xe/xe_svm.c68
-rw-r--r--drivers/gpu/drm/xe/xe_svm.h18
-rw-r--r--drivers/gpu/drm/xe/xe_sync.c20
-rw-r--r--drivers/gpu/drm/xe/xe_sysctrl_mailbox.c7
-rw-r--r--drivers/gpu/drm/xe/xe_sysctrl_mailbox_types.h3
-rw-r--r--drivers/gpu/drm/xe/xe_tlb_inval.c54
-rw-r--r--drivers/gpu/drm/xe/xe_tlb_inval_job.c28
-rw-r--r--drivers/gpu/drm/xe/xe_tlb_inval_job.h4
-rw-r--r--drivers/gpu/drm/xe/xe_tlb_inval_types.h17
-rw-r--r--drivers/gpu/drm/xe/xe_trace.h2
-rw-r--r--drivers/gpu/drm/xe/xe_trace_bo.h58
-rw-r--r--drivers/gpu/drm/xe/xe_tuning.c4
-rw-r--r--drivers/gpu/drm/xe/xe_uc_fw.c1
-rw-r--r--drivers/gpu/drm/xe/xe_vm.c275
-rw-r--r--drivers/gpu/drm/xe/xe_vm.h3
-rw-r--r--drivers/gpu/drm/xe/xe_vm_madvise.c6
-rw-r--r--drivers/gpu/drm/xe/xe_vm_types.h12
-rw-r--r--drivers/gpu/drm/xe/xe_wa_oob.rules4
-rw-r--r--include/drm/drm_gpusvm.h6
-rw-r--r--include/drm/drm_pagemap.h7
-rw-r--r--include/drm/intel/pciids.h1
81 files changed, 2721 insertions, 1374 deletions
diff --git a/Documentation/ABI/testing/sysfs-driver-intel-xe-gpu b/Documentation/ABI/testing/sysfs-driver-intel-xe-gpu
new file mode 100644
index 000000000000..4dc4afc3961d
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-driver-intel-xe-gpu
@@ -0,0 +1,10 @@
+What: /sys/bus/pci/drivers/xe/.../device_uid
+Date: October 2026
+KernelVersion: 7.4
+Contact: intel-xe@lists.freedesktop.org
+Description:
+ RO. Unique 64-bit identifier of the GPU SoC device, exposed as
+ hexadecimal value.
+
+ This sysfs file is present only on supported Intel Xe platforms.
+ Accessible only to users with administrative privileges.
diff --git a/Documentation/gpu/xe/xe_migrate.rst b/Documentation/gpu/xe/xe_migrate.rst
index f92faec0ac94..d297ee53a582 100644
--- a/Documentation/gpu/xe/xe_migrate.rst
+++ b/Documentation/gpu/xe/xe_migrate.rst
@@ -6,3 +6,6 @@ Migrate Layer
.. kernel-doc:: drivers/gpu/drm/xe/xe_migrate_doc.h
:doc: Migrate Layer
+
+.. kernel-doc:: drivers/gpu/drm/xe/xe_migrate.c
+ :doc: ULLS (Ultra Low Latency Submission) for migration jobs
diff --git a/drivers/gpu/drm/drm_gpusvm.c b/drivers/gpu/drm/drm_gpusvm.c
index b6c9d3a07dc8..a37b26881b73 100644
--- a/drivers/gpu/drm/drm_gpusvm.c
+++ b/drivers/gpu/drm/drm_gpusvm.c
@@ -1528,7 +1528,49 @@ static bool drm_gpusvm_pages_inlinable(struct drm_gpusvm_pages *svm_pages,
}
/**
- * drm_gpusvm_dma_map_pages() - DMA map one drm_gpusvm_pages instance
+ * drm_gpusvm_walk_devmem() - Invoke the devmem callback across faulted pages
+ * @gpusvm: Pointer to the GPU SVM structure
+ * @pfns: The already-faulted pfn array (size @npages)
+ * @npages: Number of pages in the CPU range
+ * @ctx: GPU SVM context, with a non-NULL &drm_gpusvm_ctx.devmem_fn
+ *
+ * Invoke &drm_gpusvm_ctx.devmem_fn once per contiguous run of @pfns backed by
+ * the same device-memory allocation. Must be called under the notifier lock.
+ */
+static void drm_gpusvm_walk_devmem(struct drm_gpusvm *gpusvm,
+ unsigned long *pfns,
+ unsigned long npages,
+ const struct drm_gpusvm_ctx *ctx)
+{
+ struct drm_pagemap_devmem *last = NULL;
+ unsigned int order = 0;
+ unsigned long i;
+
+ lockdep_assert_held(&gpusvm->notifier_lock);
+
+ for (i = 0; i < npages; i += 1 << order) {
+ struct page *page = hmm_pfn_to_page(pfns[i]);
+ struct drm_pagemap_devmem *devmem;
+
+ order = drm_gpusvm_hmm_pfn_to_order(pfns[i], i, npages);
+
+ if (!is_device_private_page(page) &&
+ !is_device_coherent_page(page)) {
+ last = NULL;
+ continue;
+ }
+
+ devmem = drm_pagemap_page_to_devmem(page);
+ if (devmem == last)
+ continue;
+
+ last = devmem;
+ ctx->devmem_fn(devmem);
+ }
+}
+
+/**
+ * drm_gpusvm_dma_map_pages() - Walk and DMA map one drm_gpusvm_pages instance
* @gpusvm: Pointer to the GPU SVM structure
* @svm_pages: The SVM pages instance to populate with dma-addresses
* @pfns: The already-faulted pfn array (size @npages)
@@ -1536,10 +1578,10 @@ static bool drm_gpusvm_pages_inlinable(struct drm_gpusvm_pages *svm_pages,
* @ctx: GPU SVM context
* @dma_dir: DMA data direction for the mappings
*
- * Map the faulted @pfns into @svm_pages for DMA access through its owning
- * drm_device. Must be called under the notifier lock and only for an instance
- * without a live mapping. On failure this unwinds the partial mapping of this
- * instance before returning.
+ * Walk the faulted @pfns and map them into @svm_pages for DMA access through
+ * its owning drm_device. Must be called under the notifier lock and only for
+ * an instance without a live mapping. On failure this unwinds the partial
+ * mapping of this instance before returning.
*
* Return: 0 on success, negative error code on failure.
*/
@@ -1774,9 +1816,20 @@ retry:
hmm_range.notifier_seq = mmu_interval_read_begin(notifier);
- if (map_dma &&
- drm_gpusvm_pages_valid_unlocked(gpusvm, svm_pages, num_pages))
- goto set_seqno;
+ /*
+ * drm_gpusvm_pages_valid_unlocked() also resets any instance whose
+ * mapping was invalidated, so it must run on the map_dma path even when
+ * devmem_fn suppresses the fast path; otherwise a stale inline mapping
+ * is later mistaken for a dma_addr array by drm_gpusvm_dma_map_pages().
+ */
+ if (map_dma) {
+ bool pages_valid = drm_gpusvm_pages_valid_unlocked(gpusvm,
+ svm_pages,
+ num_pages);
+
+ if (!ctx->devmem_fn && pages_valid)
+ goto set_seqno;
+ }
pfns = kvmalloc_array(npages, sizeof(*pfns), GFP_KERNEL);
if (!pfns)
@@ -1829,28 +1882,30 @@ retry:
goto retry;
}
- if (!map_dma)
- goto done_mapping;
+ if (ctx->devmem_fn)
+ drm_gpusvm_walk_devmem(gpusvm, pfns, npages, ctx);
- for (p = 0; p < num_pages; ++p) {
- if (drm_gpusvm_pages_valid(gpusvm, &svm_pages[p]))
- continue;
+ if (map_dma) {
+ for (p = 0; p < num_pages; ++p) {
+ if (drm_gpusvm_pages_valid(gpusvm, &svm_pages[p]))
+ continue;
- err = drm_gpusvm_dma_map_pages(gpusvm, &svm_pages[p], pfns,
- npages, ctx, dma_dir);
- if (err) {
- /*
- * The failing instance was unwound by the helper. Keep
- * the ones mapped earlier: the -EAGAIN retry reuses
- * them, and the driver unmaps every instance with the
- * range on the other error paths.
- */
- drm_gpusvm_notifier_unlock(gpusvm);
- goto err_free;
+ err = drm_gpusvm_dma_map_pages(gpusvm, &svm_pages[p], pfns,
+ npages, ctx, dma_dir);
+ if (err) {
+ /*
+ * The failing instance was unwound by the
+ * helper. Keep the ones mapped earlier: the
+ * -EAGAIN retry reuses them, and the driver
+ * unmaps every instance with the range on the
+ * other error paths.
+ */
+ drm_gpusvm_notifier_unlock(gpusvm);
+ goto err_free;
+ }
}
}
-done_mapping:
drm_gpusvm_notifier_unlock(gpusvm);
kvfree(pfns);
set_seqno:
diff --git a/drivers/gpu/drm/drm_pagemap.c b/drivers/gpu/drm/drm_pagemap.c
index c66c58f85c34..bbef0c225b7e 100644
--- a/drivers/gpu/drm/drm_pagemap.c
+++ b/drivers/gpu/drm/drm_pagemap.c
@@ -1102,7 +1102,7 @@ static void drm_pagemap_dev_unhold_work(struct work_struct *work)
struct drm_device *drm = dev_hold->drm;
struct module *module = drm->driver->fops->owner;
- drm_dbg_ratelimited(drm, "Releasing reference on provider device and module.\n");
+ drm_dbg(drm, "Releasing reference on provider device and module.\n");
drm_dev_put(drm);
module_put(module);
kfree(dev_hold);
@@ -1630,6 +1630,21 @@ struct drm_pagemap *drm_pagemap_page_to_dpagemap(struct page *page)
EXPORT_SYMBOL_GPL(drm_pagemap_page_to_dpagemap);
/**
+ * drm_pagemap_page_to_devmem() - Return the devmem allocation backing a page
+ * @page: The struct page.
+ *
+ * Return: The &drm_pagemap_devmem backing @page. Undefined if @page was not
+ * populated from a &drm_pagemap.
+ */
+struct drm_pagemap_devmem *drm_pagemap_page_to_devmem(struct page *page)
+{
+ struct drm_pagemap_zdd *zdd = drm_pagemap_page_zone_device_data(page);
+
+ return zdd->devmem_allocation;
+}
+EXPORT_SYMBOL_GPL(drm_pagemap_page_to_devmem);
+
+/**
* drm_pagemap_populate_mm() - Populate a virtual range with device memory pages
* @dpagemap: Pointer to the drm_pagemap managing the device memory
* @start: Start of the virtual range to populate.
diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile
index 67b8b5477639..0670a3f08ec8 100644
--- a/drivers/gpu/drm/xe/Makefile
+++ b/drivers/gpu/drm/xe/Makefile
@@ -35,6 +35,7 @@ $(obj)/generated/%_device_wa_oob.c $(obj)/generated/%_device_wa_oob.h: $(obj)/xe
xe-y += xe_bb.o \
xe_bo.o \
xe_bo_evict.o \
+ xe_cpu_bind.o \
xe_dep_scheduler.o \
xe_devcoredump.o \
xe_device.o \
diff --git a/drivers/gpu/drm/xe/abi/guc_klvs_abi.h b/drivers/gpu/drm/xe/abi/guc_klvs_abi.h
index 685c4ef17b73..4bcdc1a96785 100644
--- a/drivers/gpu/drm/xe/abi/guc_klvs_abi.h
+++ b/drivers/gpu/drm/xe/abi/guc_klvs_abi.h
@@ -525,6 +525,7 @@ enum xe_guc_klv_ids {
GUC_WA_KLV_CLR_CS_INDIRECT_RING_STATE_IF_IDLE_AT_CTX_REG = 0x900e,
GUC_WA_KLV_REMAP_RANGED_TLB_INV = 0x900f,
GUC_WA_KLV_IGNORE_MMIO_READ_SEM_TOKEN_64 = 0x9010,
+ GUC_WA_KLV_IGNORE_MULTIQ_CTX_COMPLETE_WITH_PREEMPT_IN_CSB = 0x9011,
};
/**
diff --git a/drivers/gpu/drm/xe/regs/xe_gt_regs.h b/drivers/gpu/drm/xe/regs/xe_gt_regs.h
index 2e059cb36c82..650f117c3e13 100644
--- a/drivers/gpu/drm/xe/regs/xe_gt_regs.h
+++ b/drivers/gpu/drm/xe/regs/xe_gt_regs.h
@@ -468,6 +468,7 @@
#define L3_SQ_DISABLE_COAMA_2WAY_COH REG_BIT(30)
#define L3_SQ_DISABLE_COAMA REG_BIT(22)
#define COMPMEMRD256BOVRFETCHEN REG_BIT(20)
+#define NONCOMPMEMRD256BOVRFETCHEN REG_BIT(14)
#define L3SQCREG3 XE_REG_MCR(0xb108)
#define COMPPWOVERFETCHEN REG_BIT(28)
@@ -636,7 +637,7 @@
* [4-6] RSVD
* [7] Disabled
*/
-#define CCS_MODE XE_REG(0x14804, XE_REG_OPTION_MASKED)
+#define CCS_MODE XE_REG(0x14804)
#define CCS_MODE_CSLICE_0_3_MASK REG_GENMASK(11, 0) /* 3 bits per cslice */
#define CCS_MODE_CSLICE_MASK 0x7 /* CCS0-3 + rsvd */
#define CCS_MODE_CSLICE_WIDTH ilog2(CCS_MODE_CSLICE_MASK + 1)
diff --git a/drivers/gpu/drm/xe/regs/xe_regs.h b/drivers/gpu/drm/xe/regs/xe_regs.h
index ef4746b7b5d3..437485b5a0af 100644
--- a/drivers/gpu/drm/xe/regs/xe_regs.h
+++ b/drivers/gpu/drm/xe/regs/xe_regs.h
@@ -30,6 +30,8 @@
#define XEHP_MTCFG_ADDR XE_REG(0x101800)
#define TILE_COUNT REG_GENMASK(15, 8)
+#define CRI_DEVICE_UID XE_REG(0x102008)
+
#define GGC XE_REG(0x108040)
#define GMS_MASK REG_GENMASK(15, 8)
#define GGMS_MASK REG_GENMASK(7, 6)
diff --git a/drivers/gpu/drm/xe/xe_bo.c b/drivers/gpu/drm/xe/xe_bo.c
index f2ab9bf43a86..6921b6967330 100644
--- a/drivers/gpu/drm/xe/xe_bo.c
+++ b/drivers/gpu/drm/xe/xe_bo.c
@@ -1879,7 +1879,7 @@ static void xe_ttm_bo_destroy(struct ttm_buffer_object *ttm_bo)
xe_drm_client_remove_bo(bo);
#endif
- if (bo->vm && xe_bo_is_user(bo))
+ if (bo->vm && (xe_bo_is_user(bo) || bo->flags & XE_BO_FLAG_PAGETABLE))
xe_vm_put(bo->vm);
if (bo->parent_obj)
@@ -2575,7 +2575,7 @@ __xe_bo_create_locked(struct xe_device *xe,
* by having all the vm's bo refereferences released at vm close
* time.
*/
- if (vm && xe_bo_is_user(bo))
+ if (vm && (xe_bo_is_user(bo) || bo->flags & XE_BO_FLAG_PAGETABLE))
xe_vm_get(vm);
bo->vm = vm;
diff --git a/drivers/gpu/drm/xe/xe_bo.h b/drivers/gpu/drm/xe/xe_bo.h
index 290ca624e2a7..861b1be231de 100644
--- a/drivers/gpu/drm/xe/xe_bo.h
+++ b/drivers/gpu/drm/xe/xe_bo.h
@@ -9,6 +9,8 @@
#include <drm/drm_prime.h>
#include <drm/ttm/ttm_tt.h>
+#include <xen/xen.h>
+
#include "xe_bo_types.h"
#include "xe_ggtt.h"
#include "xe_macros.h"
@@ -494,6 +496,8 @@ void __xe_bo_release_dummy(struct kref *kref);
* @bo: The bo to put.
* @deferred: List to which to add the buffer object if we cannot put, or
* NULL if the function is to put unconditionally.
+ * @added: BO was added to deferred list, written back to caller, can be NULL if
+ * writeback is not needed. Only set to true when added, never set to false.
*
* Since the final freeing of an object includes both sleeping and (!)
* memory allocation in the dma_resv individualization, it's not ok
@@ -513,7 +517,8 @@ void __xe_bo_release_dummy(struct kref *kref);
* false otherwise.
*/
static inline bool
-xe_bo_put_deferred(struct xe_bo *bo, struct llist_head *deferred)
+xe_bo_put_deferred(struct xe_bo *bo, struct llist_head *deferred,
+ bool *added)
{
if (!deferred) {
xe_bo_put(bo);
@@ -523,6 +528,9 @@ xe_bo_put_deferred(struct xe_bo *bo, struct llist_head *deferred)
if (!kref_put(&bo->ttm.base.refcount, __xe_bo_release_dummy))
return false;
+ if (added)
+ *added = true;
+
return llist_add(&bo->freed, deferred);
}
@@ -539,7 +547,7 @@ xe_bo_put_async(struct xe_bo *bo)
{
struct xe_bo_dev *bo_device = &xe_bo_device(bo)->bo_device;
- if (xe_bo_put_deferred(bo, &bo_device->async_list))
+ if (xe_bo_put_deferred(bo, &bo_device->async_list, NULL))
schedule_work(&bo_device->async_free);
}
@@ -574,6 +582,23 @@ static inline unsigned int xe_sg_segment_size(struct device *dev)
struct scatterlist __maybe_unused sg;
size_t max = BIT_ULL(sizeof(sg.length) * 8) - 1;
+ /*
+ * For Xen PV guests pages aren't contiguous in DMA (machine) address
+ * space. The DMA API takes care of that both in dma_alloc_* (by
+ * calling into the hypervisor to make the pages contiguous) and in
+ * dma_map_* (by bounce buffering). But xe (like i915, see commit
+ * 78a07fe777c4) ignores the coherency aspects of the DMA API and thus
+ * can't cope with bounce buffering actually happening, so add a hack
+ * here to force small allocations and mappings when running in PV
+ * mode on Xen.
+ *
+ * Note this will still break if bounce buffering is required for other
+ * reasons, like confidential computing hypervisors or PCIe root ports
+ * with addressing limitations.
+ */
+ if (xen_pv_domain())
+ return PAGE_SIZE;
+
max = min_t(size_t, max, dma_max_mapping_size(dev));
/*
diff --git a/drivers/gpu/drm/xe/xe_bo_types.h b/drivers/gpu/drm/xe/xe_bo_types.h
index 0eb93052c1d5..8ec4a01a0092 100644
--- a/drivers/gpu/drm/xe/xe_bo_types.h
+++ b/drivers/gpu/drm/xe/xe_bo_types.h
@@ -90,8 +90,6 @@ struct xe_bo {
/** @freed: List node for delayed put. */
struct llist_node freed;
- /** @update_index: Update index if PT BO */
- int update_index;
/** @created: Whether the bo has passed initial creation */
bool created;
diff --git a/drivers/gpu/drm/xe/xe_configfs.c b/drivers/gpu/drm/xe/xe_configfs.c
index f5c828cf7e8f..ad4bfd3c0703 100644
--- a/drivers/gpu/drm/xe/xe_configfs.c
+++ b/drivers/gpu/drm/xe/xe_configfs.c
@@ -61,6 +61,7 @@
* ├── survivability_mode
* ├── gt_types_allowed
* ├── engines_allowed
+ * ├── migrate_ulls_period_ms
* ├── enable_psmi
* └── disable_vram_page_offline
*
@@ -262,6 +263,20 @@
*
* This attribute can only be set before binding to the device.
*
+ * Migrate ULLS Period (ms)
+ * ------------------------
+ *
+ * Migrate ULLS period, in milliseconds. This is the delay between entering
+ * migrate ULLS (a continuously running batch) and exiting it. Migrate ULLS is
+ * currently entered during page faults and SVM prefetch operations. Default 5,
+ * zero indicates ULLS is disabled.
+ *
+ * How to disable migration ULLS:
+ *
+ * # echo 0 > /sys/kernel/config/xe/0000:03:00.0/migrate_ulls_period_ms
+ *
+ * This attribute can only be set before binding to the device.
+ *
* Remove devices
* ==============
*
@@ -283,6 +298,7 @@ struct xe_config_group_device {
struct xe_config_device {
u64 gt_types_allowed;
u64 engines_allowed;
+ u32 migrate_ulls_period_ms;
struct wa_bb ctx_restore_post_bb[XE_ENGINE_CLASS_MAX];
struct wa_bb ctx_restore_mid_bb[XE_ENGINE_CLASS_MAX];
bool survivability_mode;
@@ -306,6 +322,7 @@ struct xe_config_group_device {
static const struct xe_config_device device_defaults = {
.gt_types_allowed = U64_MAX,
.engines_allowed = U64_MAX,
+ .migrate_ulls_period_ms = 5,
.survivability_mode = false,
.enable_psmi = false,
.enable_multi_queue = true,
@@ -631,6 +648,33 @@ static ssize_t enable_multi_queue_store(struct config_item *item, const char *pa
return len;
}
+static ssize_t migrate_ulls_period_ms_show(struct config_item *item, char *page)
+{
+ struct xe_config_device *dev = to_xe_config_device(item);
+
+ return sprintf(page, "%u\n", dev->migrate_ulls_period_ms);
+}
+
+static ssize_t migrate_ulls_period_ms_store(struct config_item *item,
+ const char *page, size_t len)
+{
+ struct xe_config_group_device *dev = to_xe_config_group_device(item);
+ u32 val;
+ int ret;
+
+ ret = kstrtou32(page, 0, &val);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&dev->lock);
+ if (is_bound(dev))
+ return -EBUSY;
+
+ dev->config.migrate_ulls_period_ms = val;
+
+ return len;
+}
+
static ssize_t disable_vram_page_offline_show(struct config_item *item, char *page)
{
struct xe_config_device *dev = to_xe_config_device(item);
@@ -896,6 +940,7 @@ static ssize_t ctx_restore_post_bb_store(struct config_item *item,
CONFIGFS_ATTR(, ctx_restore_mid_bb);
CONFIGFS_ATTR(, ctx_restore_post_bb);
CONFIGFS_ATTR(, enable_multi_queue);
+CONFIGFS_ATTR(, migrate_ulls_period_ms);
CONFIGFS_ATTR(, enable_psmi);
CONFIGFS_ATTR(, disable_vram_page_offline);
CONFIGFS_ATTR(, engines_allowed);
@@ -906,6 +951,7 @@ static struct configfs_attribute *xe_config_device_attrs[] = {
&attr_ctx_restore_mid_bb,
&attr_ctx_restore_post_bb,
&attr_enable_multi_queue,
+ &attr_migrate_ulls_period_ms,
&attr_enable_psmi,
&attr_disable_vram_page_offline,
&attr_engines_allowed,
@@ -1189,6 +1235,7 @@ static void dump_custom_dev_config(struct pci_dev *pdev,
PRI_CUSTOM_ATTR("%llx", gt_types_allowed);
PRI_CUSTOM_ATTR("%llx", engines_allowed);
+ PRI_CUSTOM_ATTR("%u", migrate_ulls_period_ms);
PRI_CUSTOM_ATTR("%d", enable_multi_queue);
PRI_CUSTOM_ATTR("%d", enable_psmi);
PRI_CUSTOM_ATTR("%d", disable_vram_page_offline);
@@ -1341,6 +1388,26 @@ bool xe_configfs_get_enable_multi_queue(struct pci_dev *pdev)
}
/**
+ * xe_configfs_get_migrate_ulls_period_ms - get configfs migrate_ulls_period_ms setting
+ * @pdev: pci device
+ *
+ * Return: Migrate ULLS period in milliseconds (zero is disabled).
+ */
+u32 xe_configfs_get_migrate_ulls_period_ms(struct pci_dev *pdev)
+{
+ struct xe_config_group_device *dev = find_xe_config_group_device(pdev);
+ u32 ret;
+
+ if (!dev)
+ return device_defaults.migrate_ulls_period_ms;
+
+ ret = dev->config.migrate_ulls_period_ms;
+ config_group_put(&dev->group);
+
+ return ret;
+}
+
+/**
* xe_configfs_get_disable_vram_page_offline - get configfs disable_vram_page_offline setting
* @pdev: pci device
*
diff --git a/drivers/gpu/drm/xe/xe_configfs.h b/drivers/gpu/drm/xe/xe_configfs.h
index 42cd1a491d01..10a00d6bbf8f 100644
--- a/drivers/gpu/drm/xe/xe_configfs.h
+++ b/drivers/gpu/drm/xe/xe_configfs.h
@@ -24,6 +24,7 @@ bool xe_configfs_media_gt_allowed(struct pci_dev *pdev);
u64 xe_configfs_get_engines_allowed(struct pci_dev *pdev);
bool xe_configfs_get_psmi_enabled(struct pci_dev *pdev);
bool xe_configfs_get_enable_multi_queue(struct pci_dev *pdev);
+u32 xe_configfs_get_migrate_ulls_period_ms(struct pci_dev *pdev);
bool xe_configfs_get_disable_vram_page_offline(struct pci_dev *pdev);
u32 xe_configfs_get_ctx_restore_mid_bb(struct pci_dev *pdev,
enum xe_engine_class class,
@@ -45,6 +46,7 @@ static inline bool xe_configfs_media_gt_allowed(struct pci_dev *pdev) { return t
static inline u64 xe_configfs_get_engines_allowed(struct pci_dev *pdev) { return U64_MAX; }
static inline bool xe_configfs_get_psmi_enabled(struct pci_dev *pdev) { return false; }
static inline bool xe_configfs_get_enable_multi_queue(struct pci_dev *pdev) { return true; }
+static inline u32 xe_configfs_get_migrate_ulls_period_ms(struct pci_dev *pdev) { return 5; }
static inline bool xe_configfs_get_disable_vram_page_offline(struct pci_dev *pdev) { return false; }
static inline u32 xe_configfs_get_ctx_restore_mid_bb(struct pci_dev *pdev,
enum xe_engine_class class,
diff --git a/drivers/gpu/drm/xe/xe_cpu_bind.c b/drivers/gpu/drm/xe/xe_cpu_bind.c
new file mode 100644
index 000000000000..318978298420
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_cpu_bind.c
@@ -0,0 +1,296 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2026 Intel Corporation
+ */
+
+#include <drm/drm_managed.h>
+#include <linux/mutex.h>
+
+#include "xe_cpu_bind.h"
+#include "xe_device_types.h"
+#include "xe_exec_queue.h"
+#include "xe_pt.h"
+#include "xe_sched_job.h"
+#include "xe_trace_bo.h"
+#include "xe_vm.h"
+
+/**
+ * struct xe_cpu_bind - cpu_bind context.
+ */
+struct xe_cpu_bind {
+ /** @xe: Xe device */
+ struct xe_device *xe;
+ /** @q: Default exec queue used for kernel binds */
+ struct xe_exec_queue *q;
+ /** @job_mutex: Timeline mutex for @q. */
+ struct mutex job_mutex;
+};
+
+static bool is_cpu_bind_queue(struct xe_cpu_bind *cpu_bind,
+ struct xe_exec_queue *q)
+{
+ return cpu_bind->q == q;
+}
+
+static void xe_cpu_bind_fini(void *arg)
+{
+ struct xe_cpu_bind *cpu_bind = arg;
+
+ mutex_destroy(&cpu_bind->job_mutex);
+ xe_exec_queue_put(cpu_bind->q);
+}
+
+/**
+ * xe_cpu_bind_init() - Initialize a cpu_bind context
+ * @xe: &struct xe_device
+ *
+ * Return: 0 if successful, negative error code on failure
+ */
+int xe_cpu_bind_init(struct xe_device *xe)
+{
+ struct xe_cpu_bind *cpu_bind =
+ drmm_kzalloc(&xe->drm, sizeof(*cpu_bind), GFP_KERNEL);
+ struct xe_exec_queue *q;
+
+ if (!cpu_bind)
+ return -ENOMEM;
+
+ q = xe_exec_queue_create_bind(xe, xe_device_get_root_tile(xe), NULL,
+ EXEC_QUEUE_FLAG_KERNEL |
+ EXEC_QUEUE_FLAG_MIGRATE, 0);
+ if (IS_ERR(q))
+ return PTR_ERR(q);
+
+ cpu_bind->xe = xe;
+ cpu_bind->q = q;
+ xe->cpu_bind = cpu_bind;
+
+ mutex_init(&cpu_bind->job_mutex);
+
+ fs_reclaim_acquire(GFP_KERNEL);
+ might_lock(&cpu_bind->job_mutex);
+ fs_reclaim_release(GFP_KERNEL);
+
+ return devm_add_action_or_reset(cpu_bind->xe->drm.dev, xe_cpu_bind_fini,
+ cpu_bind);
+}
+
+/**
+ * xe_cpu_bind_queue() - Get the bind queue from cpu_bind context.
+ * @cpu_bind: The cpu bind context.
+ *
+ * Return: Pointer to bind queue.
+ */
+struct xe_exec_queue *xe_cpu_bind_queue(struct xe_cpu_bind *cpu_bind)
+{
+ return cpu_bind->q;
+}
+
+/**
+ * xe_cpu_bind_update_pgtables_execute() - Update a VM's PTEs via the CPU
+ * @vm: The VM being updated
+ * @tile: The tile being updated
+ * @ops: The CPU bind PT update ops
+ * @pt_op: The VM PT update op
+ * @num_ops: The number of The VM PT update ops
+ * @force_clear: Force clear operation
+ *
+ * Execute the VM PT update ops array which results in a VM's PTEs being updated
+ * via the CPU.
+ */
+void
+xe_cpu_bind_update_pgtables_execute(struct xe_vm *vm, struct xe_tile *tile,
+ const struct xe_cpu_bind_pt_update_ops *ops,
+ struct xe_vm_pgtable_update_op *pt_op,
+ u32 num_ops, bool force_clear)
+{
+ u32 j, i;
+
+ for (j = 0; j < num_ops; ++j, ++pt_op) {
+ for (i = 0; i < pt_op->num_entries; i++) {
+ const struct xe_vm_pgtable_update *update =
+ &pt_op->entries[i];
+
+ xe_assert(vm->xe, !iosys_map_is_null(&update->pt_bo->vmap));
+
+ if (pt_op->bind && !force_clear)
+ ops->populate(tile, &update->pt_bo->vmap,
+ update);
+ else
+ ops->clear(vm, tile, &update->pt_bo->vmap,
+ update);
+ }
+ }
+
+ trace_xe_vm_cpu_bind(vm);
+ xe_device_wmb(vm->xe);
+}
+
+static struct dma_fence *
+xe_cpu_bind_update_pgtables_no_job(struct xe_cpu_bind *cpu_bind,
+ struct xe_cpu_bind_pt_update *pt_update)
+{
+ const struct xe_cpu_bind_pt_update_ops *ops = pt_update->ops;
+ struct xe_vm *vm = pt_update->vops->vm;
+ struct xe_tile *tile;
+ int err, id;
+
+ if (ops->pre_commit) {
+ pt_update->job = NULL;
+ err = ops->pre_commit(pt_update);
+ if (err)
+ return ERR_PTR(err);
+ }
+
+ for_each_tile(tile, vm->xe, id) {
+ struct xe_vm_pgtable_update_ops *pt_update_ops =
+ &pt_update->vops->pt_update_ops[tile->id];
+
+ if (!pt_update_ops->pt_job_ops)
+ continue;
+
+ xe_cpu_bind_update_pgtables_execute(vm, tile, ops,
+ pt_update_ops->pt_job_ops->ops,
+ pt_update_ops->pt_job_ops->current_op,
+ false);
+ }
+
+ return dma_fence_get_stub();
+}
+
+static struct dma_fence *
+xe_cpu_bind_update_pgtables_job(struct xe_cpu_bind *cpu_bind,
+ struct xe_cpu_bind_pt_update *pt_update)
+{
+ const struct xe_cpu_bind_pt_update_ops *ops = pt_update->ops;
+ struct xe_exec_queue *q = pt_update->vops->q;
+ struct xe_device *xe = cpu_bind->xe;
+ struct xe_sched_job *job;
+ struct dma_fence *fence;
+ struct xe_tile *tile;
+ int err, id;
+ bool is_cpu_bind = is_cpu_bind_queue(cpu_bind, q);
+
+ job = xe_sched_job_create(q, NULL);
+ if (IS_ERR(job))
+ return ERR_CAST(job);
+
+ xe_assert(xe, job->is_pt_job);
+
+ if (ops->pre_commit) {
+ pt_update->job = job;
+ err = ops->pre_commit(pt_update);
+ if (err)
+ goto err_job;
+ }
+
+ if (is_cpu_bind)
+ mutex_lock(&cpu_bind->job_mutex);
+
+ job->pt_update[0].vm = pt_update->vops->vm;
+ job->pt_update[0].ops = ops;
+ for_each_tile(tile, xe, id) {
+ struct xe_vm_pgtable_update_ops *pt_update_ops =
+ &pt_update->vops->pt_update_ops[tile->id];
+
+ job->pt_update[0].pt_job_ops[tile->id] =
+ xe_pt_job_ops_get(pt_update_ops->pt_job_ops);
+ }
+
+ xe_sched_job_arm(job);
+ fence = dma_fence_get(&job->drm.s_fence->finished);
+ xe_sched_job_push(job);
+
+ if (is_cpu_bind)
+ mutex_unlock(&cpu_bind->job_mutex);
+
+ return fence;
+
+err_job:
+ xe_sched_job_put(job);
+ return ERR_PTR(err);
+}
+
+/**
+ * xe_cpu_bind_update_pgtables() - Pipelined page-table update
+ * @cpu_bind: The cpu bind context.
+ * @pt_update: PT update arguments
+ *
+ * Perform a pipelined page-table update. The update descriptors are typically
+ * built under the same lock critical section as a call to this function. If
+ * using the default engine for the updates, they will be performed in the
+ * order they grab the job_mutex. If different engines are used, external
+ * synchronization is needed for overlapping updates to maintain page-table
+ * consistency. Note that the meaning of "overlapping" is that the updates
+ * touch the same page-table, which might be a higher-level page-directory.
+ * If no pipelining is needed, then updates may be performed by the cpu.
+ *
+ * Return: A dma_fence that, when signaled, indicates the update completion.
+ */
+struct dma_fence *
+xe_cpu_bind_update_pgtables(struct xe_cpu_bind *cpu_bind,
+ struct xe_cpu_bind_pt_update *pt_update)
+{
+ struct dma_fence *fence;
+
+ fence = xe_cpu_bind_update_pgtables_no_job(cpu_bind, pt_update);
+
+ /* -ETIME indicates a job is needed, anything else is legit error */
+ if (!IS_ERR(fence) || PTR_ERR(fence) != -ETIME)
+ return fence;
+
+ return xe_cpu_bind_update_pgtables_job(cpu_bind, pt_update);
+}
+
+/**
+ * xe_cpu_bind_job_lock() - Lock cpu_bind job lock
+ * @cpu_bind: The cpu bind context.
+ * @q: Queue associated with the operation which requires a lock
+ *
+ * Lock the cpu_bind job lock if the queue is a cpu bind queue, otherwise
+ * assert the VM's dma-resv is held (user queue's have own locking).
+ */
+void xe_cpu_bind_job_lock(struct xe_cpu_bind *cpu_bind,
+ struct xe_exec_queue *q)
+{
+ bool is_cpu_bind = is_cpu_bind_queue(cpu_bind, q);
+
+ if (is_cpu_bind)
+ mutex_lock(&cpu_bind->job_mutex);
+ else
+ xe_vm_assert_held(q->user_vm); /* User queues VM's should be locked */
+}
+
+/**
+ * xe_cpu_bind_job_unlock() - Unlock cpu_bind job lock
+ * @cpu_bind: The cpu bind context.
+ * @q: Queue associated with the operation which requires a lock
+ *
+ * Unlock the cpu_bind job lock if the queue is a cpu bind queue, otherwise
+ * assert the VM's dma-resv is held (user queue's have own locking).
+ */
+void xe_cpu_bind_job_unlock(struct xe_cpu_bind *cpu_bind,
+ struct xe_exec_queue *q)
+{
+ bool is_cpu_bind = is_cpu_bind_queue(cpu_bind, q);
+
+ if (is_cpu_bind)
+ mutex_unlock(&cpu_bind->job_mutex);
+ else
+ xe_vm_assert_held(q->user_vm); /* User queues VM's should be locked */
+}
+
+#if IS_ENABLED(CONFIG_PROVE_LOCKING)
+/**
+ * xe_cpu_bind_job_lock_assert() - Assert cpu_bind job lock held of queue
+ * @q: cpu bind queue
+ */
+void xe_cpu_bind_job_lock_assert(struct xe_exec_queue *q)
+{
+ struct xe_device *xe = gt_to_xe(q->gt);
+ struct xe_cpu_bind *cpu_bind = xe->cpu_bind;
+
+ xe_assert(xe, q == cpu_bind->q);
+ lockdep_assert_held(&cpu_bind->job_mutex);
+}
+#endif
diff --git a/drivers/gpu/drm/xe/xe_cpu_bind.h b/drivers/gpu/drm/xe/xe_cpu_bind.h
new file mode 100644
index 000000000000..69aad5edf204
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_cpu_bind.h
@@ -0,0 +1,111 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2026 Intel Corporation
+ */
+
+#ifndef _XE_CPU_BIND_H_
+#define _XE_CPU_BIND_H_
+
+#include <linux/types.h>
+
+struct dma_fence;
+struct iosys_map;
+struct xe_cpu_bind;
+struct xe_cpu_bind_pt_update;
+struct xe_device;
+struct xe_tlb_inval_job;
+struct xe_tile;
+struct xe_vm;
+struct xe_vm_pgtable_update;
+struct xe_vm_pgtable_update_op;
+struct xe_vma_ops;
+
+/**
+ * struct xe_cpu_bind_pt_update_ops - Callbacks for the
+ * xe_cpu_bind_update_pgtables() function.
+ */
+struct xe_cpu_bind_pt_update_ops {
+ /**
+ * @populate: Populate a page-table with ptes.
+ * @tile: The tile for the current operation.
+ * @map: struct iosys_map into the memory to be populated.
+ * @update: Information about the PTEs to be inserted.
+ *
+ * This interface is intended to be used as a callback into the
+ * page-table system to populate shared page-tables with PTEs.
+ */
+ void (*populate)(struct xe_tile *tile, struct iosys_map *map,
+ const struct xe_vm_pgtable_update *update);
+ /**
+ * @clear: Clear a page-table's ptes.
+ * @vm: VM being updated
+ * @tile: The tile for the current operation.
+ * @map: struct iosys_map into the page-table to be cleared.
+ * @update: Information about the PTEs to be cleared.
+ *
+ * This interface is intended to be used as a callback into the
+ * page-table system to clear PTEs from shared page-tables.
+ */
+ void (*clear)(struct xe_vm *vm, struct xe_tile *tile,
+ struct iosys_map *map,
+ const struct xe_vm_pgtable_update *update);
+
+ /**
+ * @pre_commit: Callback to be called just before arming the
+ * sched_job.
+ * @pt_update: Pointer to embeddable callback argument.
+ *
+ * Return: 0 on success, negative error code on error.
+ */
+ int (*pre_commit)(struct xe_cpu_bind_pt_update *pt_update);
+};
+
+/**
+ * struct xe_cpu_bind_pt_update - Argument to the struct
+ * xe_cpu_bind_pt_update_ops callbacks.
+ *
+ * Intended to be subclassed to support additional arguments if necessary.
+ */
+struct xe_cpu_bind_pt_update {
+ /** @ops: Pointer to the struct xe_cpu_bind_pt_update_ops callbacks */
+ const struct xe_cpu_bind_pt_update_ops *ops;
+ /** @vops: VMA operations */
+ struct xe_vma_ops *vops;
+ /** @job: The bind job, or NULL if the update is issued immediatel */
+ struct xe_sched_job *job;
+ /**
+ * @ijobs: The TLB invalidation jobs, individual instances can be NULL
+ */
+#define XE_CPU_BIND_INVAL_JOB_COUNT 4
+ struct xe_tlb_inval_job *ijobs[XE_CPU_BIND_INVAL_JOB_COUNT];
+};
+
+int xe_cpu_bind_init(struct xe_device *xe);
+
+struct xe_exec_queue *xe_cpu_bind_queue(struct xe_cpu_bind *cpu_bind);
+
+void
+xe_cpu_bind_update_pgtables_execute(struct xe_vm *vm, struct xe_tile *tile,
+ const struct xe_cpu_bind_pt_update_ops *ops,
+ struct xe_vm_pgtable_update_op *pt_op,
+ u32 num_ops, bool force_clear);
+
+struct dma_fence *
+xe_cpu_bind_update_pgtables(struct xe_cpu_bind *cpu_bind,
+ struct xe_cpu_bind_pt_update *pt_update);
+
+void xe_cpu_bind_job_lock(struct xe_cpu_bind *cpu_bind,
+ struct xe_exec_queue *q);
+
+void xe_cpu_bind_job_unlock(struct xe_cpu_bind *cpu_bind,
+ struct xe_exec_queue *q);
+
+#if IS_ENABLED(CONFIG_PROVE_LOCKING)
+void xe_cpu_bind_job_lock_assert(struct xe_exec_queue *q);
+#else
+static inline void xe_cpu_bind_job_lock_assert(struct xe_exec_queue *q)
+{
+}
+#endif
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_devcoredump.c b/drivers/gpu/drm/xe/xe_devcoredump.c
index b918f046ae15..ab681b148869 100644
--- a/drivers/gpu/drm/xe/xe_devcoredump.c
+++ b/drivers/gpu/drm/xe/xe_devcoredump.c
@@ -74,11 +74,6 @@ static struct xe_device *coredump_to_xe(const struct xe_devcoredump *coredump)
return container_of(coredump, struct xe_device, devcoredump);
}
-static struct xe_guc *exec_queue_to_guc(struct xe_exec_queue *q)
-{
- return &q->gt->uc.guc;
-}
-
static ssize_t __xe_devcoredump_read(char *buffer, ssize_t count,
ssize_t start,
struct xe_devcoredump *coredump)
@@ -325,40 +320,44 @@ static void xe_devcoredump_deferred_snap_work(struct work_struct *work)
}
static void devcoredump_snapshot(struct xe_devcoredump *coredump,
+ struct xe_gt *gt,
struct xe_exec_queue *q,
struct xe_sched_job *job)
{
struct xe_devcoredump_snapshot *ss = &coredump->snapshot;
- struct xe_guc *guc = exec_queue_to_guc(q);
+ struct xe_guc *guc = &gt->uc.guc;
const char *process_name = "no process";
bool cookie;
ss->snapshot_time = ktime_get_real();
ss->boot_time = ktime_get_boottime();
- if (q->vm && q->vm->xef) {
+ if (q && q->vm && q->vm->xef) {
process_name = q->vm->xef->process_name;
ss->pid = q->vm->xef->pid;
}
strscpy(ss->process_name, process_name);
- ss->gt = q->gt;
+ ss->gt = gt;
INIT_WORK(&ss->work, xe_devcoredump_deferred_snap_work);
/* keep going if fw fails as we still want to save the memory and SW data */
- CLASS(xe_force_wake, fw_ref)(gt_to_fw(q->gt), XE_FORCEWAKE_ALL);
+ CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FORCEWAKE_ALL);
cookie = dma_fence_begin_signalling();
ss->guc.log = xe_guc_log_snapshot_capture(&guc->log, true);
ss->guc.ct = xe_guc_ct_snapshot_capture(&guc->ct);
- ss->ge = xe_guc_exec_queue_snapshot_capture(q);
- if (job)
- ss->job = xe_sched_job_snapshot_capture(job);
- ss->vm = xe_vm_snapshot_capture(q->vm);
- xe_engine_snapshot_capture_for_queue(q);
+ if (q) {
+ ss->ge = xe_guc_exec_queue_snapshot_capture(q);
+ if (job)
+ ss->job = xe_sched_job_snapshot_capture(job);
+ ss->vm = xe_vm_snapshot_capture(q->vm);
+
+ xe_engine_snapshot_capture_for_queue(q);
+ }
queue_work(system_dfl_wq, &ss->work);
@@ -366,19 +365,24 @@ static void devcoredump_snapshot(struct xe_devcoredump *coredump,
}
/**
- * xe_devcoredump - Take the required snapshots and initialize coredump device.
- * @q: The faulty xe_exec_queue, where the issue was detected.
- * @job: The faulty xe_sched_job, where the issue was detected.
+ * __xe_devcoredump - Take the required snapshots and initialize coredump device.
+ * @gt: The GT where the issue was detected.
+ * @q: The faulty xe_exec_queue, where the issue was detected, may be NULL for
+ * hangs that are not tied to an exec queue (e.g. TLB invalidation
+ * timeouts); in that case only the GT-level state (GuC log and CT state)
+ * is captured.
+ * @job: The faulty xe_sched_job, where the issue was detected, may be NULL.
* @fmt: Printf format + args to describe the reason for the core dump
*
* This function should be called at the crash time within the serialized
* gt_reset. It is skipped if we still have the core dump device available
* with the information of the 'first' snapshot.
*/
-__printf(3, 4)
-void xe_devcoredump(struct xe_exec_queue *q, struct xe_sched_job *job, const char *fmt, ...)
+__printf(4, 5)
+void __xe_devcoredump(struct xe_gt *gt, struct xe_exec_queue *q,
+ struct xe_sched_job *job, const char *fmt, ...)
{
- struct xe_device *xe = gt_to_xe(q->gt);
+ struct xe_device *xe = gt_to_xe(gt);
struct xe_devcoredump *coredump = &xe->devcoredump;
va_list varg;
@@ -396,7 +400,7 @@ void xe_devcoredump(struct xe_exec_queue *q, struct xe_sched_job *job, const cha
coredump->snapshot.reason = kvasprintf(GFP_ATOMIC, fmt, varg);
va_end(varg);
- devcoredump_snapshot(coredump, q, job);
+ devcoredump_snapshot(coredump, gt, q, job);
drm_info(&xe->drm, "Xe device coredump has been created\n");
drm_info(&xe->drm, "Check your /sys/class/drm/card%d/device/devcoredump/data\n",
diff --git a/drivers/gpu/drm/xe/xe_devcoredump.h b/drivers/gpu/drm/xe/xe_devcoredump.h
index 5391a80a4d1b..bc4800b3a3f3 100644
--- a/drivers/gpu/drm/xe/xe_devcoredump.h
+++ b/drivers/gpu/drm/xe/xe_devcoredump.h
@@ -11,15 +11,17 @@
struct drm_printer;
struct xe_device;
struct xe_exec_queue;
+struct xe_gt;
struct xe_sched_job;
#ifdef CONFIG_DEV_COREDUMP
-void xe_devcoredump(struct xe_exec_queue *q, struct xe_sched_job *job, const char *fmt, ...);
+void __xe_devcoredump(struct xe_gt *gt, struct xe_exec_queue *q,
+ struct xe_sched_job *job, const char *fmt, ...);
int xe_devcoredump_init(struct xe_device *xe);
#else
-static inline void xe_devcoredump(struct xe_exec_queue *q,
- struct xe_sched_job *job,
- const char *fmt, ...)
+static inline void __xe_devcoredump(struct xe_gt *gt, struct xe_exec_queue *q,
+ struct xe_sched_job *job,
+ const char *fmt, ...)
{
}
@@ -29,6 +31,11 @@ static inline int xe_devcoredump_init(struct xe_device *xe)
}
#endif
+#define xe_devcoredump(_q, _job, _fmt, ...) \
+ __xe_devcoredump((_q)->gt, _q, _job, _fmt, ##__VA_ARGS__)
+#define xe_devcoredump_gt(_gt, _fmt, ...) \
+ __xe_devcoredump(_gt, NULL, NULL, _fmt, ##__VA_ARGS__)
+
void xe_print_blob_ascii85(struct drm_printer *p, const char *prefix, char suffix,
const void *blob, size_t offset, size_t size);
diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c
index 205cb4e7f9e8..e1979bbcf7bc 100644
--- a/drivers/gpu/drm/xe/xe_device.c
+++ b/drivers/gpu/drm/xe/xe_device.c
@@ -27,6 +27,7 @@
#include "xe_bo_evict.h"
#include "xe_configfs.h"
#include "xe_debugfs.h"
+#include "xe_cpu_bind.h"
#include "xe_defaults.h"
#include "xe_devcoredump.h"
#include "xe_device_sysfs.h"
@@ -671,6 +672,7 @@ static void vf_update_device_info(struct xe_device *xe)
xe->info.skip_guc_pc = 1;
xe->info.skip_pcode = 1;
xe->info.has_drm_ras = false;
+ xe->info.has_device_uid = false;
}
static int xe_device_vram_alloc(struct xe_device *xe)
@@ -871,6 +873,12 @@ static int xe_debug_page_size_alloc_ctrl_init(struct xe_device *xe)
}
#endif
+static void xe_uid_probe(struct xe_device *xe)
+{
+ if (xe->info.has_device_uid)
+ xe->device_uid = xe_mmio_read64_2x32(xe_root_tile_mmio(xe), CRI_DEVICE_UID);
+}
+
int xe_device_probe(struct xe_device *xe)
{
struct xe_tile *tile;
@@ -878,6 +886,8 @@ int xe_device_probe(struct xe_device *xe)
int err;
u8 id;
+ xe_uid_probe(xe);
+
xe_pat_init_early(xe);
err = xe_sriov_init(xe);
@@ -993,6 +1003,10 @@ int xe_device_probe(struct xe_device *xe)
if (err)
return err;
+ err = xe_cpu_bind_init(xe);
+ if (err)
+ return err;
+
err = xe_pagefault_init(xe);
if (err)
return err;
diff --git a/drivers/gpu/drm/xe/xe_device_sysfs.c b/drivers/gpu/drm/xe/xe_device_sysfs.c
index a73e0e957cb0..18783a286a83 100644
--- a/drivers/gpu/drm/xe/xe_device_sysfs.c
+++ b/drivers/gpu/drm/xe/xe_device_sysfs.c
@@ -8,6 +8,7 @@
#include <linux/pci.h>
#include <linux/sysfs.h>
+#include "regs/xe_regs.h"
#include "xe_device.h"
#include "xe_device_sysfs.h"
#include "xe_mmio.h"
@@ -264,6 +265,35 @@ static const struct attribute_group auto_link_downgrade_attr_group = {
.attrs = auto_link_downgrade_attrs,
};
+/**
+ * DOC: Device Unique ID
+ *
+ * On supported platforms, Xe driver exposes a unique 64-bit GPU SOC
+ * device identifier through the 'device_uid' sysfs entry.
+ *
+ * See Documentation/ABI/testing/sysfs-driver-intel-xe-gpu for the ABI
+ * specification.
+ */
+
+static ssize_t
+device_uid_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct xe_device *xe = pdev_to_xe_device(pdev);
+
+ return sysfs_emit(buf, "0x%016llx\n", xe->device_uid);
+}
+static DEVICE_ATTR_ADMIN_RO(device_uid);
+
+static struct attribute *device_uid_attrs[] = {
+ &dev_attr_device_uid.attr,
+ NULL
+};
+
+static const struct attribute_group device_uid_attr_group = {
+ .attrs = device_uid_attrs,
+};
+
int xe_device_sysfs_init(struct xe_device *xe)
{
struct device *dev = xe->drm.dev;
@@ -285,5 +315,11 @@ int xe_device_sysfs_init(struct xe_device *xe)
return ret;
}
+ if (xe->info.has_device_uid) {
+ ret = devm_device_add_group(dev, &device_uid_attr_group);
+ if (ret)
+ return ret;
+ }
+
return 0;
}
diff --git a/drivers/gpu/drm/xe/xe_device_types.h b/drivers/gpu/drm/xe/xe_device_types.h
index 4661bfce2f4e..4e0e1bc040c1 100644
--- a/drivers/gpu/drm/xe/xe_device_types.h
+++ b/drivers/gpu/drm/xe/xe_device_types.h
@@ -38,6 +38,7 @@
struct drm_pagemap_shrinker;
struct intel_display;
struct intel_dg_nvm_dev;
+struct xe_cpu_bind;
struct xe_ggtt;
struct xe_i2c;
struct xe_mmio_gem;
@@ -89,9 +90,7 @@ enum xe_page_size_alloc_ctrl_mode {
#define XE_VRAM_FLAGS_NEED64K BIT(0)
-#define XE_GT0 0
-#define XE_GT1 1
-#define XE_MAX_TILES_PER_DEVICE (XE_GT1 + 1)
+#define XE_MAX_TILES_PER_DEVICE 2
/*
* Highest GT/tile count for any platform. Used only for memory allocation
@@ -180,6 +179,8 @@ struct xe_device {
u8 has_cached_pt:1;
/** @info.has_device_atomics_on_smem: Supports device atomics on SMEM */
u8 has_device_atomics_on_smem:1;
+ /** @info.has_device_uid: Device supports unique 64-bit GPU SOC ID */
+ u8 has_device_uid:1;
/** @info.has_drm_ras: Device supports drm_ras (Reliability, Availability, Serviceability) */
u8 has_drm_ras:1;
/** @info.has_fan_control: Device supports fan control */
@@ -230,6 +231,8 @@ struct xe_device {
u8 has_usm:1;
/** @info.has_64bit_timestamp: Device supports 64-bit timestamps */
u8 has_64bit_timestamp:1;
+ /** @info.has_pt_mirror: Device has PT mirroring across tiles */
+ u8 has_pt_mirror:1;
/** @info.is_dgfx: is discrete device */
u8 is_dgfx:1;
/** @info.needs_scratch: needs scratch page for oob prefetch to work */
@@ -264,6 +267,9 @@ struct xe_device {
bool oob_initialized;
} wa_active;
+ /** @device_uid: unique 64-bit GPU SOC identifier */
+ u64 device_uid;
+
/** @survivability: survivability information for device */
struct xe_survivability survivability;
@@ -566,6 +572,9 @@ struct xe_device {
/** @sc: System Controller */
struct xe_sysctrl sc;
+ /** @cpu_bind: CPU bind object */
+ struct xe_cpu_bind *cpu_bind;
+
/** @atomic_svm_timeslice_ms: Atomic SVM fault timeslice MS */
u32 atomic_svm_timeslice_ms;
diff --git a/drivers/gpu/drm/xe/xe_drm_client.c b/drivers/gpu/drm/xe/xe_drm_client.c
index e116fb562c4c..4c424d1c6721 100644
--- a/drivers/gpu/drm/xe/xe_drm_client.c
+++ b/drivers/gpu/drm/xe/xe_drm_client.c
@@ -256,7 +256,7 @@ static void show_meminfo(struct drm_printer *p, struct drm_file *file)
xe_assert(xef->xe, !list_empty(&bo->client_link));
}
- xe_bo_put_deferred(bo, &deferred);
+ xe_bo_put_deferred(bo, &deferred, NULL);
}
spin_unlock(&client->bos_lock);
diff --git a/drivers/gpu/drm/xe/xe_exec_queue.c b/drivers/gpu/drm/xe/xe_exec_queue.c
index e63559a2f582..9a8efa2bf2bf 100644
--- a/drivers/gpu/drm/xe/xe_exec_queue.c
+++ b/drivers/gpu/drm/xe/xe_exec_queue.c
@@ -14,6 +14,7 @@
#include <uapi/drm/xe_drm.h>
#include "xe_bo.h"
+#include "xe_cpu_bind.h"
#include "xe_dep_scheduler.h"
#include "xe_device.h"
#include "xe_gt.h"
@@ -142,9 +143,8 @@ static void __xe_exec_queue_free(struct xe_exec_queue *q)
{
int i;
- for (i = 0; i < XE_EXEC_QUEUE_TLB_INVAL_COUNT; ++i)
- if (q->tlb_inval[i].dep_scheduler)
- xe_dep_scheduler_fini(q->tlb_inval[i].dep_scheduler);
+ for_each_tlb_inval(q, i)
+ xe_dep_scheduler_fini(q->tlb_inval[i].dep_scheduler);
if (xe_exec_queue_uses_pxp(q))
xe_pxp_exec_queue_remove(gt_to_xe(q->gt)->pxp, q);
@@ -166,31 +166,34 @@ static void __xe_exec_queue_free(struct xe_exec_queue *q)
static int alloc_dep_schedulers(struct xe_device *xe, struct xe_exec_queue *q)
{
- struct xe_tile *tile = gt_to_tile(q->gt);
- int i;
+ struct xe_tile *tile;
+ int i = 0, j;
+ u8 id;
- for (i = 0; i < XE_EXEC_QUEUE_TLB_INVAL_COUNT; ++i) {
- struct xe_dep_scheduler *dep_scheduler;
- struct xe_gt *gt;
- struct workqueue_struct *wq;
+ for_each_tile(tile, xe, id) {
+ for (j = 0; j < (XE_EXEC_QUEUE_TLB_INVAL_MEDIA_GT + 1); ++j, ++i) {
+ struct xe_dep_scheduler *dep_scheduler;
+ struct xe_gt *gt;
+ struct workqueue_struct *wq;
- if (i == XE_EXEC_QUEUE_TLB_INVAL_PRIMARY_GT)
- gt = tile->primary_gt;
- else
- gt = tile->media_gt;
+ if (j == XE_EXEC_QUEUE_TLB_INVAL_PRIMARY_GT)
+ gt = tile->primary_gt;
+ else
+ gt = tile->media_gt;
- if (!gt)
- continue;
+ if (!gt)
+ continue;
- wq = gt->tlb_inval.job_wq;
+ wq = gt->tlb_inval.job_wq;
#define MAX_TLB_INVAL_JOBS 16 /* Picking a reasonable value */
- dep_scheduler = xe_dep_scheduler_create(xe, wq, q->name,
- MAX_TLB_INVAL_JOBS);
- if (IS_ERR(dep_scheduler))
- return PTR_ERR(dep_scheduler);
+ dep_scheduler = xe_dep_scheduler_create(xe, wq, q->name,
+ MAX_TLB_INVAL_JOBS);
+ if (IS_ERR(dep_scheduler))
+ return PTR_ERR(dep_scheduler);
- q->tlb_inval[i].dep_scheduler = dep_scheduler;
+ q->tlb_inval[i].dep_scheduler = dep_scheduler;
+ }
}
#undef MAX_TLB_INVAL_JOBS
@@ -224,7 +227,6 @@ static struct xe_exec_queue *__xe_exec_queue_alloc(struct xe_device *xe,
q->ops = gt->exec_queue_ops;
INIT_LIST_HEAD(&q->lr.link);
INIT_LIST_HEAD(&q->vm_exec_queue_link);
- INIT_LIST_HEAD(&q->multi_gt_link);
INIT_LIST_HEAD(&q->hw_engine_group_link);
INIT_LIST_HEAD(&q->pxp.link);
spin_lock_init(&q->multi_queue.lock);
@@ -636,7 +638,6 @@ ALLOW_ERROR_INJECTION(xe_exec_queue_create_bind, ERRNO);
void xe_exec_queue_destroy(struct kref *ref)
{
struct xe_exec_queue *q = container_of(ref, struct xe_exec_queue, refcount);
- struct xe_exec_queue *eq, *next;
int i;
xe_assert(gt_to_xe(q->gt), atomic_read(&q->job_cnt) == 0);
@@ -648,15 +649,9 @@ void xe_exec_queue_destroy(struct kref *ref)
xe_pxp_exec_queue_remove(gt_to_xe(q->gt)->pxp, q);
xe_exec_queue_last_fence_put_unlocked(q);
- for_each_tlb_inval(i)
+ for_each_tlb_inval(q, i)
xe_exec_queue_tlb_inval_last_fence_put_unlocked(q, i);
- if (!(q->flags & EXEC_QUEUE_FLAG_BIND_ENGINE_CHILD)) {
- list_for_each_entry_safe(eq, next, &q->multi_gt_list,
- multi_gt_link)
- xe_exec_queue_put(eq);
- }
-
if (q->user_vm) {
xe_vm_put(q->user_vm);
q->user_vm = NULL;
@@ -1343,7 +1338,6 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data,
u64_to_user_ptr(args->instances);
struct xe_hw_engine *hwe;
struct xe_vm *vm;
- struct xe_tile *tile;
struct xe_exec_queue *q = NULL;
u32 logical_mask;
u32 flags = 0;
@@ -1392,31 +1386,16 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data,
return -ENOENT;
}
- for_each_tile(tile, xe, id) {
- struct xe_exec_queue *new;
-
- flags |= EXEC_QUEUE_FLAG_VM;
- if (id)
- flags |= EXEC_QUEUE_FLAG_BIND_ENGINE_CHILD;
-
- new = xe_exec_queue_create_bind(xe, tile, vm, flags,
- args->extensions);
- if (IS_ERR(new)) {
- up_read(&vm->lock);
- xe_vm_put(vm);
- err = PTR_ERR(new);
- if (q)
- goto put_exec_queue;
- return err;
- }
- if (id == 0)
- q = new;
- else
- list_add_tail(&new->multi_gt_list,
- &q->multi_gt_link);
- }
+ flags |= EXEC_QUEUE_FLAG_VM;
+
+ q = xe_exec_queue_create_bind(xe, xe_device_get_root_tile(xe),
+ vm, flags, args->extensions);
up_read(&vm->lock);
xe_vm_put(vm);
+ if (IS_ERR(q)) {
+ err = PTR_ERR(q);
+ return err;
+ }
} else {
logical_mask = calc_validate_logical_mask(xe, eci,
args->width,
@@ -1567,6 +1546,7 @@ bool xe_exec_queue_is_lr(struct xe_exec_queue *q)
/**
* xe_exec_queue_is_idle() - Whether an exec_queue is idle.
* @q: The exec_queue
+ * @extra_jobs: Extra jobs on the queue
*
* FIXME: Need to determine what to use as the short-lived
* timeline lock for the exec_queues, so that the return value
@@ -1578,22 +1558,9 @@ bool xe_exec_queue_is_lr(struct xe_exec_queue *q)
*
* Return: True if the exec_queue is idle, false otherwise.
*/
-bool xe_exec_queue_is_idle(struct xe_exec_queue *q)
+bool xe_exec_queue_is_idle(struct xe_exec_queue *q, int extra_jobs)
{
- if (xe_exec_queue_is_parallel(q)) {
- int i;
-
- for (i = 0; i < q->width; ++i) {
- if (xe_lrc_seqno(q->lrc[i]) !=
- q->lrc[i]->fence_ctx.next_seqno - 1)
- return false;
- }
-
- return true;
- }
-
- return xe_lrc_seqno(q->lrc[0]) ==
- q->lrc[0]->fence_ctx.next_seqno - 1;
+ return !(atomic_read(&q->job_cnt) - extra_jobs);
}
/**
@@ -1651,14 +1618,6 @@ void xe_exec_queue_update_run_ticks(struct xe_exec_queue *q)
*/
void xe_exec_queue_kill(struct xe_exec_queue *q)
{
- struct xe_exec_queue *eq = q, *next;
-
- list_for_each_entry_safe(eq, next, &eq->multi_gt_list,
- multi_gt_link) {
- q->ops->kill(eq);
- xe_vm_remove_compute_exec_queue(q->vm, eq);
- }
-
q->ops->kill(q);
xe_vm_remove_compute_exec_queue(q->vm, q);
}
@@ -1709,7 +1668,7 @@ static void xe_exec_queue_last_fence_lockdep_assert(struct xe_exec_queue *q,
struct xe_vm *vm)
{
if (q->flags & EXEC_QUEUE_FLAG_MIGRATE) {
- xe_migrate_job_lock_assert(q);
+ xe_cpu_bind_job_lock_assert(q);
} else if (q->flags & EXEC_QUEUE_FLAG_VM) {
lockdep_assert_held(&vm->lock);
} else {
@@ -1819,42 +1778,40 @@ void xe_exec_queue_last_fence_set(struct xe_exec_queue *q, struct xe_vm *vm,
* xe_exec_queue_tlb_inval_last_fence_put() - Drop ref to last TLB invalidation fence
* @q: The exec queue
* @vm: The VM the engine does a bind for
- * @type: Either primary or media GT
+ * @idx: Index of tlb invalidation
*/
void xe_exec_queue_tlb_inval_last_fence_put(struct xe_exec_queue *q,
struct xe_vm *vm,
- unsigned int type)
+ unsigned int idx)
{
xe_exec_queue_last_fence_lockdep_assert(q, vm);
- xe_assert(vm->xe, type == XE_EXEC_QUEUE_TLB_INVAL_MEDIA_GT ||
- type == XE_EXEC_QUEUE_TLB_INVAL_PRIMARY_GT);
+ xe_assert(vm->xe, idx < XE_EXEC_QUEUE_TLB_INVAL_COUNT);
- xe_exec_queue_tlb_inval_last_fence_put_unlocked(q, type);
+ xe_exec_queue_tlb_inval_last_fence_put_unlocked(q, idx);
}
/**
* xe_exec_queue_tlb_inval_last_fence_put_unlocked() - Drop ref to last TLB
* invalidation fence unlocked
* @q: The exec queue
- * @type: Either primary or media GT
+ * @idx: Index of tlb invalidation
*
* Only safe to be called from xe_exec_queue_destroy().
*/
void xe_exec_queue_tlb_inval_last_fence_put_unlocked(struct xe_exec_queue *q,
- unsigned int type)
+ unsigned int idx)
{
- xe_assert(gt_to_xe(q->gt), type == XE_EXEC_QUEUE_TLB_INVAL_MEDIA_GT ||
- type == XE_EXEC_QUEUE_TLB_INVAL_PRIMARY_GT);
+ xe_assert(gt_to_xe(q->gt), idx < XE_EXEC_QUEUE_TLB_INVAL_COUNT);
- dma_fence_put(q->tlb_inval[type].last_fence);
- q->tlb_inval[type].last_fence = NULL;
+ dma_fence_put(q->tlb_inval[idx].last_fence);
+ q->tlb_inval[idx].last_fence = NULL;
}
/**
* xe_exec_queue_tlb_inval_last_fence_get() - Get last fence for TLB invalidation
* @q: The exec queue
* @vm: The VM the engine does a bind for
- * @type: Either primary or media GT
+ * @idx: Index of tlb invalidation
*
* Get last fence, takes a ref
*
@@ -1862,22 +1819,21 @@ void xe_exec_queue_tlb_inval_last_fence_put_unlocked(struct xe_exec_queue *q,
*/
struct dma_fence *xe_exec_queue_tlb_inval_last_fence_get(struct xe_exec_queue *q,
struct xe_vm *vm,
- unsigned int type)
+ unsigned int idx)
{
struct dma_fence *fence;
xe_exec_queue_last_fence_lockdep_assert(q, vm);
- xe_assert(vm->xe, type == XE_EXEC_QUEUE_TLB_INVAL_MEDIA_GT ||
- type == XE_EXEC_QUEUE_TLB_INVAL_PRIMARY_GT);
+ xe_assert(vm->xe, idx < XE_EXEC_QUEUE_TLB_INVAL_COUNT);
xe_assert(vm->xe, q->flags & (EXEC_QUEUE_FLAG_VM |
EXEC_QUEUE_FLAG_MIGRATE));
- if (q->tlb_inval[type].last_fence &&
+ if (q->tlb_inval[idx].last_fence &&
test_bit(DMA_FENCE_FLAG_SIGNALED_BIT,
- &q->tlb_inval[type].last_fence->flags))
- xe_exec_queue_tlb_inval_last_fence_put(q, vm, type);
+ &q->tlb_inval[idx].last_fence->flags))
+ xe_exec_queue_tlb_inval_last_fence_put(q, vm, idx);
- fence = q->tlb_inval[type].last_fence ?: dma_fence_get_stub();
+ fence = q->tlb_inval[idx].last_fence ?: dma_fence_get_stub();
dma_fence_get(fence);
return fence;
}
@@ -1887,26 +1843,25 @@ struct dma_fence *xe_exec_queue_tlb_inval_last_fence_get(struct xe_exec_queue *q
* @q: The exec queue
* @vm: The VM the engine does a bind for
* @fence: The fence
- * @type: Either primary or media GT
+ * @idx: Index of tlb invalidation
*
- * Set the last fence for the tlb invalidation type on the queue. Increases
+ * Set the last fence for the tlb invalidation client on the queue. Increases
* reference count for fence, when closing queue
* xe_exec_queue_tlb_inval_last_fence_put should be called.
*/
void xe_exec_queue_tlb_inval_last_fence_set(struct xe_exec_queue *q,
struct xe_vm *vm,
struct dma_fence *fence,
- unsigned int type)
+ unsigned int idx)
{
xe_exec_queue_last_fence_lockdep_assert(q, vm);
- xe_assert(vm->xe, type == XE_EXEC_QUEUE_TLB_INVAL_MEDIA_GT ||
- type == XE_EXEC_QUEUE_TLB_INVAL_PRIMARY_GT);
+ xe_assert(vm->xe, idx < XE_EXEC_QUEUE_TLB_INVAL_COUNT);
xe_assert(vm->xe, q->flags & (EXEC_QUEUE_FLAG_VM |
EXEC_QUEUE_FLAG_MIGRATE));
xe_assert(vm->xe, !dma_fence_is_container(fence));
- xe_exec_queue_tlb_inval_last_fence_put(q, vm, type);
- q->tlb_inval[type].last_fence = dma_fence_get(fence);
+ xe_exec_queue_tlb_inval_last_fence_put(q, vm, idx);
+ q->tlb_inval[idx].last_fence = dma_fence_get(fence);
}
/**
diff --git a/drivers/gpu/drm/xe/xe_exec_queue.h b/drivers/gpu/drm/xe/xe_exec_queue.h
index 0225426c57b0..e8963f85cabd 100644
--- a/drivers/gpu/drm/xe/xe_exec_queue.h
+++ b/drivers/gpu/drm/xe/xe_exec_queue.h
@@ -14,9 +14,9 @@ struct drm_file;
struct xe_device;
struct xe_file;
-#define for_each_tlb_inval(__i) \
- for (__i = XE_EXEC_QUEUE_TLB_INVAL_PRIMARY_GT; \
- __i <= XE_EXEC_QUEUE_TLB_INVAL_MEDIA_GT; ++__i)
+#define for_each_tlb_inval(__q, __i) \
+ for (__i = 0; __i < XE_EXEC_QUEUE_TLB_INVAL_COUNT; ++__i) \
+ for_each_if((__q)->tlb_inval[__i].dep_scheduler)
struct xe_exec_queue *xe_exec_queue_create(struct xe_device *xe, struct xe_vm *vm,
u32 logical_mask, u16 width,
@@ -116,7 +116,7 @@ static inline struct xe_exec_queue *xe_exec_queue_multi_queue_primary(struct xe_
bool xe_exec_queue_is_lr(struct xe_exec_queue *q);
-bool xe_exec_queue_is_idle(struct xe_exec_queue *q);
+bool xe_exec_queue_is_idle(struct xe_exec_queue *q, int extra_jobs);
void xe_exec_queue_kill(struct xe_exec_queue *q);
@@ -141,19 +141,19 @@ void xe_exec_queue_last_fence_set(struct xe_exec_queue *e, struct xe_vm *vm,
void xe_exec_queue_tlb_inval_last_fence_put(struct xe_exec_queue *q,
struct xe_vm *vm,
- unsigned int type);
+ unsigned int idx);
void xe_exec_queue_tlb_inval_last_fence_put_unlocked(struct xe_exec_queue *q,
- unsigned int type);
+ unsigned int idx);
struct dma_fence *xe_exec_queue_tlb_inval_last_fence_get(struct xe_exec_queue *q,
struct xe_vm *vm,
- unsigned int type);
+ unsigned int idx);
void xe_exec_queue_tlb_inval_last_fence_set(struct xe_exec_queue *q,
struct xe_vm *vm,
struct dma_fence *fence,
- unsigned int type);
+ unsigned int idx);
void xe_exec_queue_update_run_ticks(struct xe_exec_queue *q);
diff --git a/drivers/gpu/drm/xe/xe_exec_queue_types.h b/drivers/gpu/drm/xe/xe_exec_queue_types.h
index 836f88fc0faa..48c05398b016 100644
--- a/drivers/gpu/drm/xe/xe_exec_queue_types.h
+++ b/drivers/gpu/drm/xe/xe_exec_queue_types.h
@@ -137,16 +137,14 @@ struct xe_exec_queue {
#define EXEC_QUEUE_FLAG_KERNEL BIT(0)
/* for VM jobs. Caller needs to hold rpm ref when creating queue with this flag */
#define EXEC_QUEUE_FLAG_VM BIT(1)
-/* child of VM queue for multi-tile VM jobs */
-#define EXEC_QUEUE_FLAG_BIND_ENGINE_CHILD BIT(2)
/* kernel exec_queue only, set priority to highest level */
-#define EXEC_QUEUE_FLAG_HIGH_PRIORITY BIT(3)
+#define EXEC_QUEUE_FLAG_HIGH_PRIORITY BIT(2)
/* flag to indicate low latency hint to guc */
-#define EXEC_QUEUE_FLAG_LOW_LATENCY BIT(4)
+#define EXEC_QUEUE_FLAG_LOW_LATENCY BIT(3)
/* for migration (kernel copy, clear, bind) jobs */
-#define EXEC_QUEUE_FLAG_MIGRATE BIT(5)
+#define EXEC_QUEUE_FLAG_MIGRATE BIT(4)
/* for programming COMMON_SLICE_CHICKEN3 on first submission */
-#define EXEC_QUEUE_FLAG_DISABLE_STATE_CACHE_PERF_FIX BIT(6)
+#define EXEC_QUEUE_FLAG_DISABLE_STATE_CACHE_PERF_FIX BIT(5)
/**
* @flags: flags for this exec queue, should statically setup aside from ban
@@ -158,13 +156,6 @@ struct xe_exec_queue {
atomic_t ban_reason;
union {
- /** @multi_gt_list: list head for VM bind engines if multi-GT */
- struct list_head multi_gt_list;
- /** @multi_gt_link: link for VM bind engines if multi-GT */
- struct list_head multi_gt_link;
- };
-
- union {
/** @execlist: execlist backend specific state for exec queue */
struct xe_execlist_exec_queue *execlist;
/** @guc: GuC backend specific state for exec queue */
@@ -230,7 +221,8 @@ struct xe_exec_queue {
#define XE_EXEC_QUEUE_TLB_INVAL_PRIMARY_GT 0
#define XE_EXEC_QUEUE_TLB_INVAL_MEDIA_GT 1
-#define XE_EXEC_QUEUE_TLB_INVAL_COUNT (XE_EXEC_QUEUE_TLB_INVAL_MEDIA_GT + 1)
+#define XE_EXEC_QUEUE_TLB_INVAL_COUNT \
+ ((XE_EXEC_QUEUE_TLB_INVAL_MEDIA_GT + 1) * 2)
/** @tlb_inval: TLB invalidations exec queue state */
struct {
diff --git a/drivers/gpu/drm/xe/xe_gt_ccs_mode.c b/drivers/gpu/drm/xe/xe_gt_ccs_mode.c
index baee1f4a6b01..224a15675203 100644
--- a/drivers/gpu/drm/xe/xe_gt_ccs_mode.c
+++ b/drivers/gpu/drm/xe/xe_gt_ccs_mode.c
@@ -70,12 +70,6 @@ static void __xe_gt_apply_ccs_mode(struct xe_gt *gt, u32 num_engines)
}
}
- /*
- * Mask bits need to be set for the register. Though only Xe2+
- * platforms require setting of mask bits, it won't harm for older
- * platforms as these bits are unused there.
- */
- mode |= CCS_MODE_CSLICE_0_3_MASK << 16;
xe_mmio_write32(&gt->mmio, CCS_MODE, mode);
xe_gt_dbg(gt, "CCS_MODE=%x config:%08x, num_engines:%d, num_slices:%d\n",
diff --git a/drivers/gpu/drm/xe/xe_guc.c b/drivers/gpu/drm/xe/xe_guc.c
index c7f8bbd4cb92..3ab4cb9e4964 100644
--- a/drivers/gpu/drm/xe/xe_guc.c
+++ b/drivers/gpu/drm/xe/xe_guc.c
@@ -1469,6 +1469,22 @@ int xe_guc_suspend(struct xe_guc *guc)
return 0;
}
+/**
+ * xe_guc_bo_wa_flags - Extra BO flags for memory shared with the GuC
+ * @gt: the &xe_gt whose GuC the buffer will be shared with
+ *
+ * Wa_22016122933: on the standalone media GT, memory shared between the
+ * CPU and the GuC must not be mapped cached on the CPU side, otherwise
+ * the CPU can read stale data written by the GuC (e.g. G2H CTB writes)
+ * for multiple seconds.
+ *
+ * Return: additional XE_BO_FLAG_* to use when allocating GuC-shared memory
+ */
+u32 xe_guc_bo_wa_flags(struct xe_gt *gt)
+{
+ return XE_GT_WA(gt, 22016122933) ? XE_BO_FLAG_NEEDS_UC : 0;
+}
+
void xe_guc_notify(struct xe_guc *guc)
{
struct xe_gt *gt = guc_to_gt(guc);
diff --git a/drivers/gpu/drm/xe/xe_guc.h b/drivers/gpu/drm/xe/xe_guc.h
index 61e3ee19a59b..c4eca40d69cf 100644
--- a/drivers/gpu/drm/xe/xe_guc.h
+++ b/drivers/gpu/drm/xe/xe_guc.h
@@ -30,6 +30,7 @@
xe_guc_fw_version_at_least((guc), MAKE_GUC_VER_ARGS(ver))
struct drm_printer;
+struct xe_gt;
void xe_guc_comm_init_early(struct xe_guc *guc);
int xe_guc_init_noalloc(struct xe_guc *guc);
@@ -45,6 +46,7 @@ void xe_guc_runtime_suspend(struct xe_guc *guc);
void xe_guc_runtime_resume(struct xe_guc *guc);
int xe_guc_suspend(struct xe_guc *guc);
int xe_guc_softreset(struct xe_guc *guc);
+u32 xe_guc_bo_wa_flags(struct xe_gt *gt);
void xe_guc_notify(struct xe_guc *guc);
int xe_guc_auth_huc(struct xe_guc *guc, u32 rsa_addr);
int xe_guc_mmio_send(struct xe_guc *guc, const u32 *request, u32 len);
diff --git a/drivers/gpu/drm/xe/xe_guc_ads.c b/drivers/gpu/drm/xe/xe_guc_ads.c
index ff8eee3831aa..43a2951a66d6 100644
--- a/drivers/gpu/drm/xe/xe_guc_ads.c
+++ b/drivers/gpu/drm/xe/xe_guc_ads.c
@@ -383,6 +383,12 @@ static void guc_waklv_init(struct xe_guc_ads *ads)
guc_waklv_enable(ads, NULL, 0, &offset, &remain,
GUC_WA_KLV_IGNORE_MMIO_READ_SEM_TOKEN_64);
+ /* GuC only applies this WA for scheduled MultiQ contexts; no KMD-side gating needed. */
+ if (XE_GT_WA(gt, 16030224309) &&
+ GUC_FIRMWARE_VER_AT_LEAST(&gt->uc.guc, 70, 73))
+ guc_waklv_enable(ads, NULL, 0, &offset, &remain,
+ GUC_WA_KLV_IGNORE_MULTIQ_CTX_COMPLETE_WITH_PREEMPT_IN_CSB);
+
/*
* On GuC firmware 70.66 and above, use the Feature KLV (shared with the
* WA KLV buffer); older firmware uses GUC_CTL_DISABLE_MULTI_QUEUE in
@@ -435,7 +441,8 @@ int xe_guc_ads_init(struct xe_guc_ads *ads)
XE_BO_FLAG_SYSTEM |
XE_BO_FLAG_GGTT |
XE_BO_FLAG_GGTT_INVALIDATE |
- XE_BO_FLAG_PINNED_NORESTORE);
+ XE_BO_FLAG_PINNED_NORESTORE |
+ xe_guc_bo_wa_flags(gt));
if (IS_ERR(bo))
return PTR_ERR(bo);
@@ -864,7 +871,7 @@ static unsigned int guc_mmio_regset_write(struct xe_guc_ads *ads,
}
}
- if (XE_GT_WA(hwe->gt, 16023105232))
+ if (XE_GT_WA(hwe->gt, 16023105232) || XE_GT_WA(hwe->gt, 14025941587))
guc_mmio_regset_write_one(ads, regset_map,
RING_IDLEDLY(hwe->mmio_base),
count++);
diff --git a/drivers/gpu/drm/xe/xe_guc_ct.c b/drivers/gpu/drm/xe/xe_guc_ct.c
index 5c4733da385c..da5ce97484b8 100644
--- a/drivers/gpu/drm/xe/xe_guc_ct.c
+++ b/drivers/gpu/drm/xe/xe_guc_ct.c
@@ -376,7 +376,8 @@ int xe_guc_ct_init(struct xe_guc_ct *ct)
XE_BO_FLAG_SYSTEM |
XE_BO_FLAG_GGTT |
XE_BO_FLAG_GGTT_INVALIDATE |
- XE_BO_FLAG_PINNED_NORESTORE);
+ XE_BO_FLAG_PINNED_NORESTORE |
+ xe_guc_bo_wa_flags(gt));
if (IS_ERR(bo))
return PTR_ERR(bo);
@@ -386,7 +387,8 @@ int xe_guc_ct_init(struct xe_guc_ct *ct)
XE_BO_FLAG_SYSTEM |
XE_BO_FLAG_GGTT |
XE_BO_FLAG_GGTT_INVALIDATE |
- XE_BO_FLAG_PINNED_NORESTORE);
+ XE_BO_FLAG_PINNED_NORESTORE |
+ xe_guc_bo_wa_flags(gt));
if (IS_ERR(bo))
return PTR_ERR(bo);
@@ -1288,11 +1290,23 @@ int xe_guc_ct_send(struct xe_guc_ct *ct, const u32 *action, u32 len,
* @ct: GuC CT object
* @action: payload dwords (HxG header dword is expected at @action[-1])
* @len: number of payload dwords in @action
+ * @g2h_len: G2H response space to reserve in dwords, or 0
+ * @num_g2h: number of G2H messages expected, or 0
* @defer_flush: defer publishing/doorbell for batching
*
* Sends a single H2G message to the GuC CT buffer while the caller already
* holds @ct->lock.
*
+ * Callers that expect the GuC to reply with a G2H message must reserve the
+ * matching credit via @g2h_len / @num_g2h. The G2H receive path releases that
+ * credit unconditionally when the reply arrives, so failing to reserve it here
+ * corrupts the G2H accounting and kills the CT channel.
+ *
+ * Reserving G2H credit may wait for, and process, outstanding G2H messages, so
+ * this must not be called with a non-zero @g2h_len from a G2H handler. Handlers
+ * should use xe_guc_ct_send_g2h_handler(), with any reply credit reserved up
+ * front by the H2G that triggered the handler.
+ *
* If @defer_flush is false, the function completes the submission immediately:
* it makes the payload visible to the device, updates the H2G descriptor and
* rings the GuC doorbell.
@@ -1310,11 +1324,12 @@ int xe_guc_ct_send(struct xe_guc_ct *ct, const u32 *action, u32 len,
* Must be called with @ct->lock held.
*/
int xe_guc_ct_send_locked(struct xe_guc_ct *ct, const u32 *action, u32 len,
- bool defer_flush)
+ u32 g2h_len, u32 num_g2h, bool defer_flush)
{
int ret;
- ret = guc_ct_send_locked(ct, action, len, 0, 0, NULL, defer_flush);
+ ret = guc_ct_send_locked(ct, action, len, g2h_len, num_g2h, NULL,
+ defer_flush);
if (ret == -EDEADLK)
kick_reset(ct);
diff --git a/drivers/gpu/drm/xe/xe_guc_ct.h b/drivers/gpu/drm/xe/xe_guc_ct.h
index 3ddc665ab84a..e2a33249d65d 100644
--- a/drivers/gpu/drm/xe/xe_guc_ct.h
+++ b/drivers/gpu/drm/xe/xe_guc_ct.h
@@ -56,7 +56,7 @@ static inline void xe_guc_ct_irq_handler(struct xe_guc_ct *ct)
int xe_guc_ct_send(struct xe_guc_ct *ct, const u32 *action, u32 len,
u32 g2h_len, u32 num_g2h);
int xe_guc_ct_send_locked(struct xe_guc_ct *ct, const u32 *action, u32 len,
- bool defer_flush);
+ u32 g2h_len, u32 num_g2h, bool defer_flush);
int xe_guc_ct_send_recv(struct xe_guc_ct *ct, const u32 *action, u32 len,
u32 *response_buffer);
static inline int
diff --git a/drivers/gpu/drm/xe/xe_guc_engine_activity.c b/drivers/gpu/drm/xe/xe_guc_engine_activity.c
index a782be57caad..729ce8ac140b 100644
--- a/drivers/gpu/drm/xe/xe_guc_engine_activity.c
+++ b/drivers/gpu/drm/xe/xe_guc_engine_activity.c
@@ -97,7 +97,8 @@ static int allocate_engine_activity_buffers(struct xe_guc *guc,
metadata_bo = xe_bo_create_pin_map_novm(gt_to_xe(gt), tile, PAGE_ALIGN(metadata_size),
ttm_bo_type_kernel, XE_BO_FLAG_SYSTEM |
- XE_BO_FLAG_GGTT | XE_BO_FLAG_GGTT_INVALIDATE,
+ XE_BO_FLAG_GGTT | XE_BO_FLAG_GGTT_INVALIDATE |
+ xe_guc_bo_wa_flags(gt),
false);
if (IS_ERR(metadata_bo))
@@ -105,7 +106,8 @@ static int allocate_engine_activity_buffers(struct xe_guc *guc,
bo = xe_bo_create_pin_map_novm(gt_to_xe(gt), tile, PAGE_ALIGN(size),
ttm_bo_type_kernel, XE_BO_FLAG_VRAM_IF_DGFX(tile) |
- XE_BO_FLAG_GGTT | XE_BO_FLAG_GGTT_INVALIDATE, false);
+ XE_BO_FLAG_GGTT | XE_BO_FLAG_GGTT_INVALIDATE |
+ xe_guc_bo_wa_flags(gt), false);
if (IS_ERR(bo)) {
xe_bo_unpin_map_no_vm(metadata_bo);
diff --git a/drivers/gpu/drm/xe/xe_guc_hwconfig.c b/drivers/gpu/drm/xe/xe_guc_hwconfig.c
index b300901dbb8e..dc667a71b438 100644
--- a/drivers/gpu/drm/xe/xe_guc_hwconfig.c
+++ b/drivers/gpu/drm/xe/xe_guc_hwconfig.c
@@ -65,7 +65,7 @@ int xe_guc_hwconfig_init(struct xe_guc *guc)
/*
* All hwconfig the same across GTs so only GT0 needs to be configured
*/
- if (gt->info.id != XE_GT0)
+ if (gt != xe_root_mmio_gt(xe))
return 0;
/* ADL_P, DG2+ supports hwconfig table */
diff --git a/drivers/gpu/drm/xe/xe_guc_log.c b/drivers/gpu/drm/xe/xe_guc_log.c
index 538d4df0f7aa..7d006268ce91 100644
--- a/drivers/gpu/drm/xe/xe_guc_log.c
+++ b/drivers/gpu/drm/xe/xe_guc_log.c
@@ -17,6 +17,7 @@
#include "xe_force_wake.h"
#include "xe_gt_printk.h"
#include "xe_gt_types.h"
+#include "xe_guc.h"
#include "xe_map.h"
#include "xe_mmio.h"
#include "xe_module.h"
@@ -624,14 +625,16 @@ void xe_guc_log_print_lfd(struct xe_guc_log *log, struct drm_printer *p)
int xe_guc_log_init(struct xe_guc_log *log)
{
struct xe_device *xe = log_to_xe(log);
- struct xe_tile *tile = gt_to_tile(log_to_gt(log));
+ struct xe_gt *gt = log_to_gt(log);
+ struct xe_tile *tile = gt_to_tile(gt);
struct xe_bo *bo;
bo = xe_managed_bo_create_pin_map(xe, tile, GUC_LOG_SIZE,
XE_BO_FLAG_SYSTEM |
XE_BO_FLAG_GGTT |
XE_BO_FLAG_GGTT_INVALIDATE |
- XE_BO_FLAG_PINNED_NORESTORE);
+ XE_BO_FLAG_PINNED_NORESTORE |
+ xe_guc_bo_wa_flags(gt));
if (IS_ERR(bo))
return PTR_ERR(bo);
diff --git a/drivers/gpu/drm/xe/xe_guc_pagefault.c b/drivers/gpu/drm/xe/xe_guc_pagefault.c
index df237fd40551..8d665d1dac7f 100644
--- a/drivers/gpu/drm/xe/xe_guc_pagefault.c
+++ b/drivers/gpu/drm/xe/xe_guc_pagefault.c
@@ -4,9 +4,11 @@
*/
#include "abi/guc_actions_abi.h"
+#include "xe_gt_printk.h"
#include "xe_guc.h"
#include "xe_guc_ct.h"
#include "xe_guc_pagefault.h"
+#include "xe_hw_engine.h"
#include "xe_pagefault.h"
#include "xe_pagefault_types.h"
@@ -55,7 +57,8 @@ static void guc_ack_fault(struct xe_pagefault *pf, int err)
bool write_only = guc->pagefault_ack_counter++ &
(XE_GUC_PAGEFAULT_FLUSH_PERIOD - 1);
- xe_guc_ct_send_locked(&guc->ct, action, ARRAY_SIZE(action),
+ /* Pagefault acks are fire-and-forget, no G2H reply expected. */
+ xe_guc_ct_send_locked(&guc->ct, action, ARRAY_SIZE(action), 0, 0,
write_only);
}
@@ -68,10 +71,40 @@ static void guc_ack_fault_end(void *private)
xe_guc_ct_unlock(&guc->ct);
}
+static void xe_guc_pagefault_print(struct xe_pagefault *pf,
+ const char *err_str)
+{
+ const u32 *msg = pf->producer.msg;
+ u32 engine_class = FIELD_GET(PFD_ENG_CLASS, msg[0]);
+
+ xe_gt_info(pf->gt, "\n\tASID: %lu\n"
+ "\tFaulted Address: 0x%08lx%08lx\n"
+ "\tFaultType: %lu\n"
+ "\tAccessType: %lu\n"
+ "\tFaultLevel: %lu\n"
+ "\tEngineClass: %u %s\n"
+ "\tEngineInstance: %lu\n"
+ "\tSRCID: 0x%02lx\n"
+ "\tError: %s\n",
+ FIELD_GET(PFD_ASID, msg[1]),
+ FIELD_GET(PFD_VIRTUAL_ADDR_HI, msg[3]),
+ FIELD_GET(PFD_VIRTUAL_ADDR_LO, msg[2]) <<
+ PFD_VIRTUAL_ADDR_LO_SHIFT,
+ FIELD_GET(PFD_FAULT_TYPE, msg[2]),
+ FIELD_GET(PFD_ACCESS_TYPE, msg[2]),
+ FIELD_GET(PFD_FAULT_LEVEL, msg[0]),
+ engine_class,
+ xe_hw_engine_class_to_str(engine_class),
+ FIELD_GET(PFD_ENG_INSTANCE, msg[0]),
+ FIELD_GET(PFD_SRC_ID, msg[0]),
+ err_str);
+}
+
static const struct xe_pagefault_ops guc_pagefault_ops = {
.ack_fault_begin = guc_ack_fault_begin,
.ack_fault = guc_ack_fault,
.ack_fault_end = guc_ack_fault_end,
+ .print = xe_guc_pagefault_print,
};
/**
diff --git a/drivers/gpu/drm/xe/xe_guc_pc.c b/drivers/gpu/drm/xe/xe_guc_pc.c
index 097b075bd89a..e0105222a2c7 100644
--- a/drivers/gpu/drm/xe/xe_guc_pc.c
+++ b/drivers/gpu/drm/xe/xe_guc_pc.c
@@ -1391,7 +1391,8 @@ int xe_guc_pc_init(struct xe_guc_pc *pc)
XE_BO_FLAG_VRAM_IF_DGFX(tile) |
XE_BO_FLAG_GGTT |
XE_BO_FLAG_GGTT_INVALIDATE |
- XE_BO_FLAG_PINNED_NORESTORE);
+ XE_BO_FLAG_PINNED_NORESTORE |
+ xe_guc_bo_wa_flags(gt));
if (IS_ERR(bo))
return PTR_ERR(bo);
diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c
index f3ba8abfc228..0b231228bde8 100644
--- a/drivers/gpu/drm/xe/xe_guc_submit.c
+++ b/drivers/gpu/drm/xe/xe_guc_submit.c
@@ -19,6 +19,7 @@
#include "abi/guc_klvs_abi.h"
#include "xe_assert.h"
#include "xe_bo.h"
+#include "xe_cpu_bind.h"
#include "xe_devcoredump.h"
#include "xe_device.h"
#include "xe_exec_queue.h"
@@ -42,6 +43,7 @@
#include "xe_mocs.h"
#include "xe_module.h"
#include "xe_pm.h"
+#include "xe_pt.h"
#include "xe_ring_ops_types.h"
#include "xe_sched_job.h"
#include "xe_sleep.h"
@@ -1188,9 +1190,15 @@ static void submit_exec_queue(struct xe_exec_queue *q, struct xe_sched_job *job)
xe_gt_assert(guc_to_gt(guc), exec_queue_registered(q));
if (!job->restore_replay || job->last_replay) {
+ /*
+ * A chained ULLS job relies on its predecessor's postamble to
+ * publish its ring tail. After a reset the context comes back
+ * disabled with that postamble gone, so publish it here.
+ */
if (xe_exec_queue_is_parallel(q))
wq_item_append(q);
- else
+ else if (!xe_sched_job_ulls_is_chained(job) ||
+ !exec_queue_enabled(q))
xe_lrc_set_ring_tail(lrc, lrc->ring.tail);
job->last_replay = false;
}
@@ -1207,6 +1215,9 @@ static void submit_exec_queue(struct xe_exec_queue *q, struct xe_sched_job *job)
if (exec_queue_suspended(q))
return;
+ if (xe_sched_job_ulls_is_chained(job))
+ xe_lrc_set_ulls_semaphore(lrc, xe_sched_job_lrc_seqno(job));
+
if (!exec_queue_enabled(q)) {
action[len++] = XE_GUC_ACTION_SCHED_CONTEXT_MODE_SET;
action[len++] = q->guc->id;
@@ -1220,13 +1231,14 @@ static void submit_exec_queue(struct xe_exec_queue *q, struct xe_sched_job *job)
set_exec_queue_pending_enable(q);
set_exec_queue_enabled(q);
trace_xe_exec_queue_scheduling_enable(q);
- } else {
+ } else if (!xe_sched_job_ulls_is_chained(job)) {
action[len++] = XE_GUC_ACTION_SCHED_CONTEXT;
action[len++] = q->guc->id;
trace_xe_exec_queue_submit(q);
}
- xe_guc_ct_send(&guc->ct, action, len, g2h_len, num_g2h);
+ if (!xe_sched_job_ulls_is_chained(job) || num_g2h)
+ xe_guc_ct_send(&guc->ct, action, len, g2h_len, num_g2h);
if (extra_submit) {
len = 0;
@@ -1238,21 +1250,70 @@ static void submit_exec_queue(struct xe_exec_queue *q, struct xe_sched_job *job)
}
}
+static bool is_pt_job(struct xe_sched_job *job)
+{
+ return job->is_pt_job;
+}
+
+static void run_pt_job(struct xe_device *xe, struct xe_sched_job *job,
+ bool force_clear)
+{
+ struct xe_tile *tile;
+ int id;
+
+ for_each_tile(tile, xe, id) {
+ struct xe_pt_job_ops *pt_job_ops =
+ job->pt_update[0].pt_job_ops[id];
+
+ if (!pt_job_ops || !pt_job_ops->current_op)
+ continue;
+
+ xe_cpu_bind_update_pgtables_execute(job->pt_update[0].vm, tile,
+ job->pt_update[0].ops,
+ pt_job_ops->ops,
+ pt_job_ops->current_op,
+ force_clear);
+ }
+}
+
+static void put_pt_job(struct xe_device *xe, struct xe_sched_job *job)
+{
+ struct xe_tile *tile;
+ int id;
+
+ for_each_tile(tile, xe, id) {
+ struct xe_pt_job_ops *pt_job_ops =
+ job->pt_update[0].pt_job_ops[id];
+
+ xe_pt_job_ops_put(pt_job_ops);
+ }
+}
+
static struct dma_fence *
guc_exec_queue_run_job(struct drm_sched_job *drm_job)
{
struct xe_sched_job *job = to_xe_sched_job(drm_job);
struct xe_exec_queue *q = job->q;
struct xe_guc *guc = exec_queue_to_guc(q);
- bool killed_or_banned_or_wedged =
- exec_queue_killed_or_banned_or_wedged(q);
+ bool killed_or_banned_or_wedged_or_error =
+ exec_queue_killed_or_banned_or_wedged(q) ||
+ xe_sched_job_is_error(job);
xe_gt_assert(guc_to_gt(guc), !(exec_queue_destroyed(q) || exec_queue_pending_disable(q)) ||
exec_queue_banned(q) || exec_queue_suspended(q));
trace_xe_sched_job_run(job);
- if (!killed_or_banned_or_wedged && !xe_sched_job_is_error(job)) {
+ if (is_pt_job(job)) {
+ xe_gt_assert(guc_to_gt(guc), !exec_queue_registered(q));
+
+ run_pt_job(guc_to_xe(guc), job,
+ killed_or_banned_or_wedged_or_error);
+ put_pt_job(guc_to_xe(guc), job);
+ dma_fence_put(job->fence); /* Drop ref from xe_sched_job_arm */
+
+ return NULL;
+ } else if (!killed_or_banned_or_wedged_or_error) {
if (xe_exec_queue_is_multi_queue_secondary(q)) {
struct xe_exec_queue *primary = xe_exec_queue_multi_queue_primary(q);
@@ -2049,6 +2110,7 @@ static int guc_exec_queue_init(struct xe_exec_queue *q)
struct xe_guc_exec_queue *ge;
long timeout;
int err, i;
+ int max_jobs = (xe_lrc_ring_size() / MAX_JOB_SIZE_BYTES);
xe_gt_assert(guc_to_gt(guc), xe_device_uc_enabled(guc_to_xe(guc)));
@@ -2088,8 +2150,11 @@ static int guc_exec_queue_init(struct xe_exec_queue *q)
submit_wq = primary->guc->sched.base.submit_wq;
}
+ if (q->vm && q->vm->flags & XE_VM_FLAG_MIGRATION)
+ max_jobs = min(max_jobs, LRC_MIGRATION_ULLS_SEMAPHORE_COUNT - 1);
+
err = xe_sched_init(&ge->sched, &drm_sched_ops, &xe_sched_ops,
- submit_wq, xe_lrc_ring_size() / MAX_JOB_SIZE_BYTES, 64,
+ submit_wq, max_jobs, 64,
timeout, guc_to_gt(guc)->ordered_wq, NULL,
ge->name, gt_to_xe(q->gt)->drm.dev);
if (err)
@@ -2993,6 +3058,15 @@ static void guc_exec_queue_start(struct xe_exec_queue *q)
* increasing as jobs are written out.
*/
q->lrc[i]->ring.tail = job->ptrs[i].head;
+ /*
+ * A ULLS job's slot can have been consumed by
+ * the CS without the job running, leaving the
+ * saved head past it. Rewind so the re-emitted
+ * job is ahead of the head, not behind it.
+ */
+ if (xe_sched_job_is_ulls(job))
+ xe_lrc_set_ring_head(q->lrc[i],
+ job->ptrs[i].head);
xe_lrc_set_ring_tail(q->lrc[i],
xe_lrc_ring_head(q->lrc[i]));
}
@@ -3686,6 +3760,7 @@ xe_guc_exec_queue_snapshot_capture(struct xe_exec_queue *q)
snapshot->logical_mask = q->logical_mask;
snapshot->width = q->width;
snapshot->refcount = kref_read(&q->refcount);
+ snapshot->jobcount = atomic_read(&q->job_cnt);
snapshot->sched_timeout = sched->base.timeout;
snapshot->sched_props.timeslice_us = q->sched_props.timeslice_us;
snapshot->sched_props.preempt_timeout_us =
@@ -3758,6 +3833,7 @@ xe_guc_exec_queue_snapshot_print(struct xe_guc_submit_exec_queue_snapshot *snaps
drm_printf(p, "\tLogical mask: 0x%x\n", snapshot->logical_mask);
drm_printf(p, "\tWidth: %d\n", snapshot->width);
drm_printf(p, "\tRef: %d\n", snapshot->refcount);
+ drm_printf(p, "\tJob count: %u\n", snapshot->jobcount);
drm_printf(p, "\tTimeout: %ld (ms)\n", snapshot->sched_timeout);
drm_printf(p, "\tTimeslice: %u (us)\n",
snapshot->sched_props.timeslice_us);
diff --git a/drivers/gpu/drm/xe/xe_guc_submit_types.h b/drivers/gpu/drm/xe/xe_guc_submit_types.h
index 7824f61b1290..8271702e692e 100644
--- a/drivers/gpu/drm/xe/xe_guc_submit_types.h
+++ b/drivers/gpu/drm/xe/xe_guc_submit_types.h
@@ -77,6 +77,8 @@ struct xe_guc_submit_exec_queue_snapshot {
u16 width;
/** @refcount: ref count of this exec queue */
u32 refcount;
+ /** @jobcount: job count of this exec queue */
+ u32 jobcount;
/**
* @sched_timeout: the time after which a job is removed from the
* scheduler.
diff --git a/drivers/gpu/drm/xe/xe_hw_engine.c b/drivers/gpu/drm/xe/xe_hw_engine.c
index 010499766fce..9680d2a5adec 100644
--- a/drivers/gpu/drm/xe/xe_hw_engine.c
+++ b/drivers/gpu/drm/xe/xe_hw_engine.c
@@ -585,28 +585,102 @@ static void hw_engine_init_early(struct xe_gt *gt, struct xe_hw_engine *hwe,
xe_reg_whitelist_process_engine(hwe);
}
+static u32 idledly_floor_ticks(u32 idledly_ns, u32 idledly_units_ps)
+{
+ return DIV_ROUND_DOWN_ULL((u64)idledly_ns * 1000, idledly_units_ps);
+}
+
static void adjust_idledly(struct xe_hw_engine *hwe)
{
struct xe_gt *gt = hwe->gt;
- u32 idledly, maxcnt;
+ u32 idledly, idledly_hw, idledly_reg_val, maxcnt;
u32 idledly_units_ps = 8 * gt->info.timestamp_base;
u32 maxcnt_units_ns = 640;
- bool inhibit_switch = 0;
+ bool inhibit_switch = false;
+ bool wa_applied = false;
+ bool clamped_below_maxcnt = false;
+
+ if ((!IS_SRIOV_VF(gt_to_xe(gt)) && XE_GT_WA(gt, 16023105232)) ||
+ XE_GT_WA(gt, 14025941587)) {
+ u32 mincnt_idledly_ns = 5000;
+
+ /* xe_gt_clock_init() warns and zeroes timestamp_base on unknown crystal clock. */
+ if (!idledly_units_ps)
+ return;
- if (!IS_SRIOV_VF(gt_to_xe(hwe->gt)) && XE_GT_WA(gt, 16023105232)) {
- idledly = xe_mmio_read32(&gt->mmio, RING_IDLEDLY(hwe->mmio_base));
+ idledly_reg_val = xe_mmio_read32(&gt->mmio, RING_IDLEDLY(hwe->mmio_base));
maxcnt = xe_mmio_read32(&gt->mmio, RING_PWRCTX_MAXCNT(hwe->mmio_base));
- inhibit_switch = idledly & INHIBIT_SWITCH_UNTIL_PREEMPTED;
- idledly = REG_FIELD_GET(IDLE_DELAY, idledly);
- idledly = DIV_ROUND_CLOSEST(idledly * idledly_units_ps, 1000);
+ inhibit_switch = idledly_reg_val & INHIBIT_SWITCH_UNTIL_PREEMPTED;
+ idledly = REG_FIELD_GET(IDLE_DELAY, idledly_reg_val);
+ idledly = DIV_ROUND_CLOSEST_ULL((u64)idledly * idledly_units_ps, 1000);
+ idledly_hw = idledly;
maxcnt = REG_FIELD_GET(IDLE_WAIT_TIME, maxcnt);
maxcnt *= maxcnt_units_ns;
- if (xe_gt_WARN_ON(gt, idledly >= maxcnt || inhibit_switch)) {
- idledly = DIV_ROUND_CLOSEST(((maxcnt - 1) * 1000),
- idledly_units_ps);
- xe_mmio_write32(&gt->mmio, RING_IDLEDLY(hwe->mmio_base), idledly);
+ /*
+ * Wa_14025941587 is applied before Wa_16023105232, which takes
+ * priority if the two ever conflict (not expected in practice).
+ */
+ if (XE_GT_WA(gt, 14025941587) &&
+ idledly < mincnt_idledly_ns) {
+ idledly = mincnt_idledly_ns;
+ wa_applied = true;
+ }
+
+ if (XE_GT_WA(gt, 16023105232)) {
+ /* Clear the inhibit switch without disturbing a valid delay. */
+ if (inhibit_switch) {
+ idledly_reg_val &= ~INHIBIT_SWITCH_UNTIL_PREEMPTED;
+ wa_applied = true;
+ }
+
+ /* Warn only on the value read from hardware. */
+ xe_gt_WARN_ON(gt, idledly_hw >= maxcnt);
+
+ if (idledly >= maxcnt) {
+ /* maxcnt may be 0 if IDLE_WAIT_TIME is unprogrammed. */
+ idledly = maxcnt ? maxcnt - 1 : 0;
+ clamped_below_maxcnt = true;
+ wa_applied = true;
+ }
+ }
+
+ if (wa_applied) {
+ u32 idledly_ticks;
+
+ /*
+ * Wa_16023105232 requires idledly < maxcnt, so floor
+ * that clamp; otherwise round up to guarantee the
+ * Wa_14025941587 minimum survives tick quantization.
+ */
+ if (clamped_below_maxcnt)
+ idledly_ticks = idledly_floor_ticks(idledly, idledly_units_ps);
+ else
+ idledly_ticks = DIV_ROUND_UP_ULL((u64)idledly * 1000,
+ idledly_units_ps);
+
+ /*
+ * Tick quantization can still push the rounded-up value
+ * to/above maxcnt; re-floor here so Wa_16023105232 keeps
+ * priority even in that case.
+ */
+ if (!clamped_below_maxcnt && XE_GT_WA(gt, 16023105232) &&
+ (u64)idledly_ticks * idledly_units_ps >= (u64)maxcnt * 1000) {
+ xe_gt_dbg(gt, "idledly %s: %u ticks would exceed maxcnt=%u, so flooring\n",
+ hwe->name, idledly_ticks, maxcnt);
+ idledly = maxcnt ? maxcnt - 1 : 0;
+ idledly_ticks = idledly_floor_ticks(idledly, idledly_units_ps);
+ }
+
+ idledly_reg_val &= ~IDLE_DELAY;
+ idledly_reg_val |= REG_FIELD_PREP(IDLE_DELAY, idledly_ticks);
+ xe_gt_dbg(gt, "idledly %s: set %u max=%u inh=%u ts=%u\n",
+ hwe->name, idledly, maxcnt,
+ !!inhibit_switch, gt->info.timestamp_base);
+ xe_mmio_write32(&gt->mmio,
+ RING_IDLEDLY(hwe->mmio_base),
+ idledly_reg_val);
}
}
}
diff --git a/drivers/gpu/drm/xe/xe_i2c.c b/drivers/gpu/drm/xe/xe_i2c.c
index 51ca9fb73b70..f4f381988289 100644
--- a/drivers/gpu/drm/xe/xe_i2c.c
+++ b/drivers/gpu/drm/xe/xe_i2c.c
@@ -111,14 +111,9 @@ static int xe_i2c_register_adapter(struct xe_i2c *i2c)
{
struct pci_dev *pci = to_pci_dev(i2c->drm_dev);
struct platform_device *pdev;
- struct fwnode_handle *fwnode;
int ret;
u32 id;
- fwnode = fwnode_create_software_node(xe_i2c_adapter_properties, NULL);
- if (IS_ERR(fwnode))
- return PTR_ERR(fwnode);
-
id = (pci_domain_nr(pci->bus) << 16) | pci_dev_id(pci);
/*
@@ -128,14 +123,16 @@ static int xe_i2c_register_adapter(struct xe_i2c *i2c)
* platform_device_register_full() is done.
*/
pdev = platform_device_alloc(adapter_name, id);
- if (!pdev) {
- ret = -ENOMEM;
- goto err_fwnode_remove;
- }
+ if (!pdev)
+ return -ENOMEM;
+
+ ret = device_create_managed_software_node(&pdev->dev,
+ xe_i2c_adapter_properties,
+ NULL);
+ if (ret)
+ goto err_pdev_put;
pdev->dev.parent = i2c->drm_dev;
- pdev->dev.fwnode = fwnode;
- i2c->adapter_node = fwnode;
i2c->pdev = pdev;
ret = platform_device_add(pdev);
@@ -146,8 +143,6 @@ static int xe_i2c_register_adapter(struct xe_i2c *i2c)
err_pdev_put:
platform_device_put(pdev);
-err_fwnode_remove:
- fwnode_remove_software_node(fwnode);
return ret;
}
@@ -155,7 +150,6 @@ err_fwnode_remove:
static void xe_i2c_unregister_adapter(struct xe_i2c *i2c)
{
platform_device_unregister(i2c->pdev);
- fwnode_remove_software_node(i2c->adapter_node);
}
/**
diff --git a/drivers/gpu/drm/xe/xe_i2c.h b/drivers/gpu/drm/xe/xe_i2c.h
index d63adacfefe7..5969054a6b6b 100644
--- a/drivers/gpu/drm/xe/xe_i2c.h
+++ b/drivers/gpu/drm/xe/xe_i2c.h
@@ -33,7 +33,6 @@ struct xe_i2c_endpoint {
};
struct xe_i2c {
- struct fwnode_handle *adapter_node;
struct platform_device *pdev;
struct i2c_adapter *adapter;
struct i2c_client *client[XE_I2C_MAX_CLIENTS];
diff --git a/drivers/gpu/drm/xe/xe_lrc.c b/drivers/gpu/drm/xe/xe_lrc.c
index f1cf1463f1b2..bf5c2351aed3 100644
--- a/drivers/gpu/drm/xe/xe_lrc.c
+++ b/drivers/gpu/drm/xe/xe_lrc.c
@@ -706,6 +706,7 @@ u32 xe_lrc_pphwsp_offset(struct xe_lrc *lrc)
#define LRC_CTX_JOB_TIMESTAMP_OFFSET 512
#define LRC_ENGINE_ID_PPHWSP_OFFSET 1024
#define LRC_PARALLEL_PPHWSP_OFFSET 2048
+#define LRC_ULLS_PPHWSP_OFFSET 2048 /* Mutually exclusive with parallel */
#define LRC_SEQNO_OFFSET 0
#define LRC_START_SEQNO_OFFSET (LRC_SEQNO_OFFSET + 8)
@@ -768,6 +769,12 @@ static inline u32 __xe_lrc_engine_id_offset(struct xe_lrc *lrc)
return xe_lrc_pphwsp_offset(lrc) + LRC_ENGINE_ID_PPHWSP_OFFSET;
}
+static u32 __xe_lrc_ulls_offset(struct xe_lrc *lrc)
+{
+ /* The ulls is stored in the driver-defined portion of PPHWSP */
+ return xe_lrc_pphwsp_offset(lrc) + LRC_ULLS_PPHWSP_OFFSET;
+}
+
static u32 __xe_lrc_ctx_timestamp_offset(struct xe_lrc *lrc)
{
return __xe_lrc_regs_offset(lrc) + CTX_TIMESTAMP * sizeof(u32);
@@ -835,6 +842,7 @@ DECL_MAP_ADDR_HELPERS(ctx_job_timestamp, lrc->bo)
DECL_MAP_ADDR_HELPERS(ctx_timestamp, lrc->bo)
DECL_MAP_ADDR_HELPERS(ctx_timestamp_udw, lrc->bo)
DECL_MAP_ADDR_HELPERS(parallel, lrc->bo)
+DECL_MAP_ADDR_HELPERS(ulls, lrc->bo)
DECL_MAP_ADDR_HELPERS(indirect_ring, lrc->bo)
DECL_MAP_ADDR_HELPERS(engine_id, lrc->bo)
DECL_MAP_ADDR_HELPERS(queue_timestamp, lrc->bo)
@@ -1805,6 +1813,26 @@ void xe_lrc_set_ring_tail(struct xe_lrc *lrc, u32 tail)
xe_lrc_write_ctx_reg(lrc, CTX_RING_TAIL, tail);
}
+/**
+ * xe_lrc_ring_tail_ggtt_addr() - Saved ring tail GGTT address
+ * @lrc: Pointer to the lrc.
+ *
+ * GGTT address of the ring tail as saved for this context - in the indirect
+ * ring state on platforms which have it, otherwise in the context image.
+ * This is what a context restore loads the tail register from, so a ring
+ * which advances the tail register itself must keep this in sync.
+ *
+ * Returns: saved ring tail GGTT address
+ */
+u32 xe_lrc_ring_tail_ggtt_addr(struct xe_lrc *lrc)
+{
+ if (xe_lrc_has_indirect_ring_state(lrc))
+ return __xe_lrc_indirect_ring_ggtt_addr(lrc) +
+ INDIRECT_CTX_RING_TAIL * sizeof(u32);
+
+ return __xe_lrc_regs_ggtt_addr(lrc) + CTX_RING_TAIL * sizeof(u32);
+}
+
u32 xe_lrc_ring_tail(struct xe_lrc *lrc)
{
if (xe_lrc_has_indirect_ring_state(lrc))
@@ -1984,6 +2012,51 @@ static u32 xe_lrc_engine_id(struct xe_lrc *lrc)
return xe_map_read32(xe, &map);
}
+#define semaphore_offset(seqno) \
+ (sizeof(u32) * ((seqno) % LRC_MIGRATION_ULLS_SEMAPHORE_COUNT))
+
+/**
+ * xe_lrc_ulls_semaphore_ggtt_addr() - ULLS semaphore GGTT address
+ * @lrc: Pointer to the lrc.
+ * @seqno: seqno of current job.
+ *
+ * Calculate ULLS semaphore GGTT address based on input seqno
+ *
+ * Returns: ULLS semaphore GGTT address
+ */
+u32 xe_lrc_ulls_semaphore_ggtt_addr(struct xe_lrc *lrc, u32 seqno)
+{
+ xe_assert(lrc_to_xe(lrc), semaphore_offset(seqno) <
+ LRC_PPHWSP_SIZE - LRC_ULLS_PPHWSP_OFFSET);
+
+ return __xe_lrc_ulls_ggtt_addr(lrc) + semaphore_offset(seqno);
+}
+
+/**
+ * xe_lrc_set_ulls_semaphore() - Set ULLS semaphore
+ * @lrc: Pointer to the lrc.
+ * @seqno: seqno of current job.
+ *
+ * Set ULLS semaphore based on input seqno
+ */
+void xe_lrc_set_ulls_semaphore(struct xe_lrc *lrc, u32 seqno)
+{
+ struct xe_device *xe = lrc_to_xe(lrc);
+ struct iosys_map map = __xe_lrc_ulls_map(lrc);
+
+ xe_assert(xe, semaphore_offset(seqno) <
+ LRC_PPHWSP_SIZE - LRC_ULLS_PPHWSP_OFFSET);
+
+ /*
+ * The ring contents this semaphore releases are ordered by the
+ * xe_device_wmb() at the end of xe_lrc_write_ring().
+ */
+ iosys_map_incr(&map, semaphore_offset(seqno));
+ xe_map_write32(xe, &map, LRC_MIGRATION_ULLS_SEMAPHORE_SIGNAL);
+
+ xe_device_wmb(xe); /* Flush write to hardware */
+}
+
static int instr_dw(u32 cmd_header)
{
/* GFXPIPE "SINGLE_DW" opcodes are a single dword */
diff --git a/drivers/gpu/drm/xe/xe_lrc.h b/drivers/gpu/drm/xe/xe_lrc.h
index a8ff4e59a1f4..f2e829e62168 100644
--- a/drivers/gpu/drm/xe/xe_lrc.h
+++ b/drivers/gpu/drm/xe/xe_lrc.h
@@ -112,6 +112,7 @@ u32 xe_lrc_regs_offset(struct xe_lrc *lrc);
void xe_lrc_set_ring_tail(struct xe_lrc *lrc, u32 tail);
u32 xe_lrc_ring_tail(struct xe_lrc *lrc);
+u32 xe_lrc_ring_tail_ggtt_addr(struct xe_lrc *lrc);
void xe_lrc_set_ring_head(struct xe_lrc *lrc, u32 head);
u32 xe_lrc_ring_head(struct xe_lrc *lrc);
u32 xe_lrc_ring_space(struct xe_lrc *lrc);
@@ -127,6 +128,9 @@ void xe_default_lrc_update_memirq_regs_with_address(struct xe_hw_engine *hwe);
void xe_lrc_update_memirq_regs_with_address(struct xe_lrc *lrc, struct xe_hw_engine *hwe,
u32 *regs);
+u32 xe_lrc_ulls_semaphore_ggtt_addr(struct xe_lrc *lrc, u32 seqno);
+void xe_lrc_set_ulls_semaphore(struct xe_lrc *lrc, u32 seqno);
+
u32 xe_lrc_read_ctx_reg(struct xe_lrc *lrc, int reg_nr);
void xe_lrc_write_ctx_reg(struct xe_lrc *lrc, int reg_nr, u32 val);
diff --git a/drivers/gpu/drm/xe/xe_lrc_types.h b/drivers/gpu/drm/xe/xe_lrc_types.h
index 53ef48feebfc..b18daf1092e6 100644
--- a/drivers/gpu/drm/xe/xe_lrc_types.h
+++ b/drivers/gpu/drm/xe/xe_lrc_types.h
@@ -12,6 +12,10 @@
struct xe_bo;
+#define LRC_MIGRATION_ULLS_SEMAPHORE_COUNT 64 /* Must be pow2 */
+#define LRC_MIGRATION_ULLS_SEMAPHORE_CLEAR 0
+#define LRC_MIGRATION_ULLS_SEMAPHORE_SIGNAL 1
+
/**
* struct xe_lrc - Logical ring context (LRC) and submission ring object
*/
diff --git a/drivers/gpu/drm/xe/xe_migrate.c b/drivers/gpu/drm/xe/xe_migrate.c
index ff45c24d8889..fffb3eb6c76e 100644
--- a/drivers/gpu/drm/xe/xe_migrate.c
+++ b/drivers/gpu/drm/xe/xe_migrate.c
@@ -8,6 +8,7 @@
#include <linux/bitfield.h>
#include <linux/sizes.h>
+#include <drm/drm_drv.h>
#include <drm/drm_managed.h>
#include <drm/drm_pagemap.h>
#include <drm/ttm/ttm_tt.h>
@@ -22,6 +23,7 @@
#include "xe_assert.h"
#include "xe_bb.h"
#include "xe_bo.h"
+#include "xe_configfs.h"
#include "xe_exec_queue.h"
#include "xe_ggtt.h"
#include "xe_gt.h"
@@ -32,6 +34,7 @@
#include "xe_mem_pool.h"
#include "xe_mocs.h"
#include "xe_pat.h"
+#include "xe_pm.h"
#include "xe_printk.h"
#include "xe_pt.h"
#include "xe_res_cursor.h"
@@ -46,6 +49,157 @@
#include "xe_vram.h"
/**
+ * DOC: ULLS (Ultra Low Latency Submission) for migration jobs
+ *
+ * Migration jobs issued on behalf of GPU page faults and SVM prefetches sit
+ * directly in the critical path of a stalled GPU workload. The dominant cost
+ * of such a job is not the copy or clear itself but the submission latency:
+ * the H2G round trip to GuC, the GuC scheduling decision, and the hardware
+ * context switch required to place the migration LRC on an engine.
+ *
+ * ULLS removes that cost by keeping the migration context resident and
+ * *running* on the hardware engine across jobs. Instead of the ring going
+ * empty and the context being switched out between jobs, the tail of every
+ * ULLS job parks the engine on a semaphore wait for the *next* job's
+ * semaphore, and then advances the ring tail itself. Submitting the next job
+ * therefore costs the CPU a single write to signal that semaphore - no H2G,
+ * no GuC round trip, no context switch, no MMIO.
+ *
+ * Requirements
+ * ------------
+ *
+ * ULLS is only used on dGFX platforms with USM support, where a hardware
+ * engine is reserved exclusively for migration jobs. Because the engine
+ * spins on a semaphore while ULLS is active, it cannot be shared with
+ * user submissions.
+ *
+ * ULLS can be disabled by setting the ``migrate_ulls_period_ms`` configfs
+ * attribute to 0. Otherwise, the same attribute controls how long ULLS
+ * remains active before exiting, in milliseconds.
+ *
+ * Fixed size jobs
+ * ---------------
+ *
+ * A job updates the ring tail to cover its successor, but it is emitted long
+ * before that successor exists, so it can not know how much ring the
+ * successor will occupy. Every ULLS job is therefore padded out to exactly
+ * ULLS_JOB_SIZE_BYTES, which lets the next tail be computed arithmetically
+ * from where the current job started.
+ *
+ * This is why the shorter jobs still have to reach the same size: the "last"
+ * job skips the batch buffers and the postamble, and pads the difference with
+ * MI_NOOP. The "first" job is not covered by any predecessor's tail update
+ * and so is unconstrained, but is padded anyway to keep the arithmetic
+ * uniform.
+ *
+ * Leaving ULLS mode always goes through a "last" job, which emits no tail
+ * update, so an ordinary variable length migration job never follows a
+ * prediction.
+ *
+ * Semaphores
+ * ----------
+ *
+ * The semaphores live in the driver-defined portion of the migration LRC's
+ * PPHWSP (see LRC_ULLS_PPHWSP_OFFSET, mutually exclusive with the parallel
+ * submission area). There are LRC_MIGRATION_ULLS_SEMAPHORE_COUNT of them and
+ * a job's semaphore is selected by ``seqno % COUNT``, so the semaphore ring
+ * wraps with the job seqnos. To guarantee a job can never overwrite the
+ * semaphore of a job still in flight, the GuC backend caps the migration
+ * queue's scheduler job count at LRC_MIGRATION_ULLS_SEMAPHORE_COUNT - 1.
+ *
+ * Ring layout of a ULLS job
+ * -------------------------
+ *
+ * Emitted by emit_migration_job_gen12() in xe_ring_ops.c::
+ *
+ * preamble: clear semaphore[seqno] (reuse for a later wrap)
+ * <copy timestamp, start seqno store>
+ * <batch buffer start(s)> (skipped on first/last job)
+ * <seqno write + user interrupt>
+ * postamble: wait on semaphore[seqno + 1]
+ * SDI saved ring tail = end of next job
+ * LRI RING_TAIL = end of next job
+ * (skipped on the last job)
+ * pad: MI_NOOP up to ULLS_JOB_SIZE_DW
+ *
+ * The preamble clears the current job's semaphore so it can be reused once
+ * the seqno space wraps. The postamble is what keeps the engine busy: it
+ * blocks on the next job's semaphore, which is only signaled when that job is
+ * actually submitted, and then advances the ring tail over it. It advances
+ * the saved tail as well as the tail register, keeping the two in step
+ * without any help from the CPU, so a context save and restore can not rewind
+ * the tail behind work which has already been published.
+ *
+ * The tail register write must be non-posted, i.e. it must not carry
+ * MI_LRI_FORCE_POSTED. Posted, the new tail is free to land after the command
+ * streamer has already drained the rest of the job, at which point the command
+ * streamer sees head == the old tail and parks as though the ring were empty.
+ * A parked context can be switched off the hardware, and the fast path below
+ * has no H2G with which to ask GuC to bring it back.
+ *
+ * The tail must be published after the semaphore wait, not before it. The
+ * command streamer fetches ring contents up to the tail while it is parked,
+ * so a tail published ahead of the wait lets it fetch the next job's slot
+ * before the CPU has written the job there. Once released it then executes
+ * the MI_NOOPs it fetched instead of the job, that job's fence never signals,
+ * and the engine drains and idles with nothing left to wake it.
+ *
+ * Submission fast path
+ * --------------------
+ *
+ * In submit_exec_queue() (xe_guc_submit.c), a ULLS job that is not the first
+ * one reduces to::
+ *
+ * xe_lrc_set_ulls_semaphore(lrc, seqno); release previous job
+ *
+ * The XE_GUC_ACTION_SCHED_CONTEXT H2G is suppressed, and so is the write of
+ * the saved ring tail: the previous job's postamble is parked on this job's
+ * semaphore and publishes this job's tail, both in the tail register and in
+ * the context image, as soon as it is released. The semaphore signal is all
+ * that is left, and the engine walks straight into this job.
+ *
+ * This does assume the context stays resident for as long as ULLS mode is
+ * active. Nothing else is scheduled on the reserved engine, so the only ways
+ * off the hardware are the "last" job below, or a reset - and a migration job
+ * failing already wedges the device.
+ *
+ * Enter / exit
+ * ------------
+ *
+ * xe_migrate_ulls_enter() is called from the page fault handler and from the
+ * SVM prefetch path, i.e. exactly where low latency migration matters. It
+ * takes a PM runtime reference (the device must not suspend while the engine
+ * spins), then submits a "first" ULLS job. That first job carries no batch
+ * buffer; it exists only to get the context onto the hardware through the
+ * normal GuC path and to leave the engine waiting on the next semaphore,
+ * pipelining the GuC/HW context switch out of the critical path.
+ *
+ * No forcewake reference is required. Nothing in the fast path touches MMIO,
+ * and the engine keeps itself awake for as long as it is executing the ring.
+ * Not needing host MMIO access is also what lets ULLS run on SRIOV VFs.
+ *
+ * Keeping an engine spinning costs power, so ULLS is not left enabled
+ * indefinitely. Every enter and every ULLS job submission re-arms
+ * @xe_migrate.ulls.exit_work with a ULLS_EXIT_JIFFIES delay. When it fires
+ * with the queue idle, it submits a "last" ULLS job - again with no batch
+ * buffer and, crucially, with no postamble semaphore wait or tail update -
+ * which lets the ring drain so the context can be switched off the hardware.
+ * The PM reference is then dropped. If the queue was not idle, the worker
+ * simply re-arms itself.
+ *
+ * Job state
+ * ---------
+ *
+ * The state above is communicated to the ring ops and GuC backend via
+ * @xe_sched_job.ulls, set under @xe_migrate.job_mutex:
+ *
+ * - %ULLS_NONE: job submitted outside of ULLS mode
+ * - %ULLS_ENTER: job that enters ULLS mode
+ * - %ULLS_ACTIVE: job submitted while in ULLS mode
+ * - %ULLS_EXIT: job that exits ULLS mode
+ */
+
+/**
* struct xe_migrate - migrate context.
*/
struct xe_migrate {
@@ -75,18 +229,23 @@ struct xe_migrate {
* Protected by @job_mutex.
*/
struct dma_fence *fence;
- /**
- * @vm_update_sa: For integrated, used to suballocate page-tables
- * out of the pt_bo.
- */
- struct drm_suballoc_manager vm_update_sa;
/** @min_chunk_size: For dgfx, Minimum chunk size */
u64 min_chunk_size;
+ /** @ulls: ULLS support */
+ struct {
+ /** @ulls.exit_ms: ULLS exit period milliseconds */
+ u32 exit_ms;
+ /** @ulls.enabled: ULLS is enabled, protected by job_mutex */
+ bool enabled;
+ /** @ulls.exit_work: ULLS exit worker */
+ struct delayed_work exit_work;
+ } ulls;
};
+#define ULLS_EXIT_JIFFIES(_m) msecs_to_jiffies((_m)->ulls.exit_ms)
+
#define MAX_PREEMPTDISABLE_TRANSFER SZ_8M /* Around 1ms. */
#define MAX_CCS_LIMITED_TRANSFER SZ_4M /* XE_PAGE_SIZE * (FIELD_MAX(XE2_CCS_SIZE_MASK) + 1) */
-#define NUM_KERNEL_PDE 15
#define NUM_PT_SLOTS 48
#define LEVEL0_PAGE_TABLE_ENCODE_SIZE SZ_2M
#define MAX_NUM_PTE 512
@@ -101,9 +260,30 @@ struct xe_migrate {
*/
#define MAX_PTE_PER_SDI 0x1FEU
+static bool xe_migrate_ulls_enabled(struct xe_migrate *m)
+{
+ lockdep_assert_held(&m->job_mutex);
+ return m->ulls.enabled;
+}
+
+static void xe_migrate_ulls_toggle_enable(struct xe_migrate *m, bool enabled)
+{
+ lockdep_assert_held(&m->job_mutex);
+ m->ulls.enabled = enabled;
+}
+
static void xe_migrate_fini(void *arg)
{
struct xe_migrate *m = arg;
+ struct xe_device *xe = tile_to_xe(m->tile);
+
+ disable_delayed_work_sync(&m->ulls.exit_work);
+ scoped_guard(mutex, &m->job_mutex) {
+ if (xe_migrate_ulls_enabled(m)) {
+ xe_pm_runtime_put(xe);
+ xe_migrate_ulls_toggle_enable(m, false);
+ }
+ }
xe_vm_lock(m->q->vm, false);
xe_bo_unpin(m->pt_bo);
@@ -111,7 +291,6 @@ static void xe_migrate_fini(void *arg)
dma_fence_put(m->fence);
xe_bo_put(m->pt_bo);
- drm_suballoc_manager_fini(&m->vm_update_sa);
mutex_destroy(&m->job_mutex);
xe_vm_close_and_put(m->q->vm);
xe_exec_queue_put(m->q);
@@ -231,8 +410,6 @@ static int xe_migrate_pt_bo_alloc(struct xe_tile *tile, struct xe_migrate *m,
BUILD_BUG_ON(NUM_PT_SLOTS > SZ_2M/XE_PAGE_SIZE);
/* Must be a multiple of 64K to support all platforms */
BUILD_BUG_ON(NUM_PT_SLOTS * XE_PAGE_SIZE % SZ_64K);
- /* And one slot reserved for the 4KiB page table updates */
- BUILD_BUG_ON(!(NUM_KERNEL_PDE & 1));
/* Need to be sure everything fits in the first PT, or create more */
xe_tile_assert(tile, m->batch_base_ofs + xe_bo_size(batch) < SZ_2M);
@@ -255,7 +432,8 @@ static void xe_migrate_prepare_vm(struct xe_tile *tile, struct xe_migrate *m,
struct xe_device *xe = tile_to_xe(tile);
u16 pat_index = xe_cache_pat_idx(xe, XE_CACHE_WB);
u8 id = tile->id;
- u32 num_entries = NUM_PT_SLOTS, num_level = vm->pt_root[id]->level;
+ u32 num_entries = NUM_PT_SLOTS, num_level =
+ xe_vm_pt_root(vm, id)->level;
#define VRAM_IDENTITY_MAP_PT_COUNT 4
u32 num_setup = num_level + VRAM_IDENTITY_MAP_PT_COUNT;
#undef VRAM_IDENTITY_MAP_PT_COUNT
@@ -267,7 +445,7 @@ static void xe_migrate_prepare_vm(struct xe_tile *tile, struct xe_migrate *m,
u64 l1_pt_ofs = xe_bo_size(bo) - 5 * XE_PAGE_SIZE;
entry = vm->pt_ops->pde_encode_bo(bo, l1_pt_ofs);
- xe_pt_write(xe, &vm->pt_root[id]->bo->vmap, 0, entry);
+ xe_pt_write(xe, &xe_vm_pt_root(vm, id)->bo->vmap, 0, entry);
map_ofs = (num_entries - num_setup) * XE_PAGE_SIZE;
@@ -388,17 +566,9 @@ static void xe_migrate_prepare_vm(struct xe_tile *tile, struct xe_migrate *m,
}
}
- if (ofs)
- *ofs = map_ofs;
-}
-
-static void xe_migrate_suballoc_manager_init(struct xe_migrate *m, u32 map_ofs)
-{
/*
* Example layout created above, with root level = 3:
* [PT0...PT7]: kernel PT's for copy/clear; 64 or 4KiB PTE's
- * [PT8]: Kernel PT for VM_BIND, 4 KiB PTE's
- * [PT9...PT40]: Userspace PT's for VM_BIND, 4 KiB PTE's
* [PT41 = PDE 0] [PT44...PT47 = 4K and 2M vram identity maps]
*
* This makes the lowest part of the VM point to the pagetables.
@@ -406,19 +576,13 @@ static void xe_migrate_suballoc_manager_init(struct xe_migrate *m, u32 map_ofs)
* and flushes, other parts of the VM can be used either for copying and
* clearing.
*
- * For performance, the kernel reserves PDE's, so about 20 are left
- * for async VM updates.
- *
* To make it easier to work, each scratch PT is put in slot (1 + PT #)
* everywhere, this allows lockless updates to scratch pages by using
* the different addresses in VM.
*/
-#define NUM_VMUSA_UNIT_PER_PAGE 32
-#define VM_SA_UPDATE_UNIT_SIZE (XE_PAGE_SIZE / NUM_VMUSA_UNIT_PER_PAGE)
-#define NUM_VMUSA_WRITES_PER_UNIT (VM_SA_UPDATE_UNIT_SIZE / sizeof(u64))
- drm_suballoc_manager_init(&m->vm_update_sa,
- (size_t)(map_ofs / XE_PAGE_SIZE - NUM_KERNEL_PDE) *
- NUM_VMUSA_UNIT_PER_PAGE, 0);
+
+ if (ofs)
+ *ofs = map_ofs;
}
static bool xe_migrate_needs_ccs_emit(struct xe_device *xe)
@@ -463,7 +627,6 @@ static int xe_migrate_lock_prepare_vm(struct xe_tile *tile, struct xe_migrate *m
return err;
xe_migrate_prepare_vm(tile, m, vm, &map_ofs);
- xe_migrate_suballoc_manager_init(m, map_ofs);
drm_exec_retry_on_contention(&exec);
xe_validation_retry_on_oom(&ctx, &err);
}
@@ -471,6 +634,150 @@ static int xe_migrate_lock_prepare_vm(struct xe_tile *tile, struct xe_migrate *m
return err;
}
+static struct dma_fence *__xe_migrate_job_push(struct xe_migrate *m,
+ struct xe_sched_job *job,
+ enum xe_ulls_state ulls)
+{
+ struct dma_fence *fence;
+
+ lockdep_assert_held(&m->job_mutex);
+ xe_tile_assert(m->tile, m->q == job->q);
+
+ job->ulls = ulls;
+ xe_sched_job_arm(job);
+ fence = dma_fence_get(&job->drm.s_fence->finished);
+ xe_sched_job_push(job);
+
+ return fence;
+}
+
+/*
+ * Arm and push a migration job, tagging it as a ULLS job and deferring the
+ * ULLS exit while ULLS mode is active.
+ *
+ * Returns a reference to the job's finished fence.
+ */
+static struct dma_fence *xe_migrate_job_push(struct xe_migrate *m,
+ struct xe_sched_job *job)
+{
+ enum xe_ulls_state ulls = ULLS_NONE;
+
+ lockdep_assert_held(&m->job_mutex);
+
+ if (xe_migrate_ulls_enabled(m)) {
+ ulls = ULLS_ACTIVE;
+ mod_delayed_work(system_percpu_wq, &m->ulls.exit_work,
+ ULLS_EXIT_JIFFIES(m));
+ }
+
+ return __xe_migrate_job_push(m, job, ulls);
+}
+
+/**
+ * xe_migrate_ulls_enter() - Enter ULLS mode
+ * @m: The migration context.
+ *
+ * If DGFX, enter ULLS mode bypassing GuC / HW context switches by utilizing
+ * semaphore and continuously running batches.
+ */
+void xe_migrate_ulls_enter(struct xe_migrate *m)
+{
+ struct xe_device *xe = tile_to_xe(m->tile);
+ struct xe_sched_job *job = NULL;
+ u64 batch_addr[2] = { 0, 0 };
+ bool alloc = false;
+
+ xe_assert(xe, xe->info.has_usm);
+
+ if (!IS_DGFX(xe) || !m->ulls.exit_ms)
+ return;
+
+job_alloc:
+ if (alloc) {
+ /*
+ * Must be done outside job_mutex as that lock is tainted with
+ * reclaim.
+ */
+ job = xe_sched_job_create(m->q, batch_addr);
+ if (WARN_ON_ONCE(IS_ERR(job)))
+ return; /* Not fatal */
+ }
+
+ mutex_lock(&m->job_mutex);
+ if (!xe_migrate_ulls_enabled(m)) {
+ struct dma_fence *fence;
+
+ if (!job) {
+ alloc = true;
+ mutex_unlock(&m->job_mutex);
+ goto job_alloc;
+ }
+
+ /* Pairs with PM put on ULLS exit */
+ xe_pm_runtime_get_noresume(xe);
+
+ xe_sched_job_get(job);
+ fence = __xe_migrate_job_push(m, job, ULLS_ENTER);
+ dma_fence_put(fence);
+
+ xe_dbg(xe, "Migrate ULLS mode enter");
+ xe_migrate_ulls_toggle_enable(m, true);
+ }
+ if (job)
+ xe_sched_job_put(job);
+ if (xe_migrate_ulls_enabled(m))
+ mod_delayed_work(system_percpu_wq, &m->ulls.exit_work,
+ ULLS_EXIT_JIFFIES(m));
+ mutex_unlock(&m->job_mutex);
+}
+
+static void xe_migrate_ulls_exit(struct work_struct *work)
+{
+ struct xe_migrate *m = container_of(work, struct xe_migrate,
+ ulls.exit_work.work);
+ struct xe_device *xe = tile_to_xe(m->tile);
+ struct xe_sched_job *job = NULL;
+ struct dma_fence *fence = NULL;
+ u64 batch_addr[2] = { 0, 0 };
+ int idx;
+
+ xe_assert(xe, m->ulls.enabled);
+
+ if (!drm_dev_enter(&xe->drm, &idx))
+ return;
+
+ /*
+ * Must be done outside job_mutex as that lock is tainted with
+ * reclaim and must be done holding a pm ref.
+ */
+ job = xe_sched_job_create(m->q, batch_addr);
+ if (WARN_ON_ONCE(IS_ERR(job))) {
+ drm_dev_exit(idx);
+ mod_delayed_work(system_percpu_wq, &m->ulls.exit_work,
+ ULLS_EXIT_JIFFIES(m));
+ return; /* Not fatal */
+ }
+
+ scoped_guard(mutex, &m->job_mutex) {
+ if (xe_exec_queue_is_idle(m->q, 1)) {
+ fence = __xe_migrate_job_push(m, job, ULLS_EXIT);
+ dma_fence_put(fence);
+
+ xe_pm_runtime_put(xe); /* Pairs with PM get in enter */
+ xe_migrate_ulls_toggle_enable(m, false);
+ cancel_delayed_work(&m->ulls.exit_work);
+
+ xe_dbg(xe, "Migrate ULLS mode exit");
+ } else {
+ xe_sched_job_put(job);
+ mod_delayed_work(system_percpu_wq, &m->ulls.exit_work,
+ ULLS_EXIT_JIFFIES(m));
+ }
+ }
+
+ drm_dev_exit(idx);
+}
+
/**
* xe_migrate_init() - Initialize a migrate context
* @m: The migration context
@@ -529,6 +836,8 @@ int xe_migrate_init(struct xe_migrate *m)
might_lock(&m->job_mutex);
fs_reclaim_release(GFP_KERNEL);
+ INIT_DELAYED_WORK(&m->ulls.exit_work, xe_migrate_ulls_exit);
+
err = devm_add_action_or_reset(xe->drm.dev, xe_migrate_fini, m);
if (err)
return err;
@@ -546,6 +855,9 @@ int xe_migrate_init(struct xe_migrate *m)
(unsigned long long)m->min_chunk_size);
}
+ m->ulls.exit_ms =
+ xe_configfs_get_migrate_ulls_period_ms(to_pci_dev(xe->drm.dev));
+
return err;
err_out:
@@ -1056,10 +1368,8 @@ static struct dma_fence *__xe_migrate_copy(struct xe_migrate *m,
}
mutex_lock(&m->job_mutex);
- xe_sched_job_arm(job);
dma_fence_put(fence);
- fence = dma_fence_get(&job->drm.s_fence->finished);
- xe_sched_job_push(job);
+ fence = xe_migrate_job_push(m, job);
dma_fence_put(m->fence);
m->fence = dma_fence_get(fence);
@@ -1146,6 +1456,9 @@ struct xe_lrc *xe_migrate_lrc(struct xe_migrate *migrate)
return migrate->q->lrc[0];
}
+/* XXX: With CPU binds this can be removed in a follow up */
+#define NUM_KERNEL_PDE 15
+
static u64 migrate_vm_ppgtt_addr_tlb_inval(void)
{
/*
@@ -1484,10 +1797,8 @@ struct dma_fence *xe_migrate_vram_copy_chunk(struct xe_bo *vram_bo, u64 vram_off
DMA_RESV_USAGE_BOOKKEEP));
scoped_guard(mutex, &m->job_mutex) {
- xe_sched_job_arm(job);
dma_fence_put(fence);
- fence = dma_fence_get(&job->drm.s_fence->finished);
- xe_sched_job_push(job);
+ fence = xe_migrate_job_push(m, job);
dma_fence_put(m->fence);
m->fence = dma_fence_get(fence);
@@ -1721,10 +2032,8 @@ struct dma_fence *xe_migrate_clear(struct xe_migrate *m,
}
mutex_lock(&m->job_mutex);
- xe_sched_job_arm(job);
dma_fence_put(fence);
- fence = dma_fence_get(&job->drm.s_fence->finished);
- xe_sched_job_push(job);
+ fence = xe_migrate_job_push(m, job);
dma_fence_put(m->fence);
m->fence = dma_fence_get(fence);
@@ -1754,55 +2063,6 @@ err_sync:
return fence;
}
-static void write_pgtable(struct xe_tile *tile, struct xe_bb *bb, u64 ppgtt_ofs,
- const struct xe_vm_pgtable_update_op *pt_op,
- const struct xe_vm_pgtable_update *update,
- struct xe_migrate_pt_update *pt_update)
-{
- const struct xe_migrate_pt_update_ops *ops = pt_update->ops;
- u32 chunk;
- u32 ofs = update->ofs, size = update->qwords;
-
- /*
- * If we have 512 entries (max), we would populate it ourselves,
- * and update the PDE above it to the new pointer.
- * The only time this can only happen if we have to update the top
- * PDE. This requires a BO that is almost vm->size big.
- *
- * This shouldn't be possible in practice.. might change when 16K
- * pages are used. Hence the assert.
- */
- xe_tile_assert(tile, update->qwords < MAX_NUM_PTE);
- if (!ppgtt_ofs)
- ppgtt_ofs = xe_migrate_vram_ofs(tile_to_xe(tile),
- xe_bo_addr(update->pt_bo, 0,
- XE_PAGE_SIZE), false);
-
- do {
- u64 addr = ppgtt_ofs + ofs * 8;
-
- chunk = min(size, MAX_PTE_PER_SDI);
-
- /* Ensure populatefn can do memset64 by aligning bb->cs */
- if (!(bb->len & 1))
- bb->cs[bb->len++] = MI_NOOP;
-
- bb->cs[bb->len++] = MI_STORE_DATA_IMM | MI_SDI_NUM_QW(chunk);
- bb->cs[bb->len++] = lower_32_bits(addr);
- bb->cs[bb->len++] = upper_32_bits(addr);
- if (pt_op->bind)
- ops->populate(pt_update, tile, NULL, bb->cs + bb->len,
- ofs, chunk, update);
- else
- ops->clear(pt_update, tile, NULL, bb->cs + bb->len,
- ofs, chunk, update);
-
- bb->len += chunk * 2;
- ofs += chunk;
- size -= chunk;
- } while (size);
-}
-
struct xe_vm *xe_migrate_get_vm(struct xe_migrate *m)
{
return xe_vm_get(m->q->vm);
@@ -1818,284 +2078,6 @@ struct migrate_test_params {
container_of(_priv, struct migrate_test_params, base)
#endif
-static struct dma_fence *
-xe_migrate_update_pgtables_cpu(struct xe_migrate *m,
- struct xe_migrate_pt_update *pt_update)
-{
- XE_TEST_DECLARE(struct migrate_test_params *test =
- to_migrate_test_params
- (xe_cur_kunit_priv(XE_TEST_LIVE_MIGRATE));)
- const struct xe_migrate_pt_update_ops *ops = pt_update->ops;
- struct xe_vm *vm = pt_update->vops->vm;
- struct xe_vm_pgtable_update_ops *pt_update_ops =
- &pt_update->vops->pt_update_ops[pt_update->tile_id];
- int err;
- u32 i, j;
-
- if (XE_TEST_ONLY(test && test->force_gpu))
- return ERR_PTR(-ETIME);
-
- if (ops->pre_commit) {
- pt_update->job = NULL;
- err = ops->pre_commit(pt_update);
- if (err)
- return ERR_PTR(err);
- }
-
- for (i = 0; i < pt_update_ops->num_ops; ++i) {
- const struct xe_vm_pgtable_update_op *pt_op =
- &pt_update_ops->ops[i];
-
- for (j = 0; j < pt_op->num_entries; j++) {
- const struct xe_vm_pgtable_update *update =
- &pt_op->entries[j];
-
- if (pt_op->bind)
- ops->populate(pt_update, m->tile,
- &update->pt_bo->vmap, NULL,
- update->ofs, update->qwords,
- update);
- else
- ops->clear(pt_update, m->tile,
- &update->pt_bo->vmap, NULL,
- update->ofs, update->qwords, update);
- }
- }
-
- trace_xe_vm_cpu_bind(vm);
- xe_device_wmb(vm->xe);
-
- return dma_fence_get_stub();
-}
-
-static struct dma_fence *
-__xe_migrate_update_pgtables(struct xe_migrate *m,
- struct xe_migrate_pt_update *pt_update,
- struct xe_vm_pgtable_update_ops *pt_update_ops)
-{
- const struct xe_migrate_pt_update_ops *ops = pt_update->ops;
- struct xe_tile *tile = m->tile;
- struct xe_gt *gt = tile->primary_gt;
- struct xe_device *xe = tile_to_xe(tile);
- struct xe_sched_job *job;
- struct dma_fence *fence;
- struct drm_suballoc *sa_bo = NULL;
- struct xe_bb *bb;
- u32 i, j, batch_size = 0, ppgtt_ofs, update_idx, page_ofs = 0;
- u32 num_updates = 0, current_update = 0;
- u64 addr;
- int err = 0;
- bool is_migrate = pt_update_ops->q == m->q;
- bool usm = is_migrate && xe->info.has_usm;
-
- for (i = 0; i < pt_update_ops->num_ops; ++i) {
- struct xe_vm_pgtable_update_op *pt_op = &pt_update_ops->ops[i];
- struct xe_vm_pgtable_update *updates = pt_op->entries;
-
- num_updates += pt_op->num_entries;
- for (j = 0; j < pt_op->num_entries; ++j) {
- u32 num_cmds = DIV_ROUND_UP(updates[j].qwords,
- MAX_PTE_PER_SDI);
-
- /* align noop + MI_STORE_DATA_IMM cmd prefix */
- batch_size += 4 * num_cmds + updates[j].qwords * 2;
- }
- }
-
- /* fixed + PTE entries */
- if (IS_DGFX(xe))
- batch_size += 2;
- else
- batch_size += 6 * (num_updates / MAX_PTE_PER_SDI + 1) +
- num_updates * 2;
-
- bb = xe_bb_new(gt, batch_size, usm);
- if (IS_ERR(bb))
- return ERR_CAST(bb);
-
- /* For sysmem PTE's, need to map them in our hole.. */
- if (!IS_DGFX(xe)) {
- u16 pat_index = xe_cache_pat_idx(xe, XE_CACHE_WB);
- u32 ptes, ofs;
-
- ppgtt_ofs = NUM_KERNEL_PDE - 1;
- if (!is_migrate) {
- u32 num_units = DIV_ROUND_UP(num_updates,
- NUM_VMUSA_WRITES_PER_UNIT);
-
- if (num_units > m->vm_update_sa.size) {
- err = -ENOBUFS;
- goto err_bb;
- }
- sa_bo = drm_suballoc_new(&m->vm_update_sa, num_units,
- GFP_KERNEL, true, 0);
- if (IS_ERR(sa_bo)) {
- err = PTR_ERR(sa_bo);
- goto err_bb;
- }
-
- ppgtt_ofs = NUM_KERNEL_PDE +
- (drm_suballoc_soffset(sa_bo) /
- NUM_VMUSA_UNIT_PER_PAGE);
- page_ofs = (drm_suballoc_soffset(sa_bo) %
- NUM_VMUSA_UNIT_PER_PAGE) *
- VM_SA_UPDATE_UNIT_SIZE;
- }
-
- /* Map our PT's to gtt */
- i = 0;
- j = 0;
- ptes = num_updates;
- ofs = ppgtt_ofs * XE_PAGE_SIZE + page_ofs;
- while (ptes) {
- u32 chunk = min(MAX_PTE_PER_SDI, ptes);
- u32 idx = 0;
-
- bb->cs[bb->len++] = MI_STORE_DATA_IMM |
- MI_SDI_NUM_QW(chunk);
- bb->cs[bb->len++] = ofs;
- bb->cs[bb->len++] = 0; /* upper_32_bits */
-
- for (; i < pt_update_ops->num_ops; ++i) {
- struct xe_vm_pgtable_update_op *pt_op =
- &pt_update_ops->ops[i];
- struct xe_vm_pgtable_update *updates = pt_op->entries;
-
- for (; j < pt_op->num_entries; ++j, ++current_update, ++idx) {
- struct xe_vm *vm = pt_update->vops->vm;
- struct xe_bo *pt_bo = updates[j].pt_bo;
-
- if (idx == chunk)
- goto next_cmd;
-
- xe_tile_assert(tile, xe_bo_size(pt_bo) == SZ_4K);
-
- /* Map a PT at most once */
- if (pt_bo->update_index < 0)
- pt_bo->update_index = current_update;
-
- addr = vm->pt_ops->pte_encode_bo(pt_bo, 0,
- pat_index, 0);
- bb->cs[bb->len++] = lower_32_bits(addr);
- bb->cs[bb->len++] = upper_32_bits(addr);
- }
-
- j = 0;
- }
-
-next_cmd:
- ptes -= chunk;
- ofs += chunk * sizeof(u64);
- }
-
- bb->cs[bb->len++] = MI_BATCH_BUFFER_END;
- update_idx = bb->len;
-
- addr = xe_migrate_vm_addr(ppgtt_ofs, 0) +
- (page_ofs / sizeof(u64)) * XE_PAGE_SIZE;
- for (i = 0; i < pt_update_ops->num_ops; ++i) {
- struct xe_vm_pgtable_update_op *pt_op =
- &pt_update_ops->ops[i];
- struct xe_vm_pgtable_update *updates = pt_op->entries;
-
- for (j = 0; j < pt_op->num_entries; ++j) {
- struct xe_bo *pt_bo = updates[j].pt_bo;
-
- write_pgtable(tile, bb, addr +
- pt_bo->update_index * XE_PAGE_SIZE,
- pt_op, &updates[j], pt_update);
- }
- }
- } else {
- /* phys pages, no preamble required */
- bb->cs[bb->len++] = MI_BATCH_BUFFER_END;
- update_idx = bb->len;
-
- for (i = 0; i < pt_update_ops->num_ops; ++i) {
- struct xe_vm_pgtable_update_op *pt_op =
- &pt_update_ops->ops[i];
- struct xe_vm_pgtable_update *updates = pt_op->entries;
-
- for (j = 0; j < pt_op->num_entries; ++j)
- write_pgtable(tile, bb, 0, pt_op, &updates[j],
- pt_update);
- }
- }
-
- job = xe_bb_create_migration_job(pt_update_ops->q, bb,
- xe_migrate_batch_base(m, usm),
- update_idx);
- if (IS_ERR(job)) {
- err = PTR_ERR(job);
- goto err_sa;
- }
-
- xe_sched_job_add_migrate_flush(job, MI_INVALIDATE_TLB);
-
- if (ops->pre_commit) {
- pt_update->job = job;
- err = ops->pre_commit(pt_update);
- if (err)
- goto err_job;
- }
- if (is_migrate)
- mutex_lock(&m->job_mutex);
-
- xe_sched_job_arm(job);
- fence = dma_fence_get(&job->drm.s_fence->finished);
- xe_sched_job_push(job);
-
- if (is_migrate)
- mutex_unlock(&m->job_mutex);
-
- xe_bb_free(bb, fence);
- drm_suballoc_free(sa_bo, fence);
-
- return fence;
-
-err_job:
- xe_sched_job_put(job);
-err_sa:
- drm_suballoc_free(sa_bo, NULL);
-err_bb:
- xe_bb_free(bb, NULL);
- return ERR_PTR(err);
-}
-
-/**
- * xe_migrate_update_pgtables() - Pipelined page-table update
- * @m: The migrate context.
- * @pt_update: PT update arguments
- *
- * Perform a pipelined page-table update. The update descriptors are typically
- * built under the same lock critical section as a call to this function. If
- * using the default engine for the updates, they will be performed in the
- * order they grab the job_mutex. If different engines are used, external
- * synchronization is needed for overlapping updates to maintain page-table
- * consistency. Note that the meaning of "overlapping" is that the updates
- * touch the same page-table, which might be a higher-level page-directory.
- * If no pipelining is needed, then updates may be performed by the cpu.
- *
- * Return: A dma_fence that, when signaled, indicates the update completion.
- */
-struct dma_fence *
-xe_migrate_update_pgtables(struct xe_migrate *m,
- struct xe_migrate_pt_update *pt_update)
-
-{
- struct xe_vm_pgtable_update_ops *pt_update_ops =
- &pt_update->vops->pt_update_ops[pt_update->tile_id];
- struct dma_fence *fence;
-
- fence = xe_migrate_update_pgtables_cpu(m, pt_update);
-
- /* -ETIME indicates a job is needed, anything else is legit error */
- if (!IS_ERR(fence) || PTR_ERR(fence) != -ETIME)
- return fence;
-
- return __xe_migrate_update_pgtables(m, pt_update, pt_update_ops);
-}
-
/**
* xe_migrate_wait() - Complete all operations using the xe_migrate context
* @m: Migrate context to wait for.
@@ -2327,9 +2309,7 @@ static struct dma_fence *xe_migrate_vram(struct xe_migrate *m,
}
mutex_lock(&m->job_mutex);
- xe_sched_job_arm(job);
- fence = dma_fence_get(&job->drm.s_fence->finished);
- xe_sched_job_push(job);
+ fence = xe_migrate_job_push(m, job);
dma_fence_put(m->fence);
m->fence = dma_fence_get(fence);
@@ -2597,56 +2577,6 @@ out_err:
return IS_ERR(fence) ? PTR_ERR(fence) : 0;
}
-/**
- * xe_migrate_job_lock() - Lock migrate job lock
- * @m: The migration context.
- * @q: Queue associated with the operation which requires a lock
- *
- * Lock the migrate job lock if the queue is a migration queue, otherwise
- * assert the VM's dma-resv is held (user queue's have own locking).
- */
-void xe_migrate_job_lock(struct xe_migrate *m, struct xe_exec_queue *q)
-{
- bool is_migrate = q == m->q;
-
- if (is_migrate)
- mutex_lock(&m->job_mutex);
- else
- xe_vm_assert_held(q->user_vm); /* User queues VM's should be locked */
-}
-
-/**
- * xe_migrate_job_unlock() - Unlock migrate job lock
- * @m: The migration context.
- * @q: Queue associated with the operation which requires a lock
- *
- * Unlock the migrate job lock if the queue is a migration queue, otherwise
- * assert the VM's dma-resv is held (user queue's have own locking).
- */
-void xe_migrate_job_unlock(struct xe_migrate *m, struct xe_exec_queue *q)
-{
- bool is_migrate = q == m->q;
-
- if (is_migrate)
- mutex_unlock(&m->job_mutex);
- else
- xe_vm_assert_held(q->user_vm); /* User queues VM's should be locked */
-}
-
-#if IS_ENABLED(CONFIG_PROVE_LOCKING)
-/**
- * xe_migrate_job_lock_assert() - Assert migrate job lock held of queue
- * @q: Migrate queue
- */
-void xe_migrate_job_lock_assert(struct xe_exec_queue *q)
-{
- struct xe_migrate *m = gt_to_tile(q->gt)->migrate;
-
- xe_gt_assert(q->gt, q == m->q);
- lockdep_assert_held(&m->job_mutex);
-}
-#endif
-
#if IS_ENABLED(CONFIG_DRM_XE_KUNIT_TEST)
#include "tests/xe_migrate.c"
#endif
@@ -2696,10 +2626,7 @@ int xe_migrate_debug_ccs_overlap(struct xe_migrate *m,
xe_sched_job_add_migrate_flush(job, MI_FLUSH_DW_CCS);
mutex_lock(&m->job_mutex);
- xe_sched_job_arm(job);
-
- fence = dma_fence_get(&job->drm.s_fence->finished);
- xe_sched_job_push(job);
+ fence = xe_migrate_job_push(m, job);
mutex_unlock(&m->job_mutex);
dma_fence_wait(fence, false);
diff --git a/drivers/gpu/drm/xe/xe_migrate.h b/drivers/gpu/drm/xe/xe_migrate.h
index a9acc62f78f0..2e8be10fdb71 100644
--- a/drivers/gpu/drm/xe/xe_migrate.h
+++ b/drivers/gpu/drm/xe/xe_migrate.h
@@ -24,6 +24,7 @@ struct xe_pt;
struct xe_tile;
struct xe_vm;
struct xe_vm_pgtable_update;
+struct xe_vm_pgtable_update_op;
struct xe_vma;
enum xe_sriov_vf_ccs_rw_ctxs;
@@ -33,83 +34,6 @@ enum xe_migrate_copy_dir {
XE_MIGRATE_COPY_TO_SRAM,
};
-/**
- * struct xe_migrate_pt_update_ops - Callbacks for the
- * xe_migrate_update_pgtables() function.
- */
-struct xe_migrate_pt_update_ops {
- /**
- * @populate: Populate a command buffer or page-table with ptes.
- * @pt_update: Embeddable callback argument.
- * @tile: The tile for the current operation.
- * @map: struct iosys_map into the memory to be populated.
- * @pos: If @map is NULL, map into the memory to be populated.
- * @ofs: qword offset into @map, unused if @map is NULL.
- * @num_qwords: Number of qwords to write.
- * @update: Information about the PTEs to be inserted.
- *
- * This interface is intended to be used as a callback into the
- * page-table system to populate command buffers or shared
- * page-tables with PTEs.
- */
- void (*populate)(struct xe_migrate_pt_update *pt_update,
- struct xe_tile *tile, struct iosys_map *map,
- void *pos, u32 ofs, u32 num_qwords,
- const struct xe_vm_pgtable_update *update);
- /**
- * @clear: Clear a command buffer or page-table with ptes.
- * @pt_update: Embeddable callback argument.
- * @tile: The tile for the current operation.
- * @map: struct iosys_map into the memory to be populated.
- * @pos: If @map is NULL, map into the memory to be populated.
- * @ofs: qword offset into @map, unused if @map is NULL.
- * @num_qwords: Number of qwords to write.
- * @update: Information about the PTEs to be inserted.
- *
- * This interface is intended to be used as a callback into the
- * page-table system to populate command buffers or shared
- * page-tables with PTEs.
- */
- void (*clear)(struct xe_migrate_pt_update *pt_update,
- struct xe_tile *tile, struct iosys_map *map,
- void *pos, u32 ofs, u32 num_qwords,
- const struct xe_vm_pgtable_update *update);
-
- /**
- * @pre_commit: Callback to be called just before arming the
- * sched_job.
- * @pt_update: Pointer to embeddable callback argument.
- *
- * Return: 0 on success, negative error code on error.
- */
- int (*pre_commit)(struct xe_migrate_pt_update *pt_update);
-};
-
-/**
- * struct xe_migrate_pt_update - Argument to the
- * struct xe_migrate_pt_update_ops callbacks.
- *
- * Intended to be subclassed to support additional arguments if necessary.
- */
-struct xe_migrate_pt_update {
- /** @ops: Pointer to the struct xe_migrate_pt_update_ops callbacks */
- const struct xe_migrate_pt_update_ops *ops;
- /** @vops: VMA operations */
- struct xe_vma_ops *vops;
- /** @job: The job if a GPU page-table update. NULL otherwise */
- struct xe_sched_job *job;
- /**
- * @ijob: The TLB invalidation job for primary GT. NULL otherwise
- */
- struct xe_tlb_inval_job *ijob;
- /**
- * @mjob: The TLB invalidation job for media GT. NULL otherwise
- */
- struct xe_tlb_inval_job *mjob;
- /** @tile_id: Tile ID of the update */
- u8 tile_id;
-};
-
struct xe_migrate *xe_migrate_alloc(struct xe_tile *tile);
int xe_migrate_init(struct xe_migrate *m);
@@ -165,27 +89,14 @@ struct dma_fence *xe_migrate_clear(struct xe_migrate *m,
struct xe_vm *xe_migrate_get_vm(struct xe_migrate *m);
-struct dma_fence *
-xe_migrate_update_pgtables(struct xe_migrate *m,
- struct xe_migrate_pt_update *pt_update);
-
void xe_migrate_wait(struct xe_migrate *m);
-#if IS_ENABLED(CONFIG_PROVE_LOCKING)
-void xe_migrate_job_lock_assert(struct xe_exec_queue *q);
-#else
-static inline void xe_migrate_job_lock_assert(struct xe_exec_queue *q)
-{
-}
-#endif
-
-void xe_migrate_job_lock(struct xe_migrate *m, struct xe_exec_queue *q);
-void xe_migrate_job_unlock(struct xe_migrate *m, struct xe_exec_queue *q);
-
#if IS_ENABLED(CONFIG_DRM_XE_DEBUG_MEM)
int xe_migrate_debug_ccs_overlap(struct xe_migrate *m,
struct xe_bo *scratch_bo,
bool write_to_ccs);
#endif
+void xe_migrate_ulls_enter(struct xe_migrate *m);
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_mmio.c b/drivers/gpu/drm/xe/xe_mmio.c
index 7fa18dfcb5a2..455a07d79cd9 100644
--- a/drivers/gpu/drm/xe/xe_mmio.c
+++ b/drivers/gpu/drm/xe/xe_mmio.c
@@ -320,6 +320,7 @@ u64 xe_mmio_read64_2x32(struct xe_mmio *mmio, struct xe_reg reg)
return (u64)udw << 32 | ldw;
}
+#define __XE_MMIO_WAIT_MAX_BACKOFF_100MS (100 * USEC_PER_MSEC)
static int __xe_mmio_wait32(struct xe_mmio *mmio, struct xe_reg reg, u32 mask, u32 val,
u32 timeout_us, u32 *out_val, bool atomic, bool expect_match)
{
@@ -350,10 +351,10 @@ static int __xe_mmio_wait32(struct xe_mmio *mmio, struct xe_reg reg, u32 mask, u
wait = ktime_us_delta(end, cur);
if (atomic)
- udelay(wait);
+ udelay(min_t(s64, wait, (MAX_UDELAY_MS * 1000)));
else
usleep_range(wait, wait << 1);
- wait <<= 1;
+ wait = min_t(s64, wait << 1, __XE_MMIO_WAIT_MAX_BACKOFF_100MS);
}
if (ret != 0) {
diff --git a/drivers/gpu/drm/xe/xe_pagefault.c b/drivers/gpu/drm/xe/xe_pagefault.c
index f9e5bd41cfd1..5247321c805e 100644
--- a/drivers/gpu/drm/xe/xe_pagefault.c
+++ b/drivers/gpu/drm/xe/xe_pagefault.c
@@ -13,8 +13,8 @@
#include "xe_gt_printk.h"
#include "xe_gt_types.h"
#include "xe_gt_stats.h"
-#include "xe_hw_engine.h"
#include "xe_log.h"
+#include "xe_migrate.h"
#include "xe_pagefault.h"
#include "xe_pagefault_types.h"
#include "xe_pm.h"
@@ -267,6 +267,7 @@ static struct xe_vm *xe_pagefault_asid_to_vm(struct xe_pagefault *pf, u32 asid)
static int xe_pagefault_service(struct xe_pagefault *pf)
{
struct xe_gt *gt = pf->gt;
+ struct xe_device *xe = gt_to_xe(gt);
struct xe_vm *vm;
struct xe_vma *vma = NULL;
int err;
@@ -278,8 +279,15 @@ static int xe_pagefault_service(struct xe_pagefault *pf)
return -EFAULT;
vm = xe_pagefault_asid_to_vm(pf, asid);
- if (IS_ERR(vm))
- return PTR_ERR(vm);
+ if (IS_ERR(vm)) {
+ err = PTR_ERR(vm);
+ trace_xe_pagefault_fail(xe, NULL, NULL, asid,
+ xe_pagefault_addr(pf),
+ xe_pagefault_get_error(pf), err);
+ return err;
+ }
+
+ xe_migrate_ulls_enter(gt_to_tile(gt)->migrate);
down_read(&vm->lock);
@@ -312,6 +320,11 @@ static int xe_pagefault_service(struct xe_pagefault *pf)
err = xe_pagefault_handle_vma(gt, vma, pf, atomic);
unlock_vm:
+ if (err)
+ trace_xe_pagefault_fail(xe, vm, vma, asid,
+ xe_pagefault_addr(pf),
+ xe_pagefault_get_error(pf), err);
+
up_read(&vm->lock);
xe_vm_put(vm);
@@ -607,36 +620,12 @@ static const char *xe_pagefault_error_to_str(enum xe_pagefault_error error)
static void xe_pagefault_print(struct xe_pagefault *pf)
{
- u8 engine_class = FIELD_GET(XE_PAGEFAULT_ENGINE_CLASS_MASK,
- pf->consumer.engine_class_instance);
- u64 addr = xe_pagefault_addr(pf);
-
- xe_gt_info(pf->gt, "\n\tASID: %lu\n"
- "\tFaulted Address: 0x%08x%08x\n"
- "\tFaultType: %lu\n"
- "\tAccessType: %lu\n"
- "\tFaultLevel: %lu\n"
- "\tEngineClass: %d %s\n"
- "\tEngineInstance: %lu\n"
- "\tSRCID: 0x%02lx\n"
- "\tError: %s\n",
- FIELD_GET(XE_PAGEFAULT_ASID_MASK,
- pf->consumer.id),
- upper_32_bits(addr),
- lower_32_bits(addr),
- FIELD_GET(XE_PAGEFAULT_TYPE_MASK,
- pf->consumer.fault_type_level),
- FIELD_GET(XE_PAGEFAULT_ACCESS_TYPE_MASK,
- pf->consumer.access_type),
- FIELD_GET(XE_PAGEFAULT_LEVEL_MASK,
- pf->consumer.fault_type_level),
- engine_class,
- xe_hw_engine_class_to_str(engine_class),
- FIELD_GET(XE_PAGEFAULT_ENGINE_INSTANCE_MASK,
- pf->consumer.engine_class_instance),
- FIELD_GET(XE_PAGEFAULT_SRCID_MASK,
- pf->consumer.id),
- xe_pagefault_error_to_str(xe_pagefault_get_error(pf)));
+ enum xe_pagefault_error err = xe_pagefault_get_error(pf);
+
+ if (WARN_ON_ONCE(!pf->producer.ops->print))
+ return;
+
+ pf->producer.ops->print(pf, xe_pagefault_error_to_str(err));
}
static void xe_pagefault_save_to_vm(struct xe_device *xe, struct xe_pagefault *pf)
diff --git a/drivers/gpu/drm/xe/xe_pagefault_types.h b/drivers/gpu/drm/xe/xe_pagefault_types.h
index 8ae6b9848bdc..ee467117afe9 100644
--- a/drivers/gpu/drm/xe/xe_pagefault_types.h
+++ b/drivers/gpu/drm/xe/xe_pagefault_types.h
@@ -139,6 +139,16 @@ struct xe_pagefault_ops {
* Page fault producer ends acknowledgment from the consumer.
*/
void (*ack_fault_end)(void *private);
+ /**
+ * @print: Print pagefault info
+ * @pf: Page fault
+ * @err_str: Consumer servicing error string
+ *
+ * Print pagefault identification fields decoded from producer,
+ * along with consumer-specific @err_str prints.
+ * Must be implemented by all producers.
+ */
+ void (*print)(struct xe_pagefault *pf, const char *err_str);
};
/**
diff --git a/drivers/gpu/drm/xe/xe_pci.c b/drivers/gpu/drm/xe/xe_pci.c
index d66d5a0339ae..e44b74b9aa27 100644
--- a/drivers/gpu/drm/xe/xe_pci.c
+++ b/drivers/gpu/drm/xe/xe_pci.c
@@ -371,6 +371,7 @@ static const __maybe_unused struct xe_device_desc pvc_desc = {
.has_display = false,
.has_drm_ras = true,
.has_gsc_nvm = 1,
+ .has_pt_mirror = 1,
.has_heci_gscfi = 1,
.max_gt_per_tile = 1,
.max_remote_tiles = 1,
@@ -460,6 +461,7 @@ static const struct xe_device_desc nvls_desc = {
.has_flat_ccs = 1,
.has_pre_prod_wa = 1,
.has_sriov = true,
+ .has_pxp = true,
.max_gt_per_tile = 2,
MULTI_LRC_MASK,
.va_bits = 48,
@@ -471,6 +473,7 @@ static const struct xe_device_desc cri_desc = {
PLATFORM(CRESCENTISLAND),
.dma_mask_size = 52,
.has_display = false,
+ .has_device_uid = true,
.has_drm_ras = true,
.has_flat_ccs = false,
.has_gsc_nvm = 1,
@@ -791,6 +794,7 @@ static int xe_info_init_early(struct xe_device *xe,
xe->info.is_dgfx = desc->is_dgfx;
xe->info.has_cached_pt = desc->has_cached_pt;
+ xe->info.has_device_uid = desc->has_device_uid;
xe->info.has_drm_ras = desc->has_drm_ras;
xe->info.has_fan_control = desc->has_fan_control;
/* runtime fusing may force flat_ccs to disabled later */
@@ -806,6 +810,7 @@ static int xe_info_init_early(struct xe_device *xe,
xe->info.has_mert = desc->has_mert;
xe->info.has_page_reclaim_hw_assist = desc->has_page_reclaim_hw_assist;
xe->info.has_pre_prod_wa = desc->has_pre_prod_wa;
+ xe->info.has_pt_mirror = desc->has_pt_mirror;
xe->info.has_pxp = desc->has_pxp;
xe->info.has_soc_remapper_sysctrl = desc->has_soc_remapper_sysctrl;
xe->info.has_soc_remapper_telem = desc->has_soc_remapper_telem;
diff --git a/drivers/gpu/drm/xe/xe_pci_sriov.c b/drivers/gpu/drm/xe/xe_pci_sriov.c
index c023a2a1fc17..31abcee55211 100644
--- a/drivers/gpu/drm/xe/xe_pci_sriov.c
+++ b/drivers/gpu/drm/xe/xe_pci_sriov.c
@@ -177,6 +177,7 @@ static int pf_enable_vfs(struct xe_device *xe, int num_vfs)
return num_vfs;
failed:
+ pf_resize_vf_vram_bar(xe, xe->sriov.pf.device_total_vfs);
xe_sriov_pf_unprovision_vfs(xe, num_vfs);
xe_pm_runtime_put(xe);
pf_finish_vfs_enabling(xe);
@@ -204,6 +205,8 @@ static int pf_disable_vfs(struct xe_device *xe)
pci_disable_sriov(pdev);
+ pf_resize_vf_vram_bar(xe, xe->sriov.pf.device_total_vfs);
+
xe_sriov_pf_reprovision_default(xe);
pf_reset_vfs(xe, num_vfs);
diff --git a/drivers/gpu/drm/xe/xe_pci_types.h b/drivers/gpu/drm/xe/xe_pci_types.h
index fed509ff601e..448bbe5ba1d7 100644
--- a/drivers/gpu/drm/xe/xe_pci_types.h
+++ b/drivers/gpu/drm/xe/xe_pci_types.h
@@ -40,6 +40,7 @@ struct xe_device_desc {
u8 has_cached_pt:1;
u8 has_display:1;
+ u8 has_device_uid:1;
u8 has_drm_ras:1;
u8 has_fan_control:1;
u8 has_flat_ccs:1;
@@ -52,8 +53,9 @@ struct xe_device_desc {
u8 has_mbx_power_limits:1;
u8 has_mbx_thermal_info:1;
u8 has_mert:1;
- u8 has_pre_prod_wa:1;
u8 has_page_reclaim_hw_assist:1;
+ u8 has_pre_prod_wa:1;
+ u8 has_pt_mirror:1;
u8 has_pxp:1;
u8 has_soc_remapper_sysctrl:1;
u8 has_soc_remapper_telem:1;
diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c
index 4b351dbf6572..6ad4bbaa1860 100644
--- a/drivers/gpu/drm/xe/xe_pt.c
+++ b/drivers/gpu/drm/xe/xe_pt.c
@@ -7,12 +7,12 @@
#include "regs/xe_gtt_defs.h"
#include "xe_bo.h"
+#include "xe_cpu_bind.h"
#include "xe_device.h"
#include "xe_drm_client.h"
#include "xe_exec_queue.h"
#include "xe_gt.h"
#include "xe_gt_stats.h"
-#include "xe_migrate.h"
#include "xe_page_reclaim.h"
#include "xe_pat.h"
#include "xe_pt_types.h"
@@ -206,6 +206,7 @@ unsigned int xe_pt_shift(unsigned int level)
*/
void xe_pt_destroy(struct xe_pt *pt, u32 flags, struct llist_head *deferred)
{
+ bool added = false;
int i;
if (!pt)
@@ -213,7 +214,9 @@ void xe_pt_destroy(struct xe_pt *pt, u32 flags, struct llist_head *deferred)
XE_WARN_ON(!list_empty(&pt->bo->ttm.base.gpuva.list));
xe_bo_unpin(pt->bo);
- xe_bo_put_deferred(pt->bo, deferred);
+ xe_bo_put_deferred(pt->bo, deferred, &added);
+ if (added)
+ xe_assert(pt->bo->vm->xe, !kref_read(&pt->bo->ttm.base.refcount));
if (pt->level > 0 && pt->num_live) {
struct xe_pt_dir *pt_dir = as_xe_pt_dir(pt);
@@ -376,7 +379,7 @@ xe_pt_new_shared(struct xe_walk_update *wupd, struct xe_pt *parent,
entry->pt = parent;
entry->flags = 0;
entry->qwords = 0;
- entry->pt_bo->update_index = -1;
+ entry->level = parent->level;
if (alloc_entries) {
entry->pt_entries = kmalloc_objs(*entry->pt_entries, XE_PDES);
@@ -801,7 +804,7 @@ xe_pt_stage_bind(struct xe_tile *tile, struct xe_vma *vma,
.wupd.entries = entries,
.clear_pt = clear_pt,
};
- struct xe_pt *pt = vm->pt_root[tile->id];
+ struct xe_pt *pt = xe_vm_pt_root(vm, tile->id);
int ret;
bool is_purged = false;
@@ -1002,6 +1005,11 @@ static int xe_pt_zap_ptes_entry(struct xe_ptw *parent, pgoff_t offset,
return 0;
}
+static bool pt_mirroring_disabled_for_tile(struct xe_vm *vm, u8 tile_id)
+{
+ return !vm->xe->info.has_pt_mirror && tile_id;
+}
+
static const struct xe_pt_walk_ops xe_pt_zap_ptes_ops = {
.pt_entry = xe_pt_zap_ptes_entry,
};
@@ -1043,6 +1051,9 @@ bool xe_pt_zap_ptes(struct xe_tile *tile, struct xe_vma *vma)
if (!(pt_mask & BIT(tile->id)))
return false;
+ if (pt_mirroring_disabled_for_tile(xe_vma_vm(vma), tile->id))
+ return true;
+
(void)xe_pt_walk_shared(&pt->base, pt->level, xe_vma_start(vma),
xe_vma_end(vma), &xe_walk.base);
@@ -1095,6 +1106,9 @@ bool xe_pt_zap_ptes_range(struct xe_tile *tile, struct xe_vm *vm,
if (!(pt_mask & BIT(tile->id)))
return false;
+ if (pt_mirroring_disabled_for_tile(vm, tile->id))
+ return true;
+
(void)xe_pt_walk_shared(&pt->base, pt->level, xe_svm_range_start(range),
xe_svm_range_end(range), &xe_walk.base);
@@ -1102,32 +1116,18 @@ bool xe_pt_zap_ptes_range(struct xe_tile *tile, struct xe_vm *vm,
}
static void
-xe_vm_populate_pgtable(struct xe_migrate_pt_update *pt_update, struct xe_tile *tile,
- struct iosys_map *map, void *data,
- u32 qword_ofs, u32 num_qwords,
+xe_vm_populate_pgtable(struct xe_tile *tile, struct iosys_map *map,
const struct xe_vm_pgtable_update *update)
{
struct xe_pt_entry *ptes = update->pt_entries;
- u64 *ptr = data;
u32 i;
- /*
- * @qword_ofs is the absolute entry offset within the page table, while
- * @ptes is indexed relative to @update->ofs (its first entry). The GPU
- * path (write_pgtable) splits a single update into MAX_PTE_PER_SDI-sized
- * chunks, calling this with an advancing @qword_ofs but a fresh @data
- * pointer per chunk, so translate back into a @ptes index rather than
- * assuming the chunk starts at ptes[0].
- */
- for (i = 0; i < num_qwords; i++) {
- u32 idx = qword_ofs - update->ofs + i;
+ xe_assert(tile_to_xe(tile), map);
+ xe_assert(tile_to_xe(tile), !iosys_map_is_null(map));
- if (map)
- xe_map_wr(tile_to_xe(tile), map, (qword_ofs + i) *
- sizeof(u64), u64, ptes[idx].pte);
- else
- ptr[i] = ptes[idx].pte;
- }
+ for (i = 0; i < update->qwords; i++)
+ xe_map_wr(tile_to_xe(tile), map, (update->ofs + i) *
+ sizeof(u64), u64, ptes[i].pte);
}
static void xe_pt_cancel_bind(struct xe_vma *vma,
@@ -1411,11 +1411,9 @@ static int op_add_deps(struct xe_vm *vm, struct xe_vma_op *op,
}
static int xe_pt_vm_dependencies(struct xe_sched_job *job,
- struct xe_tlb_inval_job *ijob,
- struct xe_tlb_inval_job *mjob,
+ struct xe_tlb_inval_job **ijobs,
struct xe_vm *vm,
struct xe_vma_ops *vops,
- struct xe_vm_pgtable_update_ops *pt_update_ops,
struct xe_range_fence_tree *rftree)
{
struct xe_range_fence *rtfence;
@@ -1428,20 +1426,22 @@ static int xe_pt_vm_dependencies(struct xe_sched_job *job,
if (!job && !no_in_syncs(vops->syncs, vops->num_syncs))
return -ETIME;
- if (!job && !xe_exec_queue_is_idle(pt_update_ops->q))
+ if (!job && !xe_exec_queue_is_idle(vops->q, 0))
return -ETIME;
- if (pt_update_ops->wait_vm_bookkeep || pt_update_ops->wait_vm_kernel) {
- err = job_test_add_deps(job, xe_vm_resv(vm),
- pt_update_ops->wait_vm_bookkeep ?
- DMA_RESV_USAGE_BOOKKEEP :
- DMA_RESV_USAGE_KERNEL);
+ if (vops->flags & (XE_VMA_OPS_FLAG_WAIT_VM_BOOKKEEP |
+ XE_VMA_OPS_FLAG_WAIT_VM_KERNEL)) {
+ enum dma_resv_usage usage = DMA_RESV_USAGE_KERNEL;
+
+ if (vops->flags & XE_VMA_OPS_FLAG_WAIT_VM_BOOKKEEP)
+ usage = DMA_RESV_USAGE_BOOKKEEP;
+
+ err = job_test_add_deps(job, xe_vm_resv(vm), usage);
if (err)
return err;
}
- rtfence = xe_range_fence_tree_first(rftree, pt_update_ops->start,
- pt_update_ops->last);
+ rtfence = xe_range_fence_tree_first(rftree, vops->start, vops->last);
while (rtfence) {
fence = rtfence->fence;
@@ -1459,9 +1459,8 @@ static int xe_pt_vm_dependencies(struct xe_sched_job *job,
return err;
}
- rtfence = xe_range_fence_tree_next(rtfence,
- pt_update_ops->start,
- pt_update_ops->last);
+ rtfence = xe_range_fence_tree_next(rtfence, vops->start,
+ vops->last);
}
list_for_each_entry(op, &vops->list, link) {
@@ -1474,14 +1473,11 @@ static int xe_pt_vm_dependencies(struct xe_sched_job *job,
err = xe_sync_entry_add_deps(&vops->syncs[i], job);
if (job) {
- if (ijob) {
- err = xe_tlb_inval_job_alloc_dep(ijob);
- if (err)
- return err;
- }
+ for (i = 0; i < XE_CPU_BIND_INVAL_JOB_COUNT; ++i) {
+ if (!ijobs[i])
+ continue;
- if (mjob) {
- err = xe_tlb_inval_job_alloc_dep(mjob);
+ err = xe_tlb_inval_job_alloc_dep(ijobs[i]);
if (err)
return err;
}
@@ -1490,17 +1486,14 @@ static int xe_pt_vm_dependencies(struct xe_sched_job *job,
return err;
}
-static int xe_pt_pre_commit(struct xe_migrate_pt_update *pt_update)
+static int xe_pt_pre_commit(struct xe_cpu_bind_pt_update *pt_update)
{
struct xe_vma_ops *vops = pt_update->vops;
struct xe_vm *vm = vops->vm;
- struct xe_range_fence_tree *rftree = &vm->rftree[pt_update->tile_id];
- struct xe_vm_pgtable_update_ops *pt_update_ops =
- &vops->pt_update_ops[pt_update->tile_id];
+ struct xe_range_fence_tree *rftree = &vm->rftree;
- return xe_pt_vm_dependencies(pt_update->job, pt_update->ijob,
- pt_update->mjob, vm, pt_update->vops,
- pt_update_ops, rftree);
+ return xe_pt_vm_dependencies(pt_update->job, pt_update->ijobs,
+ vm, vops, rftree);
}
#if IS_ENABLED(CONFIG_DRM_GPUSVM)
@@ -1560,8 +1553,7 @@ static bool xe_pt_userptr_inject_eagain(struct xe_userptr_vma *uvma)
#endif
-static int vma_check_userptr(struct xe_vm *vm, struct xe_vma *vma,
- struct xe_vm_pgtable_update_ops *pt_update)
+static int vma_check_userptr(struct xe_vm *vm, struct xe_vma *vma)
{
struct xe_userptr_vma *uvma;
unsigned long notifier_seq;
@@ -1591,8 +1583,7 @@ static int vma_check_userptr(struct xe_vm *vm, struct xe_vma *vma,
return 0;
}
-static int op_check_svm_userptr(struct xe_vm *vm, struct xe_vma_op *op,
- struct xe_vm_pgtable_update_ops *pt_update)
+static int op_check_svm_userptr(struct xe_vm *vm, struct xe_vma_op *op)
{
int err = 0;
@@ -1603,13 +1594,13 @@ static int op_check_svm_userptr(struct xe_vm *vm, struct xe_vma_op *op,
if (!op->map.immediate && xe_vm_in_fault_mode(vm))
break;
- err = vma_check_userptr(vm, op->map.vma, pt_update);
+ err = vma_check_userptr(vm, op->map.vma);
break;
case DRM_GPUVA_OP_REMAP:
if (op->remap.prev && !op->remap.skip_prev)
- err = vma_check_userptr(vm, op->remap.prev, pt_update);
+ err = vma_check_userptr(vm, op->remap.prev);
if (!err && op->remap.next && !op->remap.skip_next)
- err = vma_check_userptr(vm, op->remap.next, pt_update);
+ err = vma_check_userptr(vm, op->remap.next);
break;
case DRM_GPUVA_OP_UNMAP:
break;
@@ -1629,7 +1620,7 @@ static int op_check_svm_userptr(struct xe_vm *vm, struct xe_vma_op *op,
}
}
} else {
- err = vma_check_userptr(vm, gpuva_to_vma(op->base.prefetch.va), pt_update);
+ err = vma_check_userptr(vm, gpuva_to_vma(op->base.prefetch.va));
}
break;
#if IS_ENABLED(CONFIG_DRM_XE_GPUSVM)
@@ -1655,12 +1646,10 @@ static int op_check_svm_userptr(struct xe_vm *vm, struct xe_vma_op *op,
return err;
}
-static int xe_pt_svm_userptr_pre_commit(struct xe_migrate_pt_update *pt_update)
+static int xe_pt_svm_userptr_pre_commit(struct xe_cpu_bind_pt_update *pt_update)
{
struct xe_vm *vm = pt_update->vops->vm;
struct xe_vma_ops *vops = pt_update->vops;
- struct xe_vm_pgtable_update_ops *pt_update_ops =
- &vops->pt_update_ops[pt_update->tile_id];
struct xe_vma_op *op;
int err;
@@ -1671,7 +1660,7 @@ static int xe_pt_svm_userptr_pre_commit(struct xe_migrate_pt_update *pt_update)
xe_pt_svm_userptr_notifier_lock(vm);
list_for_each_entry(op, &vops->list, link) {
- err = op_check_svm_userptr(vm, op, pt_update_ops);
+ err = op_check_svm_userptr(vm, op);
if (err) {
xe_pt_svm_userptr_notifier_unlock(vm);
break;
@@ -2009,7 +1998,7 @@ static unsigned int xe_pt_stage_unbind(struct xe_tile *tile,
.wupd.entries = entries,
.prl = pt_update_op->prl,
};
- struct xe_pt *pt = vm->pt_root[tile->id];
+ struct xe_pt *pt = xe_vm_pt_root(vm, tile->id);
(void)xe_pt_walk_shared(&pt->base, pt->level, start, end,
&xe_walk.base);
@@ -2018,24 +2007,23 @@ static unsigned int xe_pt_stage_unbind(struct xe_tile *tile,
}
static void
-xe_migrate_clear_pgtable_callback(struct xe_migrate_pt_update *pt_update,
- struct xe_tile *tile, struct iosys_map *map,
- void *ptr, u32 qword_ofs, u32 num_qwords,
- const struct xe_vm_pgtable_update *update)
+xe_pt_clear_pgtable_callback(struct xe_vm *vm, struct xe_tile *tile,
+ struct iosys_map *map,
+ const struct xe_vm_pgtable_update *update)
{
- struct xe_vm *vm = pt_update->vops->vm;
- u64 empty = __xe_pt_empty_pte(tile, vm, update->pt->level);
+ u64 empty = __xe_pt_empty_pte(tile, vm, update->level);
int i;
- if (map && map->is_iomem)
- for (i = 0; i < num_qwords; ++i)
- xe_map_wr(tile_to_xe(tile), map, (qword_ofs + i) *
+ xe_assert(vm->xe, map);
+ xe_assert(vm->xe, !iosys_map_is_null(map));
+
+ if (map->is_iomem)
+ for (i = 0; i < update->qwords; ++i)
+ xe_map_wr(tile_to_xe(tile), map, (update->ofs + i) *
sizeof(u64), u64, empty);
- else if (map)
- memset64(map->vaddr + qword_ofs * sizeof(u64), empty,
- num_qwords);
else
- memset64(ptr, empty, num_qwords);
+ memset64(map->vaddr + update->ofs * sizeof(u64), empty,
+ update->qwords);
}
static void xe_pt_abort_unbind(struct xe_vma *vma,
@@ -2090,13 +2078,40 @@ xe_pt_commit_prepare_unbind(struct xe_vma *vma,
}
}
+static struct xe_vm_pgtable_update_op *
+to_pt_op(struct xe_vm_pgtable_update_ops *pt_update_ops, u32 op_idx)
+{
+ return &pt_update_ops->pt_job_ops->ops[op_idx];
+}
+
+static u32
+get_current_op(struct xe_vm_pgtable_update_ops *pt_update_ops)
+{
+ if (!pt_update_ops->pt_job_ops)
+ return 0;
+
+ return pt_update_ops->pt_job_ops->current_op;
+}
+
+static struct xe_vm_pgtable_update_op *
+to_current_pt_op(struct xe_vm_pgtable_update_ops *pt_update_ops)
+{
+ return to_pt_op(pt_update_ops, get_current_op(pt_update_ops));
+}
+
+static void
+incr_current_op(struct xe_vm_pgtable_update_ops *pt_update_ops)
+{
+ ++pt_update_ops->pt_job_ops->current_op;
+}
+
static void
xe_pt_update_ops_rfence_interval(struct xe_vm_pgtable_update_ops *pt_update_ops,
u64 start, u64 end)
{
u64 last;
- u32 current_op = pt_update_ops->current_op;
- struct xe_vm_pgtable_update_op *pt_op = &pt_update_ops->ops[current_op];
+ struct xe_vm_pgtable_update_op *pt_op =
+ to_current_pt_op(pt_update_ops);
int i, level = 0;
for (i = 0; i < pt_op->num_entries; i++) {
@@ -2131,8 +2146,8 @@ static int bind_op_prepare(struct xe_vm *vm, struct xe_tile *tile,
struct xe_vm_pgtable_update_ops *pt_update_ops,
struct xe_vma *vma, bool invalidate_on_bind)
{
- u32 current_op = pt_update_ops->current_op;
- struct xe_vm_pgtable_update_op *pt_op = &pt_update_ops->ops[current_op];
+ struct xe_vm_pgtable_update_op *pt_op =
+ to_current_pt_op(pt_update_ops);
int err;
xe_tile_assert(tile, !xe_vma_is_cpu_addr_mirror(vma));
@@ -2161,7 +2176,7 @@ static int bind_op_prepare(struct xe_vm *vm, struct xe_tile *tile,
xe_pt_update_ops_rfence_interval(pt_update_ops,
xe_vma_start(vma),
xe_vma_end(vma));
- ++pt_update_ops->current_op;
+ incr_current_op(pt_update_ops);
pt_update_ops->needs_svm_lock |= xe_vma_is_userptr(vma);
/*
@@ -2204,8 +2219,8 @@ static int bind_range_prepare(struct xe_vm *vm, struct xe_tile *tile,
struct xe_vm_pgtable_update_ops *pt_update_ops,
struct xe_vma *vma, struct xe_svm_range *range)
{
- u32 current_op = pt_update_ops->current_op;
- struct xe_vm_pgtable_update_op *pt_op = &pt_update_ops->ops[current_op];
+ struct xe_vm_pgtable_update_op *pt_op =
+ to_current_pt_op(pt_update_ops);
int err;
xe_tile_assert(tile, xe_vma_is_cpu_addr_mirror(vma));
@@ -2229,7 +2244,7 @@ static int bind_range_prepare(struct xe_vm *vm, struct xe_tile *tile,
xe_pt_update_ops_rfence_interval(pt_update_ops,
xe_svm_range_start(range),
xe_svm_range_end(range));
- ++pt_update_ops->current_op;
+ incr_current_op(pt_update_ops);
pt_update_ops->needs_svm_lock = true;
pt_op->vma = vma;
@@ -2247,8 +2262,8 @@ static int unbind_op_prepare(struct xe_tile *tile,
struct xe_vma *vma)
{
struct xe_device *xe = tile_to_xe(tile);
- u32 current_op = pt_update_ops->current_op;
- struct xe_vm_pgtable_update_op *pt_op = &pt_update_ops->ops[current_op];
+ struct xe_vm_pgtable_update_op *pt_op =
+ to_current_pt_op(pt_update_ops);
int err;
if (!((vma->tile_present | vma->tile_staged) & BIT(tile->id)))
@@ -2287,7 +2302,7 @@ static int unbind_op_prepare(struct xe_tile *tile,
pt_op->num_entries, false);
xe_pt_update_ops_rfence_interval(pt_update_ops, xe_vma_start(vma),
xe_vma_end(vma));
- ++pt_update_ops->current_op;
+ incr_current_op(pt_update_ops);
pt_update_ops->needs_svm_lock |= xe_vma_is_userptr(vma);
pt_update_ops->needs_invalidation = true;
@@ -2327,8 +2342,8 @@ static int unbind_range_prepare(struct xe_vm *vm,
struct xe_vm_pgtable_update_ops *pt_update_ops,
struct xe_svm_range *range)
{
- u32 current_op = pt_update_ops->current_op;
- struct xe_vm_pgtable_update_op *pt_op = &pt_update_ops->ops[current_op];
+ struct xe_vm_pgtable_update_op *pt_op =
+ to_current_pt_op(pt_update_ops);
if (!(range->tile_present & BIT(tile->id)))
return 0;
@@ -2349,7 +2364,7 @@ static int unbind_range_prepare(struct xe_vm *vm,
pt_op->num_entries, false);
xe_pt_update_ops_rfence_interval(pt_update_ops, xe_svm_range_start(range),
xe_svm_range_end(range));
- ++pt_update_ops->current_op;
+ incr_current_op(pt_update_ops);
pt_update_ops->needs_svm_lock = true;
pt_update_ops->needs_invalidation |= xe_vm_has_scratch(vm) ||
xe_vm_has_valid_gpu_mapping(tile, range->tile_present,
@@ -2364,6 +2379,7 @@ static int unbind_range_prepare(struct xe_vm *vm,
static int op_prepare(struct xe_vm *vm,
struct xe_tile *tile,
+ struct xe_vma_ops *vops,
struct xe_vm_pgtable_update_ops *pt_update_ops,
struct xe_vma_op *op)
{
@@ -2380,7 +2396,7 @@ static int op_prepare(struct xe_vm *vm,
err = bind_op_prepare(vm, tile, pt_update_ops, op->map.vma,
op->map.invalidate_on_bind);
- pt_update_ops->wait_vm_kernel = true;
+ vops->flags |= XE_VMA_OPS_FLAG_WAIT_VM_KERNEL;
break;
case DRM_GPUVA_OP_REMAP:
{
@@ -2394,12 +2410,12 @@ static int op_prepare(struct xe_vm *vm,
if (!err && op->remap.prev && !op->remap.skip_prev) {
err = bind_op_prepare(vm, tile, pt_update_ops,
op->remap.prev, false);
- pt_update_ops->wait_vm_bookkeep = true;
+ vops->flags |= XE_VMA_OPS_FLAG_WAIT_VM_BOOKKEEP;
}
if (!err && op->remap.next && !op->remap.skip_next) {
err = bind_op_prepare(vm, tile, pt_update_ops,
op->remap.next, false);
- pt_update_ops->wait_vm_bookkeep = true;
+ vops->flags |= XE_VMA_OPS_FLAG_WAIT_VM_BOOKKEEP;
}
break;
}
@@ -2435,7 +2451,7 @@ static int op_prepare(struct xe_vm *vm,
}
} else {
err = bind_op_prepare(vm, tile, pt_update_ops, vma, false);
- pt_update_ops->wait_vm_kernel = true;
+ vops->flags |= XE_VMA_OPS_FLAG_WAIT_VM_KERNEL;
}
break;
}
@@ -2461,8 +2477,7 @@ static int op_prepare(struct xe_vm *vm,
static void
xe_pt_update_ops_init(struct xe_vm_pgtable_update_ops *pt_update_ops)
{
- init_llist_head(&pt_update_ops->deferred);
- pt_update_ops->current_op = 0;
+ pt_update_ops->pt_job_ops->current_op = 0;
pt_update_ops->start = ~0x0ull;
pt_update_ops->last = 0x0ull;
pt_update_ops->needs_svm_lock = false;
@@ -2470,18 +2485,8 @@ xe_pt_update_ops_init(struct xe_vm_pgtable_update_ops *pt_update_ops)
xe_page_reclaim_list_init(&pt_update_ops->prl);
}
-/**
- * xe_pt_update_ops_prepare() - Prepare PT update operations
- * @tile: Tile of PT update operations
- * @vops: VMA operationa
- *
- * Prepare PT update operations which includes updating internal PT state,
- * allocate memory for page tables, populate page table being pruned in, and
- * create PT update operations for leaf insertion / removal.
- *
- * Return: 0 on success, negative error code on error.
- */
-int xe_pt_update_ops_prepare(struct xe_tile *tile, struct xe_vma_ops *vops)
+static int __xe_pt_update_ops_prepare(struct xe_tile *tile,
+ struct xe_vma_ops *vops)
{
struct xe_vm_pgtable_update_ops *pt_update_ops =
&vops->pt_update_ops[tile->id];
@@ -2500,15 +2505,25 @@ int xe_pt_update_ops_prepare(struct xe_tile *tile, struct xe_vma_ops *vops)
return err;
list_for_each_entry(op, &vops->list, link) {
- err = op_prepare(vops->vm, tile, pt_update_ops, op);
+ err = op_prepare(vops->vm, tile, vops, pt_update_ops, op);
if (err)
return err;
}
- xe_tile_assert(tile, pt_update_ops->current_op <=
+ xe_tile_assert(tile, get_current_op(pt_update_ops) <=
pt_update_ops->num_ops);
+ /* Propagate individual tile state up to VMA operation */
+ if (pt_update_ops->start < vops->start)
+ vops->start = pt_update_ops->start;
+ if (pt_update_ops->last > vops->last)
+ vops->last = pt_update_ops->last;
+ if (pt_update_ops->needs_invalidation)
+ vops->flags |= XE_VMA_OPS_FLAG_NEEDS_INVALIDATION;
+ if (pt_update_ops->needs_svm_lock)
+ vops->flags |= XE_VMA_OPS_FLAG_NEEDS_SVM_LOCK;
+
#ifdef TEST_VM_OPS_ERROR
if (vops->inject_error &&
vops->vm->xe->vm_inject_error_position == FORCE_OP_ERROR_PREPARE)
@@ -2517,35 +2532,80 @@ int xe_pt_update_ops_prepare(struct xe_tile *tile, struct xe_vma_ops *vops)
return 0;
}
-ALLOW_ERROR_INJECTION(xe_pt_update_ops_prepare, ERRNO);
-static void bind_op_commit(struct xe_vm *vm, struct xe_tile *tile,
- struct xe_vm_pgtable_update_ops *pt_update_ops,
- struct xe_vma *vma, struct dma_fence *fence,
- struct dma_fence *fence2, bool invalidate_on_bind)
+/**
+ * xe_pt_update_ops_prepare() - Prepare PT update operations
+ * @xe: xe device.
+ * @vops: VMA operationa
+ *
+ * Prepare PT update operations which includes updating internal PT state,
+ * allocate memory for page tables, populate page table being pruned in, and
+ * create PT update operations for leaf insertion / removal.
+ *
+ * Return: 0 on success, negative error code on error.
+ */
+int xe_pt_update_ops_prepare(struct xe_device *xe, struct xe_vma_ops *vops)
{
- xe_tile_assert(tile, !xe_vma_is_cpu_addr_mirror(vma));
+ struct xe_tile *tile;
+ int id, err;
+
+ for_each_tile(tile, xe, id) {
+ struct xe_vm_pgtable_update_ops *pt_update_ops =
+ &vops->pt_update_ops[id];
+
+ if (!vops->pt_update_ops[id].num_ops)
+ continue;
+
+ if (pt_mirroring_disabled_for_tile(vops->vm, id)) {
+ struct xe_page_reclaim_list *prl = &pt_update_ops->prl;
+
+ /* Transfer root PT update ops PRL to current */
+ *prl = vops->pt_update_ops[0].prl;
+ xe_page_reclaim_entries_get(prl->entries);
+ continue;
+ }
- if (!xe_vma_has_no_bo(vma) && !xe_vma_bo(vma)->vm) {
- dma_resv_add_fence(xe_vma_bo(vma)->ttm.base.resv, fence,
- pt_update_ops->wait_vm_bookkeep ?
- DMA_RESV_USAGE_KERNEL :
- DMA_RESV_USAGE_BOOKKEEP);
- if (fence2)
- dma_resv_add_fence(xe_vma_bo(vma)->ttm.base.resv, fence2,
- pt_update_ops->wait_vm_bookkeep ?
- DMA_RESV_USAGE_KERNEL :
- DMA_RESV_USAGE_BOOKKEEP);
+ err = __xe_pt_update_ops_prepare(tile, vops);
+ if (err)
+ return err;
}
+
+ return 0;
+}
+ALLOW_ERROR_INJECTION(xe_pt_update_ops_prepare, ERRNO);
+
+static void vma_add_fences(struct xe_vma *vma, struct dma_fence **fences,
+ int fence_count, enum dma_resv_usage usage)
+{
+ int i;
+
+ if (xe_vma_has_no_bo(vma) || xe_vma_bo(vma)->vm)
+ return;
+
+ for (i = 0; i < fence_count; ++i)
+ if (fences[i])
+ dma_resv_add_fence(xe_vma_bo(vma)->ttm.base.resv,
+ fences[i], usage);
+}
+
+static void bind_op_commit(struct xe_vm *vm, struct xe_vma *vma,
+ struct dma_fence **fences, int fence_count,
+ enum dma_resv_usage usage, u8 tile_mask,
+ bool invalidate_on_bind)
+{
+ xe_assert(vm->xe, !xe_vma_is_cpu_addr_mirror(vma));
+
+ vma_add_fences(vma, fences, fence_count, usage);
+
/* All WRITE_ONCE pair with READ_ONCE in xe_vm_has_valid_gpu_mapping() */
- WRITE_ONCE(vma->tile_present, vma->tile_present | BIT(tile->id));
+ WRITE_ONCE(vma->tile_present, vma->tile_present | tile_mask);
if (invalidate_on_bind)
WRITE_ONCE(vma->tile_invalidated,
- vma->tile_invalidated | BIT(tile->id));
+ vma->tile_invalidated | tile_mask);
else
WRITE_ONCE(vma->tile_invalidated,
- vma->tile_invalidated & ~BIT(tile->id));
- vma->tile_staged &= ~BIT(tile->id);
+ vma->tile_invalidated & ~tile_mask);
+ vma->tile_staged &= ~tile_mask;
if (xe_vma_is_userptr(vma)) {
xe_svm_assert_held_read_or_inject_write(vm);
to_userptr_vma(vma)->userptr.initial_bind = true;
@@ -2555,31 +2615,21 @@ static void bind_op_commit(struct xe_vm *vm, struct xe_tile *tile,
* Kick rebind worker if this bind triggers preempt fences and not in
* the rebind worker
*/
- if (pt_update_ops->wait_vm_bookkeep &&
+ if (usage == DMA_RESV_USAGE_KERNEL &&
xe_vm_in_preempt_fence_mode(vm) &&
!current->mm)
xe_vm_queue_rebind_worker(vm);
}
-static void unbind_op_commit(struct xe_vm *vm, struct xe_tile *tile,
- struct xe_vm_pgtable_update_ops *pt_update_ops,
- struct xe_vma *vma, struct dma_fence *fence,
- struct dma_fence *fence2)
+static void unbind_op_commit(struct xe_vm *vm, struct xe_vma *vma,
+ struct dma_fence **fences, int fence_count,
+ enum dma_resv_usage usage, u8 tile_mask)
{
- xe_tile_assert(tile, !xe_vma_is_cpu_addr_mirror(vma));
+ xe_assert(vm->xe, !xe_vma_is_cpu_addr_mirror(vma));
+
+ vma_add_fences(vma, fences, fence_count, usage);
- if (!xe_vma_has_no_bo(vma) && !xe_vma_bo(vma)->vm) {
- dma_resv_add_fence(xe_vma_bo(vma)->ttm.base.resv, fence,
- pt_update_ops->wait_vm_bookkeep ?
- DMA_RESV_USAGE_KERNEL :
- DMA_RESV_USAGE_BOOKKEEP);
- if (fence2)
- dma_resv_add_fence(xe_vma_bo(vma)->ttm.base.resv, fence2,
- pt_update_ops->wait_vm_bookkeep ?
- DMA_RESV_USAGE_KERNEL :
- DMA_RESV_USAGE_BOOKKEEP);
- }
- vma->tile_present &= ~BIT(tile->id);
+ vma->tile_present &= ~tile_mask;
if (!vma->tile_present) {
list_del_init(&vma->combined_links.rebind);
if (xe_vma_is_userptr(vma)) {
@@ -2594,21 +2644,19 @@ static void unbind_op_commit(struct xe_vm *vm, struct xe_tile *tile,
static void range_present_and_invalidated_tile(struct xe_vm *vm,
struct xe_svm_range *range,
- u8 tile_id)
+ u8 tile_mask)
{
/* All WRITE_ONCE pair with READ_ONCE in xe_vm_has_valid_gpu_mapping() */
lockdep_assert_held(&vm->svm.gpusvm.notifier_lock);
- WRITE_ONCE(range->tile_present, range->tile_present | BIT(tile_id));
- WRITE_ONCE(range->tile_invalidated, range->tile_invalidated & ~BIT(tile_id));
+ WRITE_ONCE(range->tile_present, range->tile_present | tile_mask);
+ WRITE_ONCE(range->tile_invalidated, range->tile_invalidated & ~tile_mask);
}
-static void op_commit(struct xe_vm *vm,
- struct xe_tile *tile,
- struct xe_vm_pgtable_update_ops *pt_update_ops,
- struct xe_vma_op *op, struct dma_fence *fence,
- struct dma_fence *fence2)
+static void op_commit(struct xe_vm *vm, struct xe_vma_op *op,
+ struct dma_fence **fences, int fence_count,
+ enum dma_resv_usage usage, u8 tile_mask)
{
xe_vm_assert_held(vm);
@@ -2618,8 +2666,8 @@ static void op_commit(struct xe_vm *vm,
(op->map.vma_flags & XE_VMA_SYSTEM_ALLOCATOR))
break;
- bind_op_commit(vm, tile, pt_update_ops, op->map.vma, fence,
- fence2, op->map.invalidate_on_bind);
+ bind_op_commit(vm, op->map.vma, fences, fence_count, usage,
+ tile_mask, op->map.invalidate_on_bind);
break;
case DRM_GPUVA_OP_REMAP:
{
@@ -2628,14 +2676,15 @@ static void op_commit(struct xe_vm *vm,
if (xe_vma_is_cpu_addr_mirror(old))
break;
- unbind_op_commit(vm, tile, pt_update_ops, old, fence, fence2);
+ unbind_op_commit(vm, old, fences, fence_count, usage,
+ tile_mask);
if (op->remap.prev && !op->remap.skip_prev)
- bind_op_commit(vm, tile, pt_update_ops, op->remap.prev,
- fence, fence2, false);
+ bind_op_commit(vm, op->remap.prev, fences, fence_count,
+ usage, tile_mask, false);
if (op->remap.next && !op->remap.skip_next)
- bind_op_commit(vm, tile, pt_update_ops, op->remap.next,
- fence, fence2, false);
+ bind_op_commit(vm, op->remap.next, fences, fence_count,
+ usage, tile_mask, false);
break;
}
case DRM_GPUVA_OP_UNMAP:
@@ -2643,8 +2692,8 @@ static void op_commit(struct xe_vm *vm,
struct xe_vma *vma = gpuva_to_vma(op->base.unmap.va);
if (!xe_vma_is_cpu_addr_mirror(vma))
- unbind_op_commit(vm, tile, pt_update_ops, vma, fence,
- fence2);
+ unbind_op_commit(vm, vma, fences, fence_count,
+ usage, tile_mask);
break;
}
case DRM_GPUVA_OP_PREFETCH:
@@ -2656,10 +2705,11 @@ static void op_commit(struct xe_vm *vm,
unsigned long i;
xa_for_each(&op->prefetch_range.range, i, range)
- range_present_and_invalidated_tile(vm, range, tile->id);
+ range_present_and_invalidated_tile(vm, range,
+ tile_mask);
} else {
- bind_op_commit(vm, tile, pt_update_ops, vma, fence,
- fence2, false);
+ bind_op_commit(vm, vma, fences, fence_count, usage,
+ tile_mask, false);
}
break;
}
@@ -2667,11 +2717,12 @@ static void op_commit(struct xe_vm *vm,
{
/* WRITE_ONCE pairs with READ_ONCE in xe_vm_has_valid_gpu_mapping() */
if (op->subop == XE_VMA_SUBOP_MAP_RANGE)
- range_present_and_invalidated_tile(vm, op->map_range.range, tile->id);
+ range_present_and_invalidated_tile(vm, op->map_range.range,
+ tile_mask);
else if (op->subop == XE_VMA_SUBOP_UNMAP_RANGE)
WRITE_ONCE(op->unmap_range.range->tile_present,
op->unmap_range.range->tile_present &
- ~BIT(tile->id));
+ ~tile_mask);
break;
}
@@ -2680,34 +2731,25 @@ static void op_commit(struct xe_vm *vm,
}
}
-static const struct xe_migrate_pt_update_ops migrate_ops = {
+static const struct xe_cpu_bind_pt_update_ops cpu_bind_ops = {
.populate = xe_vm_populate_pgtable,
- .clear = xe_migrate_clear_pgtable_callback,
+ .clear = xe_pt_clear_pgtable_callback,
.pre_commit = xe_pt_pre_commit,
};
#if IS_ENABLED(CONFIG_DRM_GPUSVM)
-static const struct xe_migrate_pt_update_ops svm_userptr_migrate_ops = {
+static const struct xe_cpu_bind_pt_update_ops svm_userptr_cpu_bind_ops = {
.populate = xe_vm_populate_pgtable,
- .clear = xe_migrate_clear_pgtable_callback,
+ .clear = xe_pt_clear_pgtable_callback,
.pre_commit = xe_pt_svm_userptr_pre_commit,
};
#else
-static const struct xe_migrate_pt_update_ops svm_userptr_migrate_ops;
+static const struct xe_cpu_bind_pt_update_ops svm_userptr_cpu_bind_ops;
#endif
-static struct xe_dep_scheduler *to_dep_scheduler(struct xe_exec_queue *q,
- struct xe_gt *gt)
-{
- if (xe_gt_is_media_type(gt))
- return q->tlb_inval[XE_EXEC_QUEUE_TLB_INVAL_MEDIA_GT].dep_scheduler;
-
- return q->tlb_inval[XE_EXEC_QUEUE_TLB_INVAL_PRIMARY_GT].dep_scheduler;
-}
-
/**
* xe_pt_update_ops_run() - Run PT update operations
- * @tile: Tile of PT update operations
+ * @xe: xe device.
* @vops: VMA operationa
*
* Run PT update operations which includes committing internal PT state changes,
@@ -2717,80 +2759,83 @@ static struct xe_dep_scheduler *to_dep_scheduler(struct xe_exec_queue *q,
* Return: fence on success, negative ERR_PTR on error.
*/
struct dma_fence *
-xe_pt_update_ops_run(struct xe_tile *tile, struct xe_vma_ops *vops)
+xe_pt_update_ops_run(struct xe_device *xe, struct xe_vma_ops *vops)
{
struct xe_vm *vm = vops->vm;
- struct xe_vm_pgtable_update_ops *pt_update_ops =
- &vops->pt_update_ops[tile->id];
- struct xe_exec_queue *q = pt_update_ops->q;
- struct dma_fence *fence, *ifence = NULL, *mfence = NULL;
- struct xe_tlb_inval_job *ijob = NULL, *mjob = NULL;
+ struct xe_exec_queue *q = vops->q;
+ struct dma_fence *fence;
+ struct dma_fence *ifences[XE_CPU_BIND_INVAL_JOB_COUNT] = {};
struct xe_range_fence *rfence;
+ enum dma_resv_usage usage = DMA_RESV_USAGE_BOOKKEEP;
struct xe_vma_op *op;
- int err = 0, i;
- struct xe_migrate_pt_update update = {
- .ops = pt_update_ops->needs_svm_lock ?
- &svm_userptr_migrate_ops :
- &migrate_ops,
+ struct xe_tile *tile;
+ int err = 0, total_ops = 0, i, j;
+ u8 tile_mask = 0;
+ bool needs_invalidation = vops->flags &
+ XE_VMA_OPS_FLAG_NEEDS_INVALIDATION;
+ bool needs_svm_lock = vops->flags &
+ XE_VMA_OPS_FLAG_NEEDS_SVM_LOCK;
+ struct xe_cpu_bind_pt_update update = {
+ .ops = needs_svm_lock ? &svm_userptr_cpu_bind_ops :
+ &cpu_bind_ops,
.vops = vops,
- .tile_id = tile->id,
};
lockdep_assert_held(&vm->lock);
xe_vm_assert_held(vm);
- if (!pt_update_ops->current_op) {
- xe_tile_assert(tile, xe_vm_in_fault_mode(vm));
+ for_each_tile(tile, xe, j) {
+ struct xe_vm_pgtable_update_ops *pt_update_ops =
+ &vops->pt_update_ops[j];
+ total_ops += get_current_op(pt_update_ops);
+ }
+ if (!total_ops) {
+ xe_assert(xe, xe_vm_in_fault_mode(vm));
return dma_fence_get_stub();
}
#ifdef TEST_VM_OPS_ERROR
if (vops->inject_error &&
- vm->xe->vm_inject_error_position == FORCE_OP_ERROR_RUN)
+ xe->vm_inject_error_position == FORCE_OP_ERROR_RUN)
return ERR_PTR(-ENOSPC);
#endif
- if (pt_update_ops->needs_invalidation) {
- struct xe_dep_scheduler *dep_scheduler =
- to_dep_scheduler(q, tile->primary_gt);
-
- ijob = xe_tlb_inval_job_create(q, &tile->primary_gt->tlb_inval,
- dep_scheduler, vm,
- pt_update_ops->start,
- pt_update_ops->last,
- XE_EXEC_QUEUE_TLB_INVAL_PRIMARY_GT);
- if (IS_ERR(ijob)) {
- err = PTR_ERR(ijob);
- goto kill_vm_tile1;
- }
- update.ijob = ijob;
- /*
- * Only add page reclaim for the primary GT. Media GT does not have
- * any PPC to flush, so enabling the PPC flush bit for media is
- * effectively a NOP and provides no performance benefit nor
- * interfere with primary GT.
- */
- if (xe_page_reclaim_list_valid(&pt_update_ops->prl)) {
- xe_tlb_inval_job_add_page_reclaim(ijob, &pt_update_ops->prl);
- /* Release ref from alloc, job will now handle it */
- xe_page_reclaim_list_invalidate(&pt_update_ops->prl);
- }
-
- if (tile->media_gt) {
- dep_scheduler = to_dep_scheduler(q, tile->media_gt);
-
- mjob = xe_tlb_inval_job_create(q,
- &tile->media_gt->tlb_inval,
- dep_scheduler, vm,
- pt_update_ops->start,
- pt_update_ops->last,
- XE_EXEC_QUEUE_TLB_INVAL_MEDIA_GT);
- if (IS_ERR(mjob)) {
- err = PTR_ERR(mjob);
+ if (needs_invalidation) {
+ for_each_tlb_inval(q, i) {
+ struct xe_dep_scheduler *dep_scheduler =
+ q->tlb_inval[i].dep_scheduler;
+ struct xe_tile *tile =
+ &xe->tiles[i / XE_MAX_GT_PER_TILE];
+ struct xe_vm_pgtable_update_ops *pt_update_ops =
+ &vops->pt_update_ops[tile->id];
+ struct xe_page_reclaim_list *prl = &pt_update_ops->prl;
+ struct xe_tlb_inval_job *ijob;
+ struct xe_gt *gt = i % XE_MAX_GT_PER_TILE ?
+ tile->media_gt : tile->primary_gt;
+
+ ijob = xe_tlb_inval_job_create(q, &gt->tlb_inval,
+ dep_scheduler,
+ vm, vops->start,
+ vops->last, i);
+ if (IS_ERR(ijob)) {
+ err = PTR_ERR(ijob);
goto free_ijob;
}
- update.mjob = mjob;
+
+ update.ijobs[i] = ijob;
+
+ /*
+ * Only add page reclaim for the primary GT. Media GT
+ * does not have any PPC to flush, so enabling the PPC
+ * flush bit for media is effectively a NOP and provides
+ * no performance benefit nor interfere with primary GT.
+ */
+ if (xe_page_reclaim_list_valid(prl)) {
+ xe_tlb_inval_job_add_page_reclaim(ijob, prl);
+ /* Release ref from alloc, job will now handle it */
+ xe_page_reclaim_list_invalidate(prl);
+ }
}
}
@@ -2800,65 +2845,67 @@ xe_pt_update_ops_run(struct xe_tile *tile, struct xe_vma_ops *vops)
goto free_ijob;
}
- fence = xe_migrate_update_pgtables(tile->migrate, &update);
+ fence = xe_cpu_bind_update_pgtables(xe->cpu_bind, &update);
if (IS_ERR(fence)) {
err = PTR_ERR(fence);
goto free_rfence;
}
/* Point of no return - VM killed if failure after this */
- for (i = 0; i < pt_update_ops->current_op; ++i) {
- struct xe_vm_pgtable_update_op *pt_op = &pt_update_ops->ops[i];
+ for_each_tile(tile, xe, j) {
+ struct xe_vm_pgtable_update_ops *pt_update_ops =
+ &vops->pt_update_ops[j];
+
+ if (pt_mirroring_disabled_for_tile(vm, j)) {
+ xe_tile_assert(tile, !get_current_op(pt_update_ops));
+ tile_mask |= BIT(tile->id);
+ continue;
+ }
+
+ for (i = 0; i < get_current_op(pt_update_ops); ++i) {
+ struct xe_vm_pgtable_update_op *pt_op =
+ to_pt_op(pt_update_ops, i);
- xe_pt_commit(pt_op->vma, pt_op->entries,
- pt_op->num_entries, &pt_update_ops->deferred);
- pt_op->vma = NULL; /* skip in xe_pt_update_ops_abort */
+ xe_pt_commit(pt_op->vma, pt_op->entries,
+ pt_op->num_entries,
+ &pt_update_ops->pt_job_ops->deferred);
+ pt_op->vma = NULL; /* skip in xe_pt_update_ops_abort */
+ tile_mask |= BIT(tile->id);
+ }
}
- if (xe_range_fence_insert(&vm->rftree[tile->id], rfence,
+ if (xe_range_fence_insert(&vm->rftree, rfence,
&xe_range_fence_kfree_ops,
- pt_update_ops->start,
- pt_update_ops->last, fence))
+ vops->start, vops->last, fence))
dma_fence_wait(fence, false);
- if (ijob)
- ifence = xe_tlb_inval_job_push(ijob, tile->migrate, fence);
- if (mjob)
- mfence = xe_tlb_inval_job_push(mjob, tile->migrate, fence);
+ if (vops->flags & XE_VMA_OPS_FLAG_WAIT_VM_BOOKKEEP)
+ usage = DMA_RESV_USAGE_KERNEL;
- if (!mjob && !ijob) {
- dma_resv_add_fence(xe_vm_resv(vm), fence,
- pt_update_ops->wait_vm_bookkeep ?
- DMA_RESV_USAGE_KERNEL :
- DMA_RESV_USAGE_BOOKKEEP);
+ if (!needs_invalidation) {
+ dma_resv_add_fence(xe_vm_resv(vm), fence, usage);
list_for_each_entry(op, &vops->list, link)
- op_commit(vops->vm, tile, pt_update_ops, op, fence, NULL);
- } else if (ijob && !mjob) {
- dma_resv_add_fence(xe_vm_resv(vm), ifence,
- pt_update_ops->wait_vm_bookkeep ?
- DMA_RESV_USAGE_KERNEL :
- DMA_RESV_USAGE_BOOKKEEP);
-
- list_for_each_entry(op, &vops->list, link)
- op_commit(vops->vm, tile, pt_update_ops, op, ifence, NULL);
+ op_commit(vops->vm, op, &fence, 1, usage, tile_mask);
} else {
- dma_resv_add_fence(xe_vm_resv(vm), ifence,
- pt_update_ops->wait_vm_bookkeep ?
- DMA_RESV_USAGE_KERNEL :
- DMA_RESV_USAGE_BOOKKEEP);
+ for (i = 0; i < XE_CPU_BIND_INVAL_JOB_COUNT; ++i) {
+ if (!update.ijobs[i])
+ continue;
- dma_resv_add_fence(xe_vm_resv(vm), mfence,
- pt_update_ops->wait_vm_bookkeep ?
- DMA_RESV_USAGE_KERNEL :
- DMA_RESV_USAGE_BOOKKEEP);
+ ifences[i] = xe_tlb_inval_job_push(update.ijobs[i],
+ fence);
+ xe_assert(xe, !IS_ERR_OR_NULL(ifences[i]));
+
+ dma_resv_add_fence(xe_vm_resv(vm), ifences[i], usage);
+ }
list_for_each_entry(op, &vops->list, link)
- op_commit(vops->vm, tile, pt_update_ops, op, ifence,
- mfence);
+ op_commit(vops->vm, op, ifences,
+ XE_CPU_BIND_INVAL_JOB_COUNT, usage,
+ tile_mask);
}
- if (pt_update_ops->needs_svm_lock)
+ if (needs_svm_lock)
xe_pt_svm_userptr_notifier_unlock(vm);
/*
@@ -2868,21 +2915,18 @@ xe_pt_update_ops_run(struct xe_tile *tile, struct xe_vma_ops *vops)
if (!(q->flags & EXEC_QUEUE_FLAG_MIGRATE))
xe_exec_queue_last_fence_set(q, vm, fence);
- xe_tlb_inval_job_put(mjob);
- xe_tlb_inval_job_put(ijob);
- dma_fence_put(ifence);
- dma_fence_put(mfence);
+ for (i = 0; i < XE_CPU_BIND_INVAL_JOB_COUNT; ++i) {
+ xe_tlb_inval_job_put(update.ijobs[i]);
+ dma_fence_put(ifences[i]);
+ }
return fence;
free_rfence:
kfree(rfence);
free_ijob:
- xe_tlb_inval_job_put(mjob);
- xe_tlb_inval_job_put(ijob);
-kill_vm_tile1:
- if (err != -EAGAIN && err != -ENODATA && tile->id)
- xe_vm_kill(vops->vm, false);
+ for (i = 0; i < XE_CPU_BIND_INVAL_JOB_COUNT; ++i)
+ xe_tlb_inval_job_put(update.ijobs[i]);
return ERR_PTR(err);
}
@@ -2890,61 +2934,164 @@ ALLOW_ERROR_INJECTION(xe_pt_update_ops_run, ERRNO);
/**
* xe_pt_update_ops_fini() - Finish PT update operations
- * @tile: Tile of PT update operations
+ * @xe: xe device.
* @vops: VMA operations
*
* Finish PT update operations by committing to destroy page table memory
*/
-void xe_pt_update_ops_fini(struct xe_tile *tile, struct xe_vma_ops *vops)
+void xe_pt_update_ops_fini(struct xe_device *xe, struct xe_vma_ops *vops)
{
- struct xe_vm_pgtable_update_ops *pt_update_ops =
- &vops->pt_update_ops[tile->id];
- int i;
-
- xe_page_reclaim_entries_put(pt_update_ops->prl.entries);
+ struct xe_tile *tile;
+ int id;
- lockdep_assert_held(&vops->vm->lock);
- xe_vm_assert_held(vops->vm);
+ for_each_tile(tile, xe, id) {
+ struct xe_vm_pgtable_update_ops *pt_update_ops =
+ &vops->pt_update_ops[id];
- for (i = 0; i < pt_update_ops->current_op; ++i) {
- struct xe_vm_pgtable_update_op *pt_op = &pt_update_ops->ops[i];
+ if (!pt_update_ops->num_ops)
+ continue;
- xe_pt_free_bind(pt_op->entries, pt_op->num_entries);
+ xe_page_reclaim_entries_put(pt_update_ops->prl.entries);
}
- xe_bo_put_commit(&vops->pt_update_ops[tile->id].deferred);
}
/**
* xe_pt_update_ops_abort() - Abort PT update operations
- * @tile: Tile of PT update operations
+ * @xe: xe device.
* @vops: VMA operationa
*
* Abort PT update operations by unwinding internal PT state
*/
-void xe_pt_update_ops_abort(struct xe_tile *tile, struct xe_vma_ops *vops)
+void xe_pt_update_ops_abort(struct xe_device *xe, struct xe_vma_ops *vops)
{
- struct xe_vm_pgtable_update_ops *pt_update_ops =
- &vops->pt_update_ops[tile->id];
- int i;
+ struct xe_tile *tile;
+ int id;
lockdep_assert_held(&vops->vm->lock);
xe_vm_assert_held(vops->vm);
- for (i = pt_update_ops->num_ops - 1; i >= 0; --i) {
- struct xe_vm_pgtable_update_op *pt_op =
- &pt_update_ops->ops[i];
+ for_each_tile(tile, xe, id) {
+ struct xe_vm_pgtable_update_ops *pt_update_ops =
+ &vops->pt_update_ops[id];
+ int i;
- if (!pt_op->vma || i >= pt_update_ops->current_op)
+ if (pt_mirroring_disabled_for_tile(vops->vm, id))
continue;
- if (pt_op->bind)
- xe_pt_abort_bind(pt_op->vma, pt_op->entries,
- pt_op->num_entries,
- pt_op->rebind);
- else
- xe_pt_abort_unbind(pt_op->vma, pt_op->entries,
- pt_op->num_entries);
+ for (i = pt_update_ops->num_ops - 1; i >= 0; --i) {
+ struct xe_vm_pgtable_update_op *pt_op =
+ to_pt_op(pt_update_ops, i);
+
+ if (!pt_op->vma || i >= get_current_op(pt_update_ops))
+ continue;
+
+ if (pt_op->bind)
+ xe_pt_abort_bind(pt_op->vma, pt_op->entries,
+ pt_op->num_entries,
+ pt_op->rebind);
+ else
+ xe_pt_abort_unbind(pt_op->vma, pt_op->entries,
+ pt_op->num_entries);
+ }
+ }
+
+ xe_pt_update_ops_fini(xe, vops);
+}
+
+/**
+ * xe_pt_job_ops_alloc() - Allocate PT job ops
+ * @num_ops: Number of VM PT update ops
+ *
+ * Allocate PT job ops and internal array of VM PT update ops.
+ *
+ * Return: Pointer to PT job ops or NULL
+ */
+struct xe_pt_job_ops *xe_pt_job_ops_alloc(u32 num_ops)
+{
+ struct xe_pt_job_ops *pt_job_ops;
+
+ pt_job_ops = kmalloc_obj(*pt_job_ops);
+ if (!pt_job_ops)
+ return NULL;
+
+ pt_job_ops->ops = kvmalloc_array(num_ops, sizeof(*pt_job_ops->ops),
+ GFP_KERNEL);
+ if (!pt_job_ops->ops) {
+ kfree(pt_job_ops);
+ return NULL;
}
- xe_pt_update_ops_fini(tile, vops);
+ pt_job_ops->current_op = 0;
+ kref_init(&pt_job_ops->refcount);
+ init_llist_head(&pt_job_ops->deferred);
+
+ return pt_job_ops;
+}
+
+/**
+ * xe_pt_job_ops_get() - Get PT job ops
+ * @pt_job_ops: PT job ops to get
+ *
+ * Take a reference to PT job ops
+ *
+ * Return: Pointer to PT job ops or NULL
+ */
+struct xe_pt_job_ops *xe_pt_job_ops_get(struct xe_pt_job_ops *pt_job_ops)
+{
+ if (pt_job_ops)
+ kref_get(&pt_job_ops->refcount);
+
+ return pt_job_ops;
+}
+
+static void xe_pt_update_ops_free(struct xe_vm_pgtable_update_op *pt_op,
+ u32 num_ops)
+{
+ u32 i;
+
+ for (i = 0; i < num_ops; ++i, ++pt_op)
+ xe_pt_free_bind(pt_op->entries, pt_op->num_entries);
+}
+
+static void xe_pt_job_ops_destroy(struct kref *ref)
+{
+ struct xe_pt_job_ops *pt_job_ops =
+ container_of(ref, struct xe_pt_job_ops, refcount);
+ struct llist_node *freed;
+ struct xe_bo *bo, *next;
+
+ xe_pt_update_ops_free(pt_job_ops->ops,
+ pt_job_ops->current_op);
+
+ freed = llist_del_all(&pt_job_ops->deferred);
+ if (freed) {
+ llist_for_each_entry_safe(bo, next, freed, freed) {
+ struct xe_bo_dev *bo_device =
+ &xe_bo_device(bo)->bo_device;
+ /*
+ * If called from run_job, we are in the dma-fencing
+ * path and cannot take dma-resv locks so use an async
+ * put.
+ */
+ if (llist_add(&bo->freed, &bo_device->async_list))
+ schedule_work(&bo_device->async_free);
+ }
+ }
+
+ kvfree(pt_job_ops->ops);
+ kfree(pt_job_ops);
+}
+
+/**
+ * xe_pt_job_ops_put() - Put PT job ops
+ * @pt_job_ops: PT job ops to put
+ *
+ * Drop a reference to PT job ops
+ */
+void xe_pt_job_ops_put(struct xe_pt_job_ops *pt_job_ops)
+{
+ if (!pt_job_ops)
+ return;
+
+ kref_put(&pt_job_ops->refcount, xe_pt_job_ops_destroy);
}
diff --git a/drivers/gpu/drm/xe/xe_pt.h b/drivers/gpu/drm/xe/xe_pt.h
index 4daeebaab5a1..cd78141fb81c 100644
--- a/drivers/gpu/drm/xe/xe_pt.h
+++ b/drivers/gpu/drm/xe/xe_pt.h
@@ -39,14 +39,18 @@ void xe_pt_destroy(struct xe_pt *pt, u32 flags, struct llist_head *deferred);
void xe_pt_clear(struct xe_device *xe, struct xe_pt *pt);
-int xe_pt_update_ops_prepare(struct xe_tile *tile, struct xe_vma_ops *vops);
-struct dma_fence *xe_pt_update_ops_run(struct xe_tile *tile,
+int xe_pt_update_ops_prepare(struct xe_device *xe, struct xe_vma_ops *vops);
+struct dma_fence *xe_pt_update_ops_run(struct xe_device *xe,
struct xe_vma_ops *vops);
-void xe_pt_update_ops_fini(struct xe_tile *tile, struct xe_vma_ops *vops);
-void xe_pt_update_ops_abort(struct xe_tile *tile, struct xe_vma_ops *vops);
+void xe_pt_update_ops_fini(struct xe_device *xe, struct xe_vma_ops *vops);
+void xe_pt_update_ops_abort(struct xe_device *xe, struct xe_vma_ops *vops);
bool xe_pt_zap_ptes(struct xe_tile *tile, struct xe_vma *vma);
bool xe_pt_zap_ptes_range(struct xe_tile *tile, struct xe_vm *vm,
struct xe_svm_range *range);
+struct xe_pt_job_ops *xe_pt_job_ops_alloc(u32 num_ops);
+struct xe_pt_job_ops *xe_pt_job_ops_get(struct xe_pt_job_ops *pt_job_ops);
+void xe_pt_job_ops_put(struct xe_pt_job_ops *pt_job_ops);
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_pt_types.h b/drivers/gpu/drm/xe/xe_pt_types.h
index a7d1bb708b69..0d4bac22ee6c 100644
--- a/drivers/gpu/drm/xe/xe_pt_types.h
+++ b/drivers/gpu/drm/xe/xe_pt_types.h
@@ -65,12 +65,18 @@ struct xe_vm_pgtable_update {
/** @qwords: number of PTE's to write */
u32 qwords;
- /** @pt: opaque pointer useful for the caller of xe_migrate_update_pgtables */
+ /**
+ * @pt: opaque pointer useful for PT building in the bind IOCTL. Only
+ * safe to touch during the bind IOCTL (i.e., not in bind jobs).
+ */
struct xe_pt *pt;
/** @pt_entries: Newly added pagetable entries */
struct xe_pt_entry *pt_entries;
+ /** @level: level of update */
+ unsigned int level;
+
/** @flags: Target flags */
u32 flags;
};
@@ -91,14 +97,29 @@ struct xe_vm_pgtable_update_op {
bool rebind;
};
+/**
+ * struct xe_pt_job_ops - Page-table update operations (dynamically allocated)
+ *
+ * This is the portion of &struct xe_vma_ops and
+ * &struct xe_vm_pgtable_update_ops that is dynamically allocated, as it
+ * must remain valid until the associated bind job completes. A reference
+ * count controls its lifetime.
+ */
+struct xe_pt_job_ops {
+ /** @current_op: current page-table update operation */
+ u32 current_op;
+ /** @refcount: reference count */
+ struct kref refcount;
+ /** @deferred: list of deferred PT entries to destroy */
+ struct llist_head deferred;
+ /** @ops: page-table update operations */
+ struct xe_vm_pgtable_update_op *ops;
+};
+
/** struct xe_vm_pgtable_update_ops: page table update operations */
struct xe_vm_pgtable_update_ops {
- /** @ops: operations */
- struct xe_vm_pgtable_update_op *ops;
- /** @deferred: deferred list to destroy PT entries */
- struct llist_head deferred;
- /** @q: exec queue for PT operations */
- struct xe_exec_queue *q;
+ /** @pt_job_ops: PT update operations dynamic allocation*/
+ struct xe_pt_job_ops *pt_job_ops;
/** @prl: embedded page reclaim list */
struct xe_page_reclaim_list prl;
/** @start: start address of ops */
@@ -107,24 +128,10 @@ struct xe_vm_pgtable_update_ops {
u64 last;
/** @num_ops: number of operations */
u32 num_ops;
- /** @current_op: current operations */
- u32 current_op;
/** @needs_svm_lock: Needs SVM lock */
bool needs_svm_lock;
/** @needs_invalidation: Needs invalidation */
bool needs_invalidation;
- /**
- * @wait_vm_bookkeep: PT operations need to wait until VM is idle
- * (bookkeep dma-resv slots are idle) and stage all future VM activity
- * behind these operations (install PT operations into VM kernel
- * dma-resv slot).
- */
- bool wait_vm_bookkeep;
- /**
- * @wait_vm_kernel: PT operations need to wait until VM kernel dma-resv
- * slots are idle.
- */
- bool wait_vm_kernel;
};
#endif
diff --git a/drivers/gpu/drm/xe/xe_ring_ops.c b/drivers/gpu/drm/xe/xe_ring_ops.c
index 46ab1f0f3564..ad63181938e3 100644
--- a/drivers/gpu/drm/xe/xe_ring_ops.c
+++ b/drivers/gpu/drm/xe/xe_ring_ops.c
@@ -143,9 +143,8 @@ static int emit_bb_start(u64 batch_addr, u32 ppgtt_flag, u32 *dw, int i)
static int emit_flush_invalidate(u32 addr, u32 val, u32 flush_flags, u32 *dw, int i)
{
- dw[i++] = MI_FLUSH_DW | MI_FLUSH_DW_OP_STOREDW |
- MI_FLUSH_IMM_DW | (flush_flags & MI_INVALIDATE_TLB) ?: 0;
-
+ dw[i++] = MI_FLUSH_DW | MI_FLUSH_DW_OP_STOREDW | MI_FLUSH_IMM_DW |
+ (flush_flags & MI_INVALIDATE_TLB);
dw[i++] = addr | MI_FLUSH_DW_USE_GTT;
dw[i++] = 0;
dw[i++] = val;
@@ -505,6 +504,74 @@ static void __emit_job_gen12_render_compute(struct xe_sched_job *job,
xe_lrc_write_ring(lrc, dw, i * sizeof(*dw));
}
+static int emit_ulls_preamble(struct xe_lrc *lrc, u32 *dw, int i, u32 seqno)
+{
+ u32 addr = xe_lrc_ulls_semaphore_ggtt_addr(lrc, seqno);
+
+ return emit_store_imm_ggtt(addr, LRC_MIGRATION_ULLS_SEMAPHORE_CLEAR,
+ dw, i);
+}
+
+/*
+ * Advance the ring tail from within the ring, so submitting the next ULLS job
+ * needs nothing from the CPU beyond signalling the semaphore. All ULLS jobs
+ * occupy exactly ULLS_JOB_SIZE_BYTES, so the tail the next job ends at is two
+ * job slots on from where this job started, even though that job has not been
+ * emitted yet. Both LRC and MMIO ring tail advanced in step.
+ */
+static int emit_ulls_ring_tail(struct xe_gt *gt, struct xe_lrc *lrc, u32 *dw,
+ int i, u32 head)
+{
+ u32 next_tail = (head + 2 * ULLS_JOB_SIZE_BYTES) & (lrc->ring.size - 1);
+
+ xe_gt_assert(gt, IS_ALIGNED(next_tail, 8));
+
+ i = emit_store_imm_ggtt(xe_lrc_ring_tail_ggtt_addr(lrc), next_tail,
+ dw, i);
+
+ dw[i++] = MI_LOAD_REGISTER_IMM | MI_LRI_NUM_REGS(1) |
+ MI_LRI_LRM_CS_MMIO;
+ dw[i++] = RING_TAIL(0).addr;
+ dw[i++] = next_tail;
+
+ return i;
+}
+
+/*
+ * Park the engine on the next job's semaphore, then publish its tail.
+ *
+ * The tail must not be published before the wait. The command streamer
+ * fetches ring contents up to the tail while parked, so it would fetch the
+ * next job's slot while it still holds MI_NOOPs and execute those once
+ * released, rather than the job the CPU writes there later. Publishing after
+ * the wait keeps the slot beyond the tail until the job is in place.
+ */
+static int emit_ulls_postamble(struct xe_gt *gt, struct xe_lrc *lrc, u32 *dw,
+ int i, u32 seqno, u32 head)
+{
+ dw[i++] = MI_SEMAPHORE_WAIT |
+ MI_SEMW_GGTT |
+ MI_SEMW_POLL |
+ MI_SEMW_COMPARE(SAD_EQ_SDD);
+ dw[i++] = LRC_MIGRATION_ULLS_SEMAPHORE_SIGNAL;
+ dw[i++] = xe_lrc_ulls_semaphore_ggtt_addr(lrc, seqno + 1);
+ dw[i++] = 0;
+ dw[i++] = 0;
+
+ return emit_ulls_ring_tail(gt, lrc, dw, i, head);
+}
+
+/* Pad out to the fixed ULLS job size */
+static int emit_ulls_pad(struct xe_gt *gt, u32 *dw, int i)
+{
+ xe_gt_assert(gt, i <= ULLS_JOB_SIZE_DW);
+
+ while (i < ULLS_JOB_SIZE_DW)
+ dw[i++] = MI_NOOP;
+
+ return i;
+}
+
static void emit_migration_job_gen12(struct xe_sched_job *job,
struct xe_lrc *lrc, u32 *head,
u32 seqno)
@@ -518,10 +585,16 @@ static void emit_migration_job_gen12(struct xe_sched_job *job,
xe_gt_assert(gt, !job->ring_ops_force_reset);
+ if (xe_sched_job_is_ulls(job))
+ i = emit_ulls_preamble(lrc, dw, i, seqno);
+
i = emit_copy_timestamp(xe, lrc, dw, i);
i = emit_store_imm_ggtt(saddr, seqno, dw, i);
+ if (!xe_sched_job_ulls_has_batch(job))
+ goto seqno_write;
+
dw[i++] = MI_ARB_ON_OFF | MI_ARB_DISABLE; /* Enabled again below */
i = emit_bb_start(job->ptrs[0].batch_addr, BIT(8), dw, i);
@@ -532,12 +605,19 @@ static void emit_migration_job_gen12(struct xe_sched_job *job,
i = emit_bb_start(job->ptrs[1].batch_addr, BIT(8), dw, i);
+seqno_write:
i = emit_flush_imm_ggtt(xe_lrc_seqno_ggtt_addr(lrc), seqno,
job->migrate_flush_flags,
dw, i);
i = emit_user_interrupt(dw, i);
+ if (xe_sched_job_ulls_parks(job))
+ i = emit_ulls_postamble(gt, lrc, dw, i, seqno, *head);
+
+ if (xe_sched_job_is_ulls(job))
+ i = emit_ulls_pad(gt, dw, i);
+
xe_gt_assert(job->q->gt, i <= MAX_JOB_SIZE_DW);
xe_lrc_write_ring(lrc, dw, i * sizeof(*dw));
diff --git a/drivers/gpu/drm/xe/xe_ring_ops_types.h b/drivers/gpu/drm/xe/xe_ring_ops_types.h
index 52ff96bc4100..ea4af321dd7c 100644
--- a/drivers/gpu/drm/xe/xe_ring_ops_types.h
+++ b/drivers/gpu/drm/xe/xe_ring_ops_types.h
@@ -6,6 +6,7 @@
#ifndef _XE_RING_OPS_TYPES_H_
#define _XE_RING_OPS_TYPES_H_
+#include <linux/build_bug.h>
#include <linux/types.h>
struct xe_gt;
@@ -14,6 +15,29 @@ struct xe_sched_job;
#define MAX_JOB_SIZE_DW 74
#define MAX_JOB_SIZE_BYTES (MAX_JOB_SIZE_DW * 4)
+/*
+ * ULLS migration jobs advance the ring tail from within the ring itself, so a
+ * job has to know where its successor will end before that successor has been
+ * emitted. Every ULLS job is therefore padded to a fixed size, letting the
+ * next tail be derived arithmetically.
+ *
+ * Sized for the largest such job, emitted by emit_migration_job_gen12():
+ * preamble (4), copy timestamp (8, its size on an SRIOV VF), start seqno
+ * store (4), arbitration off (1), batch buffer starts (2 * 3), pre-parser
+ * bracketed flush invalidate (6), seqno flush (4), user interrupt (3) and
+ * postamble (7 + 5).
+ */
+#define ULLS_JOB_SIZE_DW 48
+#define ULLS_JOB_SIZE_BYTES (ULLS_JOB_SIZE_DW * 4)
+
+/*
+ * RING_TAIL only encodes a qword aligned offset, and xe_lrc_write_ring()
+ * appends a NOP to anything shorter, either of which would desynchronise the
+ * ring from the tail a job predicts for its successor.
+ */
+static_assert(ULLS_JOB_SIZE_BYTES % 8 == 0);
+static_assert(ULLS_JOB_SIZE_DW <= MAX_JOB_SIZE_DW);
+
/**
* struct xe_ring_ops - Ring operations
*/
diff --git a/drivers/gpu/drm/xe/xe_sched_job.c b/drivers/gpu/drm/xe/xe_sched_job.c
index a4fa00632a30..59f7215f27b1 100644
--- a/drivers/gpu/drm/xe/xe_sched_job.c
+++ b/drivers/gpu/drm/xe/xe_sched_job.c
@@ -26,19 +26,22 @@ static struct kmem_cache *xe_sched_job_parallel_slab;
int __init xe_sched_job_module_init(void)
{
+ struct xe_sched_job *job;
+ size_t size;
+
+ size = struct_size(job, ptrs, 1);
xe_sched_job_slab =
- kmem_cache_create("xe_sched_job",
- sizeof(struct xe_sched_job) +
- sizeof(struct xe_job_ptrs), 0,
+ kmem_cache_create("xe_sched_job", size, 0,
SLAB_HWCACHE_ALIGN, NULL);
if (!xe_sched_job_slab)
return -ENOMEM;
+ size = max_t(size_t,
+ struct_size(job, ptrs,
+ XE_HW_ENGINE_MAX_INSTANCE),
+ struct_size(job, pt_update, 1));
xe_sched_job_parallel_slab =
- kmem_cache_create("xe_sched_job_parallel",
- sizeof(struct xe_sched_job) +
- sizeof(struct xe_job_ptrs) *
- XE_HW_ENGINE_MAX_INSTANCE, 0,
+ kmem_cache_create("xe_sched_job_parallel", size, 0,
SLAB_HWCACHE_ALIGN, NULL);
if (!xe_sched_job_parallel_slab) {
kmem_cache_destroy(xe_sched_job_slab);
@@ -70,8 +73,9 @@ static void job_free(struct xe_sched_job *job)
struct xe_exec_queue *q = job->q;
bool is_migration = xe_sched_job_is_migration(q);
- kmem_cache_free(xe_exec_queue_is_parallel(job->q) || is_migration ?
- xe_sched_job_parallel_slab : xe_sched_job_slab, job);
+ kmem_cache_free(job->is_pt_job || xe_exec_queue_is_parallel(job->q) ||
+ is_migration ? xe_sched_job_parallel_slab :
+ xe_sched_job_slab, job);
}
static struct xe_device *job_to_xe(struct xe_sched_job *job)
@@ -84,6 +88,9 @@ static void xe_sched_job_free_fences(struct xe_sched_job *job)
{
int i;
+ if (job->is_pt_job)
+ return;
+
for (i = 0; i < job->q->width; ++i) {
struct xe_job_ptrs *ptrs = &job->ptrs[i];
@@ -93,10 +100,23 @@ static void xe_sched_job_free_fences(struct xe_sched_job *job)
}
}
+/**
+ * xe_sched_job_create() - Create a scheduler job
+ * @q: exec queue to create the scheduler job for
+ * @batch_addr: array of batch addresses for the job; must match the width of
+ * @q, or NULL to indicate a PT job that does not require a batch address
+ *
+ * Create a scheduler job for submission.
+ *
+ * Context: Reclaim
+ *
+ * Return: a &xe_sched_job object on success, or an ERR_PTR on failure.
+ */
struct xe_sched_job *xe_sched_job_create(struct xe_exec_queue *q,
u64 *batch_addr)
{
bool is_migration = xe_sched_job_is_migration(q);
+ struct xe_device *xe = gt_to_xe(q->gt);
struct xe_sched_job *job;
int err;
int i;
@@ -105,10 +125,15 @@ struct xe_sched_job *xe_sched_job_create(struct xe_exec_queue *q,
/* only a kernel context can submit a vm-less job */
XE_WARN_ON(!q->vm && !(q->flags & EXEC_QUEUE_FLAG_KERNEL));
- job = job_alloc(xe_exec_queue_is_parallel(q) || is_migration);
+ xe_assert(xe, batch_addr ||
+ q->flags & (EXEC_QUEUE_FLAG_VM | EXEC_QUEUE_FLAG_MIGRATE));
+
+ job = job_alloc(!batch_addr || xe_exec_queue_is_parallel(q) ||
+ is_migration);
if (!job)
return ERR_PTR(-ENOMEM);
+ job->is_pt_job = !batch_addr;
job->q = q;
job->sample_timestamp = U64_MAX;
kref_init(&job->refcount);
@@ -119,34 +144,38 @@ struct xe_sched_job *xe_sched_job_create(struct xe_exec_queue *q,
if (err)
goto err_free;
- for (i = 0; i < q->width; ++i) {
- struct dma_fence *fence = xe_lrc_alloc_seqno_fence();
- struct dma_fence_chain *chain;
-
- if (IS_ERR(fence)) {
- err = PTR_ERR(fence);
- goto err_sched_job;
+ if (!batch_addr) {
+ job->fence = dma_fence_get_stub();
+ } else {
+ for (i = 0; i < q->width; ++i) {
+ struct dma_fence *fence = xe_lrc_alloc_seqno_fence();
+ struct dma_fence_chain *chain;
+
+ if (IS_ERR(fence)) {
+ err = PTR_ERR(fence);
+ goto err_sched_job;
+ }
+ job->ptrs[i].lrc_fence = fence;
+
+ if (i + 1 == q->width)
+ continue;
+
+ chain = dma_fence_chain_alloc();
+ if (!chain) {
+ err = -ENOMEM;
+ goto err_sched_job;
+ }
+ job->ptrs[i].chain_fence = chain;
}
- job->ptrs[i].lrc_fence = fence;
- if (i + 1 == q->width)
- continue;
+ width = q->width;
+ if (is_migration)
+ width = 2;
- chain = dma_fence_chain_alloc();
- if (!chain) {
- err = -ENOMEM;
- goto err_sched_job;
- }
- job->ptrs[i].chain_fence = chain;
+ for (i = 0; i < width; ++i)
+ job->ptrs[i].batch_addr = batch_addr[i];
}
- width = q->width;
- if (is_migration)
- width = 2;
-
- for (i = 0; i < width; ++i)
- job->ptrs[i].batch_addr = batch_addr[i];
-
atomic_inc(&q->job_cnt);
xe_pm_runtime_get_noresume(job_to_xe(job));
trace_xe_sched_job_create(job);
@@ -246,7 +275,7 @@ bool xe_sched_job_completed(struct xe_sched_job *job)
void xe_sched_job_arm(struct xe_sched_job *job)
{
struct xe_exec_queue *q = job->q;
- struct dma_fence *fence, *prev;
+ struct dma_fence *fence = job->fence, *prev;
struct xe_vm *vm = q->vm;
u64 seqno = 0;
int i;
@@ -259,6 +288,9 @@ void xe_sched_job_arm(struct xe_sched_job *job)
xe_vm_assert_held(q->vm);
}
+ if (job->is_pt_job)
+ goto arm;
+
if (vm && !xe_sched_job_is_migration(q) && !xe_vm_in_lr_mode(vm) &&
(vm->batch_invalidate_tlb || vm->tlb_flush_seqno != q->tlb_flush_seqno)) {
xe_vm_assert_held(vm);
@@ -286,6 +318,7 @@ void xe_sched_job_arm(struct xe_sched_job *job)
fence = &chain->base;
}
+arm:
job->fence = dma_fence_get(fence); /* Pairs with put in scheduler */
drm_sched_job_arm(&job->drm);
}
@@ -329,7 +362,7 @@ xe_sched_job_snapshot_capture(struct xe_sched_job *job)
snapshot->batch_addr_len = q->width;
for (i = 0; i < q->width; i++)
- snapshot->batch_addr[i] =
+ snapshot->batch_addr[i] = job->is_pt_job ? 0 :
xe_device_uncanonicalize_addr(xe, job->ptrs[i].batch_addr);
return snapshot;
diff --git a/drivers/gpu/drm/xe/xe_sched_job.h b/drivers/gpu/drm/xe/xe_sched_job.h
index 1c1cb44216c3..bbf5e3864ba3 100644
--- a/drivers/gpu/drm/xe/xe_sched_job.h
+++ b/drivers/gpu/drm/xe/xe_sched_job.h
@@ -83,6 +83,62 @@ xe_sched_job_add_migrate_flush(struct xe_sched_job *job, u32 flags)
job->migrate_flush_flags = flags;
}
+/**
+ * xe_sched_job_is_ulls() - Is a ULLS job
+ * @job: Xe schedule job object
+ *
+ * Return: True if @job is submitted as part of a ULLS sequence, False
+ * otherwise.
+ */
+static inline bool xe_sched_job_is_ulls(struct xe_sched_job *job)
+{
+ return job->ulls != ULLS_NONE;
+}
+
+/**
+ * xe_sched_job_ulls_has_batch() - Does a job carry batch buffers
+ * @job: Xe schedule job object
+ *
+ * The ULLS jobs which enter and exit ULLS mode exist only to move the
+ * migration context on and off the hardware, and carry no batch buffers.
+ *
+ * Return: True if @job carries batch buffers, False otherwise.
+ */
+static inline bool xe_sched_job_ulls_has_batch(struct xe_sched_job *job)
+{
+ return job->ulls == ULLS_NONE || job->ulls == ULLS_ACTIVE;
+}
+
+/**
+ * xe_sched_job_ulls_parks() - Does a job park the engine for its successor
+ * @job: Xe schedule job object
+ *
+ * A ULLS job which is not the last one emits a postamble, parking the engine
+ * on its successor's semaphore and publishing that successor's ring tail.
+ *
+ * Return: True if @job emits a ULLS postamble, False otherwise.
+ */
+static inline bool xe_sched_job_ulls_parks(struct xe_sched_job *job)
+{
+ return job->ulls == ULLS_ENTER || job->ulls == ULLS_ACTIVE;
+}
+
+/**
+ * xe_sched_job_ulls_is_chained() - Has a job's predecessor already published it
+ * @job: Xe schedule job object
+ *
+ * A ULLS job which is not the first one has had its ring tail published by its
+ * predecessor's postamble, which also left the engine parked on this job's
+ * semaphore. Submitting it is a semaphore write alone - no H2G and no ring
+ * tail write.
+ *
+ * Return: True if @job was published by its predecessor, False otherwise.
+ */
+static inline bool xe_sched_job_ulls_is_chained(struct xe_sched_job *job)
+{
+ return job->ulls == ULLS_ACTIVE || job->ulls == ULLS_EXIT;
+}
+
bool xe_sched_job_is_migration(struct xe_exec_queue *q);
struct xe_sched_job_snapshot *xe_sched_job_snapshot_capture(struct xe_sched_job *job);
diff --git a/drivers/gpu/drm/xe/xe_sched_job_types.h b/drivers/gpu/drm/xe/xe_sched_job_types.h
index 0490b1247a6e..c8cdbe68843f 100644
--- a/drivers/gpu/drm/xe/xe_sched_job_types.h
+++ b/drivers/gpu/drm/xe/xe_sched_job_types.h
@@ -10,10 +10,28 @@
#include <drm/gpu_scheduler.h>
-struct xe_exec_queue;
struct dma_fence;
struct dma_fence_chain;
+struct xe_exec_queue;
+struct xe_cpu_bind_pt_update_ops;
+struct xe_pt_job_ops;
+struct xe_tile;
+struct xe_vm;
+
+/**
+ * struct xe_pt_update_args - PT update arguments
+ */
+struct xe_pt_update_args {
+ /** @vm: VM which is being bound */
+ struct xe_vm *vm;
+ /** @ops: CPU bind PT update ops */
+ const struct xe_cpu_bind_pt_update_ops *ops;
+#define XE_PT_UPDATE_JOB_OPS_COUNT 2
+ /** @pt_job_ops: PT job ops state */
+ struct xe_pt_job_ops *pt_job_ops[XE_PT_UPDATE_JOB_OPS_COUNT];
+};
+
/**
* struct xe_job_ptrs - Per hw engine instance data
*/
@@ -32,6 +50,23 @@ struct xe_job_ptrs {
};
/**
+ * enum xe_ulls_state - ULLS state of a migration job
+ *
+ * Describes where a job sits in a ULLS (Ultra Low Latency Submission)
+ * sequence. See the ULLS documentation in xe_migrate.c.
+ */
+enum xe_ulls_state {
+ /** @ULLS_NONE: Not a ULLS job */
+ ULLS_NONE = 0,
+ /** @ULLS_ENTER: Job which enters ULLS mode */
+ ULLS_ENTER,
+ /** @ULLS_ACTIVE: Job submitted while in ULLS mode */
+ ULLS_ACTIVE,
+ /** @ULLS_EXIT: Job which exits ULLS mode */
+ ULLS_EXIT,
+};
+
+/**
* struct xe_sched_job - Xe schedule job (batch buffer tracking)
*/
struct xe_sched_job {
@@ -61,6 +96,8 @@ struct xe_sched_job {
u32 migrate_flush_flags;
/** @sample_timestamp: Sampling of job timestamp in TDR */
u64 sample_timestamp;
+ /** @ulls: ULLS state of this job */
+ enum xe_ulls_state ulls;
/** @ring_ops_flush_tlb: The ring ops need to flush TLB before payload. */
bool ring_ops_flush_tlb;
/** @ring_ops_force_reset: The ring ops need to trigger a reset before payload. */
@@ -71,8 +108,14 @@ struct xe_sched_job {
bool restore_replay;
/** @last_replay: last job being replayed */
bool last_replay;
- /** @ptrs: per instance pointers. */
- struct xe_job_ptrs ptrs[];
+ /** @is_pt_job: is a PT job */
+ bool is_pt_job;
+ union {
+ /** @ptrs: per instance pointers. */
+ DECLARE_FLEX_ARRAY(struct xe_job_ptrs, ptrs);
+ /** @pt_update: PT update arguments */
+ DECLARE_FLEX_ARRAY(struct xe_pt_update_args, pt_update);
+ };
};
struct xe_sched_job_snapshot {
diff --git a/drivers/gpu/drm/xe/xe_sriov_pf_provision.c b/drivers/gpu/drm/xe/xe_sriov_pf_provision.c
index 0ec7ea83f12a..c37e422b5a5c 100644
--- a/drivers/gpu/drm/xe/xe_sriov_pf_provision.c
+++ b/drivers/gpu/drm/xe/xe_sriov_pf_provision.c
@@ -112,7 +112,7 @@ static int pf_reprovision_default(struct xe_device *xe)
int result = 0;
int err;
- guard(mutex)(xe_sriov_pf_master_mutex(xe));
+ lockdep_assert_held(xe_sriov_pf_master_mutex(xe));
for_each_gt(gt, xe, id) {
err = xe_gt_sriov_pf_policy_set_sched_if_idle_locked(gt, false);
@@ -138,6 +138,8 @@ int xe_sriov_pf_reprovision_default(struct xe_device *xe)
{
xe_assert(xe, IS_SRIOV_PF(xe));
+ guard(mutex)(xe_sriov_pf_master_mutex(xe));
+
if (!pf_auto_provisioning_mode(xe))
return 0;
diff --git a/drivers/gpu/drm/xe/xe_svm.c b/drivers/gpu/drm/xe/xe_svm.c
index f39e647512ad..909a602d091e 100644
--- a/drivers/gpu/drm/xe/xe_svm.c
+++ b/drivers/gpu/drm/xe/xe_svm.c
@@ -9,6 +9,7 @@
#include <drm/drm_managed.h>
#include <drm/drm_pagemap.h>
#include <drm/drm_pagemap_util.h>
+#include <drm/ttm/ttm_bo.h>
#include "xe_bo.h"
#include "xe_exec_queue_types.h"
@@ -1626,6 +1627,73 @@ int xe_svm_range_get_pages(struct xe_vm *vm, struct xe_svm_range *range,
}
/**
+ * xe_svm_devmem_lru_bump() - Move a range's backing BO to the TTM LRU tail
+ * @devmem_allocation: The device-memory allocation backing the range's pages
+ *
+ * Intended as a &drm_gpusvm_ctx.devmem_fn, so drm_gpusvm_get_pages() calls it
+ * under the GPUSVM notifier lock. That lock sits in the reclaim path, so
+ * dma-resv cannot be acquired blocking beneath it; dma-resv is also the outer
+ * lock of the notifier when memory is allocated and migrated, so a blocking
+ * acquire here would invert that order. Hence the trylock. The BO is private
+ * (dedicated dma-resv), so the trylock succeeds unless the BO is being moved
+ * elsewhere (e.g. eviction); a contended BO is simply skipped.
+ *
+ * A shared device_private_page_owner can hand us allocations backed by a
+ * foreign driver; skip anything that is not an Xe BO before casting.
+ */
+void xe_svm_devmem_lru_bump(struct drm_pagemap_devmem *devmem_allocation)
+{
+#if IS_ENABLED(CONFIG_DRM_XE_PAGEMAP)
+ struct xe_bo *bo;
+
+ if (devmem_allocation->ops != &dpagemap_devmem_ops)
+ return;
+
+ bo = to_xe_bo(devmem_allocation);
+
+ if (!dma_resv_trylock(bo->ttm.base.resv))
+ return;
+
+ ttm_bo_move_to_lru_tail_unlocked(&bo->ttm);
+ dma_resv_unlock(bo->ttm.base.resv);
+#endif
+}
+
+/**
+ * xe_svm_range_prefetch_lru_bump() - Bump the TTM LRU for an already-valid range
+ * @vm: Pointer to the struct xe_vm
+ * @vma: Pointer to the VMA covering the range
+ * @range: Pointer to the xe SVM range structure
+ * @dpagemap: Target pagemap of the prefetch, or %NULL for system memory
+ *
+ * A valid range skips migration and binding, so nothing else refreshes its
+ * backing BOs on the LRU. Re-fault the CPU pages without touching the DMA
+ * mappings (@no_dma_map) and move each backing BO to the LRU tail.
+ *
+ * Prefetching is a best-effort optimization, so any failure is ignored; a
+ * later fault will refault the range.
+ */
+void xe_svm_range_prefetch_lru_bump(struct xe_vm *vm, struct xe_vma *vma,
+ struct xe_svm_range *range,
+ struct drm_pagemap *dpagemap)
+{
+ struct drm_gpusvm_ctx ctx = {
+ .read_only = xe_vma_read_only(vma),
+ .device_private_page_owner =
+ xe_svm_private_page_owner(vm, !dpagemap),
+ .no_dma_map = 1,
+ .devmem_fn = xe_svm_devmem_lru_bump,
+ };
+
+ guard(mutex)(&range->lock);
+
+ if (xe_svm_range_is_removed(range))
+ return;
+
+ xe_svm_range_get_pages(vm, range, &ctx);
+}
+
+/**
* xe_svm_ranges_zap_ptes_in_range - clear ptes of svm ranges in input range
* @vm: Pointer to the xe_vm structure
* @start: Start of the input range
diff --git a/drivers/gpu/drm/xe/xe_svm.h b/drivers/gpu/drm/xe/xe_svm.h
index 2ef4ef026ccd..ffc1cc808102 100644
--- a/drivers/gpu/drm/xe/xe_svm.h
+++ b/drivers/gpu/drm/xe/xe_svm.h
@@ -128,6 +128,12 @@ struct xe_svm_range *xe_svm_range_find_or_insert(struct xe_vm *vm, u64 addr,
int xe_svm_range_get_pages(struct xe_vm *vm, struct xe_svm_range *range,
struct drm_gpusvm_ctx *ctx);
+void xe_svm_devmem_lru_bump(struct drm_pagemap_devmem *devmem_allocation);
+
+void xe_svm_range_prefetch_lru_bump(struct xe_vm *vm, struct xe_vma *vma,
+ struct xe_svm_range *range,
+ struct drm_pagemap *dpagemap);
+
bool xe_svm_range_needs_migrate_to_vram(struct xe_svm_range *range, struct xe_vma *vma,
const struct drm_pagemap *dpagemap);
@@ -356,6 +362,18 @@ int xe_svm_range_get_pages(struct xe_vm *vm, struct xe_svm_range *range,
return -EINVAL;
}
+static inline
+void xe_svm_devmem_lru_bump(struct drm_pagemap_devmem *devmem_allocation)
+{
+}
+
+static inline
+void xe_svm_range_prefetch_lru_bump(struct xe_vm *vm, struct xe_vma *vma,
+ struct xe_svm_range *range,
+ struct drm_pagemap *dpagemap)
+{
+}
+
static inline struct xe_svm_range *to_xe_range(struct drm_gpusvm_range *r)
{
return NULL;
diff --git a/drivers/gpu/drm/xe/xe_sync.c b/drivers/gpu/drm/xe/xe_sync.c
index 37866768d64c..06b1c913588a 100644
--- a/drivers/gpu/drm/xe/xe_sync.c
+++ b/drivers/gpu/drm/xe/xe_sync.c
@@ -345,15 +345,9 @@ xe_sync_in_fence_get(struct xe_sync_entry *sync, int num_sync,
return ERR_PTR(-EOPNOTSUPP);
if (q->flags & EXEC_QUEUE_FLAG_VM) {
- struct xe_exec_queue *__q;
- struct xe_tile *tile;
- u8 id;
-
- for_each_tile(tile, vm->xe, id) {
+ num_fence++;
+ for_each_tlb_inval(q, i)
num_fence++;
- for_each_tlb_inval(i)
- num_fence++;
- }
fences = kmalloc_objs(*fences, num_fence);
if (!fences)
@@ -361,17 +355,9 @@ xe_sync_in_fence_get(struct xe_sync_entry *sync, int num_sync,
fences[current_fence++] =
xe_exec_queue_last_fence_get(q, vm);
- for_each_tlb_inval(i)
+ for_each_tlb_inval(q, i)
fences[current_fence++] =
xe_exec_queue_tlb_inval_last_fence_get(q, vm, i);
- list_for_each_entry(__q, &q->multi_gt_list,
- multi_gt_link) {
- fences[current_fence++] =
- xe_exec_queue_last_fence_get(__q, vm);
- for_each_tlb_inval(i)
- fences[current_fence++] =
- xe_exec_queue_tlb_inval_last_fence_get(__q, vm, i);
- }
xe_assert(vm->xe, current_fence == num_fence);
cf = dma_fence_array_create(num_fence, fences,
diff --git a/drivers/gpu/drm/xe/xe_sysctrl_mailbox.c b/drivers/gpu/drm/xe/xe_sysctrl_mailbox.c
index 72baf1aa4b3a..e21848ca7594 100644
--- a/drivers/gpu/drm/xe/xe_sysctrl_mailbox.c
+++ b/drivers/gpu/drm/xe/xe_sysctrl_mailbox.c
@@ -334,6 +334,9 @@ void xe_sysctrl_create_command(struct xe_sysctrl_mailbox_command *command, u8 gr
* If a response is expected, @cmd->data_out must point to a buffer of
* size @cmd->data_out_len supplied by caller.
*
+ * @cmd->timeout_ms specifies the response timeout in milliseconds.
+ * A value of 0 uses %XE_SYSCTRL_MB_DEFAULT_TIMEOUT_MS.
+ *
* On success, @rdata_len is updated with number of valid response bytes
* returned by firmware, bounded by @cmd->data_out_len.
*
@@ -347,6 +350,7 @@ int xe_sysctrl_send_command(struct xe_sysctrl *sc,
u8 group_id, command_code;
u8 *mbox_cmd = NULL;
size_t cmd_size = 0;
+ unsigned int timeout_ms;
int ret;
guard(xe_pm_runtime_noresume)(xe);
@@ -360,6 +364,7 @@ int xe_sysctrl_send_command(struct xe_sysctrl *sc,
group_id = XE_SYSCTRL_APP_HDR_GROUP_ID(&cmd->header);
command_code = XE_SYSCTRL_APP_HDR_COMMAND(&cmd->header);
+ timeout_ms = cmd->timeout_ms ?: XE_SYSCTRL_MB_DEFAULT_TIMEOUT_MS;
might_sleep();
@@ -375,7 +380,7 @@ int xe_sysctrl_send_command(struct xe_sysctrl *sc,
ret = sysctrl_send_command(sc, mbox_cmd, cmd_size,
cmd->data_out, cmd->data_out_len, rdata_len,
- XE_SYSCTRL_MB_DEFAULT_TIMEOUT_MS);
+ timeout_ms);
if (ret)
xe_err(xe, "sysctrl: Mailbox command failed: %pe\n", ERR_PTR(ret));
diff --git a/drivers/gpu/drm/xe/xe_sysctrl_mailbox_types.h b/drivers/gpu/drm/xe/xe_sysctrl_mailbox_types.h
index c236e5377f30..e98fa6877fec 100644
--- a/drivers/gpu/drm/xe/xe_sysctrl_mailbox_types.h
+++ b/drivers/gpu/drm/xe/xe_sysctrl_mailbox_types.h
@@ -105,6 +105,9 @@ struct xe_sysctrl_mailbox_command {
/** @data_out_len: Size of output buffer in bytes (0 if no response expected) */
size_t data_out_len;
+
+ /** @timeout_ms: Response timeout in ms, or 0 for %XE_SYSCTRL_MB_DEFAULT_TIMEOUT_MS */
+ unsigned int timeout_ms;
};
/* Modify as needed */
diff --git a/drivers/gpu/drm/xe/xe_tlb_inval.c b/drivers/gpu/drm/xe/xe_tlb_inval.c
index dd1030de9ab6..c9cc5842d65b 100644
--- a/drivers/gpu/drm/xe/xe_tlb_inval.c
+++ b/drivers/gpu/drm/xe/xe_tlb_inval.c
@@ -5,6 +5,7 @@
#include <drm/drm_managed.h>
+#include "xe_devcoredump.h"
#include "xe_device_types.h"
#include "xe_force_wake.h"
#include "xe_gt_stats.h"
@@ -13,6 +14,7 @@
#include "xe_guc_tlb_inval.h"
#include "xe_mmio.h"
#include "xe_pm.h"
+#include "xe_printk.h"
#include "xe_tlb_inval.h"
#include "xe_trace.h"
@@ -29,6 +31,12 @@
#define FENCE_STACK_BIT DMA_FENCE_FLAG_USER_BITS
+/* The frontend is only ever embedded in a GT */
+static struct xe_gt *tlb_inval_to_gt(struct xe_tlb_inval *tlb_inval)
+{
+ return container_of(tlb_inval, struct xe_gt, tlb_inval);
+}
+
static void xe_tlb_inval_fence_fini(struct xe_tlb_inval_fence *fence)
{
if (WARN_ON_ONCE(!fence->tlb_inval))
@@ -73,6 +81,7 @@ static void xe_tlb_inval_fence_timeout(struct work_struct *work)
struct xe_device *xe = tlb_inval->xe;
struct xe_tlb_inval_fence *fence, *next;
long timeout_delay = tlb_inval->ops->timeout_delay(tlb_inval);
+ int timedout_seqno = 0, seqno_recv = 0;
tlb_inval->ops->flush(tlb_inval);
@@ -90,13 +99,45 @@ static void xe_tlb_inval_fence_timeout(struct work_struct *work)
"TLB invalidation fence timeout, seqno=%d recv=%d",
fence->seqno, tlb_inval->seqno_recv);
+ if (!timedout_seqno) {
+ /*
+ * Hold a PM reference across the capture below. Every
+ * pending fence holds one, so the device is awake
+ * here, but signalling them may drop the last
+ * reference and let it autosuspend before the
+ * snapshot touches the hardware.
+ */
+ xe_pm_runtime_get_noresume(xe);
+ }
+
+ timedout_seqno = fence->seqno;
+ if (!tlb_inval->timedout_seqno) {
+ tlb_inval->timedout_seqno = fence->seqno;
+ tlb_inval->timedout_inval_time = fence->inval_time;
+ tlb_inval->timedout_time = ktime_get();
+ }
+
fence->base.error = -ETIME;
xe_tlb_inval_fence_signal(fence);
}
if (!list_empty(&tlb_inval->pending_fences))
queue_delayed_work(tlb_inval->timeout_wq, &tlb_inval->fence_tdr,
timeout_delay);
+ seqno_recv = tlb_inval->seqno_recv;
spin_unlock_irq(&tlb_inval->pending_lock);
+
+ /*
+ * Capture the GuC log and CT state so the firmware side of the hang
+ * can be inspected; there is no queue or job to blame here. Must be
+ * outside pending_lock as the capture takes sleeping locks, hence
+ * @seqno_recv is sampled above while the lock is still held.
+ */
+ if (timedout_seqno) {
+ xe_devcoredump_gt(tlb_inval_to_gt(tlb_inval),
+ "TLB invalidation fence timeout, seqno=%d recv=%d",
+ timedout_seqno, seqno_recv);
+ xe_pm_runtime_put(xe);
+ }
}
/**
@@ -207,6 +248,7 @@ void xe_tlb_inval_reset(struct xe_tlb_inval *tlb_inval)
else
pending_seqno = tlb_inval->seqno - 1;
WRITE_ONCE(tlb_inval->seqno_recv, pending_seqno);
+ tlb_inval->timedout_seqno = 0;
list_for_each_entry_safe(fence, next,
&tlb_inval->pending_fences, link)
@@ -435,6 +477,18 @@ void xe_tlb_inval_done_handler(struct xe_tlb_inval *tlb_inval, int seqno)
WRITE_ONCE(tlb_inval->seqno_recv, seqno);
+ if (tlb_inval->timedout_seqno &&
+ xe_tlb_inval_seqno_past(tlb_inval, tlb_inval->timedout_seqno)) {
+ ktime_t now = ktime_get();
+
+ xe_warn(xe,
+ "TLB invalidation late ack: seqno=%d recv=%d, request-to-ack=%lldms, timeout-to-ack=%lldms",
+ tlb_inval->timedout_seqno, seqno,
+ ktime_ms_delta(now, tlb_inval->timedout_inval_time),
+ ktime_ms_delta(now, tlb_inval->timedout_time));
+ tlb_inval->timedout_seqno = 0;
+ }
+
list_for_each_entry_safe(fence, next,
&tlb_inval->pending_fences, link) {
trace_xe_tlb_inval_fence_recv(xe, fence);
diff --git a/drivers/gpu/drm/xe/xe_tlb_inval_job.c b/drivers/gpu/drm/xe/xe_tlb_inval_job.c
index 04d21015cd5d..7378cfe6e855 100644
--- a/drivers/gpu/drm/xe/xe_tlb_inval_job.c
+++ b/drivers/gpu/drm/xe/xe_tlb_inval_job.c
@@ -4,6 +4,7 @@
*/
#include "xe_assert.h"
+#include "xe_cpu_bind.h"
#include "xe_dep_job_types.h"
#include "xe_dep_scheduler.h"
#include "xe_exec_queue.h"
@@ -12,7 +13,6 @@
#include "xe_page_reclaim.h"
#include "xe_tlb_inval.h"
#include "xe_tlb_inval_job.h"
-#include "xe_migrate.h"
#include "xe_pm.h"
#include "xe_vm.h"
@@ -39,8 +39,8 @@ struct xe_tlb_inval_job {
u64 start;
/** @end: End address to invalidate */
u64 end;
- /** @type: GT type */
- int type;
+ /** @idx: Index of tlb invalidation */
+ int idx;
/** @fence_armed: Fence has been armed */
bool fence_armed;
};
@@ -87,7 +87,7 @@ static const struct xe_dep_job_ops dep_job_ops = {
* @vm: VM which TLB invalidation is being issued for
* @start: Start address to invalidate
* @end: End address to invalidate
- * @type: GT type
+ * @idx: Index of tlb invalidation
*
* Create a TLB invalidation job and initialize internal fields. The caller is
* responsible for releasing the creation reference.
@@ -97,7 +97,7 @@ static const struct xe_dep_job_ops dep_job_ops = {
struct xe_tlb_inval_job *
xe_tlb_inval_job_create(struct xe_exec_queue *q, struct xe_tlb_inval *tlb_inval,
struct xe_dep_scheduler *dep_scheduler,
- struct xe_vm *vm, u64 start, u64 end, int type)
+ struct xe_vm *vm, u64 start, u64 end, int idx)
{
struct xe_tlb_inval_job *job;
struct drm_sched_entity *entity =
@@ -105,8 +105,7 @@ xe_tlb_inval_job_create(struct xe_exec_queue *q, struct xe_tlb_inval *tlb_inval,
struct xe_tlb_inval_fence *ifence;
int err;
- xe_assert(vm->xe, type == XE_EXEC_QUEUE_TLB_INVAL_MEDIA_GT ||
- type == XE_EXEC_QUEUE_TLB_INVAL_PRIMARY_GT);
+ xe_assert(vm->xe, idx < XE_EXEC_QUEUE_TLB_INVAL_COUNT);
job = kmalloc_obj(*job);
if (!job)
@@ -120,7 +119,7 @@ xe_tlb_inval_job_create(struct xe_exec_queue *q, struct xe_tlb_inval *tlb_inval,
job->fence_armed = false;
xe_page_reclaim_list_init(&job->prl);
job->dep.ops = &dep_job_ops;
- job->type = type;
+ job->idx = idx;
kref_init(&job->refcount);
xe_exec_queue_get(q); /* Pairs with put in xe_tlb_inval_job_destroy */
xe_vm_get(vm); /* Pairs with put in xe_tlb_inval_job_destroy */
@@ -219,7 +218,6 @@ int xe_tlb_inval_job_alloc_dep(struct xe_tlb_inval_job *job)
/**
* xe_tlb_inval_job_push() - TLB invalidation job push
* @job: TLB invalidation job to push
- * @m: The migration object being used
* @fence: Dependency for TLB invalidation job
*
* Pushes a TLB invalidation job for execution, using @fence as a dependency.
@@ -231,11 +229,11 @@ int xe_tlb_inval_job_alloc_dep(struct xe_tlb_inval_job *job)
* Return: Job's finished fence on success, cannot fail
*/
struct dma_fence *xe_tlb_inval_job_push(struct xe_tlb_inval_job *job,
- struct xe_migrate *m,
struct dma_fence *fence)
{
struct xe_tlb_inval_fence *ifence =
container_of(job->fence, typeof(*ifence), base);
+ struct xe_cpu_bind *cpu_bind = gt_to_xe(job->q->gt)->cpu_bind;
if (!dma_fence_is_signaled(fence)) {
void *ptr;
@@ -259,11 +257,11 @@ struct dma_fence *xe_tlb_inval_job_push(struct xe_tlb_inval_job *job,
job->fence_armed = true;
/*
- * We need the migration lock to protect the job's seqno and the spsc
- * queue, only taken on migration queue, user queues protected dma-resv
+ * We need the cpu_bind lock to protect the job's seqno and the spsc
+ * queue, only taken on cpu_bind queue, user queues protected dma-resv
* VM lock.
*/
- xe_migrate_job_lock(m, job->q);
+ xe_cpu_bind_job_lock(cpu_bind, job->q);
/* Creation ref pairs with put in xe_tlb_inval_job_destroy */
xe_tlb_inval_fence_init(job->tlb_inval, ifence, false);
@@ -280,9 +278,9 @@ struct dma_fence *xe_tlb_inval_job_push(struct xe_tlb_inval_job *job,
/* Let the upper layers fish this out */
xe_exec_queue_tlb_inval_last_fence_set(job->q, job->vm,
&job->dep.drm.s_fence->finished,
- job->type);
+ job->idx);
- xe_migrate_job_unlock(m, job->q);
+ xe_cpu_bind_job_unlock(cpu_bind, job->q);
/*
* Not using job->fence, as it has its own dma-fence context, which does
diff --git a/drivers/gpu/drm/xe/xe_tlb_inval_job.h b/drivers/gpu/drm/xe/xe_tlb_inval_job.h
index 03d6e21cd611..97e032ea21c3 100644
--- a/drivers/gpu/drm/xe/xe_tlb_inval_job.h
+++ b/drivers/gpu/drm/xe/xe_tlb_inval_job.h
@@ -11,7 +11,6 @@
struct dma_fence;
struct xe_dep_scheduler;
struct xe_exec_queue;
-struct xe_migrate;
struct xe_page_reclaim_list;
struct xe_tlb_inval;
struct xe_tlb_inval_job;
@@ -20,7 +19,7 @@ struct xe_vm;
struct xe_tlb_inval_job *
xe_tlb_inval_job_create(struct xe_exec_queue *q, struct xe_tlb_inval *tlb_inval,
struct xe_dep_scheduler *dep_scheduler,
- struct xe_vm *vm, u64 start, u64 end, int type);
+ struct xe_vm *vm, u64 start, u64 end, int idx);
void xe_tlb_inval_job_add_page_reclaim(struct xe_tlb_inval_job *job,
struct xe_page_reclaim_list *prl);
@@ -28,7 +27,6 @@ void xe_tlb_inval_job_add_page_reclaim(struct xe_tlb_inval_job *job,
int xe_tlb_inval_job_alloc_dep(struct xe_tlb_inval_job *job);
struct dma_fence *xe_tlb_inval_job_push(struct xe_tlb_inval_job *job,
- struct xe_migrate *m,
struct dma_fence *fence);
void xe_tlb_inval_job_get(struct xe_tlb_inval_job *job);
diff --git a/drivers/gpu/drm/xe/xe_tlb_inval_types.h b/drivers/gpu/drm/xe/xe_tlb_inval_types.h
index d77be1aedc91..80d2019fa20b 100644
--- a/drivers/gpu/drm/xe/xe_tlb_inval_types.h
+++ b/drivers/gpu/drm/xe/xe_tlb_inval_types.h
@@ -113,6 +113,23 @@ struct xe_tlb_inval {
*/
spinlock_t pending_lock;
/**
+ * @timedout_seqno: seqno of the most recent timed out TLB
+ * invalidation, 0 if none. Used to measure how late the ack for a
+ * timed out invalidation actually arrives. Protected by
+ * @pending_lock.
+ */
+ int timedout_seqno;
+ /**
+ * @timedout_inval_time: request time of @timedout_seqno. Protected by
+ * @pending_lock.
+ */
+ ktime_t timedout_inval_time;
+ /**
+ * @timedout_time: time @timedout_seqno was signaled with -ETIME.
+ * Protected by @pending_lock.
+ */
+ ktime_t timedout_time;
+ /**
* @fence_tdr: schedules a delayed call to xe_tlb_fence_timeout after
* the timeout interval is over.
*/
diff --git a/drivers/gpu/drm/xe/xe_trace.h b/drivers/gpu/drm/xe/xe_trace.h
index 2fe8f89a1e34..d4e9d91f6f7f 100644
--- a/drivers/gpu/drm/xe/xe_trace.h
+++ b/drivers/gpu/drm/xe/xe_trace.h
@@ -261,7 +261,7 @@ DECLARE_EVENT_CLASS(xe_sched_job,
__entry->flags = job->q->flags;
__entry->error = job->fence ? job->fence->error : 0;
__entry->fence = job->fence;
- __entry->batch_addr = (u64)job->ptrs[0].batch_addr;
+ __entry->batch_addr = job->is_pt_job ? 0 : (u64)job->ptrs[0].batch_addr;
),
TP_printk("dev=%s, fence=%p, seqno=%u, lrc_seqno=%u, gt=%u, guc_id=%d, batch_addr=0x%012llx, guc_state=0x%x, flags=0x%x, error=%d",
diff --git a/drivers/gpu/drm/xe/xe_trace_bo.h b/drivers/gpu/drm/xe/xe_trace_bo.h
index 86323cf3be2c..36fc7eb65882 100644
--- a/drivers/gpu/drm/xe/xe_trace_bo.h
+++ b/drivers/gpu/drm/xe/xe_trace_bo.h
@@ -16,10 +16,15 @@
#include "xe_bo_types.h"
#include "xe_vm.h"
+#define __dev_name_xe(xe) dev_name((xe)->drm.dev)
#define __dev_name_bo(bo) dev_name(xe_bo_device(bo)->drm.dev)
#define __dev_name_vm(vm) dev_name((vm)->xe->drm.dev)
#define __dev_name_vma(vma) __dev_name_vm(xe_vma_vm(vma))
+#define XE_VM_TRACE_MODE_DMA_FENCE 0
+#define XE_VM_TRACE_MODE_PREEMPT_FENCE 1
+#define XE_VM_TRACE_MODE_FAULT 2
+
DECLARE_EVENT_CLASS(xe_bo,
TP_PROTO(struct xe_bo *bo),
TP_ARGS(bo),
@@ -194,6 +199,7 @@ DECLARE_EVENT_CLASS(xe_vm,
__field(struct xe_vm *, vm)
__field(u32, asid)
__field(u32, flags)
+ __field(u8, mode)
),
TP_fast_assign(
@@ -201,11 +207,20 @@ DECLARE_EVENT_CLASS(xe_vm,
__entry->vm = vm;
__entry->asid = vm->usm.asid;
__entry->flags = vm->flags;
+ __entry->mode = xe_vm_in_fault_mode(vm) ?
+ XE_VM_TRACE_MODE_FAULT :
+ xe_vm_in_preempt_fence_mode(vm) ?
+ XE_VM_TRACE_MODE_PREEMPT_FENCE :
+ XE_VM_TRACE_MODE_DMA_FENCE;
),
- TP_printk("dev=%s, vm=%p, asid=0x%05x, vm flags=0x%05x",
+ TP_printk("dev=%s, vm=%p, asid=0x%05x, vm flags=0x%05x, mode=%s",
__get_str(dev), __entry->vm, __entry->asid,
- __entry->flags)
+ __entry->flags,
+ __print_symbolic(__entry->mode,
+ { XE_VM_TRACE_MODE_DMA_FENCE, "dma-fence" },
+ { XE_VM_TRACE_MODE_PREEMPT_FENCE, "preempt-fence" },
+ { XE_VM_TRACE_MODE_FAULT, "fault" }))
);
DEFINE_EVENT(xe_vm, xe_vm_kill,
@@ -223,6 +238,16 @@ DEFINE_EVENT(xe_vm, xe_vm_free,
TP_ARGS(vm)
);
+DEFINE_EVENT(xe_vm, xe_vm_close_and_put,
+ TP_PROTO(struct xe_vm *vm),
+ TP_ARGS(vm)
+);
+
+DEFINE_EVENT(xe_vm, xe_vm_asid_release,
+ TP_PROTO(struct xe_vm *vm),
+ TP_ARGS(vm)
+);
+
DEFINE_EVENT(xe_vm, xe_vm_cpu_bind,
TP_PROTO(struct xe_vm *vm),
TP_ARGS(vm)
@@ -253,6 +278,35 @@ DEFINE_EVENT(xe_vm, xe_vm_ops_fail,
TP_ARGS(vm)
);
+TRACE_EVENT(xe_pagefault_fail,
+ TP_PROTO(struct xe_device *xe, struct xe_vm *vm, struct xe_vma *vma,
+ u32 asid, u64 page_addr, u8 reason, int err),
+ TP_ARGS(xe, vm, vma, asid, page_addr, reason, err),
+
+ TP_STRUCT__entry(__string(dev, __dev_name_xe(xe))
+ __field(struct xe_vm *, vm)
+ __field(struct xe_vma *, vma)
+ __field(u32, asid)
+ __field(u64, page_addr)
+ __field(u8, reason)
+ __field(int, err)
+ ),
+
+ TP_fast_assign(__assign_str(dev);
+ __entry->vm = vm;
+ __entry->vma = vma;
+ __entry->asid = asid;
+ __entry->page_addr = page_addr;
+ __entry->reason = reason;
+ __entry->err = err;
+ ),
+
+ TP_printk("dev=%s, vm=%p, vma=%p, asid=0x%05x, page_addr=0x%012llx, reason=%u, err=%d",
+ __get_str(dev), __entry->vm, __entry->vma,
+ __entry->asid, __entry->page_addr, __entry->reason,
+ __entry->err)
+);
+
#endif
/* This part must be outside protection */
diff --git a/drivers/gpu/drm/xe/xe_tuning.c b/drivers/gpu/drm/xe/xe_tuning.c
index bcec40ca2d35..a9bd3f8969a5 100644
--- a/drivers/gpu/drm/xe/xe_tuning.c
+++ b/drivers/gpu/drm/xe/xe_tuning.c
@@ -100,6 +100,10 @@ VISIBLE_IF_KUNIT const struct xe_rtp_table_sr gt_tunings = XE_RTP_TABLE_SR(
XE_RTP_ACTIONS(FIELD_SET(GAMSTLB_CTRL, BANK_HASH_MODE,
BANK_HASH_4KB_MODE))
},
+ { XE_RTP_NAME("Tuning: Force All Non-Compressible Memory Reads to be 256B Overfetches"),
+ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(3510, XE_RTP_END_VERSION_UNDEFINED)),
+ XE_RTP_ACTIONS(SET(L3SQCREG2, NONCOMPMEMRD256BOVRFETCHEN))
+ },
);
EXPORT_SYMBOL_IF_KUNIT(gt_tunings);
diff --git a/drivers/gpu/drm/xe/xe_uc_fw.c b/drivers/gpu/drm/xe/xe_uc_fw.c
index a8e6f18cc9b4..f61910431cf2 100644
--- a/drivers/gpu/drm/xe/xe_uc_fw.c
+++ b/drivers/gpu/drm/xe/xe_uc_fw.c
@@ -141,6 +141,7 @@ struct fw_blobs_by_type {
/* for the GSC FW we match the compatibility version and not the release one */
#define XE_GSC_FIRMWARE_DEFS(fw_def, major_ver) \
+ fw_def(NOVALAKE_S, GT_TYPE_ANY, major_ver(xe, gsc, nvl, 108, 0, 0)) \
fw_def(PANTHERLAKE, GT_TYPE_ANY, major_ver(xe, gsc, ptl, 105, 1, 0)) \
fw_def(LUNARLAKE, GT_TYPE_ANY, major_ver(xe, gsc, lnl, 104, 1, 0)) \
fw_def(METEORLAKE, GT_TYPE_ANY, major_ver(i915, gsc, mtl, 102, 1, 0))
diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
index 17dc4debe7c1..2422bfde9177 100644
--- a/drivers/gpu/drm/xe/xe_vm.c
+++ b/drivers/gpu/drm/xe/xe_vm.c
@@ -24,6 +24,7 @@
#include "regs/xe_gtt_defs.h"
#include "xe_assert.h"
#include "xe_bo.h"
+#include "xe_cpu_bind.h"
#include "xe_device.h"
#include "xe_drm_client.h"
#include "xe_exec_queue.h"
@@ -148,7 +149,7 @@ static bool xe_vm_is_idle(struct xe_vm *vm)
xe_vm_assert_held(vm);
list_for_each_entry(q, &vm->preempt.exec_queues, lr.link) {
- if (!xe_exec_queue_is_idle(q))
+ if (!xe_exec_queue_is_idle(q, 0))
return false;
}
@@ -682,11 +683,9 @@ static int xe_vma_ops_alloc(struct xe_vma_ops *vops, bool array_of_binds)
if (!vops->pt_update_ops[i].num_ops)
continue;
- vops->pt_update_ops[i].ops =
- kmalloc_objs(*vops->pt_update_ops[i].ops,
- vops->pt_update_ops[i].num_ops,
- GFP_KERNEL | __GFP_RETRY_MAYFAIL | __GFP_NOWARN);
- if (!vops->pt_update_ops[i].ops)
+ vops->pt_update_ops[i].pt_job_ops =
+ xe_pt_job_ops_alloc(vops->pt_update_ops[i].num_ops);
+ if (!vops->pt_update_ops[i].pt_job_ops)
return array_of_binds ? -ENOBUFS : -ENOMEM;
}
@@ -733,7 +732,7 @@ static void xe_vma_ops_fini(struct xe_vma_ops *vops)
xe_vma_svm_prefetch_ops_fini(vops);
for (i = 0; i < XE_MAX_TILES_PER_DEVICE; ++i)
- kfree(vops->pt_update_ops[i].ops);
+ xe_pt_job_ops_put(vops->pt_update_ops[i].pt_job_ops);
}
static void xe_vma_ops_incr_pt_update_ops(struct xe_vma_ops *vops, u8 tile_mask, int inc_val)
@@ -798,16 +797,16 @@ int xe_vm_rebind(struct xe_vm *vm, bool rebind_worker)
struct xe_vma *vma, *next;
struct xe_vma_ops vops;
struct xe_vma_op *op, *next_op;
- int err, i;
+ int err;
lockdep_assert_held(&vm->lock);
if ((xe_vm_in_lr_mode(vm) && !rebind_worker) ||
list_empty(&vm->rebind_list))
return 0;
- xe_vma_ops_init(&vops, vm, NULL, NULL, 0);
- for (i = 0; i < XE_MAX_TILES_PER_DEVICE; ++i)
- vops.pt_update_ops[i].wait_vm_bookkeep = true;
+ xe_vma_ops_init(&vops, vm, xe_cpu_bind_queue(vm->xe->cpu_bind),
+ NULL, 0);
+ vops.flags |= XE_VMA_OPS_FLAG_WAIT_VM_BOOKKEEP;
xe_vm_assert_held(vm);
list_for_each_entry(vma, &vm->rebind_list, combined_links.rebind) {
@@ -847,28 +846,32 @@ free_ops:
return err;
}
+static u8 adjust_rebind_tile_mask(struct xe_vm *vm, u8 tile_mask)
+{
+ if (vm->xe->info.has_pt_mirror)
+ return tile_mask;
+
+ return (0x1 << vm->xe->info.tile_count) - 1;
+}
+
struct dma_fence *xe_vma_rebind(struct xe_vm *vm, struct xe_vma *vma, u8 tile_mask)
{
struct dma_fence *fence = NULL;
struct xe_vma_ops vops;
struct xe_vma_op *op, *next_op;
- struct xe_tile *tile;
- u8 id;
int err;
lockdep_assert_held(&vm->lock);
xe_vm_assert_held(vm);
xe_assert(vm->xe, xe_vm_in_fault_mode(vm));
- xe_vma_ops_init(&vops, vm, NULL, NULL, 0);
- vops.flags |= XE_VMA_OPS_FLAG_SKIP_TLB_WAIT;
- for_each_tile(tile, vm->xe, id) {
- vops.pt_update_ops[id].wait_vm_bookkeep = true;
- vops.pt_update_ops[tile->id].q =
- xe_migrate_exec_queue(tile->migrate);
- }
+ xe_vma_ops_init(&vops, vm, xe_cpu_bind_queue(vm->xe->cpu_bind),
+ NULL, 0);
+ vops.flags |= XE_VMA_OPS_FLAG_SKIP_TLB_WAIT |
+ XE_VMA_OPS_FLAG_WAIT_VM_BOOKKEEP;
- err = xe_vm_ops_add_rebind(&vops, vma, tile_mask);
+ err = xe_vm_ops_add_rebind(&vops, vma,
+ adjust_rebind_tile_mask(vm, tile_mask));
if (err)
return ERR_PTR(err);
@@ -942,8 +945,6 @@ struct dma_fence *xe_vm_range_rebind(struct xe_vm *vm,
struct dma_fence *fence = NULL;
struct xe_vma_ops vops;
struct xe_vma_op *op, *next_op;
- struct xe_tile *tile;
- u8 id;
int err;
lockdep_assert_held(&range->lock);
@@ -952,15 +953,13 @@ struct dma_fence *xe_vm_range_rebind(struct xe_vm *vm,
xe_assert(vm->xe, xe_vm_in_fault_mode(vm));
xe_assert(vm->xe, xe_vma_is_cpu_addr_mirror(vma));
- xe_vma_ops_init(&vops, vm, NULL, NULL, 0);
- vops.flags |= XE_VMA_OPS_FLAG_SKIP_TLB_WAIT;
- for_each_tile(tile, vm->xe, id) {
- vops.pt_update_ops[id].wait_vm_bookkeep = true;
- vops.pt_update_ops[tile->id].q =
- xe_migrate_exec_queue(tile->migrate);
- }
+ xe_vma_ops_init(&vops, vm, xe_cpu_bind_queue(vm->xe->cpu_bind),
+ NULL, 0);
+ vops.flags |= XE_VMA_OPS_FLAG_SKIP_TLB_WAIT |
+ XE_VMA_OPS_FLAG_WAIT_VM_BOOKKEEP;
- err = xe_vm_ops_add_range_rebind(&vops, vma, range, tile_mask);
+ err = xe_vm_ops_add_range_rebind(&vops, vma, range,
+ adjust_rebind_tile_mask(vm, tile_mask));
if (err)
return ERR_PTR(err);
@@ -1025,8 +1024,6 @@ struct dma_fence *xe_vm_range_unbind(struct xe_vm *vm,
struct dma_fence *fence = NULL;
struct xe_vma_ops vops;
struct xe_vma_op *op, *next_op;
- struct xe_tile *tile;
- u8 id;
int err;
lockdep_assert_held(&range->lock);
@@ -1037,12 +1034,9 @@ struct dma_fence *xe_vm_range_unbind(struct xe_vm *vm,
if (!range->tile_present)
return dma_fence_get_stub();
- xe_vma_ops_init(&vops, vm, NULL, NULL, 0);
- for_each_tile(tile, vm->xe, id) {
- vops.pt_update_ops[id].wait_vm_bookkeep = true;
- vops.pt_update_ops[tile->id].q =
- xe_migrate_exec_queue(tile->migrate);
- }
+ xe_vma_ops_init(&vops, vm, xe_cpu_bind_queue(vm->xe->cpu_bind),
+ NULL, 0);
+ vops.flags |= XE_VMA_OPS_FLAG_WAIT_VM_BOOKKEEP;
err = xe_vm_ops_add_range_unbind(&vops, range);
if (err)
@@ -1713,9 +1707,7 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags, struct xe_file *xef)
init_rwsem(&vm->exec_queues.lock);
xe_vm_init_prove_locking(xe, vm);
-
- for_each_tile(tile, xe, id)
- xe_range_fence_tree_init(&vm->rftree[id]);
+ xe_range_fence_tree_init(&vm->rftree);
vm->pt_ops = &xelp_pt_ops;
@@ -1757,7 +1749,8 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags, struct xe_file *xef)
for_each_tile(tile, xe, id) {
if (flags & XE_VM_FLAG_MIGRATION &&
- tile->id != XE_VM_FLAG_TILE_ID(flags))
+ tile->id != XE_VM_FLAG_TILE_ID(flags) &&
+ (vm->xe->info.has_pt_mirror || id))
continue;
vm->pt_root[id] = xe_pt_create(vm, tile, xe->info.vm_max_level,
@@ -1813,7 +1806,7 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags, struct xe_file *xef)
struct xe_exec_queue *q;
u32 create_flags = EXEC_QUEUE_FLAG_VM;
- if (!vm->pt_root[id])
+ if (!vm->pt_root[id] || vm->q)
continue;
if (!xef) /* Not from userspace */
@@ -1824,7 +1817,7 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags, struct xe_file *xef)
err = PTR_ERR(q);
goto err_close;
}
- vm->q[id] = q;
+ vm->q = q;
}
}
@@ -1857,8 +1850,7 @@ err_svm_fini:
xe_svm_fini(vm);
err_no_resv:
mutex_destroy(&vm->snap_mutex);
- for_each_tile(tile, xe, id)
- xe_range_fence_tree_fini(&vm->rftree[id]);
+ xe_range_fence_tree_fini(&vm->rftree);
ttm_lru_bulk_move_fini(&xe->ttm, &vm->lru_bulk_move);
if (vm->xef)
xe_file_put(vm->xef);
@@ -1918,13 +1910,13 @@ void xe_vm_close_and_put(struct xe_vm *vm)
{
LIST_HEAD(contested);
struct xe_device *xe = vm->xe;
- struct xe_tile *tile;
struct xe_vma *vma, *next_vma;
struct drm_gpuva *gpuva, *next;
- u8 id;
xe_assert(xe, !vm->preempt.num_exec_queues);
+ trace_xe_vm_close_and_put(vm);
+
xe_vm_close(vm);
if (xe_vm_in_preempt_fence_mode(vm)) {
mutex_lock(&xe->rebind_resume_lock);
@@ -1935,24 +1927,18 @@ void xe_vm_close_and_put(struct xe_vm *vm)
if (xe_vm_in_fault_mode(vm))
xe_svm_close(vm);
- down_write(&vm->lock);
- for_each_tile(tile, xe, id) {
- if (vm->q[id]) {
- int i;
+ if (vm->q) {
+ int i;
- xe_exec_queue_last_fence_put(vm->q[id], vm);
- for_each_tlb_inval(i)
- xe_exec_queue_tlb_inval_last_fence_put(vm->q[id], vm, i);
- }
- }
- up_write(&vm->lock);
+ down_write(&vm->lock);
+ xe_exec_queue_last_fence_put(vm->q, vm);
+ for_each_tlb_inval(vm->q, i)
+ xe_exec_queue_tlb_inval_last_fence_put(vm->q, vm, i);
+ up_write(&vm->lock);
- for_each_tile(tile, xe, id) {
- if (vm->q[id]) {
- xe_exec_queue_kill(vm->q[id]);
- xe_exec_queue_put(vm->q[id]);
- vm->q[id] = NULL;
- }
+ xe_exec_queue_kill(vm->q);
+ xe_exec_queue_put(vm->q);
+ vm->q = NULL;
}
down_write(&vm->lock);
@@ -1979,21 +1965,13 @@ void xe_vm_close_and_put(struct xe_vm *vm)
vma->gpuva.flags |= XE_VMA_DESTROYED;
}
- /*
- * All vm operations will add shared fences to resv.
- * The only exception is eviction for a shared object,
- * but even so, the unbind when evicted would still
- * install a fence to resv. Hence it's safe to
- * destroy the pagetables immediately.
- */
- xe_vm_free_scratch(vm);
- xe_vm_pt_destroy(vm);
xe_vm_unlock(vm);
/*
- * VM is now dead, cannot re-add nodes to vm->vmas if it's NULL
- * Since we hold a refcount to the bo, we can remove and free
- * the members safely without locking.
+ * Unlink and destroy all contested external-BO VMAs before destroying
+ * the page tables. Otherwise, concurrent eviction holding only bo->resv
+ * can walk the BO's VMAs and attempt to invalidate/zap page tables that
+ * have already been freed.
*/
list_for_each_entry_safe(vma, next_vma, &contested,
combined_links.destroy) {
@@ -2001,6 +1979,11 @@ void xe_vm_close_and_put(struct xe_vm *vm)
xe_vma_destroy_unlocked(vma);
}
+ xe_vm_lock(vm, false);
+ xe_vm_free_scratch(vm);
+ xe_vm_pt_destroy(vm);
+ xe_vm_unlock(vm);
+
xe_svm_fini(vm);
up_write(&vm->lock);
@@ -2012,6 +1995,8 @@ void xe_vm_close_and_put(struct xe_vm *vm)
xe_assert(xe, xe->info.has_asid);
xe_assert(xe, !(vm->flags & XE_VM_FLAG_MIGRATION));
+ trace_xe_vm_asid_release(vm);
+
lookup = xa_erase(&xe->usm.asid_to_vm, vm->usm.asid);
xe_assert(xe, lookup == vm);
}
@@ -2019,8 +2004,7 @@ void xe_vm_close_and_put(struct xe_vm *vm)
xe_vm_clear_fault_entries(vm);
- for_each_tile(tile, xe, id)
- xe_range_fence_tree_fini(&vm->rftree[id]);
+ xe_range_fence_tree_fini(&vm->rftree);
xe_vm_put(vm);
}
@@ -2080,13 +2064,13 @@ struct xe_vm *xe_vm_lookup(struct xe_file *xef, u32 id)
u64 xe_vm_pdp4_descriptor(struct xe_vm *vm, struct xe_tile *tile)
{
- return vm->pt_ops->pde_encode_bo(vm->pt_root[tile->id]->bo, 0);
+ return vm->pt_ops->pde_encode_bo(xe_vm_pt_root(vm, tile->id)->bo, 0);
}
static struct xe_exec_queue *
to_wait_exec_queue(struct xe_vm *vm, struct xe_exec_queue *q)
{
- return q ? q : vm->q[0];
+ return q ? q : vm->q;
}
static struct xe_user_fence *
@@ -2548,8 +2532,10 @@ vm_bind_ioctl_ops_create(struct xe_vm *vm, struct xe_vma_ops *vops,
ctx.devmem_possible = IS_DGFX(vm->xe) &&
IS_ENABLED(CONFIG_DRM_XE_PAGEMAP);
- for_each_tile(tile, vm->xe, id)
+ for_each_tile(tile, vm->xe, id) {
+ xe_migrate_ulls_enter(tile->migrate);
tile_mask |= 0x1 << id;
+ }
if (prefetch_region == DRM_XE_CONSULT_MEM_ADVISE_PREF_LOC) {
dpagemap = xe_vma_resolve_pagemap(vma,
@@ -2584,6 +2570,11 @@ alloc_next_range:
dpagemap, &valid_pages)) {
xe_svm_range_debug(svm_range, "PREFETCH - RANGE IS VALID");
xe_assert(vm->xe, valid_pages);
+
+ if (dpagemap)
+ xe_svm_range_prefetch_lru_bump(vm, vma, svm_range,
+ dpagemap);
+
need_put = true;
goto check_next_range;
}
@@ -3260,6 +3251,7 @@ static int prefetch_ranges(struct xe_vm *vm, struct xe_vma_ops *vops,
ctx.devmem_possible = devmem_possible;
ctx.check_pages_threshold = devmem_possible ? SZ_64K : 0;
ctx.device_private_page_owner = xe_svm_private_page_owner(vm, !dpagemap);
+ ctx.devmem_fn = xe_svm_devmem_lru_bump;
skip_threads = op->prefetch_range.ranges_count == 1 ||
(!dpagemap && !(vops->flags &
@@ -3515,28 +3507,18 @@ static void trace_xe_vm_ops_execute(struct xe_vma_ops *vops)
op_trace(op);
}
-static int vm_ops_setup_tile_args(struct xe_vm *vm, struct xe_vma_ops *vops)
+static int vm_ops_setup(struct xe_vm *vm, struct xe_vma_ops *vops)
{
- struct xe_exec_queue *q = vops->q;
struct xe_tile *tile;
int number_tiles = 0;
u8 id;
- for_each_tile(tile, vm->xe, id) {
+ for_each_tile(tile, vm->xe, id)
if (vops->pt_update_ops[id].num_ops)
++number_tiles;
- if (vops->pt_update_ops[id].q)
- continue;
-
- if (q) {
- vops->pt_update_ops[id].q = q;
- if (vm->pt_root[id] && !list_empty(&q->multi_gt_list))
- q = list_next_entry(q, multi_gt_list);
- } else {
- vops->pt_update_ops[id].q = vm->q[id];
- }
- }
+ if (!vops->q)
+ vops->q = vm->q;
return number_tiles;
}
@@ -3544,26 +3526,21 @@ static int vm_ops_setup_tile_args(struct xe_vm *vm, struct xe_vma_ops *vops)
static struct dma_fence *ops_execute(struct xe_vm *vm,
struct xe_vma_ops *vops)
{
- struct xe_tile *tile;
+ struct xe_device *xe = vm->xe;
struct dma_fence *fence = NULL;
struct dma_fence **fences = NULL;
struct dma_fence_array *cf = NULL;
- int number_tiles = 0, current_fence = 0, n_fence = 0, err, i;
- u8 id;
+ int current_fence = 0, n_fence = 1, err, i;
- number_tiles = vm_ops_setup_tile_args(vm, vops);
- if (number_tiles == 0)
+ if (!vm_ops_setup(vm, vops))
return ERR_PTR(-ENODATA);
- for_each_tile(tile, vm->xe, id) {
- ++n_fence;
-
- if (!(vops->flags & XE_VMA_OPS_FLAG_SKIP_TLB_WAIT))
- for_each_tlb_inval(i)
- ++n_fence;
+ if (!(vops->flags & XE_VMA_OPS_FLAG_SKIP_TLB_WAIT)) {
+ for_each_tlb_inval(vops->q, i)
+ ++n_fence;
}
- fences = kmalloc_objs(*fences, n_fence);
+ fences = kcalloc(n_fence, sizeof(*fences), GFP_KERNEL);
if (!fences) {
fence = ERR_PTR(-ENOMEM);
goto err_trace;
@@ -3575,65 +3552,39 @@ static struct dma_fence *ops_execute(struct xe_vm *vm,
goto err_out;
}
- for_each_tile(tile, vm->xe, id) {
- if (!vops->pt_update_ops[id].num_ops)
- continue;
-
- err = xe_pt_update_ops_prepare(tile, vops);
- if (err) {
- fence = ERR_PTR(err);
- goto err_out;
- }
+ err = xe_pt_update_ops_prepare(xe, vops);
+ if (err) {
+ fence = ERR_PTR(err);
+ goto err_out;
}
trace_xe_vm_ops_execute(vops);
- for_each_tile(tile, vm->xe, id) {
- struct xe_exec_queue *q = vops->pt_update_ops[tile->id].q;
-
- fence = NULL;
- if (!vops->pt_update_ops[id].num_ops)
- goto collect_fences;
-
- fence = xe_pt_update_ops_run(tile, vops);
- if (IS_ERR(fence))
- goto err_out;
-
-collect_fences:
- fences[current_fence++] = fence ?: dma_fence_get_stub();
- if (vops->flags & XE_VMA_OPS_FLAG_SKIP_TLB_WAIT)
- continue;
+ fence = xe_pt_update_ops_run(xe, vops);
+ if (IS_ERR(fence))
+ goto err_out;
+ fences[current_fence++] = fence;
- xe_migrate_job_lock(tile->migrate, q);
- for_each_tlb_inval(i)
+ if (!(vops->flags & XE_VMA_OPS_FLAG_SKIP_TLB_WAIT)) {
+ xe_cpu_bind_job_lock(xe->cpu_bind, vops->q);
+ for_each_tlb_inval(vops->q, i)
fences[current_fence++] =
- xe_exec_queue_tlb_inval_last_fence_get(q, vm, i);
- xe_migrate_job_unlock(tile->migrate, q);
+ xe_exec_queue_tlb_inval_last_fence_get(vops->q,
+ vm, i);
+ xe_cpu_bind_job_unlock(xe->cpu_bind, vops->q);
}
- xe_assert(vm->xe, current_fence == n_fence);
+ xe_assert(xe, current_fence == n_fence);
dma_fence_array_init(cf, n_fence, fences, dma_fence_context_alloc(1),
1);
fence = &cf->base;
- for_each_tile(tile, vm->xe, id) {
- if (!vops->pt_update_ops[id].num_ops)
- continue;
-
- xe_pt_update_ops_fini(tile, vops);
- }
+ xe_pt_update_ops_fini(xe, vops);
return fence;
err_out:
- for_each_tile(tile, vm->xe, id) {
- if (!vops->pt_update_ops[id].num_ops)
- continue;
-
- xe_pt_update_ops_abort(tile, vops);
- }
- while (current_fence)
- dma_fence_put(fences[--current_fence]);
+ xe_pt_update_ops_abort(xe, vops);
kfree(fences);
kfree(cf);
@@ -3943,6 +3894,8 @@ static void xe_vma_ops_init(struct xe_vma_ops *vops, struct xe_vm *vm,
vops->syncs = syncs;
vops->num_syncs = num_syncs;
vops->flags = 0;
+ vops->start = ~0x0ull;
+ vops->last = 0x0ull;
}
static int xe_vm_bind_ioctl_validate_bo(struct xe_device *xe, struct xe_bo *bo,
@@ -4137,7 +4090,7 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
syncs_user = u64_to_user_ptr(args->syncs);
for (num_syncs = 0; num_syncs < args->num_syncs; num_syncs++) {
- struct xe_exec_queue *__q = q ?: vm->q[0];
+ struct xe_exec_queue *__q = q ?: vm->q;
err = xe_sync_entry_parse(xe, xef, &syncs[num_syncs],
&syncs_user[num_syncs],
@@ -5142,3 +5095,21 @@ void xe_vm_remove_exec_queue(struct xe_vm *vm, struct xe_exec_queue *q)
}
up_write(&vm->exec_queues.lock);
}
+
+/**
+ * xe_vm_pt_root() - Retrieve VM page-table root
+ * @vm: The VM.
+ * @tile_id: Tile ID
+ *
+ * Retrieve VM page-table root for a tile ID, used to abstract if PT mirroring is
+ * enabled across tiles.
+ *
+ * Return: VM page-table root for a tile ID
+ */
+struct xe_pt *xe_vm_pt_root(struct xe_vm *vm, u8 tile_id)
+{
+ if (vm->xe->info.has_pt_mirror)
+ return vm->pt_root[tile_id];
+
+ return vm->pt_root[0];
+}
diff --git a/drivers/gpu/drm/xe/xe_vm.h b/drivers/gpu/drm/xe/xe_vm.h
index c5b900f38ded..89c4f95984a2 100644
--- a/drivers/gpu/drm/xe/xe_vm.h
+++ b/drivers/gpu/drm/xe/xe_vm.h
@@ -436,4 +436,7 @@ static inline struct drm_exec *xe_vm_validation_exec(struct xe_vm *vm)
((READ_ONCE(tile_present) & ~READ_ONCE(tile_invalidated)) & BIT((tile)->id))
void xe_vma_mem_attr_copy(struct xe_vma_mem_attr *to, struct xe_vma_mem_attr *from);
+
+struct xe_pt *xe_vm_pt_root(struct xe_vm *vm, u8 tile_id);
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_vm_madvise.c b/drivers/gpu/drm/xe/xe_vm_madvise.c
index 0474768a38aa..5c7aefc641ee 100644
--- a/drivers/gpu/drm/xe/xe_vm_madvise.c
+++ b/drivers/gpu/drm/xe/xe_vm_madvise.c
@@ -713,12 +713,6 @@ int xe_vm_madvise_ioctl(struct drm_device *dev, void *data, struct drm_file *fil
attr_type = array_index_nospec(args->type, ARRAY_SIZE(madvise_funcs));
- /* Ensure the madvise function exists for this type */
- if (!madvise_funcs[attr_type]) {
- err = -EINVAL;
- goto err_fini;
- }
-
madvise_funcs[attr_type](xe, vm, madvise_range.vmas, madvise_range.num_vmas, args,
&details);
diff --git a/drivers/gpu/drm/xe/xe_vm_types.h b/drivers/gpu/drm/xe/xe_vm_types.h
index 648031e64145..fb9305124679 100644
--- a/drivers/gpu/drm/xe/xe_vm_types.h
+++ b/drivers/gpu/drm/xe/xe_vm_types.h
@@ -262,7 +262,7 @@ struct xe_vm {
struct xe_device *xe;
/* exec queue used for (un)binding vma's */
- struct xe_exec_queue *q[XE_MAX_TILES_PER_DEVICE];
+ struct xe_exec_queue *q;
/** @lru_bulk_move: Bulk LRU move list for this VM's BOs */
struct ttm_lru_bulk_move lru_bulk_move;
@@ -317,7 +317,7 @@ struct xe_vm {
* @rftree: range fence tree to track updates to page table structure.
* Used to implement conflict tracking between independent bind engines.
*/
- struct xe_range_fence_tree rftree[XE_MAX_TILES_PER_DEVICE];
+ struct xe_range_fence_tree rftree;
const struct xe_pt_ops *pt_ops;
@@ -557,6 +557,10 @@ struct xe_vma_ops {
u32 num_syncs;
/** @pt_update_ops: page table update operations */
struct xe_vm_pgtable_update_ops pt_update_ops[XE_MAX_TILES_PER_DEVICE];
+ /** @start: start address of ops */
+ u64 start;
+ /** @last: last address of ops */
+ u64 last;
/** @flag: signify the properties within xe_vma_ops*/
#define XE_VMA_OPS_FLAG_HAS_SVM_PREFETCH BIT(0)
#define XE_VMA_OPS_FLAG_MADVISE BIT(1)
@@ -566,6 +570,10 @@ struct xe_vma_ops {
#define XE_VMA_OPS_FLAG_MODIFIES_GPUVA BIT(5)
#define XE_VMA_OPS_FLAG_DOWNGRADE_LOCK BIT(6)
#define XE_VMA_OPS_FLAG_HAS_SVM_VALID_RANGE BIT(7)
+#define XE_VMA_OPS_FLAG_WAIT_VM_BOOKKEEP BIT(8)
+#define XE_VMA_OPS_FLAG_WAIT_VM_KERNEL BIT(9)
+#define XE_VMA_OPS_FLAG_NEEDS_INVALIDATION BIT(10)
+#define XE_VMA_OPS_FLAG_NEEDS_SVM_LOCK BIT(11)
u32 flags;
#ifdef TEST_VM_OPS_ERROR
/** @inject_error: inject error to test error handling */
diff --git a/drivers/gpu/drm/xe/xe_wa_oob.rules b/drivers/gpu/drm/xe/xe_wa_oob.rules
index dd69ad07f7a9..f35181fc4e49 100644
--- a/drivers/gpu/drm/xe/xe_wa_oob.rules
+++ b/drivers/gpu/drm/xe/xe_wa_oob.rules
@@ -14,6 +14,7 @@
16017236439 PLATFORM(PVC)
14019821291 MEDIA_VERSION_RANGE(1300, 2000)
14015076503 MEDIA_VERSION(1300)
+22016122933 MEDIA_VERSION(1300)
14018913170 GRAPHICS_VERSION_RANGE(1270, 1274)
MEDIA_VERSION(1300)
PLATFORM(DG2)
@@ -72,3 +73,6 @@
16029897822 MEDIA_VERSION(3500)
GRAPHICS_VERSION(3510)
14027054324 GRAPHICS_VERSION(3511)
+14025941587 GRAPHICS_VERSION_RANGE(2001, 3511), FUNC(xe_rtp_match_not_sriov_vf)
+ MEDIA_VERSION_RANGE(1301, 3503), FUNC(xe_rtp_match_not_sriov_vf)
+16030224309 GRAPHICS_VERSION(3510)
diff --git a/include/drm/drm_gpusvm.h b/include/drm/drm_gpusvm.h
index 9e35584812fd..4d31c6bba745 100644
--- a/include/drm/drm_gpusvm.h
+++ b/include/drm/drm_gpusvm.h
@@ -274,6 +274,11 @@ struct drm_gpusvm {
* pages as valid; the caller revalidates the snapshot itself, see
* drm_gpusvm_get_pages(). @devmem_only is rejected and no page
* type check is performed, so @allow_mixed has no effect.
+ * @devmem_fn: Optional callback invoked under the notifier lock, once per
+ * contiguous run of pages backed by the same &drm_pagemap_devmem.
+ * Must not sleep and must be idempotent, as a non-contiguous
+ * allocation is reported more than once. Fires even with
+ * @no_dma_map. May be NULL.
*
* Context that is DRM GPUSVM is operating in (i.e. user arguments).
*/
@@ -281,6 +286,7 @@ struct drm_gpusvm_ctx {
void *device_private_page_owner;
unsigned long check_pages_threshold;
unsigned long timeslice_ms;
+ void (*devmem_fn)(struct drm_pagemap_devmem *devmem);
unsigned int in_notifier :1;
unsigned int read_only :1;
unsigned int devmem_possible :1;
diff --git a/include/drm/drm_pagemap.h b/include/drm/drm_pagemap.h
index ebbd3b0ddf36..d12a509d6baf 100644
--- a/include/drm/drm_pagemap.h
+++ b/include/drm/drm_pagemap.h
@@ -258,6 +258,8 @@ struct drm_pagemap *drm_pagemap_create(struct drm_device *drm,
struct drm_pagemap *drm_pagemap_page_to_dpagemap(struct page *page);
+struct drm_pagemap_devmem *drm_pagemap_page_to_devmem(struct page *page);
+
void drm_pagemap_put(struct drm_pagemap *dpagemap);
#else
@@ -267,6 +269,11 @@ static inline struct drm_pagemap *drm_pagemap_page_to_dpagemap(struct page *page
return NULL;
}
+static inline struct drm_pagemap_devmem *drm_pagemap_page_to_devmem(struct page *page)
+{
+ return NULL;
+}
+
static inline void drm_pagemap_put(struct drm_pagemap *dpagemap)
{
}
diff --git a/include/drm/intel/pciids.h b/include/drm/intel/pciids.h
index dff389b56eb3..32c88a690ad8 100644
--- a/include/drm/intel/pciids.h
+++ b/include/drm/intel/pciids.h
@@ -880,6 +880,7 @@
MACRO__(0xB08F, ## __VA_ARGS__), \
MACRO__(0xB090, ## __VA_ARGS__), \
MACRO__(0xB0A0, ## __VA_ARGS__), \
+ MACRO__(0xB0A1, ## __VA_ARGS__), \
MACRO__(0xB0B0, ## __VA_ARGS__)
/* WCL */