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

This commit is contained in:
qctecmdr 2021-04-12 23:41:43 -07:00 • committed by Gerrit - the friendly Code Review server
commit bccd122b9d
5 changed files with 131 additions and 5 deletions

View file

@ -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);

View file

@ -1591,7 +1591,8 @@ static void _a6xx_do_crashdump(struct kgsl_device *device)
timeout = ktime_add_ms(ktime_get(), CP_CRASH_DUMPER_TIMEOUT);
might_sleep();
if (!device->snapshot_atomic)
might_sleep();
for (;;) {
/* make sure we're reading the latest value */
@ -1603,7 +1604,8 @@ static void _a6xx_do_crashdump(struct kgsl_device *device)
break;
/* Wait 1msec to avoid unnecessary looping */
usleep_range(100, 1000);
if (!device->snapshot_atomic)
usleep_range(100, 1000);
}
kgsl_regread(device, A6XX_CP_CRASH_DUMP_STATUS, &val);

View file

@ -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);

View file

@ -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*/

View file

@ -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 @@ err:
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);