From: "Luis Claudio R. Goncalves" <lclaudio@uudg.org>
To: linux-rt-users <linux-rt-users@vger.kernel.org>
Cc: Steven Rostedt <rostedt@goodmis.org>, Ingo Molnar <mingo@elte.hu>,
williams <williams@redhat.com>, tglx <tglx@linutronix.de>
Subject: [PATCH 4/9] tracing: make the function profiler per cpu
Date: Tue, 19 May 2009 11:52:57 -0300 [thread overview]
Message-ID: <20090519145257.GL27687@unix.sh> (raw)
In-Reply-To: <20090519143607.GH27687@unix.sh>
Impact: speed enhancement
Backport to 2.6.29.3-rt14.
| From: Steven Rostedt <srostedt@redhat.com>
|
| Impact: speed enhancement
|
| By making the function profiler record in per cpu data we not only
| get better readings, avoid races, we also do not have to take any
| locks.
|
| Signed-off-by: Steven Rostedt <srostedt@redhat.com>
Signed-off-by: Luis Claudio R. Goncalves <lgoncalv@redhat.com>
---
kernel/trace/ftrace.c | 201 ++++++++++++++++++++++++++++++++-----------------
1 files changed, 131 insertions(+), 70 deletions(-)
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index a9ccd71..ed1fc50 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -257,28 +257,28 @@ struct ftrace_profile_page {
struct ftrace_profile records[];
};
+struct ftrace_profile_stat {
+ atomic_t disabled;
+ struct hlist_head *hash;
+ struct ftrace_profile_page *pages;
+ struct ftrace_profile_page *start;
+ struct tracer_stat stat;
+};
+
#define PROFILE_RECORDS_SIZE \
(PAGE_SIZE - offsetof(struct ftrace_profile_page, records))
#define PROFILES_PER_PAGE \
(PROFILE_RECORDS_SIZE / sizeof(struct ftrace_profile))
-/* TODO: make these percpu, to prevent cache line bouncing */
-static struct ftrace_profile_page *profile_pages_start;
-static struct ftrace_profile_page *profile_pages;
-
-static struct hlist_head *ftrace_profile_hash;
static int ftrace_profile_bits;
static int ftrace_profile_enabled;
static DEFINE_MUTEX(ftrace_profile_lock);
-static DEFINE_PER_CPU(atomic_t, ftrace_profile_disable);
+static DEFINE_PER_CPU(struct ftrace_profile_stat, ftrace_profile_stats);
#define FTRACE_PROFILE_HASH_SIZE 1024 /* must be power of 2 */
-static raw_spinlock_t ftrace_profile_rec_lock =
- (raw_spinlock_t)__RAW_SPIN_LOCK_UNLOCKED;
-
static void *
function_stat_next(void *v, int idx)
{
@@ -303,7 +303,13 @@ function_stat_next(void *v, int idx)
static void *function_stat_start(void)
{
- return function_stat_next(&profile_pages_start->records[0], 0);
+ struct ftrace_profile_stat *stat =
+ container_of(trace, struct ftrace_profile_stat, stat);
+
+ if (!stat || !stat->start)
+ return NULL;
+
+ return function_stat_next(&stat->start->records[0], 0);
}
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
@@ -374,20 +380,11 @@ static int function_stat_show(struct seq_file *m, void *v)
return 0;
}
-static struct tracer_stat function_stats = {
- .name = "functions",
- .stat_start = function_stat_start,
- .stat_next = function_stat_next,
- .stat_cmp = function_stat_cmp,
- .stat_headers = function_stat_headers,
- .stat_show = function_stat_show
-};
-
-static void ftrace_profile_reset(void)
+static void ftrace_profile_reset(struct ftrace_profile_stat *stat)
{
struct ftrace_profile_page *pg;
- pg = profile_pages = profile_pages_start;
+ pg = stat->pages = stat->start;
while (pg) {
memset(pg->records, 0, PROFILE_RECORDS_SIZE);
@@ -395,24 +392,24 @@ static void ftrace_profile_reset(void)
pg = pg->next;
}
- memset(ftrace_profile_hash, 0,
+ memset(stat->hash, 0,
FTRACE_PROFILE_HASH_SIZE * sizeof(struct hlist_head));
}
-int ftrace_profile_pages_init(void)
+int ftrace_profile_pages_init(struct ftrace_profile_stat *stat)
{
struct ftrace_profile_page *pg;
int i;
/* If we already allocated, do nothing */
- if (profile_pages)
+ if (stat->pages)
return 0;
- profile_pages = (void *)get_zeroed_page(GFP_KERNEL);
- if (!profile_pages)
+ stat->pages = (void *)get_zeroed_page(GFP_KERNEL);
+ if (!stat->pages)
return -ENOMEM;
- pg = profile_pages_start = profile_pages;
+ pg = stat->start = stat->pages;
/* allocate 10 more pages to start */
for (i = 0; i < 10; i++) {
@@ -430,13 +427,16 @@ int ftrace_profile_pages_init(void)
return 0;
}
-static int ftrace_profile_init(void)
+static int ftrace_profile_init_cpu(int cpu)
{
+ struct ftrace_profile_stat *stat;
int size;
- if (ftrace_profile_hash) {
+ stat = &per_cpu(ftrace_profile_stats, cpu);
+
+ if (stat->hash) {
/* If the profile is already created, simply reset it */
- ftrace_profile_reset();
+ ftrace_profile_reset(stat);
return 0;
}
@@ -446,29 +446,45 @@ static int ftrace_profile_init(void)
*/
size = FTRACE_PROFILE_HASH_SIZE;
- ftrace_profile_hash =
- kzalloc(sizeof(struct hlist_head) * size, GFP_KERNEL);
+ stat->hash = kzalloc(sizeof(struct hlist_head) * size, GFP_KERNEL);
- if (!ftrace_profile_hash)
+ if (!stat->hash)
return -ENOMEM;
- size--;
+ if (!ftrace_profile_bits) {
+ size--;
- for (; size; size >>= 1)
- ftrace_profile_bits++;
+ for (; size; size >>= 1)
+ ftrace_profile_bits++;
+ }
/* Preallocate a few pages */
- if (ftrace_profile_pages_init() < 0) {
- kfree(ftrace_profile_hash);
- ftrace_profile_hash = NULL;
+ if (ftrace_profile_pages_init(stat) < 0) {
+ kfree(stat->hash);
+ stat->hash = NULL;
return -ENOMEM;
}
return 0;
}
+static int ftrace_profile_init(void)
+{
+ int cpu;
+ int ret = 0;
+
+ for_each_online_cpu(cpu) {
+ ret = ftrace_profile_init_cpu(cpu);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
/* interrupts must be disabled */
-static struct ftrace_profile *ftrace_find_profiled_func(unsigned long ip)
+static struct ftrace_profile *
+ftrace_find_profiled_func(struct ftrace_profile_stat *stat, unsigned long ip)
{
struct ftrace_profile *rec;
struct hlist_head *hhd;
@@ -476,7 +492,7 @@ static struct ftrace_profile *ftrace_find_profiled_func(unsigned long ip)
unsigned long key;
key = hash_long(ip, ftrace_profile_bits);
- hhd = &ftrace_profile_hash[key];
+ hhd = &stat->hash[key];
if (hlist_empty(hhd))
return NULL;
@@ -489,52 +505,50 @@ static struct ftrace_profile *ftrace_find_profiled_func(unsigned long ip)
return NULL;
}
-static void ftrace_add_profile(struct ftrace_profile *rec)
+static void ftrace_add_profile(struct ftrace_profile_stat *stat,
+ struct ftrace_profile *rec)
{
unsigned long key;
key = hash_long(rec->ip, ftrace_profile_bits);
- hlist_add_head_rcu(&rec->node, &ftrace_profile_hash[key]);
+ hlist_add_head_rcu(&rec->node, &stat->hash[key]);
}
/* Interrupts must be disabled calling this */
static struct ftrace_profile *
-ftrace_profile_alloc(unsigned long ip, bool alloc_safe)
+ftrace_profile_alloc(struct ftrace_profile_stat *stat,
+ unsigned long ip, bool alloc_safe)
{
struct ftrace_profile *rec = NULL;
/* prevent recursion */
- if (atomic_inc_return(&__get_cpu_var(ftrace_profile_disable)) != 1)
+ if (atomic_inc_return(&stat->disabled) != 1)
goto out;
- __raw_spin_lock(&ftrace_profile_rec_lock);
-
/* Try to always keep another page available */
- if (!profile_pages->next && alloc_safe)
- profile_pages->next = (void *)get_zeroed_page(GFP_ATOMIC);
+ if (!stat->pages->next && alloc_safe)
+ stat->pages->next = (void *)get_zeroed_page(GFP_ATOMIC);
/*
* Try to find the function again since another
* task on another CPU could have added it
*/
- rec = ftrace_find_profiled_func(ip);
+ rec = ftrace_find_profiled_func(stat, ip);
if (rec)
- goto out_unlock;
+ goto out;
- if (profile_pages->index == PROFILES_PER_PAGE) {
- if (!profile_pages->next)
- goto out_unlock;
- profile_pages = profile_pages->next;
+ if (stat->pages->index == PROFILES_PER_PAGE) {
+ if (!stat->pages->next)
+ goto out;
+ stat->pages = stat->pages->next;
}
- rec = &profile_pages->records[profile_pages->index++];
+ rec = &stat->pages->records[stat->pages->index++];
rec->ip = ip;
- ftrace_add_profile(rec);
+ ftrace_add_profile(stat, rec);
- out_unlock:
- __raw_spin_unlock(&ftrace_profile_rec_lock);
out:
- atomic_dec(&__get_cpu_var(ftrace_profile_disable));
+ atomic_dec(&stat->disabled);
return rec;
}
@@ -552,6 +566,7 @@ static bool ftrace_safe_to_allocate(void)
static void
function_profile_call(unsigned long ip, unsigned long parent_ip)
{
+ struct ftrace_profile_stat *stat;
struct ftrace_profile *rec;
unsigned long flags;
bool alloc_safe;
@@ -562,9 +577,14 @@ function_profile_call(unsigned long ip, unsigned long parent_ip)
alloc_safe = ftrace_safe_to_allocate();
local_irq_save(flags);
- rec = ftrace_find_profiled_func(ip);
+
+ stat = &__get_cpu_var(ftrace_profile_stats);
+ if (!stat->hash)
+ goto out;
+
+ rec = ftrace_find_profiled_func(stat, ip);
if (!rec) {
- rec = ftrace_profile_alloc(ip, alloc_safe);
+ rec = ftrace_profile_alloc(stat, ip, alloc_safe);
if (!rec)
goto out;
}
@@ -583,13 +603,19 @@ static int profile_graph_entry(struct ftrace_graph_ent *trace)
static void profile_graph_return(struct ftrace_graph_ret *trace)
{
- unsigned long flags;
+ struct ftrace_profile_stat *stat;
struct ftrace_profile *rec;
+ unsigned long flags;
local_irq_save(flags);
- rec = ftrace_find_profiled_func(trace->func);
+ stat = &__get_cpu_var(ftrace_profile_stats);
+ if (!stat->hash)
+ goto out;
+
+ rec = ftrace_find_profiled_func(stat, trace->func);
if (rec)
rec->time += trace->rettime - trace->calltime;
+ out:
local_irq_restore(flags);
}
@@ -687,16 +713,51 @@ static const struct file_operations ftrace_profile_fops = {
.write = ftrace_profile_write,
};
+/* used to initialize the real stat files */
+static struct tracer_stat function_stats __initdata = {
+ .name = "functions",
+ .stat_start = function_stat_start,
+ .stat_next = function_stat_next,
+ .stat_cmp = function_stat_cmp,
+ .stat_headers = function_stat_headers,
+ .stat_show = function_stat_show
+};
+
static void ftrace_profile_debugfs(struct dentry *d_tracer)
{
+ struct ftrace_profile_stat *stat;
struct dentry *entry;
+ char *name;
int ret;
+ int cpu;
- ret = register_stat_tracer(&function_stats);
- if (ret) {
- pr_warning("Warning: could not register "
- "function stats\n");
- return;
+ for_each_possible_cpu(cpu) {
+ stat = &per_cpu(ftrace_profile_stats, cpu);
+
+ /* allocate enough for function name + cpu number */
+ name = kmalloc(32, GFP_KERNEL);
+ if (!name) {
+ /*
+ * The files created are permanent, if something happens
+ * we still do not free memory.
+ */
+ kfree(stat);
+ WARN(1,
+ "Could not allocate stat file for cpu %d\n",
+ cpu);
+ return;
+ }
+ stat->stat = function_stats;
+ snprintf(name, 32, "function%d", cpu);
+ stat->stat.name = name;
+ ret = register_stat_tracer(&stat->stat);
+ if (ret) {
+ WARN(1,
+ "Could not register function stat for cpu %d\n",
+ cpu);
+ kfree(name);
+ return;
+ }
}
entry = debugfs_create_file("function_profile_enabled", 0644,
--
1.6.2
--
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
--
[ Luis Claudio R. Goncalves Bass - Gospel - RT ]
[ Fingerprint: 4FDD B8C4 3C59 34BD 8BE9 2696 7203 D980 A448 C8F8 ]
next prev parent reply other threads:[~2009-05-19 15:00 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-05-19 14:36 [PATCH 0/9] Backport of ftrace's function profiler Luis Claudio R. Goncalves
2009-05-19 14:43 ` [PATCH 1/9] tracing: add " Luis Claudio R. Goncalves
2009-05-19 14:45 ` [PATCH 2/9] reduce size of memory in " Luis Claudio R. Goncalves
2009-05-19 14:48 ` [PATCH 3/9] tracing: adding function timings to function profile Luis Claudio R. Goncalves
2009-05-19 14:52 ` Luis Claudio R. Goncalves [this message]
2009-05-19 14:55 ` [PATCH 5/9] function graph add option to calculate graph time off Luis Claudio R. Goncalves
2009-05-19 14:58 ` [PATCH 6/9] tracing: remove on the fly allocator from function profiler Luis Claudio R. Goncalves
2009-05-19 15:01 ` [PATCH 7/9] tracing: add average time in function to " Luis Claudio R. Goncalves
2009-05-19 15:03 ` [PATCH 8/9] backport function profiler fixes Luis Claudio R. Goncalves
2009-05-19 15:05 ` [PATCH 9/9] ftrace: function profiler band-aid Luis Claudio R. Goncalves
2009-05-19 15:06 ` [PATCH 0/9] Backport of ftrace's function profiler Steven Rostedt
2009-05-19 15:12 ` Luis Claudio R. Goncalves
2009-05-19 15:15 ` Steven Rostedt
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20090519145257.GL27687@unix.sh \
--to=lclaudio@uudg.org \
--cc=linux-rt-users@vger.kernel.org \
--cc=mingo@elte.hu \
--cc=rostedt@goodmis.org \
--cc=tglx@linutronix.de \
--cc=williams@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.