Intel-XE Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Francois Dugast <francois.dugast@intel.com>
To: Matthew Brost <matthew.brost@intel.com>
Cc: intel-xe@lists.freedesktop.org, dri-devel@lists.freedesktop.org,
	"Alice Ryhl" <aliceryhl@google.com>,
	"Boris Brezillon" <boris.brezillon@collabora.com>,
	"Danilo Krummrich" <dakr@kernel.org>,
	"David Airlie" <airlied@gmail.com>,
	"Jonathan Corbet" <corbet@lwn.net>,
	"Liviu Dudau" <liviu.dudau@arm.com>,
	"Maarten Lankhorst" <maarten.lankhorst@linux.intel.com>,
	"Maxime Ripard" <mripard@kernel.org>,
	"Rodrigo Vivi" <rodrigo.vivi@intel.com>,
	"Shuah Khan" <skhan@linuxfoundation.org>,
	"Simona Vetter" <simona@ffwll.ch>,
	"Steven Price" <steven.price@arm.com>,
	"Thomas Hellström" <thomas.hellstrom@linux.intel.com>,
	"Thomas Zimmermann" <tzimmermann@suse.de>
Subject: Re: [PATCH 2/3] drm/xe: lock the resident BOs of an exec last
Date: Thu, 1 Oct 2026 11:55:58 +0200	[thread overview]
Message-ID: <ar4uLiFuUucrQXCg@fdugast-desk> (raw)
In-Reply-To: <20260814073258.893007-3-matthew.brost@intel.com>

On Fri, Aug 14, 2026 at 12:32:57AM -0700, Matthew Brost wrote:
> xe_exec_ioctl() locks the dma-resv of every BO mapped in the VM in one
> drm_exec transaction, then validates, rebinds and submits. Any migration
> or fault-in a client needs therefore happens while it holds the dma-resv
> of every object it has mapped, including the ones shared with other
> processes. A client faulting in a large buffer of its own stalls whoever
> else has those shared objects mapped, so the compositor it is presenting
> to can miss a deadline over a set of BOs it has nothing to do with.
> 
> Most of those objects are not ones the exec has to validate. Make the
> exec transaction two pass, so that it locks the evicted BOs first,
> validates them, and only then locks the resident ones. It ends up
> holding exactly the locks it holds today, it just takes the ones it does
> not have to validate last, once the expensive work is already done.
> 
> Only the external BOs are actually split between the passes. The VM's
> dma-resv is held from the start, as before, so the evicted private BOs
> are validated in the early pass too, without anything extra being
> locked for them.
> 
> Nothing is unlocked in between the passes, so this needs no recheck and
> no fallback. The late pass can still find something to validate, since a
> BO it had not locked yet may have been evicted meanwhile; that is handled
> the way it is today, with every lock held.
> 
> Two details are worth pointing out. xe_vm_rebind() rebinds the whole
> rebind list in one go and attaches a fence to the dma-resv of every BO
> on it, so it needs all of them locked; the early pass deliberately does
> not hold the resident ones, so it leaves the rebind to the late pass
> entirely. That is also the better order, since rebinding allocates page
> tables and can therefore evict the very BOs the early pass is trying to
> leave alone. And the sched job's fence slot is reserved in the late pass
> only, that being the one which holds every lock the transaction is going
> to hold, so it is still reserved exactly once per object.
> 
> A concern with splitting the passes is that validating in the early pass
> could evict the very BOs the late pass is about to lock, moving the work
> back under the full set of locks. Xe is immune to this by construction:
> __xe_bo_validate() brackets its ttm_bo_validate() call with
> xe_vm_set_validating(), and xe_bo_eviction_valuable() walks the
> drm_gpuvm_bos of any eviction candidate and refuses the ones bound to a VM
> the current task is validating. The early pass therefore cannot evict a BO
> mapped in the VM it is validating, whether or not the late pass was going
> to lock it. That guard predates this patch; self-eviction is pointless
> work in a single pass too.
> 
> While at it, xe_gpuvm_validate() is changed to clear the evicted state
> with drm_gpuvm_bo_evict() rather than by assigning drm_gpuvm_bo::evicted
> behind GPUVM's back, so that the bookkeeping GPUVM now does there is not
> bypassed.
> 
> Cc: Alice Ryhl <aliceryhl@google.com>
> Cc: Boris Brezillon <boris.brezillon@collabora.com>
> Cc: Danilo Krummrich <dakr@kernel.org>
> Cc: David Airlie <airlied@gmail.com>
> Cc: Jonathan Corbet <corbet@lwn.net>
> Cc: Liviu Dudau <liviu.dudau@arm.com>
> Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
> Cc: Maxime Ripard <mripard@kernel.org>
> Cc: Rodrigo Vivi <rodrigo.vivi@intel.com>
> Cc: Shuah Khan <skhan@linuxfoundation.org>
> Cc: Simona Vetter <simona@ffwll.ch>
> Cc: Steven Price <steven.price@arm.com>
> Cc: Thomas Hellström <thomas.hellstrom@linux.intel.com>
> Cc: Thomas Zimmermann <tzimmermann@suse.de>
> Signed-off-by: Matthew Brost <matthew.brost@intel.com>

