diff mbox series

usb: gadget: functions: add ftrace export over USB

Message ID 20190321094748.7031-1-felipe.balbi@linux.intel.com (mailing list archive)
State New, archived
Headers show
Series usb: gadget: functions: add ftrace export over USB | expand

Commit Message

Felipe Balbi March 21, 2019, 9:47 a.m. UTC
Allow for ftrace data to be exported over a USB Gadget
Controller. With this, we have a potentially very fast pipe for
transmitting ftrace data to a Host PC for further analysis.

Note that in order to decode the data, one needs access to kernel
symbols in order to convert binary data into function names and what
not.

Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
---
 drivers/usb/gadget/Kconfig            |  15 +
 drivers/usb/gadget/function/Makefile  |   2 +
 drivers/usb/gadget/function/f-trace.c | 401 ++++++++++++++++++++++++++
 3 files changed, 418 insertions(+)
 create mode 100644 drivers/usb/gadget/function/f-trace.c

Comments

Felipe Balbi March 21, 2019, 9:50 a.m. UTC | #1
Felipe Balbi <felipe.balbi@linux.intel.com> writes:

> Allow for ftrace data to be exported over a USB Gadget
> Controller. With this, we have a potentially very fast pipe for
> transmitting ftrace data to a Host PC for further analysis.
>
> Note that in order to decode the data, one needs access to kernel
> symbols in order to convert binary data into function names and what
> not.
>
> Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
> ---
>  drivers/usb/gadget/Kconfig            |  15 +
>  drivers/usb/gadget/function/Makefile  |   2 +
>  drivers/usb/gadget/function/f-trace.c | 401 ++++++++++++++++++++++++++
>  3 files changed, 418 insertions(+)
>  create mode 100644 drivers/usb/gadget/function/f-trace.c
>
> diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
> index 31cce7805eb2..dfd181e0d717 100644
> --- a/drivers/usb/gadget/Kconfig
> +++ b/drivers/usb/gadget/Kconfig
> @@ -191,6 +191,9 @@ config USB_F_MASS_STORAGE
>  config USB_F_FS
>  	tristate
>  
> +config USB_F_TRACE
> +	tristate
> +
>  config USB_F_UAC1
>  	tristate
>  
> @@ -368,6 +371,18 @@ config USB_CONFIGFS_F_FS
>  	  implemented in kernel space (for instance Ethernet, serial or
>  	  mass storage) and other are implemented in user space.
>  
> +config USB_CONFIGFS_F_TRACE
> +	bool "Linux FTrace Export Over USB"
> +	depends on USB_CONFIGFS
> +	select USB_F_TRACE
> +	help
> +	  The Linux FTrace Export Over USB lets one export ftrace buffer
> +	  over a USB cable to a host computer for further processing.
> +
> +	  If you want support for that, say Y or M here. Otherwise say N.
> +
> +	  If unsure, say N.
> +
>  config USB_CONFIGFS_F_UAC1
>  	bool "Audio Class 1.0"
>  	depends on USB_CONFIGFS
> diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile
> index 5d3a6cf02218..eb851309006c 100644
> --- a/drivers/usb/gadget/function/Makefile
> +++ b/drivers/usb/gadget/function/Makefile
> @@ -50,3 +50,5 @@ usb_f_printer-y			:= f_printer.o
>  obj-$(CONFIG_USB_F_PRINTER)	+= usb_f_printer.o
>  usb_f_tcm-y			:= f_tcm.o
>  obj-$(CONFIG_USB_F_TCM)		+= usb_f_tcm.o
> +usb_f_trace-y			:= f-trace.o
> +obj-$(CONFIG_USB_F_TRACE)	+= usb_f_trace.o
> diff --git a/drivers/usb/gadget/function/f-trace.c b/drivers/usb/gadget/function/f-trace.c
> new file mode 100644
> index 000000000000..b5941df128a0
> --- /dev/null
> +++ b/drivers/usb/gadget/function/f-trace.c
> @@ -0,0 +1,401 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * f_trace.c -- USB FTrace Export
> + *
> + * Copyright (C) 2019 Intel Corporation
> + * Author: Felipe Balbi <felipe.balbi@linux.intel.com>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License v2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/device.h>
> +#include <linux/kernel.h>
> +#include <linux/list.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/trace.h>
> +#include <linux/usb.h>
> +#include <linux/usb/composite.h>
> +#include <linux/usb/gadget.h>
> +#include <linux/workqueue.h>
> +
> +struct usb_ftrace {
> +	struct trace_export ftrace;
> +	struct usb_function function;
> +	struct work_struct queue_work;
> +	spinlock_t lock;
> +
> +	struct list_head list;
> +	struct list_head pending;
> +	struct list_head queued;
> +
> +	struct usb_ep *in;
> +
> +	u8 intf_id;
> +};
> +#define ftrace_to_trace(f)	(container_of((f), struct usb_ftrace, ftrace))
> +#define work_to_trace(w)	(container_of((w), struct usb_ftrace, queue_work))
> +#define to_trace(f)		(container_of((f), struct usb_ftrace, function))
> +
> +#define FTRACE_REQUEST_QUEUE_LENGTH	250
> +
> +static inline struct usb_request *next_request(struct list_head *list)
> +{
> +	return list_first_entry_or_null(list, struct usb_request, list);
> +}
> +
> +struct usb_ftrace_opts {
> +	struct usb_function_instance func_inst;
> +};
> +#define to_opts(fi)	(container_of((fi), struct usb_ftrace_opts, func_inst))
> +
> +static struct usb_interface_descriptor ftrace_intf_desc = {
> +	.bLength		= USB_DT_INTERFACE_SIZE,
> +	.bDescriptorType	= USB_DT_INTERFACE,
> +
> +	.bAlternateSetting	= 0,
> +	.bNumEndpoints		= 1,
> +	.bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
> +	.bInterfaceSubClass	= USB_SUBCLASS_VENDOR_SPEC,

We *may* be able to use the USB_DEBUG_CLASS here with a vendor protocol
which we define to be Linux Ftrace. If folks think it would be best,
then I can go look at the debug class and check whether it would be
feasible.
Greg Kroah-Hartman March 21, 2019, 9:59 a.m. UTC | #2
On Thu, Mar 21, 2019 at 11:47:48AM +0200, Felipe Balbi wrote:
> Allow for ftrace data to be exported over a USB Gadget
> Controller. With this, we have a potentially very fast pipe for
> transmitting ftrace data to a Host PC for further analysis.
> 
> Note that in order to decode the data, one needs access to kernel
> symbols in order to convert binary data into function names and what
> not.
> 
> Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
> ---
>  drivers/usb/gadget/Kconfig            |  15 +
>  drivers/usb/gadget/function/Makefile  |   2 +
>  drivers/usb/gadget/function/f-trace.c | 401 ++++++++++++++++++++++++++
>  3 files changed, 418 insertions(+)
>  create mode 100644 drivers/usb/gadget/function/f-trace.c

This is funny, nice work :)

Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Greg Kroah-Hartman March 21, 2019, 10:03 a.m. UTC | #3
On Thu, Mar 21, 2019 at 11:50:04AM +0200, Felipe Balbi wrote:
> > +static struct usb_interface_descriptor ftrace_intf_desc = {
> > +	.bLength		= USB_DT_INTERFACE_SIZE,
> > +	.bDescriptorType	= USB_DT_INTERFACE,
> > +
> > +	.bAlternateSetting	= 0,
> > +	.bNumEndpoints		= 1,
> > +	.bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
> > +	.bInterfaceSubClass	= USB_SUBCLASS_VENDOR_SPEC,
> 
> We *may* be able to use the USB_DEBUG_CLASS here with a vendor protocol
> which we define to be Linux Ftrace. If folks think it would be best,
> then I can go look at the debug class and check whether it would be
> feasible.

I didn't think that that class allowed vendor protocols, but if it does,
sure, that would be nice.

I don't have access to usb-if specs since they kicked Linux out of the
organization, and because of that, I really don't care about their specs
anymore :)

