Commit b179d48b authored by Daniel Bristot de Oliveira's avatar Daniel Bristot de Oliveira Committed by Steven Rostedt (Google)
Browse files

tracing/osnoise: Add osnoise/options file

Add the tracing/osnoise/options file to control
osnoise/timerlat tracer features. It is a single
file to contain multiple features, similar to
the sched/features file.

Reading the file displays a list of options. Writing
the OPTION_NAME enables it, writing NO_OPTION_NAME disables
it.

The DEAFULTS is a particular option that resets the options
to the default ones.

It uses a bitmask to keep track of the status of the option. When
needed, we can add a list of static keys, but for now
it does not justify the memory increase.

Link: https://lkml.kernel.org/r/f8d34aefdb225d2603fcb4c02a120832a0cd3339.1668692096.git.bristot@kernel.org



Cc: Daniel Bristot de Oliveira <bristot@kernel.org>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Jonathan Corbet <corbet@lwn.net>
Signed-off-by: default avatarDaniel Bristot de Oliveira <bristot@kernel.org>
Signed-off-by: default avatarSteven Rostedt (Google) <rostedt@goodmis.org>
parent 04aabc32
Loading
Loading
Loading
Loading
+170 −0
Original line number Diff line number Diff line
@@ -48,6 +48,19 @@
#define DEFAULT_TIMERLAT_PERIOD	1000			/* 1ms */
#define DEFAULT_TIMERLAT_PRIO	95			/* FIFO 95 */

/*
 * osnoise/options entries.
 */
enum osnoise_options_index {
	OSN_DEFAULTS = 0,
	OSN_MAX
};

static const char * const osnoise_options_str[OSN_MAX] = { "DEFAULTS" };

#define OSN_DEFAULT_OPTIONS	0
unsigned long osnoise_options	= OSN_DEFAULT_OPTIONS;

/*
 * trace_array of the enabled osnoise/timerlat instances.
 */
@@ -1860,6 +1873,150 @@ static void osnoise_init_hotplug_support(void)
}
#endif /* CONFIG_HOTPLUG_CPU */

/*
 * seq file functions for the osnoise/options file.
 */
static void *s_options_start(struct seq_file *s, loff_t *pos)
{
	int option = *pos;

	mutex_lock(&interface_lock);

	if (option >= OSN_MAX)
		return NULL;

	return pos;
}

static void *s_options_next(struct seq_file *s, void *v, loff_t *pos)
{
	int option = ++(*pos);

	if (option >= OSN_MAX)
		return NULL;

	return pos;
}

static int s_options_show(struct seq_file *s, void *v)
{
	loff_t *pos = v;
	int option = *pos;

	if (option == OSN_DEFAULTS) {
		if (osnoise_options == OSN_DEFAULT_OPTIONS)
			seq_printf(s, "%s", osnoise_options_str[option]);
		else
			seq_printf(s, "NO_%s", osnoise_options_str[option]);
		goto out;
	}

	if (test_bit(option, &osnoise_options))
		seq_printf(s, "%s", osnoise_options_str[option]);
	else
		seq_printf(s, "NO_%s", osnoise_options_str[option]);

out:
	if (option != OSN_MAX)
		seq_puts(s, " ");

	return 0;
}

static void s_options_stop(struct seq_file *s, void *v)
{
	seq_puts(s, "\n");
	mutex_unlock(&interface_lock);
}

static const struct seq_operations osnoise_options_seq_ops = {
	.start		= s_options_start,
	.next		= s_options_next,
	.show		= s_options_show,
	.stop		= s_options_stop
};

static int osnoise_options_open(struct inode *inode, struct file *file)
{
	return seq_open(file, &osnoise_options_seq_ops);
};

/**
 * osnoise_options_write - Write function for "options" entry
 * @filp: The active open file structure
 * @ubuf: The user buffer that contains the value to write
 * @cnt: The maximum number of bytes to write to "file"
 * @ppos: The current position in @file
 *
 * Writing the option name sets the option, writing the "NO_"
 * prefix in front of the option name disables it.
 *
 * Writing "DEFAULTS" resets the option values to the default ones.
 */
static ssize_t osnoise_options_write(struct file *filp, const char __user *ubuf,
				     size_t cnt, loff_t *ppos)
{
	int running, option, enable, retval;
	char buf[256], *option_str;

	if (cnt >= 256)
		return -EINVAL;

	if (copy_from_user(buf, ubuf, cnt))
		return -EFAULT;

	buf[cnt] = 0;

	if (strncmp(buf, "NO_", 3)) {
		option_str = strstrip(buf);
		enable = true;
	} else {
		option_str = strstrip(&buf[3]);
		enable = false;
	}

	option = match_string(osnoise_options_str, OSN_MAX, option_str);
	if (option < 0)
		return -EINVAL;

	/*
	 * trace_types_lock is taken to avoid concurrency on start/stop.
	 */
	mutex_lock(&trace_types_lock);
	running = osnoise_has_registered_instances();
	if (running)
		stop_per_cpu_kthreads();

	mutex_lock(&interface_lock);
	/*
	 * avoid CPU hotplug operations that might read options.
	 */
	cpus_read_lock();

	retval = cnt;

	if (enable) {
		if (option == OSN_DEFAULTS)
			osnoise_options = OSN_DEFAULT_OPTIONS;
		else
			set_bit(option, &osnoise_options);
	} else {
		if (option == OSN_DEFAULTS)
			retval = -EINVAL;
		else
			clear_bit(option, &osnoise_options);
	}

	cpus_read_unlock();
	mutex_unlock(&interface_lock);

	if (running)
		start_per_cpu_kthreads();
	mutex_unlock(&trace_types_lock);

	return retval;
}

/*
 * osnoise_cpus_read - Read function for reading the "cpus" file
 * @filp: The active open file structure
@@ -2042,6 +2199,14 @@ static const struct file_operations cpus_fops = {
	.llseek		= generic_file_llseek,
};

static const struct file_operations osnoise_options_fops = {
	.open		= osnoise_options_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= seq_release,
	.write		= osnoise_options_write
};

#ifdef CONFIG_TIMERLAT_TRACER
#ifdef CONFIG_STACKTRACE
static int init_timerlat_stack_tracefs(struct dentry *top_dir)
@@ -2128,6 +2293,11 @@ static int init_tracefs(void)
	if (!tmp)
		goto err;

	tmp = trace_create_file("options", TRACE_MODE_WRITE, top_dir, NULL,
				&osnoise_options_fops);
	if (!tmp)
		goto err;

	ret = init_timerlat_tracefs(top_dir);
	if (ret)
		goto err;