Select Git revision
ConnectionStatusBar.tsx
trigger.c 8.79 KiB
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/delay.h>
#include <linux/ktime.h>
#include <asm/uaccess.h>
#include <linux/debugfs.h>
#include <linux/workqueue.h>
#include "trigger.h"
#include "pmccntr_timestamp.h"
#include <linux/measuremore.h>
#include "kernel_memo_git_commit.h"
#include "kernel_memo_git_status.h"
#define pr_here() pr_err("%s:%d %s called\n", __FILE__, __LINE__, __func__)
#define ECHO_1_OUTPUT "1\n"
#define DEVICE_NAME KBUILD_MODNAME
#define CLASS_NAME "interrupt_generator"
#define GPIO_A6 0x60+26
#define GPIO_A7 0x60+27
#define GPIO_IRQ GPIO_A6
#define GPIO_OUT GPIO_A7
struct dentry *memo_root;
static int irq_number;
/**************
* Workqueues *
**************/
static void memo_work_handler(struct work_struct *work)
{
memo_checkpoint("work");
}
static DECLARE_WORK(memo_work, memo_work_handler);
/************
* Tasklets *
************/
static void memo_tasklet_handler(unsigned long priv)
{
memo_checkpoint("tasklet");
}
static DECLARE_TASKLET(memo_tasklet, memo_tasklet_handler, 0);
/***********
* Softirq *
***********/
static void memo_softirq_handler(struct softirq_action *unused)
{
memo_checkpoint("softirq");
}
/* For this to work you have to insert MEASUREMORE_SOFTIRQ into the softirq-enum
* in include/linux/interrupt.h */
static void memo_install_softirq(void)
{
pr_err("Opening softirq %d\n", MEASUREMORE_SOFTIRQ);
open_softirq(MEASUREMORE_SOFTIRQ, memo_softirq_handler);
}
/******************
* Bottom Handler *
******************/
enum memo_bottom_handler {
MMBH_WORKQUEUE,
MMBH_TASKLET,
MMBH_SOFTIRQ
};
static char *bottom_handler_map_strings[] = {"workqueue\n", "tasklet\n", "softirq\n", NULL};
static enum memo_bottom_handler bottom_handler_map[] = {MMBH_WORKQUEUE, MMBH_TASKLET, MMBH_SOFTIRQ};
static const char *option_bottom_handler_str = "workqueue\n";
static enum memo_bottom_handler option_bottom_handler = MMBH_WORKQUEUE;
static ssize_t bottom_handler_read(struct file *fp, char __user *user_buffer,
size_t count, loff_t *position)
{
return simple_read_from_buffer(user_buffer, count, position,
option_bottom_handler_str,
strlen(option_bottom_handler_str));
}
static ssize_t bottom_handler_write(struct file *fp,
const char __user *user_buffer,
size_t count, loff_t *position)
{
char buf[count+1];
buf[count] = '\0';
if (simple_write_to_buffer(buf, count, position, user_buffer, count) < 0) {
BUG();
}
for (int i = 0; bottom_handler_map_strings[i]; i++) {
if (strcmp(bottom_handler_map_strings[i], buf) == 0) {
option_bottom_handler = bottom_handler_map[i];
option_bottom_handler_str = bottom_handler_map_strings[i];
return count;
}
}
pr_err("Unrecognized bottom handler string '%s'\n", buf);
return count;
}
static const struct file_operations bottom_handler_fops = {
/* TODO: Not sure if this is legit, can't read/write only ask for a
* portion of the string? */
.read = bottom_handler_read,
.write = bottom_handler_write,
};
/******************************
* Vars Exposed Through Sysfs *
******************************/
static u64 option_delay_ms = 20;
static u64 option_reps = 1000;
static u64 option_cp_capacity = 25;
/**************
* Interrupts *
**************/
static irqreturn_t memo_irq_handler(int irq, void *dev_id)
{
memo_checkpoint("irq");
switch (option_bottom_handler) {
case MMBH_WORKQUEUE:
queue_work(system_highpri_wq, &memo_work);
break;
case MMBH_TASKLET:
tasklet_schedule(&memo_tasklet);
break;
case MMBH_SOFTIRQ:
raise_softirq(MEASUREMORE_SOFTIRQ);
break;
default:
BUG();
break;
}
return IRQ_HANDLED;
}
static int memo_request_irq(int irqnum)
{
return request_any_context_irq(irqnum, memo_irq_handler,
IRQF_TRIGGER_FALLING, DEVICE_NAME, NULL);
}
/*******************
* Sysfs Interface *
*******************/
static void memo_trigger_interrupt(void)
{
gpio_set_value(GPIO_OUT, 1);
gpio_set_value(GPIO_OUT, 0);
}
static int memo_trigger(void) {
const uint64_t delay_ms = option_delay_ms;
const uint64_t delay_us = delay_ms*1000;
const uint64_t delay_ns = delay_ms*1000*1000;
const unsigned long reps = option_reps;
const unsigned long cp_capacity = option_cp_capacity;
memo_start_benchmark(reps, cp_capacity);
for (unsigned long i = 0; i < reps; i++) {
memo_start_run();
memo_checkpoint("start");
msleep(delay_ms);
memo_checkpoint("stop");
memo_stop_run();
}
memo_stop_benchmark();
return 0; /* TODO: Signal error. */
}
static ssize_t trigger_write(struct file *file, const char __user *addr,
size_t len, loff_t *pos)
{
pr_info("trigger write\n");
int err = memo_trigger();
if (err) {
pr_err("trigger failed with error %d\n", err);
}
return len;
}
static ssize_t memo_empty_read(struct file *fp, char __user *user_buffer,
size_t count, loff_t *position)
{
return simple_read_from_buffer(user_buffer, count, position, "", 0);
}
/* TODO: Does block when logging in, triggering, logging out, triggering again. */
static const struct file_operations trigger_fops = {
.write = trigger_write,
.read = memo_empty_read,
.llseek = noop_llseek, /* TODO: via fs.c gcov_reset_fops, not sure why */
};
/*****************
* File Creation *
*****************/
static struct dentry *memo_debugfs_create_str(const char *name, const char *str,
struct debugfs_blob_wrapper *blob)
{
blob->data = (char *) str;
blob->size = strlen(blob->data);
return debugfs_create_blob(name, 0400, memo_root, blob);
}
static struct debugfs_blob_wrapper kernel_memo_git_commit_blob;
static struct debugfs_blob_wrapper kernel_memo_git_status_blob;
static int create_files(void)
{
/* TODO: error handling */
memo_debugfs_create_str("kernel_measuremore_git_commit", kernel_memo_git_commit,
&kernel_memo_git_commit_blob);
memo_debugfs_create_str("kernel_measuremore_git_status", kernel_memo_git_status,
&kernel_memo_git_status_blob);
debugfs_create_bool("vmalloc", 0400, memo_root, &memo_flag_vmalloc);
debugfs_create_u64("delay_ms", 0600, memo_root, &option_delay_ms);
debugfs_create_u64("reps", 0600, memo_root, &option_reps);
debugfs_create_u64("checkpoint_capacity", 0600, memo_root,
&option_cp_capacity);
debugfs_create_file("bottom_handler", 0600, memo_root, NULL,
&bottom_handler_fops);
debugfs_create_u32("progress_interval", 0600, memo_root,
&memo_option_progress_interval);
/* We have to use debugfs_create_file_unsafe() here since we want to
* remove other debugfs files in the fops' write function. Otherwise
* those calls to remove would deadlock. TODO: Not sure whether this is
* the right solution, maybe, to be completely correct, we must use
* debugfs_use_file_start/finish() inside the write function. */
debugfs_create_file_unsafe("trigger", 0600, memo_root, NULL, &trigger_fops);
return 0;
}
static int gpio_init(void) {
int err = gpio_request_one(GPIO_OUT, GPIOF_OUT_INIT_HIGH, DEVICE_NAME " gpio");
if (err) {
goto ret;
}
err = gpio_request_one(GPIO_IRQ, GPIOF_IN, DEVICE_NAME " irq");
if (err) {
goto free_a7;
}
goto ret;
free_a7:
gpio_free(GPIO_OUT);
ret:
return err;
}
static void release_gpio(void)
{
gpio_free(GPIO_IRQ);
gpio_free(GPIO_OUT);
}
static int late_init(void)
{
int err = gpio_init();
if (err) {
goto ret;
}
irq_number = gpio_to_irq(GPIO_IRQ);
if (irq_number < 0) {
pr_err("could not get irq for gpio\n");
err = irq_number;
goto release_gpio;
}
pr_info("requesting irq %u\n", irq_number);
err = memo_request_irq(irq_number);
if (err) {
pr_err("irq request failed");
goto release_gpio;
}
err = create_files();
if (err) {
goto free_irq;
}
#ifdef CONFIG_MEASUREMORE_TIMESTAMP_TYPE_PMCCNTR
pmccntr_enable();
#endif
goto ret;
free_irq:
free_irq(irq_number, NULL);
release_gpio:
release_gpio();
ret:
return err;
}
static ssize_t late_init_write(struct file *file, const char __user *addr,
size_t len, loff_t *pos)
{
int err = late_init();
if (err) {
pr_err("Late init failed with error %d\n", err);
}
return len;
}
static const struct file_operations late_init_fops = {
.write = late_init_write,
.read = memo_empty_read, /* So cp -r doen't fail. */
.llseek = noop_llseek, /* TODO: via fs.c gcov_reset_fops, not sure why */
};
static int __init memo_init(void)
{
pr_err("init\n");
memo_install_softirq();
memo_root = debugfs_create_dir("measuremore", NULL);
BUG_ON(!memo_root);
debugfs_create_file("init", 0600, memo_root, NULL, &late_init_fops);
return 0;
}
static void __exit memo_exit(void)
{
debugfs_remove_recursive(memo_root);
free_irq(irq_number, NULL);
release_gpio();
}
module_init(memo_init);
module_exit(memo_exit);
MODULE_DESCRIPTION("Trigger interrupt top / bottom handlers");
MODULE_AUTHOR("Luis Gerhorst");
MODULE_LICENSE("GPL");