Commit dd600db4 authored by Dani Liberman's avatar Dani Liberman Committed by Oded Gabbay
Browse files

habanalabs: add page fault info uapi



Only the first page fault will be saved.
Besides the address which caused the page fault, the driver captures
all of the mmu user mappings.
User can retrieve this data via the new uapi (new opcode in INFO ioctl).

Signed-off-by: default avatarDani Liberman <dliberman@habana.ai>
Reviewed-by: default avatarOded Gabbay <ogabbay@kernel.org>
Signed-off-by: default avatarOded Gabbay <ogabbay@kernel.org>
parent 6d1c567f
Loading
Loading
Loading
Loading
+58 −0
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@

#include <linux/pci.h>
#include <linux/hwmon.h>
#include <linux/vmalloc.h>

#include <trace/events/habanalabs.h>

@@ -2199,6 +2200,8 @@ void hl_device_fini(struct hl_device *hdev)

	hl_mmu_fini(hdev);

	vfree(hdev->captured_err_info.pgf_info.user_mappings);

	hl_eq_fini(hdev, &hdev->event_queue);

	kfree(hdev->shadow_cs_queue);
@@ -2275,3 +2278,58 @@ void hl_capture_razwi(struct hl_device *hdev, u64 addr, u16 *engine_id, u16 num_
			num_of_engines * sizeof(u16));
	hdev->captured_err_info.razwi.flags = flags;
}
static void hl_capture_user_mappings(struct hl_device *hdev)
{
	struct page_fault_info *pgf_info = &hdev->captured_err_info.pgf_info;
	struct hl_vm_hash_node *hnode;
	struct hl_userptr *userptr;
	struct hl_ctx *ctx;
	u32 map_idx = 0;
	int i;

	ctx = hl_get_compute_ctx(hdev);
	if (!ctx) {
		dev_err(hdev->dev, "Can't get user context for user mappings\n");
		return;
	}

	mutex_lock(&ctx->mem_hash_lock);
	hash_for_each(ctx->mem_hash, i, hnode, node)
	pgf_info->num_of_user_mappings++;

	if (!pgf_info->num_of_user_mappings)
		goto finish;

	/* In case we already allocated in previous session, need to release it before
	 * allocating new buffer.
	 */
	vfree(pgf_info->user_mappings);
	pgf_info->user_mappings =
			vmalloc(pgf_info->num_of_user_mappings * sizeof(struct hl_user_mapping));
	if (!pgf_info->user_mappings) {
		pgf_info->num_of_user_mappings = 0;
		goto finish;
	}

	hash_for_each(ctx->mem_hash, i, hnode, node) {
		userptr = hnode->ptr;
		pgf_info->user_mappings[map_idx].dev_va = hnode->vaddr;
		pgf_info->user_mappings[map_idx].size = userptr->size;
		map_idx++;
	}
finish:
	mutex_unlock(&ctx->mem_hash_lock);
	hl_ctx_put(ctx);
}

void hl_capture_page_fault(struct hl_device *hdev, u64 addr, u16 eng_id, bool is_pmmu)
{
	/* Capture only the first page fault */
	if (atomic_cmpxchg(&hdev->captured_err_info.pgf_info_recorded, 0, 1))
		return;

	hdev->captured_err_info.pgf_info.pgf.timestamp = ktime_to_ns(ktime_get());
	hdev->captured_err_info.pgf_info.pgf.addr = addr;
	hdev->captured_err_info.pgf_info.pgf.engine_id = eng_id;
	hl_capture_user_mappings(hdev);
}
+21 −1
Original line number Diff line number Diff line
@@ -2957,19 +2957,38 @@ struct undefined_opcode_info {
	bool write_enable;
};

/**
 * struct page_fault_info - info about page fault
 * @pgf_info: page fault information.
 * @user_mappings: buffer containing user mappings.
 * @num_of_user_mappings: number of user mappings.
 */
struct page_fault_info {
	struct hl_page_fault_info	pgf;
	struct hl_user_mapping		*user_mappings;
	u64				num_of_user_mappings;
};

/**
 * struct hl_error_info - holds information collected during an error.
 * @cs_timeout: CS timeout error information.
 * @razwi: razwi information.
 * @razwi_info_recorded: if set writing to razwi information is enabled.
 *                otherwise - disabled, so the first (root cause) razwi will not be overwritten.
 *                       otherwise - disabled, so the first (root cause) razwi will not be
 *                       overwritten.
 * @undef_opcode: undefined opcode information
 * @pgf_info: page fault information.
 * @pgf_info_recorded: if set writing to page fault information is enabled.
 *                     otherwise - disabled, so the first (root cause) page fault will not be
 *                     overwritten.
 */
struct hl_error_info {
	struct cs_timeout_info		cs_timeout;
	struct hl_info_razwi_event	razwi;
	atomic_t			razwi_info_recorded;
	struct undefined_opcode_info	undef_opcode;
	struct page_fault_info		pgf_info;
	atomic_t			pgf_info_recorded;
};

/**
@@ -3781,6 +3800,7 @@ hl_mmap_mem_buf_alloc(struct hl_mem_mgr *mmg,
__printf(2, 3) void hl_engine_data_sprintf(struct engines_data *e, const char *fmt, ...);
void hl_capture_razwi(struct hl_device *hdev, u64 addr, u16 *engine_id, u16 num_of_engines,
			u8 flags);
void hl_capture_page_fault(struct hl_device *hdev, u64 addr, u16 eng_id, bool is_pmmu);

#ifdef CONFIG_DEBUG_FS

+1 −0
Original line number Diff line number Diff line
@@ -213,6 +213,7 @@ int hl_device_open(struct inode *inode, struct file *filp)

	atomic_set(&hdev->captured_err_info.cs_timeout.write_enable, 1);
	atomic_set(&hdev->captured_err_info.razwi_info_recorded, 0);
	atomic_set(&hdev->captured_err_info.pgf_info_recorded, 0);
	hdev->captured_err_info.undef_opcode.write_enable = true;

	hdev->open_counter++;
+42 −0
Original line number Diff line number Diff line
@@ -778,6 +778,42 @@ static int engine_status_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
	return rc;
}

static int page_fault_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
{
	struct hl_device *hdev = hpriv->hdev;
	u32 max_size = args->return_size;
	struct hl_page_fault_info *info = &hdev->captured_err_info.pgf_info.pgf;
	void __user *out = (void __user *) (uintptr_t) args->return_pointer;

	if ((!max_size) || (!out))
		return -EINVAL;

	return copy_to_user(out, info, min_t(size_t, max_size, sizeof(struct hl_page_fault_info)))
				? -EFAULT : 0;
}

static int user_mappings_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
{
	void __user *out = (void __user *) (uintptr_t) args->return_pointer;
	u32 user_buf_size = args->return_size;
	struct hl_device *hdev = hpriv->hdev;
	struct page_fault_info *pgf_info;
	u64 actual_size;

	pgf_info = &hdev->captured_err_info.pgf_info;
	args->array_size = pgf_info->num_of_user_mappings;

	if (!out)
		return -EINVAL;

	actual_size = pgf_info->num_of_user_mappings * sizeof(struct hl_user_mapping);
	if (user_buf_size < actual_size)
		return -ENOMEM;

	return copy_to_user(out, pgf_info->user_mappings, min_t(size_t, user_buf_size, actual_size))
				? -EFAULT : 0;
}

static int _hl_info_ioctl(struct hl_fpriv *hpriv, void *data,
				struct device *dev)
{
@@ -837,6 +873,12 @@ static int _hl_info_ioctl(struct hl_fpriv *hpriv, void *data,
	case HL_INFO_GET_EVENTS:
		return events_info(hpriv, args);

	case HL_INFO_PAGE_FAULT_EVENT:
		return page_fault_info(hpriv, args);

	case HL_INFO_USER_MAPPINGS:
		return user_mappings_info(hpriv, args);

	default:
		break;
	}
+2 −0
Original line number Diff line number Diff line
@@ -6755,6 +6755,8 @@ static void gaudi_print_and_get_mmu_error_info(struct hl_device *hdev, u64 *addr
		*addr |= RREG32(mmMMU_UP_PAGE_ERROR_CAPTURE_VA);

		dev_err_ratelimited(hdev->dev, "MMU page fault on va 0x%llx\n", *addr);
		hl_capture_page_fault(hdev, *addr, 0, true);

		WREG32(mmMMU_UP_PAGE_ERROR_CAPTURE, 0);
	}

Loading