Reviewed-by: Francois Dugast <francois.dugast@intel.com>

> Assisted-by: GitHub_Copilot:claude-opus-5
> ---
>  drivers/gpu/drm/xe/xe_exec.c | 23 ++++++++++++++++---
>  drivers/gpu/drm/xe/xe_vm.c   | 43 ++++++++++++++++++++++++++++++------
>  drivers/gpu/drm/xe/xe_vm.h   |  3 ++-
>  3 files changed, 58 insertions(+), 11 deletions(-)
> 
> diff --git a/drivers/gpu/drm/xe/xe_exec.c b/drivers/gpu/drm/xe/xe_exec.c
> index d5293bc33a67..abe522c19ec9 100644
> --- a/drivers/gpu/drm/xe/xe_exec.c
> +++ b/drivers/gpu/drm/xe/xe_exec.c
> @@ -79,8 +79,10 @@
>   *	<----------------------------------------------------------------------|
>   *	Lock global VM lock in read mode                                       |
>   *	Pin userptrs (also finds userptr invalidated since last exec)          |
> - *	Lock exec (VM dma-resv lock, external BOs dma-resv locks)              |
> + *	Lock exec early pass (VM and evicted external BOs dma-resv locks)      |
>   *	Validate BOs that have been evicted                                    |
> + *	Lock exec late pass (the external BOs left out above)                  |
> + *	Validate any BO evicted since the early pass looked at it              |
>   *	Create job                                                             |
>   *	Rebind invalidated userptrs + evicted BOs (non-compute-mode)           |
>   *	Add rebind fence dependency to job                                     |
> @@ -95,15 +97,22 @@
>  /*
>   * Add validation and rebinding to the drm_exec locking loop, since both can
>   * trigger eviction which may require sleeping dma_resv locks.
> + *
> + * Called once per pass, see xe_exec_ioctl(). The fence slot is intended for
> + * the exec sched job and is only reserved in the pass which holds every lock
> + * the transaction is going to hold, so that it is reserved exactly once.
>   */
>  static int xe_exec_fn(struct drm_gpuvm_exec *vm_exec)
>  {
>  	struct xe_vm *vm = container_of(vm_exec->vm, struct xe_vm, gpuvm);
> +	unsigned int num_fences;
>  	int ret;
>  
> -	/* The fence slot added here is intended for the exec sched job. */
> +	num_fences = vm_exec->pass == DRM_GPUVM_EXEC_PASS_EARLY ? 0 : 1;
> +
>  	xe_vm_set_validation_exec(vm, &vm_exec->exec);
> -	ret = xe_vm_validate_rebind(vm, &vm_exec->exec, 1);
> +	ret = xe_vm_validate_rebind(vm, &vm_exec->exec, num_fences,
> +				    vm_exec->pass);
>  	xe_vm_set_validation_exec(vm, NULL);
>  	return ret;
>  }
> @@ -268,6 +277,14 @@ int xe_exec_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
>  	if (!xe_vm_in_lr_mode(vm)) {
>  		vm_exec.vm = &vm->gpuvm;
>  		vm_exec.flags = DRM_EXEC_INTERRUPTIBLE_WAIT;
> +		/*
> +		 * Only the evicted BOs need validating, so lock those first,
> +		 * validate them, and only then lock the resident ones. A
> +		 * client faulting in a huge buffer of its own then no longer
> +		 * holds, for the duration of that, the dma-resv of a BO it
> +		 * shares with the compositor it presents to.
> +		 */
> +		vm_exec.two_pass = true;
>  		err = xe_validation_exec_lock(&ctx, &vm_exec, &xe->val);
>  		if (err)
>  			goto err_unlock_list;
> diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
> index b37ade64f4eb..3b3b01764e11 100644
> --- a/drivers/gpu/drm/xe/xe_vm.c
> +++ b/drivers/gpu/drm/xe/xe_vm.c
> @@ -355,7 +355,7 @@ static int xe_gpuvm_validate(struct drm_gpuvm_bo *vm_bo, struct drm_exec *exec)
>  
>  	/* Skip re-populating purged BOs, rebind maps scratch pages. */
>  	if (xe_bo_is_purged(bo)) {
> -		vm_bo->evicted = false;
> +		drm_gpuvm_bo_evict(vm_bo, false);
>  		return 0;
>  	}
>  
> @@ -366,7 +366,7 @@ static int xe_gpuvm_validate(struct drm_gpuvm_bo *vm_bo, struct drm_exec *exec)
>  	if (ret)
>  		return ret;
>  
> -	vm_bo->evicted = false;
> +	drm_gpuvm_bo_evict(vm_bo, false);
>  	return 0;
>  }
>  
> @@ -375,31 +375,59 @@ static int xe_gpuvm_validate(struct drm_gpuvm_bo *vm_bo, struct drm_exec *exec)
>   * @vm: The vm for which we are rebinding.
>   * @exec: The struct drm_exec with the locked GEM objects.
>   * @num_fences: The number of fences to reserve for the operation, not
> - * including rebinds and validations.
> + * including rebinds and validations. Zero reserves none, which is what the
> + * %DRM_GPUVM_EXEC_PASS_EARLY pass wants.
> + * @pass: The &enum drm_gpuvm_exec_pass @exec was locked for.
>   *
>   * Validates all evicted gem objects and rebinds their vmas. Note that
>   * rebindings may cause evictions and hence the validation-rebind
>   * sequence is rerun until there are no more objects to validate.
>   *
> + * In the %DRM_GPUVM_EXEC_PASS_EARLY pass only the validation is done, and
> + * only for the objects whose dma-resv @exec holds. The rest, along with the
> + * rebind and the fence reservation, is left to the
> + * %DRM_GPUVM_EXEC_PASS_LATE pass of the same transaction, which locks
> + * everything.
> + *
>   * Return: 0 on success, negative error code on error. In particular,
>   * may return -EINTR or -ERESTARTSYS if interrupted, and -EDEADLK if
>   * the drm_exec transaction needs to be restarted.
>   */
>  int xe_vm_validate_rebind(struct xe_vm *vm, struct drm_exec *exec,
> -			  unsigned int num_fences)
> +			  unsigned int num_fences,
> +			  enum drm_gpuvm_exec_pass pass)
>  {
>  	struct drm_gem_object *obj;
>  	int ret;
>  
>  	do {
> -		ret = drm_gpuvm_validate(&vm->gpuvm, exec);
> +		ret = drm_gpuvm_validate_pass(&vm->gpuvm, exec, pass);
>  		if (ret)
>  			return ret;
>  
> +		/*
> +		 * xe_vm_rebind() rebinds the whole rebind list in one go and
> +		 * attaches a fence to the dma-resv of every BO on it, so it
> +		 * needs all of them locked. The early pass deliberately does
> +		 * not lock the resident ones, so leave the rebind to the late
> +		 * pass, which holds everything.
> +		 */
> +		if (pass == DRM_GPUVM_EXEC_PASS_EARLY)
> +			continue;
> +
>  		ret = xe_vm_rebind(vm, false);
>  		if (ret)
>  			return ret;
> -	} while (!list_empty(&vm->gpuvm.evict.list));
> +	} while (drm_gpuvm_has_evicted(&vm->gpuvm, pass));
> +
> +	/*
> +	 * The early pass reserves nothing. It attaches no fence itself, and
> +	 * the objects it locks are still locked in the late pass, whose own
> +	 * reservation below walks every object the transaction has
> +	 * accumulated and so covers them too.
> +	 */
> +	if (!num_fences)
> +		return 0;
>  
>  	drm_exec_for_each_locked_object(exec, obj) {
>  		ret = dma_resv_reserve_fences(obj->resv, num_fences);
> @@ -444,7 +472,8 @@ static int xe_preempt_work_begin(struct drm_exec *exec, struct xe_vm *vm,
>  	 * The fence reservation here is intended for the new preempt fences
>  	 * we attach at the end of the rebind work.
>  	 */
> -	return xe_vm_validate_rebind(vm, exec, vm->preempt.num_exec_queues);
> +	return xe_vm_validate_rebind(vm, exec, vm->preempt.num_exec_queues,
> +				     DRM_GPUVM_EXEC_PASS_ALL);
>  }
>  
>  static bool vm_suspend_rebind_worker(struct xe_vm *vm)
> diff --git a/drivers/gpu/drm/xe/xe_vm.h b/drivers/gpu/drm/xe/xe_vm.h
> index c5b900f38ded..3fa2c1d721c4 100644
> --- a/drivers/gpu/drm/xe/xe_vm.h
> +++ b/drivers/gpu/drm/xe/xe_vm.h
> @@ -282,7 +282,8 @@ static inline void xe_vm_reactivate_rebind(struct xe_vm *vm)
>  int xe_vm_lock_vma(struct drm_exec *exec, struct xe_vma *vma);
>  
>  int xe_vm_validate_rebind(struct xe_vm *vm, struct drm_exec *exec,
> -			  unsigned int num_fences);
> +			  unsigned int num_fences,
> +			  enum drm_gpuvm_exec_pass pass);
>  
>  struct dma_fence *xe_vm_bind_kernel_bo(struct xe_vm *vm, struct xe_bo *bo,
>  				       struct xe_exec_queue *q, u64 addr,
> -- 
> 2.34.1
> 

  reply	other threads:[~2026-10-01  9:56 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-14  7:32 [PATCH 0/3] drm/gpuvm: two pass locking for exec Matthew Brost
2026-08-14  7:32 ` [PATCH 1/3] drm/gpuvm: allow locking external objects in two passes Matthew Brost
2026-08-14  7:51   ` sashiko-bot
2026-08-14  8:18     ` Matthew Brost
2026-09-11 10:44   ` Liviu Dudau
2026-10-01 11:46   ` Danilo Krummrich
2026-08-14  7:32 ` [PATCH 2/3] drm/xe: lock the resident BOs of an exec last Matthew Brost
2026-10-01  9:55   ` Francois Dugast [this message]
2026-08-14  7:32 ` [PATCH 3/3] drm/panthor: lock the resident BOs of a submit last Matthew Brost
2026-09-11 10:47   ` Liviu Dudau
2026-08-14  7:49 ` ✓ CI.KUnit: success for drm/gpuvm: two pass locking for exec Patchwork
2026-08-14  8:57 ` ✓ Xe.CI.BAT: " Patchwork
2026-08-14 10:58 ` ✓ Xe.CI.FULL: " Patchwork
     [not found] ` <c87a906a82dcce2c52352a2796e21cc9037c71b3.camel@linux.intel.com>
     [not found]   ` <an+Mzd5xuu0KHxhE@gsse-cloud1.jf.intel.com>
     [not found]     ` <09b2029f7942c997c9355508b1f757f8731a9ad5.camel@linux.intel.com>
2026-08-18 21:44       ` [PATCH 0/3] " Matthew Brost

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=ar4uLiFuUucrQXCg@fdugast-desk \
    --to=francois.dugast@intel.com \
    --cc=airlied@gmail.com \
    --cc=aliceryhl@google.com \
    --cc=boris.brezillon@collabora.com \
    --cc=corbet@lwn.net \
    --cc=dakr@kernel.org \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=intel-xe@lists.freedesktop.org \
    --cc=liviu.dudau@arm.com \
    --cc=maarten.lankhorst@linux.intel.com \
    --cc=matthew.brost@intel.com \
    --cc=mripard@kernel.org \
    --cc=rodrigo.vivi@intel.com \
    --cc=simona@ffwll.ch \
    --cc=skhan@linuxfoundation.org \
    --cc=steven.price@arm.com \
    --cc=thomas.hellstrom@linux.intel.com \
    --cc=tzimmermann@suse.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox