Commit 378fe94c authored by Shashank Sharma's avatar Shashank Sharma Committed by Guo Mengqi
Browse files

drm/amdgpu: change vm->task_info handling

mainline inclusion
from mainline-v6.9-rc1
commit b8f67b9ddf4f8fe6dd536590712b5912ad78f99c
category: bugfix
bugzilla: https://gitee.com/src-openeuler/kernel/issues/IADDFV
CVE: CVE-2024-41008

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b8f67b9ddf4f8fe6dd536590712b5912ad78f99c



-----------------------------------------------------------

This patch changes the handling and lifecycle of vm->task_info object.
The major changes are:
- vm->task_info is a dynamically allocated ptr now, and its uasge is
  reference counted.
- introducing two new helper funcs for task_info lifecycle management
    - amdgpu_vm_get_task_info: reference counts up task_info before
      returning this info
    - amdgpu_vm_put_task_info: reference counts down task_info
- last put to task_info() frees task_info from the vm.

This patch also does logistical changes required for existing usage
of vm->task_info.

V2: Do not block all the prints when task_info not found (Felix)

V3: Fixed review comments from Felix
   - Fix wrong indentation
   - No debug message for -ENOMEM
   - Add NULL check for task_info
   - Do not duplicate the debug messages (ti vs no ti)
   - Get first reference of task_info in vm_init(), put last
     in vm_fini()

V4: Fixed review comments from Felix
   - fix double reference increment in create_task_info
   - change amdgpu_vm_get_task_info_pasid
   - additional changes in amdgpu_gem.c while porting

Cc: Christian Koenig <christian.koenig@amd.com>
Cc: Alex Deucher <alexander.deucher@amd.com>
Cc: Felix Kuehling <Felix.Kuehling@amd.com>
Reviewed-by: default avatarFelix Kuehling <Felix.Kuehling@amd.com>
Signed-off-by: default avatarShashank Sharma <shashank.sharma@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
Conflicts:
	drivers/gpu/drm/amd/amdgpu/amdgpu_vm_pt.c
	drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c
	drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c
	drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c
	drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
	drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
	drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c
	drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h
	drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c
	drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c
	drivers/gpu/drm/amd/amdkfd/kfd_smi_events.c
	drivers/gpu/drm/amd/amdgpu/amdgpu_reset.c
	drivers/gpu/drm/amd/amdgpu/amdgpu_job.c
[1. Some drivers does not use struct task_info, so they are
no need to modify 2. use idr instead of xarray
in amdgpu_vm_get_vm_from_pasid(), because there are no functional
changes]
Signed-off-by: default avatarZhang Zekun <zhangzekun11@huawei.com>
parent 47f16eba
Loading
Loading
Loading
Loading
+10 −8
Original line number Diff line number Diff line
@@ -32,11 +32,9 @@ static void amdgpu_job_timedout(struct drm_sched_job *s_job)
{
	struct amdgpu_ring *ring = to_amdgpu_ring(s_job->sched);
	struct amdgpu_job *job = to_amdgpu_job(s_job);
	struct amdgpu_task_info ti;
	struct amdgpu_task_info *ti;
	struct amdgpu_device *adev = ring->adev;

	memset(&ti, 0, sizeof(struct amdgpu_task_info));

	if (amdgpu_gpu_recovery &&
	    amdgpu_ring_soft_recovery(ring, job->vmid, s_job->s_fence->parent)) {
		DRM_ERROR("ring %s timeout, but soft recovered\n",
@@ -44,12 +42,16 @@ static void amdgpu_job_timedout(struct drm_sched_job *s_job)
		return;
	}

	amdgpu_vm_get_task_info(ring->adev, job->pasid, &ti);
	DRM_ERROR("ring %s timeout, signaled seq=%u, emitted seq=%u\n",
		   job->base.sched->name, atomic_read(&ring->fence_drv.last_seq),
		   ring->fence_drv.sync_seq);

	ti = amdgpu_vm_get_task_info_pasid(ring->adev, job->pasid);
	if (ti) {
		DRM_ERROR("Process information: process %s pid %d thread %s pid %d\n",
		  ti.process_name, ti.tgid, ti.task_name, ti.pid);
			  ti->process_name, ti->tgid, ti->task_name, ti->pid);
		amdgpu_vm_put_task_info(ti);
	}

	if (amdgpu_device_should_recover_gpu(ring->adev)) {
		amdgpu_device_gpu_recover(ring->adev, job);
+115 −46
Original line number Diff line number Diff line
@@ -1525,16 +1525,20 @@ static int amdgpu_vm_update_ptes(struct amdgpu_vm_update_params *params,
			uint64_t upd_end = min(entry_end, frag_end);
			unsigned nptes = (upd_end - frag_start) >> shift;
			uint64_t upd_flags = flags | AMDGPU_PTE_FRAG(frag);
			struct amdgpu_task_info *ti = amdgpu_vm_get_task_info_vm(vm);

			/* This can happen when we set higher level PDs to
			 * silent to stop fault floods.
			 */
			nptes = max(nptes, 1u);

			if (ti) {
				trace_amdgpu_vm_update_ptes(params, frag_start, upd_end,
							    nptes, dst, incr, upd_flags,
						    vm->task_info.pid,
							    ti->pid,
							    vm->immediate.fence_context);
				amdgpu_vm_put_task_info(ti);
			}
			amdgpu_vm_update_flags(params, pt, cursor.level,
					       pe_start, dst, nptes, incr,
					       upd_flags);
@@ -2819,6 +2823,108 @@ long amdgpu_vm_wait_idle(struct amdgpu_vm *vm, long timeout)
	return dma_fence_wait_timeout(vm->last_unlocked, true, timeout);
}

static void amdgpu_vm_destroy_task_info(struct kref *kref)
{
	struct amdgpu_task_info *ti = container_of(kref, struct amdgpu_task_info, refcount);

	kfree(ti);
}

static inline struct amdgpu_vm *
amdgpu_vm_get_vm_from_pasid(struct amdgpu_device *adev, u32 pasid)
{
	struct amdgpu_vm *vm;
	unsigned long flags;

	spin_lock_irqsave(&adev->vm_manager.pasid_lock, flags);
	vm = idr_find(&adev->vm_manager.pasid_idr, pasid);
	spin_unlock_irqrestore(&adev->vm_manager.pasid_lock, flags);

	return vm;
}

/**
 * amdgpu_vm_put_task_info - reference down the vm task_info ptr
 *
 * @task_info: task_info struct under discussion.
 *
 * frees the vm task_info ptr at the last put
 */
void amdgpu_vm_put_task_info(struct amdgpu_task_info *task_info)
{
	kref_put(&task_info->refcount, amdgpu_vm_destroy_task_info);
}

/**
 * amdgpu_vm_get_task_info_vm - Extracts task info for a vm.
 *
 * @vm: VM to get info from
 *
 * Returns the reference counted task_info structure, which must be
 * referenced down with amdgpu_vm_put_task_info.
 */
struct amdgpu_task_info *
amdgpu_vm_get_task_info_vm(struct amdgpu_vm *vm)
{
	struct amdgpu_task_info *ti = NULL;

	if (vm) {
		ti = vm->task_info;
		kref_get(&vm->task_info->refcount);
	}

	return ti;
}

/**
 * amdgpu_vm_get_task_info_pasid - Extracts task info for a PASID.
 *
 * @adev: drm device pointer
 * @pasid: PASID identifier for VM
 *
 * Returns the reference counted task_info structure, which must be
 * referenced down with amdgpu_vm_put_task_info.
 */
struct amdgpu_task_info *
amdgpu_vm_get_task_info_pasid(struct amdgpu_device *adev, u32 pasid)
{
	return amdgpu_vm_get_task_info_vm(
			amdgpu_vm_get_vm_from_pasid(adev, pasid));
}

static int amdgpu_vm_create_task_info(struct amdgpu_vm *vm)
{
	vm->task_info = kzalloc(sizeof(struct amdgpu_task_info), GFP_KERNEL);
	if (!vm->task_info)
		return -ENOMEM;

	kref_init(&vm->task_info->refcount);
	return 0;
}

/**
 * amdgpu_vm_set_task_info - Sets VMs task info.
 *
 * @vm: vm for which to set the info
 */
void amdgpu_vm_set_task_info(struct amdgpu_vm *vm)
{
	if (!vm->task_info)
		return;

	if (vm->task_info->pid == current->pid)
		return;

	vm->task_info->pid = current->pid;
	get_task_comm(vm->task_info->task_name, current);

	if (current->group_leader->mm != current->mm)
		return;

	vm->task_info->tgid = current->group_leader->pid;
	get_task_comm(vm->task_info->process_name, current->group_leader);
}

/**
 * amdgpu_vm_init - initialize a vm instance
 *
@@ -2914,6 +3020,10 @@ int amdgpu_vm_init(struct amdgpu_device *adev, struct amdgpu_vm *vm,
	if (r)
		goto error_unreserve;

	r = amdgpu_vm_create_task_info(vm);
	if (r)
		DRM_DEBUG("Failed to create task info for VM\n");

	amdgpu_bo_unreserve(vm->root.base.bo);

	if (pasid) {
@@ -3141,6 +3251,7 @@ void amdgpu_vm_fini(struct amdgpu_device *adev, struct amdgpu_vm *vm)

	root = amdgpu_bo_ref(vm->root.base.bo);
	amdgpu_bo_reserve(root, true);
	amdgpu_vm_put_task_info(vm->task_info);
	if (vm->pasid) {
		unsigned long flags;

@@ -3309,48 +3420,6 @@ int amdgpu_vm_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
	return 0;
}

/**
 * amdgpu_vm_get_task_info - Extracts task info for a PASID.
 *
 * @adev: drm device pointer
 * @pasid: PASID identifier for VM
 * @task_info: task_info to fill.
 */
void amdgpu_vm_get_task_info(struct amdgpu_device *adev, u32 pasid,
			 struct amdgpu_task_info *task_info)
{
	struct amdgpu_vm *vm;
	unsigned long flags;

	spin_lock_irqsave(&adev->vm_manager.pasid_lock, flags);

	vm = idr_find(&adev->vm_manager.pasid_idr, pasid);
	if (vm)
		*task_info = vm->task_info;

	spin_unlock_irqrestore(&adev->vm_manager.pasid_lock, flags);
}

/**
 * amdgpu_vm_set_task_info - Sets VMs task info.
 *
 * @vm: vm for which to set the info
 */
void amdgpu_vm_set_task_info(struct amdgpu_vm *vm)
{
	if (vm->task_info.pid)
		return;

	vm->task_info.pid = current->pid;
	get_task_comm(vm->task_info.task_name, current);

	if (current->group_leader->mm != current->mm)
		return;

	vm->task_info.tgid = current->group_leader->pid;
	get_task_comm(vm->task_info.process_name, current->group_leader);
}

/**
 * amdgpu_vm_handle_fault - graceful handling of VM faults.
 * @adev: amdgpu device pointer
+14 −7
Original line number Diff line number Diff line
@@ -184,6 +184,7 @@ struct amdgpu_task_info {
	char		task_name[TASK_COMM_LEN];
	pid_t		pid;
	pid_t		tgid;
	struct kref	refcount;
};

/**
@@ -311,7 +312,7 @@ struct amdgpu_vm {
	uint64_t		pd_phys_addr;

	/* Some basic info about the task */
	struct amdgpu_task_info task_info;
	struct amdgpu_task_info *task_info;

	/* Store positions of group of BOs */
	struct ttm_lru_bulk_move lru_bulk_move;
@@ -431,8 +432,14 @@ bool amdgpu_vm_need_pipeline_sync(struct amdgpu_ring *ring,
				  struct amdgpu_job *job);
void amdgpu_vm_check_compute_bug(struct amdgpu_device *adev);

void amdgpu_vm_get_task_info(struct amdgpu_device *adev, u32 pasid,
			     struct amdgpu_task_info *task_info);
struct amdgpu_task_info *
amdgpu_vm_get_task_info_pasid(struct amdgpu_device *adev, u32 pasid);

struct amdgpu_task_info *
amdgpu_vm_get_task_info_vm(struct amdgpu_vm *vm);

void amdgpu_vm_put_task_info(struct amdgpu_task_info *task_info);

bool amdgpu_vm_handle_fault(struct amdgpu_device *adev, u32 pasid,
			    uint64_t addr);

+13 −9
Original line number Diff line number Diff line
@@ -114,18 +114,22 @@ static int gmc_v10_0_process_interrupt(struct amdgpu_device *adev,
	}

	if (printk_ratelimit()) {
		struct amdgpu_task_info task_info;

		memset(&task_info, 0, sizeof(struct amdgpu_task_info));
		amdgpu_vm_get_task_info(adev, entry->pasid, &task_info);
		struct amdgpu_task_info *task_info;

		dev_err(adev->dev,
			"[%s] page fault (src_id:%u ring:%u vmid:%u pasid:%u, "
			"for process %s pid %d thread %s pid %d)\n",
			"[%s] page fault (src_id:%u ring:%u vmid:%u pasid:%u)\n",
			entry->vmid_src ? "mmhub" : "gfxhub",
			entry->src_id, entry->ring_id, entry->vmid,
			entry->pasid, task_info.process_name, task_info.tgid,
			task_info.task_name, task_info.pid);
			entry->src_id, entry->ring_id, entry->vmid, entry->pasid);

		task_info = amdgpu_vm_get_task_info_pasid(adev, entry->pasid);
		if (task_info) {
			dev_err(adev->dev,
				" in process %s pid %d thread %s pid %d\n",
				task_info->process_name, task_info->tgid,
				task_info->task_name, task_info->pid);
			amdgpu_vm_put_task_info(task_info);
		}

		dev_err(adev->dev, "  in page starting at address 0x%016llx from client %d\n",
			addr, entry->client_id);
		if (!amdgpu_sriov_vf(adev))
+13 −7
Original line number Diff line number Diff line
@@ -1469,18 +1469,24 @@ static int gmc_v8_0_process_interrupt(struct amdgpu_device *adev,
		gmc_v8_0_set_fault_enable_default(adev, false);

	if (printk_ratelimit()) {
		struct amdgpu_task_info task_info;
		struct amdgpu_task_info *task_info;

		memset(&task_info, 0, sizeof(struct amdgpu_task_info));
		amdgpu_vm_get_task_info(adev, entry->pasid, &task_info);
		dev_err(adev->dev, "GPU fault detected: %d 0x%08x\n",
			entry->src_id, entry->src_data[0]);

		task_info = amdgpu_vm_get_task_info_pasid(adev, entry->pasid);
		if (task_info) {
			dev_err(adev->dev, " for process %s pid %d thread %s pid %d\n",
				task_info->process_name, task_info->tgid,
				task_info->task_name, task_info->pid);
			amdgpu_vm_put_task_info(task_info);
		}

		dev_err(adev->dev, "GPU fault detected: %d 0x%08x for process %s pid %d thread %s pid %d\n",
			entry->src_id, entry->src_data[0], task_info.process_name,
			task_info.tgid, task_info.task_name, task_info.pid);
		dev_err(adev->dev, "  VM_CONTEXT1_PROTECTION_FAULT_ADDR   0x%08X\n",
				addr);
		dev_err(adev->dev, "  VM_CONTEXT1_PROTECTION_FAULT_STATUS 0x%08X\n",
			status);

		gmc_v8_0_vm_decode_fault(adev, status, addr, mc_client,
					 entry->pasid);
	}
Loading