help
          See Documentation/trace/ftrace-design.rst
 
+config HAVE_FUNCTION_GRAPH_RETVAL
+       bool
+
 config HAVE_DYNAMIC_FTRACE
        bool
        help
          the return value. This is done by setting the current return
          address on the current task structure into a stack of calls.
 
+config FUNCTION_GRAPH_RETVAL
+       bool "Kernel Function Graph Return Value"
+       depends on HAVE_FUNCTION_GRAPH_RETVAL
+       depends on FUNCTION_GRAPH_TRACER
+       default n
+       help
+         Support recording and printing the function return value when
+         using function graph tracer. It can be helpful to locate functions
+         that return errors. This feature is off by default, and you can
+         enable it via the trace option funcgraph-retval.
+         See Documentation/trace/ftrace.rst
+
 config DYNAMIC_FTRACE
        bool "enable/disable function tracing dynamically"
        depends on FUNCTION_TRACER
 
  * Send the trace to the ring-buffer.
  * @return the original return address.
  */
-unsigned long ftrace_return_to_handler(unsigned long frame_pointer)
+static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs,
+                                               unsigned long frame_pointer)
 {
        struct ftrace_graph_ret trace;
        unsigned long ret;
 
        ftrace_pop_return_trace(&trace, &ret, frame_pointer);
+#ifdef CONFIG_FUNCTION_GRAPH_RETVAL
+       trace.retval = fgraph_ret_regs_return_value(ret_regs);
+#endif
        trace.rettime = trace_clock_local();
        ftrace_graph_return(&trace);
        /*
        return ret;
 }
 
+/*
+ * After all architecures have selected HAVE_FUNCTION_GRAPH_RETVAL, we can
+ * leave only ftrace_return_to_handler(ret_regs).
+ */
+#ifdef CONFIG_HAVE_FUNCTION_GRAPH_RETVAL
+unsigned long ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs)
+{
+       return __ftrace_return_to_handler(ret_regs,
+                               fgraph_ret_regs_frame_pointer(ret_regs));
+}
+#else
+unsigned long ftrace_return_to_handler(unsigned long frame_pointer)
+{
+       return __ftrace_return_to_handler(NULL, frame_pointer);
+}
+#endif
+
 /**
  * ftrace_graph_get_ret_stack - return the entry of the shadow stack
  * @task: The task to read the shadow stack from
 
 );
 
 /* Function return entry */
+#ifdef CONFIG_FUNCTION_GRAPH_RETVAL
+
+FTRACE_ENTRY_PACKED(funcgraph_exit, ftrace_graph_ret_entry,
+
+       TRACE_GRAPH_RET,
+
+       F_STRUCT(
+               __field_struct( struct ftrace_graph_ret,        ret     )
+               __field_packed( unsigned long,  ret,            func    )
+               __field_packed( unsigned long,  ret,            retval  )
+               __field_packed( int,            ret,            depth   )
+               __field_packed( unsigned int,   ret,            overrun )
+               __field_packed( unsigned long long, ret,        calltime)
+               __field_packed( unsigned long long, ret,        rettime )
+       ),
+
+       F_printk("<-- %ps (%d) (start: %llx  end: %llx) over: %d retval: %lx",
+                (void *)__entry->func, __entry->depth,
+                __entry->calltime, __entry->rettime,
+                __entry->depth, __entry->retval)
+);
+
+#else
+
 FTRACE_ENTRY_PACKED(funcgraph_exit, ftrace_graph_ret_entry,
 
        TRACE_GRAPH_RET,
                 __entry->depth)
 );
 
+#endif
+
 /*
  * Context switch trace entry - which task (and prio) we switched from/to:
  *
 
        { TRACER_OPT(funcgraph-irqs, TRACE_GRAPH_PRINT_IRQS) },
        /* Display function name after trailing } */
        { TRACER_OPT(funcgraph-tail, TRACE_GRAPH_PRINT_TAIL) },
+#ifdef CONFIG_FUNCTION_GRAPH_RETVAL
+       /* Display function return value ? */
+       { TRACER_OPT(funcgraph-retval, TRACE_GRAPH_PRINT_RETVAL) },
+       /* Display function return value in hexadecimal format ? */
+       { TRACER_OPT(funcgraph-retval-hex, TRACE_GRAPH_PRINT_RETVAL_HEX) },
+#endif
        /* Include sleep time (scheduled out) between entry and return */
        { TRACER_OPT(sleep-time, TRACE_GRAPH_SLEEP_TIME) },
 
        trace_seq_puts(s, "|  ");
 }
 
+#ifdef CONFIG_FUNCTION_GRAPH_RETVAL
+
+#define __TRACE_GRAPH_PRINT_RETVAL TRACE_GRAPH_PRINT_RETVAL
+
+static void print_graph_retval(struct trace_seq *s, unsigned long retval,
+                               bool leaf, void *func, bool hex_format)
+{
+       unsigned long err_code = 0;
+
+       if (retval == 0 || hex_format)
+               goto done;
+
+       /* Check if the return value matches the negative format */
+       if (IS_ENABLED(CONFIG_64BIT) && (retval & BIT(31)) &&
+               (((u64)retval) >> 32) == 0) {
+               /* sign extension */
+               err_code = (unsigned long)(s32)retval;
+       } else {
+               err_code = retval;
+       }
+
+       if (!IS_ERR_VALUE(err_code))
+               err_code = 0;
+
+done:
+       if (leaf) {
+               if (hex_format || (err_code == 0))
+                       trace_seq_printf(s, "%ps(); /* = 0x%lx */\n",
+                                       func, retval);
+               else
+                       trace_seq_printf(s, "%ps(); /* = %ld */\n",
+                                       func, err_code);
+       } else {
+               if (hex_format || (err_code == 0))
+                       trace_seq_printf(s, "} /* %ps = 0x%lx */\n",
+                                       func, retval);
+               else
+                       trace_seq_printf(s, "} /* %ps = %ld */\n",
+                                       func, err_code);
+       }
+}
+
+#else
+
+#define __TRACE_GRAPH_PRINT_RETVAL 0
+
+#define print_graph_retval(_seq, _retval, _leaf, _func, _format) do {} while (0)
+
+#endif
+
 /* Case of a leaf function on its call entry */
 static enum print_line_t
 print_graph_entry_leaf(struct trace_iterator *iter,
        for (i = 0; i < call->depth * TRACE_GRAPH_INDENT; i++)
                trace_seq_putc(s, ' ');
 
-       trace_seq_printf(s, "%ps();\n", (void *)call->func);
+       /*
+        * Write out the function return value if the option function-retval is
+        * enabled.
+        */
+       if (flags & __TRACE_GRAPH_PRINT_RETVAL)
+               print_graph_retval(s, graph_ret->retval, true, (void *)call->func,
+                               !!(flags & TRACE_GRAPH_PRINT_RETVAL_HEX));
+       else
+               trace_seq_printf(s, "%ps();\n", (void *)call->func);
 
        print_graph_irq(iter, graph_ret->func, TRACE_GRAPH_RET,
                        cpu, iter->ent->pid, flags);
                trace_seq_putc(s, ' ');
 
        /*
-        * If the return function does not have a matching entry,
-        * then the entry was lost. Instead of just printing
-        * the '}' and letting the user guess what function this
-        * belongs to, write out the function name. Always do
-        * that if the funcgraph-tail option is enabled.
+        * Always write out the function name and its return value if the
+        * function-retval option is enabled.
         */
-       if (func_match && !(flags & TRACE_GRAPH_PRINT_TAIL))
-               trace_seq_puts(s, "}\n");
-       else
-               trace_seq_printf(s, "} /* %ps */\n", (void *)trace->func);
+       if (flags & __TRACE_GRAPH_PRINT_RETVAL) {
+               print_graph_retval(s, trace->retval, false, (void *)trace->func,
+                       !!(flags & TRACE_GRAPH_PRINT_RETVAL_HEX));
+       } else {
+               /*
+                * If the return function does not have a matching entry,
+                * then the entry was lost. Instead of just printing
+                * the '}' and letting the user guess what function this
+                * belongs to, write out the function name. Always do
+                * that if the funcgraph-tail option is enabled.
+                */
+               if (func_match && !(flags & TRACE_GRAPH_PRINT_TAIL))
+                       trace_seq_puts(s, "}\n");
+               else
+                       trace_seq_printf(s, "} /* %ps */\n", (void *)trace->func);
+       }
 
        /* Overrun */
        if (flags & TRACE_GRAPH_PRINT_OVERRUN)