thanks,

greg k-h
Felipe Balbi March 21, 2019, 11:25 a.m. UTC | #4
hi,

Greg KH <gregkh@linuxfoundation.org> writes:

> On Thu, Mar 21, 2019 at 11:50:04AM +0200, Felipe Balbi wrote:
>> > +static struct usb_interface_descriptor ftrace_intf_desc = {
>> > +	.bLength		= USB_DT_INTERFACE_SIZE,
>> > +	.bDescriptorType	= USB_DT_INTERFACE,
>> > +
>> > +	.bAlternateSetting	= 0,
>> > +	.bNumEndpoints		= 1,
>> > +	.bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
>> > +	.bInterfaceSubClass	= USB_SUBCLASS_VENDOR_SPEC,
>> 
>> We *may* be able to use the USB_DEBUG_CLASS here with a vendor protocol
>> which we define to be Linux Ftrace. If folks think it would be best,
>> then I can go look at the debug class and check whether it would be
>> feasible.
>
> I didn't think that that class allowed vendor protocols, but if it does,
> sure, that would be nice.

I think it does. That spec is public, actually. Came out as part of usb
3.1. I'll review the spec tomorrow and, hopefully, resend this with
Diagnostic Class for the interface using DvC.Trace subclass and vendor
protocol.

cheers
kernel test robot March 22, 2019, 12:59 a.m. UTC | #5
Hi Felipe,

I love your patch! Yet something to improve:

[auto build test ERROR on balbi-usb/next]
[also build test ERROR on v5.1-rc1 next-20190321]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Felipe-Balbi/usb-gadget-functions-add-ftrace-export-over-USB/20190322-081552
base:   https://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git next
config: m68k-allyesconfig (attached as .config)
compiler: m68k-linux-gnu-gcc (Debian 7.2.0-11) 7.2.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        GCC_VERSION=7.2.0 make.cross ARCH=m68k 

All error/warnings (new ones prefixed by >>):

>> drivers/usb/gadget/function/f-trace.c:26:22: error: field 'ftrace' has incomplete type
     struct trace_export ftrace;
                         ^~~~~~
   In file included from include/linux/ioport.h:13:0,
                    from include/linux/device.h:15,
                    from drivers/usb/gadget/function/f-trace.c:13:
   drivers/usb/gadget/function/f-trace.c: In function 'ftrace_write':
>> include/linux/kernel.h:998:32: error: dereferencing pointer to incomplete type 'struct trace_export'
     BUILD_BUG_ON_MSG(!__same_type(*(ptr), ((type *)0)->member) && \
                                   ^~~~~~
   include/linux/compiler.h:324:9: note: in definition of macro '__compiletime_assert'
      if (!(condition))     \
            ^~~~~~~~~
   include/linux/compiler.h:344:2: note: in expansion of macro '_compiletime_assert'
     _compiletime_assert(condition, msg, __compiletime_assert_, __LINE__)
     ^~~~~~~~~~~~~~~~~~~
   include/linux/build_bug.h:39:37: note: in expansion of macro 'compiletime_assert'
    #define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)
                                        ^~~~~~~~~~~~~~~~~~
   include/linux/kernel.h:998:2: note: in expansion of macro 'BUILD_BUG_ON_MSG'
     BUILD_BUG_ON_MSG(!__same_type(*(ptr), ((type *)0)->member) && \
     ^~~~~~~~~~~~~~~~
   include/linux/kernel.h:998:20: note: in expansion of macro '__same_type'
     BUILD_BUG_ON_MSG(!__same_type(*(ptr), ((type *)0)->member) && \
                       ^~~~~~~~~~~
>> drivers/usb/gadget/function/f-trace.c:39:29: note: in expansion of macro 'container_of'
    #define ftrace_to_trace(f) (container_of((f), struct usb_ftrace, ftrace))
                                ^~~~~~~~~~~~
>> drivers/usb/gadget/function/f-trace.c:175:30: note: in expansion of macro 'ftrace_to_trace'
     struct usb_ftrace  *trace = ftrace_to_trace(ftrace);
                                 ^~~~~~~~~~~~~~~
   drivers/usb/gadget/function/f-trace.c: In function 'ftrace_bind':
>> drivers/usb/gadget/function/f-trace.c:295:8: error: implicit declaration of function 'register_ftrace_export'; did you mean 'register_chrdev_region'? [-Werror=implicit-function-declaration]
     ret = register_ftrace_export(&trace->ftrace);
           ^~~~~~~~~~~~~~~~~~~~~~
           register_chrdev_region
   drivers/usb/gadget/function/f-trace.c: In function 'ftrace_unbind':
>> drivers/usb/gadget/function/f-trace.c:323:2: error: implicit declaration of function 'unregister_ftrace_export'; did you mean 'unregister_chrdev_region'? [-Werror=implicit-function-declaration]
     unregister_ftrace_export(&trace->ftrace);
     ^~~~~~~~~~~~~~~~~~~~~~~~
     unregister_chrdev_region
   cc1: some warnings being treated as errors
--
   drivers/usb//gadget/function/f-trace.c:26:22: error: field 'ftrace' has incomplete type
     struct trace_export ftrace;
                         ^~~~~~
   In file included from include/linux/ioport.h:13:0,
                    from include/linux/device.h:15,
                    from drivers/usb//gadget/function/f-trace.c:13:
   drivers/usb//gadget/function/f-trace.c: In function 'ftrace_write':
>> include/linux/kernel.h:998:32: error: dereferencing pointer to incomplete type 'struct trace_export'
     BUILD_BUG_ON_MSG(!__same_type(*(ptr), ((type *)0)->member) && \
                                   ^~~~~~
   include/linux/compiler.h:324:9: note: in definition of macro '__compiletime_assert'
      if (!(condition))     \
            ^~~~~~~~~
   include/linux/compiler.h:344:2: note: in expansion of macro '_compiletime_assert'
     _compiletime_assert(condition, msg, __compiletime_assert_, __LINE__)
     ^~~~~~~~~~~~~~~~~~~
   include/linux/build_bug.h:39:37: note: in expansion of macro 'compiletime_assert'
    #define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)
                                        ^~~~~~~~~~~~~~~~~~
   include/linux/kernel.h:998:2: note: in expansion of macro 'BUILD_BUG_ON_MSG'
     BUILD_BUG_ON_MSG(!__same_type(*(ptr), ((type *)0)->member) && \
     ^~~~~~~~~~~~~~~~
   include/linux/kernel.h:998:20: note: in expansion of macro '__same_type'
     BUILD_BUG_ON_MSG(!__same_type(*(ptr), ((type *)0)->member) && \
                       ^~~~~~~~~~~
   drivers/usb//gadget/function/f-trace.c:39:29: note: in expansion of macro 'container_of'
    #define ftrace_to_trace(f) (container_of((f), struct usb_ftrace, ftrace))
                                ^~~~~~~~~~~~
   drivers/usb//gadget/function/f-trace.c:175:30: note: in expansion of macro 'ftrace_to_trace'
     struct usb_ftrace  *trace = ftrace_to_trace(ftrace);
                                 ^~~~~~~~~~~~~~~
   drivers/usb//gadget/function/f-trace.c: In function 'ftrace_bind':
   drivers/usb//gadget/function/f-trace.c:295:8: error: implicit declaration of function 'register_ftrace_export'; did you mean 'register_chrdev_region'? [-Werror=implicit-function-declaration]
     ret = register_ftrace_export(&trace->ftrace);
           ^~~~~~~~~~~~~~~~~~~~~~
           register_chrdev_region
   drivers/usb//gadget/function/f-trace.c: In function 'ftrace_unbind':
   drivers/usb//gadget/function/f-trace.c:323:2: error: implicit declaration of function 'unregister_ftrace_export'; did you mean 'unregister_chrdev_region'? [-Werror=implicit-function-declaration]
     unregister_ftrace_export(&trace->ftrace);
     ^~~~~~~~~~~~~~~~~~~~~~~~
     unregister_chrdev_region
   cc1: some warnings being treated as errors

vim +/ftrace +26 drivers/usb/gadget/function/f-trace.c

    24	
    25	struct usb_ftrace {
  > 26		struct trace_export ftrace;
    27		struct usb_function function;
    28		struct work_struct queue_work;
    29		spinlock_t lock;
    30	
    31		struct list_head list;
    32		struct list_head pending;
    33		struct list_head queued;
    34	
    35		struct usb_ep *in;
    36	
    37		u8 intf_id;
    38	};
  > 39	#define ftrace_to_trace(f)	(container_of((f), struct usb_ftrace, ftrace))
    40	#define work_to_trace(w)	(container_of((w), struct usb_ftrace, queue_work))
    41	#define to_trace(f)		(container_of((f), struct usb_ftrace, function))
    42	
    43	#define FTRACE_REQUEST_QUEUE_LENGTH	250
    44	
    45	static inline struct usb_request *next_request(struct list_head *list)
    46	{
    47		return list_first_entry_or_null(list, struct usb_request, list);
    48	}
    49	
    50	struct usb_ftrace_opts {
    51		struct usb_function_instance func_inst;
    52	};
    53	#define to_opts(fi)	(container_of((fi), struct usb_ftrace_opts, func_inst))
    54	
    55	static struct usb_interface_descriptor ftrace_intf_desc = {
    56		.bLength		= USB_DT_INTERFACE_SIZE,
    57		.bDescriptorType	= USB_DT_INTERFACE,
    58	
    59		.bAlternateSetting	= 0,
    60		.bNumEndpoints		= 1,
    61		.bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
    62		.bInterfaceSubClass	= USB_SUBCLASS_VENDOR_SPEC,
    63	};
    64	
    65	/* Super-Speed Support */
    66	static struct usb_endpoint_descriptor ftrace_ss_in_desc = {
    67		.bLength		= USB_DT_ENDPOINT_SIZE,
    68		.bDescriptorType	= USB_DT_ENDPOINT,
    69	
    70		.bEndpointAddress	= USB_DIR_IN,
    71		.bmAttributes		= USB_ENDPOINT_XFER_BULK,
    72		.wMaxPacketSize		= cpu_to_le16(1024),
    73	};
    74	
    75	static struct usb_ss_ep_comp_descriptor ftrace_ss_in_comp_desc = {
    76		.bLength		= USB_DT_SS_EP_COMP_SIZE,
    77		.bDescriptorType	= USB_DT_SS_ENDPOINT_COMP,
    78	
    79		.bMaxBurst		= 15,
    80	};
    81	
    82	static struct usb_descriptor_header *ftrace_ss_function[] = {
    83		(struct usb_descriptor_header *) &ftrace_intf_desc,
    84		(struct usb_descriptor_header *) &ftrace_ss_in_desc,
    85		(struct usb_descriptor_header *) &ftrace_ss_in_comp_desc,
    86		NULL,
    87	};
    88	
    89	/* High-Speed Support */
    90	static struct usb_endpoint_descriptor ftrace_hs_in_desc = {
    91		.bLength		= USB_DT_ENDPOINT_SIZE,
    92		.bDescriptorType	= USB_DT_ENDPOINT,
    93	
    94		.bEndpointAddress	= USB_DIR_IN,
    95		.bmAttributes		= USB_ENDPOINT_XFER_BULK,
    96		.wMaxPacketSize		= cpu_to_le16(512),
    97	};
    98	
    99	static struct usb_descriptor_header *ftrace_hs_function[] = {
   100		(struct usb_descriptor_header *) &ftrace_intf_desc,
   101		(struct usb_descriptor_header *) &ftrace_hs_in_desc,
   102		NULL,
   103	};
   104	
   105	/* Full-Speed Support */
   106	static struct usb_endpoint_descriptor ftrace_fs_in_desc = {
   107		.bLength		= USB_DT_ENDPOINT_SIZE,
   108		.bDescriptorType	= USB_DT_ENDPOINT,
   109	
   110		.bEndpointAddress	= USB_DIR_IN,
   111		.bmAttributes		= USB_ENDPOINT_XFER_BULK,
   112		.wMaxPacketSize		= cpu_to_le16(64),
   113	};
   114	
   115	static struct usb_descriptor_header *ftrace_fs_function[] = {
   116		(struct usb_descriptor_header *) &ftrace_intf_desc,
   117		(struct usb_descriptor_header *) &ftrace_fs_in_desc,
   118		NULL,
   119	};
   120	
   121	static struct usb_string ftrace_string_defs[] = {
   122		[0].s = "Linux Ftrace Export",
   123		{ },
   124	};
   125	
   126	static struct usb_gadget_strings ftrace_string_table = {
   127		.language		= 0x0409, /* en-US */
   128		.strings		= ftrace_string_defs,
   129	};
   130	
   131	static struct usb_gadget_strings *ftrace_strings[] = {
   132		&ftrace_string_table,
   133		NULL,
   134	};
   135	
   136	/* ------------------------------------------------------------------------ */
   137	
   138	static void ftrace_complete(struct usb_ep *ep, struct usb_request *req)
   139	{
   140		struct usb_ftrace		*trace = req->context;
   141	
   142		kfree(req->buf);
   143		list_move_tail(&req->list, &trace->list);
   144	}
   145	
   146	static void ftrace_queue_work(struct work_struct *work)
   147	{
   148		struct usb_ftrace		*trace = work_to_trace(work);
   149		struct usb_request		*req;
   150		struct usb_request		*tmp;
   151		struct list_head		local_list;
   152	
   153		spin_lock_irq(&trace->lock);
   154	restart:
   155		list_replace_init(&trace->pending, &local_list);
   156		spin_unlock_irq(&trace->lock);
   157	
   158		list_for_each_entry_safe(req, tmp, &local_list, list) {
   159			int			ret;
   160	
   161			ret = usb_ep_queue(trace->in, req, GFP_KERNEL);
   162			if (!ret)
   163				list_move_tail(&req->list, &trace->queued);
   164		}
   165	
   166		spin_lock_irq(&trace->lock);
   167		if (!list_empty(&trace->pending))
   168			goto restart;
   169		spin_unlock_irq(&trace->lock);
   170	}
   171	
   172	static void notrace ftrace_write(struct trace_export *ftrace, const void *buf,
   173			unsigned int len)
   174	{
 > 175		struct usb_ftrace		*trace = ftrace_to_trace(ftrace);
   176		struct usb_request		*req = next_request(&trace->list);
   177	
   178		if (!req)
   179			return;
   180	
   181		if (!trace->in->enabled)
   182			return;
   183	
   184		req->buf = kmemdup(buf, len, GFP_ATOMIC);
   185		req->length = len;
   186		req->context = trace;
   187		req->complete = ftrace_complete;
   188		list_move_tail(&req->list, &trace->pending);
   189	
   190		schedule_work(&trace->queue_work);
   191	}
   192	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
diff mbox series

Patch

diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 31cce7805eb2..dfd181e0d717 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -191,6 +191,9 @@  config USB_F_MASS_STORAGE
 config USB_F_FS
 	tristate
 
+config USB_F_TRACE
+	tristate
+
 config USB_F_UAC1
 	tristate
 
@@ -368,6 +371,18 @@  config USB_CONFIGFS_F_FS
 	  implemented in kernel space (for instance Ethernet, serial or
 	  mass storage) and other are implemented in user space.
 
+config USB_CONFIGFS_F_TRACE
+	bool "Linux FTrace Export Over USB"
+	depends on USB_CONFIGFS
+	select USB_F_TRACE
+	help
+	  The Linux FTrace Export Over USB lets one export ftrace buffer
+	  over a USB cable to a host computer for further processing.
+
+	  If you want support for that, say Y or M here. Otherwise say N.
+
+	  If unsure, say N.
+
 config USB_CONFIGFS_F_UAC1
 	bool "Audio Class 1.0"
 	depends on USB_CONFIGFS
diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile
index 5d3a6cf02218..eb851309006c 100644
--- a/drivers/usb/gadget/function/Makefile
+++ b/drivers/usb/gadget/function/Makefile
@@ -50,3 +50,5 @@  usb_f_printer-y			:= f_printer.o
 obj-$(CONFIG_USB_F_PRINTER)	+= usb_f_printer.o
 usb_f_tcm-y			:= f_tcm.o
 obj-$(CONFIG_USB_F_TCM)		+= usb_f_tcm.o
+usb_f_trace-y			:= f-trace.o
+obj-$(CONFIG_USB_F_TRACE)	+= usb_f_trace.o
diff --git a/drivers/usb/gadget/function/f-trace.c b/drivers/usb/gadget/function/f-trace.c
new file mode 100644
index 000000000000..b5941df128a0
--- /dev/null
+++ b/drivers/usb/gadget/function/f-trace.c
@@ -0,0 +1,401 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * f_trace.c -- USB FTrace Export
+ *
+ * Copyright (C) 2019 Intel Corporation
+ * Author: Felipe Balbi <felipe.balbi@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License v2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/trace.h>
+#include <linux/usb.h>
+#include <linux/usb/composite.h>
+#include <linux/usb/gadget.h>
+#include <linux/workqueue.h>
+
+struct usb_ftrace {
+	struct trace_export ftrace;
+	struct usb_function function;
+	struct work_struct queue_work;
+	spinlock_t lock;
+
+	struct list_head list;
+	struct list_head pending;
+	struct list_head queued;
+
+	struct usb_ep *in;
+
+	u8 intf_id;
+};
+#define ftrace_to_trace(f)	(container_of((f), struct usb_ftrace, ftrace))
+#define work_to_trace(w)	(container_of((w), struct usb_ftrace, queue_work))
+#define to_trace(f)		(container_of((f), struct usb_ftrace, function))
+
+#define FTRACE_REQUEST_QUEUE_LENGTH	250
+
+static inline struct usb_request *next_request(struct list_head *list)
+{
+	return list_first_entry_or_null(list, struct usb_request, list);
+}
+
+struct usb_ftrace_opts {
+	struct usb_function_instance func_inst;
+};
+#define to_opts(fi)	(container_of((fi), struct usb_ftrace_opts, func_inst))
+
+static struct usb_interface_descriptor ftrace_intf_desc = {
+	.bLength		= USB_DT_INTERFACE_SIZE,
+	.bDescriptorType	= USB_DT_INTERFACE,
+
+	.bAlternateSetting	= 0,
+	.bNumEndpoints		= 1,
+	.bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
+	.bInterfaceSubClass	= USB_SUBCLASS_VENDOR_SPEC,
+};
+
+/* Super-Speed Support */
+static struct usb_endpoint_descriptor ftrace_ss_in_desc = {
+	.bLength		= USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType	= USB_DT_ENDPOINT,
+
+	.bEndpointAddress	= USB_DIR_IN,
+	.bmAttributes		= USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize		= cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor ftrace_ss_in_comp_desc = {
+	.bLength		= USB_DT_SS_EP_COMP_SIZE,
+	.bDescriptorType	= USB_DT_SS_ENDPOINT_COMP,
+
+	.bMaxBurst		= 15,
+};
+
+static struct usb_descriptor_header *ftrace_ss_function[] = {
+	(struct usb_descriptor_header *) &ftrace_intf_desc,
+	(struct usb_descriptor_header *) &ftrace_ss_in_desc,
+	(struct usb_descriptor_header *) &ftrace_ss_in_comp_desc,
+	NULL,
+};
+
+/* High-Speed Support */
+static struct usb_endpoint_descriptor ftrace_hs_in_desc = {
+	.bLength		= USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType	= USB_DT_ENDPOINT,
+
+	.bEndpointAddress	= USB_DIR_IN,
+	.bmAttributes		= USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize		= cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *ftrace_hs_function[] = {
+	(struct usb_descriptor_header *) &ftrace_intf_desc,
+	(struct usb_descriptor_header *) &ftrace_hs_in_desc,
+	NULL,
+};
+
+/* Full-Speed Support */
+static struct usb_endpoint_descriptor ftrace_fs_in_desc = {
+	.bLength		= USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType	= USB_DT_ENDPOINT,
+
+	.bEndpointAddress	= USB_DIR_IN,
+	.bmAttributes		= USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize		= cpu_to_le16(64),
+};
+
+static struct usb_descriptor_header *ftrace_fs_function[] = {
+	(struct usb_descriptor_header *) &ftrace_intf_desc,
+	(struct usb_descriptor_header *) &ftrace_fs_in_desc,
+	NULL,
+};
+
+static struct usb_string ftrace_string_defs[] = {
+	[0].s = "Linux Ftrace Export",
+	{ },
+};
+
+static struct usb_gadget_strings ftrace_string_table = {
+	.language		= 0x0409, /* en-US */
+	.strings		= ftrace_string_defs,
+};
+
+static struct usb_gadget_strings *ftrace_strings[] = {
+	&ftrace_string_table,
+	NULL,
+};
+
+/* ------------------------------------------------------------------------ */
+
+static void ftrace_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct usb_ftrace		*trace = req->context;
+
+	kfree(req->buf);
+	list_move_tail(&req->list, &trace->list);
+}
+
+static void ftrace_queue_work(struct work_struct *work)
+{
+	struct usb_ftrace		*trace = work_to_trace(work);
+	struct usb_request		*req;
+	struct usb_request		*tmp;
+	struct list_head		local_list;
+
+	spin_lock_irq(&trace->lock);
+restart:
+	list_replace_init(&trace->pending, &local_list);
+	spin_unlock_irq(&trace->lock);
+
+	list_for_each_entry_safe(req, tmp, &local_list, list) {
+		int			ret;
+
+		ret = usb_ep_queue(trace->in, req, GFP_KERNEL);
+		if (!ret)
+			list_move_tail(&req->list, &trace->queued);
+	}
+
+	spin_lock_irq(&trace->lock);
+	if (!list_empty(&trace->pending))
+		goto restart;
+	spin_unlock_irq(&trace->lock);
+}
+
+static void notrace ftrace_write(struct trace_export *ftrace, const void *buf,
+		unsigned int len)
+{
+	struct usb_ftrace		*trace = ftrace_to_trace(ftrace);
+	struct usb_request		*req = next_request(&trace->list);
+
+	if (!req)
+		return;
+
+	if (!trace->in->enabled)
+		return;
+
+	req->buf = kmemdup(buf, len, GFP_ATOMIC);
+	req->length = len;
+	req->context = trace;
+	req->complete = ftrace_complete;
+	list_move_tail(&req->list, &trace->pending);
+
+	schedule_work(&trace->queue_work);
+}
+
+/* ------------------------------------------------------------------------ */
+
+static void ftrace_disable_endpoint(struct usb_ftrace *trace)
+{
+	if (trace->in->enabled)
+		WARN_ON(usb_ep_disable(trace->in));
+}
+
+static int ftrace_enable_endpoint(struct usb_ftrace *trace)
+{
+	if (trace->in->enabled)
+		return 0;
+
+	return usb_ep_enable(trace->in);
+}
+
+static int ftrace_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+	struct usb_ftrace		*trace = to_trace(f);
+	struct usb_composite_dev	*cdev = f->config->cdev;
+	int				ret;
+
+	if (alt != 0)
+		goto fail;
+
+	if (intf != trace->intf_id)
+		goto fail;
+
+	ftrace_disable_endpoint(trace);
+
+	if (!trace->in->desc) {
+		ret = config_ep_by_speed(cdev->gadget, f, trace->in);
+		if (ret) {
+			trace->in->desc = NULL;
+			goto fail;
+		}
+	}
+
+	ret = ftrace_enable_endpoint(trace);
+	if (ret)
+		goto fail;
+
+	return 0;
+
+fail:
+	return -EINVAL;
+}
+
+static int ftrace_bind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct usb_composite_dev	*cdev = c->cdev;
+	struct usb_ftrace		*trace = to_trace(f);
+	struct usb_string		*us;
+	struct usb_ep			*ep;
+
+	int				ret;
+	int				i;
+
+	us = usb_gstrings_attach(cdev, ftrace_strings,
+			ARRAY_SIZE(ftrace_string_defs));
+	if (IS_ERR(us))
+		return PTR_ERR(us);
+
+	ftrace_intf_desc.iInterface = us[0].id;
+
+	ret = usb_interface_id(c, f);
+	if (ret < 0)
+		goto err0;
+	trace->intf_id = ret;
+	ftrace_intf_desc.bInterfaceNumber = ret;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &ftrace_fs_in_desc);
+	if (!ep)
+		goto err0;
+	trace->in = ep;
+
+	ftrace_hs_in_desc.bEndpointAddress = ftrace_fs_in_desc.bEndpointAddress;
+	ftrace_ss_in_desc.bEndpointAddress = ftrace_fs_in_desc.bEndpointAddress;
+
+	trace->ftrace.write = ftrace_write;
+
+	spin_lock_init(&trace->lock);
+	INIT_WORK(&trace->queue_work, ftrace_queue_work);
+	INIT_LIST_HEAD(&trace->list);
+	INIT_LIST_HEAD(&trace->pending);
+	INIT_LIST_HEAD(&trace->queued);
+
+	ret = usb_assign_descriptors(f, ftrace_fs_function, ftrace_hs_function,
+			ftrace_ss_function, NULL);
+	if (ret)
+		goto err0;
+
+	for (i = 0; i < FTRACE_REQUEST_QUEUE_LENGTH; i++) {
+		struct usb_request *req;
+
+		req = usb_ep_alloc_request(trace->in, GFP_KERNEL);
+		if (!req)
+			goto err1;
+
+		list_add_tail(&req->list, &trace->list);
+	}
+
+	ret = register_ftrace_export(&trace->ftrace);
+	if (ret)
+		goto err1;
+
+	return 0;
+
+err1:
+	while (!list_empty(&trace->list)) {
+		struct usb_request *req = next_request(&trace->list);
+
+		usb_ep_free_request(trace->in, req);
+		list_del(&req->list);
+	}
+
+	usb_free_all_descriptors(f);
+
+err0:
+	ERROR(cdev, "%s: can't bind --> err %d\n", f->name, ret);
+
+	return ret;
+}
+
+static void ftrace_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct usb_ftrace		*trace = to_trace(f);
+	struct usb_request		*req;
+	struct usb_request		*tmp;
+
+	unregister_ftrace_export(&trace->ftrace);
+	cancel_work_sync(&trace->queue_work);
+	usb_free_all_descriptors(f);
+
+	list_for_each_entry(req, &trace->queued, list)
+		usb_ep_dequeue(trace->in, req);
+
+	list_for_each_entry_safe(req, tmp, &trace->pending, list) {
+		usb_ep_free_request(trace->in, req);
+		list_del(&req->list);
+	}
+
+	list_for_each_entry_safe(req, tmp, &trace->list, list) {
+		usb_ep_free_request(trace->in, req);
+		list_del(&req->list);
+	}
+}
+
+static void ftrace_disable(struct usb_function *f)
+{
+	struct usb_ftrace		*trace = to_trace(f);
+
+	ftrace_disable_endpoint(trace);
+}
+
+static void ftrace_free_func(struct usb_function *f)
+{
+	kfree(to_trace(f));
+}
+
+static struct config_item_type ftrace_func_type = {
+	.ct_owner		= THIS_MODULE,
+};
+
+static void ftrace_free_inst(struct usb_function_instance *fi)
+{
+	struct usb_ftrace_opts		*opts = to_opts(fi);
+
+	kfree(opts);
+}
+
+static struct usb_function_instance *ftrace_alloc_inst(void)
+{
+	struct usb_ftrace_opts		*opts;
+
+	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+	if (!opts)
+		return ERR_PTR(-ENOMEM);
+
+	opts->func_inst.free_func_inst = ftrace_free_inst;
+
+	config_group_init_type_name(&opts->func_inst.group, "",
+			&ftrace_func_type);
+
+	return &opts->func_inst;
+}
+
+static struct usb_function *ftrace_alloc(struct usb_function_instance *fi)
+{
+	struct usb_ftrace		*trace;
+
+	trace = kzalloc(sizeof(*trace), GFP_KERNEL);
+	if (!trace)
+		return NULL;
+
+	trace->function.name = "ftrace";
+	trace->function.bind = ftrace_bind;
+	trace->function.unbind = ftrace_unbind;
+	trace->function.set_alt = ftrace_set_alt;
+	trace->function.disable = ftrace_disable;
+	trace->function.strings = ftrace_strings;
+	trace->function.free_func = ftrace_free_func;
+
+	return &trace->function;
+}
+
+DECLARE_USB_FUNCTION_INIT(ftrace, ftrace_alloc_inst, ftrace_alloc);
+MODULE_AUTHOR("Felipe Balbi <felipe.balbi@linux.intel.com>");
+MODULE_LICENSE("GPL v2");