@@ -174,6 +174,11 @@ int ftrace_graph_active;
static struct kmem_cache *fgraph_stack_cachep;
+DEFINE_STATIC_KEY_FALSE(fgraph_ret_stack_cleanup);
+static struct workqueue_struct *fgraph_ret_stack_wq;
+static struct work_struct fgraph_ret_stack_work;
+static struct irq_work fgraph_ret_stack_irq_work;
+
static struct fgraph_ops *fgraph_array[FGRAPH_ARRAY_SIZE];
static unsigned long fgraph_array_bitmask;
@@ -849,8 +854,15 @@ static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs
*/
barrier();
current->curr_ret_stack = offset - FGRAPH_FRAME_OFFSET;
-
current->curr_ret_depth--;
+
+ /*
+ * If function graph is done and this task is no longer using ret_stack
+ * then start the work to free it.
+ */
+ if (static_branch_unlikely(&fgraph_ret_stack_cleanup) && current->curr_ret_depth < 0)
+ irq_work_queue(&fgraph_ret_stack_irq_work);
+
return ret;
}
@@ -1374,6 +1386,21 @@ static void free_ret_stacks(void)
}
}
+static void fgraph_ret_stack_work_func(struct work_struct *work)
+{
+ mutex_lock(&ftrace_lock);
+ if (!ftrace_graph_active)
+ free_ret_stacks();
+ mutex_unlock(&ftrace_lock);
+}
+
+static void fgraph_ret_stack_irq_func(struct irq_work *iwork)
+{
+ if (unlikely(!fgraph_ret_stack_wq))
+ return;
+ queue_work(fgraph_ret_stack_wq, &fgraph_ret_stack_work);
+}
+
static __init int fgraph_init(void)
{
int ret;
@@ -1384,6 +1411,12 @@ static __init int fgraph_init(void)
pr_warn("fgraph: Error to init cpu hotplug support\n");
return ret;
}
+ fgraph_ret_stack_wq = alloc_workqueue("fgraph_ret_stack_wq",
+ WQ_UNBOUND | WQ_MEM_RECLAIM, 0);
+ WARN_ON(!fgraph_ret_stack_wq);
+
+ INIT_WORK(&fgraph_ret_stack_work, fgraph_ret_stack_work_func);
+ init_irq_work(&fgraph_ret_stack_irq_work, fgraph_ret_stack_irq_func);
return 0;
}
core_initcall(fgraph_init)
@@ -1429,6 +1462,7 @@ int register_ftrace_graph(struct fgraph_ops *gops)
ftrace_graph_disable_direct(true);
if (ftrace_graph_active == 1) {
+ static_branch_disable(&fgraph_ret_stack_cleanup);
ftrace_graph_enable_direct(false, gops);
register_pm_notifier(&ftrace_suspend_notifier);
ret = start_graph_tracing();
@@ -1495,6 +1529,7 @@ void unregister_ftrace_graph(struct fgraph_ops *gops)
ftrace_graph_entry = ftrace_graph_entry_stub;
unregister_pm_notifier(&ftrace_suspend_notifier);
unregister_trace_sched_switch(ftrace_graph_probe_sched_switch, NULL);
+ static_branch_enable(&fgraph_ret_stack_cleanup);
free_ret_stacks();
}
out: