[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <CAJRGBZze+hyoj=XepAU3gfSoxzenR1OW+huqGcmF2k3hwMT4kA@mail.gmail.com>
Date: Mon, 25 Jun 2012 21:37:51 +0800
From: Luming Yu <luming.yu@...il.com>
To: arnd@...db.de
Cc: jcm@...masters.org, akpm@...ux-foundation.org,
linux-kernel@...r.kernel.org
Subject: Re: [patch update-v1] a simple hardware detector for latency as well
as throughput ver. 0.1.0
On Tue, Jun 26, 2012 at 5:23 AM, Luming Yu <luming.yu@...il.com> wrote:
> The patch is the fist step to test some basic hardware functions like
> TSC to help people understand if there is any hardware latency as well
> as throughput problem exposed on bare metal or left behind by BIOS or
> interfered by SMI. Currently the patch tests TSC, CPU Frequency, and
> RDRAND, which is a new CPU instruction to get random number introudced
> in new CPU like Intel Ivy Bridge, in stop_machine context.
>
> The tsc samples (ns) below are from a P4 system. You can change from 0
> to 1000 in /sys/kernel/debug/hw_atency_test/threshold to TSC sample at ms.
typo.
s/ms/us/
>
> [root@p4 linux]# rmmod hw_latency_test
> [root@p4 linux]# insmod drivers/misc/hw_latency_test.ko
> [root@p4 linux]# echo tsc > /sys/kernel/debug/hw_latency_test/current
> [root@p4 linux]# echo 1 > /sys/kernel/debug/hw_latency_test/enable
> [root@p4 linux]# cat /sys/kernel/debug/hw_latency_test/sample
> 1340657264.0434121340 388
> 1340657264.0935125912 379
> 1340657265.0436123548 404
> 1340657265.0937122432 441
> ....
> ^C
> [root@p4 linux]# echo 0 > /sys/kernel/debug/hw_latency_test/enable
>
> Signed-off-by: Luming Yu <luming.yu@...el.com>
> ---
> I will add more tests after the first patch gets merged for those guys
> who want to directly play with new hardware functions, and latency and
> bandwidth is concern, or simply out of curiosity. The patch is based on
> hardware latency dector written by Jcm in RT-tree. I assume I can add
> Jcm's signed off here.
>
>
> drivers/misc/Kconfig | 7 +
> drivers/misc/Makefile | 2 +
> drivers/misc/hw_latency_test.c | 833 ++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 842 insertions(+), 0 deletions(-)
>
>
> diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
> index c779509..a5216b5 100644
> --- a/drivers/misc/Kconfig
> +++ b/drivers/misc/Kconfig
> @@ -123,6 +123,13 @@ config IBM_ASM
> for information on the specific driver level and support statement
> for your IBM server.
>
> +config HW_LATENCY_TEST
> + tristate "Testing module to detect hardware lattency and throughput"
> + depends on DEBUG_FS
> + depends on RING_BUFFER
> + depends on X86
> + default m
> +
> config PHANTOM
> tristate "Sensable PHANToM (PCI)"
> depends on PCI
> diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
> index 3e1d801..f95c849 100644
> --- a/drivers/misc/Makefile
> +++ b/drivers/misc/Makefile
> @@ -48,4 +48,6 @@ obj-y += lis3lv02d/
> obj-y += carma/
> obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o
> obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/
> +obj-$(CONFIG_HW_LATENCY_TEST) += hw_latency_test.o
> +
> obj-$(CONFIG_MAX8997_MUIC) += max8997-muic.o
> diff --git a/drivers/misc/hw_latency_test.c b/drivers/misc/hw_latency_test.c
> new file mode 100644
> index 0000000..2aa3a74
> --- /dev/null
> +++ b/drivers/misc/hw_latency_test.c
> @@ -0,0 +1,833 @@
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/ring_buffer.h>
> +#include <linux/stop_machine.h>
> +#include <linux/time.h>
> +#include <linux/hrtimer.h>
> +#include <linux/kthread.h>
> +#include <linux/debugfs.h>
> +#include <linux/seq_file.h>
> +#include <linux/uaccess.h>
> +#include <linux/version.h>
> +#include <linux/delay.h>
> +#include <linux/slab.h>
> +#include <linux/random.h>
> +#include <asm/tlbflush.h>
> +
> +#define BUF_SIZE_DEFAULT 262144UL
> +#define BUF_FLAGS (RB_FL_OVERWRITE)
> +#define U64STR_SIZE 22
> +#define DEBUGFS_BUF_SIZE 1024
> +#define DEBUGFS_NAME_SIZE 32
> +
> +#define VERSION "0.1.0"
> +#define BANNER "hardware latency test"
> +#define DRVNAME "hw_latency_test"
> +
> +#define DEFAULT_SAMPLE_WINDOW 1000000
> +#define DEFAULT_SAMPLE_WIDTH 500000
> +#define DEFAULT_LAT_THRESHOLD 10
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Luming Yu <luming.yu@...il.com>");
> +MODULE_DESCRIPTION("A simple hardware latency test");
> +MODULE_VERSION(VERSION);
> +
> +static int debug;
> +static int enabled;
> +static int threshold;
> +
> +module_param(debug, int, 0);
> +module_param(enabled, int, 0);
> +module_param(threshold, int, 0);
> +
> +static struct ring_buffer *ring_buffer;
> +static DEFINE_MUTEX(ring_buffer_mutex);
> +static unsigned long buf_size = 262144UL;
> +static struct task_struct *kthread;
> +
> +struct sample {
> + u64 seqnum;
> + u64 duration;
> + struct timespec timestamp;
> + unsigned long lost;
> +};
> +
> +static struct data {
> + struct mutex lock;
> + u64 count;
> + u64 max_sample;
> + u64 threshold;
> +
> + u64 sample_window;
> + u64 sample_width;
> +
> + atomic_t sample_open;
> +
> + wait_queue_head_t wq;
> +} data;
> +
> +static ktime_t now;
> +struct sample_function {
> + const char *name;
> + struct list_head list;
> + int (*get_sample)(void *unused);
> +};
> +static struct sample_function *current_sample_func = NULL;
> +static LIST_HEAD(sample_function_list);
> +static DEFINE_MUTEX(sample_function_mutex);
> +static int sample_function_register(struct sample_function *sf);
> +static struct dentry *debug_dir;
> +
> +static int sample_function_register(struct sample_function *sf)
> +{
> + struct list_head *entry = &sample_function_list;
> + mutex_lock(&sample_function_mutex);
> + list_add(&sf->list, entry);
> + current_sample_func = sf;
> + mutex_unlock(&sample_function_mutex);
> + return 0;
> +}
> +
> +static int __buffer_add_sample(struct sample *sample)
> +{
> + return ring_buffer_write(ring_buffer,
> + sizeof(struct sample), sample);
> +}
> +
> +static struct sample *buffer_get_sample(struct sample *sample)
> +{
> + struct ring_buffer_event *e = NULL;
> + struct sample *s = NULL;
> + unsigned int cpu = 0;
> +
> + if (!sample)
> + return NULL;
> +
> + mutex_lock(&ring_buffer_mutex);
> + for_each_online_cpu(cpu) {
> + e = ring_buffer_consume(ring_buffer, cpu, NULL, &sample->lost);
> + if (e)
> + break;
> + }
> + if (e) {
> + s = ring_buffer_event_data(e);
> + memcpy(sample, s, sizeof(struct sample));
> + } else
> + sample = NULL;
> + mutex_unlock(&ring_buffer_mutex);
> + return sample;
> +}
> +
> +static int buffer_add_sample(u64 sample)
> +{
> + int ret = 0;
> +
> + if (sample > data.threshold) {
> + struct sample s;
> +
> + data.count++;
> + s.seqnum = data.count;
> + s.duration = sample;
> + s.timestamp = CURRENT_TIME;
> + ret = __buffer_add_sample(&s);
> +
> + if (sample > data.max_sample)
> + data.max_sample = sample;
> + }
> + return ret;
> +}
> +
> +/*
> + * For new instruction rdrand since Intel Ivy Bridge processor
> + */
> +static int get_random_bytes_sample(void *unused)
> +{
> + u32 *buffer;
> + ktime_t start, t1, t2;
> + s64 diff, total = 0;
> + u64 sample = 0;
> + int ret = 1;
> +
> + buffer = kzalloc(1024, GFP_KERNEL);
> +
> + start = ktime_get();
> + do {
> +
> + t1 = ktime_get();
> + get_random_bytes(buffer, 1024);
> + t2 = ktime_get();
> + total = ktime_to_us(ktime_sub(t2, start));
> + diff = ktime_to_us(ktime_sub(t2, t1));
> +
> + if (diff < 0) {
> + printk(KERN_ERR BANNER "time running backwards\n");
> + goto out;
> + }
> +
> + if (diff > sample)
> + sample = diff;
> +
> + } while (total <= data.sample_width);
> +
> + ret = buffer_add_sample(sample);
> +out:
> + kfree(buffer);
> + return ret;
> +}
> +
> +/*
> + * For cpu frequency testing
> + */
> +static int get_freq_sample(void *unused)
> +{
> + ktime_t start, t1, t2;
> + s64 diff, total = 0;
> + u32 sample = 0;
> + int ret = 1;
> + unsigned int cpu_tsc_freq;
> + static DEFINE_MUTEX(freq_pit_mutex);
> +
> + start = ktime_get();
> + do {
> + t1 = ktime_get();
> + mutex_lock(&freq_pit_mutex);
> + cpu_tsc_freq = x86_platform.calibrate_tsc();
> + mutex_unlock(&freq_pit_mutex);
> + t2 = ktime_get();
> + total = ktime_to_us(ktime_sub(t2, start));
> + diff = abs(cpu_tsc_freq - tsc_khz);
> +
> + if (diff < 0) {
> + printk(KERN_ERR BANNER "time running backwards\n");
> + goto out;
> + }
> +
> + if (diff > sample)
> + sample = diff;
> +
> + } while (total <= data.sample_width);
> +
> + ret = buffer_add_sample(sample);
> +out:
> + return ret;
> +}
> +
> +/*
> + * For TSC latency as well as SMI detecting
> + */
> +static int get_tsc_sample(void *unused)
> +{
> + ktime_t start, t1, t2;
> + s64 diff, total = 0;
> + u64 sample = 0;
> + int ret = 1;
> +
> + now = start = ktime_get();
> + do {
> + t1 = now;
> + now = t2 = ktime_get();
> +
> + total = ktime_to_ns(ktime_sub(t2, start));
> + diff = ktime_to_ns(ktime_sub(t2, t1));
> +
> + if (diff < 0) {
> + printk(KERN_ERR BANNER "time running backwards\n");
> + goto out;
> + }
> +
> + if (diff > sample)
> + sample = diff;
> +
> + } while (total <= data.sample_width);
> +
> + ret = buffer_add_sample(sample);
> +out:
> + return ret;
> +}
> +
> +
> +struct sample_function tsc_sample = {
> + .name = "tsc",
> + .get_sample = get_tsc_sample,
> +};
> +
> +struct sample_function tsc_freq_sample = {
> + .name = "freq",
> + .get_sample = get_freq_sample,
> +};
> +
> +struct sample_function random_bytes_sample = {
> + .name = "random_bytes",
> + .get_sample = get_random_bytes_sample,
> +};
> +
> +static int kthread_fn(void *unused)
> +{
> + int err = 0;
> + u64 interval = 0;
> + int (*get_sample)(void *unused);
> +
> + mutex_lock(&sample_function_mutex);
> + if (current_sample_func)
> + get_sample = current_sample_func->get_sample;
> + else
> + goto out;
> +
> + while (!kthread_should_stop()) {
> + mutex_lock(&data.lock);
> +
> + err = stop_machine(get_sample, unused, cpu_online_mask);
> + if (err) {
> + mutex_unlock(&data.lock);
> + goto err_out;
> + }
> +
> + wake_up(&data.wq);
> +
> + interval = data.sample_window - data.sample_width;
> + do_div(interval, USEC_PER_MSEC);
> +
> + mutex_unlock(&data.lock);
> + if (msleep_interruptible(interval))
> + goto out;
> + }
> + goto out;
> +err_out:
> + printk(KERN_ERR BANNER "could not call stop_machine, disabling\n");
> + enabled = 0;
> +out:
> + mutex_unlock(&sample_function_mutex);
> + return err;
> +}
> +
> +static int start_kthread(void)
> +{
> + kthread = kthread_run(kthread_fn, NULL, DRVNAME);
> + if (IS_ERR(kthread)) {
> + printk(KERN_ERR BANNER "could not start sampling thread\n");
> + enabled = 0;
> + return -ENOMEM;
> + }
> + return 0;
> +}
> +
> +static int stop_kthread(void)
> +{
> + int ret;
> + ret = kthread_stop(kthread);
> + return ret;
> +}
> +
> +static void __reset_stats(void)
> +{
> + data.count = 0;
> + data.max_sample = 0;
> + ring_buffer_reset(ring_buffer);
> +}
> +
> +static int init_stats(void)
> +{
> + int ret = -ENOMEM;
> +
> + mutex_init(&data.lock);
> + init_waitqueue_head(&data.wq);
> + atomic_set(&data.sample_open,0);
> +
> + ring_buffer = ring_buffer_alloc(buf_size, BUF_FLAGS);
> +
> + if (WARN(!ring_buffer, KERN_ERR BANNER
> + "failed to allocate ring buffer!\n"))
> + goto out;
> + __reset_stats();
> + data.threshold = DEFAULT_LAT_THRESHOLD;
> + data.sample_window = DEFAULT_SAMPLE_WINDOW;
> + data.sample_width = DEFAULT_SAMPLE_WIDTH;
> + ret = 0;
> +out:
> + return ret;
> +}
> +
> +static ssize_t simple_data_read(struct file *filp, char __user *ubuf,
> + size_t cnt, loff_t *ppos, const u64 *entry)
> +{
> + char buf[U64STR_SIZE];
> + u64 val = 0;
> + int len = 0;
> +
> + memset(buf, 0, sizeof(buf));
> + if (!entry)
> + return -EFAULT;
> + mutex_lock(&data.lock);
> + val = *entry;
> + mutex_unlock(&data.lock);
> + len = snprintf(buf, sizeof(buf), "%llu\n", (unsigned long long)val);
> + return simple_read_from_buffer(ubuf, cnt, ppos, buf, len);
> +}
> +
> +static ssize_t simple_data_write(struct file *filp, const char __user *ubuf,
> + size_t cnt, loff_t *ppos, u64 *entry)
> +{
> + char buf[U64STR_SIZE];
> + int csize = min(cnt, sizeof(buf));
> + u64 val = 0;
> + int err = 0;
> +
> + memset(buf, '\0', sizeof(buf));
> + if (copy_from_user(buf, ubuf, csize))
> + return -EFAULT;
> + buf[U64STR_SIZE-1] = '\0';
> + err = strict_strtoull(buf, 10, &val);
> + if (err)
> + return -EINVAL;
> + mutex_lock(&data.lock);
> + *entry = val;
> + mutex_unlock(&data.lock);
> + return csize;
> +}
> +
> +#define debug_available_fopen simple_open
> +
> +static ssize_t debug_available_fread(struct file *filp, char __user *ubuf,
> + size_t cnt, loff_t *ppos)
> +{
> + struct sample_function *sf;
> + ssize_t count = 0;
> + char *buf;
> +
> + buf = kzalloc(DEBUGFS_BUF_SIZE, GFP_KERNEL);
> + if (!buf)
> + return -ENOMEM;
> +
> + mutex_lock(&sample_function_mutex);
> + list_for_each_entry(sf, &sample_function_list, list) {
> + count += snprintf(buf + count,
> + max((ssize_t)(DEBUGFS_BUF_SIZE - count), (ssize_t)0),
> + "%s ", sf->name);
> + }
> + mutex_unlock(&sample_function_mutex);
> +
> + count += snprintf(buf + count,
> + max((ssize_t )DEBUGFS_BUF_SIZE - count, (ssize_t) 0),
> + "\n");
> + count = simple_read_from_buffer(ubuf, cnt, ppos, buf, count);
> + kfree(buf);
> + return count;
> +}
> +
> +#define debug_available_fwrite simple_attr_write
> +
> +#define debug_available_release simple_attr_release
> +
> +#define debug_current_fopen simple_open
> +
> +static ssize_t debug_current_fread(struct file *filp, char __user *ubuf,
> + size_t cnt, loff_t *ppos)
> +{
> + ssize_t count = 0;
> + char *buf;
> +
> + buf = kzalloc(DEBUGFS_NAME_SIZE, GFP_KERNEL);
> + if (!buf)
> + return -ENOMEM;
> +
> + count += snprintf(buf + count,
> + max((ssize_t)DEBUGFS_NAME_SIZE - count, (ssize_t)0),
> + "%s ", current_sample_func->name);
> + count += snprintf(buf + count,
> + max((ssize_t)DEBUGFS_NAME_SIZE - count, (ssize_t)0),
> + "\n");
> + count = simple_read_from_buffer(ubuf, cnt, ppos, buf, count);
> + kfree(buf);
> +
> + return count;
> +}
> +static ssize_t debug_current_fwrite(struct file *filp, const char __user *ubuf,
> + size_t cnt, loff_t *ppos)
> +{
> + char *buf;
> + ssize_t count;
> + struct sample_function *sf;
> +
> + buf = kzalloc(DEBUGFS_NAME_SIZE, GFP_KERNEL);
> + if (!buf)
> + return -ENOMEM;
> + count = simple_write_to_buffer(buf, DEBUGFS_NAME_SIZE, ppos, ubuf, cnt);
> + mutex_lock(&sample_function_mutex);
> + list_for_each_entry(sf, &sample_function_list, list) {
> + if (strncmp(sf->name, buf, count-1) !=0)
> + continue;
> + current_sample_func = sf;
> + break;
> + }
> + mutex_unlock(&sample_function_mutex);
> + return (ssize_t) count;
> +}
> +#define debug_current_release simple_attr_release
> +
> +#define debug_count_fopen simple_open
> +
> +static ssize_t debug_count_fread(struct file *filp, char __user *ubuf,
> + size_t cnt, loff_t *ppos)
> +{
> + return simple_data_read(filp, ubuf, cnt, ppos, &data.count);
> +}
> +static ssize_t debug_count_fwrite(struct file *filp, const char __user *ubuf,
> + size_t cnt,
> + loff_t *ppos)
> +{
> + return simple_data_write(filp, ubuf, cnt, ppos, &data.count);
> +}
> +#define debug_count_release simple_attr_release
> +
> +#define debug_enable_fopen simple_open
> +
> +static ssize_t debug_enable_fread(struct file *filp, char __user *ubuf,
> + size_t cnt, loff_t *ppos)
> +{
> + char buf[4];
> + if ((cnt < sizeof(buf)) || (*ppos))
> + return 0;
> + buf[0] = enabled ? '1' : '0';
> + buf[1] = '\n';
> + buf[2] = '\0';
> + if (copy_to_user(ubuf, buf, strlen(buf)))
> + return -EFAULT;
> + return *ppos = strlen(buf);
> +}
> +static ssize_t debug_enable_fwrite(struct file *filp,
> + const char __user *ubuf,
> + size_t cnt,
> + loff_t *ppos)
> +{
> + char buf[4];
> + int csize = min(cnt, sizeof(buf));
> + long val = 0;
> + int err = 0;
> +
> + memset(buf, '\0', sizeof(buf));
> + if (copy_from_user(buf, ubuf, csize))
> + return -EFAULT;
> + buf[sizeof(buf)-1] = '\0';
> + err = strict_strtoul(buf, 10, &val);
> + if (0 != err)
> + return -EINVAL;
> + if (val) {
> + if (enabled)
> + goto unlock;
> + enabled = 1;
> + if (start_kthread())
> + return -EFAULT;
> + } else {
> + if (!enabled)
> + goto unlock;
> + enabled = 0;
> + err = stop_kthread();
> + if (err) {
> + printk(KERN_ERR BANNER "cannot stop kthread\n");
> + return -EFAULT;
> + }
> + wake_up(&data.wq);
> + }
> +unlock:
> + return csize;
> +}
> +#define debug_enable_release simple_attr_release
> +
> +#define debug_max_fopen simple_open
> +
> +static ssize_t debug_max_fread(struct file *filp, char __user *ubuf,
> + size_t cnt, loff_t *ppos)
> +{
> + return simple_data_read(filp, ubuf, cnt, ppos, &data.max_sample);
> +}
> +static ssize_t debug_max_fwrite(struct file *filp,
> + const char __user *ubuf,
> + size_t cnt,
> + loff_t *ppos)
> +{
> + return simple_data_write(filp, ubuf, cnt, ppos, &data.max_sample);
> +}
> +#define debug_max_release simple_attr_release
> +
> +static int debug_sample_fopen(struct inode *inode, struct file *filp)
> +{
> + if (!atomic_add_unless(&data.sample_open, 1, 1))
> + return -EBUSY;
> + else
> + return 0;
> +}
> +static ssize_t debug_sample_fread(struct file *filp, char __user *ubuf,
> + size_t cnt, loff_t *ppos)
> +{
> + int len = 0;
> + char buf[64];
> + struct sample *sample = NULL;
> +
> + if (!enabled)
> + return 0;
> + sample = kzalloc(sizeof(struct sample), GFP_KERNEL);
> + if(!sample)
> + return -ENOMEM;
> +
> + while (!buffer_get_sample(sample)) {
> + DEFINE_WAIT(wait);
> + if (filp->f_flags & O_NONBLOCK) {
> + len = -EAGAIN;
> + goto out;
> + }
> + prepare_to_wait(&data.wq, &wait, TASK_INTERRUPTIBLE);
> + schedule();
> + finish_wait(&data.wq, &wait);
> + if (signal_pending(current)) {
> + len = -EINTR;
> + goto out;
> + }
> + if (!enabled) {
> + len = 0;
> + goto out;
> + }
> + }
> + len = snprintf(buf, sizeof(buf), "%010lu.%010lu\t%llu\n",
> + sample->timestamp.tv_sec,
> + sample->timestamp.tv_nsec,
> + sample->duration);
> + if (len > cnt)
> + goto out;
> + if (copy_to_user(ubuf, buf,len))
> + len = -EFAULT;
> +out:
> + kfree(sample);
> + return len;
> +}
> +
> +#define debug_sample_fwrite simple_attr_write
> +
> +static int debug_sample_release(struct inode *inode, struct file *filp)
> +{
> + atomic_dec(&data.sample_open);
> + return 0;
> +}
> +
> +#define debug_threshold_fopen simple_open
> +
> +static ssize_t debug_threshold_fread(struct file *filp, char __user *ubuf,
> + size_t cnt, loff_t *ppos)
> +{
> + return simple_data_read(filp, ubuf, cnt, ppos, &data.threshold);
> +}
> +static ssize_t debug_threshold_fwrite(struct file *filp,
> + const char __user *ubuf,
> + size_t cnt,
> + loff_t *ppos)
> +{
> + int ret;
> + ret = simple_data_write(filp, ubuf, cnt, ppos, &data.threshold);
> + if (enabled)
> + wake_up_process(kthread);
> + return ret;
> +}
> +#define debug_threshold_release simple_attr_release
> +
> +#define debug_width_fopen simple_open
> +
> +static ssize_t debug_width_fread(struct file *filp, char __user *ubuf,
> + size_t cnt, loff_t *ppos)
> +{
> + return simple_data_read(filp, ubuf, cnt, ppos, &data.sample_width);
> +}
> +static ssize_t debug_width_fwrite(struct file *filp,
> + const char __user *ubuf,
> + size_t cnt,
> + loff_t *ppos)
> +{
> + char buf[U64STR_SIZE];
> + int csize = min(cnt, sizeof(buf));
> + u64 val = 0;
> + int err = 0;
> +
> + memset(buf, '\0', sizeof(buf));
> + if (copy_from_user(buf, ubuf, csize))
> + return -EFAULT;
> + buf[U64STR_SIZE-1] = '\0';
> + err = strict_strtoull(buf, 10, &val);
> + if (0 != err)
> + return -EINVAL;
> + mutex_lock(&data.lock);
> + if (val < data.sample_window)
> + data.sample_width = val;
> + else {
> + mutex_unlock(&data.lock);
> + return -EINVAL;
> + }
> + mutex_unlock(&data.lock);
> + if (enabled)
> + wake_up_process(kthread);
> +
> + return csize;
> +}
> +#define debug_width_release simple_attr_release
> +
> +#define debug_window_fopen simple_open
> +
> +static ssize_t debug_window_fread(struct file *filp, char __user *ubuf,
> + size_t cnt, loff_t *ppos)
> +{
> + return simple_data_read(filp, ubuf, cnt, ppos, &data.sample_window);
> +}
> +static ssize_t debug_window_fwrite(struct file *filp,
> + const char __user *ubuf,
> + size_t cnt,
> + loff_t *ppos)
> +{
> + char buf[U64STR_SIZE];
> + int csize = min(cnt, sizeof(buf));
> + u64 val = 0;
> + int err = 0;
> +
> + memset(buf, '\0', sizeof(buf));
> + if (copy_from_user(buf, ubuf, csize))
> + return -EFAULT;
> + buf[U64STR_SIZE-1] = '\0';
> + err = strict_strtoull(buf, 10, &val);
> + if (0 != err)
> + return -EINVAL;
> + mutex_lock(&data.lock);
> + if (data.sample_width < val)
> + data.sample_window = val;
> + else {
> + mutex_unlock(&data.lock);
> + return -EINVAL;
> + }
> + mutex_unlock(&data.lock);
> + return csize;
> +}
> +#define debug_window_release simple_attr_release
> +
> +#define DEFINE_DEBUGFS_FILE(name) \
> + static const struct file_operations name##_fops = { \
> + .open = debug_##name##_fopen, \
> + .read = debug_##name##_fread, \
> + .write = debug_##name##_fwrite, \
> + .release = debug_##name##_release, \
> + .owner = THIS_MODULE, \
> + };
> +
> +DEFINE_DEBUGFS_FILE(available)
> +DEFINE_DEBUGFS_FILE(current)
> +DEFINE_DEBUGFS_FILE(count)
> +DEFINE_DEBUGFS_FILE(enable)
> +DEFINE_DEBUGFS_FILE(max)
> +DEFINE_DEBUGFS_FILE(sample)
> +DEFINE_DEBUGFS_FILE(threshold)
> +DEFINE_DEBUGFS_FILE(width)
> +DEFINE_DEBUGFS_FILE(window)
> +
> +#undef DEFINE_DEBUGFS_FILE
> +
> +#undef current
> +#define DEFINE_ENTRY(name) {__stringify(name), &name##_fops, NULL},
> +
> +static struct debugfs_file_table
> +{
> + const char *file_name;
> + const struct file_operations *fops;
> + struct dentry *dentry;
> +} file_table[] = {
> + DEFINE_ENTRY(available)
> + DEFINE_ENTRY(current)
> + DEFINE_ENTRY(sample)
> + DEFINE_ENTRY(count)
> + DEFINE_ENTRY(max)
> + DEFINE_ENTRY(window)
> + DEFINE_ENTRY(threshold)
> + DEFINE_ENTRY(enable)
> + {NULL, NULL,NULL},
> +};
> +#undef DEFINE_ENTRY
> +
> +static int init_debugfs(void)
> +{
> + int ret = -ENOMEM;
> + int i=0;
> +
> + debug_dir = debugfs_create_dir(DRVNAME, NULL);
> + if (!debug_dir)
> + goto err_debug_dir;
> +
> + while (file_table[i].fops) {
> + file_table[i].dentry =
> + debugfs_create_file(file_table[i].file_name, 0444,
> + debug_dir, NULL,
> + file_table[i].fops);
> + if (!file_table[i].dentry)
> + break;
> + i++;
> + }
> + if (file_table[i].fops) {
> + i--;
> + while (i>=0 && file_table[i].fops && file_table[i].dentry) {
> + debugfs_remove(file_table[i].dentry);
> + i--;
> + }
> + debugfs_remove(debug_dir);
> + }
> + ret = 0;
> +err_debug_dir:
> + return ret;
> +}
> +
> +static void free_debugfs(void)
> +{
> + int i=0;
> +
> + while (file_table[i].fops && file_table[i].dentry) {
> + debugfs_remove(file_table[i].dentry);
> + i++;
> + }
> + debugfs_remove(debug_dir);
> +}
> +
> +static int hw_test_init(void)
> +{
> + int ret = -ENOMEM;
> +
> + printk(KERN_INFO BANNER "version %s\n", VERSION);
> +
> + sample_function_register(&tsc_sample);
> + sample_function_register(&tsc_freq_sample);
> + sample_function_register(&random_bytes_sample);
> +
> + ret = init_stats();
> + if (0 != ret)
> + goto out;
> + ret = init_debugfs();
> + if (0 != ret)
> + goto err_stats;
> + if (enabled)
> + ret = start_kthread();
> + goto out;
> +
> +err_stats:
> + ring_buffer_free(ring_buffer);
> +out:
> + return ret;
> +}
> +
> +static void hw_test_exit(void)
> +{
> + int err;
> +
> + if (enabled) {
> + enabled = 0;
> + err = stop_kthread();
> + if (err)
> + printk(KERN_ERR BANNER "cannot stop kthread\n");
> + }
> +
> + free_debugfs();
> + ring_buffer_free(ring_buffer);
> +}
> +
> +module_init(hw_test_init);
> +module_exit(hw_test_exit);
Powered by blists - more mailing lists