Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bccd122b authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: kgsl: Add panic notifier support for GPU"

parents 131cada2 ad1aed2a
Loading
Loading
Loading
Loading
+14 −2
Original line number Diff line number Diff line
@@ -2914,7 +2914,13 @@ static void adreno_regread(struct kgsl_device *device, unsigned int offsetwords,
			offsetwords, device->reg_len >> 2))
		return;

	if (!in_interrupt())
	/*
	 * kgsl panic notifier will be called in atomic context to get
	 * GPU snapshot. Also panic handler will skip snapshot dumping
	 * incase GPU is in SLUMBER state. So we can safely ignore the
	 * kgsl_pre_hwaccess().
	 */
	if (!device->snapshot_atomic && !in_interrupt())
		kgsl_pre_hwaccess(device);

	*value = readl_relaxed(device->reg_virt + (offsetwords << 2));
@@ -2938,7 +2944,13 @@ static void adreno_regwrite(struct kgsl_device *device,
			offsetwords, device->reg_len >> 2))
		return;

	if (!in_interrupt())
	/*
	 * kgsl panic notifier will be called in atomic context to get
	 * GPU snapshot. Also panic handler will skip snapshot dumping
	 * incase GPU is in SLUMBER state. So we can safely ignore the
	 * kgsl_pre_hwaccess().
	 */
	if (!device->snapshot_atomic && !in_interrupt())
		kgsl_pre_hwaccess(device);

	trace_kgsl_regwrite(device, offsetwords, value);
+4 −2
Original line number Diff line number Diff line
@@ -1591,6 +1591,7 @@ static void _a6xx_do_crashdump(struct kgsl_device *device)

	timeout = ktime_add_ms(ktime_get(), CP_CRASH_DUMPER_TIMEOUT);

	if (!device->snapshot_atomic)
		might_sleep();

	for (;;) {
@@ -1603,6 +1604,7 @@ static void _a6xx_do_crashdump(struct kgsl_device *device)
			break;

		/* Wait 1msec to avoid unnecessary looping */
		if (!device->snapshot_atomic)
			usleep_range(100, 1000);
	}

+3 −0
Original line number Diff line number Diff line
@@ -846,6 +846,9 @@ void adreno_snapshot(struct kgsl_device *device, struct kgsl_snapshot *snapshot,
		adreno_snapshot_ringbuffer(device, snapshot,
			adreno_dev->next_rb);

	if (device->snapshot_atomic)
		return;

	/* Dump selected global buffers */
	kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_GPU_OBJECT_V2,
			snapshot, adreno_snapshot_global, device->memstore);
+7 −1
Original line number Diff line number Diff line
@@ -268,13 +268,19 @@ struct kgsl_device {
	} snapshot_memory;

	struct kgsl_snapshot *snapshot;
	/** @panic_nb: notifier block to capture GPU snapshot on kernel panic */
	struct notifier_block panic_nb;
	struct {
		void *ptr;
		u32 size;
	} snapshot_memory_atomic;

	u32 snapshot_faultcount;	/* Total number of faults since boot */
	bool force_panic;		/* Force panic after snapshot dump */
	bool skip_ib_capture;		/* Skip IB capture after snapshot */
	bool prioritize_unrecoverable;	/* Overwrite with new GMU snapshots */
	bool set_isdb_breakpoint;	/* Set isdb registers before snapshot */

	bool snapshot_atomic;		/* To capture snapshot in atomic context*/
	/* Use CP Crash dumper to get GPU snapshot*/
	bool snapshot_crashdumper;
	/* Use HOST side register reads to get GPU snapshot*/
+103 −0
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@
 * Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
 */

#include <linux/notifier.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/utsname.h>
@@ -671,6 +672,86 @@ static void set_isdb_breakpoint_registers(struct kgsl_device *device)
	device->force_panic = false;
}

static void kgsl_device_snapshot_atomic(struct kgsl_device *device)
{
	struct kgsl_snapshot_header *header;
	struct kgsl_snapshot *snapshot;
	phys_addr_t pa;

	device->snapshot_atomic = true;

	if (device->snapshot && device->force_panic)
		return;

	if (!atomic_read(&device->active_cnt)) {
		dev_err(device->dev, "snapshot: device is powered off\n");
		return;
	}

	device->snapshot_memory_atomic.size = device->snapshot_memory.size;
	device->snapshot_memory_atomic.ptr = devm_kzalloc(&device->pdev->dev,
			device->snapshot_memory_atomic.size, GFP_ATOMIC);

	if (!device->snapshot_memory_atomic.ptr) {
		dev_err(device->dev,
			"KGSL failed to allocate memory for atomic snapshot\n");
		return;
	}

	/* Allocate memory for the snapshot instance */
	snapshot = kzalloc(sizeof(*snapshot), GFP_ATOMIC);
	if (snapshot == NULL)
		return;

	INIT_LIST_HEAD(&snapshot->obj_list);
	INIT_LIST_HEAD(&snapshot->cp_list);

	snapshot->start = device->snapshot_memory_atomic.ptr;
	snapshot->ptr = device->snapshot_memory_atomic.ptr;
	snapshot->remain = device->snapshot_memory_atomic.size;

	header = (struct kgsl_snapshot_header *) snapshot->ptr;

	header->magic = SNAPSHOT_MAGIC;
	header->gpuid = kgsl_gpuid(device, &header->chipid);

	snapshot->ptr += sizeof(*header);
	snapshot->remain -= sizeof(*header);
	snapshot->size += sizeof(*header);

	/* Build the Linux specific header */
	kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_OS,
			snapshot, snapshot_os_no_ctxt, NULL);

	/*
	 * Trigger both GPU and GMU snapshot. GPU specific code
	 * will take care of whether to dumps full state or only
	 * GMU state based on current GPU power state.
	 */
	if (device->ftbl->snapshot)
		device->ftbl->snapshot(device, snapshot, NULL);

	if (msm_minidump_enabled()) {
		struct md_region md_entry;
		int ret;

		scnprintf(md_entry.name, sizeof(md_entry.name),
				"ATOMIC_GPU_SNAPSHOT");
		md_entry.virt_addr = (u64)(device->snapshot_memory_atomic.ptr);
		md_entry.phys_addr = __pa(device->snapshot_memory_atomic.ptr);
		md_entry.size = device->snapshot_memory_atomic.size;
		ret = msm_minidump_add_region(&md_entry);
		if (ret < 0)
			dev_err(device->dev,
				"Fail to add atomic snapshot with minidump: %d\n", ret);
	}

	/* log buffer info to aid in ramdump fault tolerance */
	pa = __pa(device->snapshot_memory_atomic.ptr);
	dev_err(device->dev, "Atomic GPU snapshot created at pa %pa++0x%zx\n",
			&pa, snapshot->size);
}

/**
 * kgsl_snapshot() - construct a device snapshot
 * @device: device to snapshot
@@ -1144,6 +1225,19 @@ static const struct attribute *snapshot_attrs[] = {
	NULL,
};

static int kgsl_panic_notifier_callback(struct notifier_block *nb,
		unsigned long action, void *unused)
{
	struct kgsl_device *device = container_of(nb, struct kgsl_device,
							panic_nb);

	/* To send NMI to GMU */
	device->gmu_fault = true;
	kgsl_device_snapshot_atomic(device);

	return NOTIFY_OK;
}

void kgsl_device_snapshot_probe(struct kgsl_device *device, u32 size)
{
	device->snapshot_memory.size = size;
@@ -1174,6 +1268,10 @@ void kgsl_device_snapshot_probe(struct kgsl_device *device, u32 size)
	device->snapshot_crashdumper = true;
	device->snapshot_legacy = false;

	device->snapshot_atomic = false;
	device->panic_nb.notifier_call = kgsl_panic_notifier_callback;
	device->panic_nb.priority = 1;

	/*
	 * Set this to false so that we only ever keep the first snapshot around
	 * If we want to over-write with a gmu snapshot, then set it to true
@@ -1187,6 +1285,8 @@ void kgsl_device_snapshot_probe(struct kgsl_device *device, u32 size)

	WARN_ON(sysfs_create_bin_file(&device->snapshot_kobj, &snapshot_attr));
	WARN_ON(sysfs_create_files(&device->snapshot_kobj, snapshot_attrs));
	atomic_notifier_chain_register(&panic_notifier_list,
			&device->panic_nb);
}

/**
@@ -1209,6 +1309,9 @@ void kgsl_device_snapshot_close(struct kgsl_device *device)
			dev_err(device->dev, "Failed to remove snapshot with minidump\n");
	}

	atomic_notifier_chain_unregister(&panic_notifier_list,
			&device->panic_nb);

	sysfs_remove_bin_file(&device->snapshot_kobj, &snapshot_attr);
	sysfs_remove_files(&device->snapshot_kobj, snapshot_attrs);