Netdev List
 help / color / mirror / Atom feed
* [PATCH] net: ethernet: allwinner: Remove unneeded memset
From: Hariprasad Kelam @ 2019-07-04  2:59 UTC (permalink / raw)
  To: David S. Miller, Maxime Ripard, Chen-Yu Tsai, Petr Štetiar,
	Hariprasad Kelam, Florian Fainelli, Andrew Lunn, netdev,
	linux-arm-kernel, linux-kernel

Remove unneeded memset as alloc_etherdev is using kvzalloc which uses
__GFP_ZERO flag

Signed-off-by: Hariprasad Kelam <hariprasad.kelam@gmail.com>
---
 drivers/net/ethernet/allwinner/sun4i-emac.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/net/ethernet/allwinner/sun4i-emac.c b/drivers/net/ethernet/allwinner/sun4i-emac.c
index 9e06dff..6253e5e 100644
--- a/drivers/net/ethernet/allwinner/sun4i-emac.c
+++ b/drivers/net/ethernet/allwinner/sun4i-emac.c
@@ -818,7 +818,6 @@ static int emac_probe(struct platform_device *pdev)
 	SET_NETDEV_DEV(ndev, &pdev->dev);
 
 	db = netdev_priv(ndev);
-	memset(db, 0, sizeof(*db));
 
 	db->dev = &pdev->dev;
 	db->ndev = ndev;
-- 
2.7.4


^ permalink raw reply related

* [PATCH 0/1] tools/dtrace: initial implementation of DTrace
From: Kris Van Hees @ 2019-07-04  3:13 UTC (permalink / raw)
  To: netdev, bpf, dtrace-devel, linux-kernel
  Cc: rostedt, mhiramat, acme, ast, daniel, Peter Zijlstra, Chris Mason

This patch is also available, applied to bpf-next, at the following URL:

	https://github.com/oracle/dtrace-linux-kernel/tree/dtrace-bpf

As suggested in feedback to my earlier patch submissions, this code takes an
approach to avoid kernel code changes as much as possible.  The current patch
does not involve any kernel code changes.  Further development of this code
will continue with this approach, incrementally adding features to this first
minimal implementation.  The goal is a fully featured and functional DTrace
implementation involving kernel changes only when strictly necessary.

The code presented here supports two very basic functions:

1. Listing probes that are used in BPF programs

   # dtrace -l -s bpf_sample.o
      ID   PROVIDER            MODULE                          FUNCTION NAME
   18876        fbt           vmlinux                        ksys_write entry
   70423    syscall           vmlinux                             write entry

2. Loading BPF tracing programs and collecting data that they generate

   # dtrace -s bpf_sample.o
   CPU     ID
    15  70423 0xffff8c0968bf8ec0 0x00000000000001 0x0055e019eb3f60 0x0000000000002c
    15  18876 0xffff8c0968bf8ec0 0x00000000000001 0x0055e019eb3f60 0x0000000000002c
   ...

Only kprobes and syscall tracepoints are supported since this is an initial
patch.  It does show the use of a generic BPF function to implement the actual
probe action, called from two distinct probe types.  Follow-up patches will
add more probe types, add more tracing features from the D language, add
support for D script compilation to BPF, etc.

The implementation makes use of libbpf for handling BPF ELF objects, and uses
the perf event output ring buffer (supported through BPF) to retrieve the
tracing data.  The next step in development will be adding support to libbpf
for programs using shared functions from a collection of functions included in
the BPF ELF object (as suggested by Alexei).  

The code is structured as follows:
 tools/dtrace/dtrace.c      = command line utility
 tools/dtrace/dt_bpf.c      = interface to libbpf
 tools/dtrace/dt_buffer.c   = perf event output buffer handling
 tools/dtrace/dt_fbt.c      = kprobes probe provider
 tools/dtrace/dt_syscall.c  = syscall tracepoint probe provider
 tools/dtrace/dt_probe.c    = generic probe and probe provider handling code
                              This implements a generic interface to the actual
                              probe providers (dt_fbt and dt_syscall).
 tools/dtrace/dt_hash.c     = general probe hashing implementation
 tools/dtrace/dt_utils.c    = support code (manage list of online CPUs)
 tools/dtrace/dtrace.h      = API header file (used by BPF program source code)
 tools/dtrace/dtrace_impl.h = implementation header file
 tools/dtrace/bpf_sample.c  = sample BPF program using two probe types

I included an entry for the MAINTAINERS file.  I offer to actively maintain
this code, and to keep advancing its development.

	Cheers,
	Kris Van Hees

^ permalink raw reply

* [PATCH v2 net-next] net: socionext: remove set but not used variable 'pkts'
From: YueHaibing @ 2019-07-04  3:21 UTC (permalink / raw)
  To: Jassi Brar, Alexei Starovoitov, David S . Miller,
	Ilias Apalodimas, Daniel Borkmann, Jakub Kicinski,
	Jesper Dangaard Brouer
  Cc: YueHaibing, netdev
In-Reply-To: <20190703024213.191191-1-yuehaibing@huawei.com>

Cc: netdev@vger.kernel.org,
    xdp-newbies@vger.kernel.org,
    bpf@vger.kernel.org,
    kernel-janitors@vger.kernel.org

Fixes gcc '-Wunused-but-set-variable' warning:

drivers/net/ethernet/socionext/netsec.c: In function 'netsec_clean_tx_dring':
drivers/net/ethernet/socionext/netsec.c:637:15: warning:
 variable 'pkts' set but not used [-Wunused-but-set-variable]

It is not used since commit ba2b232108d3 ("net: netsec: add XDP support")

Signed-off-by: YueHaibing <yuehaibing@huawei.com>
Acked-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
---
v2: keep reverse christmas-tree ordering of the local variables
---
 drivers/net/ethernet/socionext/netsec.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/socionext/netsec.c b/drivers/net/ethernet/socionext/netsec.c
index 5544a722543f..d8d640b01119 100644
--- a/drivers/net/ethernet/socionext/netsec.c
+++ b/drivers/net/ethernet/socionext/netsec.c
@@ -634,15 +634,14 @@ static void netsec_set_rx_de(struct netsec_priv *priv,
 static bool netsec_clean_tx_dring(struct netsec_priv *priv)
 {
 	struct netsec_desc_ring *dring = &priv->desc_ring[NETSEC_RING_TX];
-	unsigned int pkts, bytes;
 	struct netsec_de *entry;
 	int tail = dring->tail;
+	unsigned int bytes;
 	int cnt = 0;
 
 	if (dring->is_xdp)
 		spin_lock(&dring->lock);
 
-	pkts = 0;
 	bytes = 0;
 	entry = dring->vaddr + DESC_SZ * tail;




^ permalink raw reply related

* [PATCH 1/1] tools/dtrace: initial implementation of DTrace
From: Kris Van Hees @ 2019-07-04  3:14 UTC (permalink / raw)
  To: netdev, bpf, dtrace-devel, linux-kernel
  Cc: rostedt, mhiramat, acme, ast, daniel, Peter Zijlstra, Chris Mason
In-Reply-To: <201907040313.x643D8Pg025951@userv0121.oracle.com>

This initial implementation of a tiny subset of DTrace functionality
provides the following options:

	dtrace [-lvV] [-b bufsz] -s script
	    -b  set trace buffer size
	    -l  list probes (only works with '-s script' for now)
	    -s  enable or list probes for the specified BPF program
	    -V  report DTrace API version

The patch comprises quite a bit of code due to DTrace requiring a few
crucial components, even in its most basic form.

The code is structured around the command line interface implemented in
dtrace.c.  It provides option parsing and drives the three modes of
operation that are currently implemented:

1. Report DTrace API version information.
	Report the version information and terminate.

2. List probes in BPF programs.
	Initialize the list of probes that DTrace recognizes, load BPF
	programs, parse all BPF ELF section names, resolve them into
	known probes, and emit the probe names.  Then terminate.

3. Load BPF programs and collect tracing data.
	Initialize the list of probes that DTrace recognizes, load BPF
	programs and attach them to their corresponding probes, set up
	perf event output buffers, and start processing tracing data.

This implementation makes extensive use of BPF (handled by dt_bpf.c) and
the perf event output ring buffer (handled by dt_buffer.c).  DTrace-style
probe handling (dt_probe.c) offers an interface to probes that hides the
implementation details of the individual probe types by provider (dt_fbt.c
and dt_syscall.c).  Probe lookup by name uses a hashtable implementation
(dt_hash.c).  The dt_utils.c code populates a list of online CPU ids, so
we know what CPUs we can obtain tracing data from.

Building the tool is trivial because its only dependency (libbpf) is in
the kernel tree under tools/lib/bpf.  A simple 'make' in the tools/dtrace
directory suffices.

The 'dtrace' executable needs to run as root because BPF programs cannot
be loaded by non-root users.

Signed-off-by: Kris Van Hees <kris.van.hees@oracle.com>
Reviewed-by: David Mc Lean <david.mclean@oracle.com>
Reviewed-by: Eugene Loh <eugene.loh@oracle.com>
---
 MAINTAINERS                |   6 +
 tools/dtrace/Makefile      |  88 ++++++++++
 tools/dtrace/bpf_sample.c  | 145 ++++++++++++++++
 tools/dtrace/dt_bpf.c      | 188 +++++++++++++++++++++
 tools/dtrace/dt_buffer.c   | 331 +++++++++++++++++++++++++++++++++++++
 tools/dtrace/dt_fbt.c      | 201 ++++++++++++++++++++++
 tools/dtrace/dt_hash.c     | 211 +++++++++++++++++++++++
 tools/dtrace/dt_probe.c    | 230 ++++++++++++++++++++++++++
 tools/dtrace/dt_syscall.c  | 179 ++++++++++++++++++++
 tools/dtrace/dt_utils.c    | 132 +++++++++++++++
 tools/dtrace/dtrace.c      | 249 ++++++++++++++++++++++++++++
 tools/dtrace/dtrace.h      |  13 ++
 tools/dtrace/dtrace_impl.h | 101 +++++++++++
 13 files changed, 2074 insertions(+)
 create mode 100644 tools/dtrace/Makefile
 create mode 100644 tools/dtrace/bpf_sample.c
 create mode 100644 tools/dtrace/dt_bpf.c
 create mode 100644 tools/dtrace/dt_buffer.c
 create mode 100644 tools/dtrace/dt_fbt.c
 create mode 100644 tools/dtrace/dt_hash.c
 create mode 100644 tools/dtrace/dt_probe.c
 create mode 100644 tools/dtrace/dt_syscall.c
 create mode 100644 tools/dtrace/dt_utils.c
 create mode 100644 tools/dtrace/dtrace.c
 create mode 100644 tools/dtrace/dtrace.h
 create mode 100644 tools/dtrace/dtrace_impl.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 606d1f80bc49..668468834865 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5474,6 +5474,12 @@ W:	https://linuxtv.org
 S:	Odd Fixes
 F:	drivers/media/pci/dt3155/
 
+DTRACE
+M:	Kris Van Hees <kris.van.hees@oracle.com>
+L:	dtrace-devel@oss.oracle.com
+S:	Maintained
+F:	tools/dtrace/
+
 DVB_USB_AF9015 MEDIA DRIVER
 M:	Antti Palosaari <crope@iki.fi>
 L:	linux-media@vger.kernel.org
diff --git a/tools/dtrace/Makefile b/tools/dtrace/Makefile
new file mode 100644
index 000000000000..99fd0f9dd1d6
--- /dev/null
+++ b/tools/dtrace/Makefile
@@ -0,0 +1,88 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# This Makefile is based on samples/bpf.
+#
+# Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+
+DT_VERSION		:= 2.0.0
+DT_GIT_VERSION		:= $(shell git rev-parse HEAD 2>/dev/null || \
+				   echo Unknown)
+
+DTRACE_PATH		?= $(abspath $(srctree)/$(src))
+TOOLS_PATH		:= $(DTRACE_PATH)/..
+SAMPLES_PATH		:= $(DTRACE_PATH)/../../samples
+
+hostprogs-y		:= dtrace
+
+LIBBPF			:= $(TOOLS_PATH)/lib/bpf/libbpf.a
+OBJS			:= dt_bpf.o dt_buffer.o dt_utils.o dt_probe.o \
+			   dt_hash.o \
+			   dt_fbt.o dt_syscall.o
+
+dtrace-objs		:= $(OBJS) dtrace.o
+
+always			:= $(hostprogs-y)
+always			+= bpf_sample.o
+
+KBUILD_HOSTCFLAGS	+= -DDT_VERSION=\"$(DT_VERSION)\"
+KBUILD_HOSTCFLAGS	+= -DDT_GIT_VERSION=\"$(DT_GIT_VERSION)\"
+KBUILD_HOSTCFLAGS	+= -I$(srctree)/tools/lib
+KBUILD_HOSTCFLAGS	+= -I$(srctree)/tools/perf
+KBUILD_HOSTCFLAGS	+= -I$(srctree)/tools/include/uapi
+KBUILD_HOSTCFLAGS	+= -I$(srctree)/tools/include/
+KBUILD_HOSTCFLAGS	+= -I$(srctree)/usr/include
+
+KBUILD_HOSTLDLIBS	:= $(LIBBPF) -lelf
+
+LLC			?= llc
+CLANG			?= clang
+LLVM_OBJCOPY		?= llvm-objcopy
+
+ifdef CROSS_COMPILE
+HOSTCC			= $(CROSS_COMPILE)gcc
+CLANG_ARCH_ARGS		= -target $(ARCH)
+endif
+
+all:
+	$(MAKE) -C ../../ $(CURDIR)/ DTRACE_PATH=$(CURDIR)
+
+clean:
+	$(MAKE) -C ../../ M=$(CURDIR) clean
+	@rm -f *~
+
+$(LIBBPF): FORCE
+	$(MAKE) -C $(dir $@) RM='rm -rf' LDFLAGS= srctree=$(DTRACE_PATH)/../../ O=
+
+FORCE:
+
+.PHONY: verify_cmds verify_target_bpf $(CLANG) $(LLC)
+
+verify_cmds: $(CLANG) $(LLC)
+	@for TOOL in $^ ; do \
+		if ! (which -- "$${TOOL}" > /dev/null 2>&1); then \
+			echo "*** ERROR: Cannot find LLVM tool $${TOOL}" ;\
+			exit 1; \
+		else true; fi; \
+	done
+
+verify_target_bpf: verify_cmds
+	@if ! (${LLC} -march=bpf -mattr=help > /dev/null 2>&1); then \
+		echo "*** ERROR: LLVM (${LLC}) does not support 'bpf' target" ;\
+		echo "   NOTICE: LLVM version >= 3.7.1 required" ;\
+		exit 2; \
+	else true; fi
+
+$(DTRACE_PATH)/*.c: verify_target_bpf $(LIBBPF)
+$(src)/*.c: verify_target_bpf $(LIBBPF)
+
+$(obj)/%.o: $(src)/%.c
+	@echo "  CLANG-bpf " $@
+	$(Q)$(CLANG) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) $(EXTRA_CFLAGS) -I$(obj) \
+		-I$(srctree)/tools/testing/selftests/bpf/ \
+		-D__KERNEL__ -D__BPF_TRACING__ -Wno-unused-value -Wno-pointer-sign \
+		-D__TARGET_ARCH_$(ARCH) -Wno-compare-distinct-pointer-types \
+		-Wno-gnu-variable-sized-type-not-at-end \
+		-Wno-address-of-packed-member -Wno-tautological-compare \
+		-Wno-unknown-warning-option $(CLANG_ARCH_ARGS) \
+		-I$(srctree)/samples/bpf/ -include asm_goto_workaround.h \
+		-O2 -emit-llvm -c $< -o -| $(LLC) -march=bpf $(LLC_FLAGS) -filetype=obj -o $@
diff --git a/tools/dtrace/bpf_sample.c b/tools/dtrace/bpf_sample.c
new file mode 100644
index 000000000000..49f350390b5f
--- /dev/null
+++ b/tools/dtrace/bpf_sample.c
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This sample DTrace BPF tracing program demonstrates how actions can be
+ * associated with different probe types.
+ *
+ * The kprobe/ksys_write probe is a Function Boundary Tracing (FBT) entry probe
+ * on the ksys_write(fd, buf, count) function in the kernel.  Arguments to the
+ * function can be retrieved from the CPU registers (struct pt_regs).
+ *
+ * The tracepoint/syscalls/sys_enter_write probe is a System Call entry probe
+ * for the write(d, buf, count) system call.  Arguments to the system call can
+ * be retrieved from the tracepoint data passed to the BPF program as context
+ * struct syscall_data) when the probe fires.
+ *
+ * The BPF program associated with each probe prepares a DTrace BPF context
+ * (struct dt_bpf_context) that stores the probe ID and up to 10 arguments.
+ * Only 3 arguments are used in this sample.  Then the prorgams call a shared
+ * BPF function (bpf_action) that implements the actual action to be taken when
+ * a probe fires.  It prepares a data record to be stored in the tracing buffer
+ * and submits it to the buffer.  The data in the data record is obtained from
+ * the DTrace BPF context.
+ *
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ */
+#include <uapi/linux/bpf.h>
+#include <linux/ptrace.h>
+#include <linux/version.h>
+#include <uapi/linux/unistd.h>
+#include "bpf_helpers.h"
+
+#include "dtrace.h"
+
+struct syscall_data {
+	struct pt_regs *regs;
+	long syscall_nr;
+	long arg[6];
+};
+
+struct bpf_map_def SEC("maps") buffers = {
+	.type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
+	.key_size = sizeof(u32),
+	.value_size = sizeof(u32),
+	.max_entries = NR_CPUS,
+};
+
+#if defined(__amd64)
+# define GET_REGS_ARG0(regs)	((regs)->di)
+# define GET_REGS_ARG1(regs)	((regs)->si)
+# define GET_REGS_ARG2(regs)	((regs)->dx)
+# define GET_REGS_ARG3(regs)	((regs)->cx)
+# define GET_REGS_ARG4(regs)	((regs)->r8)
+# define GET_REGS_ARG5(regs)	((regs)->r9)
+#else
+# warning Argument retrieval from pt_regs is not supported yet on this arch.
+# define GET_REGS_ARG0(regs)	0
+# define GET_REGS_ARG1(regs)	0
+# define GET_REGS_ARG2(regs)	0
+# define GET_REGS_ARG3(regs)	0
+# define GET_REGS_ARG4(regs)	0
+# define GET_REGS_ARG5(regs)	0
+#endif
+
+/*
+ * We must pass a valid BPF context pointer because the bpf_perf_event_output()
+ * helper requires a BPF context pointer as first argument (and the verifier is
+ * validating that we pass a value that is known to be a context pointer).
+ *
+ * This BPF function implements the following D action:
+ * {
+ *	trace(curthread);
+ *	trace(arg0);
+ *	trace(arg1);
+ *	trace(arg2);
+ * }
+ *
+ * Expected output will look like:
+ *   CPU     ID
+ *    15  70423 0xffff8c0968bf8ec0 0x00000000000001 0x0055e019eb3f60 0x0000000000002c
+ *    15  18876 0xffff8c0968bf8ec0 0x00000000000001 0x0055e019eb3f60 0x0000000000002c
+ *    |   |     +-- curthread      +--> arg0 (fd)   +--> arg1 (buf)  +-- arg2 (count)
+ *    |   |
+ *    |   +--> probe ID
+ *    |
+ *    +--> CPU the probe fired on
+ */
+static noinline int bpf_action(void *bpf_ctx, struct dt_bpf_context *ctx)
+{
+	int			cpu = bpf_get_smp_processor_id();
+	struct data {
+		u32	probe_id;	/* mandatory */
+
+		u64	task;		/* first data item (current task) */
+		u64	arg0;		/* 2nd data item (arg0, fd) */
+		u64	arg1;		/* 3rd data item (arg1, buf) */
+		u64	arg2;		/* 4th data item (arg2, count) */
+	}			rec;
+
+	memset(&rec, 0, sizeof(rec));
+
+	rec.probe_id = ctx->probe_id;
+	rec.task = bpf_get_current_task();
+	rec.arg0 = ctx->argv[0];
+	rec.arg1 = ctx->argv[1];
+	rec.arg2 = ctx->argv[2];
+
+	bpf_perf_event_output(bpf_ctx, &buffers, cpu, &rec, sizeof(rec));
+
+	return 0;
+}
+
+SEC("kprobe/ksys_write")
+int bpf_kprobe(struct pt_regs *regs)
+{
+	struct dt_bpf_context	ctx;
+
+	memset(&ctx, 0, sizeof(ctx));
+
+	ctx.probe_id = 18876;
+	ctx.argv[0] = GET_REGS_ARG0(regs);
+	ctx.argv[1] = GET_REGS_ARG1(regs);
+	ctx.argv[2] = GET_REGS_ARG2(regs);
+	ctx.argv[3] = GET_REGS_ARG3(regs);
+	ctx.argv[4] = GET_REGS_ARG4(regs);
+	ctx.argv[5] = GET_REGS_ARG5(regs);
+
+	return bpf_action(regs, &ctx);
+}
+
+SEC("tracepoint/syscalls/sys_enter_write")
+int bpf_tp(struct syscall_data *scd)
+{
+	struct dt_bpf_context	ctx;
+
+	memset(&ctx, 0, sizeof(ctx));
+
+	ctx.probe_id = 70423;
+	ctx.argv[0] = scd->arg[0];
+	ctx.argv[1] = scd->arg[1];
+	ctx.argv[2] = scd->arg[2];
+
+	return bpf_action(scd, &ctx);
+}
+
+char _license[] SEC("license") = "GPL";
+u32 _version SEC("version") = LINUX_VERSION_CODE;
diff --git a/tools/dtrace/dt_bpf.c b/tools/dtrace/dt_bpf.c
new file mode 100644
index 000000000000..6db1833c731a
--- /dev/null
+++ b/tools/dtrace/dt_bpf.c
@@ -0,0 +1,188 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file provides the interface for handling BPF.  It uses the bpf library
+ * to interact with BPF ELF object files.
+ *
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ */
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <bpf/libbpf.h>
+#include <linux/kernel.h>
+#include <linux/perf_event.h>
+#include <sys/ioctl.h>
+
+#include "dtrace_impl.h"
+
+/*
+ * Validate the output buffer map that is specified in the BPF ELF object.  It
+ * must match the following definition to be valid:
+ *
+ * struct bpf_map_def SEC("maps") buffers = {
+ *	.type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
+ *	.key_size = sizeof(u32),
+ *	.value_size = sizeof(u32),
+ *	.max_entries = num,
+ * };
+ * where num is greater than dt_maxcpuid.
+ */
+static int is_valid_buffers(const struct bpf_map_def *mdef)
+{
+	return mdef->type == BPF_MAP_TYPE_PERF_EVENT_ARRAY &&
+	       mdef->key_size == sizeof(u32) &&
+	       mdef->value_size == sizeof(u32) &&
+	       mdef->max_entries > dt_maxcpuid;
+}
+
+/*
+ * List the probes specified in the given BPF ELF object file.
+ */
+int dt_bpf_list_probes(const char *fn)
+{
+	struct bpf_object	*obj;
+	struct bpf_program	*prog;
+	int			rc, fd;
+
+	libbpf_set_print(NULL);
+
+	/*
+	 * Listing probes is done before the DTrace command line utility loads
+	 * the supplied programs.  We load them here without attaching them to
+	 * probes so that we can retrieve the ELF section names for each BPF
+	 * program.  The section name indicates the probe that the program is
+	 * associated with.
+	 */
+	rc = bpf_prog_load(fn, BPF_PROG_TYPE_UNSPEC, &obj, &fd);
+	if (rc)
+		return rc;
+
+	/*
+	 * Loop through the programs in the BPF ELF object, and try to resolve
+	 * the section names into probes.  Use the supplied callback function
+	 * to emit the probe description.
+	 */
+	for (prog = bpf_program__next(NULL, obj); prog != NULL;
+	     prog = bpf_program__next(prog, obj)) {
+		struct dt_probe	*probe;
+
+		probe = dt_probe_resolve_event(bpf_program__title(prog, false));
+
+		printf("%5d %10s %17s %33s %s\n", probe->id,
+		       probe->prv_name ? probe->prv_name : "",
+		       probe->mod_name ? probe->mod_name : "",
+		       probe->fun_name ? probe->fun_name : "",
+		       probe->prb_name ? probe->prb_name : "");
+	}
+
+
+	/* Done with the BPF ELF object.  */
+	bpf_object__close(obj);
+
+	return 0;
+}
+
+/*
+ * Load the given BPF ELF object file.
+ */
+int dt_bpf_load_file(const char *fn)
+{
+	struct bpf_object	*obj;
+	struct bpf_map		*map;
+	struct bpf_program	*prog;
+	int			rc, fd;
+
+	libbpf_set_print(NULL);
+
+	/* Load the BPF ELF object file. */
+	rc = bpf_prog_load(fn, BPF_PROG_TYPE_UNSPEC, &obj, &fd);
+	if (rc)
+		return rc;
+
+	/* Validate buffers map. */
+	map = bpf_object__find_map_by_name(obj, "buffers");
+	if (map && is_valid_buffers(bpf_map__def(map)))
+		dt_bufmap_fd = bpf_map__fd(map);
+	else
+		goto fail;
+
+	/*
+	 * Loop through the programs and resolve each into the matching probe.
+	 * Attach the program to the probe.
+	 */
+	for (prog = bpf_program__next(NULL, obj); prog != NULL;
+	     prog = bpf_program__next(prog, obj)) {
+		struct dt_probe	*probe;
+
+		probe = dt_probe_resolve_event(bpf_program__title(prog, false));
+		if (!probe)
+			return -ENOENT;
+		if (probe->prov && probe->prov->attach)
+			probe->prov->attach(bpf_program__title(prog, false),
+					    bpf_program__fd(prog));
+	}
+
+	return 0;
+
+fail:
+	bpf_object__close(obj);
+	return -EINVAL;
+}
+
+/*
+ * Store the (key, value) pair in the map referenced by the given fd.
+ */
+int dt_bpf_map_update(int fd, const void *key, const void *val)
+{
+	union bpf_attr	attr;
+
+	memset(&attr, 0, sizeof(attr));
+
+	attr.map_fd = fd;
+	attr.key = (u64)(unsigned long)key;
+	attr.value = (u64)(unsigned long)val;
+	attr.flags = 0;
+
+	return bpf(BPF_MAP_UPDATE_ELEM, &attr);
+}
+
+/*
+ * Attach a trace event and associate a BPF program with it.
+ */
+int dt_bpf_attach(int event_id, int bpf_fd)
+{
+	int			event_fd;
+	int			rc;
+	struct perf_event_attr	attr = {};
+
+	attr.type = PERF_TYPE_TRACEPOINT;
+	attr.sample_type = PERF_SAMPLE_RAW;
+	attr.sample_period = 1;
+	attr.wakeup_events = 1;
+	attr.config = event_id;
+
+	/* Register the event (based on its id), and obtain a fd. */
+	event_fd = perf_event_open(&attr, -1, 0, -1, 0);
+	if (event_fd < 0) {
+		perror("sys_perf_event_open");
+		return -1;
+	}
+
+	/* Enable the probe. */
+	rc = ioctl(event_fd, PERF_EVENT_IOC_ENABLE, 0);
+	if (rc < 0) {
+		perror("PERF_EVENT_IOC_ENABLE");
+		return -1;
+	}
+
+	/* Associate the BPF program with the event. */
+	rc = ioctl(event_fd, PERF_EVENT_IOC_SET_BPF, bpf_fd);
+	if (rc < 0) {
+		perror("PERF_EVENT_IOC_SET_BPF");
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/tools/dtrace/dt_buffer.c b/tools/dtrace/dt_buffer.c
new file mode 100644
index 000000000000..1714388a7ae6
--- /dev/null
+++ b/tools/dtrace/dt_buffer.c
@@ -0,0 +1,331 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file provides the tracing buffer handling for DTrace.  It makes use of
+ * the perf event output ring buffers that can be written to from BPF programs.
+ *
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syscall.h>
+#include <unistd.h>
+#include <sys/epoll.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <linux/bpf.h>
+#include <linux/perf_event.h>
+
+#include "dtrace_impl.h"
+
+/*
+ * Probe data is recorded in per-CPU perf ring buffers.
+ */
+struct dtrace_buffer {
+	int	cpu;			/* ID of CPU that uses this buffer */
+	int	fd;			/* fd of perf output buffer */
+	size_t	page_size;		/* size of each page in buffer */
+	size_t	data_size;		/* total buffer size */
+	void	*base;			/* address of buffer */
+};
+
+static struct dtrace_buffer	*dt_buffers;
+
+/*
+ * File descriptor for the BPF map that holds the buffers for the online CPUs.
+ * The map is a bpf_array indexed by CPU id, and it stores a file descriptor as
+ * value (the fd for the perf_event that represents the CPU buffer).
+ */
+int				dt_bufmap_fd = -1;
+
+/*
+ * Create a perf_event buffer for the given DTrace buffer.  This will create
+ * a perf_event ring_buffer, mmap it, and enable the perf_event that owns the
+ * buffer.
+ */
+static int perf_buffer_open(struct dtrace_buffer *buf)
+{
+	int			pefd;
+	struct perf_event_attr	attr = {};
+
+	/*
+	 * Event configuration for BPF-generated output in perf_event ring
+	 * buffers.
+	 */
+	attr.config = PERF_COUNT_SW_BPF_OUTPUT;
+	attr.type = PERF_TYPE_SOFTWARE;
+	attr.sample_type = PERF_SAMPLE_RAW;
+	attr.sample_period = 1;
+	attr.wakeup_events = 1;
+	pefd = perf_event_open(&attr, -1, buf->cpu, -1, PERF_FLAG_FD_CLOEXEC);
+	if (pefd < 0) {
+		fprintf(stderr, "perf_event_open(cpu %d): %s\n", buf->cpu,
+			strerror(errno));
+		goto fail;
+	}
+
+	/*
+	 * We add buf->page_size to the buf->data_size, because perf maintains
+	 * a meta-data page at the beginning of the memory region.  That page
+	 * is used for reader/writer symchronization.
+	 */
+	buf->fd = pefd;
+	buf->base = mmap(NULL, buf->page_size + buf->data_size,
+			 PROT_READ | PROT_WRITE, MAP_SHARED, buf->fd, 0);
+	if (!buf->base)
+		goto fail;
+
+	if (ioctl(pefd, PERF_EVENT_IOC_ENABLE, 0) < 0) {
+		fprintf(stderr, "PERF_EVENT_IOC_ENABLE(cpu %d): %s\n",
+			buf->cpu, strerror(errno));
+		goto fail;
+	}
+
+	return 0;
+
+fail:
+	if (buf->base) {
+		munmap(buf->base, buf->page_size + buf->data_size);
+		buf->base = NULL;
+	}
+	if (buf->fd) {
+		close(buf->fd);
+		buf->fd = -1;
+	}
+
+	return -1;
+}
+
+/*
+ * Close the given DTrace buffer.  This function disables the perf_event that
+ * owns the buffer, munmaps the memory space, and closes the perf buffer fd.
+ */
+static void perf_buffer_close(struct dtrace_buffer *buf)
+{
+	/*
+	 * If the perf buffer failed to open, there is no need to close it.
+	 */
+	if (buf->fd < 0)
+		return;
+
+	if (ioctl(buf->fd, PERF_EVENT_IOC_DISABLE, 0) < 0)
+		fprintf(stderr, "PERF_EVENT_IOC_DISABLE(cpu %d): %s\n",
+			buf->cpu, strerror(errno));
+
+	munmap(buf->base, buf->page_size + buf->data_size);
+
+	if (close(buf->fd))
+		fprintf(stderr, "perf buffer close(cpu %d): %s\n",
+			buf->cpu, strerror(errno));
+
+	buf->base = NULL;
+	buf->fd = -1;
+}
+
+/*
+ * Initialize the probe data buffers (one per online CPU).  Each buffer will
+ * contain the given number of pages (i.e. total size of each buffer will be
+ * num_pages * getpagesize()).  This function also sets up an event polling
+ * descriptor that monitors all CPU buffers at once.
+ */
+int dt_buffer_init(int num_pages)
+{
+	int	i;
+	int	epoll_fd;
+
+	if (dt_bufmap_fd < 0)
+		return -EINVAL;
+
+	/* Allocate the per-CPU buffer structs. */
+	dt_buffers = calloc(dt_numcpus, sizeof(struct dtrace_buffer));
+	if (dt_buffers == NULL)
+		return -ENOMEM;
+
+	/* Set up the event polling file descriptor. */
+	epoll_fd = epoll_create1(EPOLL_CLOEXEC);
+	if (epoll_fd < 0) {
+		free(dt_buffers);
+		return -errno;
+	}
+
+	for (i = 0; i < dt_numcpus; i++) {
+		int			cpu = dt_cpuids[i];
+		struct epoll_event	ev;
+		struct dtrace_buffer	*buf = &dt_buffers[i];
+
+		buf->cpu = cpu;
+		buf->page_size = getpagesize();
+		buf->data_size = num_pages * buf->page_size;
+
+		/* Try to create the perf buffer for this DTrace buffer. */
+		if (perf_buffer_open(buf) == -1)
+			continue;
+
+		/* Store the perf buffer fd in the buffer map. */
+		dt_bpf_map_update(dt_bufmap_fd, &cpu, &buf->fd);
+
+		/* Add the buffer to the event polling descriptor. */
+		ev.events = EPOLLIN;
+		ev.data.ptr = buf;
+		if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, buf->fd, &ev) == -1) {
+			fprintf(stderr, "EPOLL_CTL_ADD(cpu %d): %s\n",
+				buf->cpu, strerror(errno));
+			continue;
+		}
+	}
+
+	return epoll_fd;
+}
+
+/*
+ * Clean up the buffers.
+ */
+void dt_buffer_exit(int epoll_fd)
+{
+	int	i;
+
+	for (i = 0; i < dt_numcpus; i++)
+		perf_buffer_close(&dt_buffers[i]);
+
+	free(dt_buffers);
+	close(epoll_fd);
+}
+
+/*
+ * Read the data_head offset from the header page of the ring buffer.  The
+ * argument is declared 'volatile' because it references a memory mapped page
+ * that the kernel may be writing to while we access it here.
+ */
+static u64 read_rb_head(volatile struct perf_event_mmap_page *rb_page)
+{
+	u64	head = rb_page->data_head;
+
+	asm volatile("" ::: "memory");
+
+	return head;
+}
+
+/*
+ * Write the data_tail offset in the header page of the ring buffer.  The
+ * argument is declared 'volatile' because it references a memory mapped page
+ * that the kernel may be writing to while we access it here.
+ */
+static void write_rb_tail(volatile struct perf_event_mmap_page *rb_page,
+			  u64 tail)
+{
+	asm volatile("" ::: "memory");
+
+	rb_page->data_tail = tail;
+}
+
+/*
+ * Process and output the probe data at the supplied address.
+ */
+static int output_event(int cpu, u64 *buf)
+{
+	u8				*data = (u8 *)buf;
+	struct perf_event_header	*hdr;
+
+	hdr = (struct perf_event_header *)data;
+	data += sizeof(struct perf_event_header);
+
+	if (hdr->type == PERF_RECORD_SAMPLE) {
+		u8		*ptr = data;
+		u32		i, size, probe_id;
+
+		/*
+		 * struct {
+		 *	struct perf_event_header	header;
+		 *	u32				size;
+		 *	u32				probe_id;
+		 *	u32				gap;
+		 *	u64				data[n];
+		 * }
+		 * and data points to the 'size' member at this point.
+		 */
+		if (ptr > (u8 *)buf + hdr->size) {
+			fprintf(stderr, "BAD: corrupted sample header\n");
+			goto out;
+		}
+
+		size = *(u32 *)data;
+		data += sizeof(size);
+		ptr += sizeof(size) + size;
+		if (ptr != (u8 *)buf + hdr->size) {
+			fprintf(stderr, "BAD: invalid sample size\n");
+			goto out;
+		}
+
+		probe_id = *(u32 *)data;
+		data += sizeof(probe_id);
+		size -= sizeof(probe_id);
+		data += sizeof(u32);		/* skip 32-bit gap */
+		size -= sizeof(u32);
+		buf = (u64 *)data;
+
+		printf("%3d %6d ", cpu, probe_id);
+		for (i = 0, size /= sizeof(u64); i < size; i++)
+			printf("%#016lx ", buf[i]);
+		printf("\n");
+	} else if (hdr->type == PERF_RECORD_LOST) {
+		u64	lost;
+
+		/*
+		 * struct {
+		 *	struct perf_event_header	header;
+		 *	u64				id;
+		 *	u64				lost;
+		 * }
+		 * and data points to the 'id' member at this point.
+		 */
+		lost = *(u64 *)(data + sizeof(u64));
+
+		printf("[%ld probes dropped]\n", lost);
+	} else
+		fprintf(stderr, "UNKNOWN: record type %d\n", hdr->type);
+
+out:
+	return hdr->size;
+}
+
+/*
+ * Process the available probe data in the given buffer.
+ */
+static void process_data(struct dtrace_buffer *buf)
+{
+	/* This is volatile because the kernel may be updating the content. */
+	volatile struct perf_event_mmap_page	*rb_page = buf->base;
+	u8					*base = (u8 *)buf->base +
+							buf->page_size;
+	u64					head = read_rb_head(rb_page);
+
+	while (rb_page->data_tail != head) {
+		u64	tail = rb_page->data_tail;
+		u64	*ptr = (u64 *)(base + tail % buf->data_size);
+		int	len;
+
+		len = output_event(buf->cpu, ptr);
+
+		write_rb_tail(rb_page, tail + len);
+		head = read_rb_head(rb_page);
+	}
+}
+
+/*
+ * Wait for data to become available in any of the buffers.
+ */
+int dt_buffer_poll(int epoll_fd, int timeout)
+{
+	struct epoll_event	events[dt_numcpus];
+	int			i, cnt;
+
+	cnt = epoll_wait(epoll_fd, events, dt_numcpus, timeout);
+	if (cnt < 0)
+		return -errno;
+
+	for (i = 0; i < cnt; i++)
+		process_data((struct dtrace_buffer *)events[i].data.ptr);
+
+	return cnt;
+}
diff --git a/tools/dtrace/dt_fbt.c b/tools/dtrace/dt_fbt.c
new file mode 100644
index 000000000000..fcf95243bf97
--- /dev/null
+++ b/tools/dtrace/dt_fbt.c
@@ -0,0 +1,201 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * The Function Boundary Tracing (FBT) provider for DTrace.
+ *
+ * FBT probes are exposed by the kernel as kprobes.  They are listed in the
+ * TRACEFS/available_filter_functions file.  Some kprobes are associated with
+ * a specific kernel module, while most are in the core kernel.
+ *
+ * Mapping from event name to DTrace probe name:
+ *
+ *      <name>					fbt:vmlinux:<name>:entry
+ *						fbt:vmlinux:<name>:return
+ *   or
+ *      <name> [<modname>]			fbt:<modname>:<name>:entry
+ *						fbt:<modname>:<name>:return
+ *
+ * Mapping from BPF section name to DTrace probe name:
+ *
+ *      kprobe/<name>				fbt:vmlinux:<name>:entry
+ *      kretprobe/<name>			fbt:vmlinux:<name>:return
+ *
+ * (Note that the BPF section does not carry information about the module that
+ *  the function is found in.  This means that BPF section name cannot be used
+ *  to distinguish between functions with the same name occurring in different
+ *  modules.)
+ *
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ */
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <linux/bpf.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "dtrace_impl.h"
+
+#define KPROBE_EVENTS	TRACEFS "kprobe_events"
+#define PROBE_LIST	TRACEFS "available_filter_functions"
+
+static const char	provname[] = "fbt";
+static const char	modname[] = "vmlinux";
+
+/*
+ * Scan the PROBE_LIST file and add entry and return probes for every function
+ * that is listed.
+ */
+static int fbt_populate(void)
+{
+	FILE			*f;
+	char			buf[256];
+	char			*p;
+
+	f = fopen(PROBE_LIST, "r");
+	if (f == NULL)
+		return -1;
+
+	while (fgets(buf, sizeof(buf), f)) {
+		/*
+		 * Here buf is either "funcname\n" or "funcname [modname]\n".
+		 */
+		p = strchr(buf, '\n');
+		if (p) {
+			*p = '\0';
+			if (p > buf && *(--p) == ']')
+				*p = '\0';
+		} else {
+			/* If we didn't see a newline, the line was too long.
+			 * Report it, and continue until the end of the line.
+			 */
+			fprintf(stderr, "%s: Line too long: %s\n",
+				PROBE_LIST, buf);
+			do
+				fgets(buf, sizeof(buf), f);
+			while (strchr(buf, '\n') == NULL);
+			continue;
+		}
+
+		/*
+		 * Now buf is either "funcname" or "funcname [modname".  If
+		 * there is no module name provided, we will use the default.
+		 */
+		p = strchr(buf, ' ');
+		if (p) {
+			*p++ = '\0';
+			if (*p == '[')
+				p++;
+		}
+
+		dt_probe_new(&dt_fbt, provname, p ? p : modname, buf, "entry");
+		dt_probe_new(&dt_fbt, provname, p ? p : modname, buf, "return");
+	}
+
+	fclose(f);
+
+	return 0;
+}
+
+#define ENTRY_PREFIX	"kprobe/"
+#define EXIT_PREFIX	"kretprobe/"
+
+/*
+ * Perform a probe lookup based on an event name (BPF ELF section name).
+ */
+static struct dt_probe *fbt_resolve_event(const char *name)
+{
+	const char	*prbname;
+	struct dt_probe	tmpl;
+	struct dt_probe	*probe;
+
+	if (!name)
+		return NULL;
+
+	if (strncmp(name, ENTRY_PREFIX, sizeof(ENTRY_PREFIX) - 1) == 0) {
+		name += sizeof(ENTRY_PREFIX) - 1;
+		prbname = "entry";
+	} else if (strncmp(name, EXIT_PREFIX, sizeof(EXIT_PREFIX) - 1) == 0) {
+		name += sizeof(EXIT_PREFIX) - 1;
+		prbname = "return";
+	} else
+		return NULL;
+
+	memset(&tmpl, 0, sizeof(tmpl));
+	tmpl.prv_name = provname;
+	tmpl.mod_name = modname;
+	tmpl.fun_name = name;
+	tmpl.prb_name = prbname;
+
+	probe = dt_probe_by_name(&tmpl);
+
+	return probe;
+}
+
+/*
+ * Attach the given BPF program (identified by its file descriptor) to the
+ * kprobe identified by the given section name.
+ */
+static int fbt_attach(const char *name, int bpf_fd)
+{
+	char    efn[256];
+	char    buf[256];
+	int	event_id, fd, rc;
+
+	name += 7;				/* skip "kprobe/" */
+	snprintf(buf, sizeof(buf), "p:%s %s\n", name, name);
+
+	/*
+	 * Register the kprobe with the tracing subsystem.  This will create
+	 * a tracepoint event.
+	 */
+	fd = open(KPROBE_EVENTS, O_WRONLY | O_APPEND);
+	if (fd < 0) {
+		perror(KPROBE_EVENTS);
+		return -1;
+	}
+	rc = write(fd, buf, strlen(buf));
+	if (rc < 0) {
+		perror(KPROBE_EVENTS);
+		close(fd);
+		return -1;
+	}
+	close(fd);
+
+	/*
+	 * Read the tracepoint event id for the kprobe we just registered.
+	 */
+	strcpy(efn, EVENTSFS);
+	strcat(efn, "kprobes/");
+	strcat(efn, name);
+	strcat(efn, "/id");
+
+	fd = open(efn, O_RDONLY);
+	if (fd < 0) {
+		perror(efn);
+		return -1;
+	}
+	rc = read(fd, buf, sizeof(buf));
+	if (rc < 0 || rc >= sizeof(buf)) {
+		perror(efn);
+		close(fd);
+		return -1;
+	}
+	close(fd);
+	buf[rc] = '\0';
+	event_id = atoi(buf);
+
+	/*
+	 * Attaching a BPF program (by file descriptor) to an event (by ID) is
+	 * a generic operation provided by the BPF interface code.
+	 */
+	return dt_bpf_attach(event_id, bpf_fd);
+}
+
+struct dt_provider	dt_fbt = {
+	.name		= "fbt",
+	.populate	= &fbt_populate,
+	.resolve_event	= &fbt_resolve_event,
+	.attach		= &fbt_attach,
+};
diff --git a/tools/dtrace/dt_hash.c b/tools/dtrace/dt_hash.c
new file mode 100644
index 000000000000..b1f563bc0773
--- /dev/null
+++ b/tools/dtrace/dt_hash.c
@@ -0,0 +1,211 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file provides a generic hashtable implementation for probes.
+ *
+ * The hashtable is created with 4 user-provided functions:
+ *	hval(probe)		- calculate a hash value for the given probe
+ *	cmp(probe1, probe2)	- compare two probes
+ *	add(head, probe)	- add a probe to a list of probes
+ *	del(head, probe)	- delete a probe from a list of probes
+ *
+ * Probes are hashed into a hashtable slot based on the return value of
+ * hval(probe).  Each hashtable slot holds a list of buckets, with each
+ * bucket storing probes that are equal under the cmp(probe1, probe2)
+ * function. Probes are added to the list of probes in a bucket using the
+ * add(head, probe) function, and they are deleted using a call to the
+ * del(head, probe) function.
+ *
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ */
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "dtrace_impl.h"
+
+/*
+ * Hashtable implementation for probes.
+ */
+struct dt_hbucket {
+	u32			hval;
+	struct dt_hbucket	*next;
+	struct dt_probe		*head;
+	int			nprobes;
+};
+
+struct dt_htab {
+	struct dt_hbucket	**tab;
+	int			size;
+	int			mask;
+	int			nbuckets;
+	dt_hval_fn		hval;		/* calculate hash value */
+	dt_cmp_fn		cmp;		/* compare 2 probes */
+	dt_add_fn		add;		/* add probe to list */
+	dt_del_fn		del;		/* delete probe from list */
+};
+
+/*
+ * Create a new (empty) hashtable.
+ */
+struct dt_htab *dt_htab_new(dt_hval_fn hval, dt_cmp_fn cmp, dt_add_fn add,
+			    dt_del_fn del)
+{
+	struct dt_htab	*htab = malloc(sizeof(struct dt_htab));
+
+	if (!htab)
+		return NULL;
+
+	htab->size = 1;
+	htab->mask = htab->size - 1;
+	htab->nbuckets = 0;
+	htab->hval = hval;
+	htab->cmp = cmp;
+	htab->add = add;
+	htab->del = del;
+
+	htab->tab = calloc(htab->size, sizeof(struct dt_hbucket *));
+	if (!htab->tab) {
+		free(htab);
+		return NULL;
+	}
+
+	return htab;
+}
+
+/*
+ * Resize the hashtable by doubling the number of slots.
+ */
+static int resize(struct dt_htab *htab)
+{
+	int			i;
+	int			osize = htab->size;
+	int			nsize = osize << 1;
+	int			nmask = nsize - 1;
+	struct dt_hbucket	**ntab;
+
+	ntab = calloc(nsize, sizeof(struct dt_hbucket *));
+	if (!ntab)
+		return -ENOMEM;
+
+	for (i = 0; i < osize; i++) {
+		struct dt_hbucket	*bucket, *next;
+
+		for (bucket = htab->tab[i]; bucket; bucket = next) {
+			int	idx	= bucket->hval & nmask;
+
+			next = bucket->next;
+			bucket->next = ntab[idx];
+			ntab[idx] = bucket;
+		}
+	}
+
+	free(htab->tab);
+	htab->tab = ntab;
+	htab->size = nsize;
+	htab->mask = nmask;
+
+	return 0;
+}
+
+/*
+ * Add a probe to the hashtable.  Resize if necessary, and allocate a new
+ * bucket if necessary.
+ */
+int dt_htab_add(struct dt_htab *htab, struct dt_probe *probe)
+{
+	u32			hval = htab->hval(probe);
+	int			idx;
+	struct dt_hbucket	*bucket;
+
+retry:
+	idx = hval & htab->mask;
+	for (bucket = htab->tab[idx]; bucket; bucket = bucket->next) {
+		if (htab->cmp(bucket->head, probe) == 0)
+			goto add;
+	}
+
+	if ((htab->nbuckets >> 1) > htab->size) {
+		int	err;
+
+		err = resize(htab);
+		if (err)
+			return err;
+
+		goto retry;
+	}
+
+	bucket = malloc(sizeof(struct dt_hbucket));
+	if (!bucket)
+		return -ENOMEM;
+
+	bucket->hval = hval;
+	bucket->next = htab->tab[idx];
+	bucket->head = NULL;
+	bucket->nprobes = 0;
+	htab->tab[idx] = bucket;
+	htab->nbuckets++;
+
+add:
+	bucket->head = htab->add(bucket->head, probe);
+	bucket->nprobes++;
+
+	return 0;
+}
+
+/*
+ * Find a probe in the hashtable.
+ */
+struct dt_probe *dt_htab_lookup(const struct dt_htab *htab,
+				const struct dt_probe *probe)
+{
+	u32			hval = htab->hval(probe);
+	int			idx = hval & htab->mask;
+	struct dt_hbucket	*bucket;
+
+	for (bucket = htab->tab[idx]; bucket; bucket = bucket->next) {
+		if (htab->cmp(bucket->head, probe) == 0)
+			return bucket->head;
+	}
+
+	return NULL;
+}
+
+/*
+ * Remove a probe from the hashtable.  If we are deleting the last probe in a
+ * bucket, get rid of the bucket.
+ */
+int dt_htab_del(struct dt_htab *htab, struct dt_probe *probe)
+{
+	u32			hval = htab->hval(probe);
+	int			idx = hval & htab->mask;
+	struct dt_hbucket	*bucket;
+	struct dt_probe		*head;
+
+	for (bucket = htab->tab[idx]; bucket; bucket = bucket->next) {
+		if (htab->cmp(bucket->head, probe) == 0)
+			break;
+	}
+
+	if (bucket == NULL)
+		return -ENOENT;
+
+	head = htab->del(bucket->head, probe);
+	if (!head) {
+		struct dt_hbucket	*b = htab->tab[idx];
+
+		if (bucket == b)
+			htab->tab[idx] = bucket->next;
+		else {
+			while (b->next != bucket)
+				b = b->next;
+
+			b->next = bucket->next;
+		}
+
+		htab->nbuckets--;
+		free(bucket);
+	} else
+		bucket->head = head;
+
+	return 0;
+}
diff --git a/tools/dtrace/dt_probe.c b/tools/dtrace/dt_probe.c
new file mode 100644
index 000000000000..0b6228eaff29
--- /dev/null
+++ b/tools/dtrace/dt_probe.c
@@ -0,0 +1,230 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file implements the interface to probes grouped by provider.
+ *
+ * Probes are named by a set of 4 identifiers:
+ *	- provider name
+ *	- module name
+ *	- function name
+ *	- probe name
+ *
+ * The Fully Qualified Name (FQN) is "provider:module:function:name".
+ *
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <linux/bpf.h>
+#include <linux/kernel.h>
+
+#include "dtrace_impl.h"
+
+static struct dt_provider      *dt_providers[] = {
+							&dt_fbt,
+							&dt_syscall,
+						 };
+
+static struct dt_htab	*ht_byfqn;
+
+static u32		next_probe_id;
+
+/*
+ * Calculate a hash value based on a given string and an initial value.  The
+ * initial value is used to calculate compound hash values, e.g.
+ *
+ *	u32	hval;
+ *
+ *	hval = str2hval(str1, 0);
+ *	hval = str2hval(str2, hval);
+ */
+static u32 str2hval(const char *p, u32 hval)
+{
+	u32	g;
+
+	if (!p)
+		return hval;
+
+	while (*p) {
+		hval = (hval << 4) + *p++;
+		g = hval & 0xf0000000;
+		if (g != 0)
+			hval ^= g >> 24;
+
+		hval &= ~g;
+	}
+
+	return hval;
+}
+
+/*
+ * String compare function that can handle either or both strings being NULL.
+ */
+static int safe_strcmp(const char *p, const char *q)
+{
+	return (!p) ? (!q) ? 0
+			   : -1
+		    : (!q) ? 1
+			   : strcmp(p, q);
+}
+
+/*
+ * Calculate the hash value of a probe as the cummulative hash value of the
+ * FQN.
+ */
+static u32 fqn_hval(const struct dt_probe *probe)
+{
+	u32	hval = 0;
+
+	hval = str2hval(probe->prv_name, hval);
+	hval = str2hval(":", hval);
+	hval = str2hval(probe->mod_name, hval);
+	hval = str2hval(":", hval);
+	hval = str2hval(probe->fun_name, hval);
+	hval = str2hval(":", hval);
+	hval = str2hval(probe->prb_name, hval);
+
+	return hval;
+}
+
+/*
+ * Compare two probes based on the FQN.
+ */
+static int fqn_cmp(const struct dt_probe *p, const struct dt_probe *q)
+{
+	int	rc;
+
+	rc = safe_strcmp(p->prv_name, q->prv_name);
+	if (rc)
+		return rc;
+	rc = safe_strcmp(p->mod_name, q->mod_name);
+	if (rc)
+		return rc;
+	rc = safe_strcmp(p->fun_name, q->fun_name);
+	if (rc)
+		return rc;
+	rc = safe_strcmp(p->prb_name, q->prb_name);
+	if (rc)
+		return rc;
+
+	return 0;
+}
+
+/*
+ * Add the given probe 'new' to the double-linked probe list 'head'.  Probe
+ * 'new' becomes the new list head.
+ */
+static struct dt_probe *fqn_add(struct dt_probe *head, struct dt_probe *new)
+{
+	if (!head)
+		return new;
+
+	new->he_fqn.next = head;
+	head->he_fqn.prev = new;
+
+	return new;
+}
+
+/*
+ * Remove the given probe 'probe' from the double-linked probe list 'head'.
+ * If we are deleting the current head, the next probe in the list is returned
+ * as the new head.  If that value is NULL, the list is now empty.
+ */
+static struct dt_probe *fqn_del(struct dt_probe *head, struct dt_probe *probe)
+{
+	if (head == probe) {
+		if (!probe->he_fqn.next)
+			return NULL;
+
+		head = probe->he_fqn.next;
+		head->he_fqn.prev = NULL;
+		probe->he_fqn.next = NULL;
+
+		return head;
+	}
+
+	if (!probe->he_fqn.next) {
+		probe->he_fqn.prev->he_fqn.next = NULL;
+		probe->he_fqn.prev = NULL;
+
+		return head;
+	}
+
+	probe->he_fqn.prev->he_fqn.next = probe->he_fqn.next;
+	probe->he_fqn.next->he_fqn.prev = probe->he_fqn.prev;
+	probe->he_fqn.prev = probe->he_fqn.next = NULL;
+
+	return head;
+}
+
+/*
+ * Initialize the probe handling by populating the FQN hashtable with probes
+ * from all providers.
+ */
+int dt_probe_init(void)
+{
+	int	i;
+
+	ht_byfqn = dt_htab_new(fqn_hval, fqn_cmp, fqn_add, fqn_del);
+
+	for (i = 0; i < ARRAY_SIZE(dt_providers); i++) {
+		if (dt_providers[i]->populate() < 0)
+			return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * Allocate a new probe and add it to the FQN hashtable.
+ */
+int dt_probe_new(const struct dt_provider *prov, const char *pname,
+		 const char *mname, const char *fname, const char *name)
+{
+	struct dt_probe	*probe;
+
+	probe = malloc(sizeof(struct dt_probe));
+	if (!probe)
+		return -ENOMEM;
+
+	memset(probe, 0, sizeof(struct dt_probe));
+	probe->id = next_probe_id++;
+	probe->prov = prov;
+	probe->prv_name = pname ? strdup(pname) : NULL;
+	probe->mod_name = mname ? strdup(mname) : NULL;
+	probe->fun_name = fname ? strdup(fname) : NULL;
+	probe->prb_name = name ? strdup(name) : NULL;
+
+	dt_htab_add(ht_byfqn, probe);
+
+	return 0;
+}
+
+/*
+ * Perform a probe lookup based on FQN.
+ */
+struct dt_probe *dt_probe_by_name(const struct dt_probe *tmpl)
+{
+	return dt_htab_lookup(ht_byfqn, tmpl);
+}
+
+/*
+ * Resolve an event name (BPF ELF section name) into a probe.  We query each
+ * provider, and as soon as we get a hit, we return the result.
+ */
+struct dt_probe *dt_probe_resolve_event(const char *name)
+{
+	int		i;
+	struct dt_probe	*probe;
+
+	for (i = 0; i < ARRAY_SIZE(dt_providers); i++) {
+		if (!dt_providers[i]->resolve_event)
+			continue;
+		probe = dt_providers[i]->resolve_event(name);
+		if (probe)
+			return probe;
+	}
+
+	return NULL;
+}
diff --git a/tools/dtrace/dt_syscall.c b/tools/dtrace/dt_syscall.c
new file mode 100644
index 000000000000..6695a4a1c701
--- /dev/null
+++ b/tools/dtrace/dt_syscall.c
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * The syscall provider for DTrace.
+ *
+ * System call probes are exposed by the kernel as tracepoint events in the
+ * "syscalls" group.  Entry probe names start with "sys_enter_" and exit probes
+ * start with "sys_exit_".
+ *
+ * Mapping from event name to DTrace probe name:
+ *
+ *	syscalls:sys_enter_<name>		syscall:vmlinux:<name>:entry
+ *	syscalls:sys_exit_<name>		syscall:vmlinux:<name>:return
+ *
+ * Mapping from BPF section name to DTrace probe name:
+ *
+ *	tracepoint/syscalls/sys_enter_<name>	syscall:vmlinux:<name>:entry
+ *	tracepoint/syscalls/sys_exit_<name>	syscall:vmlinux:<name>:return
+ *
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ */
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <linux/bpf.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "dtrace_impl.h"
+
+static const char	provname[] = "syscall";
+static const char	modname[] = "vmlinux";
+
+#define PROBE_LIST	TRACEFS "available_events"
+
+#define PROV_PREFIX	"syscalls:"
+#define ENTRY_PREFIX	"sys_enter_"
+#define EXIT_PREFIX	"sys_exit_"
+
+/*
+ * Scan the PROBE_LIST file and add probes for any syscalls events.
+ */
+static int syscall_populate(void)
+{
+	FILE			*f;
+	char			buf[256];
+
+	f = fopen(PROBE_LIST, "r");
+	if (f == NULL)
+		return -1;
+
+	while (fgets(buf, sizeof(buf), f)) {
+		char	*p;
+
+		/* * Here buf is "group:event".  */
+		p = strchr(buf, '\n');
+		if (p)
+			*p = '\0';
+		else {
+			/*
+			 * If we didn't see a newline, the line was too long.
+			 * Report it, and continue until the end of the line.
+			 */
+			fprintf(stderr, "%s: Line too long: %s\n",
+				PROBE_LIST, buf);
+			do
+				fgets(buf, sizeof(buf), f);
+			while (strchr(buf, '\n') == NULL);
+			continue;
+		}
+
+		/* We need "group:" to match "syscalls:". */
+		p = buf;
+		if (memcmp(p, PROV_PREFIX, sizeof(PROV_PREFIX) - 1) != 0)
+			continue;
+
+		p += sizeof(PROV_PREFIX) - 1;
+		/*
+		 * Now p will be just "event", and we are only interested in
+		 * events that match "sys_enter_*" or "sys_exit_*".
+		 */
+		if (!memcmp(p, ENTRY_PREFIX, sizeof(ENTRY_PREFIX) - 1)) {
+			p += sizeof(ENTRY_PREFIX) - 1;
+			dt_probe_new(&dt_syscall, provname, modname, p,
+				     "entry");
+		} else if (!memcmp(p, EXIT_PREFIX, sizeof(EXIT_PREFIX) - 1)) {
+			p += sizeof(EXIT_PREFIX) - 1;
+			dt_probe_new(&dt_syscall, provname, modname, p,
+				     "return");
+		}
+	}
+
+	fclose(f);
+
+	return 0;
+}
+
+#define EVENT_PREFIX	"tracepoint/syscalls/"
+
+/*
+ * Perform a probe lookup based on an event name (BPF ELF section name).
+ */
+static struct dt_probe *systrace_resolve_event(const char *name)
+{
+	const char	*prbname;
+	struct dt_probe	tmpl;
+	struct dt_probe	*probe;
+
+	if (!name)
+		return NULL;
+
+	/* Exclude anything that is not a syscalls tracepoint */
+	if (strncmp(name, EVENT_PREFIX, sizeof(EVENT_PREFIX) - 1) != 0)
+		return NULL;
+	name += sizeof(EVENT_PREFIX) - 1;
+
+	if (strncmp(name, ENTRY_PREFIX, sizeof(ENTRY_PREFIX) - 1) == 0) {
+		name += sizeof(ENTRY_PREFIX) - 1;
+		prbname = "entry";
+	} else if (strncmp(name, EXIT_PREFIX, sizeof(EXIT_PREFIX) - 1) == 0) {
+		name += sizeof(EXIT_PREFIX) - 1;
+		prbname = "return";
+	} else
+		return NULL;
+
+	memset(&tmpl, 0, sizeof(tmpl));
+	tmpl.prv_name = provname;
+	tmpl.mod_name = modname;
+	tmpl.fun_name = name;
+	tmpl.prb_name = prbname;
+
+	probe = dt_probe_by_name(&tmpl);
+
+	return probe;
+}
+
+#define SYSCALLSFS	EVENTSFS "syscalls/"
+
+/*
+ * Attach the given BPF program (identified by its file descriptor) to the
+ * event identified by the given section name.
+ */
+static int syscall_attach(const char *name, int bpf_fd)
+{
+	char    efn[256];
+	char    buf[256];
+	int	event_id, fd, rc;
+
+	name += sizeof(EVENT_PREFIX) - 1;
+	strcpy(efn, SYSCALLSFS);
+	strcat(efn, name);
+	strcat(efn, "/id");
+
+	fd = open(efn, O_RDONLY);
+	if (fd < 0) {
+		perror(efn);
+		return -1;
+	}
+	rc = read(fd, buf, sizeof(buf));
+	if (rc < 0 || rc >= sizeof(buf)) {
+		perror(efn);
+		close(fd);
+		return -1;
+	}
+	close(fd);
+	buf[rc] = '\0';
+	event_id = atoi(buf);
+
+	return dt_bpf_attach(event_id, bpf_fd);
+}
+
+struct dt_provider	dt_syscall = {
+	.name		= "syscall",
+	.populate	= &syscall_populate,
+	.resolve_event	= &systrace_resolve_event,
+	.attach		= &syscall_attach,
+};
diff --git a/tools/dtrace/dt_utils.c b/tools/dtrace/dt_utils.c
new file mode 100644
index 000000000000..55d51bae1d97
--- /dev/null
+++ b/tools/dtrace/dt_utils.c
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "dtrace_impl.h"
+
+#define BUF_SIZE	1024		/* max size for online cpu data */
+
+int	dt_numcpus;			/* number of online CPUs */
+int	dt_maxcpuid;			/* highest CPU id */
+int	*dt_cpuids;			/* list of CPU ids */
+
+/*
+ * Populate the online CPU id information from sysfs data.  We only do this
+ * once because we do not care about CPUs coming online after we started
+ * tracing.  If a CPU goes offline during tracing, we do not care either
+ * because that simply means that it won't be writing any new probe data into
+ * its buffer.
+ */
+void cpu_list_populate(void)
+{
+	char buf[BUF_SIZE];
+	int fd, cnt, start, end, i;
+	int *cpu;
+	char *p, *q;
+
+	fd = open("/sys/devices/system/cpu/online", O_RDONLY);
+	if (fd < 0)
+		goto fail;
+	cnt = read(fd, buf, sizeof(buf));
+	close(fd);
+	if (cnt <= 0)
+		goto fail;
+
+	/*
+	 * The string should always end with a newline, but let's make sure.
+	 */
+	if (buf[cnt - 1] == '\n')
+		buf[--cnt] = 0;
+
+	/*
+	 * Count how many CPUs we have.
+	 */
+	dt_numcpus = 0;
+	p = buf;
+	do {
+		start = (int)strtol(p, &q, 10);
+		switch (*q) {
+		case '-':		/* range */
+			p = q + 1;
+			end = (int)strtol(p, &q, 10);
+			dt_numcpus += end - start + 1;
+			if (*q == 0) {	/* end of string */
+				p = q;
+				break;
+			}
+			if (*q != ',')
+				goto fail;
+			p = q + 1;
+			break;
+		case 0:			/* end of string */
+			dt_numcpus++;
+			p = q;
+			break;
+		case ',':	/* gap  */
+			dt_numcpus++;
+			p = q + 1;
+			break;
+		}
+	} while (*p != 0);
+
+	dt_cpuids = calloc(dt_numcpus,  sizeof(int));
+	cpu = dt_cpuids;
+
+	/*
+	 * Fill in the CPU ids.
+	 */
+	p = buf;
+	do {
+		start = (int)strtol(p, &q, 10);
+		switch (*q) {
+		case '-':		/* range */
+			p = q + 1;
+			end = (int)strtol(p, &q, 10);
+			for (i = start; i <= end; i++)
+				*cpu++ = i;
+			if (*q == 0) {	/* end of string */
+				p = q;
+				break;
+			}
+			if (*q != ',')
+				goto fail;
+			p = q + 1;
+			break;
+		case 0:			/* end of string */
+			*cpu = start;
+			p = q;
+			break;
+		case ',':	/* gap  */
+			*cpu++ = start;
+			p = q + 1;
+			break;
+		}
+	} while (*p != 0);
+
+	/* Record the highest CPU id of the set of online CPUs. */
+	dt_maxcpuid = *(cpu - 1);
+
+	return;
+fail:
+	if (dt_cpuids)
+		free(dt_cpuids);
+
+	dt_numcpus = 0;
+	dt_maxcpuid = 0;
+	dt_cpuids = NULL;
+}
+
+void cpu_list_free(void)
+{
+	free(dt_cpuids);
+	dt_numcpus = 0;
+	dt_maxcpuid = 0;
+	dt_cpuids = NULL;
+}
diff --git a/tools/dtrace/dtrace.c b/tools/dtrace/dtrace.c
new file mode 100644
index 000000000000..36ad526c1cd4
--- /dev/null
+++ b/tools/dtrace/dtrace.c
@@ -0,0 +1,249 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ */
+#include <errno.h>
+#include <libgen.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <linux/log2.h>
+
+#include "dtrace_impl.h"
+
+#define DTRACE_BUFSIZE	32		/* default buffer size (in pages) */
+
+#define DMODE_VERS	0		/* display version information (-V) */
+#define DMODE_LIST	1		/* list probes (-l) */
+#define DMODE_EXEC	2		/* compile program and start tracing */
+
+#define E_SUCCESS	0
+#define E_ERROR		1
+#define E_USAGE		2
+
+#define NUM_PAGES(sz)	(((sz) + getpagesize() - 1) / getpagesize())
+
+static const char		*dtrace_options = "+b:ls:V";
+
+static char			*g_pname;
+static int			g_mode = DMODE_EXEC;
+
+static int usage(void)
+{
+	fprintf(stderr, "Usage: %s [-lV] [-b bufsz] -s script\n", g_pname);
+	fprintf(stderr,
+	"\t-b  set trace buffer size\n"
+	"\t-l  list probes matching specified criteria\n"
+	"\t-s  enable or list probes for the specified BPF program\n"
+	"\t-V  report DTrace API version\n");
+
+	return E_USAGE;
+}
+
+static u64 parse_size(const char *arg)
+{
+	long long	mul = 1;
+	long long	neg, val;
+	size_t		len;
+	char		*end;
+
+	if (!arg)
+		return -1;
+
+	len = strlen(arg);
+	if (!len)
+		return -1;
+
+	switch (arg[len - 1]) {
+	case 't':
+	case 'T':
+		mul *= 1024;
+		/* fall-through */
+	case 'g':
+	case 'G':
+		mul *= 1024;
+		/* fall-through */
+	case 'm':
+	case 'M':
+		mul *= 1024;
+		/* fall-through */
+	case 'k':
+	case 'K':
+		mul *= 1024;
+		/* fall-through */
+	default:
+		break;
+	}
+
+	neg = strtoll(arg, NULL, 0);
+	errno = 0;
+	val = strtoull(arg, &end, 0) * mul;
+
+	if ((mul > 1 && end != &arg[len - 1]) || (mul == 1 && *end != '\0') ||
+	    val < 0 || neg < 0 || errno != 0)
+		return -1;
+
+	return val;
+}
+
+int main(int argc, char *argv[])
+{
+	int	i;
+	int	modec = 0;
+	int	bufsize = DTRACE_BUFSIZE;
+	int	epoll_fd;
+	int	cnt;
+	char	**prgv;
+	int	prgc;
+
+	g_pname = basename(argv[0]);
+
+	if (argc == 1)
+		return usage();
+
+	prgc = 0;
+	prgv = calloc(argc, sizeof(char *));
+	if (!prgv) {
+		fprintf(stderr, "failed to allocate memory for arguments: %s\n",
+			strerror(errno));
+		return E_ERROR;
+	}
+
+	argv[0] = g_pname;			/* argv[0] for getopt errors */
+
+	for (optind = 1; optind < argc; optind++) {
+		int	opt;
+
+		while ((opt = getopt(argc, argv, dtrace_options)) != EOF) {
+			u64			val;
+
+			switch (opt) {
+			case 'b':
+				val = parse_size(optarg);
+				if (val < 0) {
+					fprintf(stderr, "invalid: -b %s\n",
+						optarg);
+					return E_ERROR;
+				}
+
+				/*
+				 * Bufsize needs to be a number of pages, and
+				 * must be a power of 2.  This is required by
+				 * the perf event buffer code.
+				 */
+				bufsize = roundup_pow_of_two(NUM_PAGES(val));
+				if ((u64)bufsize * getpagesize() > val)
+					fprintf(stderr,
+						"bufsize increased to %ld\n",
+						(u64)bufsize * getpagesize());
+
+				break;
+			case 'l':
+				g_mode = DMODE_LIST;
+				modec++;
+				break;
+			case 's':
+				prgv[prgc++] = optarg;
+				break;
+			case 'V':
+				g_mode = DMODE_VERS;
+				modec++;
+				break;
+			default:
+				if (strchr(dtrace_options, opt) == NULL)
+					return usage();
+			}
+		}
+
+		if (optind < argc) {
+			fprintf(stderr, "unknown option '%s'\n", argv[optind]);
+			return E_ERROR;
+		}
+	}
+
+	if (modec > 1) {
+		fprintf(stderr,
+			"only one of [-lV] can be specified at a time\n");
+		return E_USAGE;
+	}
+
+	/*
+	 * We handle requests for version information first because we do not
+	 * need probe information for it.
+	 */
+	if (g_mode == DMODE_VERS) {
+		printf("%s\n"
+		       "This is DTrace %s\n"
+		       "dtrace(1) version-control ID: %s\n",
+		       DT_VERS_STRING, DT_VERSION, DT_GIT_VERSION);
+
+		return E_SUCCESS;
+	}
+
+	/* Initialize probes. */
+	if (dt_probe_init() < 0) {
+		fprintf(stderr, "failed to initialize probes: %s\n",
+			strerror(errno));
+		return E_ERROR;
+	}
+
+	/*
+	 * We handle requests to list probes next.
+	 */
+	if (g_mode == DMODE_LIST) {
+		int	rc = 0;
+
+		printf("%5s %10s %17s %33s %s\n",
+		       "ID", "PROVIDER", "MODULE", "FUNCTION", "NAME");
+		for (i = 0; i < prgc; i++) {
+			rc = dt_bpf_list_probes(prgv[i]);
+			if (rc < 0)
+				fprintf(stderr, "failed to load %s: %s\n",
+					prgv[i], strerror(errno));
+		}
+
+		return rc ? E_ERROR : E_SUCCESS;
+	}
+
+	if (!prgc) {
+		fprintf(stderr, "missing BPF program(s)\n");
+		return E_ERROR;
+	}
+
+	/* Process the BPF program. */
+	for (i = 0; i < prgc; i++) {
+		int	err;
+
+		err = dt_bpf_load_file(prgv[i]);
+		if (err) {
+			errno = -err;
+			fprintf(stderr, "failed to load %s: %s\n",
+				prgv[i], strerror(errno));
+			return E_ERROR;
+		}
+	}
+
+	/* Get the list of online CPUs. */
+	cpu_list_populate();
+
+	/* Initialize buffers. */
+	epoll_fd = dt_buffer_init(bufsize);
+	if (epoll_fd < 0) {
+		errno = -epoll_fd;
+		fprintf(stderr, "failed to allocate buffers: %s\n",
+			strerror(errno));
+		return E_ERROR;
+	}
+
+	/* Process probe data. */
+	printf("%3s %6s\n", "CPU", "ID");
+	do {
+		cnt = dt_buffer_poll(epoll_fd, 100);
+	} while (cnt >= 0);
+
+	dt_buffer_exit(epoll_fd);
+
+	return E_SUCCESS;
+}
diff --git a/tools/dtrace/dtrace.h b/tools/dtrace/dtrace.h
new file mode 100644
index 000000000000..c79398432d17
--- /dev/null
+++ b/tools/dtrace/dtrace.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ */
+#ifndef _UAPI_LINUX_DTRACE_H
+#define _UAPI_LINUX_DTRACE_H
+
+struct dt_bpf_context {
+	u32		probe_id;
+	u64		argv[10];
+};
+
+#endif /* _UAPI_LINUX_DTRACE_H */
diff --git a/tools/dtrace/dtrace_impl.h b/tools/dtrace/dtrace_impl.h
new file mode 100644
index 000000000000..9aa51b4c4aee
--- /dev/null
+++ b/tools/dtrace/dtrace_impl.h
@@ -0,0 +1,101 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ */
+#ifndef _DTRACE_H
+#define _DTRACE_H
+
+#include <unistd.h>
+#include <bpf/libbpf.h>
+#include <linux/types.h>
+#include <linux/ptrace.h>
+#include <linux/perf_event.h>
+#include <sys/syscall.h>
+
+#include "dtrace.h"
+
+#define DT_DEBUG
+
+#define DT_VERS_STRING	"Oracle D 2.0.0"
+
+#define TRACEFS		"/sys/kernel/debug/tracing/"
+#define EVENTSFS	TRACEFS "events/"
+
+extern int	dt_numcpus;
+extern int	dt_maxcpuid;
+extern int	*dt_cpuids;
+
+extern void cpu_list_populate(void);
+extern void cpu_list_free(void);
+
+struct dt_provider {
+	char		*name;
+	int		(*populate)(void);
+	struct dt_probe *(*resolve_event)(const char *name);
+	int		(*attach)(const char *name, int bpf_fd);
+};
+
+extern struct dt_provider	dt_fbt;
+extern struct dt_provider	dt_syscall;
+
+struct dt_hentry {
+	struct dt_probe		*next;
+	struct dt_probe		*prev;
+};
+
+struct dt_htab;
+
+typedef u32 (*dt_hval_fn)(const struct dt_probe *);
+typedef int (*dt_cmp_fn)(const struct dt_probe *, const struct dt_probe *);
+typedef struct dt_probe *(*dt_add_fn)(struct dt_probe *, struct dt_probe *);
+typedef struct dt_probe *(*dt_del_fn)(struct dt_probe *, struct dt_probe *);
+
+extern struct dt_htab *dt_htab_new(dt_hval_fn hval, dt_cmp_fn cmp,
+				   dt_add_fn add, dt_del_fn del);
+extern int dt_htab_add(struct dt_htab *htab, struct dt_probe *probe);
+extern struct dt_probe *dt_htab_lookup(const struct dt_htab *htab,
+				       const struct dt_probe *probe);
+extern int dt_htab_del(struct dt_htab *htab, struct dt_probe *probe);
+
+struct dt_probe {
+	u32				id;
+	int				event_fd;
+	const struct dt_provider	*prov;
+	const char			*prv_name;	/* provider name */
+	const char			*mod_name;	/* module name */
+	const char			*fun_name;	/* function name */
+	const char			*prb_name;	/* probe name */
+	struct dt_hentry		he_fqn;
+};
+
+typedef void (*dt_probe_fn)(const struct dt_probe *probe);
+
+extern int dt_probe_init(void);
+extern int dt_probe_new(const struct dt_provider *prov, const char *pname,
+			const char *mname, const char *fname, const char *name);
+extern struct dt_probe *dt_probe_by_name(const struct dt_probe *tmpl);
+extern struct dt_probe *dt_probe_resolve_event(const char *name);
+
+extern int dt_bpf_list_probes(const char *fn);
+extern int dt_bpf_load_file(const char *fn);
+extern int dt_bpf_map_update(int fd, const void *key, const void *val);
+extern int dt_bpf_attach(int event_id, int bpf_fd);
+
+extern int dt_bufmap_fd;
+
+extern int dt_buffer_init(int num_pages);
+extern int dt_buffer_poll(int epoll_fd, int timeout);
+extern void dt_buffer_exit(int epoll_fd);
+
+static inline int perf_event_open(struct perf_event_attr *attr, pid_t pid,
+				  int cpu, int group_fd, unsigned long flags)
+{
+	return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags);
+}
+
+extern inline int bpf(enum bpf_cmd cmd, union bpf_attr *attr)
+{
+	return syscall(__NR_bpf, cmd, attr, sizeof(union bpf_attr));
+}
+
+#endif /* _DTRACE_H */
-- 
2.20.1


^ permalink raw reply related

* Re: [PATCH v1 net-next] net: stmmac: enable clause 45 mdio support
From: Andrew Lunn @ 2019-07-04  3:30 UTC (permalink / raw)
  To: Voon, Weifeng
  Cc: David S. Miller, Maxime Coquelin, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, Jose Abreu, Giuseppe Cavallaro,
	Florian Fainelli, Alexandre Torgue, biao huang, Ong, Boon Leong,
	Kweh, Hock Leong
In-Reply-To: <D6759987A7968C4889FDA6FA91D5CBC8147384B6@PGSMSX103.gar.corp.intel.com>

On Thu, Jul 04, 2019 at 01:33:16AM +0000, Voon, Weifeng wrote:
> > > @@ -155,22 +171,26 @@ static int stmmac_mdio_read(struct mii_bus *bus,
> > int phyaddr, int phyreg)
> > >  	struct stmmac_priv *priv = netdev_priv(ndev);
> > >  	unsigned int mii_address = priv->hw->mii.addr;
> > >  	unsigned int mii_data = priv->hw->mii.data;
> > > -	u32 v;
> > > -	int data;
> > >  	u32 value = MII_BUSY;
> > > +	int data = 0;
> > > +	u32 v;
> > >
> > >  	value |= (phyaddr << priv->hw->mii.addr_shift)
> > >  		& priv->hw->mii.addr_mask;
> > >  	value |= (phyreg << priv->hw->mii.reg_shift) & priv->hw-
> > >mii.reg_mask;
> > >  	value |= (priv->clk_csr << priv->hw->mii.clk_csr_shift)
> > >  		& priv->hw->mii.clk_csr_mask;
> > > -	if (priv->plat->has_gmac4)
> > > +	if (priv->plat->has_gmac4) {
> > >  		value |= MII_GMAC4_READ;
> > > +		if (phyreg & MII_ADDR_C45)
> > > +			stmmac_mdio_c45_setup(priv, phyreg, &value, &data);
> > > +	}
> > >
> > >  	if (readl_poll_timeout(priv->ioaddr + mii_address, v, !(v &
> > MII_BUSY),
> > >  			       100, 10000))
> > >  		return -EBUSY;
> > >
> > > +	writel(data, priv->ioaddr + mii_data);
> > 
> > That looks odd. Could you explain why it is needed.
> > 
> > Thanks
> > 	Andrew
> 
> Hi Andrew,
> This mdio c45 support needed to access DWC xPCS which is a Clause-45

I mean it looks odd doing a write to the data register in the middle
of stmmac_mdio_read().

   Andrew

^ permalink raw reply

* [PATCH v3 net-next] net: socionext: remove set but not used variable 'pkts'
From: YueHaibing @ 2019-07-04  3:37 UTC (permalink / raw)
  To: Jassi Brar, Alexei Starovoitov, David S . Miller,
	Ilias Apalodimas, Daniel Borkmann, Jakub Kicinski,
	Jesper Dangaard Brouer
  Cc: YueHaibing, netdev, xdp-newbies, bpf, kernel-janitors
In-Reply-To: <20190704032129.169282-1-yuehaibing@huawei.com>

Fixes gcc '-Wunused-but-set-variable' warning:

drivers/net/ethernet/socionext/netsec.c: In function 'netsec_clean_tx_dring':
drivers/net/ethernet/socionext/netsec.c:637:15: warning:
 variable 'pkts' set but not used [-Wunused-but-set-variable]

It is not used since commit ba2b232108d3 ("net: netsec: add XDP support")

Signed-off-by: YueHaibing <yuehaibing@huawei.com>
Acked-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
---
v3: remove misplaced cc in patch log
v2: keep reverse christmas-tree ordering of the local variables
---
 drivers/net/ethernet/socionext/netsec.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/socionext/netsec.c b/drivers/net/ethernet/socionext/netsec.c
index 5544a722543f..d8d640b01119 100644
--- a/drivers/net/ethernet/socionext/netsec.c
+++ b/drivers/net/ethernet/socionext/netsec.c
@@ -634,15 +634,14 @@ static void netsec_set_rx_de(struct netsec_priv *priv,
 static bool netsec_clean_tx_dring(struct netsec_priv *priv)
 {
 	struct netsec_desc_ring *dring = &priv->desc_ring[NETSEC_RING_TX];
-	unsigned int pkts, bytes;
 	struct netsec_de *entry;
 	int tail = dring->tail;
+	unsigned int bytes;
 	int cnt = 0;
 
 	if (dring->is_xdp)
 		spin_lock(&dring->lock);
 
-	pkts = 0;
 	bytes = 0;
 	entry = dring->vaddr + DESC_SZ * tail;




^ permalink raw reply related

* Re: [PATCH v2 1/3] vsock/virtio: use RCU to avoid use-after-free on the_virtio_vsock
From: Jason Wang @ 2019-07-04  3:58 UTC (permalink / raw)
  To: Stefano Garzarella, Stefan Hajnoczi
  Cc: netdev, kvm, virtualization, Michael S. Tsirkin, David S. Miller,
	linux-kernel
In-Reply-To: <20190703104135.wg34dobv64k7u4jo@steredhat>


On 2019/7/3 下午6:41, Stefano Garzarella wrote:
> On Wed, Jul 03, 2019 at 05:53:58PM +0800, Jason Wang wrote:
>> On 2019/6/28 下午8:36, Stefano Garzarella wrote:
>>> Some callbacks used by the upper layers can run while we are in the
>>> .remove(). A potential use-after-free can happen, because we free
>>> the_virtio_vsock without knowing if the callbacks are over or not.
>>>
>>> To solve this issue we move the assignment of the_virtio_vsock at the
>>> end of .probe(), when we finished all the initialization, and at the
>>> beginning of .remove(), before to release resources.
>>> For the same reason, we do the same also for the vdev->priv.
>>>
>>> We use RCU to be sure that all callbacks that use the_virtio_vsock
>>> ended before freeing it. This is not required for callbacks that
>>> use vdev->priv, because after the vdev->config->del_vqs() we are sure
>>> that they are ended and will no longer be invoked.
>>>
>>> We also take the mutex during the .remove() to avoid that .probe() can
>>> run while we are resetting the device.
>>>
>>> Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
>>> ---
>>>    net/vmw_vsock/virtio_transport.c | 67 +++++++++++++++++++++-----------
>>>    1 file changed, 44 insertions(+), 23 deletions(-)
>>>
>>> diff --git a/net/vmw_vsock/virtio_transport.c b/net/vmw_vsock/virtio_transport.c
>>> index 9c287e3e393c..7ad510ec12e0 100644
>>> --- a/net/vmw_vsock/virtio_transport.c
>>> +++ b/net/vmw_vsock/virtio_transport.c
>>> @@ -65,19 +65,22 @@ struct virtio_vsock {
>>>    	u32 guest_cid;
>>>    };
>>> -static struct virtio_vsock *virtio_vsock_get(void)
>>> -{
>>> -	return the_virtio_vsock;
>>> -}
>>> -
>>>    static u32 virtio_transport_get_local_cid(void)
>>>    {
>>> -	struct virtio_vsock *vsock = virtio_vsock_get();
>>> +	struct virtio_vsock *vsock;
>>> +	u32 ret;
>>> -	if (!vsock)
>>> -		return VMADDR_CID_ANY;
>>> +	rcu_read_lock();
>>> +	vsock = rcu_dereference(the_virtio_vsock);
>>> +	if (!vsock) {
>>> +		ret = VMADDR_CID_ANY;
>>> +		goto out_rcu;
>>> +	}
>>> -	return vsock->guest_cid;
>>> +	ret = vsock->guest_cid;
>>> +out_rcu:
>>> +	rcu_read_unlock();
>>> +	return ret;
>>>    }
>>>    static void virtio_transport_loopback_work(struct work_struct *work)
>>> @@ -197,14 +200,18 @@ virtio_transport_send_pkt(struct virtio_vsock_pkt *pkt)
>>>    	struct virtio_vsock *vsock;
>>>    	int len = pkt->len;
>>> -	vsock = virtio_vsock_get();
>>> +	rcu_read_lock();
>>> +	vsock = rcu_dereference(the_virtio_vsock);
>>>    	if (!vsock) {
>>>    		virtio_transport_free_pkt(pkt);
>>> -		return -ENODEV;
>>> +		len = -ENODEV;
>>> +		goto out_rcu;
>>>    	}
>>> -	if (le64_to_cpu(pkt->hdr.dst_cid) == vsock->guest_cid)
>>> -		return virtio_transport_send_pkt_loopback(vsock, pkt);
>>> +	if (le64_to_cpu(pkt->hdr.dst_cid) == vsock->guest_cid) {
>>> +		len = virtio_transport_send_pkt_loopback(vsock, pkt);
>>> +		goto out_rcu;
>>> +	}
>>>    	if (pkt->reply)
>>>    		atomic_inc(&vsock->queued_replies);
>>> @@ -214,6 +221,9 @@ virtio_transport_send_pkt(struct virtio_vsock_pkt *pkt)
>>>    	spin_unlock_bh(&vsock->send_pkt_list_lock);
>>>    	queue_work(virtio_vsock_workqueue, &vsock->send_pkt_work);
>>> +
>>> +out_rcu:
>>> +	rcu_read_unlock();
>>>    	return len;
>>>    }
>>> @@ -222,12 +232,14 @@ virtio_transport_cancel_pkt(struct vsock_sock *vsk)
>>>    {
>>>    	struct virtio_vsock *vsock;
>>>    	struct virtio_vsock_pkt *pkt, *n;
>>> -	int cnt = 0;
>>> +	int cnt = 0, ret;
>>>    	LIST_HEAD(freeme);
>>> -	vsock = virtio_vsock_get();
>>> +	rcu_read_lock();
>>> +	vsock = rcu_dereference(the_virtio_vsock);
>>>    	if (!vsock) {
>>> -		return -ENODEV;
>>> +		ret = -ENODEV;
>>> +		goto out_rcu;
>>>    	}
>>>    	spin_lock_bh(&vsock->send_pkt_list_lock);
>>> @@ -255,7 +267,11 @@ virtio_transport_cancel_pkt(struct vsock_sock *vsk)
>>>    			queue_work(virtio_vsock_workqueue, &vsock->rx_work);
>>>    	}
>>> -	return 0;
>>> +	ret = 0;
>>> +
>>> +out_rcu:
>>> +	rcu_read_unlock();
>>> +	return ret;
>>>    }
>>>    static void virtio_vsock_rx_fill(struct virtio_vsock *vsock)
>>> @@ -590,8 +606,6 @@ static int virtio_vsock_probe(struct virtio_device *vdev)
>>>    	vsock->rx_buf_max_nr = 0;
>>>    	atomic_set(&vsock->queued_replies, 0);
>>> -	vdev->priv = vsock;
>>> -	the_virtio_vsock = vsock;
>>>    	mutex_init(&vsock->tx_lock);
>>>    	mutex_init(&vsock->rx_lock);
>>>    	mutex_init(&vsock->event_lock);
>>> @@ -613,6 +627,9 @@ static int virtio_vsock_probe(struct virtio_device *vdev)
>>>    	virtio_vsock_event_fill(vsock);
>>>    	mutex_unlock(&vsock->event_lock);
>>> +	vdev->priv = vsock;
>>> +	rcu_assign_pointer(the_virtio_vsock, vsock);
>>
>> You probably need to use rcu_dereference_protected() to access
>> the_virtio_vsock in the function in order to survive from sparse.
>>
> Ooo, thanks!
>
> Do you mean when we check if the_virtio_vsock is not null at the beginning of
> virtio_vsock_probe()?


I mean instead of:

     /* Only one virtio-vsock device per guest is supported */
     if (the_virtio_vsock) {
         ret = -EBUSY;
         goto out;
     }

you should use:

if (rcu_dereference_protected(the_virtio_vosck, 
lock_dep_is_held(&the_virtio_vsock_mutex))

...


>
>>> +
>>>    	mutex_unlock(&the_virtio_vsock_mutex);
>>>    	return 0;
>>> @@ -627,6 +644,12 @@ static void virtio_vsock_remove(struct virtio_device *vdev)
>>>    	struct virtio_vsock *vsock = vdev->priv;
>>>    	struct virtio_vsock_pkt *pkt;
>>> +	mutex_lock(&the_virtio_vsock_mutex);
>>> +
>>> +	vdev->priv = NULL;
>>> +	rcu_assign_pointer(the_virtio_vsock, NULL);
>>
>> This is still suspicious, can we access the_virtio_vsock through vdev->priv?
>> If yes, we may still get use-after-free since it was not protected by RCU.
> We will free the object only after calling the del_vqs(), so we are sure
> that the vq_callbacks ended and will no longer be invoked.
> So, IIUC it shouldn't happen.


Yes, but any dereference that is not done in vq_callbacks will be very 
dangerous in the future.

Thanks


>
>> Another more interesting question, I believe we will do singleton for
>> virtio_vsock structure. Then what's the point of using vdev->priv to access
>> the_virtio_vsock? It looks to me we can it brings extra troubles for doing
>> synchronization.
> I thought about it when I tried to use RCU to stop the worker and I
> think make sense. Maybe can be another series after this will be merged.
>
> @Stefan, what do you think about that?
>
> Thanks,
> Stefano

^ permalink raw reply

* Re: [PATCH v2 2/3] vsock/virtio: stop workers during the .remove()
From: Jason Wang @ 2019-07-04  4:00 UTC (permalink / raw)
  To: Stefano Garzarella, netdev
  Cc: kvm, virtualization, Stefan Hajnoczi, Michael S. Tsirkin,
	David S. Miller, linux-kernel
In-Reply-To: <20190628123659.139576-3-sgarzare@redhat.com>


On 2019/6/28 下午8:36, Stefano Garzarella wrote:
> Before to call vdev->config->reset(vdev) we need to be sure that
> no one is accessing the device, for this reason, we add new variables
> in the struct virtio_vsock to stop the workers during the .remove().
>
> This patch also add few comments before vdev->config->reset(vdev)
> and vdev->config->del_vqs(vdev).
>
> Suggested-by: Stefan Hajnoczi <stefanha@redhat.com>
> Suggested-by: Michael S. Tsirkin <mst@redhat.com>
> Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
> ---
>   net/vmw_vsock/virtio_transport.c | 51 +++++++++++++++++++++++++++++++-
>   1 file changed, 50 insertions(+), 1 deletion(-)


This should work. But we may consider to convert the_virtio_vosck to 
socket object and use socket refcnt and destructor in the future instead 
of inventing something new by ourselves.

Thanks


>
> diff --git a/net/vmw_vsock/virtio_transport.c b/net/vmw_vsock/virtio_transport.c
> index 7ad510ec12e0..1b44ec6f3f6c 100644
> --- a/net/vmw_vsock/virtio_transport.c
> +++ b/net/vmw_vsock/virtio_transport.c
> @@ -38,6 +38,7 @@ struct virtio_vsock {
>   	 * must be accessed with tx_lock held.
>   	 */
>   	struct mutex tx_lock;
> +	bool tx_run;
>   
>   	struct work_struct send_pkt_work;
>   	spinlock_t send_pkt_list_lock;
> @@ -53,6 +54,7 @@ struct virtio_vsock {
>   	 * must be accessed with rx_lock held.
>   	 */
>   	struct mutex rx_lock;
> +	bool rx_run;
>   	int rx_buf_nr;
>   	int rx_buf_max_nr;
>   
> @@ -60,6 +62,7 @@ struct virtio_vsock {
>   	 * vqs[VSOCK_VQ_EVENT] must be accessed with event_lock held.
>   	 */
>   	struct mutex event_lock;
> +	bool event_run;
>   	struct virtio_vsock_event event_list[8];
>   
>   	u32 guest_cid;
> @@ -94,6 +97,10 @@ static void virtio_transport_loopback_work(struct work_struct *work)
>   	spin_unlock_bh(&vsock->loopback_list_lock);
>   
>   	mutex_lock(&vsock->rx_lock);
> +
> +	if (!vsock->rx_run)
> +		goto out;
> +
>   	while (!list_empty(&pkts)) {
>   		struct virtio_vsock_pkt *pkt;
>   
> @@ -102,6 +109,7 @@ static void virtio_transport_loopback_work(struct work_struct *work)
>   
>   		virtio_transport_recv_pkt(pkt);
>   	}
> +out:
>   	mutex_unlock(&vsock->rx_lock);
>   }
>   
> @@ -130,6 +138,9 @@ virtio_transport_send_pkt_work(struct work_struct *work)
>   
>   	mutex_lock(&vsock->tx_lock);
>   
> +	if (!vsock->tx_run)
> +		goto out;
> +
>   	vq = vsock->vqs[VSOCK_VQ_TX];
>   
>   	for (;;) {
> @@ -188,6 +199,7 @@ virtio_transport_send_pkt_work(struct work_struct *work)
>   	if (added)
>   		virtqueue_kick(vq);
>   
> +out:
>   	mutex_unlock(&vsock->tx_lock);
>   
>   	if (restart_rx)
> @@ -323,6 +335,10 @@ static void virtio_transport_tx_work(struct work_struct *work)
>   
>   	vq = vsock->vqs[VSOCK_VQ_TX];
>   	mutex_lock(&vsock->tx_lock);
> +
> +	if (!vsock->tx_run)
> +		goto out;
> +
>   	do {
>   		struct virtio_vsock_pkt *pkt;
>   		unsigned int len;
> @@ -333,6 +349,8 @@ static void virtio_transport_tx_work(struct work_struct *work)
>   			added = true;
>   		}
>   	} while (!virtqueue_enable_cb(vq));
> +
> +out:
>   	mutex_unlock(&vsock->tx_lock);
>   
>   	if (added)
> @@ -361,6 +379,9 @@ static void virtio_transport_rx_work(struct work_struct *work)
>   
>   	mutex_lock(&vsock->rx_lock);
>   
> +	if (!vsock->rx_run)
> +		goto out;
> +
>   	do {
>   		virtqueue_disable_cb(vq);
>   		for (;;) {
> @@ -470,6 +491,9 @@ static void virtio_transport_event_work(struct work_struct *work)
>   
>   	mutex_lock(&vsock->event_lock);
>   
> +	if (!vsock->event_run)
> +		goto out;
> +
>   	do {
>   		struct virtio_vsock_event *event;
>   		unsigned int len;
> @@ -484,7 +508,7 @@ static void virtio_transport_event_work(struct work_struct *work)
>   	} while (!virtqueue_enable_cb(vq));
>   
>   	virtqueue_kick(vsock->vqs[VSOCK_VQ_EVENT]);
> -
> +out:
>   	mutex_unlock(&vsock->event_lock);
>   }
>   
> @@ -619,12 +643,18 @@ static int virtio_vsock_probe(struct virtio_device *vdev)
>   	INIT_WORK(&vsock->send_pkt_work, virtio_transport_send_pkt_work);
>   	INIT_WORK(&vsock->loopback_work, virtio_transport_loopback_work);
>   
> +	mutex_lock(&vsock->tx_lock);
> +	vsock->tx_run = true;
> +	mutex_unlock(&vsock->tx_lock);
> +
>   	mutex_lock(&vsock->rx_lock);
>   	virtio_vsock_rx_fill(vsock);
> +	vsock->rx_run = true;
>   	mutex_unlock(&vsock->rx_lock);
>   
>   	mutex_lock(&vsock->event_lock);
>   	virtio_vsock_event_fill(vsock);
> +	vsock->event_run = true;
>   	mutex_unlock(&vsock->event_lock);
>   
>   	vdev->priv = vsock;
> @@ -659,6 +689,24 @@ static void virtio_vsock_remove(struct virtio_device *vdev)
>   	/* Reset all connected sockets when the device disappear */
>   	vsock_for_each_connected_socket(virtio_vsock_reset_sock);
>   
> +	/* Stop all work handlers to make sure no one is accessing the device,
> +	 * so we can safely call vdev->config->reset().
> +	 */
> +	mutex_lock(&vsock->rx_lock);
> +	vsock->rx_run = false;
> +	mutex_unlock(&vsock->rx_lock);
> +
> +	mutex_lock(&vsock->tx_lock);
> +	vsock->tx_run = false;
> +	mutex_unlock(&vsock->tx_lock);
> +
> +	mutex_lock(&vsock->event_lock);
> +	vsock->event_run = false;
> +	mutex_unlock(&vsock->event_lock);
> +
> +	/* Flush all device writes and interrupts, device will not use any
> +	 * more buffers.
> +	 */
>   	vdev->config->reset(vdev);
>   
>   	mutex_lock(&vsock->rx_lock);
> @@ -689,6 +737,7 @@ static void virtio_vsock_remove(struct virtio_device *vdev)
>   	}
>   	spin_unlock_bh(&vsock->loopback_list_lock);
>   
> +	/* Delete virtqueues and flush outstanding callbacks if any */
>   	vdev->config->del_vqs(vdev);
>   
>   	mutex_unlock(&the_virtio_vsock_mutex);

^ permalink raw reply

* Re: [RFC v2] vhost: introduce mdev based hardware vhost backend
From: Jason Wang @ 2019-07-04  4:31 UTC (permalink / raw)
  To: Tiwei Bie
  Cc: mst, alex.williamson, maxime.coquelin, linux-kernel, kvm,
	virtualization, netdev, dan.daly, cunming.liang, zhihong.wang
In-Reply-To: <20190703130817.GA1978@___>


On 2019/7/3 下午9:08, Tiwei Bie wrote:
> On Wed, Jul 03, 2019 at 08:16:23PM +0800, Jason Wang wrote:
>> On 2019/7/3 下午7:52, Tiwei Bie wrote:
>>> On Wed, Jul 03, 2019 at 06:09:51PM +0800, Jason Wang wrote:
>>>> On 2019/7/3 下午5:13, Tiwei Bie wrote:
>>>>> Details about this can be found here:
>>>>>
>>>>> https://lwn.net/Articles/750770/
>>>>>
>>>>> What's new in this version
>>>>> ==========================
>>>>>
>>>>> A new VFIO device type is introduced - vfio-vhost. This addressed
>>>>> some comments from here:https://patchwork.ozlabs.org/cover/984763/
>>>>>
>>>>> Below is the updated device interface:
>>>>>
>>>>> Currently, there are two regions of this device: 1) CONFIG_REGION
>>>>> (VFIO_VHOST_CONFIG_REGION_INDEX), which can be used to setup the
>>>>> device; 2) NOTIFY_REGION (VFIO_VHOST_NOTIFY_REGION_INDEX), which
>>>>> can be used to notify the device.
>>>>>
>>>>> 1. CONFIG_REGION
>>>>>
>>>>> The region described by CONFIG_REGION is the main control interface.
>>>>> Messages will be written to or read from this region.
>>>>>
>>>>> The message type is determined by the `request` field in message
>>>>> header. The message size is encoded in the message header too.
>>>>> The message format looks like this:
>>>>>
>>>>> struct vhost_vfio_op {
>>>>> 	__u64 request;
>>>>> 	__u32 flags;
>>>>> 	/* Flag values: */
>>>>>     #define VHOST_VFIO_NEED_REPLY 0x1 /* Whether need reply */
>>>>> 	__u32 size;
>>>>> 	union {
>>>>> 		__u64 u64;
>>>>> 		struct vhost_vring_state state;
>>>>> 		struct vhost_vring_addr addr;
>>>>> 	} payload;
>>>>> };
>>>>>
>>>>> The existing vhost-kernel ioctl cmds are reused as the message
>>>>> requests in above structure.
>>>> Still a comments like V1. What's the advantage of inventing a new protocol?
>>> I'm trying to make it work in VFIO's way..
>>>
>>>> I believe either of the following should be better:
>>>>
>>>> - using vhost ioctl,  we can start from SET_VRING_KICK/SET_VRING_CALL and
>>>> extend it with e.g notify region. The advantages is that all exist userspace
>>>> program could be reused without modification (or minimal modification). And
>>>> vhost API hides lots of details that is not necessary to be understood by
>>>> application (e.g in the case of container).
>>> Do you mean reusing vhost's ioctl on VFIO device fd directly,
>>> or introducing another mdev driver (i.e. vhost_mdev instead of
>>> using the existing vfio_mdev) for mdev device?
>> Can we simply add them into ioctl of mdev_parent_ops?
> Right, either way, these ioctls have to be and just need to be
> added in the ioctl of the mdev_parent_ops. But another thing we
> also need to consider is that which file descriptor the userspace
> will do the ioctl() on. So I'm wondering do you mean let the
> userspace do the ioctl() on the VFIO device fd of the mdev
> device?
>

Yes. Is there any other way btw?

Thanks


^ permalink raw reply

* Re: i.mx6ul with DSA in multi chip addressing mode - no MDIO access
From: Benjamin Beckmeyer @ 2019-07-04  4:33 UTC (permalink / raw)
  To: Andrew Lunn; +Cc: netdev
In-Reply-To: <20190703155518.GE18473@lunn.ch>


On 03.07.19 17:55, Andrew Lunn wrote:
> On Wed, Jul 03, 2019 at 03:10:34PM +0200, Benjamin Beckmeyer wrote:
>> Hey folks,
>>
>> I'm having a problem with a custom i.mx6ul board. When DSA is loaded I can't 
>> get access to the switch via MDIO, but the DSA is working properly. I set up
>> a bridge for testing and the switch is in forwarding mode and i can ping the 
>> board. But the MDIO access isn't working at address 2 for the switch. When I 
>> delete the DSA from the devicetree and start the board up, I can access the 
>> switch via MDIO.
>>
>> With DSA up and running:
>>
>> mii -i 2 0 0x9800
>> mii -i 2 1
>> phyid:2, reg:0x01 -> 0x4000
>> mii -i 2 0 0x9803
>> mii -i 2 1
>> phyid:2, reg:0x01 -> 0x4000
>> mii -i 2 1 0x1883
>> mii -i 2 1
>> phyid:2, reg:0x01 -> 0x4000
> Hi Benjamin
>
> I'm guessing that the driver is also using register 0 and 1 at the
> same time you are, e.g. to poll the PHYs for link status etc.
>
> There are trace points for MDIO, so you can get the kernel to log all
> registers access. That should confirm if i'm right.
>
> 	  Andrew

Hi Andrew,
I will do so. Haven't done it before, so maybe it takes some time.

I'm just wondering why I haven't these problems with another switching
chip. Here we use the 88E6390 in multi chip addressing mode, in the 
other custom board we have a 88E6321 in single chip addressing mode, 
but there the registers would be polled too. Maybe you know a answer?

Thanks again for your fast reply.


^ permalink raw reply

* RE: [EXT] Re: [PATCH net-next 4/4] qed: Add devlink support for configuration attributes.
From: Sudarsana Reddy Kalluru @ 2019-07-04  5:49 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Jiri Pirko, davem@davemloft.net, netdev@vger.kernel.org,
	Michal Kalderon, Ariel Elior
In-Reply-To: <20190703104244.7d26e734@cakuba.netronome.com>

> -----Original Message-----
> From: Jakub Kicinski <jakub.kicinski@netronome.com>
> Sent: Wednesday, July 3, 2019 11:13 PM
> To: Sudarsana Reddy Kalluru <skalluru@marvell.com>
> Cc: Jiri Pirko <jiri@resnulli.us>; davem@davemloft.net;
> netdev@vger.kernel.org; Michal Kalderon <mkalderon@marvell.com>; Ariel
> Elior <aelior@marvell.com>
> Subject: Re: [EXT] Re: [PATCH net-next 4/4] qed: Add devlink support for
> configuration attributes.
> 
> On Wed, 3 Jul 2019 12:56:39 +0000, Sudarsana Reddy Kalluru wrote:
> > Apologies for bringing this topic again. From the driver(s) code
> > paths/'devlink man pages', I understood that devlink-port object is an
> > entity on top of the PCI bus device. Some drivers say NFP represents
> > vnics (on pci-dev) as a devlink-ports and, some represents
> > (virtual?) ports on the PF/device as devlink-ports. In the case of
> > Marvell NIC driver, we don't have [port] partitioning of the PCI
> > device. And the config attributes are specific to PCI-device (not the
> > vports/vnics of PF). Hence I didn't see a need for creating
> > devlink-port objects in the system for Marvell NICs. And planning to
> > add the config attributes to 'devlink-dev' object. Please let me know
> > if my understanding and the proposal is ok?
> 
> I understand where you're coming from.
> 
> We want to make that judgement call on attribute-by-attribute basis.
> We want consistency across vendors (and, frankly, multiple drivers of the
> same vendor).  If attribute looks like it belongs to the port, rather than the
> entire device/ASIC operation, we should make it a port attribute.

Thanks for your mail. I'll go with creating PCI-dev/0 (i.e., port-id 0) for port based attributes as there's no port partitioning for Marvell NICs.

^ permalink raw reply

* RE: [PATCH v1 net-next] net: stmmac: enable clause 45 mdio support
From: Voon, Weifeng @ 2019-07-04  6:05 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: David S. Miller, Maxime Coquelin, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, Jose Abreu, Giuseppe Cavallaro,
	Florian Fainelli, Alexandre Torgue, biao huang, Ong, Boon Leong,
	Kweh, Hock Leong
In-Reply-To: <20190704033038.GA6276@lunn.ch>

> > > > @@ -155,22 +171,26 @@ static int stmmac_mdio_read(struct mii_bus
> > > > *bus,
> > > int phyaddr, int phyreg)
> > > >  	struct stmmac_priv *priv = netdev_priv(ndev);
> > > >  	unsigned int mii_address = priv->hw->mii.addr;
> > > >  	unsigned int mii_data = priv->hw->mii.data;
> > > > -	u32 v;
> > > > -	int data;
> > > >  	u32 value = MII_BUSY;
> > > > +	int data = 0;
> > > > +	u32 v;
> > > >
> > > >  	value |= (phyaddr << priv->hw->mii.addr_shift)
> > > >  		& priv->hw->mii.addr_mask;
> > > >  	value |= (phyreg << priv->hw->mii.reg_shift) & priv->hw-
> > > >mii.reg_mask;
> > > >  	value |= (priv->clk_csr << priv->hw->mii.clk_csr_shift)
> > > >  		& priv->hw->mii.clk_csr_mask;
> > > > -	if (priv->plat->has_gmac4)
> > > > +	if (priv->plat->has_gmac4) {
> > > >  		value |= MII_GMAC4_READ;
> > > > +		if (phyreg & MII_ADDR_C45)
> > > > +			stmmac_mdio_c45_setup(priv, phyreg, &value, &data);
> > > > +	}
> > > >
> > > >  	if (readl_poll_timeout(priv->ioaddr + mii_address, v, !(v &
> > > MII_BUSY),
> > > >  			       100, 10000))
> > > >  		return -EBUSY;
> > > >
> > > > +	writel(data, priv->ioaddr + mii_data);
> > >
> > > That looks odd. Could you explain why it is needed.
> > >
> > > Thanks
> > > 	Andrew
> >
> > Hi Andrew,
> > This mdio c45 support needed to access DWC xPCS which is a Clause-45
> 
> I mean it looks odd doing a write to the data register in the middle of
> stmmac_mdio_read().

MAC is using an indirect access to access mdio devices. In order to read,
the driver needs to write into both mii_data and mii_address to select 
c45, read/write command, phy address, address to read, and etc. 

Weifeng


^ permalink raw reply

* Re: [RFC v2] vhost: introduce mdev based hardware vhost backend
From: Tiwei Bie @ 2019-07-04  6:21 UTC (permalink / raw)
  To: Jason Wang
  Cc: mst, alex.williamson, maxime.coquelin, linux-kernel, kvm,
	virtualization, netdev, dan.daly, cunming.liang, zhihong.wang
In-Reply-To: <b01b8e28-8d96-31dd-56f4-ca7793498c55@redhat.com>

On Thu, Jul 04, 2019 at 12:31:48PM +0800, Jason Wang wrote:
> On 2019/7/3 下午9:08, Tiwei Bie wrote:
> > On Wed, Jul 03, 2019 at 08:16:23PM +0800, Jason Wang wrote:
> > > On 2019/7/3 下午7:52, Tiwei Bie wrote:
> > > > On Wed, Jul 03, 2019 at 06:09:51PM +0800, Jason Wang wrote:
> > > > > On 2019/7/3 下午5:13, Tiwei Bie wrote:
> > > > > > Details about this can be found here:
> > > > > > 
> > > > > > https://lwn.net/Articles/750770/
> > > > > > 
> > > > > > What's new in this version
> > > > > > ==========================
> > > > > > 
> > > > > > A new VFIO device type is introduced - vfio-vhost. This addressed
> > > > > > some comments from here:https://patchwork.ozlabs.org/cover/984763/
> > > > > > 
> > > > > > Below is the updated device interface:
> > > > > > 
> > > > > > Currently, there are two regions of this device: 1) CONFIG_REGION
> > > > > > (VFIO_VHOST_CONFIG_REGION_INDEX), which can be used to setup the
> > > > > > device; 2) NOTIFY_REGION (VFIO_VHOST_NOTIFY_REGION_INDEX), which
> > > > > > can be used to notify the device.
> > > > > > 
> > > > > > 1. CONFIG_REGION
> > > > > > 
> > > > > > The region described by CONFIG_REGION is the main control interface.
> > > > > > Messages will be written to or read from this region.
> > > > > > 
> > > > > > The message type is determined by the `request` field in message
> > > > > > header. The message size is encoded in the message header too.
> > > > > > The message format looks like this:
> > > > > > 
> > > > > > struct vhost_vfio_op {
> > > > > > 	__u64 request;
> > > > > > 	__u32 flags;
> > > > > > 	/* Flag values: */
> > > > > >     #define VHOST_VFIO_NEED_REPLY 0x1 /* Whether need reply */
> > > > > > 	__u32 size;
> > > > > > 	union {
> > > > > > 		__u64 u64;
> > > > > > 		struct vhost_vring_state state;
> > > > > > 		struct vhost_vring_addr addr;
> > > > > > 	} payload;
> > > > > > };
> > > > > > 
> > > > > > The existing vhost-kernel ioctl cmds are reused as the message
> > > > > > requests in above structure.
> > > > > Still a comments like V1. What's the advantage of inventing a new protocol?
> > > > I'm trying to make it work in VFIO's way..
> > > > 
> > > > > I believe either of the following should be better:
> > > > > 
> > > > > - using vhost ioctl,  we can start from SET_VRING_KICK/SET_VRING_CALL and
> > > > > extend it with e.g notify region. The advantages is that all exist userspace
> > > > > program could be reused without modification (or minimal modification). And
> > > > > vhost API hides lots of details that is not necessary to be understood by
> > > > > application (e.g in the case of container).
> > > > Do you mean reusing vhost's ioctl on VFIO device fd directly,
> > > > or introducing another mdev driver (i.e. vhost_mdev instead of
> > > > using the existing vfio_mdev) for mdev device?
> > > Can we simply add them into ioctl of mdev_parent_ops?
> > Right, either way, these ioctls have to be and just need to be
> > added in the ioctl of the mdev_parent_ops. But another thing we
> > also need to consider is that which file descriptor the userspace
> > will do the ioctl() on. So I'm wondering do you mean let the
> > userspace do the ioctl() on the VFIO device fd of the mdev
> > device?
> > 
> 
> Yes.

Got it! I'm not sure what's Alex opinion on this. If we all
agree with this, I can do it in this way.

> Is there any other way btw?

Just a quick thought.. Maybe totally a bad idea. I was thinking
whether it would be odd to do non-VFIO's ioctls on VFIO's device
fd. So I was wondering whether it's possible to allow binding
another mdev driver (e.g. vhost_mdev) to the supported mdev
devices. The new mdev driver, vhost_mdev, can provide similar
ways to let userspace open the mdev device and do the vhost ioctls
on it. To distinguish with the vfio_mdev compatible mdev devices,
the device API of the new vhost_mdev compatible mdev devices
might be e.g. "vhost-net" for net?

So in VFIO case, the device will be for passthru directly. And
in VHOST case, the device can be used to accelerate the existing
virtualized devices.

How do you think?

Thanks,
Tiwei
> 
> Thanks
> 

^ permalink raw reply

* Re: [RFC v2] vhost: introduce mdev based hardware vhost backend
From: Jason Wang @ 2019-07-04  6:35 UTC (permalink / raw)
  To: Tiwei Bie
  Cc: mst, alex.williamson, maxime.coquelin, linux-kernel, kvm,
	virtualization, netdev, dan.daly, cunming.liang, zhihong.wang
In-Reply-To: <20190704062134.GA21116@___>


On 2019/7/4 下午2:21, Tiwei Bie wrote:
> On Thu, Jul 04, 2019 at 12:31:48PM +0800, Jason Wang wrote:
>> On 2019/7/3 下午9:08, Tiwei Bie wrote:
>>> On Wed, Jul 03, 2019 at 08:16:23PM +0800, Jason Wang wrote:
>>>> On 2019/7/3 下午7:52, Tiwei Bie wrote:
>>>>> On Wed, Jul 03, 2019 at 06:09:51PM +0800, Jason Wang wrote:
>>>>>> On 2019/7/3 下午5:13, Tiwei Bie wrote:
>>>>>>> Details about this can be found here:
>>>>>>>
>>>>>>> https://lwn.net/Articles/750770/
>>>>>>>
>>>>>>> What's new in this version
>>>>>>> ==========================
>>>>>>>
>>>>>>> A new VFIO device type is introduced - vfio-vhost. This addressed
>>>>>>> some comments from here:https://patchwork.ozlabs.org/cover/984763/
>>>>>>>
>>>>>>> Below is the updated device interface:
>>>>>>>
>>>>>>> Currently, there are two regions of this device: 1) CONFIG_REGION
>>>>>>> (VFIO_VHOST_CONFIG_REGION_INDEX), which can be used to setup the
>>>>>>> device; 2) NOTIFY_REGION (VFIO_VHOST_NOTIFY_REGION_INDEX), which
>>>>>>> can be used to notify the device.
>>>>>>>
>>>>>>> 1. CONFIG_REGION
>>>>>>>
>>>>>>> The region described by CONFIG_REGION is the main control interface.
>>>>>>> Messages will be written to or read from this region.
>>>>>>>
>>>>>>> The message type is determined by the `request` field in message
>>>>>>> header. The message size is encoded in the message header too.
>>>>>>> The message format looks like this:
>>>>>>>
>>>>>>> struct vhost_vfio_op {
>>>>>>> 	__u64 request;
>>>>>>> 	__u32 flags;
>>>>>>> 	/* Flag values: */
>>>>>>>      #define VHOST_VFIO_NEED_REPLY 0x1 /* Whether need reply */
>>>>>>> 	__u32 size;
>>>>>>> 	union {
>>>>>>> 		__u64 u64;
>>>>>>> 		struct vhost_vring_state state;
>>>>>>> 		struct vhost_vring_addr addr;
>>>>>>> 	} payload;
>>>>>>> };
>>>>>>>
>>>>>>> The existing vhost-kernel ioctl cmds are reused as the message
>>>>>>> requests in above structure.
>>>>>> Still a comments like V1. What's the advantage of inventing a new protocol?
>>>>> I'm trying to make it work in VFIO's way..
>>>>>
>>>>>> I believe either of the following should be better:
>>>>>>
>>>>>> - using vhost ioctl,  we can start from SET_VRING_KICK/SET_VRING_CALL and
>>>>>> extend it with e.g notify region. The advantages is that all exist userspace
>>>>>> program could be reused without modification (or minimal modification). And
>>>>>> vhost API hides lots of details that is not necessary to be understood by
>>>>>> application (e.g in the case of container).
>>>>> Do you mean reusing vhost's ioctl on VFIO device fd directly,
>>>>> or introducing another mdev driver (i.e. vhost_mdev instead of
>>>>> using the existing vfio_mdev) for mdev device?
>>>> Can we simply add them into ioctl of mdev_parent_ops?
>>> Right, either way, these ioctls have to be and just need to be
>>> added in the ioctl of the mdev_parent_ops. But another thing we
>>> also need to consider is that which file descriptor the userspace
>>> will do the ioctl() on. So I'm wondering do you mean let the
>>> userspace do the ioctl() on the VFIO device fd of the mdev
>>> device?
>>>
>> Yes.
> Got it! I'm not sure what's Alex opinion on this. If we all
> agree with this, I can do it in this way.
>
>> Is there any other way btw?
> Just a quick thought.. Maybe totally a bad idea.


