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

Luming Yu
2012-06-25T13:37:55Z

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=UTF-8, Size: 32410 bytes --]

On Tue, Jun 26, 2012 at 5:23 AM, Luming Yu <luming.yu@gmail.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@intel.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@gmail.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);
ÿôèº{.nÇ+‰·Ÿ®‰­†+%ŠËÿ±éÝ¶\x17¥Šwÿº{.nÇ+‰·¥Š{±þG«éÿŠ{ayº\x1dÊ‡Ú™ë,j\a­¢f£¢·hšïêÿ‘êçz_è®\x03(­éšŽŠÝ¢j"ú\x1a¶^[m§ÿÿ¾\a«þG«éÿ¢¸?™¨è­Ú&£ø§~á¶iO•æ¬z·švØ^\x14\x04\x1a¶^[m§ÿÿÃ\fÿ¶ìÿ¢¸?–I¥