From 608c25f2c91a7fc8c4fa7f5961744881e923acc5 Mon Sep 17 00:00:00 2001 From: Igor Nabirushkin Date: Thu, 17 Apr 2014 13:02:07 +0400 Subject: [PATCH] misc: tegra-profiler: unwinding: use RCU locking Unwinding: use RCU locking instead of spinlocks to protect map of regions. Bug 1502205 Change-Id: If1089b74b1f317eeaae5059de40d7a3365ae4061 Signed-off-by: Igor Nabirushkin Reviewed-on: http://git-master/r/397599 (cherry picked from commit 3504dc62f0aad25ad5c50f46c6c3319b95775966) Reviewed-on: http://git-master/r/454437 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Tested-by: Maxim Morin Reviewed-by: Mitch Luban --- drivers/misc/tegra-profiler/eh_unwind.c | 200 +++++++++++++++++------- drivers/misc/tegra-profiler/hrt.c | 20 +-- drivers/misc/tegra-profiler/version.h | 2 +- 3 files changed, 148 insertions(+), 74 deletions(-) diff --git a/drivers/misc/tegra-profiler/eh_unwind.c b/drivers/misc/tegra-profiler/eh_unwind.c index 16cc5bea70a..68b3733c09f 100644 --- a/drivers/misc/tegra-profiler/eh_unwind.c +++ b/drivers/misc/tegra-profiler/eh_unwind.c @@ -21,6 +21,7 @@ #include #include #include +#include #include @@ -58,10 +59,17 @@ struct ex_region_info { struct extables tabs; }; +struct regions_data { + struct ex_region_info *entries; + + unsigned long curr_nr; + unsigned long size; + + struct rcu_head rcu; +}; + struct quadd_unwind_ctx { - struct ex_region_info *regions; - unsigned long ri_nr; - unsigned long ri_size; + struct regions_data *rd; pid_t pid; @@ -126,11 +134,12 @@ validate_pc_addr(unsigned long addr, unsigned long nbytes) }) static int -add_ex_region(struct ex_region_info *new_entry) +add_ex_region(struct regions_data *rd, + struct ex_region_info *new_entry) { unsigned int i_min, i_max, mid; - struct ex_region_info *array = ctx.regions; - unsigned long size = ctx.ri_nr; + struct ex_region_info *array = rd->entries; + unsigned long size = rd->curr_nr; if (!array) return 0; @@ -176,11 +185,12 @@ add_ex_region(struct ex_region_info *new_entry) } static struct ex_region_info * -search_ex_region(unsigned long key, struct extables *tabs) +search_ex_region(struct ex_region_info *array, + unsigned long size, + unsigned long key, + struct extables *tabs) { unsigned int i_min, i_max, mid; - struct ex_region_info *array = ctx.regions; - unsigned long size = ctx.ri_nr; if (size == 0) return NULL; @@ -205,6 +215,25 @@ search_ex_region(unsigned long key, struct extables *tabs) return NULL; } +static long +__search_ex_region(unsigned long key, struct extables *tabs) +{ + struct regions_data *rd; + struct ex_region_info *ri = NULL; + + rcu_read_lock(); + + rd = rcu_dereference(ctx.rd); + if (!rd) + goto out; + + ri = search_ex_region(rd->entries, rd->curr_nr, key, tabs); + +out: + rcu_read_unlock(); + return ri ? 0 : -ENOENT; +} + static void pin_user_pages(struct extables *tabs) { long ret; @@ -274,16 +303,14 @@ error_out: static void pin_user_pages_work(struct work_struct *w) { + long err; struct extables tabs; - struct ex_region_info *ri; struct pin_pages_work *work; work = container_of(w, struct pin_pages_work, work); - spin_lock(&ctx.lock); - ri = search_ex_region(work->vm_start, &tabs); - spin_unlock(&ctx.lock); - if (ri) + err = __search_ex_region(work->vm_start, &tabs); + if (!err) pin_user_pages(&tabs); kfree(w); @@ -306,31 +333,75 @@ __pin_user_pages(unsigned long vm_start) return 0; } +static struct regions_data *rd_alloc(unsigned long size) +{ + struct regions_data *rd; + + rd = kzalloc(sizeof(*rd), GFP_KERNEL); + if (!rd) + return NULL; + + rd->entries = kzalloc(size * sizeof(*rd->entries), GFP_KERNEL); + if (!rd->entries) { + kfree(rd); + return NULL; + } + + rd->size = size; + rd->curr_nr = 0; + + return rd; +} + +static void rd_free(struct regions_data *rd) +{ + if (rd) + kfree(rd->entries); + + kfree(rd); +} + +static void rd_free_rcu(struct rcu_head *rh) +{ + struct regions_data *rd = container_of(rh, struct regions_data, rcu); + rd_free(rd); +} + int quadd_unwind_set_extab(struct quadd_extables *extabs) { int err = 0; + unsigned long nr_entries, nr_added, new_size; struct ex_region_info ri_entry; struct extab_info *ti; + struct regions_data *rd, *rd_new; spin_lock(&ctx.lock); - if (!ctx.regions) { + rd = rcu_dereference(ctx.rd); + if (!rd) { + pr_warn("%s: warning: rd\n", __func__); + new_size = QUADD_EXTABS_SIZE; + nr_entries = 0; + } else { + new_size = rd->size; + nr_entries = rd->curr_nr; + } + + if (nr_entries >= new_size) + new_size += new_size >> 1; + + rd_new = rd_alloc(new_size); + if (IS_ERR_OR_NULL(rd_new)) { + pr_err("%s: error: rd_alloc\n", __func__); err = -ENOMEM; goto error_out; } - if (ctx.ri_nr >= ctx.ri_size) { - struct ex_region_info *new_regions; - unsigned long newlen = ctx.ri_size + (ctx.ri_size >> 1); + if (rd && nr_entries) + memcpy(rd_new->entries, rd->entries, + nr_entries * sizeof(*rd->entries)); - new_regions = krealloc(ctx.regions, newlen, GFP_KERNEL); - if (!new_regions) { - err = -ENOMEM; - goto error_out; - } - ctx.regions = new_regions; - ctx.ri_size = newlen; - } + rd_new->curr_nr = nr_entries; ri_entry.vm_start = extabs->vm_start; ri_entry.vm_end = extabs->vm_end; @@ -343,7 +414,17 @@ int quadd_unwind_set_extab(struct quadd_extables *extabs) ti->addr = extabs->extab.addr; ti->length = extabs->extab.length; - ctx.ri_nr += add_ex_region(&ri_entry); + nr_added = add_ex_region(rd_new, &ri_entry); + if (nr_added == 0) { + rd_free(rd_new); + goto error_out; + } + rd_new->curr_nr += nr_added; + + rcu_assign_pointer(ctx.rd, rd_new); + + if (rd) + call_rcu(&rd->rcu, rd_free_rcu); spin_unlock(&ctx.lock); @@ -851,12 +932,8 @@ unwind_backtrace(struct quadd_callchain *cc, break; if (!is_vma_addr(exidx->addr, vma_pc, sizeof(u32))) { - struct ex_region_info *ri; - - spin_lock(&ctx.lock); - ri = search_ex_region(vma_pc->vm_start, &tabs); - spin_unlock(&ctx.lock); - if (!ri) { + err = __search_ex_region(vma_pc->vm_start, &tabs); + if (err) { cc->unw_rc = QUADD_URC_TBL_NOT_EXIST; break; } @@ -886,10 +963,10 @@ quadd_get_user_callchain_ut(struct pt_regs *regs, struct quadd_callchain *cc, struct task_struct *task) { + long err; unsigned long ip, sp; struct vm_area_struct *vma, *vma_sp; struct mm_struct *mm = task->mm; - struct ex_region_info *ri; struct extables tabs; cc->unw_method = QUADD_UNW_METHOD_EHT; @@ -916,10 +993,8 @@ quadd_get_user_callchain_ut(struct pt_regs *regs, if (!vma_sp) return 0; - spin_lock(&ctx.lock); - ri = search_ex_region(vma->vm_start, &tabs); - spin_unlock(&ctx.lock); - if (!ri) { + err = __search_ex_region(vma->vm_start, &tabs); + if (err) { cc->unw_rc = QUADD_URC_TBL_NOT_EXIST; return 0; } @@ -931,26 +1006,31 @@ quadd_get_user_callchain_ut(struct pt_regs *regs, int quadd_unwind_start(struct task_struct *task) { - spin_lock(&ctx.lock); - - kfree(ctx.regions); + struct regions_data *rd, *rd_old; - ctx.ri_nr = 0; - ctx.ri_size = 0; + spin_lock(&ctx.lock); - ctx.pinned_pages = 0; - ctx.pinned_size = 0; + rd_old = rcu_dereference(ctx.rd); + if (rd_old) + pr_warn("%s: warning: rd_old\n", __func__); - ctx.regions = kzalloc(QUADD_EXTABS_SIZE * sizeof(*ctx.regions), - GFP_KERNEL); - if (!ctx.regions) { + rd = rd_alloc(QUADD_EXTABS_SIZE); + if (IS_ERR_OR_NULL(rd)) { + pr_err("%s: error: rd_alloc\n", __func__); spin_unlock(&ctx.lock); return -ENOMEM; } - ctx.ri_size = QUADD_EXTABS_SIZE; + + rcu_assign_pointer(ctx.rd, rd); + + if (rd_old) + call_rcu(&rd_old->rcu, rd_free_rcu); ctx.pid = task->tgid; + ctx.pinned_pages = 0; + ctx.pinned_size = 0; + spin_unlock(&ctx.lock); return 0; @@ -958,16 +1038,18 @@ int quadd_unwind_start(struct task_struct *task) void quadd_unwind_stop(void) { - spin_lock(&ctx.lock); - - kfree(ctx.regions); - ctx.regions = NULL; + struct regions_data *rd; - ctx.ri_size = 0; - ctx.ri_nr = 0; + spin_lock(&ctx.lock); ctx.pid = 0; + rd = rcu_dereference(ctx.rd); + if (rd) { + rcu_assign_pointer(ctx.rd, NULL); + call_rcu(&rd->rcu, rd_free_rcu); + } + spin_unlock(&ctx.lock); pr_info("exception tables size: %lu bytes\n", ctx.pinned_size); @@ -977,12 +1059,9 @@ void quadd_unwind_stop(void) int quadd_unwind_init(void) { - ctx.regions = NULL; - ctx.ri_size = 0; - ctx.ri_nr = 0; - ctx.pid = 0; - spin_lock_init(&ctx.lock); + rcu_assign_pointer(ctx.rd, NULL); + ctx.pid = 0; return 0; } @@ -990,4 +1069,5 @@ int quadd_unwind_init(void) void quadd_unwind_deinit(void) { quadd_unwind_stop(); + rcu_barrier(); } diff --git a/drivers/misc/tegra-profiler/hrt.c b/drivers/misc/tegra-profiler/hrt.c index c03d53832c6..c25384429cf 100644 --- a/drivers/misc/tegra-profiler/hrt.c +++ b/drivers/misc/tegra-profiler/hrt.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -253,22 +254,15 @@ read_all_sources(struct pt_regs *regs, struct task_struct *task) if (atomic_read(&cpu_ctx->nr_active) == 0) return; - if (!task) { - pid_t pid; - struct pid *pid_s; - struct quadd_thread_data *t_data; - - t_data = &cpu_ctx->active_thread; - pid = t_data->pid; + if (!task) + task = current; - rcu_read_lock(); - pid_s = find_vpid(pid); - if (pid_s) - task = pid_task(pid_s, PIDTYPE_PID); + rcu_read_lock(); + if (!task_nsproxy(task)) { rcu_read_unlock(); - if (!task) - return; + return; } + rcu_read_unlock(); if (ctx->pmu && ctx->pmu_info.active) nr_events += read_source(ctx->pmu, regs, diff --git a/drivers/misc/tegra-profiler/version.h b/drivers/misc/tegra-profiler/version.h index b44426de71c..95f5efffeeb 100644 --- a/drivers/misc/tegra-profiler/version.h +++ b/drivers/misc/tegra-profiler/version.h @@ -18,7 +18,7 @@ #ifndef __QUADD_VERSION_H #define __QUADD_VERSION_H -#define QUADD_MODULE_VERSION "1.64" +#define QUADD_MODULE_VERSION "1.65" #define QUADD_MODULE_BRANCH "Dev" #endif /* __QUADD_VERSION_H */ -- 2.39.2