It's not for sure :)


>   I was thinking
> whether it would be odd to do non-VFIO's ioctls on VFIO's device
> fd. So I was wondering whether it's possible to allow binding
> another mdev driver (e.g. vhost_mdev) to the supported mdev
> devices. The new mdev driver, vhost_mdev, can provide similar
> ways to let userspace open the mdev device and do the vhost ioctls
> on it. To distinguish with the vfio_mdev compatible mdev devices,
> the device API of the new vhost_mdev compatible mdev devices
> might be e.g. "vhost-net" for net?
>
> So in VFIO case, the device will be for passthru directly. And
> in VHOST case, the device can be used to accelerate the existing
> virtualized devices.
>
> How do you think?


If my understanding is correct, there will be no VFIO ioctl if we go for 
vhost_mdev?

Thanks


>
> Thanks,
> Tiwei
>> Thanks
>>

^ permalink raw reply

* Re: [RFC v2] vhost: introduce mdev based hardware vhost backend
From: Tiwei Bie @ 2019-07-04  7:02 UTC (permalink / raw)
  To: Jason Wang
  Cc: mst, alex.williamson, maxime.coquelin, linux-kernel, kvm,
	virtualization, netdev, dan.daly, cunming.liang, zhihong.wang
In-Reply-To: <c67f628f-e0c1-9a41-6c5d-b6bbda31467d@redhat.com>

On Thu, Jul 04, 2019 at 02:35:20PM +0800, Jason Wang wrote:
> On 2019/7/4 下午2:21, Tiwei Bie wrote:
> > On Thu, Jul 04, 2019 at 12:31:48PM +0800, Jason Wang wrote:
> > > On 2019/7/3 下午9:08, Tiwei Bie wrote:
> > > > On Wed, Jul 03, 2019 at 08:16:23PM +0800, Jason Wang wrote:
> > > > > On 2019/7/3 下午7:52, Tiwei Bie wrote:
> > > > > > On Wed, Jul 03, 2019 at 06:09:51PM +0800, Jason Wang wrote:
> > > > > > > On 2019/7/3 下午5:13, Tiwei Bie wrote:
> > > > > > > > Details about this can be found here:
> > > > > > > > 
> > > > > > > > https://lwn.net/Articles/750770/
> > > > > > > > 
> > > > > > > > What's new in this version
> > > > > > > > ==========================
> > > > > > > > 
> > > > > > > > A new VFIO device type is introduced - vfio-vhost. This addressed
> > > > > > > > some comments from here:https://patchwork.ozlabs.org/cover/984763/
> > > > > > > > 
> > > > > > > > Below is the updated device interface:
> > > > > > > > 
> > > > > > > > Currently, there are two regions of this device: 1) CONFIG_REGION
> > > > > > > > (VFIO_VHOST_CONFIG_REGION_INDEX), which can be used to setup the
> > > > > > > > device; 2) NOTIFY_REGION (VFIO_VHOST_NOTIFY_REGION_INDEX), which
> > > > > > > > can be used to notify the device.
> > > > > > > > 
> > > > > > > > 1. CONFIG_REGION
> > > > > > > > 
> > > > > > > > The region described by CONFIG_REGION is the main control interface.
> > > > > > > > Messages will be written to or read from this region.
> > > > > > > > 
> > > > > > > > The message type is determined by the `request` field in message
> > > > > > > > header. The message size is encoded in the message header too.
> > > > > > > > The message format looks like this:
> > > > > > > > 
> > > > > > > > struct vhost_vfio_op {
> > > > > > > > 	__u64 request;
> > > > > > > > 	__u32 flags;
> > > > > > > > 	/* Flag values: */
> > > > > > > >      #define VHOST_VFIO_NEED_REPLY 0x1 /* Whether need reply */
> > > > > > > > 	__u32 size;
> > > > > > > > 	union {
> > > > > > > > 		__u64 u64;
> > > > > > > > 		struct vhost_vring_state state;
> > > > > > > > 		struct vhost_vring_addr addr;
> > > > > > > > 	} payload;
> > > > > > > > };
> > > > > > > > 
> > > > > > > > The existing vhost-kernel ioctl cmds are reused as the message
> > > > > > > > requests in above structure.
> > > > > > > Still a comments like V1. What's the advantage of inventing a new protocol?
> > > > > > I'm trying to make it work in VFIO's way..
> > > > > > 
> > > > > > > I believe either of the following should be better:
> > > > > > > 
> > > > > > > - using vhost ioctl,  we can start from SET_VRING_KICK/SET_VRING_CALL and
> > > > > > > extend it with e.g notify region. The advantages is that all exist userspace
> > > > > > > program could be reused without modification (or minimal modification). And
> > > > > > > vhost API hides lots of details that is not necessary to be understood by
> > > > > > > application (e.g in the case of container).
> > > > > > Do you mean reusing vhost's ioctl on VFIO device fd directly,
> > > > > > or introducing another mdev driver (i.e. vhost_mdev instead of
> > > > > > using the existing vfio_mdev) for mdev device?
> > > > > Can we simply add them into ioctl of mdev_parent_ops?
> > > > Right, either way, these ioctls have to be and just need to be
> > > > added in the ioctl of the mdev_parent_ops. But another thing we
> > > > also need to consider is that which file descriptor the userspace
> > > > will do the ioctl() on. So I'm wondering do you mean let the
> > > > userspace do the ioctl() on the VFIO device fd of the mdev
> > > > device?
> > > > 
> > > Yes.
> > Got it! I'm not sure what's Alex opinion on this. If we all
> > agree with this, I can do it in this way.
> > 
> > > Is there any other way btw?
> > Just a quick thought.. Maybe totally a bad idea.
> 
> 
> It's not for sure :)

Thanks!

> 
> 
> >   I was thinking
> > whether it would be odd to do non-VFIO's ioctls on VFIO's device
> > fd. So I was wondering whether it's possible to allow binding
> > another mdev driver (e.g. vhost_mdev) to the supported mdev
> > devices. The new mdev driver, vhost_mdev, can provide similar
> > ways to let userspace open the mdev device and do the vhost ioctls
> > on it. To distinguish with the vfio_mdev compatible mdev devices,
> > the device API of the new vhost_mdev compatible mdev devices
> > might be e.g. "vhost-net" for net?
> > 
> > So in VFIO case, the device will be for passthru directly. And
> > in VHOST case, the device can be used to accelerate the existing
> > virtualized devices.
> > 
> > How do you think?
> 
> 
> If my understanding is correct, there will be no VFIO ioctl if we go for
> vhost_mdev?

Yeah, exactly. If we go for vhost_mdev, we may have some vhost nodes
in /dev similar to what /dev/vfio/* does to handle the $UUID and open
the device (e.g. similar to VFIO_GROUP_GET_DEVICE_FD in VFIO). And
to setup the device, we can try to reuse the ioctls of the existing
kernel vhost as much as possible.

Thanks,
Tiwei

> 
> Thanks
> 
> 
> > 
> > Thanks,
> > Tiwei
> > > Thanks
> > > 

^ permalink raw reply

* Re: [PATCH v2 1/4] net: dsa: Change DT bindings for Vitesse VSC73xx switches
From: Linus Walleij @ 2019-07-04  7:05 UTC (permalink / raw)
  To: Pawel Dembicki
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Rob Herring, Mark Rutland, netdev,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	linux-kernel@vger.kernel.org
In-Reply-To: <20190703171924.31801-2-paweldembicki@gmail.com>

On Wed, Jul 3, 2019 at 7:21 PM Pawel Dembicki <paweldembicki@gmail.com> wrote:

> This commit introduce how to use vsc73xx platform driver.
>
> Signed-off-by: Pawel Dembicki <paweldembicki@gmail.com>

Nice!

> +If Platform driver is used, the device tree node is an platform device so it
> +must reside inside a platform bus device tree node.

I would write something like "when connected to a memory bus, and
used in memory-mapped I/O mode, a platform device is used to represent
the vsc73xx" so it is clear what is going on.

Reviewed-by: Linus Walleij <linus.walleij@linaro.org>

Yours,
Linus Walleij

^ permalink raw reply

* Re: [PATCH v2 2/4] net: dsa: vsc73xx: Split vsc73xx driver
From: Linus Walleij @ 2019-07-04  7:08 UTC (permalink / raw)
  To: Pawel Dembicki
  Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
	Rob Herring, Mark Rutland, netdev,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	linux-kernel@vger.kernel.org
In-Reply-To: <20190703171924.31801-3-paweldembicki@gmail.com>

On Wed, Jul 3, 2019 at 7:21 PM Pawel Dembicki <paweldembicki@gmail.com> wrote:

> This driver (currently) only takes control of the switch chip over
> SPI and configures it to route packages around when connected to a
> CPU port. But Vitesse chip support also parallel interface.
>
> This patch split driver into two parts: core and spi. It is required
> for add support to another managing interface.
>
> Tested-by: Linus Walleij <linus.walleij@linaro.org>
> Signed-off-by: Pawel Dembicki <paweldembicki@gmail.com>

That's a nice and clean split.
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>

Yours,
Linus Walleij

^ permalink raw reply

* [PATCH net-next 0/8] mlxsw: Enable/disable PTP shapers
From: Ido Schimmel @ 2019-07-04  7:07 UTC (permalink / raw)
  To: netdev; +Cc: davem, richardcochran, jiri, petrm, shalomt, mlxsw, Ido Schimmel

From: Ido Schimmel <idosch@mellanox.com>

Shalom says:

In order to get more accurate hardware time stamping in Spectrum-1, the
driver needs to apply a shaper on the port for speeds lower than 40Gbps.
This shaper is called a PTP shaper and it is applied on hierarchy 0,
which is the port hierarchy. This shaper may affect the shaper rates of
all hierarchies.

This patchset adds the ability to enable or disable the PTP shaper on
the port in two scenarios:
 1. When the user wants to enable/disable the hardware time stamping
 2. When the port is brought up or down (including port speed change)

Patch #1 adds the QEEC.ptps field that is used for enabling or disabling
the PTP shaper on a port.

Patch #2 adds a note about disabling the PTP shaper when calling to
mlxsw_sp_port_ets_maxrate_set().

Patch #3 adds the QPSC register that is responsible for configuring the
PTP shaper parameters per speed.

Patch #4 sets the PTP shaper parameters during the ptp_init().

Patch #5 adds new operation for getting the port's speed.

Patch #6 enables/disables the PTP shaper when turning on or off the
hardware time stamping.

Patch #7 enables/disables the PTP shaper when the port's status has
changed (including port speed change).

Patch #8 applies the PTP shaper enable/disable logic by filling the PTP
shaper parameters array.

Shalom Toledo (8):
  mlxsw: reg: Add ptps field in QoS ETS Element Configuration Register
  mlxsw: spectrum: Add note about the PTP shaper
  mlxsw: reg: Add QoS PTP Shaper Configuration Register
  mlxsw: spectrum_ptp: Set the PTP shaper parameters
  mlxsw: spectrum: Add new operation for getting the port's speed
  mlxsw: spectrum_ptp: Enable/disable PTP shaper on a port when getting
    HWTSTAMP on/off
  mlxsw: spectrum: Set up PTP shaper when port status has changed
  mlxsw: spectrum_ptp: Apply the PTP shaper enable/disable logic

 drivers/net/ethernet/mellanox/mlxsw/reg.h     | 129 ++++++++++++++
 .../net/ethernet/mellanox/mlxsw/spectrum.c    |  81 +++++----
 .../net/ethernet/mellanox/mlxsw/spectrum.h    |   2 +
 .../ethernet/mellanox/mlxsw/spectrum_ptp.c    | 158 ++++++++++++++++++
 .../ethernet/mellanox/mlxsw/spectrum_ptp.h    |  10 ++
 5 files changed, 350 insertions(+), 30 deletions(-)

-- 
2.20.1


^ permalink raw reply

* [PATCH net-next 2/8] mlxsw: spectrum: Add note about the PTP shaper
From: Ido Schimmel @ 2019-07-04  7:07 UTC (permalink / raw)
  To: netdev; +Cc: davem, richardcochran, jiri, petrm, shalomt, mlxsw, Ido Schimmel
In-Reply-To: <20190704070740.302-1-idosch@idosch.org>

From: Shalom Toledo <shalomt@mellanox.com>

Add note about disabling the PTP shaper when calling to
mlxsw_sp_port_ets_maxrate_set().

Signed-off-by: Shalom Toledo <shalomt@mellanox.com>
Acked-by: Jiri Pirko <jiri@mellanox.com>
Reviewed-by: Petr Machata <petrm@mellanox.com>
Signed-off-by: Ido Schimmel <idosch@mellanox.com>
---
 drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
index 755b14b82c8f..67133ba53015 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
@@ -3457,8 +3457,9 @@ static int mlxsw_sp_port_ets_init(struct mlxsw_sp_port *mlxsw_sp_port)
 			return err;
 	}
 
-	/* Make sure the max shaper is disabled in all hierarchies that
-	 * support it.
+	/* Make sure the max shaper is disabled in all hierarchies that support
+	 * it. Note that this disables ptps (PTP shaper), but that is intended
+	 * for the initial configuration.
 	 */
 	err = mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port,
 					    MLXSW_REG_QEEC_HIERARCY_PORT, 0, 0,
-- 
2.20.1


^ permalink raw reply related

* [PATCH net-next 1/8] mlxsw: reg: Add ptps field in QoS ETS Element Configuration Register
From: Ido Schimmel @ 2019-07-04  7:07 UTC (permalink / raw)
  To: netdev; +Cc: davem, richardcochran, jiri, petrm, shalomt, mlxsw, Ido Schimmel
In-Reply-To: <20190704070740.302-1-idosch@idosch.org>

From: Shalom Toledo <shalomt@mellanox.com>

The PTP Shaper field is used for enabling and disabling of port-rate based
shaper which is slightly lower than port rate.

Signed-off-by: Shalom Toledo <shalomt@mellanox.com>
Acked-by: Jiri Pirko <jiri@mellanox.com>
Reviewed-by: Petr Machata <petrm@mellanox.com>
Signed-off-by: Ido Schimmel <idosch@mellanox.com>
---
 drivers/net/ethernet/mellanox/mlxsw/reg.h | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h
index 76ff5b217c04..d2e2a75f7983 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/reg.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h
@@ -3515,6 +3515,18 @@ MLXSW_ITEM32(reg, qeec, next_element_index, 0x08, 0, 8);
  */
 MLXSW_ITEM32(reg, qeec, mise, 0x0C, 31, 1);
 
+/* reg_qeec_ptps
+ * PTP shaper
+ * 0: regular shaper mode
+ * 1: PTP oriented shaper
+ * Allowed only for hierarchy 0
+ * Not supported for CPU port
+ * Note that ptps mode may affect the shaper rates of all hierarchies
+ * Supported only on Spectrum-1
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, qeec, ptps, 0x0C, 29, 1);
+
 enum {
 	MLXSW_REG_QEEC_BYTES_MODE,
 	MLXSW_REG_QEEC_PACKETS_MODE,
@@ -3601,6 +3613,16 @@ static inline void mlxsw_reg_qeec_pack(char *payload, u8 local_port,
 	mlxsw_reg_qeec_next_element_index_set(payload, next_index);
 }
 
+static inline void mlxsw_reg_qeec_ptps_pack(char *payload, u8 local_port,
+					    bool ptps)
+{
+	MLXSW_REG_ZERO(qeec, payload);
+	mlxsw_reg_qeec_local_port_set(payload, local_port);
+	mlxsw_reg_qeec_element_hierarchy_set(payload,
+					     MLXSW_REG_QEEC_HIERARCY_PORT);
+	mlxsw_reg_qeec_ptps_set(payload, ptps);
+}
+
 /* QRWE - QoS ReWrite Enable
  * -------------------------
  * This register configures the rewrite enable per receive port.
-- 
2.20.1


^ permalink raw reply related

* [PATCH net-next 3/8] mlxsw: reg: Add QoS PTP Shaper Configuration Register
From: Ido Schimmel @ 2019-07-04  7:07 UTC (permalink / raw)
  To: netdev; +Cc: davem, richardcochran, jiri, petrm, shalomt, mlxsw, Ido Schimmel
In-Reply-To: <20190704070740.302-1-idosch@idosch.org>

From: Shalom Toledo <shalomt@mellanox.com>

The QPSC allows advanced configuration of the PTP shapers.

Signed-off-by: Shalom Toledo <shalomt@mellanox.com>
Acked-by: Jiri Pirko <jiri@mellanox.com>
Reviewed-by: Petr Machata <petrm@mellanox.com>
Signed-off-by: Ido Schimmel <idosch@mellanox.com>
---
 drivers/net/ethernet/mellanox/mlxsw/reg.h | 107 ++++++++++++++++++++++
 1 file changed, 107 insertions(+)

diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h
index d2e2a75f7983..ead36702549a 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/reg.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h
@@ -3836,6 +3836,112 @@ mlxsw_reg_qtctm_pack(char *payload, u8 local_port, bool mc)
 	mlxsw_reg_qtctm_mc_set(payload, mc);
 }
 
+/* QPSC - QoS PTP Shaper Configuration Register
+ * --------------------------------------------
+ * The QPSC allows advanced configuration of the shapers when QEEC.ptps=1.
+ * Supported only on Spectrum-1.
+ */
+#define MLXSW_REG_QPSC_ID 0x401B
+#define MLXSW_REG_QPSC_LEN 0x28
+
+MLXSW_REG_DEFINE(qpsc, MLXSW_REG_QPSC_ID, MLXSW_REG_QPSC_LEN);
+
+enum mlxsw_reg_qpsc_port_speed {
+	MLXSW_REG_QPSC_PORT_SPEED_100M,
+	MLXSW_REG_QPSC_PORT_SPEED_1G,
+	MLXSW_REG_QPSC_PORT_SPEED_10G,
+	MLXSW_REG_QPSC_PORT_SPEED_25G,
+};
+
+/* reg_qpsc_port_speed
+ * Port speed.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, qpsc, port_speed, 0x00, 0, 4);
+
+/* reg_qpsc_shaper_time_exp
+ * The base-time-interval for updating the shapers tokens (for all hierarchies).
+ * shaper_update_rate = 2 ^ shaper_time_exp * (1 + shaper_time_mantissa) * 32nSec
+ * shaper_rate = 64bit * shaper_inc / shaper_update_rate
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, qpsc, shaper_time_exp, 0x04, 16, 4);
+
+/* reg_qpsc_shaper_time_mantissa
+ * The base-time-interval for updating the shapers tokens (for all hierarchies).
+ * shaper_update_rate = 2 ^ shaper_time_exp * (1 + shaper_time_mantissa) * 32nSec
+ * shaper_rate = 64bit * shaper_inc / shaper_update_rate
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, qpsc, shaper_time_mantissa, 0x04, 0, 5);
+
+/* reg_qpsc_shaper_inc
+ * Number of tokens added to shaper on each update.
+ * Units of 8B.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, qpsc, shaper_inc, 0x08, 0, 5);
+
+/* reg_qpsc_shaper_bs
+ * Max shaper Burst size.
+ * Burst size is 2 ^ max_shaper_bs * 512 [bits]
+ * Range is: 5..25 (from 2KB..2GB)
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, qpsc, shaper_bs, 0x0C, 0, 6);
+
+/* reg_qpsc_ptsc_we
+ * Write enable to port_to_shaper_credits.
+ * Access: WO
+ */
+MLXSW_ITEM32(reg, qpsc, ptsc_we, 0x10, 31, 1);
+
+/* reg_qpsc_port_to_shaper_credits
+ * For split ports: range 1..57
+ * For non-split ports: range 1..112
+ * Written only when ptsc_we is set.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, qpsc, port_to_shaper_credits, 0x10, 0, 8);
+
+/* reg_qpsc_ing_timestamp_inc
+ * Ingress timestamp increment.
+ * 2's complement.
+ * The timestamp of MTPPTR at ingress will be incremented by this value. Global
+ * value for all ports.
+ * Same units as used by MTPPTR.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, qpsc, ing_timestamp_inc, 0x20, 0, 32);
+
+/* reg_qpsc_egr_timestamp_inc
+ * Egress timestamp increment.
+ * 2's complement.
+ * The timestamp of MTPPTR at egress will be incremented by this value. Global
+ * value for all ports.
+ * Same units as used by MTPPTR.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, qpsc, egr_timestamp_inc, 0x24, 0, 32);
+
+static inline void
+mlxsw_reg_qpsc_pack(char *payload, enum mlxsw_reg_qpsc_port_speed port_speed,
+		    u8 shaper_time_exp, u8 shaper_time_mantissa, u8 shaper_inc,
+		    u8 shaper_bs, u8 port_to_shaper_credits,
+		    int ing_timestamp_inc, int egr_timestamp_inc)
+{
+	MLXSW_REG_ZERO(qpsc, payload);
+	mlxsw_reg_qpsc_port_speed_set(payload, port_speed);
+	mlxsw_reg_qpsc_shaper_time_exp_set(payload, shaper_time_exp);
+	mlxsw_reg_qpsc_shaper_time_mantissa_set(payload, shaper_time_mantissa);
+	mlxsw_reg_qpsc_shaper_inc_set(payload, shaper_inc);
+	mlxsw_reg_qpsc_shaper_bs_set(payload, shaper_bs);
+	mlxsw_reg_qpsc_ptsc_we_set(payload, true);
+	mlxsw_reg_qpsc_port_to_shaper_credits_set(payload, port_to_shaper_credits);
+	mlxsw_reg_qpsc_ing_timestamp_inc_set(payload, ing_timestamp_inc);
+	mlxsw_reg_qpsc_egr_timestamp_inc_set(payload, egr_timestamp_inc);
+}
+
 /* PMLP - Ports Module to Local Port Register
  * ------------------------------------------
  * Configures the assignment of modules to local ports.
@@ -10396,6 +10502,7 @@ static const struct mlxsw_reg_info *mlxsw_reg_infos[] = {
 	MLXSW_REG(qpdsm),
 	MLXSW_REG(qpdpm),
 	MLXSW_REG(qtctm),
+	MLXSW_REG(qpsc),
 	MLXSW_REG(pmlp),
 	MLXSW_REG(pmtu),
 	MLXSW_REG(ptys),
-- 
2.20.1


^ permalink raw reply related

* [PATCH net-next 4/8] mlxsw: spectrum_ptp: Set the PTP shaper parameters
From: Ido Schimmel @ 2019-07-04  7:07 UTC (permalink / raw)
  To: netdev; +Cc: davem, richardcochran, jiri, petrm, shalomt, mlxsw, Ido Schimmel
In-Reply-To: <20190704070740.302-1-idosch@idosch.org>

From: Shalom Toledo <shalomt@mellanox.com>

Set the PTP shaper parameters during the ptp_init(). For different
speeds, there are different parameters.

When the port's speed changes and PTP shaper is enabled, the firmware
changes the ETS shaper values according to the PTP shaper parameters for
this new speed.

The PTP shaper parameters array is left empty for now, will be filled in
a follow-up patch.

Signed-off-by: Shalom Toledo <shalomt@mellanox.com>
Reviewed-by: Petr Machata <petrm@mellanox.com>
Acked-by: Jiri Pirko <jiri@mellanox.com>
Signed-off-by: Ido Schimmel <idosch@mellanox.com>
---
 .../ethernet/mellanox/mlxsw/spectrum_ptp.c    | 45 +++++++++++++++++++
 1 file changed, 45 insertions(+)

diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c
index 437023d67a3b..08e62cb3d7f2 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c
@@ -753,12 +753,57 @@ static int mlxsw_sp1_ptp_mtpppc_set(struct mlxsw_sp *mlxsw_sp,
 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mtpppc), mtpppc_pl);
 }
 
+struct mlxsw_sp1_ptp_shaper_params {
+	u32 ethtool_speed;
+	enum mlxsw_reg_qpsc_port_speed port_speed;
+	u8 shaper_time_exp;
+	u8 shaper_time_mantissa;
+	u8 shaper_inc;
+	u8 shaper_bs;
+	u8 port_to_shaper_credits;
+	int ing_timestamp_inc;
+	int egr_timestamp_inc;
+};
+
+static const struct mlxsw_sp1_ptp_shaper_params
+mlxsw_sp1_ptp_shaper_params[] = {
+};
+
+#define MLXSW_SP1_PTP_SHAPER_PARAMS_LEN ARRAY_SIZE(mlxsw_sp1_ptp_shaper_params)
+
+static int mlxsw_sp1_ptp_shaper_params_set(struct mlxsw_sp *mlxsw_sp)
+{
+	const struct mlxsw_sp1_ptp_shaper_params *params;
+	char qpsc_pl[MLXSW_REG_QPSC_LEN];
+	int i, err;
+
+	for (i = 0; i < MLXSW_SP1_PTP_SHAPER_PARAMS_LEN; i++) {
+		params = &mlxsw_sp1_ptp_shaper_params[i];
+		mlxsw_reg_qpsc_pack(qpsc_pl, params->port_speed,
+				    params->shaper_time_exp,
+				    params->shaper_time_mantissa,
+				    params->shaper_inc, params->shaper_bs,
+				    params->port_to_shaper_credits,
+				    params->ing_timestamp_inc,
+				    params->egr_timestamp_inc);
+		err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qpsc), qpsc_pl);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
 struct mlxsw_sp_ptp_state *mlxsw_sp1_ptp_init(struct mlxsw_sp *mlxsw_sp)
 {
 	struct mlxsw_sp_ptp_state *ptp_state;
 	u16 message_type;
 	int err;
 
+	err = mlxsw_sp1_ptp_shaper_params_set(mlxsw_sp);
+	if (err)
+		return ERR_PTR(err);
+
 	ptp_state = kzalloc(sizeof(*ptp_state), GFP_KERNEL);
 	if (!ptp_state)
 		return ERR_PTR(-ENOMEM);
-- 
2.20.1


^ permalink raw reply related

* [PATCH net-next 6/8] mlxsw: spectrum_ptp: Enable/disable PTP shaper on a port when getting HWTSTAMP on/off
From: Ido Schimmel @ 2019-07-04  7:07 UTC (permalink / raw)
  To: netdev; +Cc: davem, richardcochran, jiri, petrm, shalomt, mlxsw, Ido Schimmel
In-Reply-To: <20190704070740.302-1-idosch@idosch.org>

From: Shalom Toledo <shalomt@mellanox.com>

In order to get more accurate hardware time stamping, the driver needs to
enable PTP shaper on the port, for speeds lower than 40 Gbps.

Enable the PTP shaper on the port when the user turns on the hardware
time stamping, and disable it when the user turns off the hardware time
stamping.

Signed-off-by: Shalom Toledo <shalomt@mellanox.com>
Reviewed-by: Petr Machata <petrm@mellanox.com>
Acked-by: Jiri Pirko <jiri@mellanox.com>
Signed-off-by: Ido Schimmel <idosch@mellanox.com>
---
 .../ethernet/mellanox/mlxsw/spectrum_ptp.c    | 52 +++++++++++++++++++
 1 file changed, 52 insertions(+)

diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c
index 08e62cb3d7f2..49aba0ce896b 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c
@@ -952,6 +952,54 @@ static int mlxsw_sp1_ptp_mtpppc_update(struct mlxsw_sp_port *mlxsw_sp_port,
 				       ing_types, egr_types);
 }
 
+static bool mlxsw_sp1_ptp_hwtstamp_enabled(struct mlxsw_sp_port *mlxsw_sp_port)
+{
+	return mlxsw_sp_port->ptp.ing_types || mlxsw_sp_port->ptp.egr_types;
+}
+
+static int
+mlxsw_sp1_ptp_port_shaper_set(struct mlxsw_sp_port *mlxsw_sp_port, bool enable)
+{
+	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+	char qeec_pl[MLXSW_REG_QEEC_LEN];
+
+	mlxsw_reg_qeec_ptps_pack(qeec_pl, mlxsw_sp_port->local_port, enable);
+	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qeec), qeec_pl);
+}
+
+static int mlxsw_sp1_ptp_port_shaper_check(struct mlxsw_sp_port *mlxsw_sp_port)
+{
+	const struct mlxsw_sp_port_type_speed_ops *port_type_speed_ops;
+	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+	char ptys_pl[MLXSW_REG_PTYS_LEN];
+	u32 eth_proto_oper, speed;
+	bool ptps = false;
+	int err, i;
+
+	if (!mlxsw_sp1_ptp_hwtstamp_enabled(mlxsw_sp_port))
+		return mlxsw_sp1_ptp_port_shaper_set(mlxsw_sp_port, false);
+
+	port_type_speed_ops = mlxsw_sp->port_type_speed_ops;
+	port_type_speed_ops->reg_ptys_eth_pack(mlxsw_sp, ptys_pl,
+					       mlxsw_sp_port->local_port, 0,
+					       false);
+	err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl);
+	if (err)
+		return err;
+	port_type_speed_ops->reg_ptys_eth_unpack(mlxsw_sp, ptys_pl, NULL, NULL,
+						 &eth_proto_oper);
+
+	speed = port_type_speed_ops->from_ptys_speed(mlxsw_sp, eth_proto_oper);
+	for (i = 0; i < MLXSW_SP1_PTP_SHAPER_PARAMS_LEN; i++) {
+		if (mlxsw_sp1_ptp_shaper_params[i].ethtool_speed == speed) {
+			ptps = true;
+			break;
+		}
+	}
+
+	return mlxsw_sp1_ptp_port_shaper_set(mlxsw_sp_port, ptps);
+}
+
 int mlxsw_sp1_ptp_hwtstamp_set(struct mlxsw_sp_port *mlxsw_sp_port,
 			       struct hwtstamp_config *config)
 {
@@ -973,6 +1021,10 @@ int mlxsw_sp1_ptp_hwtstamp_set(struct mlxsw_sp_port *mlxsw_sp_port,
 	mlxsw_sp_port->ptp.ing_types = ing_types;
 	mlxsw_sp_port->ptp.egr_types = egr_types;
 
+	err = mlxsw_sp1_ptp_port_shaper_check(mlxsw_sp_port);
+	if (err)
+		return err;
+
 	/* Notify the ioctl caller what we are actually timestamping. */
 	config->rx_filter = rx_filter;
 
