* [PATCH bpf-next 1/3] bpf: add bpf_progenyof helper
From: Javier Honduvilla Coto @ 2019-02-26 22:36 UTC (permalink / raw)
To: netdev; +Cc: yhs, kernel-team
In-Reply-To: <20190226223651.3166820-1-javierhonduco@fb.com>
This patch adds the bpf_progenyof helper which receives a PID and returns
1 if the process currently being executed is in the process hierarchy
including itself or 0 if not.
This is very useful in tracing programs when we want to filter by a
given PID and all the children it might spawn. The current workarounds
most people implement for this purpose have issues:
- Attaching to process spawning syscalls and dynamically add those PIDs
to some bpf map that would be used to filter is cumbersome and
potentially racy.
- Unrolling some loop to perform what this helper is doing consumes lots
of instructions. That and the impossibility to jump backwards makes it
really hard to be correct in really large process chains.
Signed-off-by: Javier Honduvilla Coto <javierhonduco@fb.com>
---
include/linux/bpf.h | 1 +
include/uapi/linux/bpf.h | 3 ++-
kernel/bpf/core.c | 1 +
kernel/bpf/helpers.c | 29 +++++++++++++++++++++++++++++
kernel/trace/bpf_trace.c | 2 ++
5 files changed, 35 insertions(+), 1 deletion(-)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index de18227b3d95..447395ba202b 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -921,6 +921,7 @@ extern const struct bpf_func_proto bpf_sk_redirect_map_proto;
extern const struct bpf_func_proto bpf_spin_lock_proto;
extern const struct bpf_func_proto bpf_spin_unlock_proto;
extern const struct bpf_func_proto bpf_get_local_storage_proto;
+extern const struct bpf_func_proto bpf_progenyof_proto;
/* Shared helpers among cBPF and eBPF. */
void bpf_user_rnd_init_once(void);
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index bcdd2474eee7..804e4218eb28 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -2457,7 +2457,8 @@ union bpf_attr {
FN(spin_lock), \
FN(spin_unlock), \
FN(sk_fullsock), \
- FN(tcp_sock),
+ FN(tcp_sock), \
+ FN(progenyof),
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
* function eBPF program intends to call
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index ef88b167959d..69e209fbd128 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -2015,6 +2015,7 @@ const struct bpf_func_proto bpf_get_current_uid_gid_proto __weak;
const struct bpf_func_proto bpf_get_current_comm_proto __weak;
const struct bpf_func_proto bpf_get_current_cgroup_id_proto __weak;
const struct bpf_func_proto bpf_get_local_storage_proto __weak;
+const struct bpf_func_proto bpf_progenyof_proto __weak;
const struct bpf_func_proto * __weak bpf_get_trace_printk_proto(void)
{
diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index a411fc17d265..3899787e8dbf 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -18,6 +18,7 @@
#include <linux/sched.h>
#include <linux/uidgid.h>
#include <linux/filter.h>
+#include <linux/init_task.h>
/* If kernel subsystem is allowing eBPF programs to call this function,
* inside its own verifier_ops->get_func_proto() callback it should return
@@ -364,3 +365,31 @@ const struct bpf_func_proto bpf_get_local_storage_proto = {
};
#endif
#endif
+
+BPF_CALL_1(bpf_progenyof, int, pid)
+{
+ int result = 0;
+ struct task_struct *task = current;
+
+ if (unlikely(!task))
+ return -EINVAL;
+
+ rcu_read_lock();
+ while (task != &init_task) {
+ if (task->pid == pid) {
+ result = 1;
+ break;
+ }
+ task = rcu_dereference(task->real_parent);
+ }
+ rcu_read_unlock();
+
+ return result;
+}
+
+const struct bpf_func_proto bpf_progenyof_proto = {
+ .func = bpf_progenyof,
+ .gpl_only = false,
+ .ret_type = RET_INTEGER,
+ .arg1_type = ARG_ANYTHING,
+};
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index f1a86a0d881d..8602ae83c799 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -600,6 +600,8 @@ tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
return &bpf_get_prandom_u32_proto;
case BPF_FUNC_probe_read_str:
return &bpf_probe_read_str_proto;
+ case BPF_FUNC_progenyof:
+ return &bpf_progenyof_proto;
#ifdef CONFIG_CGROUPS
case BPF_FUNC_get_current_cgroup_id:
return &bpf_get_current_cgroup_id_proto;
--
2.17.1
^ permalink raw reply related
* [PATCH bpf-next 0/3] bpf: add progenyof helper
From: Javier Honduvilla Coto @ 2019-02-26 22:36 UTC (permalink / raw)
To: netdev; +Cc: yhs, kernel-team
Hi all,
This patch add the bpf_progenyof helper which receives a PID and returns
1 if the process currently being executed is in the process hierarchy,
including itself or 0 if not.
This is very useful in tracing programs when we want to filter by a
given PID and all the children it might have. The current workarounds
most people implement for this purpose have issues:
- Attaching to process spawning syscalls and dynamically add those PIDs
to some bpf map that would be used to filter is cumbersome and
potentially racy.
- Unrolling some loop to perform what this helper is doing consumes lots
of instructions. That and the impossibility to jump backwards makes it
really hard to be correct in really large process chains.
Let me know what do you think!
Thanks,
Javier Honduvilla Coto (3):
bpf: add bpf_progenyof helper
bpf: sync kernel uapi headers
bpf: add tests for bpf_progenyof
include/linux/bpf.h | 1 +
include/uapi/linux/bpf.h | 3 +-
kernel/bpf/core.c | 1 +
kernel/bpf/helpers.c | 29 ++
kernel/trace/bpf_trace.c | 2 +
tools/include/uapi/linux/bpf.h | 11 +-
tools/testing/selftests/bpf/.gitignore | 1 +
tools/testing/selftests/bpf/Makefile | 2 +-
tools/testing/selftests/bpf/bpf_helpers.h | 1 +
.../selftests/bpf/progs/test_progenyof_kern.c | 46 ++++
.../selftests/bpf/test_progenyof_user.c | 249 ++++++++++++++++++
11 files changed, 343 insertions(+), 3 deletions(-)
create mode 100644 tools/testing/selftests/bpf/progs/test_progenyof_kern.c
create mode 100644 tools/testing/selftests/bpf/test_progenyof_user.c
--
2.17.1
^ permalink raw reply
* [PATCH bpf-next 3/3] bpf: add tests for bpf_progenyof
From: Javier Honduvilla Coto @ 2019-02-26 22:36 UTC (permalink / raw)
To: netdev; +Cc: yhs, kernel-team
In-Reply-To: <20190226223651.3166820-1-javierhonduco@fb.com>
Adding the following test cases:
- progenyof(current->pid) == 1
- progenyof(current->real_parent->pid) == 1
- progenyof(1) == 1
- progenyof(0) == 0
- progenyof(current->children[0]->pid) == 0
Signed-off-by: Javier Honduvilla Coto <javierhonduco@fb.com>
---
tools/testing/selftests/bpf/.gitignore | 1 +
tools/testing/selftests/bpf/Makefile | 2 +-
tools/testing/selftests/bpf/bpf_helpers.h | 1 +
.../selftests/bpf/progs/test_progenyof_kern.c | 46 ++++
.../selftests/bpf/test_progenyof_user.c | 249 ++++++++++++++++++
5 files changed, 298 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/bpf/progs/test_progenyof_kern.c
create mode 100644 tools/testing/selftests/bpf/test_progenyof_user.c
diff --git a/tools/testing/selftests/bpf/.gitignore b/tools/testing/selftests/bpf/.gitignore
index e47168d1257d..677be3b05cd2 100644
--- a/tools/testing/selftests/bpf/.gitignore
+++ b/tools/testing/selftests/bpf/.gitignore
@@ -30,3 +30,4 @@ test_section_names
test_tcpnotify_user
test_libbpf
alu32
+test_progenyof_user
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
index ccffaa0a0787..8a8d4b2a5b74 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -23,7 +23,7 @@ TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test
test_align test_verifier_log test_dev_cgroup test_tcpbpf_user \
test_sock test_btf test_sockmap test_lirc_mode2_user get_cgroup_id_user \
test_socket_cookie test_cgroup_storage test_select_reuseport test_section_names \
- test_netcnt test_tcpnotify_user test_sock_fields
+ test_netcnt test_tcpnotify_user test_sock_fields test_progenyof_user
BPF_OBJ_FILES = $(patsubst %.c,%.o, $(notdir $(wildcard progs/*.c)))
TEST_GEN_FILES = $(BPF_OBJ_FILES)
diff --git a/tools/testing/selftests/bpf/bpf_helpers.h b/tools/testing/selftests/bpf/bpf_helpers.h
index d9999f1ed1d2..1f3e0d1ad159 100644
--- a/tools/testing/selftests/bpf/bpf_helpers.h
+++ b/tools/testing/selftests/bpf/bpf_helpers.h
@@ -180,6 +180,7 @@ static struct bpf_sock *(*bpf_sk_fullsock)(struct bpf_sock *sk) =
(void *) BPF_FUNC_sk_fullsock;
static struct bpf_tcp_sock *(*bpf_tcp_sock)(struct bpf_sock *sk) =
(void *) BPF_FUNC_tcp_sock;
+static int (*bpf_progenyof)(int pid) = (void *) BPF_FUNC_progenyof;
/* llvm builtin functions that eBPF C program may use to
* emit BPF_LD_ABS and BPF_LD_IND instructions
diff --git a/tools/testing/selftests/bpf/progs/test_progenyof_kern.c b/tools/testing/selftests/bpf/progs/test_progenyof_kern.c
new file mode 100644
index 000000000000..c4be2c794d83
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_progenyof_kern.c
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/bpf.h>
+#include "bpf_helpers.h"
+
+struct bpf_map_def SEC("maps") pidmap = {
+ .type = BPF_MAP_TYPE_ARRAY,
+ .key_size = sizeof(__u32),
+ .value_size = sizeof(__u32),
+ .max_entries = 2,
+};
+
+struct bpf_map_def SEC("maps") resultmap = {
+ .type = BPF_MAP_TYPE_ARRAY,
+ .key_size = sizeof(__u32),
+ .value_size = sizeof(__u32),
+ .max_entries = 1,
+};
+
+SEC("tracepoint/syscalls/sys_enter_open")
+int trace(void *ctx)
+{
+ __u32 pid = bpf_get_current_pid_tgid();
+ __u32 current_key = 0, ancestor_key = 1, *expected_pid, *ancestor_pid;
+ __u32 *val;
+
+ expected_pid = bpf_map_lookup_elem(&pidmap, ¤t_key);
+ if (!expected_pid || *expected_pid != pid)
+ return 0;
+
+ ancestor_pid = bpf_map_lookup_elem(&pidmap, &ancestor_key);
+ if (!ancestor_pid)
+ return 0;
+
+ if (!bpf_progenyof(*ancestor_pid))
+ return 0;
+
+ val = bpf_map_lookup_elem(&resultmap, ¤t_key);
+ if (val)
+ *val = *ancestor_pid;
+
+ return 0;
+}
+
+char _license[] SEC("license") = "GPL";
+__u32 _version SEC("version") = 1;
diff --git a/tools/testing/selftests/bpf/test_progenyof_user.c b/tools/testing/selftests/bpf/test_progenyof_user.c
new file mode 100644
index 000000000000..0f9572986a11
--- /dev/null
+++ b/tools/testing/selftests/bpf/test_progenyof_user.c
@@ -0,0 +1,249 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <syscall.h>
+#include <unistd.h>
+#include <linux/perf_event.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <bpf/bpf.h>
+#include <bpf/libbpf.h>
+
+#define CHECK(condition, tag, format...) \
+ ({ \
+ int __ret = !!(condition); \
+ if (__ret) { \
+ printf("%s:FAIL:%s ", __func__, tag); \
+ printf(format); \
+ } else { \
+ printf("%s:PASS:%s\n", __func__, tag); \
+ } \
+ __ret; \
+ })
+
+static int bpf_find_map(const char *test, struct bpf_object *obj,
+ const char *name)
+{
+ struct bpf_map *map;
+
+ map = bpf_object__find_map_by_name(obj, name);
+ if (!map)
+ return -1;
+ return bpf_map__fd(map);
+}
+
+int main(int argc, char **argv)
+{
+ const char *probe_name = "syscalls/sys_enter_open";
+ const char *file = "test_progenyof_kern.o";
+ int err, bytes, efd, prog_fd, pmu_fd;
+ int resultmap_fd, pidmap_fd;
+ struct perf_event_attr attr = {};
+ struct bpf_object *obj;
+ __u32 retrieved_ancestor_pid = 0;
+ __u32 key = 0, pid;
+ int exit_code = EXIT_FAILURE;
+ char buf[256];
+
+ int child_pid, ancestor_pid, root_fd;
+ __u32 ancestor_key = 1;
+ int pipefd[2];
+ char marker[1];
+
+ err = bpf_prog_load(file, BPF_PROG_TYPE_TRACEPOINT, &obj, &prog_fd);
+ if (CHECK(err, "bpf_prog_load", "err %d errno %d\n", err, errno))
+ goto fail;
+
+ resultmap_fd = bpf_find_map(__func__, obj, "resultmap");
+ if (CHECK(resultmap_fd < 0, "bpf_find_map", "err %d errno %d\n",
+ resultmap_fd, errno))
+ goto close_prog;
+
+ pidmap_fd = bpf_find_map(__func__, obj, "pidmap");
+ if (CHECK(pidmap_fd < 0, "bpf_find_map", "err %d errno %d\n", pidmap_fd,
+ errno))
+ goto close_prog;
+
+ pid = getpid();
+ bpf_map_update_elem(pidmap_fd, &key, &pid, 0);
+ bpf_map_update_elem(pidmap_fd, &ancestor_key, &pid, 0);
+
+ snprintf(buf, sizeof(buf), "/sys/kernel/debug/tracing/events/%s/id",
+ probe_name);
+ efd = open(buf, O_RDONLY, 0);
+ if (CHECK(efd < 0, "open", "err %d errno %d\n", efd, errno))
+ goto close_prog;
+ bytes = read(efd, buf, sizeof(buf));
+ close(efd);
+ if (CHECK(bytes <= 0 || bytes >= sizeof(buf), "read",
+ "bytes %d errno %d\n", bytes, errno))
+ goto close_prog;
+
+ attr.config = strtol(buf, NULL, 0);
+ attr.type = PERF_TYPE_TRACEPOINT;
+ attr.sample_type = PERF_SAMPLE_RAW;
+ attr.sample_period = 1;
+ attr.wakeup_events = 1;
+
+ pmu_fd = syscall(__NR_perf_event_open, &attr, getpid(), -1, -1, 0);
+ if (CHECK(pmu_fd < 0, "perf_event_open", "err %d errno %d\n", pmu_fd,
+ errno))
+ goto close_prog;
+
+ err = ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0);
+ if (CHECK(err, "perf_event_ioc_enable", "err %d errno %d\n", err,
+ errno))
+ goto close_pmu;
+
+ err = ioctl(pmu_fd, PERF_EVENT_IOC_SET_BPF, prog_fd);
+ if (CHECK(err, "perf_event_ioc_set_bpf", "err %d errno %d\n", err,
+ errno))
+ goto close_pmu;
+
+ // Test on itself: progenyof(current->pid) is true
+ bpf_map_update_elem(pidmap_fd, &key, &pid, 0);
+ bpf_map_update_elem(pidmap_fd, &ancestor_key, &pid, 0);
+ bpf_map_update_elem(resultmap_fd, &key, &key, 0);
+
+ root_fd = open("/", O_RDONLY);
+ if (CHECK(efd < 0, "open", "errno %d\n", errno))
+ goto close_prog;
+ close(root_fd);
+
+ err = bpf_map_lookup_elem(resultmap_fd, &key, &retrieved_ancestor_pid);
+ if (CHECK(err, "bpf_map_lookup_elem", "err %d errno %d\n", err, errno))
+ goto close_pmu;
+ if (CHECK(retrieved_ancestor_pid != pid,
+ "progenyof is true with same pid", "%d == %d\n",
+ retrieved_ancestor_pid, pid))
+ goto close_pmu;
+
+ // Test that PID 1 is among our progeny
+ bpf_map_update_elem(pidmap_fd, &key, &pid, 0);
+ ancestor_pid = 1;
+ bpf_map_update_elem(pidmap_fd, &ancestor_key, &ancestor_pid, 0);
+ bpf_map_update_elem(resultmap_fd, &key, &key, 0);
+
+ root_fd = open("/", O_RDONLY);
+ if (CHECK(efd < 0, "open", "errno %d\n", errno))
+ goto close_prog;
+ close(root_fd);
+
+ err = bpf_map_lookup_elem(resultmap_fd, &key, &retrieved_ancestor_pid);
+ if (CHECK(err, "bpf_map_lookup_elem", "err %d errno %d\n", err, errno))
+ goto close_pmu;
+ if (CHECK(retrieved_ancestor_pid != ancestor_pid,
+ "progenyof reaches init", "%d == %d\n",
+ retrieved_ancestor_pid, ancestor_pid))
+ goto close_pmu;
+
+ // Test we don't go over PID 1
+ bpf_map_update_elem(pidmap_fd, &key, &pid, 0);
+ ancestor_pid = 0;
+ bpf_map_update_elem(pidmap_fd, &ancestor_key, &ancestor_pid, 0);
+ bpf_map_update_elem(resultmap_fd, &key, &key, 0);
+
+ root_fd = open("/", O_RDONLY);
+ if (CHECK(efd < 0, "open", "errno %d\n", errno))
+ goto close_prog;
+ close(root_fd);
+
+ err = bpf_map_lookup_elem(resultmap_fd, &key, &retrieved_ancestor_pid);
+ if (CHECK(err, "bpf_map_lookup_elem", "err %d errno %d\n", err, errno))
+ goto close_pmu;
+ if (CHECK(retrieved_ancestor_pid != 0,
+ "progenyof does not go over init", "%d == %d\n",
+ retrieved_ancestor_pid, 0))
+ goto close_pmu;
+
+ // Test that we are the progeny of our child
+ pipe(pipefd);
+ child_pid = fork();
+ if (child_pid == -1) {
+ printf("fork failed\n");
+ goto close_pmu;
+ } else if (child_pid == 0) {
+ close(pipefd[1]);
+ read(pipefd[0], &marker, 1);
+
+ root_fd = open("/", O_RDONLY);
+ if (CHECK(efd < 0, "open", "errno %d\n", errno))
+ goto close_prog;
+ close(root_fd);
+
+ close(pipefd[0]);
+ _exit(EXIT_SUCCESS);
+ } else {
+ close(pipefd[0]);
+ bpf_map_update_elem(resultmap_fd, &key, &key, 0);
+ bpf_map_update_elem(pidmap_fd, &key, &child_pid, 0);
+ bpf_map_update_elem(pidmap_fd, &ancestor_key, &pid, 0);
+
+ write(pipefd[1], &marker, 1);
+ wait(NULL);
+ close(pipefd[1]);
+
+ err = bpf_map_lookup_elem(resultmap_fd, &key,
+ &retrieved_ancestor_pid);
+ if (CHECK(err, "bpf_map_lookup_elem", "err %d errno %d\n", err,
+ errno))
+ goto close_pmu;
+ if (CHECK(retrieved_ancestor_pid != pid, "progenyof of parent",
+ "%d == %d\n", retrieved_ancestor_pid, pid))
+ goto close_pmu;
+ }
+
+ // Test that a child of ours doesn't belong to our progeny
+ bpf_map_update_elem(pidmap_fd, &key, &pid, 0);
+ bpf_map_update_elem(resultmap_fd, &key, &key, 0);
+
+ pipe(pipefd);
+ child_pid = fork();
+ if (child_pid == -1) {
+ printf("fork failed\n");
+ goto close_pmu;
+ } else if (child_pid == 0) {
+ close(pipefd[1]);
+ read(pipefd[0], marker, 1);
+ close(pipefd[0]);
+ _exit(EXIT_SUCCESS);
+ } else {
+ close(pipefd[0]);
+
+ bpf_map_update_elem(pidmap_fd, &ancestor_key, &child_pid, 0);
+
+ root_fd = open("/", O_RDONLY);
+ if (CHECK(efd < 0, "open", "errno %d\n", errno))
+ goto close_prog;
+ close(root_fd);
+
+ write(pipefd[1], marker, 1);
+ wait(NULL);
+ close(pipefd[1]);
+
+ err = bpf_map_lookup_elem(resultmap_fd, &key,
+ &retrieved_ancestor_pid);
+ if (CHECK(err, "bpf_map_lookup_elem", "err %d errno %d\n", err,
+ errno))
+ goto close_pmu;
+ if (CHECK(retrieved_ancestor_pid != 0, "progenyof of child",
+ "%d == %d\n", retrieved_ancestor_pid, 0))
+ goto close_pmu;
+ }
+
+ exit_code = EXIT_SUCCESS;
+ printf("%s:PASS\n", argv[0]);
+
+close_pmu:
+ close(pmu_fd);
+close_prog:
+ bpf_object__close(obj);
+fail:
+ return exit_code;
+}
--
2.17.1
^ permalink raw reply related
* [PATCH bpf-next 2/3] bpf: sync kernel uapi headers
From: Javier Honduvilla Coto @ 2019-02-26 22:36 UTC (permalink / raw)
To: netdev; +Cc: yhs, kernel-team
In-Reply-To: <20190226223651.3166820-1-javierhonduco@fb.com>
Sync kernel uapi headers.
Signed-off-by: Javier Honduvilla Coto <javierhonduco@fb.com>
---
tools/include/uapi/linux/bpf.h | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index bcdd2474eee7..354ec295864c 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -2359,6 +2359,14 @@ union bpf_attr {
* Return
* A **struct bpf_tcp_sock** pointer on success, or NULL in
* case of failure.
+ *
+ * int bpf_progenyof(int pid)
+ * Description
+ * This helper is useful in programs that want to filter events
+ * happening to a pid of any of its descendants.
+ * Return
+ * 1 if the currently executing process' pid is in the process
+ * hierarchy of the passed pid. 0 Otherwise.
*/
#define __BPF_FUNC_MAPPER(FN) \
FN(unspec), \
@@ -2457,7 +2465,8 @@ union bpf_attr {
FN(spin_lock), \
FN(spin_unlock), \
FN(sk_fullsock), \
- FN(tcp_sock),
+ FN(tcp_sock), \
+ FN(progenyof),
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
* function eBPF program intends to call
--
2.17.1
^ permalink raw reply related
* Re: [PATCH net-next] net: sched: set dedicated tcf_walker flag when tp is empty
From: Cong Wang @ 2019-02-26 22:38 UTC (permalink / raw)
To: Vlad Buslov
Cc: Linux Kernel Network Developers, Jamal Hadi Salim, Jiri Pirko,
David Miller
In-Reply-To: <vbf4l8qmvt4.fsf@mellanox.com>
On Tue, Feb 26, 2019 at 7:08 AM Vlad Buslov <vladbu@mellanox.com> wrote:
>
>
> On Mon 25 Feb 2019 at 22:52, Cong Wang <xiyou.wangcong@gmail.com> wrote:
> > On Mon, Feb 25, 2019 at 7:38 AM Vlad Buslov <vladbu@mellanox.com> wrote:
> >>
> >> Using tcf_walker->stop flag to determine when tcf_walker->fn() was called
> >> at least once is unreliable. Some classifiers set 'stop' flag on error
> >> before calling walker callback, other classifiers used to call it with NULL
> >> filter pointer when empty. In order to prevent further regressions, extend
> >> tcf_walker structure with dedicated 'nonempty' flag. Set this flag in
> >> tcf_walker->fn() implementation that is used to check if classifier has
> >> filters configured.
> >
> >
> > So, after this patch commits like 31a998487641 ("net: sched: fw: don't
> > set arg->stop in fw_walk() when empty") can be reverted??
>
> Yes, it is safe now to revert following commits:
>
> 3027ff41f67c ("net: sched: route: don't set arg->stop in route4_walk() when empty")
> 31a998487641 ("net: sched: fw: don't set arg->stop in fw_walk() when empty")
Yeah, and probably commit d66022cd1623
("net: sched: matchall: verify that filter is not NULL in mall_walk()").
Please send a patch to revert them all.
Thanks.
^ permalink raw reply
* Re: [PATCH net-next v2 1/5] net/mlx5e: Return -EOPNOTSUPP when modify header action zero
From: Or Gerlitz @ 2019-02-26 22:41 UTC (permalink / raw)
To: Tonghao Zhang; +Cc: Saeed Mahameed, Linux Netdev List
In-Reply-To: <1551091207-10366-2-git-send-email-xiangxia.m.yue@gmail.com>
On Mon, Feb 25, 2019 at 1:06 PM <xiangxia.m.yue@gmail.com> wrote:
> When max modify header action is zero, we return -EOPNOTSUPP
> directly. In this way, we can ignore wrong message info (e.g.
> "mlx5: parsed 0 pedit actions, can't do more").
>
> This happens when offloading pedit actions on mlx VFs.
this command should work, we support header re-write (pedit offload)
for tc NIC rules
Is this CX5 VF? if yes, something broke
> For example:
> $ tc filter add dev mlx5_vf parent ffff: protocol ip prio 1 \
> flower skip_sw dst_mac 00:10:56:fb:64:e8 \
> dst_ip 1.1.1.100 src_ip 1.1.1.200 \
> action pedit ex munge eth src set 00:10:56:b4:5d:20
^ permalink raw reply
* Re: [PATCH net-next v2 5/5] net/mlx5e: Return -EOPNOTSUPP when attempting to offload an unsupported action
From: Or Gerlitz @ 2019-02-26 22:42 UTC (permalink / raw)
To: Tonghao Zhang; +Cc: Saeed Mahameed, Linux Netdev List
In-Reply-To: <1551091207-10366-6-git-send-email-xiangxia.m.yue@gmail.com>
On Mon, Feb 25, 2019 at 1:07 PM <xiangxia.m.yue@gmail.com> wrote:
> The encapsulation is not supported for mlx5 VFs. When we try to
> offload that action, the -EINVAL is returned, but not -EOPNOTSUPP.
> This patch changes the returned value and ignore to confuse user.
FWIW, note that this changes the behavior towards user-space, I don't see
concrete harm done here but we should realize that
> For example: (p2p1_0 is VF net device)
> tc filter add dev p2p1_0 protocol ip parent ffff: prio 1 flower skip_sw \
> src_mac e4:11:22:33:44:01 \
> action tunnel_key set \
> src_ip 1.1.1.100 \
> dst_ip 1.1.1.200 \
> dst_port 4789 id 100 \
> action mirred egress redirect dev vxlan0
>
> "RTNETLINK answers: Invalid argument"
^ permalink raw reply
* Re: [PATCH hyperv-fixes] hv_netvsc: Fix IP header checksum for coalesced packets
From: David Miller @ 2019-02-26 22:45 UTC (permalink / raw)
To: haiyangz, haiyangz
Cc: sashal, linux-hyperv, kys, sthemmin, olaf, vkuznets, netdev,
linux-kernel
In-Reply-To: <20190222182503.12160-1-haiyangz@linuxonhyperv.com>
From: Haiyang Zhang <haiyangz@linuxonhyperv.com>
Date: Fri, 22 Feb 2019 18:25:03 +0000
> From: Haiyang Zhang <haiyangz@microsoft.com>
>
> Incoming packets may have IP header checksum verified by the host.
> They may not have IP header checksum computed after coalescing.
> This patch re-compute the checksum when necessary, otherwise the
> packets may be dropped, because Linux network stack always checks it.
>
> Signed-off-by: Haiyang Zhang <haiyangz@microsoft.com>
Applied and queued up for -stable.
^ permalink raw reply
* Re: [PATCH][net-next] net: Use RCU_POINTER_INITIALIZER() to init static variable
From: David Miller @ 2019-02-26 22:52 UTC (permalink / raw)
To: lirongqing; +Cc: netdev
In-Reply-To: <1551062586-5658-1-git-send-email-lirongqing@baidu.com>
From: Li RongQing <lirongqing@baidu.com>
Date: Mon, 25 Feb 2019 10:43:06 +0800
> This pointer is RCU protected, so proper primitives should be used.
>
> Signed-off-by: Zhang Yu <zhangyu31@baidu.com>
> Signed-off-by: Li RongQing <lirongqing@baidu.com>
Applied.
^ permalink raw reply
* Re: [net v3 1/1] tipc: fix race condition causing hung sendto
From: David Miller @ 2019-02-26 22:52 UTC (permalink / raw)
To: tung.q.nguyen; +Cc: netdev, tipc-discussion
In-Reply-To: <20190225035720.5175-1-tung.q.nguyen@dektech.com.au>
From: Tung Nguyen <tung.q.nguyen@dektech.com.au>
Date: Mon, 25 Feb 2019 10:57:20 +0700
> When sending multicast messages via blocking socket,
> if sending link is congested (tsk->cong_link_cnt is set to 1),
> the sending thread will be put into sleeping state. However,
> tipc_sk_filter_rcv() is called under socket spin lock but
> tipc_wait_for_cond() is not. So, there is no guarantee that
> the setting of tsk->cong_link_cnt to 0 in tipc_sk_proto_rcv() in
> CPU-1 will be perceived by CPU-0. If that is the case, the sending
> thread in CPU-0 after being waken up, will continue to see
> tsk->cong_link_cnt as 1 and put the sending thread into sleeping
> state again. The sending thread will sleep forever.
...
> This commit fixes it by adding memory barrier to tipc_sk_proto_rcv()
> and tipc_wait_for_cond().
>
> Acked-by: Jon Maloy <jon.maloy@ericsson.com>
> Signed-off-by: Tung Nguyen <tung.q.nguyen@dektech.com.au>
Applied and queued up for -stable.
^ permalink raw reply
* [PATCH internal pre-review] bpf: add missing entries to bpf_helpers.h
From: Willem de Bruijn @ 2019-02-26 22:55 UTC (permalink / raw)
To: netdev, sdf; +Cc: soheil, Willem de Bruijn
From: Willem de Bruijn <willemb@google.com>
Signed-off-by: Willem de Bruijn <willemb@google.com>
---
tools/testing/selftests/bpf/bpf_helpers.h | 29 +++++++++++++++++++++++
1 file changed, 29 insertions(+)
diff --git a/tools/testing/selftests/bpf/bpf_helpers.h b/tools/testing/selftests/bpf/bpf_helpers.h
index d9999f1ed1d2a..fcd11839903c7 100644
--- a/tools/testing/selftests/bpf/bpf_helpers.h
+++ b/tools/testing/selftests/bpf/bpf_helpers.h
@@ -232,6 +232,35 @@ static int (*bpf_skb_change_head)(void *, int len, int flags) =
(void *) BPF_FUNC_skb_change_head;
static int (*bpf_skb_pull_data)(void *, int len) =
(void *) BPF_FUNC_skb_pull_data;
+static unsigned long long (*bpf_get_cgroup_classid)(void *ctx) =
+ (void *) BPF_FUNC_get_cgroup_classid;
+static unsigned long long (*bpf_get_route_realm)(void *ctx) =
+ (void *) BPF_FUNC_get_route_realm;
+static int (*bpf_skb_change_proto)(void *ctx, __be16 proto, __u64 flags) =
+ (void *) BPF_FUNC_skb_change_proto;
+static int (*bpf_skb_change_type)(void *ctx, __u32 type) =
+ (void *) BPF_FUNC_skb_change_type;
+static unsigned long long (*bpf_get_hash_recalc)(void *ctx) =
+ (void *) BPF_FUNC_get_hash_recalc;
+static unsigned long long (*bpf_get_current_task)(void *ctx) =
+ (void *) BPF_FUNC_get_current_task;
+static int (*bpf_skb_change_tail)(void *ctx, __u32 len, __u64 flags) =
+ (void *) BPF_FUNC_skb_change_tail;
+static long long (*bpf_csum_update)(void *ctx, __u32 csum) =
+ (void *) BPF_FUNC_csum_update;
+static void (*bpf_set_hash_invalid)(void *ctx) =
+ (void *) BPF_FUNC_set_hash_invalid;
+static int (*bpf_get_numa_node_id)(void) =
+ (void *) BPF_FUNC_get_numa_node_id;
+static int (*bpf_probe_read_str)(void *ctx, int size, const void *unsafe_ptr) =
+ (void *) BPF_FUNC_probe_read_str;
+static unsigned int (*bpf_get_socket_uid)(void *ctx) =
+ (void *) BPF_FUNC_get_socket_uid;
+static unsigned int (*bpf_set_hash)(void *ctx, __u32 hash) =
+ (void *) BPF_FUNC_set_hash;
+static int (*bpf_skb_adjust_room)(void *ctx, __s32 len_diff, __u32 mode,
+ unsigned long long flags) =
+ (void *) BPF_FUNC_skb_adjust_room;
/* Scan the ARCH passed in from ARCH env variable (see Makefile) */
#if defined(__TARGET_ARCH_x86)
--
2.21.0.rc2.261.ga7da99ff1b-goog
^ permalink raw reply related
* Re: [PATCH internal pre-review] bpf: add missing entries to bpf_helpers.h
From: Soheil Hassas Yeganeh @ 2019-02-26 22:59 UTC (permalink / raw)
To: Willem de Bruijn; +Cc: netdev, Stanislav Fomichev, Willem de Bruijn
In-Reply-To: <20190226225553.214360-1-willemdebruijn.kernel@gmail.com>
On Tue, Feb 26, 2019 at 5:55 PM Willem de Bruijn
<willemdebruijn.kernel@gmail.com> wrote:
>
> From: Willem de Bruijn <willemb@google.com>
>
> Signed-off-by: Willem de Bruijn <willemb@google.com>
Acked-by: Soheil Hassas Yeganeh <soheil@google.com>
> ---
> tools/testing/selftests/bpf/bpf_helpers.h | 29 +++++++++++++++++++++++
> 1 file changed, 29 insertions(+)
>
> diff --git a/tools/testing/selftests/bpf/bpf_helpers.h b/tools/testing/selftests/bpf/bpf_helpers.h
> index d9999f1ed1d2a..fcd11839903c7 100644
> --- a/tools/testing/selftests/bpf/bpf_helpers.h
> +++ b/tools/testing/selftests/bpf/bpf_helpers.h
> @@ -232,6 +232,35 @@ static int (*bpf_skb_change_head)(void *, int len, int flags) =
> (void *) BPF_FUNC_skb_change_head;
> static int (*bpf_skb_pull_data)(void *, int len) =
> (void *) BPF_FUNC_skb_pull_data;
> +static unsigned long long (*bpf_get_cgroup_classid)(void *ctx) =
> + (void *) BPF_FUNC_get_cgroup_classid;
> +static unsigned long long (*bpf_get_route_realm)(void *ctx) =
> + (void *) BPF_FUNC_get_route_realm;
> +static int (*bpf_skb_change_proto)(void *ctx, __be16 proto, __u64 flags) =
> + (void *) BPF_FUNC_skb_change_proto;
> +static int (*bpf_skb_change_type)(void *ctx, __u32 type) =
> + (void *) BPF_FUNC_skb_change_type;
> +static unsigned long long (*bpf_get_hash_recalc)(void *ctx) =
> + (void *) BPF_FUNC_get_hash_recalc;
> +static unsigned long long (*bpf_get_current_task)(void *ctx) =
> + (void *) BPF_FUNC_get_current_task;
> +static int (*bpf_skb_change_tail)(void *ctx, __u32 len, __u64 flags) =
> + (void *) BPF_FUNC_skb_change_tail;
> +static long long (*bpf_csum_update)(void *ctx, __u32 csum) =
> + (void *) BPF_FUNC_csum_update;
> +static void (*bpf_set_hash_invalid)(void *ctx) =
> + (void *) BPF_FUNC_set_hash_invalid;
> +static int (*bpf_get_numa_node_id)(void) =
> + (void *) BPF_FUNC_get_numa_node_id;
> +static int (*bpf_probe_read_str)(void *ctx, int size, const void *unsafe_ptr) =
> + (void *) BPF_FUNC_probe_read_str;
> +static unsigned int (*bpf_get_socket_uid)(void *ctx) =
> + (void *) BPF_FUNC_get_socket_uid;
> +static unsigned int (*bpf_set_hash)(void *ctx, __u32 hash) =
> + (void *) BPF_FUNC_set_hash;
> +static int (*bpf_skb_adjust_room)(void *ctx, __s32 len_diff, __u32 mode,
> + unsigned long long flags) =
> + (void *) BPF_FUNC_skb_adjust_room;
>
> /* Scan the ARCH passed in from ARCH env variable (see Makefile) */
> #if defined(__TARGET_ARCH_x86)
> --
> 2.21.0.rc2.261.ga7da99ff1b-goog
>
^ permalink raw reply
* Re: [PATCH internal pre-review] bpf: add missing entries to bpf_helpers.h
From: Willem de Bruijn @ 2019-02-26 23:00 UTC (permalink / raw)
To: Network Development, Stanislav Fomichev
Cc: Soheil Hassas Yeganeh, Willem de Bruijn
In-Reply-To: <20190226225553.214360-1-willemdebruijn.kernel@gmail.com>
On Tue, Feb 26, 2019 at 5:55 PM Willem de Bruijn
<willemdebruijn.kernel@gmail.com> wrote:
>
> From: Willem de Bruijn <willemb@google.com>
>
> Signed-off-by: Willem de Bruijn <willemb@google.com>
As the topic indicated I did not intend to send for review to netdev@
just yet. Apologies for the spam.
For one, this commit lacks a commit message.
That said. It is out there, so any comments welcome, thanks.
^ permalink raw reply
* Re: [BUG] net/sched : qlen can not really be per cpu ?
From: Eric Dumazet @ 2019-02-26 23:19 UTC (permalink / raw)
To: Eric Dumazet, John Fastabend, Networking, Jamal Hadi Salim,
Cong Wang, Jiri Pirko
In-Reply-To: <5fb87db3-d5bd-714b-213d-6821f4bf37da@gmail.com>
On 02/25/2019 10:42 PM, Eric Dumazet wrote:
> HTB + pfifo_fast as a leaf qdisc hits badly the following warning in htb_activate() :
>
> WARN_ON(cl->level || !cl->leaf.q || !cl->leaf.q->q.qlen);
>
> This is because pfifo_fast does not update sch->q.qlen, but per cpu counters.
> So cl->leaf.q->q.qlen is zero.
>
> HFSC, CBQ, DRR, QFQ have the same problem.
>
> Any ideas how we can fix this ?
What about something simple for stable ?
( I yet have to boot/test this )
diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h
index 9481f2c142e26ee1174653d673e6134edd9851da..3a9e442fcaaf2ea48ae65bc87ee95f59cd7100c8 100644
--- a/include/net/sch_generic.h
+++ b/include/net/sch_generic.h
@@ -51,7 +51,10 @@ struct qdisc_size_table {
struct qdisc_skb_head {
struct sk_buff *head;
struct sk_buff *tail;
- __u32 qlen;
+ union {
+ __u32 qlen;
+ atomic_t atomic_qlen;
+ };
spinlock_t lock;
};
@@ -408,27 +411,19 @@ static inline void qdisc_cb_private_validate(const struct sk_buff *skb, int sz)
BUILD_BUG_ON(sizeof(qcb->data) < sz);
}
-static inline int qdisc_qlen_cpu(const struct Qdisc *q)
-{
- return this_cpu_ptr(q->cpu_qstats)->qlen;
-}
-
static inline int qdisc_qlen(const struct Qdisc *q)
{
return q->q.qlen;
}
-static inline int qdisc_qlen_sum(const struct Qdisc *q)
+static inline u32 qdisc_qlen_sum(const struct Qdisc *q)
{
- __u32 qlen = q->qstats.qlen;
- int i;
+ u32 qlen = q->qstats.qlen;
- if (q->flags & TCQ_F_NOLOCK) {
- for_each_possible_cpu(i)
- qlen += per_cpu_ptr(q->cpu_qstats, i)->qlen;
- } else {
+ if (q->flags & TCQ_F_NOLOCK)
+ qlen += atomic_read(&q->q.atomic_qlen);
+ else
qlen += q->q.qlen;
- }
return qlen;
}
@@ -827,12 +822,12 @@ static inline void qdisc_qstats_cpu_backlog_inc(struct Qdisc *sch,
static inline void qdisc_qstats_cpu_qlen_inc(struct Qdisc *sch)
{
- this_cpu_inc(sch->cpu_qstats->qlen);
+ atomic_inc(&sch->q.atomic_qlen);
}
static inline void qdisc_qstats_cpu_qlen_dec(struct Qdisc *sch)
{
- this_cpu_dec(sch->cpu_qstats->qlen);
+ atomic_dec(&sch->q.atomic_qlen);
}
static inline void qdisc_qstats_cpu_requeues_inc(struct Qdisc *sch)
^ permalink raw reply related
* Re: [PATCH 0/4] mwifiex PCI/wake-up interrupt fixes
From: Brian Norris @ 2019-02-26 23:28 UTC (permalink / raw)
To: Marc Zyngier
Cc: Amitkumar Karwar, Enric Balletbo i Serra, Ganapathi Bhat,
Heiko Stuebner, Kalle Valo, Nishant Sarmukadam, Rob Herring,
Xinming Hu, David S. Miller, devicetree, linux-arm-kernel,
linux-kernel, linux-rockchip, linux-wireless, netdev, linux-pm,
Jeffy Chen, Rafael J. Wysocki, Tony Lindgren
In-Reply-To: <20190224140426.3267-1-marc.zyngier@arm.com>
+ others
Hi Marc,
Thanks for the series. I have a few bits of history to add to this, and
some comments.
On Sun, Feb 24, 2019 at 02:04:22PM +0000, Marc Zyngier wrote:
> For quite some time, I wondered why the PCI mwifiex device built in my
> Chromebook was unable to use the good old legacy interrupts. But as MSIs
> were working fine, I never really bothered investigating. I finally had a
> look, and the result isn't very pretty.
>
> On this machine (rk3399-based kevin), the wake-up interrupt is described as
> such:
>
> &pci_rootport {
> mvl_wifi: wifi@0,0 {
> compatible = "pci1b4b,2b42";
> reg = <0x83010000 0x0 0x00000000 0x0 0x00100000
> 0x83010000 0x0 0x00100000 0x0 0x00100000>;
> interrupt-parent = <&gpio0>;
> interrupts = <8 IRQ_TYPE_LEVEL_LOW>;
> pinctrl-names = "default";
> pinctrl-0 = <&wlan_host_wake_l>;
> wakeup-source;
> };
> };
>
> Note how the interrupt is part of the properties directly attached to the
> PCI node. And yet, this interrupt has nothing to do with a PCI legacy
> interrupt, as it is attached to the wake-up widget that bypasses the PCIe RC
> altogether (Yay for the broken design!). This is in total violation of the
> IEEE Std 1275-1994 spec[1], which clearly documents that such interrupt
> specifiers describe the PCI device interrupts, and must obey the
> INT-{A,B,C,D} mapping. Oops!
You're not the first person to notice this. All the motivations are not
necessarily painted clearly in their cover letter, but here are some
previous attempts at solving this problem:
[RFC PATCH v11 0/5] PCI: rockchip: Move PCIe WAKE# handling into pci core
https://lkml.kernel.org/lkml/20171225114742.18920-1-jeffy.chen@rock-chips.com/
http://lkml.kernel.org/lkml/20171226023646.17722-1-jeffy.chen@rock-chips.com/
As you can see by the 12th iteration, it wasn't left unsolved for lack
of trying...
Frankly, if a proper DT replacement to the admittedly bad binding isn't
agreed upon quickly, I'd be very happy to just have WAKE# support
removed from the DTS for now, and the existing mwifiex binding should
just be removed. (Wake-on-WiFi was never properly vetted on these
platforms anyway.) It mostly serves to just cause problems like you've
noticed.
> The net effect of the above is that Linux tries to do something vaguely
> sensible, and uses the same interrupt for both the wake-up widget and the
> PCI device. This doesn't work for two reasons: (1) the wake-up widget grabs
> the interrupt in exclusive mode, and (2) the PCI interrupt is still routed
> to the RC, leading to a screaming interrupt. This simply cannot work.
>
> To sort out this mess, we need to lift the confusion between the two
> interrupts. This is done by extending the DT binding to allow the wake-up
> interrupt to be described in a 'wake-up' subnode, sidestepping the issue
> completely. On my Chromebook, it now looks like this:
>
> &pci_rootport {
> mvl_wifi: wifi@0,0 {
> compatible = "pci1b4b,2b42";
> reg = <0x83010000 0x0 0x00000000 0x0 0x00100000
> 0x83010000 0x0 0x00100000 0x0 0x00100000>;
> pinctrl-names = "default";
> pinctrl-0 = <&wlan_host_wake_l>;
> wake-up {
> interrupt-parent = <&gpio0>;
> interrupts = <8 IRQ_TYPE_LEVEL_LOW>;
> wakeup-source;
> };
> };
> };
One problem Rockchip authors were also trying to resolve here is that
PCIe WAKE# handling should not really be something the PCI device driver
has to handle directly. Despite your complaints about not using in-band
TLP wakeup, a separate WAKE# pin is in fact a documented part of the
PCIe standard, and it so happens that the Rockchip RC does not support
handling TLPs in S3, if you want to have decent power consumption. (Your
"bad hardware" complaints could justifiably fall here, I suppose.)
Additionally, I've had pushback from PCI driver authors/maintainers on
adding more special handling for this sort of interrupt property (not
the binding specifically, but just the concept of handling WAKE# in the
driver), as they claim this should be handled by the system firmware,
when they set the appropriate wakeup flags, which filter down to
__pci_enable_wake() -> platform_pci_set_wakeup(). That's how x86 systems
do it (note: I know for a fact that many have a very similar
architecture -- WAKE# is not routed to the RC, because, why does it need
to? and they *don't* use TLP wakeup either -- they just hide it in
firmware better), and it Just Works.
So, we basically concluded that we should standardize on a way to
describe WAKE# interrupts such that PCI drivers don't have to deal with
it at all, and the PCI core can do it for us. 12 revisions later
and...we still never agreed on a good device tree binding for this.
IOW, maybe your wake-up sub-node is the best way to side-step the
problems of conflicting with the OF PCI spec. But I'd still really like
to avoid parsing it in mwifiex, if at all possible.
(We'd still be left with the marvell,wakeup-pin propery to parse
specifically in mwifiex, which sadly has to exist because....well,
Samsung decided to do chip-on-board, and then they failed to use the
correct pin on Marvell's side when wiring up WAKE#. Sigh.)
> The driver is then updated to look for this subnode first, and fallback to
> the original, broken behaviour (spitting out a warning in the offending
> configuration).
>
> For good measure, there are two additional patches:
>
> - The wake-up interrupt requesting is horribly racy, and could lead to
> unpredictable behaviours. Let's fix that properly.
Ack. That mistake was repeated in other drivers and has since been fixed
in those. We need it here too.
Brian
> - A final patch implementing the above transformation for the whole
> RK3399-based Chromebook range, which all use the same broken
> configuration.
>
> With all that, I finally have PCI legacy interrupts working with the mwifiex
> driver on my Chromebook.
>
> [1] http://www.devicetree.org/open-firmware/bindings/pci/pci2_1.pdf
>
> Marc Zyngier (4):
> dt-bindings/marvell-8xxx: Allow wake-up interrupt to be placed in a
> separate node
> mwifiex: Fetch wake-up interrupt from 'wake-up' subnode when it exists
> mwifiex: Flag wake-up interrupt as IRQ_NOAUTOEN rather than disabling
> it too late
> arm64: dts: rockchip: gru: Move wifi wake-up interrupt into its own
> subnode
>
> .../bindings/net/wireless/marvell-8xxx.txt | 23 ++++++++++++++++++-
> .../dts/rockchip/rk3399-gru-chromebook.dtsi | 8 ++++---
> drivers/net/wireless/marvell/mwifiex/main.c | 13 +++++++++--
> 3 files changed, 38 insertions(+), 6 deletions(-)
>
> --
> 2.20.1
>
^ permalink raw reply
* Re: [PATCH 3/4] mwifiex: Flag wake-up interrupt as IRQ_NOAUTOEN rather than disabling it too late
From: Brian Norris @ 2019-02-26 23:31 UTC (permalink / raw)
To: Marc Zyngier
Cc: Amitkumar Karwar, Enric Balletbo i Serra, Ganapathi Bhat,
Heiko Stuebner, Kalle Valo, Nishant Sarmukadam, Rob Herring,
Xinming Hu, David S. Miller, devicetree, linux-arm-kernel,
linux-kernel, linux-rockchip, linux-wireless, netdev
In-Reply-To: <20190224140426.3267-4-marc.zyngier@arm.com>
Hi Marc,
On Sun, Feb 24, 2019 at 02:04:25PM +0000, Marc Zyngier wrote:
> The mwifiex driver makes unsafe assumptions about the state of the
> wake-up interrupt. It requests it and only then disable it. Of
> course, the interrupt may be screaming for whatever reason at that
> time, and the handler will then be called without the interrupt
> having been registered with the PM/wakeup subsystem. Oops.
>
> The right way to handle this kind of situation is to flag the
> interrupt with IRQ_NOAUTOEN before requesting it. It will then
> stay disabled until someone (the wake-up subsystem) enables it.
>
> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
This could be:
Fixes: 853402a00823 ("mwifiex: Enable WoWLAN for both sdio and pcie")
although, that was just borrowing the existing antipattern from SDIO
code..
Reviewed-by: Brian Norris <briannorris@chromium.org>
Thanks.
> ---
> drivers/net/wireless/marvell/mwifiex/main.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c
> index 2105c2b7c627..82cf35e50579 100644
> --- a/drivers/net/wireless/marvell/mwifiex/main.c
> +++ b/drivers/net/wireless/marvell/mwifiex/main.c
> @@ -1610,6 +1610,7 @@ static void mwifiex_probe_of(struct mwifiex_adapter *adapter)
> "wake-up interrupt outside 'wake-up' subnode of %pOF\n",
> adapter->dt_node);
>
> + irq_set_status_flags(adapter->irq_wakeup, IRQ_NOAUTOEN);
> ret = devm_request_irq(dev, adapter->irq_wakeup,
> mwifiex_irq_wakeup_handler, IRQF_TRIGGER_LOW,
> "wifi_wake", adapter);
> @@ -1619,7 +1620,6 @@ static void mwifiex_probe_of(struct mwifiex_adapter *adapter)
> goto err_exit;
> }
>
> - disable_irq(adapter->irq_wakeup);
> if (device_init_wakeup(dev, true)) {
> dev_err(dev, "fail to init wakeup for mwifiex\n");
> goto err_exit;
> --
> 2.20.1
>
^ permalink raw reply
* Re: [PATCH 3/4] mwifiex: Flag wake-up interrupt as IRQ_NOAUTOEN rather than disabling it too late
From: Brian Norris @ 2019-02-26 23:34 UTC (permalink / raw)
To: Marc Zyngier
Cc: Amitkumar Karwar, Enric Balletbo i Serra, Ganapathi Bhat,
Heiko Stuebner, Kalle Valo, Nishant Sarmukadam, Rob Herring,
Xinming Hu, David S. Miller, devicetree, linux-arm-kernel,
linux-kernel, linux-rockchip, linux-wireless, netdev
In-Reply-To: <20190226233130.GB174696@google.com>
On Tue, Feb 26, 2019 at 03:31:31PM -0800, Brian Norris wrote:
> Hi Marc,
>
> On Sun, Feb 24, 2019 at 02:04:25PM +0000, Marc Zyngier wrote:
> > The mwifiex driver makes unsafe assumptions about the state of the
> > wake-up interrupt. It requests it and only then disable it. Of
> > course, the interrupt may be screaming for whatever reason at that
> > time, and the handler will then be called without the interrupt
> > having been registered with the PM/wakeup subsystem. Oops.
> >
> > The right way to handle this kind of situation is to flag the
> > interrupt with IRQ_NOAUTOEN before requesting it. It will then
> > stay disabled until someone (the wake-up subsystem) enables it.
> >
> > Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
>
> This could be:
>
> Fixes: 853402a00823 ("mwifiex: Enable WoWLAN for both sdio and pcie")
Also, this comes after a different change (that's not quite as clearly a
backport-able bugfix). Perhaps it should go first in the series?
Brian
> although, that was just borrowing the existing antipattern from SDIO
> code..
>
> Reviewed-by: Brian Norris <briannorris@chromium.org>
>
> Thanks.
>
> > ---
> > drivers/net/wireless/marvell/mwifiex/main.c | 2 +-
> > 1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c
> > index 2105c2b7c627..82cf35e50579 100644
> > --- a/drivers/net/wireless/marvell/mwifiex/main.c
> > +++ b/drivers/net/wireless/marvell/mwifiex/main.c
> > @@ -1610,6 +1610,7 @@ static void mwifiex_probe_of(struct mwifiex_adapter *adapter)
> > "wake-up interrupt outside 'wake-up' subnode of %pOF\n",
> > adapter->dt_node);
> >
> > + irq_set_status_flags(adapter->irq_wakeup, IRQ_NOAUTOEN);
> > ret = devm_request_irq(dev, adapter->irq_wakeup,
> > mwifiex_irq_wakeup_handler, IRQF_TRIGGER_LOW,
> > "wifi_wake", adapter);
> > @@ -1619,7 +1620,6 @@ static void mwifiex_probe_of(struct mwifiex_adapter *adapter)
> > goto err_exit;
> > }
> >
> > - disable_irq(adapter->irq_wakeup);
> > if (device_init_wakeup(dev, true)) {
> > dev_err(dev, "fail to init wakeup for mwifiex\n");
> > goto err_exit;
> > --
> > 2.20.1
> >
^ permalink raw reply
* [PATCH net-next 0/3] net: dsa: microchip: add KSZ9893 switch support
From: Tristram.Ha @ 2019-02-26 23:37 UTC (permalink / raw)
To: Andrew Lunn, Florian Fainelli, Pavel Machek
Cc: Tristram Ha, UNGLinuxDriver, netdev
From: Tristram Ha <Tristram.Ha@microchip.com>
This series of patches is to modify the KSZ9477 DSA driver to support
running KSZ9893 and other switches in the KSZ9477 family.
The KSZ9893 switch is similar to KSZ9477 except the ingress tail tag has
1 byte instead of 2 bytes. The XMII register that governs the MAC
communication also has different register definitions.
Tristram Ha (3):
dt-bindings: net: dsa: document additional Microchip KSZ9477 family
switches
net: dsa: microchip: add KSZ9893 switch support
net: dsa: microchip: add other KSZ9477 switch variants
Documentation/devicetree/bindings/net/dsa/ksz.txt | 43 +++
drivers/net/dsa/microchip/ksz9477.c | 337 +++++++++++++++++++++-
drivers/net/dsa/microchip/ksz9477_spi.c | 7 +-
drivers/net/dsa/microchip/ksz_common.c | 8 +-
include/net/dsa.h | 1 +
net/dsa/dsa.c | 2 +
net/dsa/dsa_priv.h | 1 +
net/dsa/tag_ksz.c | 34 +++
8 files changed, 415 insertions(+), 18 deletions(-)
--
1.9.1
^ permalink raw reply
* [PATCH net-next 1/3] dt-bindings: net: dsa: document additional Microchip KSZ9477 family switches
From: Tristram.Ha @ 2019-02-26 23:37 UTC (permalink / raw)
To: Andrew Lunn, Florian Fainelli, Pavel Machek
Cc: Tristram Ha, UNGLinuxDriver, netdev
In-Reply-To: <1551224265-9304-1-git-send-email-Tristram.Ha@microchip.com>
From: Tristram Ha <Tristram.Ha@microchip.com>
Document additional Microchip KSZ9477 family switches.
Show how KSZ8565 switch should be configured as the host port is port 7
instead of port 5.
Signed-off-by: Tristram Ha <Tristram.Ha@microchip.com>
---
Documentation/devicetree/bindings/net/dsa/ksz.txt | 43 +++++++++++++++++++++++
1 file changed, 43 insertions(+)
diff --git a/Documentation/devicetree/bindings/net/dsa/ksz.txt b/Documentation/devicetree/bindings/net/dsa/ksz.txt
index 8d58c2a..b5fec1f 100644
--- a/Documentation/devicetree/bindings/net/dsa/ksz.txt
+++ b/Documentation/devicetree/bindings/net/dsa/ksz.txt
@@ -7,6 +7,11 @@ Required properties:
of the following:
- "microchip,ksz9477"
- "microchip,ksz9897"
+ - "microchip,ksz9896"
+ - "microchip,ksz9567"
+ - "microchip,ksz8565"
+ - "microchip,ksz9893"
+ - "microchip,ksz9563"
Optional properties:
@@ -73,4 +78,42 @@ Ethernet switch connected via SPI to the host, CPU port wired to eth0:
};
};
};
+ ksz8565: ksz8565@0 {
+ compatible = "microchip,ksz8565";
+ reg = <0>;
+
+ spi-max-frequency = <44000000>;
+ spi-cpha;
+ spi-cpol;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ port@0 {
+ reg = <0>;
+ label = "lan1";
+ };
+ port@1 {
+ reg = <1>;
+ label = "lan2";
+ };
+ port@2 {
+ reg = <2>;
+ label = "lan3";
+ };
+ port@3 {
+ reg = <3>;
+ label = "lan4";
+ };
+ port@4 {
+ reg = <6>;
+ label = "cpu";
+ ethernet = <ð0>;
+ fixed-link {
+ speed = <1000>;
+ full-duplex;
+ };
+ };
+ };
+ };
};
--
1.9.1
^ permalink raw reply related
* [PATCH net-next 2/3] net: dsa: microchip: add KSZ9893 switch support
From: Tristram.Ha @ 2019-02-26 23:37 UTC (permalink / raw)
To: Andrew Lunn, Florian Fainelli, Pavel Machek
Cc: Tristram Ha, UNGLinuxDriver, netdev
In-Reply-To: <1551224265-9304-1-git-send-email-Tristram.Ha@microchip.com>
From: Tristram Ha <Tristram.Ha@microchip.com>
Add KSZ9893 switch support in KSZ9477 driver. This switch is similar to
KSZ9477 except the ingress tail tag has 1 byte instead of 2 bytes. The
XMII register that governs how the host port communicates with the MAC
also has different register definitions.
Signed-off-by: Tristram Ha <Tristram.Ha@microchip.com>
---
drivers/net/dsa/microchip/ksz9477.c | 244 ++++++++++++++++++++++++++++++--
drivers/net/dsa/microchip/ksz9477_spi.c | 4 +-
drivers/net/dsa/microchip/ksz_common.c | 4 +-
include/net/dsa.h | 1 +
net/dsa/dsa.c | 2 +
net/dsa/dsa_priv.h | 1 +
net/dsa/tag_ksz.c | 34 +++++
7 files changed, 274 insertions(+), 16 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c
index 03de50e..3bb548a 100644
--- a/drivers/net/dsa/microchip/ksz9477.c
+++ b/drivers/net/dsa/microchip/ksz9477.c
@@ -18,6 +18,11 @@
#include "ksz9477_reg.h"
#include "ksz_common.h"
+/* Used with variable features to indicate capabilities. */
+#define GBIT_SUPPORT BIT(0)
+#define NEW_XMII BIT(1)
+#define IS_9893 BIT(2)
+
static const struct {
int index;
char string[ETH_GSTRING_LEN];
@@ -328,7 +333,12 @@ static void ksz9477_port_init_cnt(struct ksz_device *dev, int port)
static enum dsa_tag_protocol ksz9477_get_tag_protocol(struct dsa_switch *ds,
int port)
{
- return DSA_TAG_PROTO_KSZ9477;
+ enum dsa_tag_protocol proto = DSA_TAG_PROTO_KSZ9477;
+ struct ksz_device *dev = ds->priv;
+
+ if (dev->features & IS_9893)
+ proto = DSA_TAG_PROTO_KSZ9893;
+ return proto;
}
static int ksz9477_phy_read16(struct dsa_switch *ds, int addr, int reg)
@@ -353,7 +363,7 @@ static int ksz9477_phy_read16(struct dsa_switch *ds, int addr, int reg)
val = 0x796d;
break;
case MII_PHYSID1:
- val = 0x0022;
+ val = KSZ9477_ID_HI;
break;
case MII_PHYSID2:
val = 0x1631;
@@ -389,6 +399,10 @@ static int ksz9477_phy_write16(struct dsa_switch *ds, int addr, int reg,
/* No real PHY after this. */
if (addr >= dev->phy_port_cnt)
return 0;
+
+ /* No gigabit support. Do not write to this register. */
+ if (!(dev->features & GBIT_SUPPORT) && reg == MII_CTRL1000)
+ return 0;
ksz_pwrite16(dev, addr, 0x100 + (reg << 1), val);
return 0;
@@ -998,11 +1012,156 @@ static void ksz9477_port_mirror_del(struct dsa_switch *ds, int port,
static void ksz9477_phy_setup(struct ksz_device *dev, int port,
struct phy_device *phy)
{
- if (port < dev->phy_port_cnt) {
- /* The MAC actually cannot run in 1000 half-duplex mode. */
+ /* Only apply to port with PHY. */
+ if (port >= dev->phy_port_cnt)
+ return;
+
+ /* The MAC actually cannot run in 1000 half-duplex mode. */
+ phy_remove_link_mode(phy,
+ ETHTOOL_LINK_MODE_1000baseT_Half_BIT);
+
+ /* PHY does not support gigabit. */
+ if (!(dev->features & GBIT_SUPPORT))
phy_remove_link_mode(phy,
- ETHTOOL_LINK_MODE_1000baseT_Half_BIT);
+ ETHTOOL_LINK_MODE_1000baseT_Full_BIT);
+}
+
+static bool ksz9477_get_gbit(struct ksz_device *dev, u8 data)
+{
+ bool gbit;
+
+ if (dev->features & NEW_XMII)
+ gbit = !(data & PORT_MII_NOT_1GBIT);
+ else
+ gbit = !!(data & PORT_MII_1000MBIT_S1);
+ return gbit;
+}
+
+static void ksz9477_set_gbit(struct ksz_device *dev, bool gbit, u8 *data)
+{
+ if (dev->features & NEW_XMII) {
+ if (gbit)
+ *data &= ~PORT_MII_NOT_1GBIT;
+ else
+ *data |= PORT_MII_NOT_1GBIT;
+ } else {
+ if (gbit)
+ *data |= PORT_MII_1000MBIT_S1;
+ else
+ *data &= ~PORT_MII_1000MBIT_S1;
+ }
+}
+
+static int ksz9477_get_xmii(struct ksz_device *dev, u8 data)
+{
+ int mode;
+
+ if (dev->features & NEW_XMII) {
+ switch (data & PORT_MII_SEL_M) {
+ case PORT_MII_SEL:
+ mode = 0;
+ break;
+ case PORT_RMII_SEL:
+ mode = 1;
+ break;
+ case PORT_GMII_SEL:
+ mode = 2;
+ break;
+ default:
+ mode = 3;
+ }
+ } else {
+ switch (data & PORT_MII_SEL_M) {
+ case PORT_MII_SEL_S1:
+ mode = 0;
+ break;
+ case PORT_RMII_SEL_S1:
+ mode = 1;
+ break;
+ case PORT_GMII_SEL_S1:
+ mode = 2;
+ break;
+ default:
+ mode = 3;
+ }
+ }
+ return mode;
+}
+
+static void ksz9477_set_xmii(struct ksz_device *dev, int mode, u8 *data)
+{
+ u8 xmii;
+
+ if (dev->features & NEW_XMII) {
+ switch (mode) {
+ case 0:
+ xmii = PORT_MII_SEL;
+ break;
+ case 1:
+ xmii = PORT_RMII_SEL;
+ break;
+ case 2:
+ xmii = PORT_GMII_SEL;
+ break;
+ default:
+ xmii = PORT_RGMII_SEL;
+ break;
+ }
+ } else {
+ switch (mode) {
+ case 0:
+ xmii = PORT_MII_SEL_S1;
+ break;
+ case 1:
+ xmii = PORT_RMII_SEL_S1;
+ break;
+ case 2:
+ xmii = PORT_GMII_SEL_S1;
+ break;
+ default:
+ xmii = PORT_RGMII_SEL_S1;
+ break;
+ }
+ }
+ *data &= ~PORT_MII_SEL_M;
+ *data |= xmii;
+}
+
+static phy_interface_t ksz9477_get_interface(struct ksz_device *dev, int port)
+{
+ phy_interface_t interface;
+ bool gbit;
+ int mode;
+ u8 data8;
+
+ if (port < dev->phy_port_cnt)
+ return PHY_INTERFACE_MODE_NA;
+ ksz_pread8(dev, port, REG_PORT_XMII_CTRL_1, &data8);
+ gbit = ksz9477_get_gbit(dev, data8);
+ mode = ksz9477_get_xmii(dev, data8);
+ switch (mode) {
+ case 2:
+ interface = PHY_INTERFACE_MODE_GMII;
+ if (gbit)
+ break;
+ case 0:
+ interface = PHY_INTERFACE_MODE_MII;
+ break;
+ case 1:
+ interface = PHY_INTERFACE_MODE_RMII;
+ break;
+ default:
+ interface = PHY_INTERFACE_MODE_RGMII;
+ if (data8 & PORT_RGMII_ID_EG_ENABLE)
+ interface = PHY_INTERFACE_MODE_RGMII_TXID;
+ if (data8 & PORT_RGMII_ID_IG_ENABLE) {
+ interface = PHY_INTERFACE_MODE_RGMII_RXID;
+ if (data8 & PORT_RGMII_ID_EG_ENABLE)
+ interface = PHY_INTERFACE_MODE_RGMII_ID;
+ }
+ break;
}
+ return interface;
}
static void ksz9477_port_setup(struct ksz_device *dev, int port, bool cpu_port)
@@ -1051,24 +1210,25 @@ static void ksz9477_port_setup(struct ksz_device *dev, int port, bool cpu_port)
/* configure MAC to 1G & RGMII mode */
ksz_pread8(dev, port, REG_PORT_XMII_CTRL_1, &data8);
- data8 &= ~PORT_MII_NOT_1GBIT;
- data8 &= ~PORT_MII_SEL_M;
switch (dev->interface) {
case PHY_INTERFACE_MODE_MII:
- data8 |= PORT_MII_NOT_1GBIT;
- data8 |= PORT_MII_SEL;
+ ksz9477_set_xmii(dev, 0, &data8);
+ ksz9477_set_gbit(dev, false, &data8);
p->phydev.speed = SPEED_100;
break;
case PHY_INTERFACE_MODE_RMII:
- data8 |= PORT_MII_NOT_1GBIT;
- data8 |= PORT_RMII_SEL;
+ ksz9477_set_xmii(dev, 1, &data8);
+ ksz9477_set_gbit(dev, false, &data8);
p->phydev.speed = SPEED_100;
break;
case PHY_INTERFACE_MODE_GMII:
- data8 |= PORT_GMII_SEL;
+ ksz9477_set_xmii(dev, 2, &data8);
+ ksz9477_set_gbit(dev, true, &data8);
p->phydev.speed = SPEED_1000;
break;
default:
+ ksz9477_set_xmii(dev, 3, &data8);
+ ksz9477_set_gbit(dev, true, &data8);
data8 &= ~PORT_RGMII_ID_IG_ENABLE;
data8 &= ~PORT_RGMII_ID_EG_ENABLE;
if (dev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
@@ -1077,7 +1237,6 @@ static void ksz9477_port_setup(struct ksz_device *dev, int port, bool cpu_port)
if (dev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
dev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
data8 |= PORT_RGMII_ID_EG_ENABLE;
- data8 |= PORT_RGMII_SEL;
p->phydev.speed = SPEED_1000;
break;
}
@@ -1115,10 +1274,25 @@ static void ksz9477_config_cpu_port(struct dsa_switch *ds)
for (i = 0; i < dev->port_cnt; i++) {
if (dsa_is_cpu_port(ds, i) && (dev->cpu_ports & (1 << i))) {
+ phy_interface_t interface;
+
dev->cpu_port = i;
dev->host_mask = (1 << dev->cpu_port);
dev->port_mask |= dev->host_mask;
+ /* Read from XMII register to determine host port
+ * interface. If set specifically in device tree
+ * note the difference to help debugging.
+ */
+ interface = ksz9477_get_interface(dev, i);
+ if (!dev->interface)
+ dev->interface = interface;
+ if (interface && interface != dev->interface)
+ dev_info(dev->dev,
+ "use %s instead of %s\n",
+ phy_modes(dev->interface),
+ phy_modes(interface));
+
/* enable cpu port */
ksz9477_port_setup(dev, i, true);
p = &dev->ports[dev->cpu_port];
@@ -1172,6 +1346,9 @@ static int ksz9477_setup(struct dsa_switch *ds)
ksz9477_cfg32(dev, REG_SW_QM_CTRL__4, UNICAST_VLAN_BOUNDARY,
true);
+ /* Do not work correctly with tail tagging. */
+ ksz_cfg(dev, REG_SW_MAC_CTRL_0, SW_CHECK_LENGTH, false);
+
/* accept packet up to 2000bytes */
ksz_cfg(dev, REG_SW_MAC_CTRL_1, SW_LEGAL_PACKET_DISABLE, true);
@@ -1230,6 +1407,8 @@ static u32 ksz9477_get_port_addr(int port, int offset)
static int ksz9477_switch_detect(struct ksz_device *dev)
{
u8 data8;
+ u8 id_hi;
+ u8 id_lo;
u32 id32;
int ret;
@@ -1247,11 +1426,40 @@ static int ksz9477_switch_detect(struct ksz_device *dev)
ret = ksz_read32(dev, REG_CHIP_ID0__1, &id32);
if (ret)
return ret;
+ ret = ksz_read8(dev, REG_GLOBAL_OPTIONS, &data8);
+ if (ret)
+ return ret;
/* Number of ports can be reduced depending on chip. */
dev->mib_port_cnt = TOTAL_PORT_NUM;
dev->phy_port_cnt = 5;
+ /* Default capability is gigabit capable. */
+ dev->features = GBIT_SUPPORT;
+
+ id_hi = (u8)(id32 >> 16);
+ id_lo = (u8)(id32 >> 8);
+ if ((id_lo & 0xf) == 3) {
+ /* Chip is from KSZ9893 design. */
+ dev->features |= IS_9893;
+
+ /* Chip does not support gigabit. */
+ if (data8 & SW_QW_ABLE)
+ dev->features &= ~GBIT_SUPPORT;
+ dev->mib_port_cnt = 3;
+ dev->phy_port_cnt = 2;
+ } else {
+ /* Chip uses new XMII register definitions. */
+ dev->features |= NEW_XMII;
+
+ /* Chip does not support gigabit. */
+ if (!(data8 & SW_GIGABIT_ABLE))
+ dev->features &= ~GBIT_SUPPORT;
+ }
+
+ /* Change chip id to known ones so it can be matched against them. */
+ id32 = (id_hi << 16) | (id_lo << 8);
+
dev->chip_id = id32;
return 0;
@@ -1286,6 +1494,15 @@ struct ksz_chip_data {
.cpu_ports = 0x7F, /* can be configured as cpu port */
.port_cnt = 7, /* total physical port count */
},
+ {
+ .chip_id = 0x00989300,
+ .dev_name = "KSZ9893",
+ .num_vlans = 4096,
+ .num_alus = 4096,
+ .num_statics = 16,
+ .cpu_ports = 0x07, /* can be configured as cpu port */
+ .port_cnt = 3, /* total port count */
+ },
};
static int ksz9477_switch_init(struct ksz_device *dev)
@@ -1333,7 +1550,6 @@ static int ksz9477_switch_init(struct ksz_device *dev)
if (!dev->ports[i].mib.counters)
return -ENOMEM;
}
- dev->interface = PHY_INTERFACE_MODE_RGMII_TXID;
return 0;
}
diff --git a/drivers/net/dsa/microchip/ksz9477_spi.c b/drivers/net/dsa/microchip/ksz9477_spi.c
index d757ba1..7517862 100644
--- a/drivers/net/dsa/microchip/ksz9477_spi.c
+++ b/drivers/net/dsa/microchip/ksz9477_spi.c
@@ -2,7 +2,7 @@
/*
* Microchip KSZ9477 series register access through SPI
*
- * Copyright (C) 2017-2018 Microchip Technology Inc.
+ * Copyright (C) 2017-2019 Microchip Technology Inc.
*/
#include <asm/unaligned.h>
@@ -155,6 +155,8 @@ static void ksz9477_spi_shutdown(struct spi_device *spi)
static const struct of_device_id ksz9477_dt_ids[] = {
{ .compatible = "microchip,ksz9477" },
{ .compatible = "microchip,ksz9897" },
+ { .compatible = "microchip,ksz9893" },
+ { .compatible = "microchip,ksz9563" },
{},
};
MODULE_DEVICE_TABLE(of, ksz9477_dt_ids);
diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
index 9328b88..39dace8 100644
--- a/drivers/net/dsa/microchip/ksz_common.c
+++ b/drivers/net/dsa/microchip/ksz_common.c
@@ -453,7 +453,9 @@ int ksz_switch_register(struct ksz_device *dev,
if (ret)
return ret;
- dev->interface = PHY_INTERFACE_MODE_MII;
+ /* Host port interface will be self detected, or specifically set in
+ * device tree.
+ */
if (dev->dev->of_node) {
ret = of_get_phy_mode(dev->dev->of_node);
if (ret >= 0)
diff --git a/include/net/dsa.h b/include/net/dsa.h
index e8ac5b3..ae480bb 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -38,6 +38,7 @@ enum dsa_tag_protocol {
DSA_TAG_PROTO_EDSA,
DSA_TAG_PROTO_GSWIP,
DSA_TAG_PROTO_KSZ9477,
+ DSA_TAG_PROTO_KSZ9893,
DSA_TAG_PROTO_LAN9303,
DSA_TAG_PROTO_MTK,
DSA_TAG_PROTO_QCA,
diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c
index aee909b..36de4f2 100644
--- a/net/dsa/dsa.c
+++ b/net/dsa/dsa.c
@@ -57,6 +57,7 @@ static struct sk_buff *dsa_slave_notag_xmit(struct sk_buff *skb,
#endif
#ifdef CONFIG_NET_DSA_TAG_KSZ9477
[DSA_TAG_PROTO_KSZ9477] = &ksz9477_netdev_ops,
+ [DSA_TAG_PROTO_KSZ9893] = &ksz9893_netdev_ops,
#endif
#ifdef CONFIG_NET_DSA_TAG_LAN9303
[DSA_TAG_PROTO_LAN9303] = &lan9303_netdev_ops,
@@ -93,6 +94,7 @@ const char *dsa_tag_protocol_to_str(const struct dsa_device_ops *ops)
#endif
#ifdef CONFIG_NET_DSA_TAG_KSZ9477
[DSA_TAG_PROTO_KSZ9477] = "ksz9477",
+ [DSA_TAG_PROTO_KSZ9893] = "ksz9893",
#endif
#ifdef CONFIG_NET_DSA_TAG_LAN9303
[DSA_TAG_PROTO_LAN9303] = "lan9303",
diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
index c6caa58..093b7d1 100644
--- a/net/dsa/dsa_priv.h
+++ b/net/dsa/dsa_priv.h
@@ -216,6 +216,7 @@ static inline struct dsa_port *dsa_slave_to_port(const struct net_device *dev)
/* tag_ksz.c */
extern const struct dsa_device_ops ksz9477_netdev_ops;
+extern const struct dsa_device_ops ksz9893_netdev_ops;
/* tag_lan9303.c */
extern const struct dsa_device_ops lan9303_netdev_ops;
diff --git a/net/dsa/tag_ksz.c b/net/dsa/tag_ksz.c
index 927e9c8..de246c9 100644
--- a/net/dsa/tag_ksz.c
+++ b/net/dsa/tag_ksz.c
@@ -16,6 +16,7 @@
/* Typically only one byte is used for tail tag. */
#define KSZ_EGRESS_TAG_LEN 1
+#define KSZ_INGRESS_TAG_LEN 1
static struct sk_buff *ksz_common_xmit(struct sk_buff *skb,
struct net_device *dev, int len)
@@ -141,3 +142,36 @@ static struct sk_buff *ksz9477_rcv(struct sk_buff *skb, struct net_device *dev,
.rcv = ksz9477_rcv,
.overhead = KSZ9477_INGRESS_TAG_LEN,
};
+
+#define KSZ9893_TAIL_TAG_OVERRIDE BIT(5)
+#define KSZ9893_TAIL_TAG_LOOKUP BIT(6)
+
+static struct sk_buff *ksz9893_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct dsa_port *dp = dsa_slave_to_port(dev);
+ struct sk_buff *nskb;
+ u8 *addr;
+ u8 *tag;
+
+ nskb = ksz_common_xmit(skb, dev, KSZ_INGRESS_TAG_LEN);
+ if (!nskb)
+ return NULL;
+
+ /* Tag encoding */
+ tag = skb_put(nskb, KSZ_INGRESS_TAG_LEN);
+ addr = skb_mac_header(nskb);
+
+ *tag = BIT(dp->index);
+
+ if (is_link_local_ether_addr(addr))
+ *tag |= KSZ9893_TAIL_TAG_OVERRIDE;
+
+ return nskb;
+}
+
+const struct dsa_device_ops ksz9893_netdev_ops = {
+ .xmit = ksz9893_xmit,
+ .rcv = ksz9477_rcv,
+ .overhead = KSZ_INGRESS_TAG_LEN,
+};
--
1.9.1
^ permalink raw reply related
* [PATCH net-next 3/3] net: dsa: microchip: add other KSZ9477 switch variants
From: Tristram.Ha @ 2019-02-26 23:37 UTC (permalink / raw)
To: Andrew Lunn, Florian Fainelli, Pavel Machek
Cc: Tristram Ha, UNGLinuxDriver, netdev
In-Reply-To: <1551224265-9304-1-git-send-email-Tristram.Ha@microchip.com>
From: Tristram Ha <Tristram.Ha@microchip.com>
Add other switches in KSZ9477 family.
KSZ9896 is a switch with 6 ports; the last one is typically used to
connect to MAC.
KSZ9567 is same as KSZ9897 but with 1588 PTP capability.
KSZ8567 is same as KSZ9567 but without gigabit capability.
KSZ9563 is same as KSZ9893 but with 1588 PTP capability.
KSZ8563 is same as KSZ9563 but without gigabit capability.
KSZ8565 is a switch with 5 ports; however, port 7 has to be used to
connect to MAC. This chip can only be set through device tree.
Signed-off-by: Tristram Ha <Tristram.Ha@microchip.com>
---
drivers/net/dsa/microchip/ksz9477.c | 93 ++++++++++++++++++++++++++++++++-
drivers/net/dsa/microchip/ksz9477_spi.c | 3 ++
drivers/net/dsa/microchip/ksz_common.c | 4 ++
3 files changed, 98 insertions(+), 2 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c
index 3bb548a..81e7c2f 100644
--- a/drivers/net/dsa/microchip/ksz9477.c
+++ b/drivers/net/dsa/microchip/ksz9477.c
@@ -1264,6 +1264,32 @@ static void ksz9477_port_setup(struct ksz_device *dev, int port, bool cpu_port)
ksz_pread16(dev, port, REG_PORT_PHY_INT_ENABLE, &data16);
}
+#define KSZ_CHIP_NAME_SIZE 18
+
+static char *ksz9477_chip_names[KSZ_CHIP_NAME_SIZE] = {
+ "Microchip KSZ9477",
+ "Microchip KSZ9897",
+ "Microchip KSZ9896",
+ "Microchip KSZ9567",
+ "Microchip KSZ8567",
+ "Microchip KSZ8565",
+ "Microchip KSZ9893",
+ "Microchip KSZ9563",
+ "Microchip KSZ8563",
+};
+
+enum {
+ KSZ9477_SW_CHIP,
+ KSZ9897_SW_CHIP,
+ KSZ9896_SW_CHIP,
+ KSZ9567_SW_CHIP,
+ KSZ8567_SW_CHIP,
+ KSZ8565_SW_CHIP,
+ KSZ9893_SW_CHIP,
+ KSZ9563_SW_CHIP,
+ KSZ8563_SW_CHIP,
+};
+
static void ksz9477_config_cpu_port(struct dsa_switch *ds)
{
struct ksz_device *dev = ds->priv;
@@ -1314,7 +1340,8 @@ static void ksz9477_config_cpu_port(struct dsa_switch *ds)
p->vid_member = (1 << i);
p->member = dev->port_mask;
ksz9477_port_stp_state_set(ds, i, BR_STATE_DISABLED);
- p->on = 1;
+ if (!dsa_is_unused_port(ds, i))
+ p->on = 1;
if (i < dev->phy_port_cnt)
p->phy = 1;
if (dev->chip_id == 0x00947700 && i == 6) {
@@ -1406,6 +1433,7 @@ static u32 ksz9477_get_port_addr(int port, int offset)
static int ksz9477_switch_detect(struct ksz_device *dev)
{
+ int chip = -1;
u8 data8;
u8 id_hi;
u8 id_lo;
@@ -1448,6 +1476,12 @@ static int ksz9477_switch_detect(struct ksz_device *dev)
dev->features &= ~GBIT_SUPPORT;
dev->mib_port_cnt = 3;
dev->phy_port_cnt = 2;
+ if (!(data8 & SW_AVB_ABLE))
+ chip = KSZ9893_SW_CHIP;
+ else if (data8 & SW_QW_ABLE)
+ chip = KSZ8563_SW_CHIP;
+ else
+ chip = KSZ9563_SW_CHIP;
} else {
/* Chip uses new XMII register definitions. */
dev->features |= NEW_XMII;
@@ -1455,6 +1489,37 @@ static int ksz9477_switch_detect(struct ksz_device *dev)
/* Chip does not support gigabit. */
if (!(data8 & SW_GIGABIT_ABLE))
dev->features &= ~GBIT_SUPPORT;
+ if ((id_lo & 0xf) == 6)
+ dev->mib_port_cnt = 6;
+ if (id_hi == FAMILY_ID_94)
+ chip = KSZ9477_SW_CHIP;
+ else if (id_hi == FAMILY_ID_98 && id_lo == CHIP_ID_97)
+ chip = KSZ9897_SW_CHIP;
+ else if (id_hi == FAMILY_ID_98 && id_lo == CHIP_ID_96)
+ chip = KSZ9896_SW_CHIP;
+ else if (id_hi == FAMILY_ID_95 && id_lo == CHIP_ID_67)
+ chip = KSZ9567_SW_CHIP;
+ else if (id_hi == FAMILY_ID_85 && id_lo == CHIP_ID_67)
+ chip = KSZ8567_SW_CHIP;
+ if (id_lo == CHIP_ID_67) {
+ id_hi = FAMILY_ID_98;
+ id_lo = CHIP_ID_97;
+ } else if (id_lo == CHIP_ID_66) {
+ id_hi = FAMILY_ID_98;
+ id_lo = CHIP_ID_96;
+ }
+ }
+ if (dev->dev->of_node) {
+ char name[80];
+
+ /* KSZ8565 chip can only be set through device tree. */
+ if (!of_modalias_node(dev->dev->of_node, name, sizeof(name))) {
+ if (!strcmp(name, "ksz8565")) {
+ chip = KSZ8565_SW_CHIP;
+ id_hi = FAMILY_ID_98;
+ id_lo = 0x95;
+ }
+ }
}
/* Change chip id to known ones so it can be matched against them. */
@@ -1462,6 +1527,10 @@ static int ksz9477_switch_detect(struct ksz_device *dev)
dev->chip_id = id32;
+ /* Update switch device name to matched chip. */
+ if (chip >= 0)
+ dev->name = ksz9477_chip_names[chip];
+
return 0;
}
@@ -1495,6 +1564,15 @@ struct ksz_chip_data {
.port_cnt = 7, /* total physical port count */
},
{
+ .chip_id = 0x00989600,
+ .dev_name = "KSZ9896",
+ .num_vlans = 4096,
+ .num_alus = 4096,
+ .num_statics = 16,
+ .cpu_ports = 0x3F, /* can be configured as cpu port */
+ .port_cnt = 6, /* total port count */
+ },
+ {
.chip_id = 0x00989300,
.dev_name = "KSZ9893",
.num_vlans = 4096,
@@ -1503,6 +1581,15 @@ struct ksz_chip_data {
.cpu_ports = 0x07, /* can be configured as cpu port */
.port_cnt = 3, /* total port count */
},
+ {
+ .chip_id = 0x00989500,
+ .dev_name = "KSZ8565",
+ .num_vlans = 4096,
+ .num_alus = 4096,
+ .num_statics = 16,
+ .cpu_ports = 0x4F, /* can be configured as cpu port */
+ .port_cnt = 7, /* total port count */
+ },
};
static int ksz9477_switch_init(struct ksz_device *dev)
@@ -1515,7 +1602,8 @@ static int ksz9477_switch_init(struct ksz_device *dev)
const struct ksz_chip_data *chip = &ksz9477_switch_chips[i];
if (dev->chip_id == chip->chip_id) {
- dev->name = chip->dev_name;
+ if (!dev->name)
+ dev->name = chip->dev_name;
dev->num_vlans = chip->num_vlans;
dev->num_alus = chip->num_alus;
dev->num_statics = chip->num_statics;
@@ -1531,6 +1619,7 @@ static int ksz9477_switch_init(struct ksz_device *dev)
return -ENODEV;
dev->port_mask = (1 << dev->port_cnt) - 1;
+ dev->port_mask &= dev->cpu_ports;
dev->reg_mib_cnt = SWITCH_COUNTER_NUM;
dev->mib_cnt = TOTAL_SWITCH_COUNTER_NUM;
diff --git a/drivers/net/dsa/microchip/ksz9477_spi.c b/drivers/net/dsa/microchip/ksz9477_spi.c
index 7517862..878dd64 100644
--- a/drivers/net/dsa/microchip/ksz9477_spi.c
+++ b/drivers/net/dsa/microchip/ksz9477_spi.c
@@ -155,6 +155,9 @@ static void ksz9477_spi_shutdown(struct spi_device *spi)
static const struct of_device_id ksz9477_dt_ids[] = {
{ .compatible = "microchip,ksz9477" },
{ .compatible = "microchip,ksz9897" },
+ { .compatible = "microchip,ksz9896" },
+ { .compatible = "microchip,ksz9567" },
+ { .compatible = "microchip,ksz8565" },
{ .compatible = "microchip,ksz9893" },
{ .compatible = "microchip,ksz9563" },
{},
diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
index 39dace8..826e046 100644
--- a/drivers/net/dsa/microchip/ksz_common.c
+++ b/drivers/net/dsa/microchip/ksz_common.c
@@ -84,6 +84,10 @@ static void ksz_mib_read_work(struct work_struct *work)
for (i = 0; i < dev->mib_port_cnt; i++) {
p = &dev->ports[i];
+
+ /* Port is not being used. */
+ if (!p->on)
+ continue;
mib = &p->mib;
mutex_lock(&mib->cnt_mutex);
--
1.9.1
^ permalink raw reply related
* Re: [PATCH 0/3] soc: fsl: dpio: enable and configure cache stashing
From: Li Yang @ 2019-02-26 23:42 UTC (permalink / raw)
To: Ioana Ciornei
Cc: Roy Pledge, Ioana Ciocoi Radulescu, Laurentiu Tudor, Horia Geanta,
brouer@redhat.com, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org
In-Reply-To: <20190223084816.28985-1-ioana.ciornei@nxp.com>
On Sat, Feb 23, 2019 at 2:49 AM Ioana Ciornei <ioana.ciornei@nxp.com> wrote:
>
> The first two patches enable cache stashing and configure the core cluster
> destination per software portal while the third patch is the one
> configuring the amount of stashing on a queue.
Series applied for next. Thanks.
Regards,
Leo
>
> Ioana Ciornei (3):
> soc: fsl: dpio: enable frame data cache stashing per software portal
> soc: fsl: dpio: configure cache stashing destination
> dpaa2-eth: configure the cache stashing amount on a queue
>
> drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c | 7 +++-
> drivers/soc/fsl/Kconfig | 1 +
> drivers/soc/fsl/dpio/dpio-cmd.h | 5 +++
> drivers/soc/fsl/dpio/dpio-driver.c | 52 ++++++++++++++++++++++++
> drivers/soc/fsl/dpio/dpio.c | 16 ++++++++
> drivers/soc/fsl/dpio/dpio.h | 5 +++
> drivers/soc/fsl/dpio/qbman-portal.c | 4 +-
> 7 files changed, 87 insertions(+), 3 deletions(-)
>
> --
> 1.9.1
>
^ permalink raw reply
* Re: [PATCH 0/4] mwifiex PCI/wake-up interrupt fixes
From: Brian Norris @ 2019-02-26 23:44 UTC (permalink / raw)
To: Marc Zyngier
Cc: Ard Biesheuvel, Amitkumar Karwar, Enric Balletbo i Serra,
Ganapathi Bhat, Heiko Stuebner, Kalle Valo, Nishant Sarmukadam,
Rob Herring, Xinming Hu, Devicetree List,
<netdev@vger.kernel.org>,
<linux-wireless@vger.kernel.org>, Linux Kernel Mailing List,
linux-rockchip, David S. Miller, linux-arm-kernel, linux-pci,
linux-pm, Jeffy Chen
In-Reply-To: <0c433a70-27f6-76ad-c46c-6015de1ffaa4@arm.com>
Hi,
On Tue, Feb 26, 2019 at 05:14:00PM +0000, Marc Zyngier wrote:
> On 26/02/2019 16:21, Ard Biesheuvel wrote:
> > On Mon, 25 Feb 2019 at 15:53, Marc Zyngier <marc.zyngier@arm.com> wrote:
> >> It outlines one thing: If you have to interpret per-device PCI
> >> properties from DT, you're in for serious trouble. I should get some
> >> better HW.
> >>
> >
> > Yeah, it obviously makes no sense at all for the interrupt parent of a
> > PCI device to deviate from the host bridge's interrupt parent, and
> > it's quite unfortunate that we can't simply ban it now that the cat is
> > out of the bag already.
> >
> > Arguably, the wake up widget is not part of the PCI device, but I have
> > no opinion as to whether it is better modeling it as a sub device as
> > you are proposing or as an entirely separate device referenced via a
> > phandle.
>
> It is not that clear. The widget seems to be an integral part of the
> device, as it is the same basic IP that is used for SDIO and USB.
It's not really a widget specific to this IP. It's just a GPIO. It so
happens that both SDIO and PCIe designs have wanted to use a GPIO for
wakeup, as many other devices do. (Note: it's not just cheap ARM
devices; pulling up some Intel Chromebook designs, I see the exact same
WAKE# GPIO on their PCIe WiFi as well.)
> It looks like the good old pre-PCI-2.2 days, where you had to have a
> separate cable between your network card and the base-board for the
> wake-up interrupt to be delivered. Starting with PCI-2.2, the bus can
> carry the signal just fine. With PCIe, it should just be an interrupt
> TLP sent to the RC, but that's obviously not within the capabilities of
> the HW.
You should search the PCI Express specification for WAKE#. There is a
clearly-documented "side-band wake" feature that is part of the
standard, as an alternative to in-band TLP wakeup. While you claim this
is an ancient thing, it in fact still in use on many systems -- it's
just usually abstracted better by ACPI firmware, whereas the dirty
laundry is aired a bit more on a Device Tree system. And we got it
wrong.
> Anyway, it'd be good if the Marvell people could chime in and let us
> know how they'd prefer to handle this.
I'm not sure this is really a Marvell-specific problem. (Well, except
for the marvell,wakeup-pin silliness, which is somewhat orthogonal.) In
fact, if we cared a little more about Wake-on-WiFi, we'd be trying to
support the same (out-of-band WAKE#) with other WiFi drivers.
Brian
^ permalink raw reply
* Re: [BUG] net/sched : qlen can not really be per cpu ?
From: Cong Wang @ 2019-02-26 23:51 UTC (permalink / raw)
To: Eric Dumazet; +Cc: John Fastabend, Networking, Jamal Hadi Salim, Jiri Pirko
In-Reply-To: <b9f45c9e-8e4e-36d6-8de1-15a759346344@gmail.com>
On Tue, Feb 26, 2019 at 3:19 PM Eric Dumazet <eric.dumazet@gmail.com> wrote:
>
>
>
> On 02/25/2019 10:42 PM, Eric Dumazet wrote:
> > HTB + pfifo_fast as a leaf qdisc hits badly the following warning in htb_activate() :
> >
> > WARN_ON(cl->level || !cl->leaf.q || !cl->leaf.q->q.qlen);
> >
> > This is because pfifo_fast does not update sch->q.qlen, but per cpu counters.
> > So cl->leaf.q->q.qlen is zero.
> >
> > HFSC, CBQ, DRR, QFQ have the same problem.
> >
> > Any ideas how we can fix this ?
>
> What about something simple for stable ?
> ( I yet have to boot/test this )
Is merely updating qlen sufficient for fixing it?
I thought it is because of the lack of qdisc_tree_reduce_backlog()
in pfifo_fast.
^ permalink raw reply
* Re: [RFC] nasty corner case in unix_dgram_sendmsg()
From: Al Viro @ 2019-02-26 23:59 UTC (permalink / raw)
To: Jason Baron; +Cc: Rainer Weikusat, netdev
In-Reply-To: <552b3d67-2f43-5831-e4ea-666827de54fe@akamai.com>
On Tue, Feb 26, 2019 at 03:35:39PM -0500, Jason Baron wrote:
> > I understand what the unix_dgram_peer_wake_me() is doing; I understand
> > what unix_dgram_poll() is using it for. What I do not understand is
> > what's the point of doing that in unix_dgram_sendmsg()...
> >
>
> Hi,
>
> So the unix_dgram_peer_wake_me() in unix_dgram_sendmsg() is there for
> epoll in edge-triggered mode. In that case, we want to ensure that if
> -EAGAIN is returned a subsequent epoll_wait() is not stuck indefinitely.
> Probably could use a comment...
*owwww*
Let me see if I've got it straight - you want the forwarding rearmed,
so that it would match the behaviour of ep_poll_callback() (i.e.
removing only when POLLFREE is passed)? Looks like an odd way to
do it, if that's what's happening...
While we are at it, why disarm a forwarder upon noticing that peer
is dead? Wouldn't it be simpler to move that
wake_up_interruptible_all(&u->peer_wait);
in unix_release_sock() to just before
unix_state_unlock(sk);
a line prior? Then anyone seeing SOCK_DEAD on (locked) peer
would be guaranteed that all forwarders are gone...
Another fun question about the same dgram sendmsg:
if (unix_peer(sk) == other) {
unix_peer(sk) = NULL;
unix_dgram_peer_wake_disconnect_wakeup(sk, other);
unix_state_unlock(sk);
unix_dgram_disconnected(sk, other);
... and we are holding any locks at the last line. What happens
if we have thread A doing
decide which address to talk to
connect(fd, that address)
send request over fd (with send(2) or write(2))
read reply from fd (recv(2) or read(2))
in a loop, with thread B doing explicit sendto(2) over the same
socket?
Suppose B happens to send to the last server thread A was talking
to and finds it just closed (e.g. because the last request from
A had been "shut down", which server has honoured). B gets ECONNREFUSED,
as it ought to, but it can also ends up disrupting the next exchange
of A.
Shouldn't we rather extract the skbs from that queue *before*
dropping sk->lock? E.g. move them to a temporary queue, and flush
that queue after we'd unlocked sk...
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox