usb: gadget: ffs: Add IPC logging via kretprobes

Add a separate module, f_fs_ipc_log, which uses kretprobes to probe into
various functions in the f_fs driver in order to log entry/exit and state
to the IPC logging facility. This aids in debugging.

Change-Id: Iaa0ce2c7739b3c244097cc491f2616a66c3e8c01
Signed-off-by: Ronak Vijay Raheja <rraheja@codeaurora.org>
This commit is contained in:
Ronak Vijay Raheja 2020-11-05 14:14:20 -08:00
parent 4cc3e391bb
commit 6b19f72aa8
3 changed files with 883 additions and 0 deletions

View File

@ -237,6 +237,17 @@ config USB_F_CDEV
config USB_F_GSI
tristate
config USB_F_FS_IPC_LOGGING
tristate "Enable IPC logging for FunctionFS via f_fs_ipc_log"
depends on IPC_LOGGING
help
Enables additional debug messages for FunctionFS driver with the help
of f_fs_ipc_log module and output via IPC Logging mechanism. This can
be useful when troubleshooting transfer stalls or other general
failures and determine if the issue is in the kernel gadget or the
userspace client. Separate IPC log contexts are created for each
function instance at mount time.
# this first set of drivers all depend on bulk-capable hardware.
config USB_CONFIGFS

View File

@ -67,3 +67,4 @@ ifeq ($(CONFIG_USB_F_RNDIS),)
usb_f_gsi-y += rndis.o
endif
obj-$(CONFIG_USB_F_GSI) += usb_f_gsi.o
obj-$(CONFIG_USB_F_FS_IPC_LOGGING) += f_fs_ipc_log.o

View File

@ -0,0 +1,871 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kprobes.h>
#include <linux/fs_parser.h>
#include <linux/ipc_logging.h>
#include <linux/usb/functionfs.h>
#include "u_fs.h"
/* Copied from f_fs.c */
struct ffs_io_data {
bool aio;
bool read;
struct kiocb *kiocb;
struct iov_iter data;
const void *to_free;
char *buf;
struct mm_struct *mm;
struct work_struct work;
struct usb_ep *ep;
struct usb_request *req;
struct sg_table sgt;
bool use_sg;
struct ffs_data *ffs;
};
/* Copied from f_fs.c */
struct ffs_epfile {
struct mutex mutex;
struct ffs_data *ffs;
struct ffs_ep *ep; /* P: ffs->eps_lock */
atomic_t opened;
struct dentry *dentry;
struct ffs_buffer *read_buffer;
#define READ_BUFFER_DROP ((struct ffs_buffer *)ERR_PTR(-ESHUTDOWN))
char name[5];
unsigned char in; /* P: ffs->eps_lock */
unsigned char isoc; /* P: ffs->eps_lock */
bool invalid;
};
/* Copied from f_fs.c */
struct ffs_ep {
struct usb_ep *ep; /* P: ffs->eps_lock */
struct usb_request *req; /* P: epfile->mutex */
/* [0]: full speed, [1]: high speed, [2]: super speed */
struct usb_endpoint_descriptor *descs[3];
u8 num;
int status; /* P: epfile->mutex */
};
/* Copied from f_fs.c */
struct ffs_sb_fill_data {
struct ffs_file_perms perms;
umode_t root_mode;
const char *dev_name;
bool no_disconnect;
struct ffs_data *ffs_data;
};
/* Copied from f_fs.c */
struct ffs_function {
struct usb_configuration *conf;
struct usb_gadget *gadget;
struct ffs_data *ffs;
struct ffs_ep *eps;
u8 eps_revmap[16];
short *interfaces_nums;
struct usb_function function;
};
/* Copied from f_fs.c */
enum ffs_os_desc_type {
FFS_OS_DESC, FFS_OS_DESC_EXT_COMPAT, FFS_OS_DESC_EXT_PROP
};
#define kprobe_log(context, func, fmt, ...) \
ipc_log_string(context, "%s: " fmt, func, ##__VA_ARGS__)
#define MAX_IPC_INSTANCES 9
/* per-probe private data */
struct kprobe_data {
void *x0;
void *x1;
void *x2;
};
/* per-device IPC log data */
struct ipc_log {
void *context;
struct ffs_data *ffs;
};
static struct ipc_log ipc_log_s[MAX_IPC_INSTANCES];
/* Number of devices for f_fs driver */
static int num_devices;
static void *get_ipc_context(struct ffs_data *ffs)
{
int i = 0;
while (ipc_log_s[i].ffs != ffs) {
if (i < num_devices)
i++;
else {
kprobe_log(ipc_log_s[0].context, "ffs_sb_fill", "error");
return NULL;
}
}
return ipc_log_s[i].context;
}
static int entry_ffs_user_copy_worker(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct work_struct *work = (struct work_struct *)regs->regs[0];
struct ffs_io_data *io_data = container_of(work, struct ffs_io_data, work);
int ret = io_data->req->status ? io_data->req->status :
io_data->req->actual;
struct ffs_data *ffs = io_data->ffs;
void *context = get_ipc_context(ffs);
data->x0 = work;
kprobe_log(context, ri->rp->kp.symbol_name,
"enter: ret %d for %s", ret, io_data->read ? "read" : "write");
return 0;
}
static int exit_ffs_user_copy_worker(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct work_struct *work = data->x0;
struct ffs_io_data *io_data = container_of(work, struct ffs_io_data, work);
void *context = get_ipc_context(io_data->ffs);
kprobe_log(context, ri->rp->kp.symbol_name, "exit");
return 0;
}
static int entry_ffs_epfile_io(struct kretprobe_instance *ri, struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct file *file = (struct file *)regs->regs[0];
struct ffs_io_data *io_data = (struct ffs_io_data *)regs->regs[1];
struct ffs_epfile *epfile = file->private_data;
void *context = get_ipc_context(epfile->ffs);
data->x0 = file;
data->x1 = io_data;
kprobe_log(context, ri->rp->kp.symbol_name,
"enter: %s about to queue %zd bytes time %lld ns",
epfile->name, iov_iter_count(&io_data->data));
return 0;
}
static int exit_ffs_epfile_io(struct kretprobe_instance *ri, struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct file *file = data->x0;
struct ffs_epfile *epfile = file->private_data;
unsigned long ret = regs_return_value(regs);
void *context = get_ipc_context(epfile->ffs);
kprobe_log(context, ri->rp->kp.symbol_name, "exit: %s ret %zd", epfile->name,
ret);
return 0;
}
static int entry_ffs_epfile_async_io_complete(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct usb_request *req = (struct usb_request *)regs->regs[1];
struct ffs_io_data *io_data = req->context;
void *context = get_ipc_context(io_data->ffs);
kprobe_log(context, ri->rp->kp.symbol_name, "enter");
return 0;
}
static int entry_ffs_epfile_write_iter(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct kiocb *kiocb = (struct kiocb *)regs->regs[0];
struct ffs_epfile *epfile = kiocb->ki_filp->private_data;
void *context = get_ipc_context(epfile->ffs);
data->x0 = kiocb;
kprobe_log(context, ri->rp->kp.symbol_name, "enter");
return 0;
}
static int exit_ffs_epfile_write_iter(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct kiocb *kiocb = data->x0;
struct ffs_epfile *epfile = kiocb->ki_filp->private_data;
void *context = get_ipc_context(epfile->ffs);
unsigned long ret = regs_return_value(regs);
if (ret != -ENOMEM && ret != -EIOCBQUEUED)
kprobe_log(context, ri->rp->kp.symbol_name, "exit: ret %zd", ret);
return 0;
}
static int entry_ffs_epfile_read_iter(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct kiocb *kiocb = (struct kiocb *)regs->regs[0];
struct ffs_epfile *epfile = kiocb->ki_filp->private_data;
void *context = get_ipc_context(epfile->ffs);
data->x0 = kiocb;
kprobe_log(context, ri->rp->kp.symbol_name, "enter");
return 0;
}
static int exit_ffs_epfile_read_iter(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct kiocb *kiocb = data->x0;
struct ffs_epfile *epfile = kiocb->ki_filp->private_data;
void *context = get_ipc_context(epfile->ffs);
unsigned long ret = regs_return_value(regs);
kprobe_log(context, ri->rp->kp.symbol_name, "exit: ret %zd", ret);
return 0;
}
static int entry_ffs_data_put(struct kretprobe_instance *ri, struct pt_regs *regs)
{
struct ffs_data *ffs = (struct ffs_data *)regs->regs[0];
void *context = get_ipc_context(ffs);
unsigned int refcount = refcount_read(&ffs->ref);
kprobe_log(context, ri->rp->kp.symbol_name, "ref %u", refcount);
if (refcount == 1) {
ipc_log_context_destroy(context);
context = NULL;
}
return 0;
}
static int entry_ffs_sb_fill(struct kretprobe_instance *ri, struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct fs_context *fc = (struct fs_context *)regs->regs[1];
struct ffs_sb_fill_data *ctx = fc->fs_private;
/* TO-DO: Change the ipcname to usb_ffs after removing logs from f_fs */
char ipcname[24] = "usb_kprobe_";
data->x1 = fc;
if (num_devices < MAX_IPC_INSTANCES) {
strlcat(ipcname, fc->source, sizeof(ipcname));
ipc_log_s[num_devices].context = ipc_log_context_create(10, ipcname, 0);
if (IS_ERR_OR_NULL(ipc_log_s[num_devices].context)) {
ipc_log_s[num_devices].context = NULL;
pr_info("%s: Could not create IPC log context for device %s\n",
__func__, fc->source);
} else {
ipc_log_s[num_devices].ffs = ctx->ffs_data;
kprobe_log(ipc_log_s[num_devices].context,
ri->rp->kp.symbol_name, "enter");
}
}
num_devices++;
return 0;
}
static int entry___ffs_ep0_queue_wait(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct ffs_data *ffs = (struct ffs_data *)regs->regs[0];
void *context = get_ipc_context(ffs);
data->x0 = ffs;
kprobe_log(context, ri->rp->kp.symbol_name,
"enter: state %d setup_state %d flags %lu", ffs->state,
ffs->setup_state, ffs->flags);
return 0;
}
static int exit___ffs_ep0_queue_wait(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct ffs_data *ffs = data->x0;
void *context = get_ipc_context(ffs);
kprobe_log(context, ri->rp->kp.symbol_name,
"exit: state %d setup_state %d flags %lu", ffs->state,
ffs->setup_state, ffs->flags);
return 0;
}
static int entry_ffs_ep0_write(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct file *file = (struct file *)regs->regs[0];
struct ffs_data *ffs = file->private_data;
unsigned int len = (unsigned int)regs->regs[2];
void *context = get_ipc_context(ffs);
data->x0 = file;
kprobe_log(context, ri->rp->kp.symbol_name,
"enter:len %zu state %d setup_state %d flags %lu", len,
ffs->state, ffs->setup_state, ffs->flags);
return 0;
}
static int exit_ffs_ep0_write(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct file *file = data->x0;
struct ffs_data *ffs = file->private_data;
void *context = get_ipc_context(ffs);
unsigned long ret = regs_return_value(regs);
if (ret != -EIDRM && ret != -EINVAL)
kprobe_log(context, ri->rp->kp.symbol_name,
"exit:ret %zd state %d setup_state %d flags %lu",
ret, ffs->state, ffs->setup_state, ffs->flags);
return 0;
}
static int entry_ffs_ep0_read(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct file *file = (struct file *)regs->regs[0];
size_t len = (size_t)regs->regs[2];
struct ffs_data *ffs = file->private_data;
void *context = get_ipc_context(ffs);
size_t n;
data->x0 = file;
n = min((len / sizeof(struct usb_functionfs_event)), (size_t)ffs->ev.count);
kprobe_log(context, ri->rp->kp.symbol_name,
"enter:len %zu state %d setup_state %d flags %lu n %zu", len,
ffs->state, ffs->setup_state, ffs->flags, n);
return 0;
}
static int exit_ffs_ep0_read(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct file *file = data->x0;
struct ffs_data *ffs = file->private_data;
void *context = get_ipc_context(ffs);
unsigned long ret = regs_return_value(regs);
kprobe_log(context, ri->rp->kp.symbol_name,
"exit:ret %d state %d setup_state %d flags %lu", ret,
ffs->state, ffs->setup_state, ffs->flags);
return 0;
}
static int entry_ffs_ep0_open(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct inode *inode = (struct inode *)regs->regs[0];
struct ffs_data *ffs = inode->i_private;
void *context = get_ipc_context(ffs);
kprobe_log(context, ri->rp->kp.symbol_name,
"state %d setup_state %d flags %lu opened %d", ffs->state,
ffs->setup_state, ffs->flags, atomic_read(&ffs->opened));
return 0;
}
static int entry_ffs_ep0_release(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct file *file = (struct file *)regs->regs[1];
struct ffs_data *ffs = file->private_data;
void *context = get_ipc_context(ffs);
kprobe_log(context, ri->rp->kp.symbol_name,
"state %d setup_state %d flags %lu opened %d", ffs->state,
ffs->setup_state, ffs->flags, atomic_read(&ffs->opened));
return 0;
}
static int entry_ffs_ep0_ioctl(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct file *file = (struct file *)regs->regs[0];
struct ffs_data *ffs = file->private_data;
void *context = get_ipc_context(ffs);
kprobe_log(context, ri->rp->kp.symbol_name,
"state %d setup_state %d flags %lu opened %d", ffs->state,
ffs->setup_state, ffs->flags, atomic_read(&ffs->opened));
return 0;
}
static int entry_ffs_ep0_poll(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct file *file = (struct file *)regs->regs[0];
struct ffs_data *ffs = file->private_data;
void *context = get_ipc_context(ffs);
data->x0 = file;
kprobe_log(context, ri->rp->kp.symbol_name,
"state %d setup_state %d flags %lu opened %d", ffs->state,
ffs->setup_state, ffs->flags, atomic_read(&ffs->opened));
return 0;
}
static int exit_ffs_ep0_poll(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
unsigned long ret = regs_return_value(regs);
struct file *file = data->x0;
struct ffs_data *ffs = file->private_data;
void *context = get_ipc_context(ffs);
kprobe_log(context, ri->rp->kp.symbol_name, "exit: mask %u", ret);
return 0;
}
static int entry_ffs_epfile_open(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct inode *inode = (struct inode *)regs->regs[0];
struct ffs_epfile *epfile = inode->i_private;
void *context = get_ipc_context(epfile->ffs);
kprobe_log(context, ri->rp->kp.symbol_name,
"%s: state %d setup_state %d flag %lu opened %u",
epfile->name, epfile->ffs->state, epfile->ffs->setup_state,
epfile->ffs->flags, atomic_read(&epfile->opened));
return 0;
}
static int entry_ffs_aio_cancel(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct kiocb *kiocb = (struct kiocb *)regs->regs[0];
struct ffs_epfile *epfile = kiocb->ki_filp->private_data;
void *context = get_ipc_context(epfile->ffs);
data->x0 = kiocb;
kprobe_log(context, ri->rp->kp.symbol_name,
"enter:state %d setup_state %d flag %lu", epfile->ffs->state,
epfile->ffs->setup_state, epfile->ffs->flags);
return 0;
}
static int exit_ffs_aio_cancel(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
unsigned long ret = regs_return_value(regs);
struct kiocb *kiocb = data->x0;
struct ffs_epfile *epfile = kiocb->ki_filp->private_data;
void *context = get_ipc_context(epfile->ffs);
kprobe_log(context, ri->rp->kp.symbol_name, "exit: mask %u", ret);
return 0;
}
static int entry_ffs_epfile_release(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct inode *inode = (struct inode *)regs->regs[0];
struct ffs_epfile *epfile = inode->i_private;
void *context = get_ipc_context(epfile->ffs);
kprobe_log(context, ri->rp->kp.symbol_name,
"%s: state %d setup_state %d flag %lu opened %u",
epfile->name, epfile->ffs->state, epfile->ffs->setup_state,
epfile->ffs->flags, atomic_read(&epfile->opened));
return 0;
}
static int entry_ffs_epfile_ioctl(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct file *file = (struct file *)regs->regs[0];
unsigned int code = (unsigned int)regs->regs[1];
unsigned long value = (unsigned long)regs->regs[2];
struct ffs_epfile *epfile = file->private_data;
void *context = get_ipc_context(epfile->ffs);
data->x0 = file;
kprobe_log(context, ri->rp->kp.symbol_name,
"%s: code 0x%08x value %#lx state %d setup_state %d flag %lu",
epfile->name, code, value, epfile->ffs->state,
epfile->ffs->setup_state, epfile->ffs->flags);
return 0;
}
static int exit_ffs_epfile_ioctl(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
unsigned long ret = regs_return_value(regs);
struct file *file = data->x0;
struct ffs_epfile *epfile = file->private_data;
void *context = get_ipc_context(epfile->ffs);
kprobe_log(context, ri->rp->kp.symbol_name,
"exit: %s: ret %d\n", epfile->name, ret);
return 0;
}
static int entry_ffs_data_opened(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct ffs_data *ffs = (struct ffs_data *)regs->regs[0];
void *context = get_ipc_context(ffs);
kprobe_log(context, ri->rp->kp.symbol_name,
"enter: state %d setup_state %d flag %lu opened %d ref %d",
ffs->state, ffs->setup_state, ffs->flags,
atomic_read(&ffs->opened), refcount_read(&ffs->ref));
return 0;
}
static int entry_ffs_data_closed(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct ffs_data *ffs = (struct ffs_data *)regs->regs[0];
void *context = get_ipc_context(ffs);
kprobe_log(context, ri->rp->kp.symbol_name,
"state %d setup_state %d flag %lu opened %d", ffs->state,
ffs->setup_state, ffs->flags, atomic_read(&ffs->opened));
return 0;
}
static int entry_ffs_data_clear(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct ffs_data *ffs = (struct ffs_data *)regs->regs[0];
void *context = get_ipc_context(ffs);
kprobe_log(context, ri->rp->kp.symbol_name,
"enter: state %d setup_state %d flag %lu", ffs->state,
ffs->setup_state, ffs->flags);
return 0;
}
static int entry_functionfs_bind(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct ffs_data *ffs = (struct ffs_data *)regs->regs[0];
void *context = get_ipc_context(ffs);
data->x0 = ffs;
kprobe_log(context, ri->rp->kp.symbol_name,
"enter: state %d setup_state %d flag %lu", ffs->state,
ffs->setup_state, ffs->flags);
return 0;
}
static int exit_functionfs_bind(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct ffs_data *ffs = data->x0;
void *context = get_ipc_context(ffs);
unsigned long ret = regs_return_value(regs);
kprobe_log(context, ri->rp->kp.symbol_name,
"functionfs_bind returned %d", ret);
return 0;
}
static int entry_ffs_func_eps_disable(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct ffs_function *func = (struct ffs_function *)regs->regs[0];
void *context = get_ipc_context(func->ffs);
kprobe_log(context, ri->rp->kp.symbol_name,
"enter: state %d setup_state %d flag %lu", func->ffs->state,
func->ffs->setup_state, func->ffs->flags);
return 0;
}
static int entry_ffs_func_bind(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct usb_function *f = (struct usb_function *)regs->regs[1];
struct ffs_function *func = container_of(f, struct ffs_function, function);
struct f_fs_opts *ffs_opts =
container_of(f->fi, struct f_fs_opts, func_inst);
struct ffs_data *ffs = ffs_opts->dev->ffs_data;
void *context = get_ipc_context(ffs);
data->x0 = ffs;
kprobe_log(context, ri->rp->kp.symbol_name, "enter");
kprobe_log(context, "_ffs_func_bind",
"enter: state %d setup_state %d flag %lu", ffs->state,
ffs->setup_state, ffs->flags);
return 0;
}
static int exit_ffs_func_bind(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
int ret = (int)regs_return_value(regs);
struct ffs_data *ffs = data->x0;
void *context = get_ipc_context(ffs);
if (ret < 0)
kprobe_log(context, ri->rp->kp.symbol_name, "exit: ret %d", ret);
return 0;
}
static int entry_ffs_reset_work(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct work_struct *work = (struct work_struct *)regs->regs[0];
struct ffs_data *ffs = container_of(work, struct ffs_data, reset_work);
void *context = get_ipc_context(ffs);
kprobe_log(context, ri->rp->kp.symbol_name, "enter");
return 0;
}
static int entry_ffs_func_set_alt(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct usb_function *f = (struct usb_function *)regs->regs[0];
struct ffs_function *func = container_of(f, struct ffs_function, function);
unsigned int alt = (unsigned int)regs->regs[2];
void *context = get_ipc_context(func->ffs);
data->x0 = func;
kprobe_log(context, ri->rp->kp.symbol_name, "enter: alt %d", (int)alt);
return 0;
}
static int entry_ffs_func_disable(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct usb_function *f = (struct usb_function *)regs->regs[0];
struct ffs_function *func = container_of(f, struct ffs_function, function);
void *context = get_ipc_context(func->ffs);
kprobe_log(context, ri->rp->kp.symbol_name, "enter");
return 0;
}
static int entry_ffs_func_setup(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct usb_function *f = (struct usb_function *)regs->regs[0];
struct ffs_function *func = container_of(f, struct ffs_function, function);
struct usb_ctrlrequest *creq = (struct usb_ctrlrequest *)regs->regs[1];
struct ffs_data *ffs = func->ffs;
void *context = get_ipc_context(func->ffs);
kprobe_log(context, ri->rp->kp.symbol_name,
"enter: state %d reqtype=%02x req=%02x wv=%04x wi=%04x wl=%04x",
ffs->state, creq->bRequestType, creq->bRequest,
le16_to_cpu(creq->wValue), le16_to_cpu(creq->wIndex),
le16_to_cpu(creq->wLength));
return 0;
}
static int entry_ffs_func_suspend(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct usb_function *f = (struct usb_function *)regs->regs[0];
struct ffs_function *func = container_of(f, struct ffs_function, function);
void *context = get_ipc_context(func->ffs);
kprobe_log(context, ri->rp->kp.symbol_name, "enter");
return 0;
}
static int entry_ffs_func_resume(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct usb_function *f = (struct usb_function *)regs->regs[0];
struct ffs_function *func = container_of(f, struct ffs_function, function);
void *context = get_ipc_context(func->ffs);
kprobe_log(context, ri->rp->kp.symbol_name, "enter");
return 0;
}
static int entry_ffs_func_unbind(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct usb_function *f = (struct usb_function *)regs->regs[1];
struct ffs_function *func = container_of(f, struct ffs_function, function);
struct ffs_data *ffs = func->ffs;
struct f_fs_opts *opts =
container_of(f->fi, struct f_fs_opts, func_inst);
void *context = get_ipc_context(ffs);
data->x1 = ffs;
kprobe_log(context, ri->rp->kp.symbol_name,
"enter: state %d setup_state %d flag %lu refcnt %u", ffs->state,
ffs->setup_state, ffs->flags, opts->refcnt);
return 0;
}
static int exit_ffs_func_unbind(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct ffs_data *ffs = data->x1;
void *context = get_ipc_context(ffs);
kprobe_log(context, ri->rp->kp.symbol_name,
"exit: state %d setup_state %d flag %lu", ffs->state,
ffs->setup_state, ffs->flags);
return 0;
}
static int entry_ffs_closed(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct ffs_data *ffs = (struct ffs_data *)regs->regs[0];
void *context = get_ipc_context(ffs);
data->x0 = ffs;
kprobe_log(context, ri->rp->kp.symbol_name, "enter");
return 0;
}
static int exit_ffs_closed(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kprobe_data *data = (struct kprobe_data *)ri->data;
struct ffs_data *ffs = data->x0;
struct ffs_dev *ffs_obj = ffs->private_data;
struct f_fs_opts *opts = ffs_obj->opts;
void *context = get_ipc_context(ffs);
if (test_bit(FFS_FL_BOUND, &ffs->flags))
kprobe_log(context, ri->rp->kp.symbol_name, "unreg gadget done");
else if (!ffs_obj || !ffs_obj->opts || opts->no_configfs ||
!opts->func_inst.group.cg_item.ci_parent
|| !kref_read(&opts->func_inst.group.cg_item.ci_kref))
kprobe_log(context, ri->rp->kp.symbol_name, "exit error");
return 0;
}
#define ENTRY_EXIT(name) {\
.handler = exit_##name,\
.entry_handler = entry_##name,\
.data_size = sizeof(struct kprobe_data),\
.maxactive = MAX_IPC_INSTANCES,\
.kp.symbol_name = #name,\
}
#define ENTRY(name) {\
.entry_handler = entry_##name,\
.data_size = sizeof(struct kprobe_data),\
.maxactive = MAX_IPC_INSTANCES,\
.kp.symbol_name = #name,\
}
static struct kretprobe ffsprobes[] = {
ENTRY_EXIT(ffs_user_copy_worker),
ENTRY_EXIT(ffs_epfile_io),
ENTRY(ffs_epfile_async_io_complete),
ENTRY_EXIT(ffs_epfile_write_iter),
ENTRY_EXIT(ffs_epfile_read_iter),
ENTRY(ffs_data_put),
ENTRY(ffs_sb_fill),
ENTRY_EXIT(__ffs_ep0_queue_wait),
ENTRY_EXIT(ffs_ep0_write),
ENTRY_EXIT(ffs_ep0_read),
ENTRY(ffs_ep0_open),
ENTRY(ffs_ep0_release),
ENTRY(ffs_ep0_ioctl),
ENTRY_EXIT(ffs_ep0_poll),
ENTRY(ffs_epfile_open),
ENTRY_EXIT(ffs_aio_cancel),
ENTRY(ffs_epfile_release),
ENTRY_EXIT(ffs_epfile_ioctl),
ENTRY(ffs_data_opened),
ENTRY(ffs_data_closed),
ENTRY(ffs_data_clear),
ENTRY_EXIT(functionfs_bind),
ENTRY(ffs_func_eps_disable),
ENTRY_EXIT(ffs_func_bind),
ENTRY(ffs_reset_work),
ENTRY(ffs_func_set_alt),
ENTRY(ffs_func_disable),
ENTRY(ffs_func_setup),
ENTRY(ffs_func_suspend),
ENTRY(ffs_func_resume),
ENTRY_EXIT(ffs_func_unbind),
ENTRY_EXIT(ffs_closed)
};
static int __init kretprobe_init(void)
{
int ret;
int i;
for (i = 0; i < ARRAY_SIZE(ffsprobes); i++) {
ret = register_kretprobe(&ffsprobes[i]);
if (ret < 0) {
pr_err("register_kretprobe failed at %s, returned %d\n",
ffsprobes[i].kp.symbol_name, ret);
}
}
return 0;
}
static void __exit kretprobe_exit(void)
{
int i;
for (i = 0; i < num_devices; i++) {
if (ipc_log_s[i].ffs) {
ipc_log_context_destroy(ipc_log_s[i].context);
ipc_log_s[i].context = NULL;
}
}
for (i = 0; i < ARRAY_SIZE(ffsprobes); i++) {
unregister_kretprobe(&ffsprobes[i]);
/* nmissed > 0 suggests that maxactive was set too low. */
if (ffsprobes[i].nmissed > 0)
pr_info("Missed probing %d instances of %s\n",
ffsprobes[i].nmissed,
ffsprobes[i].kp.symbol_name);
}
}
module_init(kretprobe_init)
module_exit(kretprobe_exit)
MODULE_LICENSE("GPL v2");