aboutsummaryrefslogtreecommitdiff
path: root/drivers/oprofile
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/oprofile')
-rw-r--r--drivers/oprofile/buffer_sync.c38
-rw-r--r--drivers/oprofile/cpu_buffer.c11
-rw-r--r--drivers/oprofile/event_buffer.c4
-rw-r--r--drivers/oprofile/nmi_timer_int.c176
-rw-r--r--drivers/oprofile/oprof.c21
-rw-r--r--drivers/oprofile/oprof.h13
-rw-r--r--drivers/oprofile/oprofile_files.c33
-rw-r--r--drivers/oprofile/oprofile_perf.c47
-rw-r--r--drivers/oprofile/oprofile_stats.c24
-rw-r--r--drivers/oprofile/oprofile_stats.h5
-rw-r--r--drivers/oprofile/oprofilefs.c93
-rw-r--r--drivers/oprofile/timer_int.c33
12 files changed, 341 insertions, 157 deletions
diff --git a/drivers/oprofile/buffer_sync.c b/drivers/oprofile/buffer_sync.c
index a3984f4ef19..d93b2b6b1f7 100644
--- a/drivers/oprofile/buffer_sync.c
+++ b/drivers/oprofile/buffer_sync.c
@@ -141,6 +141,13 @@ static struct notifier_block module_load_nb = {
.notifier_call = module_load_notify,
};
+static void free_all_tasks(void)
+{
+ /* make sure we don't leak task structs */
+ process_task_mortuary();
+ process_task_mortuary();
+}
+
int sync_start(void)
{
int err;
@@ -148,8 +155,6 @@ int sync_start(void)
if (!zalloc_cpumask_var(&marked_cpus, GFP_KERNEL))
return -ENOMEM;
- mutex_lock(&buffer_mutex);
-
err = task_handoff_register(&task_free_nb);
if (err)
goto out1;
@@ -166,7 +171,6 @@ int sync_start(void)
start_cpu_work();
out:
- mutex_unlock(&buffer_mutex);
return err;
out4:
profile_event_unregister(PROFILE_MUNMAP, &munmap_nb);
@@ -174,6 +178,7 @@ out3:
profile_event_unregister(PROFILE_TASK_EXIT, &task_exit_nb);
out2:
task_handoff_unregister(&task_free_nb);
+ free_all_tasks();
out1:
free_cpumask_var(marked_cpus);
goto out;
@@ -182,20 +187,16 @@ out1:
void sync_stop(void)
{
- /* flush buffers */
- mutex_lock(&buffer_mutex);
end_cpu_work();
unregister_module_notifier(&module_load_nb);
profile_event_unregister(PROFILE_MUNMAP, &munmap_nb);
profile_event_unregister(PROFILE_TASK_EXIT, &task_exit_nb);
task_handoff_unregister(&task_free_nb);
- mutex_unlock(&buffer_mutex);
- flush_cpu_work();
+ barrier(); /* do all of the above first */
- /* make sure we don't leak task structs */
- process_task_mortuary();
- process_task_mortuary();
+ flush_cpu_work();
+ free_all_tasks();
free_cpumask_var(marked_cpus);
}
@@ -215,7 +216,7 @@ static inline unsigned long fast_get_dcookie(struct path *path)
}
-/* Look up the dcookie for the task's first VM_EXECUTABLE mapping,
+/* Look up the dcookie for the task's mm->exe_file,
* which corresponds loosely to "application name". This is
* not strictly necessary but allows oprofile to associate
* shared-library samples with particular applications
@@ -223,21 +224,10 @@ static inline unsigned long fast_get_dcookie(struct path *path)
static unsigned long get_exec_dcookie(struct mm_struct *mm)
{
unsigned long cookie = NO_COOKIE;
- struct vm_area_struct *vma;
-
- if (!mm)
- goto out;
- for (vma = mm->mmap; vma; vma = vma->vm_next) {
- if (!vma->vm_file)
- continue;
- if (!(vma->vm_flags & VM_EXECUTABLE))
- continue;
- cookie = fast_get_dcookie(&vma->vm_file->f_path);
- break;
- }
+ if (mm && mm->exe_file)
+ cookie = fast_get_dcookie(&mm->exe_file->f_path);
-out:
return cookie;
}
diff --git a/drivers/oprofile/cpu_buffer.c b/drivers/oprofile/cpu_buffer.c
index b8ef8ddcc29..8aa73fac6ad 100644
--- a/drivers/oprofile/cpu_buffer.c
+++ b/drivers/oprofile/cpu_buffer.c
@@ -451,14 +451,9 @@ static void wq_sync_buffer(struct work_struct *work)
{
struct oprofile_cpu_buffer *b =
container_of(work, struct oprofile_cpu_buffer, work.work);
- if (b->cpu != smp_processor_id()) {
- printk(KERN_DEBUG "WQ on CPU%d, prefer CPU%d\n",
- smp_processor_id(), b->cpu);
-
- if (!cpu_online(b->cpu)) {
- cancel_delayed_work(&b->work);
- return;
- }
+ if (b->cpu != smp_processor_id() && !cpu_online(b->cpu)) {
+ cancel_delayed_work(&b->work);
+ return;
}
sync_buffer(b->cpu);
diff --git a/drivers/oprofile/event_buffer.c b/drivers/oprofile/event_buffer.c
index dd87e86048b..c0cc4e7ff02 100644
--- a/drivers/oprofile/event_buffer.c
+++ b/drivers/oprofile/event_buffer.c
@@ -82,10 +82,10 @@ int alloc_event_buffer(void)
{
unsigned long flags;
- spin_lock_irqsave(&oprofilefs_lock, flags);
+ raw_spin_lock_irqsave(&oprofilefs_lock, flags);
buffer_size = oprofile_buffer_size;
buffer_watershed = oprofile_buffer_watershed;
- spin_unlock_irqrestore(&oprofilefs_lock, flags);
+ raw_spin_unlock_irqrestore(&oprofilefs_lock, flags);
if (buffer_watershed >= buffer_size)
return -EINVAL;
diff --git a/drivers/oprofile/nmi_timer_int.c b/drivers/oprofile/nmi_timer_int.c
new file mode 100644
index 00000000000..9559829fb23
--- /dev/null
+++ b/drivers/oprofile/nmi_timer_int.c
@@ -0,0 +1,176 @@
+/**
+ * @file nmi_timer_int.c
+ *
+ * @remark Copyright 2011 Advanced Micro Devices, Inc.
+ *
+ * @author Robert Richter <robert.richter@amd.com>
+ */
+
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/errno.h>
+#include <linux/oprofile.h>
+#include <linux/perf_event.h>
+
+#ifdef CONFIG_OPROFILE_NMI_TIMER
+
+static DEFINE_PER_CPU(struct perf_event *, nmi_timer_events);
+static int ctr_running;
+
+static struct perf_event_attr nmi_timer_attr = {
+ .type = PERF_TYPE_HARDWARE,
+ .config = PERF_COUNT_HW_CPU_CYCLES,
+ .size = sizeof(struct perf_event_attr),
+ .pinned = 1,
+ .disabled = 1,
+};
+
+static void nmi_timer_callback(struct perf_event *event,
+ struct perf_sample_data *data,
+ struct pt_regs *regs)
+{
+ event->hw.interrupts = 0; /* don't throttle interrupts */
+ oprofile_add_sample(regs, 0);
+}
+
+static int nmi_timer_start_cpu(int cpu)
+{
+ struct perf_event *event = per_cpu(nmi_timer_events, cpu);
+
+ if (!event) {
+ event = perf_event_create_kernel_counter(&nmi_timer_attr, cpu, NULL,
+ nmi_timer_callback, NULL);
+ if (IS_ERR(event))
+ return PTR_ERR(event);
+ per_cpu(nmi_timer_events, cpu) = event;
+ }
+
+ if (event && ctr_running)
+ perf_event_enable(event);
+
+ return 0;
+}
+
+static void nmi_timer_stop_cpu(int cpu)
+{
+ struct perf_event *event = per_cpu(nmi_timer_events, cpu);
+
+ if (event && ctr_running)
+ perf_event_disable(event);
+}
+
+static int nmi_timer_cpu_notifier(struct notifier_block *b, unsigned long action,
+ void *data)
+{
+ int cpu = (unsigned long)data;
+ switch (action) {
+ case CPU_DOWN_FAILED:
+ case CPU_ONLINE:
+ nmi_timer_start_cpu(cpu);
+ break;
+ case CPU_DOWN_PREPARE:
+ nmi_timer_stop_cpu(cpu);
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block nmi_timer_cpu_nb = {
+ .notifier_call = nmi_timer_cpu_notifier
+};
+
+static int nmi_timer_start(void)
+{
+ int cpu;
+
+ get_online_cpus();
+ ctr_running = 1;
+ for_each_online_cpu(cpu)
+ nmi_timer_start_cpu(cpu);
+ put_online_cpus();
+
+ return 0;
+}
+
+static void nmi_timer_stop(void)
+{
+ int cpu;
+
+ get_online_cpus();
+ for_each_online_cpu(cpu)
+ nmi_timer_stop_cpu(cpu);
+ ctr_running = 0;
+ put_online_cpus();
+}
+
+static void nmi_timer_shutdown(void)
+{
+ struct perf_event *event;
+ int cpu;
+
+ cpu_notifier_register_begin();
+ __unregister_cpu_notifier(&nmi_timer_cpu_nb);
+ for_each_possible_cpu(cpu) {
+ event = per_cpu(nmi_timer_events, cpu);
+ if (!event)
+ continue;
+ perf_event_disable(event);
+ per_cpu(nmi_timer_events, cpu) = NULL;
+ perf_event_release_kernel(event);
+ }
+
+ cpu_notifier_register_done();
+}
+
+static int nmi_timer_setup(void)
+{
+ int cpu, err;
+ u64 period;
+
+ /* clock cycles per tick: */
+ period = (u64)cpu_khz * 1000;
+ do_div(period, HZ);
+ nmi_timer_attr.sample_period = period;
+
+ cpu_notifier_register_begin();
+ err = __register_cpu_notifier(&nmi_timer_cpu_nb);
+ if (err)
+ goto out;
+
+ /* can't attach events to offline cpus: */
+ for_each_online_cpu(cpu) {
+ err = nmi_timer_start_cpu(cpu);
+ if (err) {
+ cpu_notifier_register_done();
+ nmi_timer_shutdown();
+ return err;
+ }
+ }
+
+out:
+ cpu_notifier_register_done();
+ return err;
+}
+
+int __init op_nmi_timer_init(struct oprofile_operations *ops)
+{
+ int err = 0;
+
+ err = nmi_timer_setup();
+ if (err)
+ return err;
+ nmi_timer_shutdown(); /* only check, don't alloc */
+
+ ops->create_files = NULL;
+ ops->setup = nmi_timer_setup;
+ ops->shutdown = nmi_timer_shutdown;
+ ops->start = nmi_timer_start;
+ ops->stop = nmi_timer_stop;
+ ops->cpu_type = "timer";
+
+ printk(KERN_INFO "oprofile: using NMI timer interrupt.\n");
+
+ return 0;
+}
+
+#endif
diff --git a/drivers/oprofile/oprof.c b/drivers/oprofile/oprof.c
index dccd8636095..ed2c3ec0702 100644
--- a/drivers/oprofile/oprof.c
+++ b/drivers/oprofile/oprof.c
@@ -239,26 +239,39 @@ int oprofile_set_ulong(unsigned long *addr, unsigned long val)
return err;
}
+static int timer_mode;
+
static int __init oprofile_init(void)
{
int err;
+ /* always init architecture to setup backtrace support */
+ timer_mode = 0;
err = oprofile_arch_init(&oprofile_ops);
- if (err < 0 || timer) {
- printk(KERN_INFO "oprofile: using timer interrupt.\n");
+ if (!err) {
+ if (!timer && !oprofilefs_register())
+ return 0;
+ oprofile_arch_exit();
+ }
+
+ /* setup timer mode: */
+ timer_mode = 1;
+ /* no nmi timer mode if oprofile.timer is set */
+ if (timer || op_nmi_timer_init(&oprofile_ops)) {
err = oprofile_timer_init(&oprofile_ops);
if (err)
return err;
}
+
return oprofilefs_register();
}
static void __exit oprofile_exit(void)
{
- oprofile_timer_exit();
oprofilefs_unregister();
- oprofile_arch_exit();
+ if (!timer_mode)
+ oprofile_arch_exit();
}
diff --git a/drivers/oprofile/oprof.h b/drivers/oprofile/oprof.h
index 177b73de5e5..d5412060ab0 100644
--- a/drivers/oprofile/oprof.h
+++ b/drivers/oprofile/oprof.h
@@ -30,12 +30,19 @@ extern struct oprofile_operations oprofile_ops;
extern unsigned long oprofile_started;
extern unsigned long oprofile_backtrace_depth;
-struct super_block;
struct dentry;
-void oprofile_create_files(struct super_block *sb, struct dentry *root);
+void oprofile_create_files(struct dentry *root);
int oprofile_timer_init(struct oprofile_operations *ops);
-void oprofile_timer_exit(void);
+#ifdef CONFIG_OPROFILE_NMI_TIMER
+int op_nmi_timer_init(struct oprofile_operations *ops);
+#else
+static inline int op_nmi_timer_init(struct oprofile_operations *ops)
+{
+ return -ENODEV;
+}
+#endif
+
int oprofile_set_ulong(unsigned long *addr, unsigned long val);
int oprofile_set_timeout(unsigned long time);
diff --git a/drivers/oprofile/oprofile_files.c b/drivers/oprofile/oprofile_files.c
index 89f63456646..ee2cfce358b 100644
--- a/drivers/oprofile/oprofile_files.c
+++ b/drivers/oprofile/oprofile_files.c
@@ -45,7 +45,7 @@ static ssize_t timeout_write(struct file *file, char const __user *buf,
return -EINVAL;
retval = oprofilefs_ulong_from_user(&val, buf, count);
- if (retval)
+ if (retval <= 0)
return retval;
retval = oprofile_set_timeout(val);
@@ -84,7 +84,7 @@ static ssize_t depth_write(struct file *file, char const __user *buf, size_t cou
return -EINVAL;
retval = oprofilefs_ulong_from_user(&val, buf, count);
- if (retval)
+ if (retval <= 0)
return retval;
retval = oprofile_set_ulong(&oprofile_backtrace_depth, val);
@@ -141,9 +141,10 @@ static ssize_t enable_write(struct file *file, char const __user *buf, size_t co
return -EINVAL;
retval = oprofilefs_ulong_from_user(&val, buf, count);
- if (retval)
+ if (retval <= 0)
return retval;
+ retval = 0;
if (val)
retval = oprofile_start();
else
@@ -174,7 +175,7 @@ static const struct file_operations dump_fops = {
.llseek = noop_llseek,
};
-void oprofile_create_files(struct super_block *sb, struct dentry *root)
+void oprofile_create_files(struct dentry *root)
{
/* reinitialize default values */
oprofile_buffer_size = BUFFER_SIZE_DEFAULT;
@@ -182,19 +183,19 @@ void oprofile_create_files(struct super_block *sb, struct dentry *root)
oprofile_buffer_watershed = BUFFER_WATERSHED_DEFAULT;
oprofile_time_slice = msecs_to_jiffies(TIME_SLICE_DEFAULT);
- oprofilefs_create_file(sb, root, "enable", &enable_fops);
- oprofilefs_create_file_perm(sb, root, "dump", &dump_fops, 0666);
- oprofilefs_create_file(sb, root, "buffer", &event_buffer_fops);
- oprofilefs_create_ulong(sb, root, "buffer_size", &oprofile_buffer_size);
- oprofilefs_create_ulong(sb, root, "buffer_watershed", &oprofile_buffer_watershed);
- oprofilefs_create_ulong(sb, root, "cpu_buffer_size", &oprofile_cpu_buffer_size);
- oprofilefs_create_file(sb, root, "cpu_type", &cpu_type_fops);
- oprofilefs_create_file(sb, root, "backtrace_depth", &depth_fops);
- oprofilefs_create_file(sb, root, "pointer_size", &pointer_size_fops);
+ oprofilefs_create_file(root, "enable", &enable_fops);
+ oprofilefs_create_file_perm(root, "dump", &dump_fops, 0666);
+ oprofilefs_create_file(root, "buffer", &event_buffer_fops);
+ oprofilefs_create_ulong(root, "buffer_size", &oprofile_buffer_size);
+ oprofilefs_create_ulong(root, "buffer_watershed", &oprofile_buffer_watershed);
+ oprofilefs_create_ulong(root, "cpu_buffer_size", &oprofile_cpu_buffer_size);
+ oprofilefs_create_file(root, "cpu_type", &cpu_type_fops);
+ oprofilefs_create_file(root, "backtrace_depth", &depth_fops);
+ oprofilefs_create_file(root, "pointer_size", &pointer_size_fops);
#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
- oprofilefs_create_file(sb, root, "time_slice", &timeout_fops);
+ oprofilefs_create_file(root, "time_slice", &timeout_fops);
#endif
- oprofile_create_stats_files(sb, root);
+ oprofile_create_stats_files(root);
if (oprofile_ops.create_files)
- oprofile_ops.create_files(sb, root);
+ oprofile_ops.create_files(root);
}
diff --git a/drivers/oprofile/oprofile_perf.c b/drivers/oprofile/oprofile_perf.c
index 9046f7b2ed7..d5b2732b1b8 100644
--- a/drivers/oprofile/oprofile_perf.c
+++ b/drivers/oprofile/oprofile_perf.c
@@ -1,5 +1,6 @@
/*
* Copyright 2010 ARM Ltd.
+ * Copyright 2012 Advanced Micro Devices, Inc., Robert Richter
*
* Perf-events backend for OProfile.
*/
@@ -25,20 +26,20 @@ static int oprofile_perf_enabled;
static DEFINE_MUTEX(oprofile_perf_mutex);
static struct op_counter_config *counter_config;
-static struct perf_event **perf_events[nr_cpumask_bits];
+static DEFINE_PER_CPU(struct perf_event **, perf_events);
static int num_counters;
/*
* Overflow callback for oprofile.
*/
-static void op_overflow_handler(struct perf_event *event, int unused,
+static void op_overflow_handler(struct perf_event *event,
struct perf_sample_data *data, struct pt_regs *regs)
{
int id;
u32 cpu = smp_processor_id();
for (id = 0; id < num_counters; ++id)
- if (perf_events[cpu][id] == event)
+ if (per_cpu(perf_events, cpu)[id] == event)
break;
if (id != num_counters)
@@ -74,12 +75,12 @@ static int op_create_counter(int cpu, int event)
{
struct perf_event *pevent;
- if (!counter_config[event].enabled || perf_events[cpu][event])
+ if (!counter_config[event].enabled || per_cpu(perf_events, cpu)[event])
return 0;
pevent = perf_event_create_kernel_counter(&counter_config[event].attr,
cpu, NULL,
- op_overflow_handler);
+ op_overflow_handler, NULL);
if (IS_ERR(pevent))
return PTR_ERR(pevent);
@@ -91,18 +92,18 @@ static int op_create_counter(int cpu, int event)
return -EBUSY;
}
- perf_events[cpu][event] = pevent;
+ per_cpu(perf_events, cpu)[event] = pevent;
return 0;
}
static void op_destroy_counter(int cpu, int event)
{
- struct perf_event *pevent = perf_events[cpu][event];
+ struct perf_event *pevent = per_cpu(perf_events, cpu)[event];
if (pevent) {
perf_event_release_kernel(pevent);
- perf_events[cpu][event] = NULL;
+ per_cpu(perf_events, cpu)[event] = NULL;
}
}
@@ -137,7 +138,7 @@ static void op_perf_stop(void)
op_destroy_counter(cpu, event);
}
-static int oprofile_perf_create_files(struct super_block *sb, struct dentry *root)
+static int oprofile_perf_create_files(struct dentry *root)
{
unsigned int i;
@@ -146,13 +147,13 @@ static int oprofile_perf_create_files(struct super_block *sb, struct dentry *roo
char buf[4];
snprintf(buf, sizeof buf, "%d", i);
- dir = oprofilefs_mkdir(sb, root, buf);
- oprofilefs_create_ulong(sb, dir, "enabled", &counter_config[i].enabled);
- oprofilefs_create_ulong(sb, dir, "event", &counter_config[i].event);
- oprofilefs_create_ulong(sb, dir, "count", &counter_config[i].count);
- oprofilefs_create_ulong(sb, dir, "unit_mask", &counter_config[i].unit_mask);
- oprofilefs_create_ulong(sb, dir, "kernel", &counter_config[i].kernel);
- oprofilefs_create_ulong(sb, dir, "user", &counter_config[i].user);
+ dir = oprofilefs_mkdir(root, buf);
+ oprofilefs_create_ulong(dir, "enabled", &counter_config[i].enabled);
+ oprofilefs_create_ulong(dir, "event", &counter_config[i].event);
+ oprofilefs_create_ulong(dir, "count", &counter_config[i].count);
+ oprofilefs_create_ulong(dir, "unit_mask", &counter_config[i].unit_mask);
+ oprofilefs_create_ulong(dir, "kernel", &counter_config[i].kernel);
+ oprofilefs_create_ulong(dir, "user", &counter_config[i].user);
}
return 0;
@@ -160,9 +161,9 @@ static int oprofile_perf_create_files(struct super_block *sb, struct dentry *roo
static int oprofile_perf_setup(void)
{
- spin_lock(&oprofilefs_lock);
+ raw_spin_lock(&oprofilefs_lock);
op_perf_setup();
- spin_unlock(&oprofilefs_lock);
+ raw_spin_unlock(&oprofilefs_lock);
return 0;
}
@@ -257,12 +258,12 @@ void oprofile_perf_exit(void)
for_each_possible_cpu(cpu) {
for (id = 0; id < num_counters; ++id) {
- event = perf_events[cpu][id];
+ event = per_cpu(perf_events, cpu)[id];
if (event)
perf_event_release_kernel(event);
}
- kfree(perf_events[cpu]);
+ kfree(per_cpu(perf_events, cpu));
}
kfree(counter_config);
@@ -277,8 +278,6 @@ int __init oprofile_perf_init(struct oprofile_operations *ops)
if (ret)
return ret;
- memset(&perf_events, 0, sizeof(perf_events));
-
num_counters = perf_num_counters();
if (num_counters <= 0) {
pr_info("oprofile: no performance counters\n");
@@ -298,9 +297,9 @@ int __init oprofile_perf_init(struct oprofile_operations *ops)
}
for_each_possible_cpu(cpu) {
- perf_events[cpu] = kcalloc(num_counters,
+ per_cpu(perf_events, cpu) = kcalloc(num_counters,
sizeof(struct perf_event *), GFP_KERNEL);
- if (!perf_events[cpu]) {
+ if (!per_cpu(perf_events, cpu)) {
pr_info("oprofile: failed to allocate %d perf events "
"for cpu %d\n", num_counters, cpu);
ret = -ENOMEM;
diff --git a/drivers/oprofile/oprofile_stats.c b/drivers/oprofile/oprofile_stats.c
index 917d28ebeac..59659cea458 100644
--- a/drivers/oprofile/oprofile_stats.c
+++ b/drivers/oprofile/oprofile_stats.c
@@ -38,7 +38,7 @@ void oprofile_reset_stats(void)
}
-void oprofile_create_stats_files(struct super_block *sb, struct dentry *root)
+void oprofile_create_stats_files(struct dentry *root)
{
struct oprofile_cpu_buffer *cpu_buf;
struct dentry *cpudir;
@@ -46,39 +46,39 @@ void oprofile_create_stats_files(struct super_block *sb, struct dentry *root)
char buf[10];
int i;
- dir = oprofilefs_mkdir(sb, root, "stats");
+ dir = oprofilefs_mkdir(root, "stats");
if (!dir)
return;
for_each_possible_cpu(i) {
cpu_buf = &per_cpu(op_cpu_buffer, i);
snprintf(buf, 10, "cpu%d", i);
- cpudir = oprofilefs_mkdir(sb, dir, buf);
+ cpudir = oprofilefs_mkdir(dir, buf);
/* Strictly speaking access to these ulongs is racy,
* but we can't simply lock them, and they are
* informational only.
*/
- oprofilefs_create_ro_ulong(sb, cpudir, "sample_received",
+ oprofilefs_create_ro_ulong(cpudir, "sample_received",
&cpu_buf->sample_received);
- oprofilefs_create_ro_ulong(sb, cpudir, "sample_lost_overflow",
+ oprofilefs_create_ro_ulong(cpudir, "sample_lost_overflow",
&cpu_buf->sample_lost_overflow);
- oprofilefs_create_ro_ulong(sb, cpudir, "backtrace_aborted",
+ oprofilefs_create_ro_ulong(cpudir, "backtrace_aborted",
&cpu_buf->backtrace_aborted);
- oprofilefs_create_ro_ulong(sb, cpudir, "sample_invalid_eip",
+ oprofilefs_create_ro_ulong(cpudir, "sample_invalid_eip",
&cpu_buf->sample_invalid_eip);
}
- oprofilefs_create_ro_atomic(sb, dir, "sample_lost_no_mm",
+ oprofilefs_create_ro_atomic(dir, "sample_lost_no_mm",
&oprofile_stats.sample_lost_no_mm);
- oprofilefs_create_ro_atomic(sb, dir, "sample_lost_no_mapping",
+ oprofilefs_create_ro_atomic(dir, "sample_lost_no_mapping",
&oprofile_stats.sample_lost_no_mapping);
- oprofilefs_create_ro_atomic(sb, dir, "event_lost_overflow",
+ oprofilefs_create_ro_atomic(dir, "event_lost_overflow",
&oprofile_stats.event_lost_overflow);
- oprofilefs_create_ro_atomic(sb, dir, "bt_lost_no_mapping",
+ oprofilefs_create_ro_atomic(dir, "bt_lost_no_mapping",
&oprofile_stats.bt_lost_no_mapping);
#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
- oprofilefs_create_ro_atomic(sb, dir, "multiplex_counter",
+ oprofilefs_create_ro_atomic(dir, "multiplex_counter",
&oprofile_stats.multiplex_counter);
#endif
}
diff --git a/drivers/oprofile/oprofile_stats.h b/drivers/oprofile/oprofile_stats.h
index 0b54e46c3c1..1fc622bd183 100644
--- a/drivers/oprofile/oprofile_stats.h
+++ b/drivers/oprofile/oprofile_stats.h
@@ -10,7 +10,7 @@
#ifndef OPROFILE_STATS_H
#define OPROFILE_STATS_H
-#include <asm/atomic.h>
+#include <linux/atomic.h>
struct oprofile_stat_struct {
atomic_t sample_lost_no_mm;
@@ -25,10 +25,9 @@ extern struct oprofile_stat_struct oprofile_stats;
/* reset all stats to zero */
void oprofile_reset_stats(void);
-struct super_block;
struct dentry;
/* create the stats/ dir */
-void oprofile_create_stats_files(struct super_block *sb, struct dentry *root);
+void oprofile_create_stats_files(struct dentry *root);
#endif /* OPROFILE_STATS_H */
diff --git a/drivers/oprofile/oprofilefs.c b/drivers/oprofile/oprofilefs.c
index e9ff6f7770b..3f493459378 100644
--- a/drivers/oprofile/oprofilefs.c
+++ b/drivers/oprofile/oprofilefs.c
@@ -21,7 +21,7 @@
#define OPROFILEFS_MAGIC 0x6f70726f
-DEFINE_SPINLOCK(oprofilefs_lock);
+DEFINE_RAW_SPINLOCK(oprofilefs_lock);
static struct inode *oprofilefs_get_inode(struct super_block *sb, int mode)
{
@@ -60,6 +60,13 @@ ssize_t oprofilefs_ulong_to_user(unsigned long val, char __user *buf, size_t cou
}
+/*
+ * Note: If oprofilefs_ulong_from_user() returns 0, then *val remains
+ * unchanged and might be uninitialized. This follows write syscall
+ * implementation when count is zero: "If count is zero ... [and if]
+ * no errors are detected, 0 will be returned without causing any
+ * other effect." (man 2 write)
+ */
int oprofilefs_ulong_from_user(unsigned long *val, char const __user *buf, size_t count)
{
char tmpbuf[TMPBUFSIZE];
@@ -76,10 +83,10 @@ int oprofilefs_ulong_from_user(unsigned long *val, char const __user *buf, size_
if (copy_from_user(tmpbuf, buf, count))
return -EFAULT;
- spin_lock_irqsave(&oprofilefs_lock, flags);
+ raw_spin_lock_irqsave(&oprofilefs_lock, flags);
*val = simple_strtoul(tmpbuf, NULL, 0);
- spin_unlock_irqrestore(&oprofilefs_lock, flags);
- return 0;
+ raw_spin_unlock_irqrestore(&oprofilefs_lock, flags);
+ return count;
}
@@ -99,7 +106,7 @@ static ssize_t ulong_write_file(struct file *file, char const __user *buf, size_
return -EINVAL;
retval = oprofilefs_ulong_from_user(&value, buf, count);
- if (retval)
+ if (retval <= 0)
return retval;
retval = oprofile_set_ulong(file->private_data, value);
@@ -110,63 +117,59 @@ static ssize_t ulong_write_file(struct file *file, char const __user *buf, size_
}
-static int default_open(struct inode *inode, struct file *filp)
-{
- if (inode->i_private)
- filp->private_data = inode->i_private;
- return 0;
-}
-
-
static const struct file_operations ulong_fops = {
.read = ulong_read_file,
.write = ulong_write_file,
- .open = default_open,
+ .open = simple_open,
.llseek = default_llseek,
};
static const struct file_operations ulong_ro_fops = {
.read = ulong_read_file,
- .open = default_open,
+ .open = simple_open,
.llseek = default_llseek,
};
-static int __oprofilefs_create_file(struct super_block *sb,
- struct dentry *root, char const *name, const struct file_operations *fops,
- int perm, void *priv)
+static int __oprofilefs_create_file(struct dentry *root, char const *name,
+ const struct file_operations *fops, int perm, void *priv)
{
struct dentry *dentry;
struct inode *inode;
+ mutex_lock(&root->d_inode->i_mutex);
dentry = d_alloc_name(root, name);
- if (!dentry)
+ if (!dentry) {
+ mutex_unlock(&root->d_inode->i_mutex);
return -ENOMEM;
- inode = oprofilefs_get_inode(sb, S_IFREG | perm);
+ }
+ inode = oprofilefs_get_inode(root->d_sb, S_IFREG | perm);
if (!inode) {
dput(dentry);
+ mutex_unlock(&root->d_inode->i_mutex);
return -ENOMEM;
}
inode->i_fop = fops;
+ inode->i_private = priv;
d_add(dentry, inode);
- dentry->d_inode->i_private = priv;
+ mutex_unlock(&root->d_inode->i_mutex);
return 0;
}
-int oprofilefs_create_ulong(struct super_block *sb, struct dentry *root,
+int oprofilefs_create_ulong(struct dentry *root,
char const *name, unsigned long *val)
{
- return __oprofilefs_create_file(sb, root, name,
+ return __oprofilefs_create_file(root, name,
&ulong_fops, 0644, val);
}
-int oprofilefs_create_ro_ulong(struct super_block *sb, struct dentry *root,
+int oprofilefs_create_ro_ulong(struct dentry *root,
char const *name, unsigned long *val)
{
- return __oprofilefs_create_file(sb, root, name,
+ return __oprofilefs_create_file(root, name,
&ulong_ro_fops, 0444, val);
}
@@ -180,50 +183,54 @@ static ssize_t atomic_read_file(struct file *file, char __user *buf, size_t coun
static const struct file_operations atomic_ro_fops = {
.read = atomic_read_file,
- .open = default_open,
+ .open = simple_open,
.llseek = default_llseek,
};
-int oprofilefs_create_ro_atomic(struct super_block *sb, struct dentry *root,
+int oprofilefs_create_ro_atomic(struct dentry *root,
char const *name, atomic_t *val)
{
- return __oprofilefs_create_file(sb, root, name,
+ return __oprofilefs_create_file(root, name,
&atomic_ro_fops, 0444, val);
}
-int oprofilefs_create_file(struct super_block *sb, struct dentry *root,
+int oprofilefs_create_file(struct dentry *root,
char const *name, const struct file_operations *fops)
{
- return __oprofilefs_create_file(sb, root, name, fops, 0644, NULL);
+ return __oprofilefs_create_file(root, name, fops, 0644, NULL);
}
-int oprofilefs_create_file_perm(struct super_block *sb, struct dentry *root,
+int oprofilefs_create_file_perm(struct dentry *root,
char const *name, const struct file_operations *fops, int perm)
{
- return __oprofilefs_create_file(sb, root, name, fops, perm, NULL);
+ return __oprofilefs_create_file(root, name, fops, perm, NULL);
}
-struct dentry *oprofilefs_mkdir(struct super_block *sb,
- struct dentry *root, char const *name)
+struct dentry *oprofilefs_mkdir(struct dentry *parent, char const *name)
{
struct dentry *dentry;
struct inode *inode;
- dentry = d_alloc_name(root, name);
- if (!dentry)
+ mutex_lock(&parent->d_inode->i_mutex);
+ dentry = d_alloc_name(parent, name);
+ if (!dentry) {
+ mutex_unlock(&parent->d_inode->i_mutex);
return NULL;
- inode = oprofilefs_get_inode(sb, S_IFDIR | 0755);
+ }
+ inode = oprofilefs_get_inode(parent->d_sb, S_IFDIR | 0755);
if (!inode) {
dput(dentry);
+ mutex_unlock(&parent->d_inode->i_mutex);
return NULL;
}
inode->i_op = &simple_dir_inode_operations;
inode->i_fop = &simple_dir_operations;
d_add(dentry, inode);
+ mutex_unlock(&parent->d_inode->i_mutex);
return dentry;
}
@@ -231,7 +238,6 @@ struct dentry *oprofilefs_mkdir(struct super_block *sb,
static int oprofilefs_fill_super(struct super_block *sb, void *data, int silent)
{
struct inode *root_inode;
- struct dentry *root_dentry;
sb->s_blocksize = PAGE_CACHE_SIZE;
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
@@ -244,15 +250,11 @@ static int oprofilefs_fill_super(struct super_block *sb, void *data, int silent)
return -ENOMEM;
root_inode->i_op = &simple_dir_inode_operations;
root_inode->i_fop = &simple_dir_operations;
- root_dentry = d_alloc_root(root_inode);
- if (!root_dentry) {
- iput(root_inode);
+ sb->s_root = d_make_root(root_inode);
+ if (!sb->s_root)
return -ENOMEM;
- }
-
- sb->s_root = root_dentry;
- oprofile_create_files(sb, root_dentry);
+ oprofile_create_files(sb->s_root);
// FIXME: verify kill_litter_super removes our dentries
return 0;
@@ -272,6 +274,7 @@ static struct file_system_type oprofilefs_type = {
.mount = oprofilefs_mount,
.kill_sb = kill_litter_super,
};
+MODULE_ALIAS_FS("oprofilefs");
int __init oprofilefs_register(void)
diff --git a/drivers/oprofile/timer_int.c b/drivers/oprofile/timer_int.c
index 3ef44624f51..61be1d9c16c 100644
--- a/drivers/oprofile/timer_int.c
+++ b/drivers/oprofile/timer_int.c
@@ -74,8 +74,8 @@ static void oprofile_hrtimer_stop(void)
put_online_cpus();
}
-static int __cpuinit oprofile_cpu_notify(struct notifier_block *self,
- unsigned long action, void *hcpu)
+static int oprofile_cpu_notify(struct notifier_block *self,
+ unsigned long action, void *hcpu)
{
long cpu = (long) hcpu;
@@ -97,23 +97,24 @@ static struct notifier_block __refdata oprofile_cpu_notifier = {
.notifier_call = oprofile_cpu_notify,
};
-int oprofile_timer_init(struct oprofile_operations *ops)
+static int oprofile_hrtimer_setup(void)
{
- int rc;
-
- rc = register_hotcpu_notifier(&oprofile_cpu_notifier);
- if (rc)
- return rc;
- ops->create_files = NULL;
- ops->setup = NULL;
- ops->shutdown = NULL;
- ops->start = oprofile_hrtimer_start;
- ops->stop = oprofile_hrtimer_stop;
- ops->cpu_type = "timer";
- return 0;
+ return register_hotcpu_notifier(&oprofile_cpu_notifier);
}
-void oprofile_timer_exit(void)
+static void oprofile_hrtimer_shutdown(void)
{
unregister_hotcpu_notifier(&oprofile_cpu_notifier);
}
+
+int oprofile_timer_init(struct oprofile_operations *ops)
+{
+ ops->create_files = NULL;
+ ops->setup = oprofile_hrtimer_setup;
+ ops->shutdown = oprofile_hrtimer_shutdown;
+ ops->start = oprofile_hrtimer_start;
+ ops->stop = oprofile_hrtimer_stop;
+ ops->cpu_type = "timer";
+ printk(KERN_INFO "oprofile: using timer interrupt.\n");
+ return 0;
+}