123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (c) 2021, The Linux Foundation. All rights reserved.
- * Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved.
- */
- #include <linux/sched.h>
- #include <linux/sched/clock.h>
- #include <linux/slab.h>
- #include <linux/spinlock.h>
- #include "kgsl_device.h"
- #include "kgsl_eventlog.h"
- #include "kgsl_snapshot.h"
- #include "kgsl_util.h"
- #define EVENTLOG_SIZE (SZ_64K + SZ_32K)
- #define MAGIC 0xabbaabba
- #define LOG_FENCE_NAME_LEN 74
- #define KGSL_SNAPSHOT_EVENTLOG_TYPE 0x1
- #define KGSL_SNAPSHOT_EVENTLOG_VERSION 0x0
- /*
- * This an internal event used to skip empty space at the bottom of the
- * ringbuffer
- */
- #define LOG_SKIP 1
- #define LOG_FIRE_EVENT 2
- #define LOG_CMDBATCH_SUBMITTED_EVENT 3
- #define LOG_CMDBATCH_RETIRED_EVENT 4
- #define LOG_SYNCPOINT_FENCE_EVENT 5
- #define LOG_SYNCPOINT_FENCE_EXPIRE_EVENT 6
- #define LOG_TIMELINE_FENCE_ALLOC_EVENT 7
- #define LOG_TIMELINE_FENCE_RELEASE_EVENT 8
- static spinlock_t lock;
- static void *kgsl_eventlog;
- static int eventlog_wptr;
- struct kgsl_log_header {
- /** @magic: Magic value to identify header */
- u32 magic;
- /** @pid: : PID of the process */
- int pid;
- /** @time: System time in nanoseconds */
- u64 time;
- /** @event: bits[0:15] specify the event ID. bits[16:31] specify event version */
- u32 event;
- /** @size: Size of the event data in bytes */
- u32 size;
- };
- /* Add a marker to skip the rest of the eventlog and start over fresh */
- static void add_skip_header(u32 offset)
- {
- struct kgsl_log_header *header = kgsl_eventlog + offset;
- header->magic = MAGIC;
- header->time = local_clock();
- header->pid = 0;
- header->event = FIELD_PREP(GENMASK(15, 0), LOG_SKIP);
- header->size = EVENTLOG_SIZE - sizeof(*header) - offset;
- }
- static void *kgsl_eventlog_alloc(u16 eventid, u32 size)
- {
- struct kgsl_log_header *header;
- u32 datasize = size + sizeof(*header);
- unsigned long flags;
- void *data;
- if (!kgsl_eventlog)
- return NULL;
- spin_lock_irqsave(&lock, flags);
- if (eventlog_wptr + datasize > (EVENTLOG_SIZE - sizeof(*header))) {
- add_skip_header(eventlog_wptr);
- eventlog_wptr = datasize;
- data = kgsl_eventlog;
- } else {
- data = kgsl_eventlog + eventlog_wptr;
- eventlog_wptr += datasize;
- }
- spin_unlock_irqrestore(&lock, flags);
- header = data;
- header->magic = MAGIC;
- header->time = local_clock();
- header->pid = current->pid;
- header->event = FIELD_PREP(GENMASK(15, 0), eventid);
- header->size = size;
- return data + sizeof(*header);
- }
- void kgsl_eventlog_init(void)
- {
- kgsl_eventlog = kzalloc(EVENTLOG_SIZE, GFP_KERNEL);
- eventlog_wptr = 0;
- spin_lock_init(&lock);
- kgsl_add_to_minidump("KGSL_EVENTLOG", (u64) kgsl_eventlog,
- __pa(kgsl_eventlog), EVENTLOG_SIZE);
- }
- void kgsl_eventlog_exit(void)
- {
- kgsl_remove_from_minidump("KGSL_EVENTLOG", (u64) kgsl_eventlog,
- __pa(kgsl_eventlog), EVENTLOG_SIZE);
- kfree(kgsl_eventlog);
- kgsl_eventlog = NULL;
- eventlog_wptr = 0;
- }
- void log_kgsl_fire_event(u32 id, u32 ts, u32 type, u32 age)
- {
- struct {
- u32 id;
- u32 ts;
- u32 type;
- u32 age;
- } *entry;
- entry = kgsl_eventlog_alloc(LOG_FIRE_EVENT, sizeof(*entry));
- if (!entry)
- return;
- entry->id = id;
- entry->ts = ts;
- entry->type = type;
- entry->age = age;
- }
- void log_kgsl_cmdbatch_submitted_event(u32 id, u32 ts, u32 prio, u64 flags)
- {
- struct {
- u32 id;
- u32 ts;
- u32 prio;
- u64 flags;
- } *entry;
- entry = kgsl_eventlog_alloc(LOG_CMDBATCH_SUBMITTED_EVENT, sizeof(*entry));
- if (!entry)
- return;
- entry->id = id;
- entry->ts = ts;
- entry->prio = prio;
- entry->flags = flags;
- }
- void log_kgsl_cmdbatch_retired_event(u32 id, u32 ts, u32 prio, u64 flags,
- u64 start, u64 retire)
- {
- struct {
- u32 id;
- u32 ts;
- u32 prio;
- u64 flags;
- u64 start;
- u64 retire;
- } *entry;
- entry = kgsl_eventlog_alloc(LOG_CMDBATCH_RETIRED_EVENT, sizeof(*entry));
- if (!entry)
- return;
- entry->id = id;
- entry->ts = ts;
- entry->prio = prio;
- entry->flags = flags;
- entry->start = start;
- entry->retire = retire;
- }
- void log_kgsl_syncpoint_fence_event(u32 id, char *fence_name)
- {
- struct {
- u32 id;
- char name[LOG_FENCE_NAME_LEN];
- } *entry;
- entry = kgsl_eventlog_alloc(LOG_SYNCPOINT_FENCE_EVENT, sizeof(*entry));
- if (!entry)
- return;
- entry->id = id;
- memset(entry->name, 0, sizeof(entry->name));
- strscpy(entry->name, fence_name, sizeof(entry->name));
- }
- void log_kgsl_syncpoint_fence_expire_event(u32 id, char *fence_name)
- {
- struct {
- u32 id;
- char name[LOG_FENCE_NAME_LEN];
- } *entry;
- entry = kgsl_eventlog_alloc(LOG_SYNCPOINT_FENCE_EXPIRE_EVENT, sizeof(*entry));
- if (!entry)
- return;
- entry->id = id;
- memset(entry->name, 0, sizeof(entry->name));
- strscpy(entry->name, fence_name, sizeof(entry->name));
- }
- void log_kgsl_timeline_fence_alloc_event(u32 id, u64 seqno)
- {
- struct {
- u32 id;
- u64 seqno;
- } *entry;
- entry = kgsl_eventlog_alloc(LOG_TIMELINE_FENCE_ALLOC_EVENT, sizeof(*entry));
- if (!entry)
- return;
- entry->id = id;
- entry->seqno = seqno;
- }
- void log_kgsl_timeline_fence_release_event(u32 id, u64 seqno)
- {
- struct {
- u32 id;
- u64 seqno;
- } *entry;
- entry = kgsl_eventlog_alloc(LOG_TIMELINE_FENCE_RELEASE_EVENT, sizeof(*entry));
- if (!entry)
- return;
- entry->id = id;
- entry->seqno = seqno;
- }
- size_t kgsl_snapshot_eventlog_buffer(struct kgsl_device *device,
- u8 *buf, size_t remain, void *priv)
- {
- struct kgsl_snapshot_eventlog *hdr =
- (struct kgsl_snapshot_eventlog *)buf;
- u32 *data = (u32 *)(buf + sizeof(*hdr));
- if (!kgsl_eventlog)
- return 0;
- if (remain < EVENTLOG_SIZE + sizeof(*hdr)) {
- dev_err(device->dev,
- "snapshot: Not enough memory for eventlog\n");
- return 0;
- }
- hdr->size = EVENTLOG_SIZE;
- hdr->type = KGSL_SNAPSHOT_EVENTLOG_TYPE;
- hdr->version = KGSL_SNAPSHOT_EVENTLOG_VERSION;
- memcpy(data, kgsl_eventlog, EVENTLOG_SIZE);
- return EVENTLOG_SIZE + sizeof(*hdr);
- }
|