Re: [patch update-v1] a simple hardware detector for latency as wellas throughput ver. 0.1.0

From: Luming Yu
Date: Mon Jun 25 2012 - 09:37:47 EST


On Tue, Jun 26, 2012 at 5:23 AM, Luming Yu <luming.yu@xxxxxxxxx> 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@xxxxxxxxx>
> ---
> 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@xxxxxxxxx>");
> +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);
N‹§²æìr¸›yúèšØb²X¬¶ÇvØ^–)Þ{.nÇ+‰·¥Š{±‘êçzX§¶›¡Ü}©ž²ÆzÚ&j:+v‰¨¾«‘êçzZ+€Ê+zf£¢·hšˆ§~†­†Ûiÿûàz¹®w¥¢¸?™¨è­Ú&¢)ßf”ù^jÇy§m…á@A«a¶Úÿ 0¶ìh®å’i