Add samsung specific changes

This commit is contained in:
2025-08-11 14:29:00 +02:00
parent c66122e619
commit 4d134a1294
2688 changed files with 1127995 additions and 11475 deletions

View File

@@ -147,6 +147,35 @@ config PREEMPTIRQ_TRACEPOINTS
Create preempt/irq toggle tracepoints if needed, so that other parts
of the kernel can use them to generate or add hooks to them.
config IPC_LOGGING
tristate "Debug Logging for IPC Drivers"
select GENERIC_TRACER
depends on DEBUG_FS
help
IPC Logging driver provides a logging option for IPC Drivers.
This provides a cyclic buffer based logging support in a driver
specific context. This driver also provides a debugfs interface
to dump the logs in a live fashion.
If in doubt, say no.
config IPC_LOGGING_CDEV
tristate "Ipc Logging Character Device"
depends on IPC_LOGGING
help
Character device for ipc logging. Reading it will extract ipc logs up to
the specified size and increment the read index of the ipc log buffer.
Read function will return EOF when there is no longer any data to read
in the ipc log buffer.
config IPC_LOG_MINIDUMP_BUFFERS
int "Ipc log buffers count that can be dumped with minidump"
depends on IPC_LOGGING
default 0
help
This option is used to configure maximum number of ipc log
buffers that can be dumped by minidump.
# All tracer options should select GENERIC_TRACER. For those options that are
# enabled by all tracers (context switch and event tracer) they select TRACING.
# This allows those options to appear when no other tracer is selected. But the

View File

@@ -110,4 +110,8 @@ obj-$(CONFIG_FPROBE_EVENTS) += trace_fprobe.o
obj-$(CONFIG_TRACEPOINT_BENCHMARK) += trace_benchmark.o
obj-$(CONFIG_RV) += rv/
obj-$(CONFIG_IPC_LOGGING) += qcom_ipc_logging.o
qcom_ipc_logging-y := ipc_logging.o ipc_logging_debug.o
qcom_ipc_logging-$(CONFIG_IPC_LOGGING_CDEV) += ipc_logging_cdev.o
libftrace-y := ftrace.o

