* [RFC PATCH] drm/xe/ggtt: Cut GGTT initialization from 900 ms to 37 ms @ 2026-10-28 4:50 Daniel Colascione 2026-10-28 13:23 ` sashiko-bot 2026-10-29 15:16 ` Matthew Brost 0 siblings, 2 replies; 4+ messages in thread From: Daniel Colascione @ 2026-10-28 4:50 UTC (permalink / raw) To: intel-xe; +Cc: Matthew Brost, Thomas Hellstrom, Rodrigo Vivi, dri-devel We initialize the GGTT GPU page tables on startup. On my LNL, doing this one PTE at a time through the BAR takes about 900 ms and involves a million small writes. Use the GPU's copy engine to fill the PTEs in batches. We build MI_UPDATE_GTT commands in normal memory, then send them to the GPU for execution. This way, we avoid the per-PTE MMIO write and do the PTE fill in 36.77 ms, ~24x faster. The new path is enabled by default. xe.ggtt_init_gpu=0 uses the old CPU-driven path. LNL-only because I can test only LNL. Might it work elsewhere? Signed-off-by: Daniel Colascione <dancol@dancol.org> --- drivers/gpu/drm/xe/instructions/xe_mi_commands.h | 2 + drivers/gpu/drm/xe/xe_ggtt.c | 128 +++++++++++++++++++++-- drivers/gpu/drm/xe/xe_ggtt.h | 1 + drivers/gpu/drm/xe/xe_gt.c | 3 +- drivers/gpu/drm/xe/xe_module.c | 5 + drivers/gpu/drm/xe/xe_module.h | 1 + 6 files changed, 130 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/xe/instructions/xe_mi_commands.h b/drivers/gpu/drm/xe/instructions/xe_mi_commands.h index 835ef75bbeb1..04bd4225adef 100644 --- a/drivers/gpu/drm/xe/instructions/xe_mi_commands.h +++ b/drivers/gpu/drm/xe/instructions/xe_mi_commands.h @@ -62,6 +62,8 @@ #define MI_LRI_FORCE_POSTED REG_BIT(12) #define MI_LRI_LEN(x) (((x) & 0xff) + 1) +#define MI_UPDATE_GTT __MI_INSTR(0x23) + #define MI_STORE_REGISTER_MEM (__MI_INSTR(0x24) | XE_INSTR_NUM_DW(4)) #define MI_SRM_USE_GGTT REG_BIT(22) #define MI_SRM_ADD_CS_OFFSET REG_BIT(19) diff --git a/drivers/gpu/drm/xe/xe_ggtt.c b/drivers/gpu/drm/xe/xe_ggtt.c index 773777afc68a..3737d085ae68 100644 --- a/drivers/gpu/drm/xe/xe_ggtt.c +++ b/drivers/gpu/drm/xe/xe_ggtt.c @@ -15,20 +15,25 @@ #include <drm/intel/i915_drm.h> #include <generated/xe_wa_oob.h> +#include "instructions/xe_mi_commands.h" #include "regs/xe_gt_regs.h" #include "regs/xe_gtt_defs.h" #include "regs/xe_regs.h" #include "xe_assert.h" +#include "xe_bb.h" #include "xe_bo.h" +#include "xe_exec_queue.h" #include "xe_gt.h" #include "xe_gt_printk.h" #include "xe_gt_types.h" #include "xe_log.h" #include "xe_map.h" #include "xe_mmio.h" +#include "xe_module.h" #include "xe_pat.h" #include "xe_pm.h" #include "xe_res_cursor.h" +#include "xe_sched_job.h" #include "xe_sriov.h" #include "xe_tile_printk.h" #include "xe_tile_sriov_vf.h" @@ -384,9 +389,9 @@ static void dev_fini_ggtt(void *arg) * @ggtt: the &xe_ggtt to be initialized * * It allows to create new mappings usable by the GuC. - * Mappings are not usable by the HW engines, as it doesn't have scratch nor - * initial clear done to it yet. That will happen in the regular, non-early - * GGTT initialization. + * HW engines may only access explicitly mapped bootstrap buffers until the + * remaining holes have been cleared. Normally xe_ggtt_init() does this; + * the GPU clear finishes this work in xe_ggtt_init_late(). * * Return: 0 on success or a negative error code on failure. */ @@ -456,18 +461,119 @@ ALLOW_ERROR_INJECTION(xe_ggtt_init_early, ERRNO); /* See xe_pci_probe() */ static void xe_ggtt_invalidate(struct xe_ggtt *ggtt); static void xe_ggtt_invalidate_engine(struct xe_ggtt *ggtt); -static void xe_ggtt_initial_clear(struct xe_ggtt *ggtt) +static bool ggtt_use_gpu(struct xe_device *xe) { + return xe_modparam.ggtt_init_gpu && xe->info.platform == XE_LUNARLAKE; +} + +/* + * MI_UPDATE_GTT takes at most 511 inline PTEs (ACM PRM, Vol. 2a, + * pp. 1096-1097). Limit batches to 512K to fit the kernel batch pool. + */ +#define GGTT_INIT_PTES_PER_PACKET 511 +#define GGTT_INIT_BATCH_DWORDS (128 * (2 + 2 * GGTT_INIT_PTES_PER_PACKET)) + +static int ggtt_clear_gpu(struct xe_exec_queue *q, struct xe_bb *bb, + u64 pte, u64 start, u64 end) +{ + while (start < end) { + struct xe_sched_job *job; + int err; + + bb->len = 0; + while (start < end && bb->len < GGTT_INIT_BATCH_DWORDS) { + u32 count = min_t(u64, (end - start) >> XE_PTE_SHIFT, + GGTT_INIT_PTES_PER_PACKET); + + bb->cs[bb->len++] = MI_UPDATE_GTT | (2 * count); + bb->cs[bb->len++] = start; + memset64((u64 *)&bb->cs[bb->len], pte, count); + bb->len += 2 * count; + start += (u64)count << XE_PTE_SHIFT; + } + + job = xe_bb_create_job(q, bb); + if (IS_ERR(job)) + return PTR_ERR(job); + + xe_sched_job_arm(job); + xe_sched_job_get(job); + xe_sched_job_push(job); + + dma_fence_wait(&job->drm.s_fence->finished, false); + err = dma_fence_get_status(&job->drm.s_fence->finished); + xe_sched_job_put(job); + if (err < 0) + return err; + } + + return 0; +} + +static int xe_ggtt_initial_clear(struct xe_ggtt *ggtt, struct xe_hw_engine *hwe) +{ + struct xe_device *xe = tile_to_xe(ggtt->tile); + struct xe_exec_queue *q = NULL; + struct xe_bb *bb = NULL; struct drm_mm_node *hole; - u64 start, end; + u16 pat_index = xe_cache_pat_idx(xe, XE_CACHE_WB); + u64 start, end, pte = 0; + int err = 0; + + if (hwe) { + q = xe_exec_queue_create(xe, NULL, BIT(hwe->logical_instance), 1, + hwe, EXEC_QUEUE_FLAG_KERNEL, 0); + if (IS_ERR(q)) + return PTR_ERR(q); + + bb = xe_bb_new(hwe->gt, GGTT_INIT_BATCH_DWORDS, false); + if (IS_ERR(bb)) { + err = PTR_ERR(bb); + goto put_queue; + } + } - /* Display may have allocated inside ggtt, so be careful with clearing here */ + if (ggtt->scratch) + pte = xe_bo_addr(ggtt->scratch, 0, XE_PAGE_SIZE) | + ggtt->pt_ops->pte_encode_flags(ggtt->scratch, pat_index); + + /* Preserve boot mappings and exclude allocator changes until completion. */ mutex_lock(&ggtt->lock); - drm_mm_for_each_hole(hole, &ggtt->mm, start, end) - xe_ggtt_clear(ggtt, ggtt->start + start, end - start); + drm_mm_for_each_hole(hole, &ggtt->mm, start, end) { + if (q) + err = ggtt_clear_gpu(q, bb, pte, ggtt->start + start, ggtt->start + end); + else + xe_ggtt_clear(ggtt, ggtt->start + start, end - start); + if (err) + goto unlock; + } xe_ggtt_invalidate(ggtt); +unlock: mutex_unlock(&ggtt->lock); + xe_bb_free(bb, NULL); +put_queue: + if (q) + xe_exec_queue_put(q); + return err; +} + +/** + * xe_ggtt_init_late - Finish deferred GGTT initialization after GuC startup + * @ggtt: GGTT to initialize + * + * All default contexts must be recorded before waiting for a GPU clear with + * the GGTT mutex held, so reset recovery need not allocate context mappings. + * + * Return: 0 on success or a negative error code on failure. + */ +int xe_ggtt_init_late(struct xe_ggtt *ggtt) +{ + if (!ggtt_use_gpu(tile_to_xe(ggtt->tile))) + return 0; + + return xe_ggtt_initial_clear(ggtt, xe_gt_hw_engine(ggtt->tile->primary_gt, + XE_ENGINE_CLASS_COPY, 0, false)); } static void ggtt_node_fini(struct xe_ggtt_node *node) @@ -536,6 +642,9 @@ void xe_ggtt_node_remove(struct xe_ggtt_node *node, bool invalidate) * xe_ggtt_init - Regular non-early GGTT initialization * @ggtt: the &xe_ggtt to be initialized * + * Allocate the scratch page if needed and clear unused entries, or defer + * the clear to xe_ggtt_init_late() when GPU initialization is enabled. + * * Return: 0 on success or a negative error code on failure. */ int xe_ggtt_init(struct xe_ggtt *ggtt) @@ -572,7 +681,8 @@ int xe_ggtt_init(struct xe_ggtt *ggtt) xe_bo_size(ggtt->scratch)); } - xe_ggtt_initial_clear(ggtt); + if (!ggtt_use_gpu(xe)) + xe_ggtt_initial_clear(ggtt, NULL); return devm_add_action_or_reset(xe->drm.dev, ggtt_fini, ggtt); err: diff --git a/drivers/gpu/drm/xe/xe_ggtt.h b/drivers/gpu/drm/xe/xe_ggtt.h index c864cc975a69..cae8600d7142 100644 --- a/drivers/gpu/drm/xe/xe_ggtt.h +++ b/drivers/gpu/drm/xe/xe_ggtt.h @@ -17,6 +17,7 @@ struct xe_ggtt *xe_ggtt_alloc(struct xe_tile *tile); int xe_ggtt_init_early(struct xe_ggtt *ggtt); int xe_ggtt_init_kunit(struct xe_ggtt *ggtt, u32 reserved, u32 size); int xe_ggtt_init(struct xe_ggtt *ggtt); +int xe_ggtt_init_late(struct xe_ggtt *ggtt); void xe_ggtt_shift_nodes(struct xe_ggtt *ggtt, u64 new_base); u64 xe_ggtt_start(struct xe_ggtt *ggtt); diff --git a/drivers/gpu/drm/xe/xe_gt.c b/drivers/gpu/drm/xe/xe_gt.c index 45fb074812a2..d1c14a02de66 100644 --- a/drivers/gpu/drm/xe/xe_gt.c +++ b/drivers/gpu/drm/xe/xe_gt.c @@ -686,7 +686,8 @@ static int gt_init_with_all_forcewake(struct xe_gt *gt) if (err) return err; - return 0; + return xe_gt_is_main_type(gt) ? + xe_ggtt_init_late(gt_to_tile(gt)->mem.ggtt) : 0; } static void xe_gt_fini(void *arg) diff --git a/drivers/gpu/drm/xe/xe_module.c b/drivers/gpu/drm/xe/xe_module.c index 897724cb5cfb..44cc0107cf53 100644 --- a/drivers/gpu/drm/xe/xe_module.c +++ b/drivers/gpu/drm/xe/xe_module.c @@ -22,6 +22,7 @@ #include "xe_sched_job.h" struct xe_modparam xe_modparam = { + .ggtt_init_gpu = true, .probe_display = XE_DEFAULT_PROBE_DISPLAY, .guc_log_level = XE_DEFAULT_GUC_LOG_LEVEL, .force_probe = XE_DEFAULT_FORCE_PROBE, @@ -34,6 +35,10 @@ struct xe_modparam xe_modparam = { /* the rest are 0 by default */ }; +module_param_named(ggtt_init_gpu, xe_modparam.ggtt_init_gpu, bool, 0444); +MODULE_PARM_DESC(ggtt_init_gpu, + "GPU GGTT initialization on Lunar Lake (default: true)"); + module_param_named(svm_notifier_size, xe_modparam.svm_notifier_size, uint, 0600); MODULE_PARM_DESC(svm_notifier_size, "Set the svm notifier size in MiB, must be power of 2 " "[default=" __stringify(XE_DEFAULT_SVM_NOTIFIER_SIZE) "]"); diff --git a/drivers/gpu/drm/xe/xe_module.h b/drivers/gpu/drm/xe/xe_module.h index 6272d9e41207..8aa91822c35c 100644 --- a/drivers/gpu/drm/xe/xe_module.h +++ b/drivers/gpu/drm/xe/xe_module.h @@ -12,6 +12,7 @@ struct work_struct; /* Module modprobe variables */ struct xe_modparam { + bool ggtt_init_gpu; bool probe_display; int force_vram_bar_size; int guc_log_level; ^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [RFC PATCH] drm/xe/ggtt: Cut GGTT initialization from 900 ms to 37 ms 2026-10-28 4:50 [RFC PATCH] drm/xe/ggtt: Cut GGTT initialization from 900 ms to 37 ms Daniel Colascione @ 2026-10-28 13:23 ` sashiko-bot 2026-10-29 15:16 ` Matthew Brost 1 sibling, 0 replies; 4+ messages in thread From: sashiko-bot @ 2026-10-28 13:23 UTC (permalink / raw) To: Daniel Colascione, intel-xe; +Cc: dri-devel Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [High] drm/xe/ggtt: fs_reclaim lock inversion in ggtt_clear_gpu() under ggtt->lock - [Low] drm/xe: missing kernel-parameters.txt entry for xe.ggtt_init_gpu -- commit ed4c9843bc7f8d6478d283f57c556b54c7d8a262 Author: Daniel Colascione <dancol@dancol.org> drm/xe/ggtt: Cut GGTT initialization from 900 ms to 37 ms This patch uses the GPU copy engine to initialize GGTT page tables in batches via MI_UPDATE_GTT commands on Lunar Lake, deferring the initial GGTT hole clear to xe_ggtt_init_late() after GuC startup. It also adds the xe.ggtt_init_gpu module parameter to toggle this behavior. > diff --git a/drivers/gpu/drm/xe/xe_ggtt.c b/drivers/gpu/drm/xe/xe_ggtt.c > index 9ef2ce743770f..47d12e5e95458 100644 > --- a/drivers/gpu/drm/xe/xe_ggtt.c > +++ b/drivers/gpu/drm/xe/xe_ggtt.c [ ... ] > @@ -456,18 +461,119 @@ ALLOW_ERROR_INJECTION(xe_ggtt_init_early, ERRNO); /* See xe_pci_probe() */ [ ... ] > +static int ggtt_clear_gpu(struct xe_exec_queue *q, struct xe_bb *bb, > + u64 pte, u64 start, u64 end) > +{ > + while (start < end) { > + struct xe_sched_job *job; > + int err; > + > + bb->len = 0; > + while (start < end && bb->len < GGTT_INIT_BATCH_DWORDS) { > + u32 count = min_t(u64, (end - start) >> XE_PTE_SHIFT, > + GGTT_INIT_PTES_PER_PACKET); > + > + bb->cs[bb->len++] = MI_UPDATE_GTT | (2 * count); > + bb->cs[bb->len++] = start; > + memset64((u64 *)&bb->cs[bb->len], pte, count); > + bb->len += 2 * count; > + start += (u64)count << XE_PTE_SHIFT; > + } > + > + job = xe_bb_create_job(q, bb); > + if (IS_ERR(job)) > + return PTR_ERR(job); [ ... ] > +static int xe_ggtt_initial_clear(struct xe_ggtt *ggtt, struct xe_hw_engine *hwe) > +{ [ ... ] > + /* Preserve boot mappings and exclude allocator changes until completion. */ > mutex_lock(&ggtt->lock); > - drm_mm_for_each_hole(hole, &ggtt->mm, start, end) > - xe_ggtt_clear(ggtt, ggtt->start + start, end - start); > + drm_mm_for_each_hole(hole, &ggtt->mm, start, end) { > + if (q) > + err = ggtt_clear_gpu(q, bb, pte, ggtt->start + start, ggtt->start + end); > + else > + xe_ggtt_clear(ggtt, ggtt->start + start, end - start); > + if (err) > + goto unlock; > + } [Severity: High] Does calling ggtt_clear_gpu() while holding ggtt->lock cause an fs_reclaim lock inversion? During allocation in xe_ggtt_alloc(), primelockdep() in drivers/gpu/drm/xe/xe_ggtt.c establishes the ordering fs_reclaim -> ggtt->lock because BO destruction during memory reclaim acquires ggtt->lock: static void primelockdep(struct xe_ggtt *ggtt) { if (!IS_ENABLED(CONFIG_LOCKDEP)) return; fs_reclaim_acquire(GFP_KERNEL); might_lock(&ggtt->lock); fs_reclaim_release(GFP_KERNEL); } However, on Lunar Lake with xe.ggtt_init_gpu enabled, gt_init_with_all_forcewake() -> xe_ggtt_init_late() -> xe_ggtt_initial_clear() acquires ggtt->lock and calls ggtt_clear_gpu(), which invokes xe_bb_create_job() -> __xe_bb_create_job() -> xe_sched_job_create(). Inside xe_sched_job_create(), several GFP_KERNEL allocations occur under ggtt->lock, including job_alloc() in drivers/gpu/drm/xe/xe_sched_job.c: static struct xe_sched_job *job_alloc(bool parallel) { return kmem_cache_zalloc(parallel ? xe_sched_job_parallel_slab : xe_sched_job_slab, GFP_KERNEL); } as well as drm_sched_job_init() and xe_lrc_alloc_seqno_fence() -> xe_hw_fence_alloc() -> fence_alloc() in drivers/gpu/drm/xe/xe_hw_fence.c: return kmem_cache_zalloc(xe_hw_fence_slab, GFP_KERNEL); Wouldn't this violate the fs_reclaim -> ggtt->lock hierarchy, triggering a lockdep circular locking warning under CONFIG_PROVE_LOCKING during probe and risking a reclaim deadlock if direct reclaim attempts to free a GGTT-mapped BO? > diff --git a/drivers/gpu/drm/xe/xe_module.c b/drivers/gpu/drm/xe/xe_module.c > index 897724cb5cfb8..44cc0107cf53a 100644 > --- a/drivers/gpu/drm/xe/xe_module.c > +++ b/drivers/gpu/drm/xe/xe_module.c > @@ -22,6 +22,7 @@ > #include "xe_sched_job.h" > > struct xe_modparam xe_modparam = { > + .ggtt_init_gpu = true, > .probe_display = XE_DEFAULT_PROBE_DISPLAY, > .guc_log_level = XE_DEFAULT_GUC_LOG_LEVEL, > .force_probe = XE_DEFAULT_FORCE_PROBE, > @@ -34,6 +35,10 @@ struct xe_modparam xe_modparam = { > /* the rest are 0 by default */ > }; > > +module_param_named(ggtt_init_gpu, xe_modparam.ggtt_init_gpu, bool, 0444); > +MODULE_PARM_DESC(ggtt_init_gpu, > + "GPU GGTT initialization on Lunar Lake (default: true)"); [Severity: Low] Should this new xe.ggtt_init_gpu module parameter also be documented in Documentation/admin-guide/kernel-parameters.txt? -- Sashiko AI review · https://sashiko.dev/#/patchset/87v76m1l9k.fsf@dancol.org?part=1 ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [RFC PATCH] drm/xe/ggtt: Cut GGTT initialization from 900 ms to 37 ms 2026-10-28 4:50 [RFC PATCH] drm/xe/ggtt: Cut GGTT initialization from 900 ms to 37 ms Daniel Colascione 2026-10-28 13:23 ` sashiko-bot @ 2026-10-29 15:16 ` Matthew Brost 2026-10-29 15:31 ` Matthew Brost 1 sibling, 1 reply; 4+ messages in thread From: Matthew Brost @ 2026-10-29 15:16 UTC (permalink / raw) To: Daniel Colascione; +Cc: intel-xe, Thomas Hellstrom, Rodrigo Vivi, dri-devel On Tue, Oct 27, 2026 at 09:50:31PM -0700, Daniel Colascione wrote: Thanks for this - I have wondered why when I probe Xe it takes a noticable amount of time - I suspect this is why. > We initialize the GGTT GPU page tables on startup. On my LNL, doing > this one PTE at a time through the BAR takes about 900 ms and involves > a million small writes. > > Use the GPU's copy engine to fill the PTEs in batches. We build > MI_UPDATE_GTT commands in normal memory, then send them to the GPU for > execution. This way, we avoid the per-PTE MMIO write and do the PTE fill > in 36.77 ms, ~24x faster. > > The new path is enabled by default. xe.ggtt_init_gpu=0 uses the old > CPU-driven path. > > LNL-only because I can test only LNL. Might it work elsewhere? > It should work anywhere. > Signed-off-by: Daniel Colascione <dancol@dancol.org> > --- > drivers/gpu/drm/xe/instructions/xe_mi_commands.h | 2 + > drivers/gpu/drm/xe/xe_ggtt.c | 128 +++++++++++++++++++++-- > drivers/gpu/drm/xe/xe_ggtt.h | 1 + > drivers/gpu/drm/xe/xe_gt.c | 3 +- > drivers/gpu/drm/xe/xe_module.c | 5 + > drivers/gpu/drm/xe/xe_module.h | 1 + > 6 files changed, 130 insertions(+), 10 deletions(-) > > diff --git a/drivers/gpu/drm/xe/instructions/xe_mi_commands.h b/drivers/gpu/drm/xe/instructions/xe_mi_commands.h > index 835ef75bbeb1..04bd4225adef 100644 > --- a/drivers/gpu/drm/xe/instructions/xe_mi_commands.h > +++ b/drivers/gpu/drm/xe/instructions/xe_mi_commands.h > @@ -62,6 +62,8 @@ > #define MI_LRI_FORCE_POSTED REG_BIT(12) > #define MI_LRI_LEN(x) (((x) & 0xff) + 1) > > +#define MI_UPDATE_GTT __MI_INSTR(0x23) > + > #define MI_STORE_REGISTER_MEM (__MI_INSTR(0x24) | XE_INSTR_NUM_DW(4)) > #define MI_SRM_USE_GGTT REG_BIT(22) > #define MI_SRM_ADD_CS_OFFSET REG_BIT(19) > diff --git a/drivers/gpu/drm/xe/xe_ggtt.c b/drivers/gpu/drm/xe/xe_ggtt.c > index 773777afc68a..3737d085ae68 100644 > --- a/drivers/gpu/drm/xe/xe_ggtt.c > +++ b/drivers/gpu/drm/xe/xe_ggtt.c > @@ -15,20 +15,25 @@ > #include <drm/intel/i915_drm.h> > #include <generated/xe_wa_oob.h> > > +#include "instructions/xe_mi_commands.h" > #include "regs/xe_gt_regs.h" > #include "regs/xe_gtt_defs.h" > #include "regs/xe_regs.h" > #include "xe_assert.h" > +#include "xe_bb.h" > #include "xe_bo.h" > +#include "xe_exec_queue.h" > #include "xe_gt.h" > #include "xe_gt_printk.h" > #include "xe_gt_types.h" > #include "xe_log.h" > #include "xe_map.h" > #include "xe_mmio.h" > +#include "xe_module.h" > #include "xe_pat.h" > #include "xe_pm.h" > #include "xe_res_cursor.h" > +#include "xe_sched_job.h" > #include "xe_sriov.h" > #include "xe_tile_printk.h" > #include "xe_tile_sriov_vf.h" > @@ -384,9 +389,9 @@ static void dev_fini_ggtt(void *arg) > * @ggtt: the &xe_ggtt to be initialized > * > * It allows to create new mappings usable by the GuC. > - * Mappings are not usable by the HW engines, as it doesn't have scratch nor > - * initial clear done to it yet. That will happen in the regular, non-early > - * GGTT initialization. > + * HW engines may only access explicitly mapped bootstrap buffers until the > + * remaining holes have been cleared. Normally xe_ggtt_init() does this; > + * the GPU clear finishes this work in xe_ggtt_init_late(). > * > * Return: 0 on success or a negative error code on failure. > */ > @@ -456,18 +461,119 @@ ALLOW_ERROR_INJECTION(xe_ggtt_init_early, ERRNO); /* See xe_pci_probe() */ > static void xe_ggtt_invalidate(struct xe_ggtt *ggtt); > static void xe_ggtt_invalidate_engine(struct xe_ggtt *ggtt); > > -static void xe_ggtt_initial_clear(struct xe_ggtt *ggtt) > +static bool ggtt_use_gpu(struct xe_device *xe) > { > + return xe_modparam.ggtt_init_gpu && xe->info.platform == XE_LUNARLAKE; Per below (last reply) - let's just turn this everywhere in platform config. > +} > + > +/* > + * MI_UPDATE_GTT takes at most 511 inline PTEs (ACM PRM, Vol. 2a, > + * pp. 1096-1097). Limit batches to 512K to fit the kernel batch pool. > + */ > +#define GGTT_INIT_PTES_PER_PACKET 511 > +#define GGTT_INIT_BATCH_DWORDS (128 * (2 + 2 * GGTT_INIT_PTES_PER_PACKET)) > + > +static int ggtt_clear_gpu(struct xe_exec_queue *q, struct xe_bb *bb, > + u64 pte, u64 start, u64 end) s/ggtt_clear_gpu/xe_ggtt_clear_gpu > +{ > + while (start < end) { > + struct xe_sched_job *job; > + int err; > + > + bb->len = 0; > + while (start < end && bb->len < GGTT_INIT_BATCH_DWORDS) { > + u32 count = min_t(u64, (end - start) >> XE_PTE_SHIFT, > + GGTT_INIT_PTES_PER_PACKET); > + > + bb->cs[bb->len++] = MI_UPDATE_GTT | (2 * count); > + bb->cs[bb->len++] = start; > + memset64((u64 *)&bb->cs[bb->len], pte, count); > + bb->len += 2 * count; > + start += (u64)count << XE_PTE_SHIFT; > + } > + > + job = xe_bb_create_job(q, bb); > + if (IS_ERR(job)) > + return PTR_ERR(job); > + > + xe_sched_job_arm(job); > + xe_sched_job_get(job); > + xe_sched_job_push(job); > + > + dma_fence_wait(&job->drm.s_fence->finished, false); You can pipeline this function. i.e., generate fences in a loop and only wait on the last one. This is fairly common pattern, xe_svm_copy is an example of function with loop which issues a bunch of jobs and only waits on the very last fence. > + err = dma_fence_get_status(&job->drm.s_fence->finished); > + xe_sched_job_put(job); > + if (err < 0) > + return err; > + } > + > + return 0; > +} > + > +static int xe_ggtt_initial_clear(struct xe_ggtt *ggtt, struct xe_hw_engine *hwe) > +{ > + struct xe_device *xe = tile_to_xe(ggtt->tile); > + struct xe_exec_queue *q = NULL; > + struct xe_bb *bb = NULL; > struct drm_mm_node *hole; > - u64 start, end; > + u16 pat_index = xe_cache_pat_idx(xe, XE_CACHE_WB); > + u64 start, end, pte = 0; > + int err = 0; > + > + if (hwe) { > + q = xe_exec_queue_create(xe, NULL, BIT(hwe->logical_instance), 1, > + hwe, EXEC_QUEUE_FLAG_KERNEL, 0); Can we use migration queue (xe_migrate.c)? We'd probably need a bit of layering rework to allow another layer like GGTT to safely use this queue but that seems better to keep a single isolated kernel queue. > + if (IS_ERR(q)) > + return PTR_ERR(q); > + > + bb = xe_bb_new(hwe->gt, GGTT_INIT_BATCH_DWORDS, false); > + if (IS_ERR(bb)) { > + err = PTR_ERR(bb); > + goto put_queue; > + } > + } > > - /* Display may have allocated inside ggtt, so be careful with clearing here */ > + if (ggtt->scratch) > + pte = xe_bo_addr(ggtt->scratch, 0, XE_PAGE_SIZE) | > + ggtt->pt_ops->pte_encode_flags(ggtt->scratch, pat_index); > + > + /* Preserve boot mappings and exclude allocator changes until completion. */ > mutex_lock(&ggtt->lock); CI and Sashiko correctly complain that 'ggtt->lock' is tainted with reclaim and ggtt_clear_gpu will be a malloc for the GPU job. I'm actually questioning if this taint is actually valid. afiak we never shrink or evict anything GGTT mapped (i.e., that memory is always pinned). So I think we can just drop the taint - I'd do this in a standalone patch. > - drm_mm_for_each_hole(hole, &ggtt->mm, start, end) > - xe_ggtt_clear(ggtt, ggtt->start + start, end - start); > + drm_mm_for_each_hole(hole, &ggtt->mm, start, end) { > + if (q) > + err = ggtt_clear_gpu(q, bb, pte, ggtt->start + start, ggtt->start + end); > + else > + xe_ggtt_clear(ggtt, ggtt->start + start, end - start); > + if (err) > + goto unlock; > + } > > xe_ggtt_invalidate(ggtt); > +unlock: > mutex_unlock(&ggtt->lock); > + xe_bb_free(bb, NULL); > +put_queue: > + if (q) > + xe_exec_queue_put(q); > + return err; > +} > + > +/** > + * xe_ggtt_init_late - Finish deferred GGTT initialization after GuC startup > + * @ggtt: GGTT to initialize > + * > + * All default contexts must be recorded before waiting for a GPU clear with > + * the GGTT mutex held, so reset recovery need not allocate context mappings. > + * > + * Return: 0 on success or a negative error code on failure. > + */ > +int xe_ggtt_init_late(struct xe_ggtt *ggtt) > +{ > + if (!ggtt_use_gpu(tile_to_xe(ggtt->tile))) > + return 0; > + > + return xe_ggtt_initial_clear(ggtt, xe_gt_hw_engine(ggtt->tile->primary_gt, > + XE_ENGINE_CLASS_COPY, 0, false)); > } > > static void ggtt_node_fini(struct xe_ggtt_node *node) > @@ -536,6 +642,9 @@ void xe_ggtt_node_remove(struct xe_ggtt_node *node, bool invalidate) > * xe_ggtt_init - Regular non-early GGTT initialization > * @ggtt: the &xe_ggtt to be initialized > * > + * Allocate the scratch page if needed and clear unused entries, or defer > + * the clear to xe_ggtt_init_late() when GPU initialization is enabled. > + * > * Return: 0 on success or a negative error code on failure. > */ > int xe_ggtt_init(struct xe_ggtt *ggtt) > @@ -572,7 +681,8 @@ int xe_ggtt_init(struct xe_ggtt *ggtt) > xe_bo_size(ggtt->scratch)); > } > > - xe_ggtt_initial_clear(ggtt); > + if (!ggtt_use_gpu(xe)) > + xe_ggtt_initial_clear(ggtt, NULL); > > return devm_add_action_or_reset(xe->drm.dev, ggtt_fini, ggtt); > err: > diff --git a/drivers/gpu/drm/xe/xe_ggtt.h b/drivers/gpu/drm/xe/xe_ggtt.h > index c864cc975a69..cae8600d7142 100644 > --- a/drivers/gpu/drm/xe/xe_ggtt.h > +++ b/drivers/gpu/drm/xe/xe_ggtt.h > @@ -17,6 +17,7 @@ struct xe_ggtt *xe_ggtt_alloc(struct xe_tile *tile); > int xe_ggtt_init_early(struct xe_ggtt *ggtt); > int xe_ggtt_init_kunit(struct xe_ggtt *ggtt, u32 reserved, u32 size); > int xe_ggtt_init(struct xe_ggtt *ggtt); > +int xe_ggtt_init_late(struct xe_ggtt *ggtt); > > void xe_ggtt_shift_nodes(struct xe_ggtt *ggtt, u64 new_base); > u64 xe_ggtt_start(struct xe_ggtt *ggtt); > diff --git a/drivers/gpu/drm/xe/xe_gt.c b/drivers/gpu/drm/xe/xe_gt.c > index 45fb074812a2..d1c14a02de66 100644 > --- a/drivers/gpu/drm/xe/xe_gt.c > +++ b/drivers/gpu/drm/xe/xe_gt.c > @@ -686,7 +686,8 @@ static int gt_init_with_all_forcewake(struct xe_gt *gt) > if (err) > return err; > > - return 0; > + return xe_gt_is_main_type(gt) ? > + xe_ggtt_init_late(gt_to_tile(gt)->mem.ggtt) : 0; > } > > static void xe_gt_fini(void *arg) > diff --git a/drivers/gpu/drm/xe/xe_module.c b/drivers/gpu/drm/xe/xe_module.c > index 897724cb5cfb..44cc0107cf53 100644 > --- a/drivers/gpu/drm/xe/xe_module.c > +++ b/drivers/gpu/drm/xe/xe_module.c > @@ -22,6 +22,7 @@ > #include "xe_sched_job.h" > > struct xe_modparam xe_modparam = { > + .ggtt_init_gpu = true, I'd make this platform param in xe_pci.c and turn it on for every platform which supports the required GPU instructions (likely every Xe supported platform). We can let this soak in for a couple months in our CI before getting into a kernel release to ensure everything is functional and then perhaps rip out the non-GPU clear code. Matt > .probe_display = XE_DEFAULT_PROBE_DISPLAY, > .guc_log_level = XE_DEFAULT_GUC_LOG_LEVEL, > .force_probe = XE_DEFAULT_FORCE_PROBE, > @@ -34,6 +35,10 @@ struct xe_modparam xe_modparam = { > /* the rest are 0 by default */ > }; > > +module_param_named(ggtt_init_gpu, xe_modparam.ggtt_init_gpu, bool, 0444); > +MODULE_PARM_DESC(ggtt_init_gpu, > + "GPU GGTT initialization on Lunar Lake (default: true)"); > + > module_param_named(svm_notifier_size, xe_modparam.svm_notifier_size, uint, 0600); > MODULE_PARM_DESC(svm_notifier_size, "Set the svm notifier size in MiB, must be power of 2 " > "[default=" __stringify(XE_DEFAULT_SVM_NOTIFIER_SIZE) "]"); > diff --git a/drivers/gpu/drm/xe/xe_module.h b/drivers/gpu/drm/xe/xe_module.h > index 6272d9e41207..8aa91822c35c 100644 > --- a/drivers/gpu/drm/xe/xe_module.h > +++ b/drivers/gpu/drm/xe/xe_module.h > @@ -12,6 +12,7 @@ struct work_struct; > > /* Module modprobe variables */ > struct xe_modparam { > + bool ggtt_init_gpu; > bool probe_display; > int force_vram_bar_size; > int guc_log_level; ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [RFC PATCH] drm/xe/ggtt: Cut GGTT initialization from 900 ms to 37 ms 2026-10-29 15:16 ` Matthew Brost @ 2026-10-29 15:31 ` Matthew Brost 0 siblings, 0 replies; 4+ messages in thread From: Matthew Brost @ 2026-10-29 15:31 UTC (permalink / raw) To: Daniel Colascione; +Cc: intel-xe, Thomas Hellstrom, Rodrigo Vivi, dri-devel On Thu, Oct 29, 2026 at 08:16:44AM -0700, Matthew Brost wrote: Sorry double reply, just had another thought... > On Tue, Oct 27, 2026 at 09:50:31PM -0700, Daniel Colascione wrote: > > Thanks for this - I have wondered why when I probe Xe it takes a > noticable amount of time - I suspect this is why. > > > We initialize the GGTT GPU page tables on startup. On my LNL, doing > > this one PTE at a time through the BAR takes about 900 ms and involves > > a million small writes. > > > > Use the GPU's copy engine to fill the PTEs in batches. We build > > MI_UPDATE_GTT commands in normal memory, then send them to the GPU for > > execution. This way, we avoid the per-PTE MMIO write and do the PTE fill > > in 36.77 ms, ~24x faster. > > > > The new path is enabled by default. xe.ggtt_init_gpu=0 uses the old > > CPU-driven path. > > > > LNL-only because I can test only LNL. Might it work elsewhere? > > > > It should work anywhere. > > > Signed-off-by: Daniel Colascione <dancol@dancol.org> > > --- > > drivers/gpu/drm/xe/instructions/xe_mi_commands.h | 2 + > > drivers/gpu/drm/xe/xe_ggtt.c | 128 +++++++++++++++++++++-- > > drivers/gpu/drm/xe/xe_ggtt.h | 1 + > > drivers/gpu/drm/xe/xe_gt.c | 3 +- > > drivers/gpu/drm/xe/xe_module.c | 5 + > > drivers/gpu/drm/xe/xe_module.h | 1 + > > 6 files changed, 130 insertions(+), 10 deletions(-) > > > > diff --git a/drivers/gpu/drm/xe/instructions/xe_mi_commands.h b/drivers/gpu/drm/xe/instructions/xe_mi_commands.h > > index 835ef75bbeb1..04bd4225adef 100644 > > --- a/drivers/gpu/drm/xe/instructions/xe_mi_commands.h > > +++ b/drivers/gpu/drm/xe/instructions/xe_mi_commands.h > > @@ -62,6 +62,8 @@ > > #define MI_LRI_FORCE_POSTED REG_BIT(12) > > #define MI_LRI_LEN(x) (((x) & 0xff) + 1) > > > > +#define MI_UPDATE_GTT __MI_INSTR(0x23) > > + > > #define MI_STORE_REGISTER_MEM (__MI_INSTR(0x24) | XE_INSTR_NUM_DW(4)) > > #define MI_SRM_USE_GGTT REG_BIT(22) > > #define MI_SRM_ADD_CS_OFFSET REG_BIT(19) > > diff --git a/drivers/gpu/drm/xe/xe_ggtt.c b/drivers/gpu/drm/xe/xe_ggtt.c > > index 773777afc68a..3737d085ae68 100644 > > --- a/drivers/gpu/drm/xe/xe_ggtt.c > > +++ b/drivers/gpu/drm/xe/xe_ggtt.c > > @@ -15,20 +15,25 @@ > > #include <drm/intel/i915_drm.h> > > #include <generated/xe_wa_oob.h> > > > > +#include "instructions/xe_mi_commands.h" > > #include "regs/xe_gt_regs.h" > > #include "regs/xe_gtt_defs.h" > > #include "regs/xe_regs.h" > > #include "xe_assert.h" > > +#include "xe_bb.h" > > #include "xe_bo.h" > > +#include "xe_exec_queue.h" > > #include "xe_gt.h" > > #include "xe_gt_printk.h" > > #include "xe_gt_types.h" > > #include "xe_log.h" > > #include "xe_map.h" > > #include "xe_mmio.h" > > +#include "xe_module.h" > > #include "xe_pat.h" > > #include "xe_pm.h" > > #include "xe_res_cursor.h" > > +#include "xe_sched_job.h" > > #include "xe_sriov.h" > > #include "xe_tile_printk.h" > > #include "xe_tile_sriov_vf.h" > > @@ -384,9 +389,9 @@ static void dev_fini_ggtt(void *arg) > > * @ggtt: the &xe_ggtt to be initialized > > * > > * It allows to create new mappings usable by the GuC. > > - * Mappings are not usable by the HW engines, as it doesn't have scratch nor > > - * initial clear done to it yet. That will happen in the regular, non-early > > - * GGTT initialization. > > + * HW engines may only access explicitly mapped bootstrap buffers until the > > + * remaining holes have been cleared. Normally xe_ggtt_init() does this; > > + * the GPU clear finishes this work in xe_ggtt_init_late(). > > * > > * Return: 0 on success or a negative error code on failure. > > */ > > @@ -456,18 +461,119 @@ ALLOW_ERROR_INJECTION(xe_ggtt_init_early, ERRNO); /* See xe_pci_probe() */ > > static void xe_ggtt_invalidate(struct xe_ggtt *ggtt); > > static void xe_ggtt_invalidate_engine(struct xe_ggtt *ggtt); > > > > -static void xe_ggtt_initial_clear(struct xe_ggtt *ggtt) > > +static bool ggtt_use_gpu(struct xe_device *xe) > > { > > + return xe_modparam.ggtt_init_gpu && xe->info.platform == XE_LUNARLAKE; > > Per below (last reply) - let's just turn this everywhere in platform > config. > > > +} > > + > > +/* > > + * MI_UPDATE_GTT takes at most 511 inline PTEs (ACM PRM, Vol. 2a, > > + * pp. 1096-1097). Limit batches to 512K to fit the kernel batch pool. > > + */ > > +#define GGTT_INIT_PTES_PER_PACKET 511 > > +#define GGTT_INIT_BATCH_DWORDS (128 * (2 + 2 * GGTT_INIT_PTES_PER_PACKET)) > > + > > +static int ggtt_clear_gpu(struct xe_exec_queue *q, struct xe_bb *bb, > > + u64 pte, u64 start, u64 end) > > s/ggtt_clear_gpu/xe_ggtt_clear_gpu > > > +{ > > + while (start < end) { > > + struct xe_sched_job *job; > > + int err; > > + > > + bb->len = 0; > > + while (start < end && bb->len < GGTT_INIT_BATCH_DWORDS) { > > + u32 count = min_t(u64, (end - start) >> XE_PTE_SHIFT, > > + GGTT_INIT_PTES_PER_PACKET); > > + > > + bb->cs[bb->len++] = MI_UPDATE_GTT | (2 * count); > > + bb->cs[bb->len++] = start; > > + memset64((u64 *)&bb->cs[bb->len], pte, count); > > + bb->len += 2 * count; > > + start += (u64)count << XE_PTE_SHIFT; > > + } > > + > > + job = xe_bb_create_job(q, bb); > > + if (IS_ERR(job)) > > + return PTR_ERR(job); > > + > > + xe_sched_job_arm(job); > > + xe_sched_job_get(job); > > + xe_sched_job_push(job); > > + > > + dma_fence_wait(&job->drm.s_fence->finished, false); > > You can pipeline this function. > > i.e., generate fences in a loop and only wait on the last one. > > This is fairly common pattern, xe_svm_copy is an example of function > with loop which issues a bunch of jobs and only waits on the very last > fence. > > > + err = dma_fence_get_status(&job->drm.s_fence->finished); > > + xe_sched_job_put(job); > > + if (err < 0) > > + return err; > > + } > > + > > + return 0; > > +} > > + > > +static int xe_ggtt_initial_clear(struct xe_ggtt *ggtt, struct xe_hw_engine *hwe) > > +{ > > + struct xe_device *xe = tile_to_xe(ggtt->tile); > > + struct xe_exec_queue *q = NULL; > > + struct xe_bb *bb = NULL; > > struct drm_mm_node *hole; > > - u64 start, end; > > + u16 pat_index = xe_cache_pat_idx(xe, XE_CACHE_WB); > > + u64 start, end, pte = 0; > > + int err = 0; > > + > > + if (hwe) { > > + q = xe_exec_queue_create(xe, NULL, BIT(hwe->logical_instance), 1, > > + hwe, EXEC_QUEUE_FLAG_KERNEL, 0); > > Can we use migration queue (xe_migrate.c)? We'd probably need a bit of > layering rework to allow another layer like GGTT to safely use this > queue but that seems better to keep a single isolated kernel queue. > > > + if (IS_ERR(q)) > > + return PTR_ERR(q); > > + > > + bb = xe_bb_new(hwe->gt, GGTT_INIT_BATCH_DWORDS, false); > > + if (IS_ERR(bb)) { > > + err = PTR_ERR(bb); > > + goto put_queue; > > + } > > + } > > > > - /* Display may have allocated inside ggtt, so be careful with clearing here */ > > + if (ggtt->scratch) > > + pte = xe_bo_addr(ggtt->scratch, 0, XE_PAGE_SIZE) | > > + ggtt->pt_ops->pte_encode_flags(ggtt->scratch, pat_index); > > + > > + /* Preserve boot mappings and exclude allocator changes until completion. */ > > mutex_lock(&ggtt->lock); > > CI and Sashiko correctly complain that 'ggtt->lock' is tainted with > reclaim and ggtt_clear_gpu will be a malloc for the GPU job. > > I'm actually questioning if this taint is actually valid. afiak we never > shrink or evict anything GGTT mapped (i.e., that memory is always > pinned). So I think we can just drop the taint - I'd do this in a > standalone patch. > > > - drm_mm_for_each_hole(hole, &ggtt->mm, start, end) > > - xe_ggtt_clear(ggtt, ggtt->start + start, end - start); > > + drm_mm_for_each_hole(hole, &ggtt->mm, start, end) { > > + if (q) > > + err = ggtt_clear_gpu(q, bb, pte, ggtt->start + start, ggtt->start + end); > > + else > > + xe_ggtt_clear(ggtt, ggtt->start + start, end - start); > > + if (err) > > + goto unlock; > > + } > > > > xe_ggtt_invalidate(ggtt); > > +unlock: > > mutex_unlock(&ggtt->lock); > > + xe_bb_free(bb, NULL); > > +put_queue: > > + if (q) > > + xe_exec_queue_put(q); > > + return err; > > +} > > + > > +/** > > + * xe_ggtt_init_late - Finish deferred GGTT initialization after GuC startup > > + * @ggtt: GGTT to initialize > > + * > > + * All default contexts must be recorded before waiting for a GPU clear with > > + * the GGTT mutex held, so reset recovery need not allocate context mappings. > > + * > > + * Return: 0 on success or a negative error code on failure. > > + */ > > +int xe_ggtt_init_late(struct xe_ggtt *ggtt) > > +{ > > + if (!ggtt_use_gpu(tile_to_xe(ggtt->tile))) > > + return 0; > > + > > + return xe_ggtt_initial_clear(ggtt, xe_gt_hw_engine(ggtt->tile->primary_gt, > > + XE_ENGINE_CLASS_COPY, 0, false)); > > } > > > > static void ggtt_node_fini(struct xe_ggtt_node *node) > > @@ -536,6 +642,9 @@ void xe_ggtt_node_remove(struct xe_ggtt_node *node, bool invalidate) > > * xe_ggtt_init - Regular non-early GGTT initialization > > * @ggtt: the &xe_ggtt to be initialized > > * > > + * Allocate the scratch page if needed and clear unused entries, or defer > > + * the clear to xe_ggtt_init_late() when GPU initialization is enabled. > > + * > > * Return: 0 on success or a negative error code on failure. > > */ > > int xe_ggtt_init(struct xe_ggtt *ggtt) > > @@ -572,7 +681,8 @@ int xe_ggtt_init(struct xe_ggtt *ggtt) > > xe_bo_size(ggtt->scratch)); > > } > > > > - xe_ggtt_initial_clear(ggtt); > > + if (!ggtt_use_gpu(xe)) > > + xe_ggtt_initial_clear(ggtt, NULL); Hmm, so this defers GGTT clears to after we use GGTT memory for queues / jobs. The fact this works points to GGTT scratch most likely not actually being needed but that is pretty hard to reason 100% correctly. I wonder if a better solution is completely drop the GGTT clear and BO which sets XE_BO_FLAG_GGTT just allocates guard page at the end (again with a config to enable this which is on by default until we can reason this isn't needed). A lot to take in here. Let us know if you want to continue driving this, or I can try to find an engineer at Intel to own it. A reduction from 900 ms to 37 ms is a compelling result, and this clearly needs to be reworked. Matt > > > > return devm_add_action_or_reset(xe->drm.dev, ggtt_fini, ggtt); > > err: > > diff --git a/drivers/gpu/drm/xe/xe_ggtt.h b/drivers/gpu/drm/xe/xe_ggtt.h > > index c864cc975a69..cae8600d7142 100644 > > --- a/drivers/gpu/drm/xe/xe_ggtt.h > > +++ b/drivers/gpu/drm/xe/xe_ggtt.h > > @@ -17,6 +17,7 @@ struct xe_ggtt *xe_ggtt_alloc(struct xe_tile *tile); > > int xe_ggtt_init_early(struct xe_ggtt *ggtt); > > int xe_ggtt_init_kunit(struct xe_ggtt *ggtt, u32 reserved, u32 size); > > int xe_ggtt_init(struct xe_ggtt *ggtt); > > +int xe_ggtt_init_late(struct xe_ggtt *ggtt); > > > > void xe_ggtt_shift_nodes(struct xe_ggtt *ggtt, u64 new_base); > > u64 xe_ggtt_start(struct xe_ggtt *ggtt); > > diff --git a/drivers/gpu/drm/xe/xe_gt.c b/drivers/gpu/drm/xe/xe_gt.c > > index 45fb074812a2..d1c14a02de66 100644 > > --- a/drivers/gpu/drm/xe/xe_gt.c > > +++ b/drivers/gpu/drm/xe/xe_gt.c > > @@ -686,7 +686,8 @@ static int gt_init_with_all_forcewake(struct xe_gt *gt) > > if (err) > > return err; > > > > - return 0; > > + return xe_gt_is_main_type(gt) ? > > + xe_ggtt_init_late(gt_to_tile(gt)->mem.ggtt) : 0; > > } > > > > static void xe_gt_fini(void *arg) > > diff --git a/drivers/gpu/drm/xe/xe_module.c b/drivers/gpu/drm/xe/xe_module.c > > index 897724cb5cfb..44cc0107cf53 100644 > > --- a/drivers/gpu/drm/xe/xe_module.c > > +++ b/drivers/gpu/drm/xe/xe_module.c > > @@ -22,6 +22,7 @@ > > #include "xe_sched_job.h" > > > > struct xe_modparam xe_modparam = { > > + .ggtt_init_gpu = true, > > I'd make this platform param in xe_pci.c and turn it on for every > platform which supports the required GPU instructions (likely every Xe > supported platform). We can let this soak in for a couple months in our > CI before getting into a kernel release to ensure everything is > functional and then perhaps rip out the non-GPU clear code. > > Matt > > > .probe_display = XE_DEFAULT_PROBE_DISPLAY, > > .guc_log_level = XE_DEFAULT_GUC_LOG_LEVEL, > > .force_probe = XE_DEFAULT_FORCE_PROBE, > > @@ -34,6 +35,10 @@ struct xe_modparam xe_modparam = { > > /* the rest are 0 by default */ > > }; > > > > +module_param_named(ggtt_init_gpu, xe_modparam.ggtt_init_gpu, bool, 0444); > > +MODULE_PARM_DESC(ggtt_init_gpu, > > + "GPU GGTT initialization on Lunar Lake (default: true)"); > > + > > module_param_named(svm_notifier_size, xe_modparam.svm_notifier_size, uint, 0600); > > MODULE_PARM_DESC(svm_notifier_size, "Set the svm notifier size in MiB, must be power of 2 " > > "[default=" __stringify(XE_DEFAULT_SVM_NOTIFIER_SIZE) "]"); > > diff --git a/drivers/gpu/drm/xe/xe_module.h b/drivers/gpu/drm/xe/xe_module.h > > index 6272d9e41207..8aa91822c35c 100644 > > --- a/drivers/gpu/drm/xe/xe_module.h > > +++ b/drivers/gpu/drm/xe/xe_module.h > > @@ -12,6 +12,7 @@ struct work_struct; > > > > /* Module modprobe variables */ > > struct xe_modparam { > > + bool ggtt_init_gpu; > > bool probe_display; > > int force_vram_bar_size; > > int guc_log_level; ^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-10-29 15:32 UTC | newest] Thread overview: 4+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-10-28 4:50 [RFC PATCH] drm/xe/ggtt: Cut GGTT initialization from 900 ms to 37 ms Daniel Colascione 2026-10-28 13:23 ` sashiko-bot 2026-10-29 15:16 ` Matthew Brost 2026-10-29 15:31 ` Matthew Brost
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox