Commit f0ded972 authored by Dave Airlie's avatar Dave Airlie
Browse files

Merge tag 'drm-rust-next-2025-11-18' of...

Merge tag 'drm-rust-next-2025-11-18' of https://gitlab.freedesktop.org/drm/rust/kernel

 into drm-next

Cross-subsystem Changes:

Rust
  - Make slice::as_flattened usable on all supported versions of rustc.
  - Add FromBytes::from_bytes_prefix() method.

Core Changes:

  - Update Tyr in MAINTAINERS file.
  - Remove redundant device ptr from Rust GEM object.
  - Change how AlwaysRefCounted is implemented for GEM objects.
  - Add deferred vm_bo cleanup to GPUVM and use it in Panthor.

Driver Changes:

Nova Core
  - Introduction of bitfield! macro, with support for different storage sizes
    and custom visibility.
  - Introduction of safe converters between integer types for which the
    conversion is lossless.
  - GSP initialized up to fully booted state on Ampere.
  - Use more future-proof register for GPU identification.
  - Various simplifications and optimizations.

Nova
  - Select NOVA_CORE.
  - Depend on CONFIG_64BIT.

Signed-off-by: default avatarDave Airlie <airlied@redhat.com>

From: Alice Ryhl <aliceryhl@google.com>
Link: https://patch.msgid.link/aRxtJC0D1pQUepF4@google.com
parents f3a1d69f 77b686f6
Loading
Loading
Loading
Loading
+0 −30
Original line number Diff line number Diff line
@@ -44,25 +44,6 @@ automatically generates the corresponding mappings between a value and a number.
| Complexity: Beginner
| Link: https://docs.rs/num/latest/num/trait.FromPrimitive.html

Conversion from byte slices for types implementing FromBytes [TRSM]
-------------------------------------------------------------------

We retrieve several structures from byte streams coming from the BIOS or loaded
firmware. At the moment converting the bytes slice into the proper type require
an inelegant `unsafe` operation; this will go away once `FromBytes` implements
a proper `from_bytes` method.

| Complexity: Beginner

CoherentAllocation improvements [COHA]
--------------------------------------

`CoherentAllocation` needs a safe way to write into the allocation, and to
obtain slices within the allocation.

| Complexity: Beginner
| Contact: Abdiel Janulgue

Generic register abstraction [REGA]
-----------------------------------

@@ -153,17 +134,6 @@ A `num` core kernel module is being designed to provide these operations.
| Complexity: Intermediate
| Contact: Alexandre Courbot

Delay / Sleep abstractions [DLAY]
---------------------------------

Rust abstractions for the kernel's delay() and sleep() functions.

FUJITA Tomonori plans to work on abstractions for read_poll_timeout_atomic()
(and friends) [1].

| Complexity: Beginner
| Link: https://lore.kernel.org/netdev/20250228.080550.354359820929821928.fujita.tomonori@gmail.com/ [1]

IRQ abstractions
----------------

+1 −0
Original line number Diff line number Diff line
@@ -8264,6 +8264,7 @@ S: Supported
W:	https://drm.pages.freedesktop.org/maintainer-tools/drm-rust.html
T:	git https://gitlab.freedesktop.org/drm/rust/kernel.git
F:	drivers/gpu/drm/nova/
F:	drivers/gpu/drm/tyr/
F:	drivers/gpu/nova-core/
F:	rust/kernel/drm/
+190 −0
Original line number Diff line number Diff line
@@ -877,6 +877,31 @@ __drm_gpuvm_bo_list_add(struct drm_gpuvm *gpuvm, spinlock_t *lock,
	cond_spin_unlock(lock, !!lock);
}

/**
 * drm_gpuvm_bo_is_zombie() - check whether this vm_bo is scheduled for cleanup
 * @vm_bo: the &drm_gpuvm_bo
 *
 * When a vm_bo is scheduled for cleanup using the bo_defer list, it is not
 * immediately removed from the evict and extobj lists. Therefore, anyone
 * iterating these lists should skip entries that are being destroyed.
 *
 * Checking the refcount without incrementing it is okay as long as the lock
 * protecting the evict/extobj list is held for as long as you are using the
 * vm_bo, because even if the refcount hits zero while you are using it, freeing
 * the vm_bo requires taking the list's lock.
 *
 * Zombie entries can be observed on the evict and extobj lists regardless of
 * whether DRM_GPUVM_RESV_PROTECTED is used, but they remain on the lists for a
 * longer time when the resv lock is used because we can't take the resv lock
 * during run_job() in immediate mode, meaning that they need to remain on the
 * lists until drm_gpuvm_bo_deferred_cleanup() is called.
 */
static bool
drm_gpuvm_bo_is_zombie(struct drm_gpuvm_bo *vm_bo)
{
	return !kref_read(&vm_bo->kref);
}

/**
 * drm_gpuvm_bo_list_add() - insert a vm_bo into the given list
 * @__vm_bo: the &drm_gpuvm_bo
@@ -1082,6 +1107,8 @@ drm_gpuvm_init(struct drm_gpuvm *gpuvm, const char *name,
	INIT_LIST_HEAD(&gpuvm->evict.list);
	spin_lock_init(&gpuvm->evict.lock);

	init_llist_head(&gpuvm->bo_defer);

	kref_init(&gpuvm->kref);

	gpuvm->name = name ? name : "unknown";
@@ -1123,6 +1150,8 @@ drm_gpuvm_fini(struct drm_gpuvm *gpuvm)
		 "Extobj list should be empty.\n");
	drm_WARN(gpuvm->drm, !list_empty(&gpuvm->evict.list),
		 "Evict list should be empty.\n");
	drm_WARN(gpuvm->drm, !llist_empty(&gpuvm->bo_defer),
		 "VM BO cleanup list should be empty.\n");

	drm_gem_object_put(gpuvm->r_obj);
}
@@ -1218,6 +1247,9 @@ drm_gpuvm_prepare_objects_locked(struct drm_gpuvm *gpuvm,

	drm_gpuvm_resv_assert_held(gpuvm);
	list_for_each_entry(vm_bo, &gpuvm->extobj.list, list.entry.extobj) {
		if (drm_gpuvm_bo_is_zombie(vm_bo))
			continue;

		ret = exec_prepare_obj(exec, vm_bo->obj, num_fences);
		if (ret)
			break;
@@ -1461,6 +1493,9 @@ drm_gpuvm_validate_locked(struct drm_gpuvm *gpuvm, struct drm_exec *exec)

	list_for_each_entry_safe(vm_bo, next, &gpuvm->evict.list,
				 list.entry.evict) {
		if (drm_gpuvm_bo_is_zombie(vm_bo))
			continue;

		ret = ops->vm_bo_validate(vm_bo, exec);
		if (ret)
			break;
@@ -1561,6 +1596,7 @@ drm_gpuvm_bo_create(struct drm_gpuvm *gpuvm,

	INIT_LIST_HEAD(&vm_bo->list.entry.extobj);
	INIT_LIST_HEAD(&vm_bo->list.entry.evict);
	init_llist_node(&vm_bo->list.entry.bo_defer);

	return vm_bo;
}
@@ -1622,6 +1658,126 @@ drm_gpuvm_bo_put(struct drm_gpuvm_bo *vm_bo)
}
EXPORT_SYMBOL_GPL(drm_gpuvm_bo_put);

/*
 * drm_gpuvm_bo_into_zombie() - called when the vm_bo becomes a zombie due to
 * deferred cleanup
 *
 * If deferred cleanup is used, then this must be called right after the vm_bo
 * refcount drops to zero. Must be called with GEM mutex held. After releasing
 * the GEM mutex, drm_gpuvm_bo_defer_zombie_cleanup() must be called.
 */
static void
drm_gpuvm_bo_into_zombie(struct kref *kref)
{
	struct drm_gpuvm_bo *vm_bo = container_of(kref, struct drm_gpuvm_bo,
						  kref);

	if (!drm_gpuvm_resv_protected(vm_bo->vm)) {
		drm_gpuvm_bo_list_del(vm_bo, extobj, true);
		drm_gpuvm_bo_list_del(vm_bo, evict, true);
	}

	list_del(&vm_bo->list.entry.gem);
}

/*
 * drm_gpuvm_bo_defer_zombie_cleanup() - adds a new zombie vm_bo to the
 * bo_defer list
 *
 * Called after drm_gpuvm_bo_into_zombie(). GEM mutex must not be held.
 *
 * It's important that the GEM stays alive for the duration in which we hold
 * the mutex, but the instant we add the vm_bo to bo_defer, another thread
 * might call drm_gpuvm_bo_deferred_cleanup() and put the GEM. Therefore, to
 * avoid kfreeing a mutex we are holding, the GEM mutex must be released
 * *before* calling this function.
 */
static void
drm_gpuvm_bo_defer_zombie_cleanup(struct drm_gpuvm_bo *vm_bo)
{
	llist_add(&vm_bo->list.entry.bo_defer, &vm_bo->vm->bo_defer);
}

static void
drm_gpuvm_bo_defer_free(struct kref *kref)
{
	struct drm_gpuvm_bo *vm_bo = container_of(kref, struct drm_gpuvm_bo,
						  kref);

	drm_gpuvm_bo_into_zombie(kref);
	mutex_unlock(&vm_bo->obj->gpuva.lock);
	drm_gpuvm_bo_defer_zombie_cleanup(vm_bo);
}

/**
 * drm_gpuvm_bo_put_deferred() - drop a struct drm_gpuvm_bo reference with
 * deferred cleanup
 * @vm_bo: the &drm_gpuvm_bo to release the reference of
 *
 * This releases a reference to @vm_bo.
 *
 * This might take and release the GEMs GPUVA lock. You should call
 * drm_gpuvm_bo_deferred_cleanup() later to complete the cleanup process.
 *
 * Returns: true if vm_bo is being destroyed, false otherwise.
 */
bool
drm_gpuvm_bo_put_deferred(struct drm_gpuvm_bo *vm_bo)
{
	if (!vm_bo)
		return false;

	drm_WARN_ON(vm_bo->vm->drm, !drm_gpuvm_immediate_mode(vm_bo->vm));

	return !!kref_put_mutex(&vm_bo->kref,
				drm_gpuvm_bo_defer_free,
				&vm_bo->obj->gpuva.lock);
}
EXPORT_SYMBOL_GPL(drm_gpuvm_bo_put_deferred);

/**
 * drm_gpuvm_bo_deferred_cleanup() - clean up BOs in the deferred list
 * deferred cleanup
 * @gpuvm: the VM to clean up
 *
 * Cleans up &drm_gpuvm_bo instances in the deferred cleanup list.
 */
void
drm_gpuvm_bo_deferred_cleanup(struct drm_gpuvm *gpuvm)
{
	const struct drm_gpuvm_ops *ops = gpuvm->ops;
	struct drm_gpuvm_bo *vm_bo;
	struct drm_gem_object *obj;
	struct llist_node *bo_defer;

	bo_defer = llist_del_all(&gpuvm->bo_defer);
	if (!bo_defer)
		return;

	if (drm_gpuvm_resv_protected(gpuvm)) {
		dma_resv_lock(drm_gpuvm_resv(gpuvm), NULL);
		llist_for_each_entry(vm_bo, bo_defer, list.entry.bo_defer) {
			drm_gpuvm_bo_list_del(vm_bo, extobj, false);
			drm_gpuvm_bo_list_del(vm_bo, evict, false);
		}
		dma_resv_unlock(drm_gpuvm_resv(gpuvm));
	}

	while (bo_defer) {
		vm_bo = llist_entry(bo_defer, struct drm_gpuvm_bo, list.entry.bo_defer);
		bo_defer = bo_defer->next;
		obj = vm_bo->obj;
		if (ops && ops->vm_bo_free)
			ops->vm_bo_free(vm_bo);
		else
			kfree(vm_bo);

		drm_gpuvm_put(gpuvm);
		drm_gem_object_put(obj);
	}
}
EXPORT_SYMBOL_GPL(drm_gpuvm_bo_deferred_cleanup);

static struct drm_gpuvm_bo *
__drm_gpuvm_bo_find(struct drm_gpuvm *gpuvm,
		    struct drm_gem_object *obj)
@@ -1949,6 +2105,40 @@ drm_gpuva_unlink(struct drm_gpuva *va)
}
EXPORT_SYMBOL_GPL(drm_gpuva_unlink);

/**
 * drm_gpuva_unlink_defer() - unlink a &drm_gpuva with deferred vm_bo cleanup
 * @va: the &drm_gpuva to unlink
 *
 * Similar to drm_gpuva_unlink(), but uses drm_gpuvm_bo_put_deferred() and takes
 * the lock for the caller.
 */
void
drm_gpuva_unlink_defer(struct drm_gpuva *va)
{
	struct drm_gem_object *obj = va->gem.obj;
	struct drm_gpuvm_bo *vm_bo = va->vm_bo;
	bool should_defer_bo;

	if (unlikely(!obj))
		return;

	drm_WARN_ON(vm_bo->vm->drm, !drm_gpuvm_immediate_mode(vm_bo->vm));

	mutex_lock(&obj->gpuva.lock);
	list_del_init(&va->gem.entry);

	/*
	 * This is drm_gpuvm_bo_put_deferred() except we already hold the mutex.
	 */
	should_defer_bo = kref_put(&vm_bo->kref, drm_gpuvm_bo_into_zombie);
	mutex_unlock(&obj->gpuva.lock);
	if (should_defer_bo)
		drm_gpuvm_bo_defer_zombie_cleanup(vm_bo);

	va->vm_bo = NULL;
}
EXPORT_SYMBOL_GPL(drm_gpuva_unlink_defer);

/**
 * drm_gpuva_find_first() - find the first &drm_gpuva in the given range
 * @gpuvm: the &drm_gpuvm to search in
+2 −0
Original line number Diff line number Diff line
config DRM_NOVA
	tristate "Nova DRM driver"
	depends on 64BIT
	depends on DRM=y
	depends on PCI
	depends on RUST
	select AUXILIARY_BUS
	select NOVA_CORE
	default n
	help
	  Choose this if you want to build the Nova DRM driver for Nvidia
+19 −91
Original line number Diff line number Diff line
@@ -182,20 +182,6 @@ struct panthor_vm_op_ctx {
		u64 range;
	} va;

	/**
	 * @returned_vmas: List of panthor_vma objects returned after a VM operation.
	 *
	 * For unmap operations, this will contain all VMAs that were covered by the
	 * specified VA range.
	 *
	 * For map operations, this will contain all VMAs that previously mapped to
	 * the specified VA range.
	 *
	 * Those VMAs, and the resources they point to will be released as part of
	 * the op_ctx cleanup operation.
	 */
	struct list_head returned_vmas;

	/** @map: Fields specific to a map operation. */
	struct {
		/** @map.vm_bo: Buffer object to map. */
@@ -1082,47 +1068,18 @@ void panthor_vm_free_va(struct panthor_vm *vm, struct drm_mm_node *va_node)
	mutex_unlock(&vm->mm_lock);
}

static void panthor_vm_bo_put(struct drm_gpuvm_bo *vm_bo)
static void panthor_vm_bo_free(struct drm_gpuvm_bo *vm_bo)
{
	struct panthor_gem_object *bo = to_panthor_bo(vm_bo->obj);
	struct drm_gpuvm *vm = vm_bo->vm;
	bool unpin;

	/* We must retain the GEM before calling drm_gpuvm_bo_put(),
	 * otherwise the mutex might be destroyed while we hold it.
	 * Same goes for the VM, since we take the VM resv lock.
	 */
	drm_gem_object_get(&bo->base.base);
	drm_gpuvm_get(vm);

	/* We take the resv lock to protect against concurrent accesses to the
	 * gpuvm evicted/extobj lists that are modified in
	 * drm_gpuvm_bo_destroy(), which is called if drm_gpuvm_bo_put()
	 * releases sthe last vm_bo reference.
	 * We take the BO GPUVA list lock to protect the vm_bo removal from the
	 * GEM vm_bo list.
	 */
	dma_resv_lock(drm_gpuvm_resv(vm), NULL);
	mutex_lock(&bo->base.base.gpuva.lock);
	unpin = drm_gpuvm_bo_put(vm_bo);
	mutex_unlock(&bo->base.base.gpuva.lock);
	dma_resv_unlock(drm_gpuvm_resv(vm));

	/* If the vm_bo object was destroyed, release the pin reference that
	 * was hold by this object.
	 */
	if (unpin && !drm_gem_is_imported(&bo->base.base))
	if (!drm_gem_is_imported(&bo->base.base))
		drm_gem_shmem_unpin(&bo->base);

	drm_gpuvm_put(vm);
	drm_gem_object_put(&bo->base.base);
	kfree(vm_bo);
}

static void panthor_vm_cleanup_op_ctx(struct panthor_vm_op_ctx *op_ctx,
				      struct panthor_vm *vm)
{
	struct panthor_vma *vma, *tmp_vma;

	u32 remaining_pt_count = op_ctx->rsvd_page_tables.count -
				 op_ctx->rsvd_page_tables.ptr;

@@ -1135,16 +1092,12 @@ static void panthor_vm_cleanup_op_ctx(struct panthor_vm_op_ctx *op_ctx,
	kfree(op_ctx->rsvd_page_tables.pages);

	if (op_ctx->map.vm_bo)
		panthor_vm_bo_put(op_ctx->map.vm_bo);
		drm_gpuvm_bo_put_deferred(op_ctx->map.vm_bo);

	for (u32 i = 0; i < ARRAY_SIZE(op_ctx->preallocated_vmas); i++)
		kfree(op_ctx->preallocated_vmas[i]);

	list_for_each_entry_safe(vma, tmp_vma, &op_ctx->returned_vmas, node) {
		list_del(&vma->node);
		panthor_vm_bo_put(vma->base.vm_bo);
		kfree(vma);
	}
	drm_gpuvm_bo_deferred_cleanup(&vm->base);
}

static void
@@ -1247,7 +1200,6 @@ static int panthor_vm_prepare_map_op_ctx(struct panthor_vm_op_ctx *op_ctx,
		return -EINVAL;

	memset(op_ctx, 0, sizeof(*op_ctx));
	INIT_LIST_HEAD(&op_ctx->returned_vmas);
	op_ctx->flags = flags;
	op_ctx->va.range = size;
	op_ctx->va.addr = va;
@@ -1258,7 +1210,9 @@ static int panthor_vm_prepare_map_op_ctx(struct panthor_vm_op_ctx *op_ctx,

	if (!drm_gem_is_imported(&bo->base.base)) {
		/* Pre-reserve the BO pages, so the map operation doesn't have to
		 * allocate.
		 * allocate. This pin is dropped in panthor_vm_bo_free(), so
		 * once we have successfully called drm_gpuvm_bo_create(),
		 * GPUVM will take care of dropping the pin for us.
		 */
		ret = drm_gem_shmem_pin(&bo->base);
		if (ret)
@@ -1297,16 +1251,6 @@ static int panthor_vm_prepare_map_op_ctx(struct panthor_vm_op_ctx *op_ctx,
	mutex_unlock(&bo->base.base.gpuva.lock);
	dma_resv_unlock(panthor_vm_resv(vm));

	/* If the a vm_bo for this <VM,BO> combination exists, it already
	 * retains a pin ref, and we can release the one we took earlier.
	 *
	 * If our pre-allocated vm_bo is picked, it now retains the pin ref,
	 * which will be released in panthor_vm_bo_put().
	 */
	if (preallocated_vm_bo != op_ctx->map.vm_bo &&
	    !drm_gem_is_imported(&bo->base.base))
		drm_gem_shmem_unpin(&bo->base);

	op_ctx->map.bo_offset = offset;

	/* L1, L2 and L3 page tables.
@@ -1354,7 +1298,6 @@ static int panthor_vm_prepare_unmap_op_ctx(struct panthor_vm_op_ctx *op_ctx,
	int ret;

	memset(op_ctx, 0, sizeof(*op_ctx));
	INIT_LIST_HEAD(&op_ctx->returned_vmas);
	op_ctx->va.range = size;
	op_ctx->va.addr = va;
	op_ctx->flags = DRM_PANTHOR_VM_BIND_OP_TYPE_UNMAP;
@@ -1402,7 +1345,6 @@ static void panthor_vm_prepare_sync_only_op_ctx(struct panthor_vm_op_ctx *op_ctx
						struct panthor_vm *vm)
{
	memset(op_ctx, 0, sizeof(*op_ctx));
	INIT_LIST_HEAD(&op_ctx->returned_vmas);
	op_ctx->flags = DRM_PANTHOR_VM_BIND_OP_TYPE_SYNC_ONLY;
}

@@ -2048,26 +1990,13 @@ static void panthor_vma_link(struct panthor_vm *vm,

	mutex_lock(&bo->base.base.gpuva.lock);
	drm_gpuva_link(&vma->base, vm_bo);
	drm_WARN_ON(&vm->ptdev->base, drm_gpuvm_bo_put(vm_bo));
	mutex_unlock(&bo->base.base.gpuva.lock);
}

static void panthor_vma_unlink(struct panthor_vm *vm,
			       struct panthor_vma *vma)
static void panthor_vma_unlink(struct panthor_vma *vma)
{
	struct panthor_gem_object *bo = to_panthor_bo(vma->base.gem.obj);
	struct drm_gpuvm_bo *vm_bo = drm_gpuvm_bo_get(vma->base.vm_bo);

	mutex_lock(&bo->base.base.gpuva.lock);
	drm_gpuva_unlink(&vma->base);
	mutex_unlock(&bo->base.base.gpuva.lock);

	/* drm_gpuva_unlink() release the vm_bo, but we manually retained it
	 * when entering this function, so we can implement deferred VMA
	 * destruction. Re-assign it here.
	 */
	vma->base.vm_bo = vm_bo;
	list_add_tail(&vma->node, &vm->op_ctx->returned_vmas);
	drm_gpuva_unlink_defer(&vma->base);
	kfree(vma);
}

static void panthor_vma_init(struct panthor_vma *vma, u32 flags)
@@ -2101,12 +2030,12 @@ static int panthor_gpuva_sm_step_map(struct drm_gpuva_op *op, void *priv)
		return ret;
	}

	/* Ref owned by the mapping now, clear the obj field so we don't release the
	 * pinning/obj ref behind GPUVA's back.
	 */
	drm_gpuva_map(&vm->base, &vma->base, &op->map);
	panthor_vma_link(vm, vma, op_ctx->map.vm_bo);

	drm_gpuvm_bo_put_deferred(op_ctx->map.vm_bo);
	op_ctx->map.vm_bo = NULL;

	return 0;
}

@@ -2145,16 +2074,14 @@ static int panthor_gpuva_sm_step_remap(struct drm_gpuva_op *op,
		 * owned by the old mapping which will be released when this
		 * mapping is destroyed, we need to grab a ref here.
		 */
		panthor_vma_link(vm, prev_vma,
				 drm_gpuvm_bo_get(op->remap.unmap->va->vm_bo));
		panthor_vma_link(vm, prev_vma, op->remap.unmap->va->vm_bo);
	}

	if (next_vma) {
		panthor_vma_link(vm, next_vma,
				 drm_gpuvm_bo_get(op->remap.unmap->va->vm_bo));
		panthor_vma_link(vm, next_vma, op->remap.unmap->va->vm_bo);
	}

	panthor_vma_unlink(vm, unmap_vma);
	panthor_vma_unlink(unmap_vma);
	return 0;
}

@@ -2171,12 +2098,13 @@ static int panthor_gpuva_sm_step_unmap(struct drm_gpuva_op *op,
		return ret;

	drm_gpuva_unmap(&op->unmap);
	panthor_vma_unlink(vm, unmap_vma);
	panthor_vma_unlink(unmap_vma);
	return 0;
}

static const struct drm_gpuvm_ops panthor_gpuvm_ops = {
	.vm_free = panthor_vm_free,
	.vm_bo_free = panthor_vm_bo_free,
	.sm_step_map = panthor_gpuva_sm_step_map,
	.sm_step_remap = panthor_gpuva_sm_step_remap,
	.sm_step_unmap = panthor_gpuva_sm_step_unmap,
Loading