android_kernel_samsung_sm86.../qcom/opensource/graphics-kernel/kgsl_eventlog.c
David Wronek 880d405719 Add 'qcom/opensource/graphics-kernel/' from commit 'b4fdc4c04295ac59109ae19d64747522740c3f14'
git-subtree-dir: qcom/opensource/graphics-kernel
git-subtree-mainline: 992813d9c1
git-subtree-split: b4fdc4c042
Change-Id:
repo: https://git.codelinaro.org/clo/la/platform/vendor/qcom/opensource/graphics-kernel
tag: GRAPHICS.LA.14.0.r1-07700-lanai.0
2024-10-06 16:44:56 +02:00

267 lines
5.6 KiB
C

// 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);
}