-- 
2.20.1


^ permalink raw reply related

* [PATCH net-next 5/8] mlxsw: spectrum: Add new operation for getting the port's speed
From: Ido Schimmel @ 2019-07-04  7:07 UTC (permalink / raw)
  To: netdev; +Cc: davem, richardcochran, jiri, petrm, shalomt, mlxsw, Ido Schimmel
In-Reply-To: <20190704070740.302-1-idosch@idosch.org>

From: Shalom Toledo <shalomt@mellanox.com>

New operation for getting the port's speed as part of port-type-speed
operations.

Signed-off-by: Shalom Toledo <shalomt@mellanox.com>
Acked-by: Jiri Pirko <jiri@mellanox.com>
Reviewed-by: Petr Machata <petrm@mellanox.com>
Signed-off-by: Ido Schimmel <idosch@mellanox.com>
---
 .../net/ethernet/mellanox/mlxsw/spectrum.c    | 68 +++++++++++--------
 .../net/ethernet/mellanox/mlxsw/spectrum.h    |  1 +
 2 files changed, 41 insertions(+), 28 deletions(-)

diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
index 67133ba53015..7cfca2be09fc 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
@@ -2655,28 +2655,33 @@ mlxsw_sp1_from_ptys_link(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto,
 	}
 }
 
+static u32
+mlxsw_sp1_from_ptys_speed(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto)
+{
+	int i;
+
+	for (i = 0; i < MLXSW_SP1_PORT_LINK_MODE_LEN; i++) {
+		if (ptys_eth_proto & mlxsw_sp1_port_link_mode[i].mask)
+			return mlxsw_sp1_port_link_mode[i].speed;
+	}
+
+	return SPEED_UNKNOWN;
+}
+
 static void
 mlxsw_sp1_from_ptys_speed_duplex(struct mlxsw_sp *mlxsw_sp, bool carrier_ok,
 				 u32 ptys_eth_proto,
 				 struct ethtool_link_ksettings *cmd)
 {
-	u32 speed = SPEED_UNKNOWN;
-	u8 duplex = DUPLEX_UNKNOWN;
-	int i;
+	cmd->base.speed = SPEED_UNKNOWN;
+	cmd->base.duplex = DUPLEX_UNKNOWN;
 
 	if (!carrier_ok)
-		goto out;
+		return;
 
-	for (i = 0; i < MLXSW_SP1_PORT_LINK_MODE_LEN; i++) {
-		if (ptys_eth_proto & mlxsw_sp1_port_link_mode[i].mask) {
-			speed = mlxsw_sp1_port_link_mode[i].speed;
-			duplex = DUPLEX_FULL;
-			break;
-		}
-	}
-out:
-	cmd->base.speed = speed;
-	cmd->base.duplex = duplex;
+	cmd->base.speed = mlxsw_sp1_from_ptys_speed(mlxsw_sp, ptys_eth_proto);
+	if (cmd->base.speed != SPEED_UNKNOWN)
+		cmd->base.duplex = DUPLEX_FULL;
 }
 
 static u32
@@ -2747,6 +2752,7 @@ static const struct mlxsw_sp_port_type_speed_ops
 mlxsw_sp1_port_type_speed_ops = {
 	.from_ptys_supported_port	= mlxsw_sp1_from_ptys_supported_port,
 	.from_ptys_link			= mlxsw_sp1_from_ptys_link,
+	.from_ptys_speed		= mlxsw_sp1_from_ptys_speed,
 	.from_ptys_speed_duplex		= mlxsw_sp1_from_ptys_speed_duplex,
 	.to_ptys_advert_link		= mlxsw_sp1_to_ptys_advert_link,
 	.to_ptys_speed			= mlxsw_sp1_to_ptys_speed,
@@ -2997,28 +3003,33 @@ mlxsw_sp2_from_ptys_link(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto,
 	}
 }
 
+static u32
+mlxsw_sp2_from_ptys_speed(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto)
+{
+	int i;
+
+	for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) {
+		if (ptys_eth_proto & mlxsw_sp2_port_link_mode[i].mask)
+			return mlxsw_sp2_port_link_mode[i].speed;
+	}
+
+	return SPEED_UNKNOWN;
+}
+
 static void
 mlxsw_sp2_from_ptys_speed_duplex(struct mlxsw_sp *mlxsw_sp, bool carrier_ok,
 				 u32 ptys_eth_proto,
 				 struct ethtool_link_ksettings *cmd)
 {
-	u32 speed = SPEED_UNKNOWN;
-	u8 duplex = DUPLEX_UNKNOWN;
-	int i;
+	cmd->base.speed = SPEED_UNKNOWN;
+	cmd->base.duplex = DUPLEX_UNKNOWN;
 
 	if (!carrier_ok)
-		goto out;
+		return;
 
-	for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) {
-		if (ptys_eth_proto & mlxsw_sp2_port_link_mode[i].mask) {
-			speed = mlxsw_sp2_port_link_mode[i].speed;
-			duplex = DUPLEX_FULL;
-			break;
-		}
-	}
-out:
-	cmd->base.speed = speed;
-	cmd->base.duplex = duplex;
+	cmd->base.speed = mlxsw_sp2_from_ptys_speed(mlxsw_sp, ptys_eth_proto);
+	if (cmd->base.speed != SPEED_UNKNOWN)
+		cmd->base.duplex = DUPLEX_FULL;
 }
 
 static bool
@@ -3129,6 +3140,7 @@ static const struct mlxsw_sp_port_type_speed_ops
 mlxsw_sp2_port_type_speed_ops = {
 	.from_ptys_supported_port	= mlxsw_sp2_from_ptys_supported_port,
 	.from_ptys_link			= mlxsw_sp2_from_ptys_link,
+	.from_ptys_speed		= mlxsw_sp2_from_ptys_speed,
 	.from_ptys_speed_duplex		= mlxsw_sp2_from_ptys_speed_duplex,
 	.to_ptys_advert_link		= mlxsw_sp2_to_ptys_advert_link,
 	.to_ptys_speed			= mlxsw_sp2_to_ptys_speed,
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
index 7f8427c1a997..c21cd1a425c7 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
@@ -279,6 +279,7 @@ struct mlxsw_sp_port_type_speed_ops {
 					 struct ethtool_link_ksettings *cmd);
 	void (*from_ptys_link)(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto,
 			       unsigned long *mode);
+	u32 (*from_ptys_speed)(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto);
 	void (*from_ptys_speed_duplex)(struct mlxsw_sp *mlxsw_sp,
 				       bool carrier_ok, u32 ptys_eth_proto,
 				       struct ethtool_link_ksettings *cmd);
-- 
2.20.1


^ permalink raw reply related

* [PATCH net-next 7/8] mlxsw: spectrum: Set up PTP shaper when port status has changed
From: Ido Schimmel @ 2019-07-04  7:07 UTC (permalink / raw)
  To: netdev; +Cc: davem, richardcochran, jiri, petrm, shalomt, mlxsw, Ido Schimmel
In-Reply-To: <20190704070740.302-1-idosch@idosch.org>

From: Shalom Toledo <shalomt@mellanox.com>

When getting port up down event (PUDE), change the PTP shaper
configuration based on hardware time stamping on/off and the port's
speed.

Signed-off-by: Shalom Toledo <shalomt@mellanox.com>
Reviewed-by: Petr Machata <petrm@mellanox.com>
Acked-by: Jiri Pirko <jiri@mellanox.com>
Signed-off-by: Ido Schimmel <idosch@mellanox.com>
---
 drivers/net/ethernet/mellanox/mlxsw/spectrum.c  |  8 ++++++++
 drivers/net/ethernet/mellanox/mlxsw/spectrum.h  |  1 +
 .../net/ethernet/mellanox/mlxsw/spectrum_ptp.c  | 17 +++++++++++++++++
 .../net/ethernet/mellanox/mlxsw/spectrum_ptp.h  | 10 ++++++++++
 4 files changed, 36 insertions(+)

diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
index 7cfca2be09fc..ce285fbeebd3 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
@@ -171,6 +171,7 @@ struct mlxsw_sp_ptp_ops {
 			    struct hwtstamp_config *config);
 	int (*hwtstamp_set)(struct mlxsw_sp_port *mlxsw_sp_port,
 			    struct hwtstamp_config *config);
+	void (*shaper_work)(struct work_struct *work);
 	int (*get_ts_info)(struct mlxsw_sp *mlxsw_sp,
 			   struct ethtool_ts_info *info);
 };
@@ -3716,6 +3717,9 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port,
 	}
 	mlxsw_sp_port->default_vlan = mlxsw_sp_port_vlan;
 
+	INIT_DELAYED_WORK(&mlxsw_sp_port->ptp.shaper_dw,
+			  mlxsw_sp->ptp_ops->shaper_work);
+
 	mlxsw_sp->ports[local_port] = mlxsw_sp_port;
 	err = register_netdev(dev);
 	if (err) {
@@ -3770,6 +3774,7 @@ static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port)
 	struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp->ports[local_port];
 
 	cancel_delayed_work_sync(&mlxsw_sp_port->periodic_hw_stats.update_dw);
+	cancel_delayed_work_sync(&mlxsw_sp_port->ptp.shaper_dw);
 	mlxsw_sp_port_ptp_clear(mlxsw_sp_port);
 	mlxsw_core_port_clear(mlxsw_sp->core, local_port, mlxsw_sp);
 	unregister_netdev(mlxsw_sp_port->dev); /* This calls ndo_stop */
@@ -4055,6 +4060,7 @@ static void mlxsw_sp_pude_event_func(const struct mlxsw_reg_info *reg,
 	if (status == MLXSW_PORT_OPER_STATUS_UP) {
 		netdev_info(mlxsw_sp_port->dev, "link up\n");
 		netif_carrier_on(mlxsw_sp_port->dev);
+		mlxsw_core_schedule_dw(&mlxsw_sp_port->ptp.shaper_dw, 0);
 	} else {
 		netdev_info(mlxsw_sp_port->dev, "link down\n");
 		netif_carrier_off(mlxsw_sp_port->dev);
@@ -4572,6 +4578,7 @@ static const struct mlxsw_sp_ptp_ops mlxsw_sp1_ptp_ops = {
 	.transmitted	= mlxsw_sp1_ptp_transmitted,
 	.hwtstamp_get	= mlxsw_sp1_ptp_hwtstamp_get,
 	.hwtstamp_set	= mlxsw_sp1_ptp_hwtstamp_set,
+	.shaper_work	= mlxsw_sp1_ptp_shaper_work,
 	.get_ts_info	= mlxsw_sp1_ptp_get_ts_info,
 };
 
@@ -4584,6 +4591,7 @@ static const struct mlxsw_sp_ptp_ops mlxsw_sp2_ptp_ops = {
 	.transmitted	= mlxsw_sp2_ptp_transmitted,
 	.hwtstamp_get	= mlxsw_sp2_ptp_hwtstamp_get,
 	.hwtstamp_set	= mlxsw_sp2_ptp_hwtstamp_set,
+	.shaper_work	= mlxsw_sp2_ptp_shaper_work,
 	.get_ts_info	= mlxsw_sp2_ptp_get_ts_info,
 };
 
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
index c21cd1a425c7..abbb563db440 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
@@ -267,6 +267,7 @@ struct mlxsw_sp_port {
 	struct mlxsw_sp_acl_block *ing_acl_block;
 	struct mlxsw_sp_acl_block *eg_acl_block;
 	struct {
+		struct delayed_work shaper_dw;
 		struct hwtstamp_config hwtstamp_config;
 		u16 ing_types;
 		u16 egr_types;
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c
index 49aba0ce896b..0f7c4bd22a45 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c
@@ -1000,6 +1000,23 @@ static int mlxsw_sp1_ptp_port_shaper_check(struct mlxsw_sp_port *mlxsw_sp_port)
 	return mlxsw_sp1_ptp_port_shaper_set(mlxsw_sp_port, ptps);
 }
 
+void mlxsw_sp1_ptp_shaper_work(struct work_struct *work)
+{
+	struct delayed_work *dwork = to_delayed_work(work);
+	struct mlxsw_sp_port *mlxsw_sp_port;
+	int err;
+
+	mlxsw_sp_port = container_of(dwork, struct mlxsw_sp_port,
+				     ptp.shaper_dw);
+
+	if (!mlxsw_sp1_ptp_hwtstamp_enabled(mlxsw_sp_port))
+		return;
+
+	err = mlxsw_sp1_ptp_port_shaper_check(mlxsw_sp_port);
+	if (err)
+		netdev_err(mlxsw_sp_port->dev, "Failed to set up PTP shaper\n");
+}
+
 int mlxsw_sp1_ptp_hwtstamp_set(struct mlxsw_sp_port *mlxsw_sp_port,
 			       struct hwtstamp_config *config)
 {
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.h
index b23abfc0bd76..72e55f6926b9 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.h
@@ -54,6 +54,8 @@ int mlxsw_sp1_ptp_hwtstamp_get(struct mlxsw_sp_port *mlxsw_sp_port,
 int mlxsw_sp1_ptp_hwtstamp_set(struct mlxsw_sp_port *mlxsw_sp_port,
 			       struct hwtstamp_config *config);
 
+void mlxsw_sp1_ptp_shaper_work(struct work_struct *work);
+
 int mlxsw_sp1_ptp_get_ts_info(struct mlxsw_sp *mlxsw_sp,
 			      struct ethtool_ts_info *info);
 
@@ -113,6 +115,10 @@ mlxsw_sp1_ptp_hwtstamp_set(struct mlxsw_sp_port *mlxsw_sp_port,
 	return -EOPNOTSUPP;
 }
 
+static inline void mlxsw_sp1_ptp_shaper_work(struct work_struct *work)
+{
+}
+
 static inline int mlxsw_sp1_ptp_get_ts_info(struct mlxsw_sp *mlxsw_sp,
 					    struct ethtool_ts_info *info)
 {
@@ -167,6 +173,10 @@ mlxsw_sp2_ptp_hwtstamp_set(struct mlxsw_sp_port *mlxsw_sp_port,
 	return -EOPNOTSUPP;
 }
 
+static inline void mlxsw_sp2_ptp_shaper_work(struct work_struct *work)
+{
+}
+
 static inline int mlxsw_sp2_ptp_get_ts_info(struct mlxsw_sp *mlxsw_sp,
 					    struct ethtool_ts_info *info)
 {
-- 
2.20.1


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox