diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index f6bae63b3338..c9d315d66e88 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -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); diff --git a/drivers/gpu/msm/adreno_a6xx_snapshot.c b/drivers/gpu/msm/adreno_a6xx_snapshot.c index a69ec513addf..056d6c5e42a1 100644 --- a/drivers/gpu/msm/adreno_a6xx_snapshot.c +++ b/drivers/gpu/msm/adreno_a6xx_snapshot.c @@ -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); diff --git a/drivers/gpu/msm/adreno_snapshot.c b/drivers/gpu/msm/adreno_snapshot.c index 028423aefed9..1b08ec8b018f 100644 --- a/drivers/gpu/msm/adreno_snapshot.c +++ b/drivers/gpu/msm/adreno_snapshot.c @@ -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); diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h index 989e80691f3a..851d02b54e89 100644 --- a/drivers/gpu/msm/kgsl_device.h +++ b/drivers/gpu/msm/kgsl_device.h @@ -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*/ diff --git a/drivers/gpu/msm/kgsl_snapshot.c b/drivers/gpu/msm/kgsl_snapshot.c index f61d4b69070a..0ccadd55908a 100644 --- a/drivers/gpu/msm/kgsl_snapshot.c +++ b/drivers/gpu/msm/kgsl_snapshot.c @@ -3,6 +3,7 @@ * Copyright (c) 2012-2021, The Linux Foundation. All rights reserved. */ +#include #include #include #include @@ -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);