1095
kernel/trace/ipc_logging.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,197 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/idr.h>
#include <linux/ipc_logging.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/uaccess.h>
#include "ipc_logging_private.h"
#define IPL_CDEV_MAX 255
static dev_t cdev_devt;
static struct class *cdev_class;
static DEFINE_IDA(ipl_minor_ida);
static void dfunc_string(struct encode_context *ectxt, struct decode_context *dctxt)
{
tsv_timestamp_read(ectxt, dctxt, "");
tsv_qtimer_read(ectxt, dctxt, " ");
tsv_byte_array_read(ectxt, dctxt, "");
/* add trailing \n if necessary */
if (*(dctxt->buff - 1) != '\n') {
if (dctxt->size) {
++dctxt->buff;
--dctxt->size;
}
*(dctxt->buff - 1) = '\n';
}
}
static int debug_log(struct ipc_log_context *ilctxt, char *buff, int size, int cont)
{
int i = 0;
int ret;
if (size < MAX_MSG_DECODED_SIZE) {
pr_err("%s: buffer size %d < %d\n", __func__, size, MAX_MSG_DECODED_SIZE);
return -ENOMEM;
}
do {
i = ipc_log_extract(ilctxt, buff, size - 1);
if (cont && i == 0) {
ret = wait_for_completion_interruptible(&ilctxt->read_avail);
if (ret < 0)
return ret;
}
} while (cont && i == 0);
return i;
}
static char *ipc_log_cdev_devnode(const struct device *dev, umode_t *mode)
{
return kasprintf(GFP_KERNEL, "ipc_logging/%s", dev_name(dev));
}
static int ipc_log_cdev_open(struct inode *inode, struct file *filp)
{
struct ipc_log_cdev *ipl_cdev;
ipl_cdev = container_of(inode->i_cdev, struct ipc_log_cdev, cdev);
filp->private_data = container_of(ipl_cdev, struct ipc_log_context, cdev);
return 0;
}
/*
* VFS Read operation which dispatches the call to the DevFS read command stored in
* file->private_data.
*
* @filp File structure
* @buff user buffer
* @count size of user buffer
* @offp file position to read from (only a value of 0 is accepted)
*
* @returns = 0 end of file
* > 0 number of bytes read
* < 0 error
*/
static ssize_t ipc_log_cdev_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
int ret, bsize;
char *buffer;
struct ipc_log_context *ilctxt;
ilctxt = filp->private_data;
ret = kref_get_unless_zero(&ilctxt->refcount) ? 0 : -EIO;
if (ret)
return ret;
buffer = kmalloc(count, GFP_KERNEL);
if (!buffer) {
bsize = -ENOMEM;
goto done;
}
/* only support non-continuous mode */
bsize = debug_log(ilctxt, buffer, count, 0);
if (bsize > 0) {
if (copy_to_user(buff, buffer, bsize)) {
bsize = -EFAULT;
kfree(buffer);
goto done;
}
*offp += bsize;
}
kfree(buffer);
done:
ipc_log_context_put(ilctxt);
return bsize;
}
static const struct file_operations cdev_fops = {
.owner = THIS_MODULE,
.open = ipc_log_cdev_open,
.read = ipc_log_cdev_read,
};
void ipc_log_cdev_remove(struct ipc_log_context *ilctxt)
{
if (ilctxt->cdev.dev.class) {
cdev_device_del(&ilctxt->cdev.cdev, &ilctxt->cdev.dev);
ida_free(&ipl_minor_ida, (unsigned int)MINOR(ilctxt->cdev.dev.devt));
}
}
EXPORT_SYMBOL(ipc_log_cdev_remove);
void ipc_log_cdev_create(struct ipc_log_context *ilctxt, const char *mod_name)
{
int ret;
int minor;
dev_t devno;
if (!cdev_class) {
pr_err("%s: %s no device class created\n", __func__, mod_name);
return;
}
minor = ida_alloc_range(&ipl_minor_ida, 0, IPL_CDEV_MAX, GFP_KERNEL);
if (minor < 0) {
pr_err("%s: %s failed to alloc ipl minor number %d\n", __func__, mod_name, minor);
return;
}
devno = MKDEV(MAJOR(cdev_devt), minor);
device_initialize(&ilctxt->cdev.dev);
ilctxt->cdev.dev.devt = devno;
ilctxt->cdev.dev.class = cdev_class;
dev_set_name(&ilctxt->cdev.dev, "%s", mod_name);
cdev_init(&ilctxt->cdev.cdev, &cdev_fops);
ret = cdev_device_add(&ilctxt->cdev.cdev, &ilctxt->cdev.dev);
if (ret) {
pr_err("%s: unable to add ipl cdev %s, %d\n", __func__, mod_name, ret);
ilctxt->cdev.dev.class = NULL;
ida_free(&ipl_minor_ida, (unsigned int)minor);
put_device(&ilctxt->cdev.dev);
return;
}
add_deserialization_func((void *)ilctxt, TSV_TYPE_STRING, dfunc_string);
}
EXPORT_SYMBOL(ipc_log_cdev_create);
void ipc_log_cdev_init(void)
{
int ret;
cdev_class = NULL;
ret = alloc_chrdev_region(&cdev_devt, 0, IPL_CDEV_MAX, "ipc_logging");
if (ret) {
pr_err("%s: unable to create ipl cdev regoin %d\n", __func__, ret);
return;
}
cdev_class = class_create("ipc_logging");
if (IS_ERR(cdev_class)) {
pr_err("%s: unable to create ipl cdev class %ld\n", __func__, PTR_ERR(cdev_class));
cdev_class = NULL;
unregister_chrdev_region(cdev_devt, IPL_CDEV_MAX);
return;
}
cdev_class->devnode = ipc_log_cdev_devnode;
}
EXPORT_SYMBOL(ipc_log_cdev_init);

View File

@@ -0,0 +1,191 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012-2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/jiffies.h>
#include <linux/debugfs.h>
#include <linux/io.h>
#include <linux/idr.h>
#include <linux/string.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/delay.h>
#include <linux/completion.h>
#include <linux/ipc_logging.h>
#include "ipc_logging_private.h"
static DEFINE_MUTEX(ipc_log_debugfs_init_lock);
static struct dentry *root_dent;
static int debug_log(struct ipc_log_context *ilctxt,
char *buff, int size, int cont)
{
int i = 0;
int ret;
if (size < MAX_MSG_DECODED_SIZE) {
pr_err("%s: buffer size %d < %d\n", __func__, size,
MAX_MSG_DECODED_SIZE);
return -ENOMEM;
}
do {
i = ipc_log_extract(ilctxt, buff, size - 1);
if (cont && i == 0) {
ret = wait_for_completion_interruptible(
&ilctxt->read_avail);
if (ret < 0)
return ret;
}
} while (cont && i == 0);
return i;
}
/*
* VFS Read operation helper which dispatches the call to the debugfs
* read command stored in file->private_data.
*
* @file File structure
* @buff user buffer
* @count size of user buffer
* @ppos file position to read from (only a value of 0 is accepted)
* @cont 1 = continuous mode (don't return 0 to signal end-of-file)
*
* @returns ==0 end of file
* >0 number of bytes read
* <0 error
*/
static ssize_t debug_read_helper(struct file *file, char __user *buff,
size_t count, loff_t *ppos, int cont)
{
struct ipc_log_context *ilctxt;
struct dentry *d = file->f_path.dentry;
char *buffer;
int bsize;
int r;
r = debugfs_file_get(d);
if (r)
return r;
ilctxt = file->private_data;
r = kref_get_unless_zero(&ilctxt->refcount) ? 0 : -EIO;
if (r) {
debugfs_file_put(d);
return r;
}
buffer = kmalloc(count, GFP_KERNEL);
if (!buffer) {
bsize = -ENOMEM;
goto done;
}
bsize = debug_log(ilctxt, buffer, count, cont);
if (bsize > 0) {
if (copy_to_user(buff, buffer, bsize)) {
bsize = -EFAULT;
kfree(buffer);
goto done;
}
*ppos += bsize;
}
kfree(buffer);
done:
ipc_log_context_put(ilctxt);
debugfs_file_put(d);
return bsize;
}
static ssize_t debug_read(struct file *file, char __user *buff,
size_t count, loff_t *ppos)
{
return debug_read_helper(file, buff, count, ppos, 0);
}
static ssize_t debug_read_cont(struct file *file, char __user *buff,
size_t count, loff_t *ppos)
{
return debug_read_helper(file, buff, count, ppos, 1);
}
static const struct file_operations debug_ops = {
.read = debug_read,
.open = simple_open,
};
static const struct file_operations debug_ops_cont = {
.read = debug_read_cont,
.open = simple_open,
};
static void debug_create(const char *name, mode_t mode,
struct dentry *dent,
struct ipc_log_context *ilctxt,
const struct file_operations *fops)
{
debugfs_create_file_unsafe(name, mode, dent, ilctxt, fops);
}
static void dfunc_string(struct encode_context *ectxt,
struct decode_context *dctxt)
{
tsv_timestamp_read(ectxt, dctxt, "");
tsv_qtimer_read(ectxt, dctxt, " ");
tsv_byte_array_read(ectxt, dctxt, "");
/* add trailing \n if necessary */
if (*(dctxt->buff - 1) != '\n') {
if (dctxt->size) {
++dctxt->buff;
--dctxt->size;
}
*(dctxt->buff - 1) = '\n';
}
}
void check_and_create_debugfs(void)
{
mutex_lock(&ipc_log_debugfs_init_lock);
if (!root_dent) {
root_dent = debugfs_create_dir("ipc_logging", 0);
if (IS_ERR(root_dent)) {
pr_err("%s: unable to create debugfs %ld\n",
__func__, PTR_ERR(root_dent));
root_dent = NULL;
}
}
mutex_unlock(&ipc_log_debugfs_init_lock);
}
EXPORT_SYMBOL(check_and_create_debugfs);
void create_ctx_debugfs(struct ipc_log_context *ctxt,
const char *mod_name)
{
if (!root_dent)
check_and_create_debugfs();
if (root_dent) {
ctxt->dent = debugfs_create_dir(mod_name, root_dent);
if (!IS_ERR(ctxt->dent)) {
debug_create("log", 0444, ctxt->dent,
ctxt, &debug_ops);
debug_create("log_cont", 0444, ctxt->dent,
ctxt, &debug_ops_cont);
}
}
add_deserialization_func((void *)ctxt,
TSV_TYPE_STRING, dfunc_string);
}
EXPORT_SYMBOL(create_ctx_debugfs);

View File

@@ -0,0 +1,193 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2012-2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef _IPC_LOGGING_PRIVATE_H
#define _IPC_LOGGING_PRIVATE_H
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/ipc_logging.h>
#define IPC_LOG_VERSION 0x0003
#define IPC_LOG_MAX_CONTEXT_NAME_LEN 32
/**
* struct ipc_log_page_header - Individual log page header
*
* @magic: Magic number (used for log extraction)
* @nmagic: Inverse of magic number (used for log extraction)
* @page_num: Index of page (0.. N - 1) (note top bit is always set)
* @read_offset: Read offset in page
* @write_offset: Write offset in page (or 0xFFFF if full)
* @log_id: ID of logging context that owns this page
* @start_time: Scheduler clock for first write time in page
* @end_time: Scheduler clock for last write time in page
* @ctx_offset: Signed offset from page to the logging context. Used to
* optimize ram-dump extraction.
*
* @list: Linked list of pages that make up a log
* @nd_read_offset: Non-destructive read offset used for debugfs
*
* The first part of the structure defines data that is used to extract the
* logs from a memory dump and elements in this section should not be changed
* or re-ordered. New local data structures can be added to the end of the
* structure since they will be ignored by the extraction tool.
*/
struct ipc_log_page_header {
uint32_t magic;
uint32_t nmagic;
uint32_t page_num;
uint16_t read_offset;
uint16_t write_offset;
uint64_t log_id;
uint64_t start_time;
uint64_t end_time;
int64_t ctx_offset;
/* add local data structures after this point */
struct list_head list;
uint16_t nd_read_offset;
};
/**
* struct ipc_log_page - Individual log page
*
* @hdr: Log page header
* @data: Log data
*
* Each log consists of 1 to N log pages. Data size is adjusted to always fit
* the structure into a single kernel page.
*/
struct ipc_log_page {
struct ipc_log_page_header hdr;
char data[PAGE_SIZE - sizeof(struct ipc_log_page_header)];
};
/**
* struct ipc_log_cdev - Ipc logging character device
*
* @cdev: character device structure
* @dev: device structure
*
* Character device structure for ipc logging. Used to create character device nodes in DevFS.
*/
struct ipc_log_cdev {
struct cdev cdev;
struct device dev;
};
/**
* struct ipc_log_context - main logging context
*
* @magic: Magic number (used for log extraction)
* @nmagic: Inverse of magic number (used for log extraction)
* @version: IPC Logging version of log format
* @user_version: Version number for user-defined messages
* @header_size: Size of the log header which is used to determine the offset
* of ipc_log_page::data
* @log_id: Log ID (assigned when log is created)
* @name: Name of the log used to uniquely identify the log during extraction
*
* @list: List of log contexts (struct ipc_log_context)
* @page_list: List of log pages (struct ipc_log_page)
* @first_page: First page in list of logging pages
* @last_page: Last page in list of logging pages
* @write_page: Current write page
* @read_page: Current read page (for internal reads)
* @nd_read_page: Current debugfs extraction page (non-destructive)
*
* @write_avail: Number of bytes available to write in all pages
* @dent: Debugfs node for run-time log extraction
* @dfunc_info_list: List of deserialization functions
* @context_lock_lhb1: Lock for entire structure
* @read_avail: Completed when new data is added to the log
* @cdev: Ipc logging character device
*/
struct ipc_log_context {
uint32_t magic;
uint32_t nmagic;
uint32_t version;
uint16_t user_version;
uint16_t header_size;
uint64_t log_id;
char name[IPC_LOG_MAX_CONTEXT_NAME_LEN];
/* add local data structures after this point */
struct list_head list;
struct list_head page_list;
struct ipc_log_page *first_page;
struct ipc_log_page *last_page;
struct ipc_log_page *write_page;
struct ipc_log_page *read_page;
struct ipc_log_page *nd_read_page;
uint32_t write_avail;
struct dentry *dent;
struct list_head dfunc_info_list;
spinlock_t context_lock_lhb1;
struct completion read_avail;
struct kref refcount;
bool destroyed;
struct ipc_log_cdev cdev;
};
struct dfunc_info {
struct list_head list;
int type;
void (*dfunc)(struct encode_context *enc, struct decode_context *dec);
};
enum {
TSV_TYPE_INVALID,
TSV_TYPE_TIMESTAMP,
TSV_TYPE_POINTER,
TSV_TYPE_INT32,
TSV_TYPE_BYTE_ARRAY,
TSV_TYPE_QTIMER,
};
enum {
OUTPUT_DEBUGFS,
};
#define IPC_LOG_CONTEXT_MAGIC_NUM 0x25874452
#define IPC_LOGGING_MAGIC_NUM 0x52784425
#define MIN(x, y) ((x) < (y) ? (x) : (y))
#define IS_MSG_TYPE(x) (((x) > TSV_TYPE_MSG_START) && \
((x) < TSV_TYPE_MSG_END))
#define MAX_MSG_DECODED_SIZE (MAX_MSG_SIZE*4)
void ipc_log_context_free(struct kref *kref);
static inline void ipc_log_context_put(struct ipc_log_context *ilctxt)
{
kref_put(&ilctxt->refcount, ipc_log_context_free);
}
#if (defined(CONFIG_DEBUG_FS))
void check_and_create_debugfs(void);
void create_ctx_debugfs(struct ipc_log_context *ctxt,
const char *mod_name);
#else
void check_and_create_debugfs(void)
{
}
void create_ctx_debugfs(struct ipc_log_context *ctxt, const char *mod_name)
{
}
#endif
#if IS_ENABLED(CONFIG_IPC_LOGGING_CDEV)
void ipc_log_cdev_init(void);
void ipc_log_cdev_create(struct ipc_log_context *ilctxt, const char *mod_name);
void ipc_log_cdev_remove(struct ipc_log_context *ilctxt);
#else
static inline void ipc_log_cdev_init(void) {}
static inline void ipc_log_cdev_create(struct ipc_log_context *ilctxt, const char *mod_name) {}
static inline void ipc_log_cdev_remove(struct ipc_log_context *ilctxt) {}
#endif
#endif