* Re: [PATCH net-next 3/4] bpf: BPF for lightweight tunnel encapsulation
From: Tom Herbert @ 2016-10-30 20:34 UTC (permalink / raw)
To: Thomas Graf
Cc: David S. Miller, Alexei Starovoitov, Daniel Borkmann,
Linux Kernel Network Developers, roopa
In-Reply-To: <2ab47e198828261438e8762ef484a6023481a756.1477827877.git.tgraf@suug.ch>
On Sun, Oct 30, 2016 at 4:58 AM, Thomas Graf <tgraf@suug.ch> wrote:
> Register two new BPF prog types BPF_PROG_TYPE_LWT_IN and
> BPF_PROG_TYPE_LWT_OUT which are invoked if a route contains a
> LWT redirection of type LWTUNNEL_ENCAP_BPF.
>
> The separate program types are required because manipulation of
> packet data is only allowed on the output and transmit path as
> the subsequent dst_input() call path assumes an IP header
> validated by ip_rcv(). The BPF programs will be handed an skb
> with the L3 header attached and may return one of the following
> return codes:
>
> BPF_OK - Continue routing as per nexthop
> BPF_DROP - Drop skb and return EPERM
> BPF_REDIRECT - Redirect skb to device as per redirect() helper.
> (Only valid on lwtunnel_xmit() hook)
>
> The return codes are binary compatible with their TC_ACT_
> relatives to ease compatibility.
>
> A new helper bpf_skb_push() is added which allows to preprend an
> L2 header in front of the skb, extend the existing L3 header, or
> both. This allows to address a wide range of issues:
> - Optimize L2 header construction when L2 information is always
> static to avoid ARP/NDisc lookup.
> - Extend IP header to add additional IP options.
> - Perform simple encapsulation where offload is of no concern.
> (The existing funtionality to attach a tunnel key to the skb
> and redirect to a tunnel net_device to allow for offload
> continues to work obviously).
>
> Signed-off-by: Thomas Graf <tgraf@suug.ch>
> ---
> include/linux/filter.h | 2 +-
> include/uapi/linux/bpf.h | 31 +++-
> include/uapi/linux/lwtunnel.h | 21 +++
> kernel/bpf/verifier.c | 16 +-
> net/core/Makefile | 2 +-
> net/core/filter.c | 148 ++++++++++++++++-
> net/core/lwt_bpf.c | 365 ++++++++++++++++++++++++++++++++++++++++++
> net/core/lwtunnel.c | 1 +
> 8 files changed, 579 insertions(+), 7 deletions(-)
> create mode 100644 net/core/lwt_bpf.c
>
> diff --git a/include/linux/filter.h b/include/linux/filter.h
> index 1f09c52..aad7f81 100644
> --- a/include/linux/filter.h
> +++ b/include/linux/filter.h
> @@ -438,7 +438,7 @@ struct xdp_buff {
> };
>
> /* compute the linear packet data range [data, data_end) which
> - * will be accessed by cls_bpf and act_bpf programs
> + * will be accessed by cls_bpf, act_bpf and lwt programs
> */
> static inline void bpf_compute_data_end(struct sk_buff *skb)
> {
> diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
> index e2f38e0..2ebaa3c 100644
> --- a/include/uapi/linux/bpf.h
> +++ b/include/uapi/linux/bpf.h
> @@ -96,6 +96,9 @@ enum bpf_prog_type {
> BPF_PROG_TYPE_TRACEPOINT,
> BPF_PROG_TYPE_XDP,
> BPF_PROG_TYPE_PERF_EVENT,
> + BPF_PROG_TYPE_LWT_IN,
> + BPF_PROG_TYPE_LWT_OUT,
> + BPF_PROG_TYPE_LWT_XMIT,
> };
>
> #define BPF_PSEUDO_MAP_FD 1
> @@ -383,6 +386,16 @@ union bpf_attr {
> *
> * int bpf_get_numa_node_id()
> * Return: Id of current NUMA node.
> + *
> + * int bpf_skb_push()
> + * Add room to beginning of skb and adjusts MAC header offset accordingly.
> + * Extends/reallocaes for needed skb headeroom automatically.
> + * May change skb data pointer and will thus invalidate any check done
> + * for direct packet access.
> + * @skb: pointer to skb
> + * @len: length of header to be pushed in front
> + * @flags: Flags (unused for now)
> + * Return: 0 on success or negative error
> */
> #define __BPF_FUNC_MAPPER(FN) \
> FN(unspec), \
> @@ -427,7 +440,8 @@ union bpf_attr {
> FN(skb_pull_data), \
> FN(csum_update), \
> FN(set_hash_invalid), \
> - FN(get_numa_node_id),
> + FN(get_numa_node_id), \
> + FN(skb_push),
>
> /* integer value in 'imm' field of BPF_CALL instruction selects which helper
> * function eBPF program intends to call
> @@ -511,6 +525,21 @@ struct bpf_tunnel_key {
> __u32 tunnel_label;
> };
>
> +/* Generic BPF return codes which all BPF program types may support.
> + * The values are binary compatible with their TC_ACT_* counter-part to
> + * provide backwards compatibility with existing SCHED_CLS and SCHED_ACT
> + * programs.
> + *
> + * XDP is handled seprately, see XDP_*.
> + */
> +enum bpf_ret_code {
> + BPF_OK = 0,
> + /* 1 reserved */
> + BPF_DROP = 2,
> + /* 3-6 reserved */
> + BPF_REDIRECT = 7,
> +};
> +
> /* User return codes for XDP prog type.
> * A valid XDP program must return one of these defined values. All other
> * return codes are reserved for future use. Unknown return codes will result
> diff --git a/include/uapi/linux/lwtunnel.h b/include/uapi/linux/lwtunnel.h
> index a478fe8..9354d997 100644
> --- a/include/uapi/linux/lwtunnel.h
> +++ b/include/uapi/linux/lwtunnel.h
> @@ -9,6 +9,7 @@ enum lwtunnel_encap_types {
> LWTUNNEL_ENCAP_IP,
> LWTUNNEL_ENCAP_ILA,
> LWTUNNEL_ENCAP_IP6,
> + LWTUNNEL_ENCAP_BPF,
> __LWTUNNEL_ENCAP_MAX,
> };
>
> @@ -42,4 +43,24 @@ enum lwtunnel_ip6_t {
>
> #define LWTUNNEL_IP6_MAX (__LWTUNNEL_IP6_MAX - 1)
>
> +enum {
> + LWT_BPF_PROG_UNSPEC,
> + LWT_BPF_PROG_FD,
> + LWT_BPF_PROG_NAME,
> + __LWT_BPF_PROG_MAX,
> +};
> +
> +#define LWT_BPF_PROG_MAX (__LWT_BPF_PROG_MAX - 1)
> +
> +enum {
> + LWT_BPF_UNSPEC,
> + LWT_BPF_IN,
> + LWT_BPF_OUT,
> + LWT_BPF_XMIT,
> + __LWT_BPF_MAX,
> +};
> +
> +#define LWT_BPF_MAX (__LWT_BPF_MAX - 1)
> +
> +
> #endif /* _UAPI_LWTUNNEL_H_ */
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 9002575..519b58e 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -633,12 +633,21 @@ static int check_map_access(struct bpf_verifier_env *env, u32 regno, int off,
> #define MAX_PACKET_OFF 0xffff
>
> static bool may_access_direct_pkt_data(struct bpf_verifier_env *env,
> - const struct bpf_call_arg_meta *meta)
> + const struct bpf_call_arg_meta *meta,
> + enum bpf_access_type t)
> {
> switch (env->prog->type) {
> + case BPF_PROG_TYPE_LWT_IN:
> + /* dst_input() can't write for now, orig_input may depend on
> + * IP header parsed by ip_rcv().
> + */
> + if (t == BPF_WRITE)
> + return false;
> case BPF_PROG_TYPE_SCHED_CLS:
> case BPF_PROG_TYPE_SCHED_ACT:
> case BPF_PROG_TYPE_XDP:
> + case BPF_PROG_TYPE_LWT_OUT:
> + case BPF_PROG_TYPE_LWT_XMIT:
> if (meta)
> return meta->pkt_access;
>
> @@ -837,7 +846,7 @@ static int check_mem_access(struct bpf_verifier_env *env, u32 regno, int off,
> err = check_stack_read(state, off, size, value_regno);
> }
> } else if (state->regs[regno].type == PTR_TO_PACKET) {
> - if (t == BPF_WRITE && !may_access_direct_pkt_data(env, NULL)) {
> + if (t == BPF_WRITE && !may_access_direct_pkt_data(env, NULL, t)) {
> verbose("cannot write into packet\n");
> return -EACCES;
> }
> @@ -970,7 +979,8 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno,
> return 0;
> }
>
> - if (type == PTR_TO_PACKET && !may_access_direct_pkt_data(env, meta)) {
> + if (type == PTR_TO_PACKET &&
> + !may_access_direct_pkt_data(env, meta, BPF_READ)) {
> verbose("helper access to the packet is not allowed\n");
> return -EACCES;
> }
> diff --git a/net/core/Makefile b/net/core/Makefile
> index d6508c2..a675fd3 100644
> --- a/net/core/Makefile
> +++ b/net/core/Makefile
> @@ -23,7 +23,7 @@ obj-$(CONFIG_NETWORK_PHY_TIMESTAMPING) += timestamping.o
> obj-$(CONFIG_NET_PTP_CLASSIFY) += ptp_classifier.o
> obj-$(CONFIG_CGROUP_NET_PRIO) += netprio_cgroup.o
> obj-$(CONFIG_CGROUP_NET_CLASSID) += netclassid_cgroup.o
> -obj-$(CONFIG_LWTUNNEL) += lwtunnel.o
> +obj-$(CONFIG_LWTUNNEL) += lwtunnel.o lwt_bpf.o
> obj-$(CONFIG_DST_CACHE) += dst_cache.o
> obj-$(CONFIG_HWBM) += hwbm.o
> obj-$(CONFIG_NET_DEVLINK) += devlink.o
> diff --git a/net/core/filter.c b/net/core/filter.c
> index cd9e2ba..325a9d8 100644
> --- a/net/core/filter.c
> +++ b/net/core/filter.c
> @@ -2138,6 +2138,43 @@ static const struct bpf_func_proto bpf_skb_change_tail_proto = {
> .arg3_type = ARG_ANYTHING,
> };
>
> +BPF_CALL_3(bpf_skb_push, struct sk_buff *, skb, __u32, len, u64, flags)
> +{
> + u32 new_len = skb->len + len;
> +
> + /* restrict max skb size and check for overflow */
> + if (new_len > __bpf_skb_max_len(skb) || new_len < skb->len)
> + return -ERANGE;
> +
> + if (flags)
> + return -EINVAL;
> +
> + if (len > 0) {
> + int ret;
> +
> + ret = skb_cow(skb, len);
> + if (unlikely(ret < 0))
> + return ret;
> +
> + __skb_push(skb, len);
> + memset(skb->data, 0, len);
> + }
> +
> + skb_reset_mac_header(skb);
> +
> + bpf_compute_data_end(skb);
> + return 0;
> +}
> +
> +static const struct bpf_func_proto bpf_skb_push_proto = {
> + .func = bpf_skb_push,
> + .gpl_only = false,
> + .ret_type = RET_INTEGER,
> + .arg1_type = ARG_PTR_TO_CTX,
> + .arg2_type = ARG_ANYTHING,
> + .arg3_type = ARG_ANYTHING,
> +};
> +
> bool bpf_helper_changes_skb_data(void *func)
> {
> if (func == bpf_skb_vlan_push ||
> @@ -2147,7 +2184,8 @@ bool bpf_helper_changes_skb_data(void *func)
> func == bpf_skb_change_tail ||
> func == bpf_skb_pull_data ||
> func == bpf_l3_csum_replace ||
> - func == bpf_l4_csum_replace)
> + func == bpf_l4_csum_replace ||
> + func == bpf_skb_push)
> return true;
>
> return false;
> @@ -2578,6 +2616,75 @@ xdp_func_proto(enum bpf_func_id func_id)
> }
> }
>
> +static const struct bpf_func_proto *
> +lwt_in_func_proto(enum bpf_func_id func_id)
> +{
> + switch (func_id) {
> + case BPF_FUNC_skb_load_bytes:
> + return &bpf_skb_load_bytes_proto;
> + case BPF_FUNC_skb_pull_data:
> + return &bpf_skb_pull_data_proto;
> + case BPF_FUNC_csum_diff:
> + return &bpf_csum_diff_proto;
> + case BPF_FUNC_get_cgroup_classid:
> + return &bpf_get_cgroup_classid_proto;
> + case BPF_FUNC_get_route_realm:
> + return &bpf_get_route_realm_proto;
> + case BPF_FUNC_get_hash_recalc:
> + return &bpf_get_hash_recalc_proto;
> + case BPF_FUNC_perf_event_output:
> + return &bpf_skb_event_output_proto;
> + case BPF_FUNC_get_smp_processor_id:
> + return &bpf_get_smp_processor_id_proto;
> + case BPF_FUNC_skb_under_cgroup:
> + return &bpf_skb_under_cgroup_proto;
> + default:
> + return sk_filter_func_proto(func_id);
> + }
> +}
> +
> +static const struct bpf_func_proto *
> +lwt_out_func_proto(enum bpf_func_id func_id)
> +{
> + switch (func_id) {
> + case BPF_FUNC_skb_store_bytes:
> + return &bpf_skb_store_bytes_proto;
> + case BPF_FUNC_csum_update:
> + return &bpf_csum_update_proto;
> + case BPF_FUNC_l3_csum_replace:
> + return &bpf_l3_csum_replace_proto;
> + case BPF_FUNC_l4_csum_replace:
> + return &bpf_l4_csum_replace_proto;
> + case BPF_FUNC_set_hash_invalid:
> + return &bpf_set_hash_invalid_proto;
> + default:
> + return lwt_in_func_proto(func_id);
> + }
> +}
> +
> +static const struct bpf_func_proto *
> +lwt_xmit_func_proto(enum bpf_func_id func_id)
> +{
> + switch (func_id) {
> + case BPF_FUNC_skb_get_tunnel_key:
> + return &bpf_skb_get_tunnel_key_proto;
> + case BPF_FUNC_skb_set_tunnel_key:
> + return bpf_get_skb_set_tunnel_proto(func_id);
> + case BPF_FUNC_skb_get_tunnel_opt:
> + return &bpf_skb_get_tunnel_opt_proto;
> + case BPF_FUNC_skb_set_tunnel_opt:
> + return bpf_get_skb_set_tunnel_proto(func_id);
> + case BPF_FUNC_redirect:
> + return &bpf_redirect_proto;
> + case BPF_FUNC_skb_change_tail:
> + return &bpf_skb_change_tail_proto;
> + case BPF_FUNC_skb_push:
> + return &bpf_skb_push_proto;
> + default:
> + return lwt_out_func_proto(func_id);
> + }
> +}
> +
> static bool __is_valid_access(int off, int size, enum bpf_access_type type)
> {
> if (off < 0 || off >= sizeof(struct __sk_buff))
> @@ -2940,6 +3047,27 @@ static const struct bpf_verifier_ops xdp_ops = {
> .convert_ctx_access = xdp_convert_ctx_access,
> };
>
> +static const struct bpf_verifier_ops lwt_in_ops = {
> + .get_func_proto = lwt_in_func_proto,
> + .is_valid_access = tc_cls_act_is_valid_access,
> + .convert_ctx_access = sk_filter_convert_ctx_access,
> + .gen_prologue = tc_cls_act_prologue,
> +};
> +
> +static const struct bpf_verifier_ops lwt_out_ops = {
> + .get_func_proto = lwt_out_func_proto,
> + .is_valid_access = tc_cls_act_is_valid_access,
> + .convert_ctx_access = sk_filter_convert_ctx_access,
> + .gen_prologue = tc_cls_act_prologue,
> +};
> +
> +static const struct bpf_verifier_ops lwt_xmit_ops = {
> + .get_func_proto = lwt_xmit_func_proto,
> + .is_valid_access = tc_cls_act_is_valid_access,
> + .convert_ctx_access = sk_filter_convert_ctx_access,
> + .gen_prologue = tc_cls_act_prologue,
> +};
> +
> static struct bpf_prog_type_list sk_filter_type __read_mostly = {
> .ops = &sk_filter_ops,
> .type = BPF_PROG_TYPE_SOCKET_FILTER,
> @@ -2960,12 +3088,30 @@ static struct bpf_prog_type_list xdp_type __read_mostly = {
> .type = BPF_PROG_TYPE_XDP,
> };
>
> +static struct bpf_prog_type_list lwt_in_type __read_mostly = {
> + .ops = &lwt_in_ops,
> + .type = BPF_PROG_TYPE_LWT_IN,
> +};
> +
> +static struct bpf_prog_type_list lwt_out_type __read_mostly = {
> + .ops = &lwt_out_ops,
> + .type = BPF_PROG_TYPE_LWT_OUT,
> +};
> +
> +static struct bpf_prog_type_list lwt_xmit_type __read_mostly = {
> + .ops = &lwt_xmit_ops,
> + .type = BPF_PROG_TYPE_LWT_XMIT,
> +};
> +
> static int __init register_sk_filter_ops(void)
> {
> bpf_register_prog_type(&sk_filter_type);
> bpf_register_prog_type(&sched_cls_type);
> bpf_register_prog_type(&sched_act_type);
> bpf_register_prog_type(&xdp_type);
> + bpf_register_prog_type(&lwt_in_type);
> + bpf_register_prog_type(&lwt_out_type);
> + bpf_register_prog_type(&lwt_xmit_type);
>
> return 0;
> }
> diff --git a/net/core/lwt_bpf.c b/net/core/lwt_bpf.c
> new file mode 100644
> index 0000000..8404ac6
> --- /dev/null
> +++ b/net/core/lwt_bpf.c
> @@ -0,0 +1,365 @@
> +/* Copyright (c) 2016 Thomas Graf <tgraf@tgraf.ch>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of version 2 of the GNU General Public
> + * License as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * General Public License for more details.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/skbuff.h>
> +#include <linux/types.h>
> +#include <linux/bpf.h>
> +#include <net/lwtunnel.h>
> +
> +struct bpf_lwt_prog {
> + struct bpf_prog *prog;
> + char *name;
> +};
> +
> +struct bpf_lwt {
> + struct bpf_lwt_prog in;
> + struct bpf_lwt_prog out;
> + struct bpf_lwt_prog xmit;
> +};
> +
> +#define MAX_PROG_NAME 256
> +
> +static inline struct bpf_lwt *bpf_lwt_lwtunnel(struct lwtunnel_state *lwt)
> +{
> + return (struct bpf_lwt *)lwt->data;
> +}
> +
> +#define NO_REDIRECT false
> +#define CAN_REDIRECT true
> +
> +static int run_lwt_bpf(struct sk_buff *skb, struct bpf_lwt_prog *lwt,
> + struct dst_entry *dst, bool can_redirect)
> +{
> + int ret;
> +
> + /* Preempt disable is needed to protect per-cpu redirect_info between
> + * BPF prog and skb_do_redirect(). The call_rcu in bpf_prog_put() and
> + * access to maps strictly require a rcu_read_lock() for protection,
> + * mixing with BH RCU lock doesn't work.
> + */
> + preempt_disable();
> + rcu_read_lock();
> + bpf_compute_data_end(skb);
> + ret = BPF_PROG_RUN(lwt->prog, skb);
> + rcu_read_unlock();
> +
> + switch (ret) {
> + case BPF_OK:
> + break;
> +
> + case BPF_REDIRECT:
> + if (!can_redirect) {
> + WARN_ONCE(1, "Illegal redirect return code in prog %s\n",
> + lwt->name ? : "<unknown>");
> + ret = BPF_OK;
> + } else {
> + ret = skb_do_redirect(skb);
> + if (ret == 0)
> + ret = BPF_REDIRECT;
> + }
> + break;
> +
> + case BPF_DROP:
> + kfree_skb(skb);
> + ret = -EPERM;
> + break;
> +
> + default:
> + WARN_ONCE(1, "Illegal LWT BPF return value %u, expect packet loss\n",
> + ret);
> + kfree_skb(skb);
> + ret = -EINVAL;
> + break;
> + }
> +
> + preempt_enable();
> +
> + return ret;
> +}
> +
> +static int bpf_input(struct sk_buff *skb)
> +{
> + struct dst_entry *dst = skb_dst(skb);
> + struct bpf_lwt *bpf;
> + int ret;
> +
> + bpf = bpf_lwt_lwtunnel(dst->lwtstate);
> + if (bpf->in.prog) {
> + ret = run_lwt_bpf(skb, &bpf->in, dst, NO_REDIRECT);
> + if (ret < 0)
> + return ret;
> + }
> +
> + if (unlikely(!dst->lwtstate->orig_input)) {
> + WARN_ONCE(1, "orig_input not set on dst for prog %s\n",
> + bpf->out.name);
> + kfree_skb(skb);
> + return -EINVAL;
> + }
> +
> + return dst->lwtstate->orig_input(skb);
> +}
> +
> +static int bpf_output(struct net *net, struct sock *sk, struct sk_buff *skb)
> +{
> + struct dst_entry *dst = skb_dst(skb);
> + struct bpf_lwt *bpf;
> + int ret;
> +
> + bpf = bpf_lwt_lwtunnel(dst->lwtstate);
> + if (bpf->out.prog) {
> + ret = run_lwt_bpf(skb, &bpf->out, dst, NO_REDIRECT);
> + if (ret < 0)
> + return ret;
> + }
> +
> + if (unlikely(!dst->lwtstate->orig_output)) {
> + WARN_ONCE(1, "orig_output not set on dst for prog %s\n",
> + bpf->out.name);
> + kfree_skb(skb);
> + return -EINVAL;
> + }
> +
> + return dst->lwtstate->orig_output(net, sk, skb);
Thomas,
The BPF program may have changed the destination address so continuing
with original route in skb may not be appropriate here. This was fixed
in ila_lwt by calling ip6_route_output and we were able to dst cache
facility to cache the route to avoid cost of looking it up on every
packet. Since the kernel has no insight into what the BPF program
does to the packet I'd suggest 1) checking if destination address
changed by BPF and if it did then call route_output to get new route
2) If the LWT destination is a host route then try to keep a dst
cache. This would entail checking destination address on return that
it is the same one as kept in the dst cache.
Tom
> +}
> +
> +static int bpf_xmit(struct sk_buff *skb)
> +{
> + struct dst_entry *dst = skb_dst(skb);
> + struct bpf_lwt *bpf;
> +
> + bpf = bpf_lwt_lwtunnel(dst->lwtstate);
> + if (bpf->xmit.prog) {
> + int ret;
> +
> + ret = run_lwt_bpf(skb, &bpf->xmit, dst, CAN_REDIRECT);
> + switch (ret) {
> + case BPF_OK:
> + return LWTUNNEL_XMIT_CONTINUE;
> + case BPF_REDIRECT:
> + return LWTUNNEL_XMIT_DONE;
> + default:
> + return ret;
> + }
> + }
> +
> + return LWTUNNEL_XMIT_CONTINUE;
> +}
> +
> +static void bpf_lwt_prog_destroy(struct bpf_lwt_prog *prog)
> +{
> + if (prog->prog)
> + bpf_prog_put(prog->prog);
> +
> + kfree(prog->name);
> +}
> +
> +static void bpf_destroy_state(struct lwtunnel_state *lwt)
> +{
> + struct bpf_lwt *bpf = bpf_lwt_lwtunnel(lwt);
> +
> + bpf_lwt_prog_destroy(&bpf->in);
> + bpf_lwt_prog_destroy(&bpf->out);
> + bpf_lwt_prog_destroy(&bpf->xmit);
> +}
> +
> +static const struct nla_policy bpf_prog_policy[LWT_BPF_PROG_MAX + 1] = {
> + [LWT_BPF_PROG_FD] = { .type = NLA_U32, },
> + [LWT_BPF_PROG_NAME] = { .type = NLA_NUL_STRING,
> + .len = MAX_PROG_NAME },
> +};
> +
> +static int bpf_parse_prog(struct nlattr *attr, struct bpf_lwt_prog *prog,
> + enum bpf_prog_type type)
> +{
> + struct nlattr *tb[LWT_BPF_PROG_MAX + 1];
> + struct bpf_prog *p;
> + int ret;
> + u32 fd;
> +
> + ret = nla_parse_nested(tb, LWT_BPF_PROG_MAX, attr, bpf_prog_policy);
> + if (ret < 0)
> + return ret;
> +
> + if (!tb[LWT_BPF_PROG_FD] || !tb[LWT_BPF_PROG_NAME])
> + return -EINVAL;
> +
> + prog->name = nla_memdup(tb[LWT_BPF_PROG_NAME], GFP_KERNEL);
> + if (!prog->name)
> + return -ENOMEM;
> +
> + fd = nla_get_u32(tb[LWT_BPF_PROG_FD]);
> + p = bpf_prog_get_type(fd, type);
> + if (IS_ERR(p))
> + return PTR_ERR(p);
> +
> + prog->prog = p;
> +
> + return 0;
> +}
> +
> +static const struct nla_policy bpf_nl_policy[LWT_BPF_MAX + 1] = {
> + [LWT_BPF_IN] = { .type = NLA_NESTED, },
> + [LWT_BPF_OUT] = { .type = NLA_NESTED, },
> + [LWT_BPF_XMIT] = { .type = NLA_NESTED, },
> +};
> +
> +static int bpf_build_state(struct net_device *dev, struct nlattr *nla,
> + unsigned int family, const void *cfg,
> + struct lwtunnel_state **ts)
> +{
> + struct nlattr *tb[LWT_BPF_MAX + 1];
> + struct lwtunnel_state *newts;
> + struct bpf_lwt *bpf;
> + int ret;
> +
> + ret = nla_parse_nested(tb, LWT_BPF_MAX, nla, bpf_nl_policy);
> + if (ret < 0)
> + return ret;
> +
> + if (!tb[LWT_BPF_IN] && !tb[LWT_BPF_OUT] && !tb[LWT_BPF_XMIT])
> + return -EINVAL;
> +
> + newts = lwtunnel_state_alloc(sizeof(*bpf));
> + if (!newts)
> + return -ENOMEM;
> +
> + newts->type = LWTUNNEL_ENCAP_BPF;
> + bpf = bpf_lwt_lwtunnel(newts);
> +
> + if (tb[LWT_BPF_IN]) {
> + ret = bpf_parse_prog(tb[LWT_BPF_IN], &bpf->in,
> + BPF_PROG_TYPE_LWT_IN);
> + if (ret < 0) {
> + kfree(newts);
> + return ret;
> + }
> +
> + newts->flags |= LWTUNNEL_STATE_INPUT_REDIRECT;
> + }
> +
> + if (tb[LWT_BPF_OUT]) {
> + ret = bpf_parse_prog(tb[LWT_BPF_OUT], &bpf->out,
> + BPF_PROG_TYPE_LWT_OUT);
> + if (ret < 0) {
> + bpf_destroy_state(newts);
> + kfree(newts);
> + return ret;
> + }
> +
> + newts->flags |= LWTUNNEL_STATE_OUTPUT_REDIRECT;
> + }
> +
> + if (tb[LWT_BPF_XMIT]) {
> + ret = bpf_parse_prog(tb[LWT_BPF_XMIT], &bpf->xmit,
> + BPF_PROG_TYPE_LWT_XMIT);
> + if (ret < 0) {
> + bpf_destroy_state(newts);
> + kfree(newts);
> + return ret;
> + }
> +
> + newts->flags |= LWTUNNEL_STATE_XMIT_REDIRECT;
> + }
> +
> + *ts = newts;
> +
> + return 0;
> +}
> +
> +static int bpf_fill_lwt_prog(struct sk_buff *skb, int attr,
> + struct bpf_lwt_prog *prog)
> +{
> + struct nlattr *nest;
> +
> + if (!prog->prog)
> + return 0;
> +
> + nest = nla_nest_start(skb, attr);
> + if (!nest)
> + return -EMSGSIZE;
> +
> + if (prog->name &&
> + nla_put_string(skb, LWT_BPF_PROG_NAME, prog->name))
> + return -EMSGSIZE;
> +
> + return nla_nest_end(skb, nest);
> +}
> +
> +static int bpf_fill_encap_info(struct sk_buff *skb, struct lwtunnel_state *lwt)
> +{
> + struct bpf_lwt *bpf = bpf_lwt_lwtunnel(lwt);
> +
> + if (bpf_fill_lwt_prog(skb, LWT_BPF_IN, &bpf->in) < 0 ||
> + bpf_fill_lwt_prog(skb, LWT_BPF_OUT, &bpf->out) < 0 ||
> + bpf_fill_lwt_prog(skb, LWT_BPF_XMIT, &bpf->xmit) < 0)
> + return -EMSGSIZE;
> +
> + return 0;
> +}
> +
> +static int bpf_encap_nlsize(struct lwtunnel_state *lwtstate)
> +{
> + int nest_len = nla_total_size(sizeof(struct nlattr)) +
> + nla_total_size(MAX_PROG_NAME) + /* LWT_BPF_PROG_NAME */
> + 0;
> +
> + return nest_len + /* LWT_BPF_IN */
> + nest_len + /* LWT_BPF_OUT */
> + nest_len + /* LWT_BPF_XMIT */
> + 0;
> +}
> +
> +int bpf_lwt_prog_cmp(struct bpf_lwt_prog *a, struct bpf_lwt_prog *b)
> +{
> + /* FIXME:
> + * The LWT state is currently rebuilt for delete requests which
> + * results in a new bpf_prog instance. Comparing names for now.
> + */
> + if (!a->name && !b->name)
> + return 0;
> +
> + if (!a->name || !b->name)
> + return 1;
> +
> + return strcmp(a->name, b->name);
> +}
> +
> +static int bpf_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b)
> +{
> + struct bpf_lwt *a_bpf = bpf_lwt_lwtunnel(a);
> + struct bpf_lwt *b_bpf = bpf_lwt_lwtunnel(b);
> +
> + return bpf_lwt_prog_cmp(&a_bpf->in, &b_bpf->in) ||
> + bpf_lwt_prog_cmp(&a_bpf->out, &b_bpf->out) ||
> + bpf_lwt_prog_cmp(&a_bpf->xmit, &b_bpf->xmit);
> +}
> +
> +static const struct lwtunnel_encap_ops bpf_encap_ops = {
> + .build_state = bpf_build_state,
> + .destroy_state = bpf_destroy_state,
> + .input = bpf_input,
> + .output = bpf_output,
> + .xmit = bpf_xmit,
> + .fill_encap = bpf_fill_encap_info,
> + .get_encap_size = bpf_encap_nlsize,
> + .cmp_encap = bpf_encap_cmp,
> +};
> +
> +static int __init bpf_lwt_init(void)
> +{
> + return lwtunnel_encap_add_ops(&bpf_encap_ops, LWTUNNEL_ENCAP_BPF);
> +}
> +
> +subsys_initcall(bpf_lwt_init)
> diff --git a/net/core/lwtunnel.c b/net/core/lwtunnel.c
> index 88fd642..554d901 100644
> --- a/net/core/lwtunnel.c
> +++ b/net/core/lwtunnel.c
> @@ -39,6 +39,7 @@ static const char *lwtunnel_encap_str(enum lwtunnel_encap_types encap_type)
> return "MPLS";
> case LWTUNNEL_ENCAP_ILA:
> return "ILA";
> + case LWTUNNEL_ENCAP_BPF:
> case LWTUNNEL_ENCAP_IP6:
> case LWTUNNEL_ENCAP_IP:
> case LWTUNNEL_ENCAP_NONE:
> --
> 2.7.4
>
^ permalink raw reply
* [net-next PATCH 7/7] stmmac: dwmac-sti: remove unused priv dev member
From: Joachim Eastwood @ 2016-10-30 20:05 UTC (permalink / raw)
To: davem; +Cc: Joachim Eastwood, peppe.cavallaro, alexandre.torgue, netdev
In-Reply-To: <20161030200507.851-1-manabian@gmail.com>
The dev member of struct sti_dwmac is not used anywhere in the driver
so lets just remove it.
Signed-off-by: Joachim Eastwood <manabian@gmail.com>
---
drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c | 2 --
1 file changed, 2 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c
index 0af3faa..f51fb16 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c
@@ -126,7 +126,6 @@ struct sti_dwmac {
struct clk *clk; /* PHY clock */
u32 ctrl_reg; /* GMAC glue-logic control register */
int clk_sel_reg; /* GMAC ext clk selection register */
- struct device *dev;
struct regmap *regmap;
bool gmac_en;
u32 speed;
@@ -274,7 +273,6 @@ static int sti_dwmac_parse_data(struct sti_dwmac *dwmac,
return err;
}
- dwmac->dev = dev;
dwmac->interface = of_get_phy_mode(np);
dwmac->regmap = regmap;
dwmac->gmac_en = of_property_read_bool(np, "st,gmac_en");
--
2.10.1
^ permalink raw reply related
* [net-next PATCH 6/7] stmmac: dwmac-sti: clean up and rename sti_dwmac_init
From: Joachim Eastwood @ 2016-10-30 20:05 UTC (permalink / raw)
To: davem; +Cc: Joachim Eastwood, peppe.cavallaro, alexandre.torgue, netdev
In-Reply-To: <20161030200507.851-1-manabian@gmail.com>
Rename sti_dwmac_init to sti_dwmac_set_phy_mode which is a better
description for what it really does.
Signed-off-by: Joachim Eastwood <manabian@gmail.com>
---
drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c
index aa75a27..0af3faa 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c
@@ -229,9 +229,8 @@ static void stid127_fix_retime_src(void *priv, u32 spd)
regmap_update_bits(dwmac->regmap, reg, STID127_RETIME_SRC_MASK, val);
}
-static int sti_dwmac_init(struct platform_device *pdev, void *priv)
+static int sti_dwmac_set_phy_mode(struct sti_dwmac *dwmac)
{
- struct sti_dwmac *dwmac = priv;
struct regmap *regmap = dwmac->regmap;
int iface = dwmac->interface;
u32 reg = dwmac->ctrl_reg;
@@ -245,7 +244,7 @@ static int sti_dwmac_init(struct platform_device *pdev, void *priv)
val = (iface == PHY_INTERFACE_MODE_REVMII) ? 0 : ENMII;
regmap_update_bits(regmap, reg, ENMII_MASK, val);
- dwmac->fix_retime_src(priv, dwmac->speed);
+ dwmac->fix_retime_src(dwmac, dwmac->speed);
return 0;
}
@@ -350,7 +349,7 @@ static int sti_dwmac_probe(struct platform_device *pdev)
if (ret)
return ret;
- ret = sti_dwmac_init(pdev, plat_dat->bsp_priv);
+ ret = sti_dwmac_set_phy_mode(dwmac);
if (ret)
goto disable_clk;
@@ -389,10 +388,9 @@ static int sti_dwmac_suspend(struct device *dev)
static int sti_dwmac_resume(struct device *dev)
{
struct sti_dwmac *dwmac = get_stmmac_bsp_priv(dev);
- struct platform_device *pdev = to_platform_device(dev);
clk_prepare_enable(dwmac->clk);
- sti_dwmac_init(pdev, dwmac);
+ sti_dwmac_set_phy_mode(dwmac);
return stmmac_resume(dev);
}
--
2.10.1
^ permalink raw reply related
* [net-next PATCH 5/7] stmmac: dwmac-sti: move clk_prepare_enable out of init and add error handling
From: Joachim Eastwood @ 2016-10-30 20:05 UTC (permalink / raw)
To: davem; +Cc: Joachim Eastwood, peppe.cavallaro, alexandre.torgue, netdev
In-Reply-To: <20161030200507.851-1-manabian@gmail.com>
Add clock error handling to probe and in the process move clock enabling
out of sti_dwmac_init() to make this easier.
Signed-off-by: Joachim Eastwood <manabian@gmail.com>
---
drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c | 19 +++++++++++++++----
1 file changed, 15 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c
index e814b68..aa75a27 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c
@@ -237,8 +237,6 @@ static int sti_dwmac_init(struct platform_device *pdev, void *priv)
u32 reg = dwmac->ctrl_reg;
u32 val;
- clk_prepare_enable(dwmac->clk);
-
if (dwmac->gmac_en)
regmap_update_bits(regmap, reg, EN_MASK, EN);
@@ -348,11 +346,23 @@ static int sti_dwmac_probe(struct platform_device *pdev)
plat_dat->bsp_priv = dwmac;
plat_dat->fix_mac_speed = data->fix_retime_src;
- ret = sti_dwmac_init(pdev, plat_dat->bsp_priv);
+ ret = clk_prepare_enable(dwmac->clk);
if (ret)
return ret;
- return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
+ ret = sti_dwmac_init(pdev, plat_dat->bsp_priv);
+ if (ret)
+ goto disable_clk;
+
+ ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
+ if (ret)
+ goto disable_clk;
+
+ return 0;
+
+disable_clk:
+ clk_disable_unprepare(dwmac->clk);
+ return ret;
}
static int sti_dwmac_remove(struct platform_device *pdev)
@@ -381,6 +391,7 @@ static int sti_dwmac_resume(struct device *dev)
struct sti_dwmac *dwmac = get_stmmac_bsp_priv(dev);
struct platform_device *pdev = to_platform_device(dev);
+ clk_prepare_enable(dwmac->clk);
sti_dwmac_init(pdev, dwmac);
return stmmac_resume(dev);
--
2.10.1
^ permalink raw reply related
* [net-next PATCH 4/7] stmmac: dwmac-sti: move st,gmac_en parsing to sti_dwmac_parse_data
From: Joachim Eastwood @ 2016-10-30 20:05 UTC (permalink / raw)
To: davem; +Cc: Joachim Eastwood, peppe.cavallaro, alexandre.torgue, netdev
In-Reply-To: <20161030200507.851-1-manabian@gmail.com>
The sti_dwmac_init() function is called both from probe and resume.
Since DT properties doesn't change between suspend/resume cycles move
parsing of this parameter into sti_dwmac_parse_data() where it belongs.
Signed-off-by: Joachim Eastwood <manabian@gmail.com>
---
drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c
index 09dd2be..e814b68 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c
@@ -128,6 +128,7 @@ struct sti_dwmac {
int clk_sel_reg; /* GMAC ext clk selection register */
struct device *dev;
struct regmap *regmap;
+ bool gmac_en;
u32 speed;
void (*fix_retime_src)(void *priv, unsigned int speed);
};
@@ -233,14 +234,12 @@ static int sti_dwmac_init(struct platform_device *pdev, void *priv)
struct sti_dwmac *dwmac = priv;
struct regmap *regmap = dwmac->regmap;
int iface = dwmac->interface;
- struct device *dev = dwmac->dev;
- struct device_node *np = dev->of_node;
u32 reg = dwmac->ctrl_reg;
u32 val;
clk_prepare_enable(dwmac->clk);
- if (of_property_read_bool(np, "st,gmac_en"))
+ if (dwmac->gmac_en)
regmap_update_bits(regmap, reg, EN_MASK, EN);
regmap_update_bits(regmap, reg, MII_PHY_SEL_MASK, phy_intf_sels[iface]);
@@ -281,6 +280,7 @@ static int sti_dwmac_parse_data(struct sti_dwmac *dwmac,
dwmac->dev = dev;
dwmac->interface = of_get_phy_mode(np);
dwmac->regmap = regmap;
+ dwmac->gmac_en = of_property_read_bool(np, "st,gmac_en");
dwmac->ext_phyclk = of_property_read_bool(np, "st,ext-phyclk");
dwmac->tx_retime_src = TX_RETIME_SRC_NA;
dwmac->speed = SPEED_100;
--
2.10.1
^ permalink raw reply related
* [net-next PATCH 3/7] stmmac: dwmac-sti: add PM ops and resume function
From: Joachim Eastwood @ 2016-10-30 20:05 UTC (permalink / raw)
To: davem; +Cc: Joachim Eastwood, peppe.cavallaro, alexandre.torgue, netdev
In-Reply-To: <20161030200507.851-1-manabian@gmail.com>
Implement PM callbacks and driver remove in the driver instead
of relying on the init/exit hooks in stmmac_platform. This gives
the driver more flexibility in how the code is organized.
Eventually the init/exit callbacks will be deprecated in favor
of the standard PM callbacks and driver remove function.
Signed-off-by: Joachim Eastwood <manabian@gmail.com>
---
drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c | 46 +++++++++++++++++++------
1 file changed, 36 insertions(+), 10 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c
index f009bf4..09dd2be 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c
@@ -253,12 +253,6 @@ static int sti_dwmac_init(struct platform_device *pdev, void *priv)
return 0;
}
-static void sti_dwmac_exit(struct platform_device *pdev, void *priv)
-{
- struct sti_dwmac *dwmac = priv;
-
- clk_disable_unprepare(dwmac->clk);
-}
static int sti_dwmac_parse_data(struct sti_dwmac *dwmac,
struct platform_device *pdev)
{
@@ -352,8 +346,6 @@ static int sti_dwmac_probe(struct platform_device *pdev)
dwmac->fix_retime_src = data->fix_retime_src;
plat_dat->bsp_priv = dwmac;
- plat_dat->init = sti_dwmac_init;
- plat_dat->exit = sti_dwmac_exit;
plat_dat->fix_mac_speed = data->fix_retime_src;
ret = sti_dwmac_init(pdev, plat_dat->bsp_priv);
@@ -363,6 +355,40 @@ static int sti_dwmac_probe(struct platform_device *pdev)
return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
}
+static int sti_dwmac_remove(struct platform_device *pdev)
+{
+ struct sti_dwmac *dwmac = get_stmmac_bsp_priv(&pdev->dev);
+ int ret = stmmac_dvr_remove(&pdev->dev);
+
+ clk_disable_unprepare(dwmac->clk);
+
+ return ret;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int sti_dwmac_suspend(struct device *dev)
+{
+ struct sti_dwmac *dwmac = get_stmmac_bsp_priv(dev);
+ int ret = stmmac_suspend(dev);
+
+ clk_disable_unprepare(dwmac->clk);
+
+ return ret;
+}
+
+static int sti_dwmac_resume(struct device *dev)
+{
+ struct sti_dwmac *dwmac = get_stmmac_bsp_priv(dev);
+ struct platform_device *pdev = to_platform_device(dev);
+
+ sti_dwmac_init(pdev, dwmac);
+
+ return stmmac_resume(dev);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+SIMPLE_DEV_PM_OPS(sti_dwmac_pm_ops, sti_dwmac_suspend, sti_dwmac_resume);
+
static const struct sti_dwmac_of_data stih4xx_dwmac_data = {
.fix_retime_src = stih4xx_fix_retime_src,
};
@@ -382,10 +408,10 @@ MODULE_DEVICE_TABLE(of, sti_dwmac_match);
static struct platform_driver sti_dwmac_driver = {
.probe = sti_dwmac_probe,
- .remove = stmmac_pltfr_remove,
+ .remove = sti_dwmac_remove,
.driver = {
.name = "sti-dwmac",
- .pm = &stmmac_pltfr_pm_ops,
+ .pm = &sti_dwmac_pm_ops,
.of_match_table = sti_dwmac_match,
},
};
--
2.10.1
^ permalink raw reply related
* [net-next PATCH 2/7] stmmac: dwmac-sti: remove clk NULL checks
From: Joachim Eastwood @ 2016-10-30 20:05 UTC (permalink / raw)
To: davem; +Cc: Joachim Eastwood, peppe.cavallaro, alexandre.torgue, netdev
In-Reply-To: <20161030200507.851-1-manabian@gmail.com>
Since sti_dwmac_parse_data() sets dwmac->clk to NULL if not clock was
provided in DT and NULL is a valid clock there is no need to check for
NULL before using this clock.
Signed-off-by: Joachim Eastwood <manabian@gmail.com>
---
drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c
index 075ed42..f009bf4 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c
@@ -191,7 +191,7 @@ static void stih4xx_fix_retime_src(void *priv, u32 spd)
}
}
- if (src == TX_RETIME_SRC_CLKGEN && dwmac->clk && freq)
+ if (src == TX_RETIME_SRC_CLKGEN && freq)
clk_set_rate(dwmac->clk, freq);
regmap_update_bits(dwmac->regmap, reg, STIH4XX_RETIME_SRC_MASK,
@@ -222,7 +222,7 @@ static void stid127_fix_retime_src(void *priv, u32 spd)
freq = DWMAC_2_5MHZ;
}
- if (dwmac->clk && freq)
+ if (freq)
clk_set_rate(dwmac->clk, freq);
regmap_update_bits(dwmac->regmap, reg, STID127_RETIME_SRC_MASK, val);
@@ -238,8 +238,7 @@ static int sti_dwmac_init(struct platform_device *pdev, void *priv)
u32 reg = dwmac->ctrl_reg;
u32 val;
- if (dwmac->clk)
- clk_prepare_enable(dwmac->clk);
+ clk_prepare_enable(dwmac->clk);
if (of_property_read_bool(np, "st,gmac_en"))
regmap_update_bits(regmap, reg, EN_MASK, EN);
@@ -258,8 +257,7 @@ static void sti_dwmac_exit(struct platform_device *pdev, void *priv)
{
struct sti_dwmac *dwmac = priv;
- if (dwmac->clk)
- clk_disable_unprepare(dwmac->clk);
+ clk_disable_unprepare(dwmac->clk);
}
static int sti_dwmac_parse_data(struct sti_dwmac *dwmac,
struct platform_device *pdev)
--
2.10.1
^ permalink raw reply related
* [net-next PATCH 1/7] stmmac: dwmac-sti: remove useless of_node check
From: Joachim Eastwood @ 2016-10-30 20:05 UTC (permalink / raw)
To: davem; +Cc: Joachim Eastwood, peppe.cavallaro, alexandre.torgue, netdev
In-Reply-To: <20161030200507.851-1-manabian@gmail.com>
Since dwmac-sti is a DT only driver checking for OF node is not necessary.
Signed-off-by: Joachim Eastwood <manabian@gmail.com>
---
drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c | 3 ---
1 file changed, 3 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c
index 58c05ac..075ed42 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c
@@ -270,9 +270,6 @@ static int sti_dwmac_parse_data(struct sti_dwmac *dwmac,
struct regmap *regmap;
int err;
- if (!np)
- return -EINVAL;
-
/* clk selection from extra syscfg register */
dwmac->clk_sel_reg = -ENXIO;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sti-clkconf");
--
2.10.1
^ permalink raw reply related
* [net-next PATCH 0/7] stmmac: dwmac-sti refactor+cleanup
From: Joachim Eastwood @ 2016-10-30 20:05 UTC (permalink / raw)
To: davem; +Cc: Joachim Eastwood, peppe.cavallaro, alexandre.torgue, netdev
This patch set aims to remove the init/exit callbacks from the
dwmac-sti driver and instead use standard PM callbacks. Doing this
will also allow us to cleanup the driver.
Eventually the init/exit callbacks will be deprecated and removed
from all drivers dwmac-* except for dwmac-generic. Drivers will be
refactored to use standard PM and remove callbacks.
Note that this patch set has only been test compiled and no functional
change is intended.
Joachim Eastwood (7):
stmmac: dwmac-sti: remove useless of_node check
stmmac: dwmac-sti: remove clk NULL checks
stmmac: dwmac-sti: add PM ops and resume function
stmmac: dwmac-sti: move st,gmac_en parsing to sti_dwmac_parse_data
stmmac: dwmac-sti: move clk_prepare_enable out of init and add error handling
stmmac: dwmac-sti: clean up and rename sti_dwmac_init
stmmac: dwmac-sti: remove unused priv dev member
drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c | 86 ++++++++++++++++---------
1 file changed, 57 insertions(+), 29 deletions(-)
--
2.10.1
^ permalink raw reply
* Re: Let's do P4
From: Jiri Pirko @ 2016-10-30 19:56 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Thomas Graf, John Fastabend, netdev, davem, jhs, roopa,
simon.horman, ast, daniel, prem, hannes, jbenc, tom, mattyk,
idosch, eladr, yotamg, nogahf, ogerlitz, linville, andy,
f.fainelli, dsa, vivien.didelot, andrew, ivecera,
Maciej Żenczykowski
In-Reply-To: <20161030184443.21b8a3d4@laptop>
Sun, Oct 30, 2016 at 07:44:43PM CET, kubakici@wp.pl wrote:
>On Sun, 30 Oct 2016 19:01:03 +0100, Jiri Pirko wrote:
>> Sun, Oct 30, 2016 at 06:45:26PM CET, kubakici@wp.pl wrote:
>> >On Sun, 30 Oct 2016 17:38:36 +0100, Jiri Pirko wrote:
>> >> Sun, Oct 30, 2016 at 11:26:49AM CET, tgraf@suug.ch wrote:
>> [...]
>> [...]
>> >> [...]
>> >> [...]
>> >> [...]
>> >> [...]
>> [...]
>> >>
>> >> Agreed.
>> >
>> >Just to clarify my intention here was not to suggest the use of eBPF as
>> >the IR. I was merely cautioning against bundling the new API with P4,
>> >for multiple reasons. As John mentioned P4 spec was evolving in the
>> >past. The spec is designed for HW more capable than the switch ASICs we
>> >have today. As vendors move to provide more configurability we may need
>> >to extend the API beyond P4. We may want to extend this API to for SW
>> >hand-offs (as suggested by Thomas) which are not part of P4 spec. Also
>> >John showed examples of matchd software which already uses P4 at the
>> >frontend today and translates it to different targets (eBPF, u32, HW).
>> >It may just be about the naming but I feel like calling the new API
>> >more generically, switch AST or some such may help to avoid unnecessary
>> >ties and confusion.
>>
>> Well, that basically means to create "something" that could be be used
>> to translate p4 source to. Not sure how exactly this "something" should
>> look like and how different would it be from p4. I thought it might
>> be good to benefit from the p4 definition and use it directly. Not sure.
>
>We have to translate the P4 into "something" already, that something
>is the AST we will load into the kernel. Or were you planning to use
>some official P4 AST? I'm not suggesting we add our own high level
I'm not aware of existence of some official P4 AST. We have to figure it
out.
>language. I agree that P4 is a good starting point, and perhaps a good
>high level language. I'm just cautious of creating an equivalency
>between high level language (P4) and the kernel ABI.
Understood. Definitelly good to be very cautious when defining a kernel
API.
>
>Perhaps I'm just wasting everyone's time with this.
>
>> >>
>> >> Exactly. Following drawing shows p4 pipeline setup for SW and Hw:
>> >>
>> >> |
>> >> | +--> ebpf engine
>> >> | |
>> >> | |
>> >> | compilerB
>> >> | ^
>> >> | |
>> >> p4src --> compilerA --> p4ast --TCNL--> cls_p4 --+-> driver -> compilerC -> HW
>> >> |
>> >> userspace | kernel
>> >> |
>> >>
>> >> Now please consider runtime API for rule insertion/removal/stats/etc.
>> >> Also, the single API is cls_p4 here:
>> >>
>> >> |
>> >> |
>> >> |
>> >> |
>> >> | ebpf map fillup
>> >> | ^
>> >> | |
>> >> p4 rule --TCNL--> cls_p4 --+-> driver -> HW table fillup
>> >> |
>> >> userspace | kernel
>> >>
>> >
>> >My understanding was that the main purpose of SW eBPF translation would
>> >be to piggy back on eBPF userspace map API. This seems not to be the
>> >case here? Is "P4 rule" being added via some new API? From performance
>>
>> cls_p4 TC classifier.
>
>Oh, so the cls_p4 is just a proxy forwarding the requests to drivers
>or eBPF backend. Got it. Sorry for being slow. And the requests
>come down via change() op or something new? I wonder how such scheme
>compares to eBPF maps performance-wise (updates/sec).
I have no numbers at this time. I guess Jamal and Alexei did some
measurements in this are in the past.
>
>> >perspective the SW AST implementation would probably not be any slower
>> >than u32, so I don't think we need eBPF for performance. I must be
>> >misreading this, if we want eBPF fallback we must extend eBPF with all
>> >the map types anyway... so we could just use eBPF map API? I believe
>> >John has already done some work in this space (see his GitHub :))
>>
>> I don't think you can use existing BPF maps kernel API. You would still
>> have to have another API just for the offloaded datapath. And that is
>> a bypass. I strongly believe we need a single kernel API for both
>> SW and HW datapath setup and runtime configuration.
>
>Agreed, single API is a must. What is the HW characteristic which
>doesn't fit with eBPF map API, though? For eBPF offload I was planning
>on adding offload hooks on eBPF map lookup/update paths and a way of
>associating the map with a netdev. This should be enough to forward
>updates to the driver and intercept reads to return the right
>statistics.
^ permalink raw reply
* Re: Let's do P4
From: Jakub Kicinski @ 2016-10-30 18:44 UTC (permalink / raw)
To: Jiri Pirko
Cc: Thomas Graf, John Fastabend, netdev, davem, jhs, roopa,
simon.horman, ast, daniel, prem, hannes, jbenc, tom, mattyk,
idosch, eladr, yotamg, nogahf, ogerlitz, linville, andy,
f.fainelli, dsa, vivien.didelot, andrew, ivecera,
Maciej Żenczykowski
In-Reply-To: <20161030180103.GD1686@nanopsycho.orion>
On Sun, 30 Oct 2016 19:01:03 +0100, Jiri Pirko wrote:
> Sun, Oct 30, 2016 at 06:45:26PM CET, kubakici@wp.pl wrote:
> >On Sun, 30 Oct 2016 17:38:36 +0100, Jiri Pirko wrote:
> >> Sun, Oct 30, 2016 at 11:26:49AM CET, tgraf@suug.ch wrote:
> [...]
> [...]
> >> [...]
> >> [...]
> >> [...]
> >> [...]
> [...]
> >>
> >> Agreed.
> >
> >Just to clarify my intention here was not to suggest the use of eBPF as
> >the IR. I was merely cautioning against bundling the new API with P4,
> >for multiple reasons. As John mentioned P4 spec was evolving in the
> >past. The spec is designed for HW more capable than the switch ASICs we
> >have today. As vendors move to provide more configurability we may need
> >to extend the API beyond P4. We may want to extend this API to for SW
> >hand-offs (as suggested by Thomas) which are not part of P4 spec. Also
> >John showed examples of matchd software which already uses P4 at the
> >frontend today and translates it to different targets (eBPF, u32, HW).
> >It may just be about the naming but I feel like calling the new API
> >more generically, switch AST or some such may help to avoid unnecessary
> >ties and confusion.
>
> Well, that basically means to create "something" that could be be used
> to translate p4 source to. Not sure how exactly this "something" should
> look like and how different would it be from p4. I thought it might
> be good to benefit from the p4 definition and use it directly. Not sure.
We have to translate the P4 into "something" already, that something
is the AST we will load into the kernel. Or were you planning to use
some official P4 AST? I'm not suggesting we add our own high level
language. I agree that P4 is a good starting point, and perhaps a good
high level language. I'm just cautious of creating an equivalency
between high level language (P4) and the kernel ABI.
Perhaps I'm just wasting everyone's time with this.
> >>
> >> Exactly. Following drawing shows p4 pipeline setup for SW and Hw:
> >>
> >> |
> >> | +--> ebpf engine
> >> | |
> >> | |
> >> | compilerB
> >> | ^
> >> | |
> >> p4src --> compilerA --> p4ast --TCNL--> cls_p4 --+-> driver -> compilerC -> HW
> >> |
> >> userspace | kernel
> >> |
> >>
> >> Now please consider runtime API for rule insertion/removal/stats/etc.
> >> Also, the single API is cls_p4 here:
> >>
> >> |
> >> |
> >> |
> >> |
> >> | ebpf map fillup
> >> | ^
> >> | |
> >> p4 rule --TCNL--> cls_p4 --+-> driver -> HW table fillup
> >> |
> >> userspace | kernel
> >>
> >
> >My understanding was that the main purpose of SW eBPF translation would
> >be to piggy back on eBPF userspace map API. This seems not to be the
> >case here? Is "P4 rule" being added via some new API? From performance
>
> cls_p4 TC classifier.
Oh, so the cls_p4 is just a proxy forwarding the requests to drivers
or eBPF backend. Got it. Sorry for being slow. And the requests
come down via change() op or something new? I wonder how such scheme
compares to eBPF maps performance-wise (updates/sec).
> >perspective the SW AST implementation would probably not be any slower
> >than u32, so I don't think we need eBPF for performance. I must be
> >misreading this, if we want eBPF fallback we must extend eBPF with all
> >the map types anyway... so we could just use eBPF map API? I believe
> >John has already done some work in this space (see his GitHub :))
>
> I don't think you can use existing BPF maps kernel API. You would still
> have to have another API just for the offloaded datapath. And that is
> a bypass. I strongly believe we need a single kernel API for both
> SW and HW datapath setup and runtime configuration.
Agreed, single API is a must. What is the HW characteristic which
doesn't fit with eBPF map API, though? For eBPF offload I was planning
on adding offload hooks on eBPF map lookup/update paths and a way of
associating the map with a netdev. This should be enough to forward
updates to the driver and intercept reads to return the right
statistics.
^ permalink raw reply
* Re: Let's do P4
From: Jiri Pirko @ 2016-10-30 18:01 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Thomas Graf, John Fastabend, netdev, davem, jhs, roopa,
simon.horman, ast, daniel, prem, hannes, jbenc, tom, mattyk,
idosch, eladr, yotamg, nogahf, ogerlitz, linville, andy,
f.fainelli, dsa, vivien.didelot, andrew, ivecera,
Maciej Żenczykowski
In-Reply-To: <20161030174526.4947c424@laptop>
Sun, Oct 30, 2016 at 06:45:26PM CET, kubakici@wp.pl wrote:
>On Sun, 30 Oct 2016 17:38:36 +0100, Jiri Pirko wrote:
>> Sun, Oct 30, 2016 at 11:26:49AM CET, tgraf@suug.ch wrote:
>> >On 10/30/16 at 08:44am, Jiri Pirko wrote:
>> >> Sat, Oct 29, 2016 at 06:46:21PM CEST, john.fastabend@gmail.com wrote:
>> [...]
>> [...]
>> [...]
>> [...]
>> >
>> >My assumption was that a new IR is defined which is easier to parse than
>> >eBPF which is targeted at execution on a CPU and not indented for pattern
>> >matching. Just looking at how llvm creates different patterns and reorders
>> >instructions, I'm not seeing how eBPF can serve as a general purpose IR
>> >if the objective is to allow fairly flexible generation of the bytecode.
>> >Hence the alternative IR serving as additional metadata complementing the
>> >eBPF program.
>>
>> Agreed.
>
>Just to clarify my intention here was not to suggest the use of eBPF as
>the IR. I was merely cautioning against bundling the new API with P4,
>for multiple reasons. As John mentioned P4 spec was evolving in the
>past. The spec is designed for HW more capable than the switch ASICs we
>have today. As vendors move to provide more configurability we may need
>to extend the API beyond P4. We may want to extend this API to for SW
>hand-offs (as suggested by Thomas) which are not part of P4 spec. Also
>John showed examples of matchd software which already uses P4 at the
>frontend today and translates it to different targets (eBPF, u32, HW).
>It may just be about the naming but I feel like calling the new API
>more generically, switch AST or some such may help to avoid unnecessary
>ties and confusion.
Well, that basically means to create "something" that could be be used
to translate p4 source to. Not sure how exactly this "something" should
look like and how different would it be from p4. I thought it might
be good to benefit from the p4 definition and use it directly. Not sure.
>
>> >I understand what you mean with two APIs now. You want a single IR
>> >block and divide the SW/HW part in the kernel rather than let llvm or
>> >something else do it.
>>
>> Exactly. Following drawing shows p4 pipeline setup for SW and Hw:
>>
>> |
>> | +--> ebpf engine
>> | |
>> | |
>> | compilerB
>> | ^
>> | |
>> p4src --> compilerA --> p4ast --TCNL--> cls_p4 --+-> driver -> compilerC -> HW
>> |
>> userspace | kernel
>> |
>>
>> Now please consider runtime API for rule insertion/removal/stats/etc.
>> Also, the single API is cls_p4 here:
>>
>> |
>> |
>> |
>> |
>> | ebpf map fillup
>> | ^
>> | |
>> p4 rule --TCNL--> cls_p4 --+-> driver -> HW table fillup
>> |
>> userspace | kernel
>>
>
>My understanding was that the main purpose of SW eBPF translation would
>be to piggy back on eBPF userspace map API. This seems not to be the
>case here? Is "P4 rule" being added via some new API? From performance
cls_p4 TC classifier.
>perspective the SW AST implementation would probably not be any slower
>than u32, so I don't think we need eBPF for performance. I must be
>misreading this, if we want eBPF fallback we must extend eBPF with all
>the map types anyway... so we could just use eBPF map API? I believe
>John has already done some work in this space (see his GitHub :))
I don't think you can use existing BPF maps kernel API. You would still
have to have another API just for the offloaded datapath. And that is
a bypass. I strongly believe we need a single kernel API for both
SW and HW datapath setup and runtime configuration.
>
>As for AST -> eBPF translator in the kernel, IMHO it could be very
>useful. Since all the drivers will have to implement translators
>anyway, the eBPF translator may help to build a good shared
>infrastructure. I mean - it could be a starting place for sharing code
>between drivers if done properly.
Agreed.
>
>> >> Well for hw offload, every driver has to parse the IR (whatever will it
>> >> be in) and program HW accordingly. Similar parsing and translation would
>> >> be needed for SW path, to translate into eBPF. I don't think it would be
>> >> more complex than in the drivers. Should be fine.
>> >
>> >I'm not sure I see why anyone would ever want to use an IR for SW
>> >purposes which is restricted to the lowest common denominator of HW.
>> >A good example here is OpenFlow and how some of its SW consumers
>> >have evolved with extensions which cannot be mappepd to HW easily.
>> >The same seems to happen with P4 as it introduces the concept of
>> >state and other concepts which are hard to map for dumb HW. P4 doesn't
>> >magically solve this problem, the fundamental difference in
>> >capabilities between HW and SW remain.
>> >
>> [...]
>> [...]
>> [...]
>> >>
>> >> Yeah, I was also thinking about something similar to your Flow-API,
>> >> but we need something more generic I believe.
>> >>
>> [...]
>> >>
>> >> Btw, Flow-API was rejected because it was a clean kernel-bypass. In case
>> >> of p4, if we do what Thomas is suggesting, having x.bpf for SW and
>> >> x.p4ast for HW, that would be the very same kernel-bypass. Therefore I
>> >> strongly believe there should be a single kernel API for p4 SW+HW - for
>> >> both p4 program insertion and runtime configuration.
>> >
>> >I think you misunderstand me. This is not what I'm proposing at all.
>> >In either model, the kernel receives the same IR and can reject.
>> >
>> >The rule is very clear: we can't allow to program anything that the
>> >kernel is not capable of doing in SW, right? That was the key take
>> >away from that discussion.
>>
>>
>> ***
>> Exactly. But if you treat p4ast as a "metadata" of ebpf program destined
>> solely to setup HW, that in my opinion is a bypass. Because the ebpf part
>> and p4ast part could have no relacionship with each other. So I see it as
>> 2 independent APIs. One for SW, one for HW. And having this kind od API
>> for hw only is a bypass.
>
>+1
>Adding metadata to eBPF programs usually fails because the verification
>that the metadata is correct in the kernel is usually not much easier
>than generating it in the first place. And not verifying it opens up a
>way of kernel bypass.
^ permalink raw reply
* Re: Let's do P4
From: Jakub Kicinski @ 2016-10-30 17:45 UTC (permalink / raw)
To: Jiri Pirko
Cc: Thomas Graf, John Fastabend, netdev, davem, jhs, roopa,
simon.horman, ast, daniel, prem, hannes, jbenc, tom, mattyk,
idosch, eladr, yotamg, nogahf, ogerlitz, linville, andy,
f.fainelli, dsa, vivien.didelot, andrew, ivecera,
Maciej Żenczykowski
In-Reply-To: <20161030163836.GC1686@nanopsycho.orion>
On Sun, 30 Oct 2016 17:38:36 +0100, Jiri Pirko wrote:
> Sun, Oct 30, 2016 at 11:26:49AM CET, tgraf@suug.ch wrote:
> >On 10/30/16 at 08:44am, Jiri Pirko wrote:
> >> Sat, Oct 29, 2016 at 06:46:21PM CEST, john.fastabend@gmail.com wrote:
> [...]
> [...]
> [...]
> [...]
> >
> >My assumption was that a new IR is defined which is easier to parse than
> >eBPF which is targeted at execution on a CPU and not indented for pattern
> >matching. Just looking at how llvm creates different patterns and reorders
> >instructions, I'm not seeing how eBPF can serve as a general purpose IR
> >if the objective is to allow fairly flexible generation of the bytecode.
> >Hence the alternative IR serving as additional metadata complementing the
> >eBPF program.
>
> Agreed.
Just to clarify my intention here was not to suggest the use of eBPF as
the IR. I was merely cautioning against bundling the new API with P4,
for multiple reasons. As John mentioned P4 spec was evolving in the
past. The spec is designed for HW more capable than the switch ASICs we
have today. As vendors move to provide more configurability we may need
to extend the API beyond P4. We may want to extend this API to for SW
hand-offs (as suggested by Thomas) which are not part of P4 spec. Also
John showed examples of matchd software which already uses P4 at the
frontend today and translates it to different targets (eBPF, u32, HW).
It may just be about the naming but I feel like calling the new API
more generically, switch AST or some such may help to avoid unnecessary
ties and confusion.
> >I understand what you mean with two APIs now. You want a single IR
> >block and divide the SW/HW part in the kernel rather than let llvm or
> >something else do it.
>
> Exactly. Following drawing shows p4 pipeline setup for SW and Hw:
>
> |
> | +--> ebpf engine
> | |
> | |
> | compilerB
> | ^
> | |
> p4src --> compilerA --> p4ast --TCNL--> cls_p4 --+-> driver -> compilerC -> HW
> |
> userspace | kernel
> |
>
> Now please consider runtime API for rule insertion/removal/stats/etc.
> Also, the single API is cls_p4 here:
>
> |
> |
> |
> |
> | ebpf map fillup
> | ^
> | |
> p4 rule --TCNL--> cls_p4 --+-> driver -> HW table fillup
> |
> userspace | kernel
>
My understanding was that the main purpose of SW eBPF translation would
be to piggy back on eBPF userspace map API. This seems not to be the
case here? Is "P4 rule" being added via some new API? From performance
perspective the SW AST implementation would probably not be any slower
than u32, so I don't think we need eBPF for performance. I must be
misreading this, if we want eBPF fallback we must extend eBPF with all
the map types anyway... so we could just use eBPF map API? I believe
John has already done some work in this space (see his GitHub :))
As for AST -> eBPF translator in the kernel, IMHO it could be very
useful. Since all the drivers will have to implement translators
anyway, the eBPF translator may help to build a good shared
infrastructure. I mean - it could be a starting place for sharing code
between drivers if done properly.
> >> Well for hw offload, every driver has to parse the IR (whatever will it
> >> be in) and program HW accordingly. Similar parsing and translation would
> >> be needed for SW path, to translate into eBPF. I don't think it would be
> >> more complex than in the drivers. Should be fine.
> >
> >I'm not sure I see why anyone would ever want to use an IR for SW
> >purposes which is restricted to the lowest common denominator of HW.
> >A good example here is OpenFlow and how some of its SW consumers
> >have evolved with extensions which cannot be mappepd to HW easily.
> >The same seems to happen with P4 as it introduces the concept of
> >state and other concepts which are hard to map for dumb HW. P4 doesn't
> >magically solve this problem, the fundamental difference in
> >capabilities between HW and SW remain.
> >
> [...]
> [...]
> [...]
> >>
> >> Yeah, I was also thinking about something similar to your Flow-API,
> >> but we need something more generic I believe.
> >>
> [...]
> >>
> >> Btw, Flow-API was rejected because it was a clean kernel-bypass. In case
> >> of p4, if we do what Thomas is suggesting, having x.bpf for SW and
> >> x.p4ast for HW, that would be the very same kernel-bypass. Therefore I
> >> strongly believe there should be a single kernel API for p4 SW+HW - for
> >> both p4 program insertion and runtime configuration.
> >
> >I think you misunderstand me. This is not what I'm proposing at all.
> >In either model, the kernel receives the same IR and can reject.
> >
> >The rule is very clear: we can't allow to program anything that the
> >kernel is not capable of doing in SW, right? That was the key take
> >away from that discussion.
>
>
> ***
> Exactly. But if you treat p4ast as a "metadata" of ebpf program destined
> solely to setup HW, that in my opinion is a bypass. Because the ebpf part
> and p4ast part could have no relacionship with each other. So I see it as
> 2 independent APIs. One for SW, one for HW. And having this kind od API
> for hw only is a bypass.
+1
Adding metadata to eBPF programs usually fails because the verification
that the metadata is correct in the kernel is usually not much easier
than generating it in the first place. And not verifying it opens up a
way of kernel bypass.
^ permalink raw reply
* Re: [PATCH net-next 0/2] mlx4 XDP TX refactor
From: David Miller @ 2016-10-30 16:44 UTC (permalink / raw)
To: ttoukan.linux; +Cc: tariqt, netdev, eranbe
In-Reply-To: <0773c58c-293d-38be-9844-e8f92d135aec@gmail.com>
From: Tariq Toukan <ttoukan.linux@gmail.com>
Date: Sun, 30 Oct 2016 18:21:26 +0200
> Hi Dave,
>
> This series makes Brenden's fix unneeded:
> 958b3d396d7f ("net/mlx4_en: fixup xdp tx irq to match rx")
>
> The fix got into net, but yet to be in net-next.
>
> Should I wait with this series and send a re-spin, with a revert of
> the fix, once it gets into net-next?
I'm working on merging net into net-next right now, once that is
pushed out you can respin.
^ permalink raw reply
* [PATCHv2 net] sctp: return back transport in __sctp_rcv_init_lookup
From: Xin Long @ 2016-10-30 16:42 UTC (permalink / raw)
To: network dev, linux-sctp
Cc: davem, Marcelo Ricardo Leitner, Vlad Yasevich, Neil Horman
Prior to this patch, it used a local variable to save the transport that is
looked up by __sctp_lookup_association(), and didn't return it back. But in
sctp_rcv, it is used to initialize chunk->transport. So when hitting this,
even if it found the transport, it was still initializing chunk->transport
with null instead.
This patch is to return the transport back through transport pointer
that is from __sctp_rcv_lookup_harder().
Signed-off-by: Xin Long <lucien.xin@gmail.com>
---
net/sctp/input.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/net/sctp/input.c b/net/sctp/input.c
index a2ea1d1..8e0bc58 100644
--- a/net/sctp/input.c
+++ b/net/sctp/input.c
@@ -1021,7 +1021,6 @@ static struct sctp_association *__sctp_rcv_init_lookup(struct net *net,
struct sctphdr *sh = sctp_hdr(skb);
union sctp_params params;
sctp_init_chunk_t *init;
- struct sctp_transport *transport;
struct sctp_af *af;
/*
@@ -1052,7 +1051,7 @@ static struct sctp_association *__sctp_rcv_init_lookup(struct net *net,
af->from_addr_param(paddr, params.addr, sh->source, 0);
- asoc = __sctp_lookup_association(net, laddr, paddr, &transport);
+ asoc = __sctp_lookup_association(net, laddr, paddr, transportp);
if (asoc)
return asoc;
}
--
2.1.0
^ permalink raw reply related
* [PATCH net-next 7/7] qed: Learn resources from management firmware
From: Yuval Mintz @ 2016-10-30 16:38 UTC (permalink / raw)
To: davem, netdev; +Cc: Tomer Tayar, Yuval Mintz
In-Reply-To: <1477845517-17228-1-git-send-email-Yuval.Mintz@cavium.com>
From: Tomer Tayar <Tomer.Tayar@cavium.com>
Currently, each interfaces assumes it receives an equal portion
of HW/FW resources, but this is wasteful - different partitions
[and specifically, parititions exposing different protocol support]
might require different resources.
Implement a new resource learning scheme where the information is
received directly from the management firmware [which has knowledge
of all of the functions and can serve as arbiter].
Signed-off-by: Tomer Tayar <Tomer.Tayar@cavium.com>
Signed-off-by: Yuval Mintz <Yuval.Mintz@cavium.com>
---
drivers/net/ethernet/qlogic/qed/qed.h | 6 +-
drivers/net/ethernet/qlogic/qed/qed_dev.c | 291 ++++++++++++++++++++++++------
drivers/net/ethernet/qlogic/qed/qed_hsi.h | 46 +++++
drivers/net/ethernet/qlogic/qed/qed_l2.c | 2 +-
drivers/net/ethernet/qlogic/qed/qed_mcp.c | 42 +++++
drivers/net/ethernet/qlogic/qed/qed_mcp.h | 15 ++
include/linux/qed/qed_eth_if.h | 2 +-
7 files changed, 341 insertions(+), 63 deletions(-)
diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h
index 6d3013f..50b8a01 100644
--- a/drivers/net/ethernet/qlogic/qed/qed.h
+++ b/drivers/net/ethernet/qlogic/qed/qed.h
@@ -154,7 +154,10 @@ struct qed_qm_iids {
u32 tids;
};
-enum QED_RESOURCES {
+/* HW / FW resources, output of features supported below, most information
+ * is received from MFW.
+ */
+enum qed_resources {
QED_SB,
QED_L2_QUEUE,
QED_VPORT,
@@ -166,6 +169,7 @@ enum QED_RESOURCES {
QED_RDMA_CNQ_RAM,
QED_ILT,
QED_LL2_QUEUE,
+ QED_CMDQS_CQS,
QED_RDMA_STATS_QUEUE,
QED_MAX_RESC,
};
diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c
index b59da1a..edd9ad0 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_dev.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c
@@ -1511,47 +1511,240 @@ static void qed_hw_set_feat(struct qed_hwfn *p_hwfn)
RESC_NUM(p_hwfn, QED_SB), num_features);
}
-static int qed_hw_get_resc(struct qed_hwfn *p_hwfn)
+static enum resource_id_enum qed_hw_get_mfw_res_id(enum qed_resources res_id)
+{
+ enum resource_id_enum mfw_res_id = RESOURCE_NUM_INVALID;
+
+ switch (res_id) {
+ case QED_SB:
+ mfw_res_id = RESOURCE_NUM_SB_E;
+ break;
+ case QED_L2_QUEUE:
+ mfw_res_id = RESOURCE_NUM_L2_QUEUE_E;
+ break;
+ case QED_VPORT:
+ mfw_res_id = RESOURCE_NUM_VPORT_E;
+ break;
+ case QED_RSS_ENG:
+ mfw_res_id = RESOURCE_NUM_RSS_ENGINES_E;
+ break;
+ case QED_PQ:
+ mfw_res_id = RESOURCE_NUM_PQ_E;
+ break;
+ case QED_RL:
+ mfw_res_id = RESOURCE_NUM_RL_E;
+ break;
+ case QED_MAC:
+ case QED_VLAN:
+ /* Each VFC resource can accommodate both a MAC and a VLAN */
+ mfw_res_id = RESOURCE_VFC_FILTER_E;
+ break;
+ case QED_ILT:
+ mfw_res_id = RESOURCE_ILT_E;
+ break;
+ case QED_LL2_QUEUE:
+ mfw_res_id = RESOURCE_LL2_QUEUE_E;
+ break;
+ case QED_RDMA_CNQ_RAM:
+ case QED_CMDQS_CQS:
+ /* CNQ/CMDQS are the same resource */
+ mfw_res_id = RESOURCE_CQS_E;
+ break;
+ case QED_RDMA_STATS_QUEUE:
+ mfw_res_id = RESOURCE_RDMA_STATS_QUEUE_E;
+ break;
+ default:
+ break;
+ }
+
+ return mfw_res_id;
+}
+
+static u32 qed_hw_get_dflt_resc_num(struct qed_hwfn *p_hwfn,
+ enum qed_resources res_id)
{
- u8 enabled_func_idx = p_hwfn->enabled_func_idx;
- u32 *resc_start = p_hwfn->hw_info.resc_start;
u8 num_funcs = p_hwfn->num_funcs_on_engine;
- u32 *resc_num = p_hwfn->hw_info.resc_num;
struct qed_sb_cnt_info sb_cnt_info;
- int i, max_vf_vlan_filters;
+ u32 dflt_resc_num = 0;
- memset(&sb_cnt_info, 0, sizeof(sb_cnt_info));
+ switch (res_id) {
+ case QED_SB:
+ memset(&sb_cnt_info, 0, sizeof(sb_cnt_info));
+ qed_int_get_num_sbs(p_hwfn, &sb_cnt_info);
+ dflt_resc_num = sb_cnt_info.sb_cnt;
+ break;
+ case QED_L2_QUEUE:
+ dflt_resc_num = MAX_NUM_L2_QUEUES_BB / num_funcs;
+ break;
+ case QED_VPORT:
+ dflt_resc_num = MAX_NUM_VPORTS_BB / num_funcs;
+ break;
+ case QED_RSS_ENG:
+ dflt_resc_num = ETH_RSS_ENGINE_NUM_BB / num_funcs;
+ break;
+ case QED_PQ:
+ /* The granularity of the PQs is 8 */
+ dflt_resc_num = MAX_QM_TX_QUEUES_BB / num_funcs;
+ dflt_resc_num &= ~0x7;
+ break;
+ case QED_RL:
+ dflt_resc_num = MAX_QM_GLOBAL_RLS / num_funcs;
+ break;
+ case QED_MAC:
+ case QED_VLAN:
+ /* Each VFC resource can accommodate both a MAC and a VLAN */
+ dflt_resc_num = ETH_NUM_MAC_FILTERS / num_funcs;
+ break;
+ case QED_ILT:
+ dflt_resc_num = PXP_NUM_ILT_RECORDS_BB / num_funcs;
+ break;
+ case QED_LL2_QUEUE:
+ dflt_resc_num = MAX_NUM_LL2_RX_QUEUES / num_funcs;
+ break;
+ case QED_RDMA_CNQ_RAM:
+ case QED_CMDQS_CQS:
+ /* CNQ/CMDQS are the same resource */
+ dflt_resc_num = NUM_OF_CMDQS_CQS / num_funcs;
+ break;
+ case QED_RDMA_STATS_QUEUE:
+ dflt_resc_num = RDMA_NUM_STATISTIC_COUNTERS_BB / num_funcs;
+ break;
+ default:
+ break;
+ }
-#ifdef CONFIG_QED_SRIOV
- max_vf_vlan_filters = QED_ETH_MAX_VF_NUM_VLAN_FILTERS;
-#else
- max_vf_vlan_filters = 0;
-#endif
+ return dflt_resc_num;
+}
+
+static const char *qed_hw_get_resc_name(enum qed_resources res_id)
+{
+ switch (res_id) {
+ case QED_SB:
+ return "SB";
+ case QED_L2_QUEUE:
+ return "L2_QUEUE";
+ case QED_VPORT:
+ return "VPORT";
+ case QED_RSS_ENG:
+ return "RSS_ENG";
+ case QED_PQ:
+ return "PQ";
+ case QED_RL:
+ return "RL";
+ case QED_MAC:
+ return "MAC";
+ case QED_VLAN:
+ return "VLAN";
+ case QED_RDMA_CNQ_RAM:
+ return "RDMA_CNQ_RAM";
+ case QED_ILT:
+ return "ILT";
+ case QED_LL2_QUEUE:
+ return "LL2_QUEUE";
+ case QED_CMDQS_CQS:
+ return "CMDQS_CQS";
+ case QED_RDMA_STATS_QUEUE:
+ return "RDMA_STATS_QUEUE";
+ default:
+ return "UNKNOWN_RESOURCE";
+ }
+}
- qed_int_get_num_sbs(p_hwfn, &sb_cnt_info);
+static int qed_hw_set_resc_info(struct qed_hwfn *p_hwfn,
+ enum qed_resources res_id)
+{
+ u32 dflt_resc_num = 0, dflt_resc_start = 0, mcp_resp, mcp_param;
+ u32 *p_resc_num, *p_resc_start;
+ struct resource_info resc_info;
+ int rc;
+
+ p_resc_num = &RESC_NUM(p_hwfn, res_id);
+ p_resc_start = &RESC_START(p_hwfn, res_id);
+
+ /* Default values assumes that each function received equal share */
+ dflt_resc_num = qed_hw_get_dflt_resc_num(p_hwfn, res_id);
+ if (!dflt_resc_num) {
+ DP_ERR(p_hwfn,
+ "Failed to get default amount for resource %d [%s]\n",
+ res_id, qed_hw_get_resc_name(res_id));
+ return -EINVAL;
+ }
+ dflt_resc_start = dflt_resc_num * p_hwfn->enabled_func_idx;
+
+ memset(&resc_info, 0, sizeof(resc_info));
+ resc_info.res_id = qed_hw_get_mfw_res_id(res_id);
+ if (resc_info.res_id == RESOURCE_NUM_INVALID) {
+ DP_ERR(p_hwfn,
+ "Failed to match resource %d [%s] with the MFW resources\n",
+ res_id, qed_hw_get_resc_name(res_id));
+ return -EINVAL;
+ }
+
+ rc = qed_mcp_get_resc_info(p_hwfn, p_hwfn->p_main_ptt, &resc_info,
+ &mcp_resp, &mcp_param);
+ if (rc) {
+ DP_NOTICE(p_hwfn,
+ "MFW response failure for an allocation request for resource %d [%s]\n",
+ res_id, qed_hw_get_resc_name(res_id));
+ return rc;
+ }
+
+ /* Default driver values are applied in the following cases:
+ * - The resource allocation MB command is not supported by the MFW
+ * - There is an internal error in the MFW while processing the request
+ * - The resource ID is unknown to the MFW
+ */
+ if (mcp_resp != FW_MSG_CODE_RESOURCE_ALLOC_OK &&
+ mcp_resp != FW_MSG_CODE_RESOURCE_ALLOC_DEPRECATED) {
+ DP_NOTICE(p_hwfn,
+ "Resource %d [%s]: No allocation info was received [mcp_resp 0x%x]. Applying default values [num %d, start %d].\n",
+ res_id,
+ qed_hw_get_resc_name(res_id),
+ mcp_resp, dflt_resc_num, dflt_resc_start);
+ *p_resc_num = dflt_resc_num;
+ *p_resc_start = dflt_resc_start;
+ goto out;
+ }
+
+ /* Special handling for status blocks; Would be revised in future */
+ if (res_id == QED_SB) {
+ resc_info.size -= 1;
+ resc_info.offset -= p_hwfn->enabled_func_idx;
+ }
+
+ *p_resc_num = resc_info.size;
+ *p_resc_start = resc_info.offset;
+
+out:
+ /* PQs have to divide by 8 [that's the HW granularity].
+ * Reduce number so it would fit.
+ */
+ if ((res_id == QED_PQ) && ((*p_resc_num % 8) || (*p_resc_start % 8))) {
+ DP_INFO(p_hwfn,
+ "PQs need to align by 8; Number %08x --> %08x, Start %08x --> %08x\n",
+ *p_resc_num,
+ (*p_resc_num) & ~0x7,
+ *p_resc_start, (*p_resc_start) & ~0x7);
+ *p_resc_num &= ~0x7;
+ *p_resc_start &= ~0x7;
+ }
- resc_num[QED_SB] = min_t(u32,
- (MAX_SB_PER_PATH_BB / num_funcs),
- sb_cnt_info.sb_cnt);
- resc_num[QED_L2_QUEUE] = MAX_NUM_L2_QUEUES_BB / num_funcs;
- resc_num[QED_VPORT] = MAX_NUM_VPORTS_BB / num_funcs;
- resc_num[QED_RSS_ENG] = ETH_RSS_ENGINE_NUM_BB / num_funcs;
- resc_num[QED_PQ] = MAX_QM_TX_QUEUES_BB / num_funcs;
- resc_num[QED_RL] = min_t(u32, 64, resc_num[QED_VPORT]);
- resc_num[QED_MAC] = ETH_NUM_MAC_FILTERS / num_funcs;
- resc_num[QED_VLAN] = (ETH_NUM_VLAN_FILTERS - 1 /*For vlan0*/) /
- num_funcs;
- resc_num[QED_ILT] = PXP_NUM_ILT_RECORDS_BB / num_funcs;
- resc_num[QED_LL2_QUEUE] = MAX_NUM_LL2_RX_QUEUES / num_funcs;
- resc_num[QED_RDMA_CNQ_RAM] = NUM_OF_CMDQS_CQS / num_funcs;
- resc_num[QED_RDMA_STATS_QUEUE] = RDMA_NUM_STATISTIC_COUNTERS_BB /
- num_funcs;
-
- for (i = 0; i < QED_MAX_RESC; i++)
- resc_start[i] = resc_num[i] * enabled_func_idx;
+ return 0;
+}
+
+static int qed_hw_get_resc(struct qed_hwfn *p_hwfn)
+{
+ u8 res_id;
+ int rc;
+
+ for (res_id = 0; res_id < QED_MAX_RESC; res_id++) {
+ rc = qed_hw_set_resc_info(p_hwfn, res_id);
+ if (rc)
+ return rc;
+ }
/* Sanity for ILT */
- if (RESC_END(p_hwfn, QED_ILT) > PXP_NUM_ILT_RECORDS_BB) {
+ if ((RESC_END(p_hwfn, QED_ILT) > PXP_NUM_ILT_RECORDS_BB)) {
DP_NOTICE(p_hwfn, "Can't assign ILT pages [%08x,...,%08x]\n",
RESC_START(p_hwfn, QED_ILT),
RESC_END(p_hwfn, QED_ILT) - 1);
@@ -1561,34 +1754,12 @@ static int qed_hw_get_resc(struct qed_hwfn *p_hwfn)
qed_hw_set_feat(p_hwfn);
DP_VERBOSE(p_hwfn, NETIF_MSG_PROBE,
- "The numbers for each resource are:\n"
- "SB = %d start = %d\n"
- "L2_QUEUE = %d start = %d\n"
- "VPORT = %d start = %d\n"
- "PQ = %d start = %d\n"
- "RL = %d start = %d\n"
- "MAC = %d start = %d\n"
- "VLAN = %d start = %d\n"
- "ILT = %d start = %d\n"
- "LL2_QUEUE = %d start = %d\n",
- p_hwfn->hw_info.resc_num[QED_SB],
- p_hwfn->hw_info.resc_start[QED_SB],
- p_hwfn->hw_info.resc_num[QED_L2_QUEUE],
- p_hwfn->hw_info.resc_start[QED_L2_QUEUE],
- p_hwfn->hw_info.resc_num[QED_VPORT],
- p_hwfn->hw_info.resc_start[QED_VPORT],
- p_hwfn->hw_info.resc_num[QED_PQ],
- p_hwfn->hw_info.resc_start[QED_PQ],
- p_hwfn->hw_info.resc_num[QED_RL],
- p_hwfn->hw_info.resc_start[QED_RL],
- p_hwfn->hw_info.resc_num[QED_MAC],
- p_hwfn->hw_info.resc_start[QED_MAC],
- p_hwfn->hw_info.resc_num[QED_VLAN],
- p_hwfn->hw_info.resc_start[QED_VLAN],
- p_hwfn->hw_info.resc_num[QED_ILT],
- p_hwfn->hw_info.resc_start[QED_ILT],
- RESC_NUM(p_hwfn, QED_LL2_QUEUE),
- RESC_START(p_hwfn, QED_LL2_QUEUE));
+ "The numbers for each resource are:\n");
+ for (res_id = 0; res_id < QED_MAX_RESC; res_id++)
+ DP_VERBOSE(p_hwfn, NETIF_MSG_PROBE, "%s = %d start = %d\n",
+ qed_hw_get_resc_name(res_id),
+ RESC_NUM(p_hwfn, res_id),
+ RESC_START(p_hwfn, res_id));
return 0;
}
diff --git a/drivers/net/ethernet/qlogic/qed/qed_hsi.h b/drivers/net/ethernet/qlogic/qed/qed_hsi.h
index 1d113ce..048f9a3 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_hsi.h
+++ b/drivers/net/ethernet/qlogic/qed/qed_hsi.h
@@ -8529,6 +8529,41 @@ struct mdump_config_stc {
u32 valid_logs;
};
+enum resource_id_enum {
+ RESOURCE_NUM_SB_E = 0,
+ RESOURCE_NUM_L2_QUEUE_E = 1,
+ RESOURCE_NUM_VPORT_E = 2,
+ RESOURCE_NUM_VMQ_E = 3,
+ RESOURCE_FACTOR_NUM_RSS_PF_E = 4,
+ RESOURCE_FACTOR_RSS_PER_VF_E = 5,
+ RESOURCE_NUM_RL_E = 6,
+ RESOURCE_NUM_PQ_E = 7,
+ RESOURCE_NUM_VF_E = 8,
+ RESOURCE_VFC_FILTER_E = 9,
+ RESOURCE_ILT_E = 10,
+ RESOURCE_CQS_E = 11,
+ RESOURCE_GFT_PROFILES_E = 12,
+ RESOURCE_NUM_TC_E = 13,
+ RESOURCE_NUM_RSS_ENGINES_E = 14,
+ RESOURCE_LL2_QUEUE_E = 15,
+ RESOURCE_RDMA_STATS_QUEUE_E = 16,
+ RESOURCE_MAX_NUM,
+ RESOURCE_NUM_INVALID = 0xFFFFFFFF
+};
+
+/* Resource ID is to be filled by the driver in the MB request
+ * Size, offset & flags to be filled by the MFW in the MB response
+ */
+struct resource_info {
+ enum resource_id_enum res_id;
+ u32 size; /* number of allocated resources */
+ u32 offset; /* Offset of the 1st resource */
+ u32 vf_size;
+ u32 vf_offset;
+ u32 flags;
+#define RESOURCE_ELEMENT_STRICT (1 << 0)
+};
+
union drv_union_data {
u32 ver_str[MCP_DRV_VER_STR_SIZE_DWORD];
struct mcp_mac wol_mac;
@@ -8549,6 +8584,7 @@ struct mdump_config_stc {
u64 reserved_stats[11];
struct ocbb_data_stc ocbb_info;
struct temperature_status_stc temp_info;
+ struct resource_info resource;
struct bist_nvm_image_att nvm_image_att;
struct mdump_config_stc mdump_config;
};
@@ -8576,6 +8612,7 @@ struct public_drv_mb {
#define DRV_MSG_CODE_BW_UPDATE_ACK 0x32000000
#define DRV_MSG_CODE_NIG_DRAIN 0x30000000
+#define DRV_MSG_GET_RESOURCE_ALLOC_MSG 0x34000000
#define DRV_MSG_CODE_VF_DISABLED_DONE 0xc0000000
#define DRV_MSG_CODE_CFG_VF_MSIX 0xc0010000
#define DRV_MSG_CODE_NVM_GET_FILE_ATT 0x00030000
@@ -8666,6 +8703,12 @@ struct public_drv_mb {
#define DRV_MB_PARAM_SET_LED_MODE_ON 0x1
#define DRV_MB_PARAM_SET_LED_MODE_OFF 0x2
+ /* Resource Allocation params - Driver version support */
+#define DRV_MB_PARAM_RESOURCE_ALLOC_VERSION_MAJOR_MASK 0xFFFF0000
+#define DRV_MB_PARAM_RESOURCE_ALLOC_VERSION_MAJOR_SHIFT 16
+#define DRV_MB_PARAM_RESOURCE_ALLOC_VERSION_MINOR_MASK 0x0000FFFF
+#define DRV_MB_PARAM_RESOURCE_ALLOC_VERSION_MINOR_SHIFT 0
+
#define DRV_MB_PARAM_BIST_REGISTER_TEST 1
#define DRV_MB_PARAM_BIST_CLOCK_TEST 2
#define DRV_MB_PARAM_BIST_NVM_TEST_NUM_IMAGES 3
@@ -8694,6 +8737,9 @@ struct public_drv_mb {
#define FW_MSG_CODE_DRV_UNLOAD_PORT 0x20120000
#define FW_MSG_CODE_DRV_UNLOAD_FUNCTION 0x20130000
#define FW_MSG_CODE_DRV_UNLOAD_DONE 0x21100000
+#define FW_MSG_CODE_RESOURCE_ALLOC_OK 0x34000000
+#define FW_MSG_CODE_RESOURCE_ALLOC_UNKNOWN 0x35000000
+#define FW_MSG_CODE_RESOURCE_ALLOC_DEPRECATED 0x36000000
#define FW_MSG_CODE_DRV_CFG_VF_MSIX_DONE 0xb0010000
#define FW_MSG_CODE_NVM_OK 0x00010000
diff --git a/drivers/net/ethernet/qlogic/qed/qed_l2.c b/drivers/net/ethernet/qlogic/qed/qed_l2.c
index 6b0e22d..1e3a16e 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_l2.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_l2.c
@@ -1691,7 +1691,7 @@ static int qed_fill_eth_dev_info(struct qed_dev *cdev,
}
qed_vf_get_num_vlan_filters(&cdev->hwfns[0],
- &info->num_vlan_filters);
+ (u8 *)&info->num_vlan_filters);
qed_vf_get_port_mac(&cdev->hwfns[0], info->port_mac);
info->is_legacy = !!cdev->hwfns[0].vf_iov_info->b_pre_fp_hsi;
diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c
index 0927488..d8e499e 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c
@@ -1683,3 +1683,45 @@ int qed_mcp_bist_nvm_test_get_image_att(struct qed_hwfn *p_hwfn,
return rc;
}
+
+#define QED_RESC_ALLOC_VERSION_MAJOR 1
+#define QED_RESC_ALLOC_VERSION_MINOR 0
+#define QED_RESC_ALLOC_VERSION \
+ ((QED_RESC_ALLOC_VERSION_MAJOR << \
+ DRV_MB_PARAM_RESOURCE_ALLOC_VERSION_MAJOR_SHIFT) | \
+ (QED_RESC_ALLOC_VERSION_MINOR << \
+ DRV_MB_PARAM_RESOURCE_ALLOC_VERSION_MINOR_SHIFT))
+int qed_mcp_get_resc_info(struct qed_hwfn *p_hwfn,
+ struct qed_ptt *p_ptt,
+ struct resource_info *p_resc_info,
+ u32 *p_mcp_resp, u32 *p_mcp_param)
+{
+ struct qed_mcp_mb_params mb_params;
+ union drv_union_data *p_union_data;
+ int rc;
+
+ memset(&mb_params, 0, sizeof(mb_params));
+ mb_params.cmd = DRV_MSG_GET_RESOURCE_ALLOC_MSG;
+ mb_params.param = QED_RESC_ALLOC_VERSION;
+ p_union_data = (union drv_union_data *)p_resc_info;
+ mb_params.p_data_src = p_union_data;
+ mb_params.p_data_dst = p_union_data;
+ rc = qed_mcp_cmd_and_union(p_hwfn, p_ptt, &mb_params);
+ if (rc)
+ return rc;
+
+ *p_mcp_resp = mb_params.mcp_resp;
+ *p_mcp_param = mb_params.mcp_param;
+
+ DP_VERBOSE(p_hwfn,
+ QED_MSG_SP,
+ "MFW resource_info: version 0x%x, res_id 0x%x, size 0x%x, offset 0x%x, vf_size 0x%x, vf_offset 0x%x, flags 0x%x\n",
+ *p_mcp_param,
+ p_resc_info->res_id,
+ p_resc_info->size,
+ p_resc_info->offset,
+ p_resc_info->vf_size,
+ p_resc_info->vf_offset, p_resc_info->flags);
+
+ return 0;
+}
diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.h b/drivers/net/ethernet/qlogic/qed/qed_mcp.h
index be8152d..407a2c1 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_mcp.h
+++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.h
@@ -689,4 +689,19 @@ int qed_mcp_ov_update_eswitch(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
enum qed_ov_eswitch eswitch);
+/**
+ * @brief - Gets the MFW allocation info for the given resource
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ * @param p_resc_info - descriptor of requested resource
+ * @param p_mcp_resp
+ * @param p_mcp_param
+ *
+ * @return int - 0 - operation was successful.
+ */
+int qed_mcp_get_resc_info(struct qed_hwfn *p_hwfn,
+ struct qed_ptt *p_ptt,
+ struct resource_info *p_resc_info,
+ u32 *p_mcp_resp, u32 *p_mcp_param);
#endif
diff --git a/include/linux/qed/qed_eth_if.h b/include/linux/qed/qed_eth_if.h
index 1513080..9755a3f 100644
--- a/include/linux/qed/qed_eth_if.h
+++ b/include/linux/qed/qed_eth_if.h
@@ -22,7 +22,7 @@ struct qed_dev_eth_info {
u8 num_tc;
u8 port_mac[ETH_ALEN];
- u8 num_vlan_filters;
+ u16 num_vlan_filters;
u16 num_mac_filters;
/* Legacy VF - this affects the datapath, so qede has to know */
--
1.9.3
^ permalink raw reply related
* [PATCH net-next 6/7] qed: Use VF-queue feature
From: Yuval Mintz @ 2016-10-30 16:38 UTC (permalink / raw)
To: davem, netdev; +Cc: Yuval Mintz
In-Reply-To: <1477845517-17228-1-git-send-email-Yuval.Mintz@cavium.com>
Driver sets several restrictions about the number of supported VFs
according to available HW/FW resources.
This creates a problem as there are constellations which can't be
supported [as limitation don't accurately describe the resources],
as well as holes where enabling IOV would fail due to supposed
lack of resources.
This introduces a new interal feature - vf-queues, which would
be used to lift some of the restriction and accurately enumerate
the queues that can be used by a given PF's VFs.
Signed-off-by: Yuval Mintz <Yuval.Mintz@cavium.com>
---
drivers/net/ethernet/qlogic/qed/qed.h | 1 +
drivers/net/ethernet/qlogic/qed/qed_dev.c | 20 ++++++++++++++----
drivers/net/ethernet/qlogic/qed/qed_int.c | 32 ++++++++++++++++++++++++++++-
drivers/net/ethernet/qlogic/qed/qed_sriov.c | 17 ++++++---------
4 files changed, 54 insertions(+), 16 deletions(-)
diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h
index 8828ffa..6d3013f 100644
--- a/drivers/net/ethernet/qlogic/qed/qed.h
+++ b/drivers/net/ethernet/qlogic/qed/qed.h
@@ -174,6 +174,7 @@ enum QED_FEATURE {
QED_PF_L2_QUE,
QED_VF,
QED_RDMA_CNQ,
+ QED_VF_L2_QUE,
QED_MAX_FEATURES,
};
diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c
index 13833a5..b59da1a 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_dev.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c
@@ -1475,6 +1475,7 @@ static void get_function_id(struct qed_hwfn *p_hwfn)
static void qed_hw_set_feat(struct qed_hwfn *p_hwfn)
{
u32 *feat_num = p_hwfn->hw_info.feat_num;
+ struct qed_sb_cnt_info sb_cnt_info;
int num_features = 1;
#if IS_ENABLED(CONFIG_INFINIBAND_QEDR)
@@ -1493,10 +1494,21 @@ static void qed_hw_set_feat(struct qed_hwfn *p_hwfn)
feat_num[QED_PF_L2_QUE] = min_t(u32, RESC_NUM(p_hwfn, QED_SB) /
num_features,
RESC_NUM(p_hwfn, QED_L2_QUEUE));
- DP_VERBOSE(p_hwfn, NETIF_MSG_PROBE,
- "#PF_L2_QUEUES=%d #SBS=%d num_features=%d\n",
- feat_num[QED_PF_L2_QUE], RESC_NUM(p_hwfn, QED_SB),
- num_features);
+
+ memset(&sb_cnt_info, 0, sizeof(sb_cnt_info));
+ qed_int_get_num_sbs(p_hwfn, &sb_cnt_info);
+ feat_num[QED_VF_L2_QUE] =
+ min_t(u32,
+ RESC_NUM(p_hwfn, QED_L2_QUEUE) -
+ FEAT_NUM(p_hwfn, QED_PF_L2_QUE), sb_cnt_info.sb_iov_cnt);
+
+ DP_VERBOSE(p_hwfn,
+ NETIF_MSG_PROBE,
+ "#PF_L2_QUEUES=%d VF_L2_QUEUES=%d #ROCE_CNQ=%d #SBS=%d num_features=%d\n",
+ (int)FEAT_NUM(p_hwfn, QED_PF_L2_QUE),
+ (int)FEAT_NUM(p_hwfn, QED_VF_L2_QUE),
+ (int)FEAT_NUM(p_hwfn, QED_RDMA_CNQ),
+ RESC_NUM(p_hwfn, QED_SB), num_features);
}
static int qed_hw_get_resc(struct qed_hwfn *p_hwfn)
diff --git a/drivers/net/ethernet/qlogic/qed/qed_int.c b/drivers/net/ethernet/qlogic/qed/qed_int.c
index 2adedc6..bb74e1c 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_int.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_int.c
@@ -3030,6 +3030,31 @@ int qed_int_igu_read_cam(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
}
}
}
+
+ /* There's a possibility the igu_sb_cnt_iov doesn't properly reflect
+ * the number of VF SBs [especially for first VF on engine, as we can't
+ * diffrentiate between empty entries and its entries].
+ * Since we don't really support more SBs than VFs today, prevent any
+ * such configuration by sanitizing the number of SBs to equal the
+ * number of VFs.
+ */
+ if (IS_PF_SRIOV(p_hwfn)) {
+ u16 total_vfs = p_hwfn->cdev->p_iov_info->total_vfs;
+
+ if (total_vfs < p_igu_info->free_blks) {
+ DP_VERBOSE(p_hwfn,
+ (NETIF_MSG_INTR | QED_MSG_IOV),
+ "Limiting number of SBs for IOV - %04x --> %04x\n",
+ p_igu_info->free_blks,
+ p_hwfn->cdev->p_iov_info->total_vfs);
+ p_igu_info->free_blks = total_vfs;
+ } else if (total_vfs > p_igu_info->free_blks) {
+ DP_NOTICE(p_hwfn,
+ "IGU has only %04x SBs for VFs while the device has %04x VFs\n",
+ p_igu_info->free_blks, total_vfs);
+ return -EINVAL;
+ }
+ }
p_igu_info->igu_sb_cnt_iov = p_igu_info->free_blks;
DP_VERBOSE(
@@ -3163,7 +3188,12 @@ u16 qed_int_queue_id_from_sb_id(struct qed_hwfn *p_hwfn, u16 sb_id)
return sb_id - p_info->igu_base_sb;
} else if ((sb_id >= p_info->igu_base_sb_iov) &&
(sb_id < p_info->igu_base_sb_iov + p_info->igu_sb_cnt_iov)) {
- return sb_id - p_info->igu_base_sb_iov + p_info->igu_sb_cnt;
+ /* We want the first VF queue to be adjacent to the
+ * last PF queue. Since L2 queues can be partial to
+ * SBs, we'll use the feature instead.
+ */
+ return sb_id - p_info->igu_base_sb_iov +
+ FEAT_NUM(p_hwfn, QED_PF_L2_QUE);
} else {
DP_NOTICE(p_hwfn, "SB %d not in range for function\n", sb_id);
return 0;
diff --git a/drivers/net/ethernet/qlogic/qed/qed_sriov.c b/drivers/net/ethernet/qlogic/qed/qed_sriov.c
index 6f029f9..f3f742a 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_sriov.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_sriov.c
@@ -3470,7 +3470,6 @@ int qed_sriov_disable(struct qed_dev *cdev, bool pci_enabled)
static int qed_sriov_enable(struct qed_dev *cdev, int num)
{
- struct qed_sb_cnt_info sb_cnt_info;
int i, j, rc;
if (num >= RESC_NUM(&cdev->hwfns[0], QED_VPORT)) {
@@ -3483,7 +3482,11 @@ static int qed_sriov_enable(struct qed_dev *cdev, int num)
for_each_hwfn(cdev, j) {
struct qed_hwfn *hwfn = &cdev->hwfns[j];
struct qed_ptt *ptt = qed_ptt_acquire(hwfn);
- int num_sbs = 0, limit = 16;
+ int num_queues;
+
+ /* Make sure not to use more than 16 queues per VF */
+ num_queues = min_t(int,
+ FEAT_NUM(hwfn, QED_VF_L2_QUE) / num, 16);
if (!ptt) {
DP_ERR(hwfn, "Failed to acquire ptt\n");
@@ -3491,19 +3494,11 @@ static int qed_sriov_enable(struct qed_dev *cdev, int num)
goto err;
}
- if (IS_MF_DEFAULT(hwfn))
- limit = MAX_NUM_VFS_BB / hwfn->num_funcs_on_engine;
-
- memset(&sb_cnt_info, 0, sizeof(sb_cnt_info));
- qed_int_get_num_sbs(hwfn, &sb_cnt_info);
- num_sbs = min_t(int, sb_cnt_info.sb_free_blk, limit);
-
for (i = 0; i < num; i++) {
if (!qed_iov_is_valid_vfid(hwfn, i, false, true))
continue;
- rc = qed_iov_init_hw_for_vf(hwfn,
- ptt, i, num_sbs / num);
+ rc = qed_iov_init_hw_for_vf(hwfn, ptt, i, num_queues);
if (rc) {
DP_ERR(cdev, "Failed to enable VF[%d]\n", i);
qed_ptt_release(hwfn, ptt);
--
1.9.3
^ permalink raw reply related
* [PATCH net-next 2/7] qed: Add nvram selftest
From: Yuval Mintz @ 2016-10-30 16:38 UTC (permalink / raw)
To: davem, netdev; +Cc: Yuval Mintz
In-Reply-To: <1477845517-17228-1-git-send-email-Yuval.Mintz@cavium.com>
Signed-off-by: Yuval Mintz <Yuval.Mintz@cavium.com>
---
drivers/net/ethernet/qlogic/qed/qed_hsi.h | 4 +
drivers/net/ethernet/qlogic/qed/qed_main.c | 1 +
drivers/net/ethernet/qlogic/qed/qed_mcp.c | 94 ++++++++++++++++++++++
drivers/net/ethernet/qlogic/qed/qed_mcp.h | 41 ++++++++++
drivers/net/ethernet/qlogic/qed/qed_selftest.c | 101 ++++++++++++++++++++++++
drivers/net/ethernet/qlogic/qed/qed_selftest.h | 10 +++
drivers/net/ethernet/qlogic/qede/qede_ethtool.c | 7 ++
include/linux/qed/qed_if.h | 9 +++
8 files changed, 267 insertions(+)
diff --git a/drivers/net/ethernet/qlogic/qed/qed_hsi.h b/drivers/net/ethernet/qlogic/qed/qed_hsi.h
index 36de87a..f7dfa2e 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_hsi.h
+++ b/drivers/net/ethernet/qlogic/qed/qed_hsi.h
@@ -8666,6 +8666,8 @@ struct public_drv_mb {
#define DRV_MB_PARAM_BIST_REGISTER_TEST 1
#define DRV_MB_PARAM_BIST_CLOCK_TEST 2
+#define DRV_MB_PARAM_BIST_NVM_TEST_NUM_IMAGES 3
+#define DRV_MB_PARAM_BIST_NVM_TEST_IMAGE_BY_INDEX 4
#define DRV_MB_PARAM_BIST_RC_UNKNOWN 0
#define DRV_MB_PARAM_BIST_RC_PASSED 1
@@ -8674,6 +8676,8 @@ struct public_drv_mb {
#define DRV_MB_PARAM_BIST_TEST_INDEX_SHIFT 0
#define DRV_MB_PARAM_BIST_TEST_INDEX_MASK 0x000000FF
+#define DRV_MB_PARAM_BIST_TEST_IMAGE_INDEX_SHIFT 8
+#define DRV_MB_PARAM_BIST_TEST_IMAGE_INDEX_MASK 0x0000FF00
u32 fw_mb_header;
#define FW_MSG_CODE_MASK 0xffff0000
diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c
index 937968b..612c094 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_main.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_main.c
@@ -1509,6 +1509,7 @@ struct qed_selftest_ops qed_selftest_ops_pass = {
.selftest_interrupt = &qed_selftest_interrupt,
.selftest_register = &qed_selftest_register,
.selftest_clock = &qed_selftest_clock,
+ .selftest_nvram = &qed_selftest_nvram,
};
const struct qed_common_ops qed_common_ops_pass = {
diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c
index 98dc913..8be6157 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c
@@ -1434,6 +1434,52 @@ int qed_mcp_mask_parities(struct qed_hwfn *p_hwfn,
return rc;
}
+int qed_mcp_nvm_read(struct qed_dev *cdev, u32 addr, u8 *p_buf, u32 len)
+{
+ u32 bytes_left = len, offset = 0, bytes_to_copy, read_len = 0;
+ struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
+ u32 resp = 0, resp_param = 0;
+ struct qed_ptt *p_ptt;
+ int rc = 0;
+
+ p_ptt = qed_ptt_acquire(p_hwfn);
+ if (!p_ptt)
+ return -EBUSY;
+
+ while (bytes_left > 0) {
+ bytes_to_copy = min_t(u32, bytes_left, MCP_DRV_NVM_BUF_LEN);
+
+ rc = qed_mcp_nvm_rd_cmd(p_hwfn, p_ptt,
+ DRV_MSG_CODE_NVM_READ_NVRAM,
+ addr + offset +
+ (bytes_to_copy <<
+ DRV_MB_PARAM_NVM_LEN_SHIFT),
+ &resp, &resp_param,
+ &read_len,
+ (u32 *)(p_buf + offset));
+
+ if (rc || (resp != FW_MSG_CODE_NVM_OK)) {
+ DP_NOTICE(cdev, "MCP command rc = %d\n", rc);
+ break;
+ }
+
+ /* This can be a lengthy process, and it's possible scheduler
+ * isn't preemptable. Sleep a bit to prevent CPU hogging.
+ */
+ if (bytes_left % 0x1000 <
+ (bytes_left - read_len) % 0x1000)
+ usleep_range(1000, 2000);
+
+ offset += read_len;
+ bytes_left -= read_len;
+ }
+
+ cdev->mcp_nvm_resp = resp;
+ qed_ptt_release(p_hwfn, p_ptt);
+
+ return rc;
+}
+
int qed_mcp_bist_register_test(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
{
u32 drv_mb_param = 0, rsp, param;
@@ -1475,3 +1521,51 @@ int qed_mcp_bist_clock_test(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
return rc;
}
+
+int qed_mcp_bist_nvm_test_get_num_images(struct qed_hwfn *p_hwfn,
+ struct qed_ptt *p_ptt,
+ u32 *num_images)
+{
+ u32 drv_mb_param = 0, rsp;
+ int rc = 0;
+
+ drv_mb_param = (DRV_MB_PARAM_BIST_NVM_TEST_NUM_IMAGES <<
+ DRV_MB_PARAM_BIST_TEST_INDEX_SHIFT);
+
+ rc = qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_BIST_TEST,
+ drv_mb_param, &rsp, num_images);
+ if (rc)
+ return rc;
+
+ if (((rsp & FW_MSG_CODE_MASK) != FW_MSG_CODE_OK))
+ rc = -EINVAL;
+
+ return rc;
+}
+
+int qed_mcp_bist_nvm_test_get_image_att(struct qed_hwfn *p_hwfn,
+ struct qed_ptt *p_ptt,
+ struct bist_nvm_image_att *p_image_att,
+ u32 image_index)
+{
+ u32 buf_size = 0, param, resp = 0, resp_param = 0;
+ int rc;
+
+ param = DRV_MB_PARAM_BIST_NVM_TEST_IMAGE_BY_INDEX <<
+ DRV_MB_PARAM_BIST_TEST_INDEX_SHIFT;
+ param |= image_index << DRV_MB_PARAM_BIST_TEST_IMAGE_INDEX_SHIFT;
+
+ rc = qed_mcp_nvm_rd_cmd(p_hwfn, p_ptt,
+ DRV_MSG_CODE_BIST_TEST, param,
+ &resp, &resp_param,
+ &buf_size,
+ (u32 *)p_image_att);
+ if (rc)
+ return rc;
+
+ if (((resp & FW_MSG_CODE_MASK) != FW_MSG_CODE_OK) ||
+ (p_image_att->return_code != 1))
+ rc = -EINVAL;
+
+ return rc;
+}
diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.h b/drivers/net/ethernet/qlogic/qed/qed_mcp.h
index 8950719..be8152d 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_mcp.h
+++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.h
@@ -380,6 +380,18 @@ int qed_mcp_set_led(struct qed_hwfn *p_hwfn,
enum qed_led_mode mode);
/**
+ * @brief Read from nvm
+ *
+ * @param cdev
+ * @param addr - nvm offset
+ * @param p_buf - nvm read buffer
+ * @param len - buffer len
+ *
+ * @return int - 0 - operation was successful.
+ */
+int qed_mcp_nvm_read(struct qed_dev *cdev, u32 addr, u8 *p_buf, u32 len);
+
+/**
* @brief Bist register test
*
* @param p_hwfn - hw function
@@ -401,6 +413,35 @@ int qed_mcp_bist_register_test(struct qed_hwfn *p_hwfn,
int qed_mcp_bist_clock_test(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt);
+/**
+ * @brief Bist nvm test - get number of images
+ *
+ * @param p_hwfn - hw function
+ * @param p_ptt - PTT required for register access
+ * @param num_images - number of images if operation was
+ * successful. 0 if not.
+ *
+ * @return int - 0 - operation was successful.
+ */
+int qed_mcp_bist_nvm_test_get_num_images(struct qed_hwfn *p_hwfn,
+ struct qed_ptt *p_ptt,
+ u32 *num_images);
+
+/**
+ * @brief Bist nvm test - get image attributes by index
+ *
+ * @param p_hwfn - hw function
+ * @param p_ptt - PTT required for register access
+ * @param p_image_att - Attributes of image
+ * @param image_index - Index of image to get information for
+ *
+ * @return int - 0 - operation was successful.
+ */
+int qed_mcp_bist_nvm_test_get_image_att(struct qed_hwfn *p_hwfn,
+ struct qed_ptt *p_ptt,
+ struct bist_nvm_image_att *p_image_att,
+ u32 image_index);
+
/* Using hwfn number (and not pf_num) is required since in CMT mode,
* same pf_num may be used by two different hwfn
* TODO - this shouldn't really be in .h file, but until all fields
diff --git a/drivers/net/ethernet/qlogic/qed/qed_selftest.c b/drivers/net/ethernet/qlogic/qed/qed_selftest.c
index 9b7678f..48bfaec 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_selftest.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_selftest.c
@@ -1,3 +1,4 @@
+#include <linux/crc32.h>
#include "qed.h"
#include "qed_dev_api.h"
#include "qed_mcp.h"
@@ -75,3 +76,103 @@ int qed_selftest_clock(struct qed_dev *cdev)
return rc;
}
+
+int qed_selftest_nvram(struct qed_dev *cdev)
+{
+ struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
+ struct qed_ptt *p_ptt = qed_ptt_acquire(p_hwfn);
+ u32 num_images, i, j, nvm_crc, calc_crc;
+ struct bist_nvm_image_att image_att;
+ u8 *buf = NULL;
+ __be32 val;
+ int rc;
+
+ if (!p_ptt) {
+ DP_ERR(p_hwfn, "failed to acquire ptt\n");
+ return -EBUSY;
+ }
+
+ /* Acquire from MFW the amount of available images */
+ rc = qed_mcp_bist_nvm_test_get_num_images(p_hwfn, p_ptt, &num_images);
+ if (rc || !num_images) {
+ DP_ERR(p_hwfn, "Failed getting number of images\n");
+ return -EINVAL;
+ }
+
+ /* Iterate over images and validate CRC */
+ for (i = 0; i < num_images; i++) {
+ /* This mailbox returns information about the image required for
+ * reading it.
+ */
+ rc = qed_mcp_bist_nvm_test_get_image_att(p_hwfn, p_ptt,
+ &image_att, i);
+ if (rc) {
+ DP_ERR(p_hwfn,
+ "Failed getting image index %d attributes\n",
+ i);
+ goto err0;
+ }
+
+ /* After MFW crash dump is collected - the image's CRC stops
+ * being valid.
+ */
+ if (image_att.image_type == NVM_TYPE_MDUMP)
+ continue;
+
+ DP_VERBOSE(p_hwfn, QED_MSG_SP, "image index %d, size %x\n",
+ i, image_att.len);
+
+ /* Allocate a buffer for holding the nvram image */
+ buf = kzalloc(image_att.len, GFP_KERNEL);
+ if (!buf) {
+ rc = -ENOMEM;
+ goto err0;
+ }
+
+ /* Read image into buffer */
+ rc = qed_mcp_nvm_read(p_hwfn->cdev, image_att.nvm_start_addr,
+ buf, image_att.len);
+ if (rc) {
+ DP_ERR(p_hwfn,
+ "Failed reading image index %d from nvm.\n", i);
+ goto err1;
+ }
+
+ /* Convert the buffer into big-endian format (excluding the
+ * closing 4 bytes of CRC).
+ */
+ for (j = 0; j < image_att.len - 4; j += 4) {
+ val = cpu_to_be32(*(u32 *)&buf[j]);
+ *(u32 *)&buf[j] = (__force u32)val;
+ }
+
+ /* Calc CRC for the "actual" image buffer, i.e. not including
+ * the last 4 CRC bytes.
+ */
+ nvm_crc = *(u32 *)(buf + image_att.len - 4);
+ calc_crc = crc32(0xffffffff, buf, image_att.len - 4);
+ calc_crc = (__force u32)~cpu_to_be32(calc_crc);
+ DP_VERBOSE(p_hwfn, QED_MSG_SP,
+ "nvm crc 0x%x, calc_crc 0x%x\n", nvm_crc, calc_crc);
+
+ if (calc_crc != nvm_crc) {
+ rc = -EINVAL;
+ goto err1;
+ }
+
+ /* Done with this image; Free to prevent double release
+ * on subsequent failure.
+ */
+ kfree(buf);
+ buf = NULL;
+ }
+
+ qed_ptt_release(p_hwfn, p_ptt);
+ return 0;
+
+err1:
+ kfree(buf);
+err0:
+ qed_ptt_release(p_hwfn, p_ptt);
+ return rc;
+}
diff --git a/drivers/net/ethernet/qlogic/qed/qed_selftest.h b/drivers/net/ethernet/qlogic/qed/qed_selftest.h
index 50eb0b49..739ddb7 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_selftest.h
+++ b/drivers/net/ethernet/qlogic/qed/qed_selftest.h
@@ -37,4 +37,14 @@
* @return int
*/
int qed_selftest_clock(struct qed_dev *cdev);
+
+/**
+ * @brief qed_selftest_nvram - Perform nvram test
+ *
+ * @param cdev
+ *
+ * @return int
+ */
+int qed_selftest_nvram(struct qed_dev *cdev);
+
#endif
diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c
index 3e477734..fa658df 100644
--- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c
+++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c
@@ -157,6 +157,7 @@ enum qede_ethtool_tests {
QEDE_ETHTOOL_MEMORY_TEST,
QEDE_ETHTOOL_REGISTER_TEST,
QEDE_ETHTOOL_CLOCK_TEST,
+ QEDE_ETHTOOL_NVRAM_TEST,
QEDE_ETHTOOL_TEST_MAX
};
@@ -166,6 +167,7 @@ enum qede_ethtool_tests {
"Memory (online)\t\t",
"Register (online)\t",
"Clock (online)\t\t",
+ "Nvram (online)\t\t",
};
static void qede_get_strings_stats(struct qede_dev *edev, u8 *buf)
@@ -1363,6 +1365,11 @@ static void qede_self_test(struct net_device *dev,
buf[QEDE_ETHTOOL_CLOCK_TEST] = 1;
etest->flags |= ETH_TEST_FL_FAILED;
}
+
+ if (edev->ops->common->selftest->selftest_nvram(edev->cdev)) {
+ buf[QEDE_ETHTOOL_NVRAM_TEST] = 1;
+ etest->flags |= ETH_TEST_FL_FAILED;
+ }
}
static int qede_set_tunable(struct net_device *dev,
diff --git a/include/linux/qed/qed_if.h b/include/linux/qed/qed_if.h
index 634ae6f..696d22d 100644
--- a/include/linux/qed/qed_if.h
+++ b/include/linux/qed/qed_if.h
@@ -401,6 +401,15 @@ struct qed_selftest_ops {
* @return 0 on success, error otherwise.
*/
int (*selftest_clock)(struct qed_dev *cdev);
+
+/**
+ * @brief selftest_nvram - Perform nvram test
+ *
+ * @param cdev
+ *
+ * @return 0 on success, error otherwise.
+ */
+ int (*selftest_nvram) (struct qed_dev *cdev);
};
struct qed_common_ops {
--
1.9.3
^ permalink raw reply related
* [PATCH net-next 5/7] qed: Learn of RDMA capabilities per-device
From: Yuval Mintz @ 2016-10-30 16:38 UTC (permalink / raw)
To: davem, netdev; +Cc: Yuval Mintz
In-Reply-To: <1477845517-17228-1-git-send-email-Yuval.Mintz@cavium.com>
Today, RDMA capabilities are learned from management firmware
which provides a per-device indication for all interfaces.
Newer management firmware is capable of providing a per-device
indication [would later be extended to either RoCE/iWARP].
Try using this newer learning mechanism, but fallback in case
management firmware is too old to retain current functionality.
Signed-off-by: Yuval Mintz <Yuval.Mintz@cavium.com>
---
drivers/net/ethernet/qlogic/qed/qed_hsi.h | 7 +++
drivers/net/ethernet/qlogic/qed/qed_mcp.c | 78 +++++++++++++++++++++++++++----
2 files changed, 77 insertions(+), 8 deletions(-)
diff --git a/drivers/net/ethernet/qlogic/qed/qed_hsi.h b/drivers/net/ethernet/qlogic/qed/qed_hsi.h
index fdb7a09..1d113ce 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_hsi.h
+++ b/drivers/net/ethernet/qlogic/qed/qed_hsi.h
@@ -8601,6 +8601,7 @@ struct public_drv_mb {
#define DRV_MSG_CODE_BIST_TEST 0x001e0000
#define DRV_MSG_CODE_SET_LED_MODE 0x00200000
+#define DRV_MSG_CODE_GET_PF_RDMA_PROTOCOL 0x002b0000
#define DRV_MSG_CODE_OS_WOL 0x002e0000
#define DRV_MSG_SEQ_NUMBER_MASK 0x0000ffff
@@ -8705,6 +8706,12 @@ struct public_drv_mb {
u32 fw_mb_param;
+ /* get pf rdma protocol command responce */
+#define FW_MB_PARAM_GET_PF_RDMA_NONE 0x0
+#define FW_MB_PARAM_GET_PF_RDMA_ROCE 0x1
+#define FW_MB_PARAM_GET_PF_RDMA_IWARP 0x2
+#define FW_MB_PARAM_GET_PF_RDMA_BOTH 0x3
+
u32 drv_pulse_mb;
#define DRV_PULSE_SEQ_MASK 0x00007fff
#define DRV_PULSE_SYSTEM_TIME_MASK 0xffff0000
diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c
index 768b35b..0927488 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c
@@ -1024,28 +1024,89 @@ int qed_mcp_get_media_type(struct qed_dev *cdev, u32 *p_media_type)
return 0;
}
+/* Old MFW has a global configuration for all PFs regarding RDMA support */
+static void
+qed_mcp_get_shmem_proto_legacy(struct qed_hwfn *p_hwfn,
+ enum qed_pci_personality *p_proto)
+{
+ /* There wasn't ever a legacy MFW that published iwarp.
+ * So at this point, this is either plain l2 or RoCE.
+ */
+ if (test_bit(QED_DEV_CAP_ROCE, &p_hwfn->hw_info.device_capabilities))
+ *p_proto = QED_PCI_ETH_ROCE;
+ else
+ *p_proto = QED_PCI_ETH;
+
+ DP_VERBOSE(p_hwfn, NETIF_MSG_IFUP,
+ "According to Legacy capabilities, L2 personality is %08x\n",
+ (u32) *p_proto);
+}
+
+static int
+qed_mcp_get_shmem_proto_mfw(struct qed_hwfn *p_hwfn,
+ struct qed_ptt *p_ptt,
+ enum qed_pci_personality *p_proto)
+{
+ u32 resp = 0, param = 0;
+ int rc;
+
+ rc = qed_mcp_cmd(p_hwfn, p_ptt,
+ DRV_MSG_CODE_GET_PF_RDMA_PROTOCOL, 0, &resp, ¶m);
+ if (rc)
+ return rc;
+ if (resp != FW_MSG_CODE_OK) {
+ DP_VERBOSE(p_hwfn, NETIF_MSG_IFUP,
+ "MFW lacks support for command; Returns %08x\n",
+ resp);
+ return -EINVAL;
+ }
+
+ switch (param) {
+ case FW_MB_PARAM_GET_PF_RDMA_NONE:
+ *p_proto = QED_PCI_ETH;
+ break;
+ case FW_MB_PARAM_GET_PF_RDMA_ROCE:
+ *p_proto = QED_PCI_ETH_ROCE;
+ break;
+ case FW_MB_PARAM_GET_PF_RDMA_BOTH:
+ DP_NOTICE(p_hwfn,
+ "Current day drivers don't support RoCE & iWARP. Default to RoCE-only\n");
+ *p_proto = QED_PCI_ETH_ROCE;
+ break;
+ case FW_MB_PARAM_GET_PF_RDMA_IWARP:
+ default:
+ DP_NOTICE(p_hwfn,
+ "MFW answers GET_PF_RDMA_PROTOCOL but param is %08x\n",
+ param);
+ return -EINVAL;
+ }
+
+ DP_VERBOSE(p_hwfn,
+ NETIF_MSG_IFUP,
+ "According to capabilities, L2 personality is %08x [resp %08x param %08x]\n",
+ (u32) *p_proto, resp, param);
+ return 0;
+}
+
static int
qed_mcp_get_shmem_proto(struct qed_hwfn *p_hwfn,
struct public_func *p_info,
+ struct qed_ptt *p_ptt,
enum qed_pci_personality *p_proto)
{
int rc = 0;
switch (p_info->config & FUNC_MF_CFG_PROTOCOL_MASK) {
case FUNC_MF_CFG_PROTOCOL_ETHERNET:
- if (test_bit(QED_DEV_CAP_ROCE,
- &p_hwfn->hw_info.device_capabilities))
- *p_proto = QED_PCI_ETH_ROCE;
- else
- *p_proto = QED_PCI_ETH;
+ if (qed_mcp_get_shmem_proto_mfw(p_hwfn, p_ptt, p_proto))
+ qed_mcp_get_shmem_proto_legacy(p_hwfn, p_proto);
break;
case FUNC_MF_CFG_PROTOCOL_ISCSI:
*p_proto = QED_PCI_ISCSI;
break;
case FUNC_MF_CFG_PROTOCOL_ROCE:
DP_NOTICE(p_hwfn, "RoCE personality is not a valid value!\n");
- rc = -EINVAL;
- break;
+ /* Fallthrough */
default:
rc = -EINVAL;
}
@@ -1065,7 +1126,8 @@ int qed_mcp_fill_shmem_func_info(struct qed_hwfn *p_hwfn,
info->pause_on_host = (shmem_info.config &
FUNC_MF_CFG_PAUSE_ON_HOST_RING) ? 1 : 0;
- if (qed_mcp_get_shmem_proto(p_hwfn, &shmem_info, &info->protocol)) {
+ if (qed_mcp_get_shmem_proto(p_hwfn, &shmem_info, p_ptt,
+ &info->protocol)) {
DP_ERR(p_hwfn, "Unknown personality %08x\n",
(u32)(shmem_info.config & FUNC_MF_CFG_PROTOCOL_MASK));
return -EINVAL;
--
1.9.3
^ permalink raw reply related
* [PATCH net-next 4/7] qede: Decouple ethtool caps from qed
From: Yuval Mintz @ 2016-10-30 16:38 UTC (permalink / raw)
To: davem, netdev; +Cc: Yuval Mintz
In-Reply-To: <1477845517-17228-1-git-send-email-Yuval.Mintz@cavium.com>
While the qed_lm_maps is closely tied with the QED_LM_* defines,
when iterating over the array use actual size instead of the qed
define to prevent future possible issues.
Signed-off-by: Yuval Mintz <Yuval.Mintz@cavium.com>
---
drivers/net/ethernet/qlogic/qede/qede_ethtool.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c
index 42d9739..d230742 100644
--- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c
+++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c
@@ -320,7 +320,7 @@ struct qede_link_mode_mapping {
{ \
int i; \
\
- for (i = 0; i < QED_LM_COUNT; i++) { \
+ for (i = 0; i < ARRAY_SIZE(qed_lm_map); i++) { \
if ((caps) & (qed_lm_map[i].qed_link_mode)) \
__set_bit(qed_lm_map[i].ethtool_link_mode,\
lk_ksettings->link_modes.name); \
@@ -331,7 +331,7 @@ struct qede_link_mode_mapping {
{ \
int i; \
\
- for (i = 0; i < QED_LM_COUNT; i++) { \
+ for (i = 0; i < ARRAY_SIZE(qed_lm_map); i++) { \
if (test_bit(qed_lm_map[i].ethtool_link_mode, \
lk_ksettings->link_modes.name)) \
caps |= qed_lm_map[i].qed_link_mode; \
--
1.9.3
^ permalink raw reply related
* [PATCH net-next 3/7] qed*: Add support for WoL
From: Yuval Mintz @ 2016-10-30 16:38 UTC (permalink / raw)
To: davem, netdev; +Cc: Yuval Mintz
In-Reply-To: <1477845517-17228-1-git-send-email-Yuval.Mintz@cavium.com>
Signed-off-by: Yuval Mintz <Yuval.Mintz@cavium.com>
---
drivers/net/ethernet/qlogic/qed/qed.h | 11 ++++-
drivers/net/ethernet/qlogic/qed/qed_dev.c | 19 ++++++++-
drivers/net/ethernet/qlogic/qed/qed_hsi.h | 4 ++
drivers/net/ethernet/qlogic/qed/qed_main.c | 29 +++++++++++++
drivers/net/ethernet/qlogic/qed/qed_mcp.c | 56 ++++++++++++++++++++++++-
drivers/net/ethernet/qlogic/qede/qede.h | 2 +
drivers/net/ethernet/qlogic/qede/qede_ethtool.c | 41 ++++++++++++++++++
drivers/net/ethernet/qlogic/qede/qede_main.c | 9 ++++
include/linux/qed/qed_if.h | 10 +++++
9 files changed, 176 insertions(+), 5 deletions(-)
diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h
index f20243c..8828ffa 100644
--- a/drivers/net/ethernet/qlogic/qed/qed.h
+++ b/drivers/net/ethernet/qlogic/qed/qed.h
@@ -195,6 +195,11 @@ enum qed_dev_cap {
QED_DEV_CAP_ROCE,
};
+enum qed_wol_support {
+ QED_WOL_SUPPORT_NONE,
+ QED_WOL_SUPPORT_PME,
+};
+
struct qed_hw_info {
/* PCI personality */
enum qed_pci_personality personality;
@@ -227,6 +232,8 @@ struct qed_hw_info {
u32 hw_mode;
unsigned long device_capabilities;
u16 mtu;
+
+ enum qed_wol_support b_wol_support;
};
struct qed_hw_cid_data {
@@ -539,7 +546,9 @@ struct qed_dev {
u8 mcp_rev;
u8 boot_mode;
- u8 wol;
+ /* WoL related configurations */
+ u8 wol_config;
+ u8 wol_mac[ETH_ALEN];
u32 int_mode;
enum qed_coalescing_mode int_coalescing_mode;
diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c
index 9ef6dfd..13833a5 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_dev.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c
@@ -1363,8 +1363,24 @@ int qed_hw_reset(struct qed_dev *cdev)
{
int rc = 0;
u32 unload_resp, unload_param;
+ u32 wol_param;
int i;
+ switch (cdev->wol_config) {
+ case QED_OV_WOL_DISABLED:
+ wol_param = DRV_MB_PARAM_UNLOAD_WOL_DISABLED;
+ break;
+ case QED_OV_WOL_ENABLED:
+ wol_param = DRV_MB_PARAM_UNLOAD_WOL_ENABLED;
+ break;
+ default:
+ DP_NOTICE(cdev,
+ "Unknown WoL configuration %02x\n", cdev->wol_config);
+ /* Fallthrough */
+ case QED_OV_WOL_DEFAULT:
+ wol_param = DRV_MB_PARAM_UNLOAD_WOL_MCP;
+ }
+
for_each_hwfn(cdev, i) {
struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
@@ -1393,8 +1409,7 @@ int qed_hw_reset(struct qed_dev *cdev)
/* Send unload command to MCP */
rc = qed_mcp_cmd(p_hwfn, p_hwfn->p_main_ptt,
- DRV_MSG_CODE_UNLOAD_REQ,
- DRV_MB_PARAM_UNLOAD_WOL_MCP,
+ DRV_MSG_CODE_UNLOAD_REQ, wol_param,
&unload_resp, &unload_param);
if (rc) {
DP_NOTICE(p_hwfn, "qed_hw_reset: UNLOAD_REQ failed\n");
diff --git a/drivers/net/ethernet/qlogic/qed/qed_hsi.h b/drivers/net/ethernet/qlogic/qed/qed_hsi.h
index f7dfa2e..fdb7a09 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_hsi.h
+++ b/drivers/net/ethernet/qlogic/qed/qed_hsi.h
@@ -8601,6 +8601,7 @@ struct public_drv_mb {
#define DRV_MSG_CODE_BIST_TEST 0x001e0000
#define DRV_MSG_CODE_SET_LED_MODE 0x00200000
+#define DRV_MSG_CODE_OS_WOL 0x002e0000
#define DRV_MSG_SEQ_NUMBER_MASK 0x0000ffff
@@ -8697,6 +8698,9 @@ struct public_drv_mb {
#define FW_MSG_CODE_NVM_OK 0x00010000
#define FW_MSG_CODE_OK 0x00160000
+#define FW_MSG_CODE_OS_WOL_SUPPORTED 0x00800000
+#define FW_MSG_CODE_OS_WOL_NOT_SUPPORTED 0x00810000
+
#define FW_MSG_SEQ_NUMBER_MASK 0x0000ffff
u32 fw_mb_param;
diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c
index 612c094..a95a1af 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_main.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_main.c
@@ -223,6 +223,10 @@ int qed_fill_dev_info(struct qed_dev *cdev,
dev_info->fw_eng = FW_ENGINEERING_VERSION;
dev_info->mf_mode = cdev->mf_mode;
dev_info->tx_switching = true;
+
+ if (QED_LEADING_HWFN(cdev)->hw_info.b_wol_support ==
+ QED_WOL_SUPPORT_PME)
+ dev_info->wol_support = true;
} else {
qed_vf_get_fw_version(&cdev->hwfns[0], &dev_info->fw_major,
&dev_info->fw_minor, &dev_info->fw_rev,
@@ -1504,6 +1508,30 @@ static int qed_update_mtu(struct qed_dev *cdev, u16 mtu)
return status;
}
+static int qed_update_wol(struct qed_dev *cdev, bool enabled)
+{
+ struct qed_hwfn *hwfn = QED_LEADING_HWFN(cdev);
+ struct qed_ptt *ptt;
+ int rc = 0;
+
+ if (IS_VF(cdev))
+ return 0;
+
+ ptt = qed_ptt_acquire(hwfn);
+ if (!ptt)
+ return -EAGAIN;
+
+ rc = qed_mcp_ov_update_wol(hwfn, ptt, enabled ? QED_OV_WOL_ENABLED
+ : QED_OV_WOL_DISABLED);
+ if (rc)
+ goto out;
+ rc = qed_mcp_ov_update_current_config(hwfn, ptt, QED_OV_CLIENT_DRV);
+
+out:
+ qed_ptt_release(hwfn, ptt);
+ return rc;
+}
+
struct qed_selftest_ops qed_selftest_ops_pass = {
.selftest_memory = &qed_selftest_memory,
.selftest_interrupt = &qed_selftest_interrupt,
@@ -1542,6 +1570,7 @@ struct qed_selftest_ops qed_selftest_ops_pass = {
.update_drv_state = &qed_update_drv_state,
.update_mac = &qed_update_mac,
.update_mtu = &qed_update_mtu,
+ .update_wol = &qed_update_wol,
};
void qed_get_protocol_stats(struct qed_dev *cdev,
diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c
index 8be6157..768b35b 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c
@@ -330,6 +330,7 @@ static int qed_mcp_cmd_and_union(struct qed_hwfn *p_hwfn,
struct qed_mcp_mb_params *p_mb_params)
{
u32 union_data_addr;
+
int rc;
/* MCP not initialized */
@@ -375,11 +376,32 @@ int qed_mcp_cmd(struct qed_hwfn *p_hwfn,
u32 *o_mcp_param)
{
struct qed_mcp_mb_params mb_params;
+ union drv_union_data data_src;
int rc;
memset(&mb_params, 0, sizeof(mb_params));
+ memset(&data_src, 0, sizeof(data_src));
mb_params.cmd = cmd;
mb_params.param = param;
+
+ /* In case of UNLOAD_DONE, set the primary MAC */
+ if ((cmd == DRV_MSG_CODE_UNLOAD_DONE) &&
+ (p_hwfn->cdev->wol_config == QED_OV_WOL_ENABLED)) {
+ u8 *p_mac = p_hwfn->cdev->wol_mac;
+
+ data_src.wol_mac.mac_upper = p_mac[0] << 8 | p_mac[1];
+ data_src.wol_mac.mac_lower = p_mac[2] << 24 | p_mac[3] << 16 |
+ p_mac[4] << 8 | p_mac[5];
+
+ DP_VERBOSE(p_hwfn,
+ (QED_MSG_SP | NETIF_MSG_IFDOWN),
+ "Setting WoL MAC: %pM --> [%08x,%08x]\n",
+ p_mac, data_src.wol_mac.mac_upper,
+ data_src.wol_mac.mac_lower);
+
+ mb_params.p_data_src = &data_src;
+ }
+
rc = qed_mcp_cmd_and_union(p_hwfn, p_ptt, &mb_params);
if (rc)
return rc;
@@ -1058,6 +1080,9 @@ int qed_mcp_fill_shmem_func_info(struct qed_hwfn *p_hwfn,
info->mac[3] = (u8)(shmem_info.mac_lower >> 16);
info->mac[4] = (u8)(shmem_info.mac_lower >> 8);
info->mac[5] = (u8)(shmem_info.mac_lower);
+
+ /* Store primary MAC for later possible WoL */
+ memcpy(&p_hwfn->cdev->wol_mac, info->mac, ETH_ALEN);
} else {
DP_NOTICE(p_hwfn, "MAC is 0 in shmem\n");
}
@@ -1071,13 +1096,28 @@ int qed_mcp_fill_shmem_func_info(struct qed_hwfn *p_hwfn,
info->mtu = (u16)shmem_info.mtu_size;
+ p_hwfn->hw_info.b_wol_support = QED_WOL_SUPPORT_NONE;
+ p_hwfn->cdev->wol_config = (u8)QED_OV_WOL_DEFAULT;
+ if (qed_mcp_is_init(p_hwfn)) {
+ u32 resp = 0, param = 0;
+ int rc;
+
+ rc = qed_mcp_cmd(p_hwfn, p_ptt,
+ DRV_MSG_CODE_OS_WOL, 0, &resp, ¶m);
+ if (rc)
+ return rc;
+ if (resp == FW_MSG_CODE_OS_WOL_SUPPORTED)
+ p_hwfn->hw_info.b_wol_support = QED_WOL_SUPPORT_PME;
+ }
+
DP_VERBOSE(p_hwfn, (QED_MSG_SP | NETIF_MSG_IFUP),
- "Read configuration from shmem: pause_on_host %02x protocol %02x BW [%02x - %02x] MAC %02x:%02x:%02x:%02x:%02x:%02x wwn port %llx node %llx ovlan %04x\n",
+ "Read configuration from shmem: pause_on_host %02x protocol %02x BW [%02x - %02x] MAC %02x:%02x:%02x:%02x:%02x:%02x wwn port %llx node %llx ovlan %04x wol %02x\n",
info->pause_on_host, info->protocol,
info->bandwidth_min, info->bandwidth_max,
info->mac[0], info->mac[1], info->mac[2],
info->mac[3], info->mac[4], info->mac[5],
- info->wwn_port, info->wwn_node, info->ovlan);
+ info->wwn_port, info->wwn_node,
+ info->ovlan, (u8)p_hwfn->hw_info.b_wol_support);
return 0;
}
@@ -1322,6 +1362,9 @@ int qed_mcp_ov_update_mac(struct qed_hwfn *p_hwfn,
if (rc)
DP_ERR(p_hwfn, "Failed to send mac address, rc = %d\n", rc);
+ /* Store primary MAC for later possible WoL */
+ memcpy(p_hwfn->cdev->wol_mac, mac, ETH_ALEN);
+
return rc;
}
@@ -1332,6 +1375,12 @@ int qed_mcp_ov_update_wol(struct qed_hwfn *p_hwfn,
u32 drv_mb_param;
int rc;
+ if (p_hwfn->hw_info.b_wol_support == QED_WOL_SUPPORT_NONE) {
+ DP_VERBOSE(p_hwfn, QED_MSG_SP,
+ "Can't change WoL configuration when WoL isn't supported\n");
+ return -EINVAL;
+ }
+
switch (wol) {
case QED_OV_WOL_DEFAULT:
drv_mb_param = DRV_MB_PARAM_WOL_DEFAULT;
@@ -1352,6 +1401,9 @@ int qed_mcp_ov_update_wol(struct qed_hwfn *p_hwfn,
if (rc)
DP_ERR(p_hwfn, "Failed to send wol mode, rc = %d\n", rc);
+ /* Store the WoL update for a future unload */
+ p_hwfn->cdev->wol_config = (u8)wol;
+
return rc;
}
diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h
index 9135b9d..39e5a29 100644
--- a/drivers/net/ethernet/qlogic/qede/qede.h
+++ b/drivers/net/ethernet/qlogic/qede/qede.h
@@ -193,6 +193,8 @@ struct qede_dev {
u16 vxlan_dst_port;
u16 geneve_dst_port;
+ bool wol_enabled;
+
struct qede_rdma_dev rdma_info;
};
diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c
index fa658df..42d9739 100644
--- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c
+++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c
@@ -483,6 +483,45 @@ static void qede_get_drvinfo(struct net_device *ndev,
strlcpy(info->bus_info, pci_name(edev->pdev), sizeof(info->bus_info));
}
+static void qede_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol)
+{
+ struct qede_dev *edev = netdev_priv(ndev);
+
+ if (edev->dev_info.common.wol_support) {
+ wol->supported = WAKE_MAGIC;
+ wol->wolopts = edev->wol_enabled ? WAKE_MAGIC : 0;
+ }
+}
+
+static int qede_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol)
+{
+ struct qede_dev *edev = netdev_priv(ndev);
+ bool wol_requested;
+ int rc;
+
+ if (wol->wolopts & ~WAKE_MAGIC) {
+ DP_INFO(edev,
+ "Can't support WoL options other than magic-packet\n");
+ return -EINVAL;
+ }
+
+ wol_requested = !!(wol->wolopts & WAKE_MAGIC);
+ if (wol_requested == edev->wol_enabled)
+ return 0;
+
+ /* Need to actually change configuration */
+ if (!edev->dev_info.common.wol_support) {
+ DP_INFO(edev, "Device doesn't support WoL\n");
+ return -EINVAL;
+ }
+
+ rc = edev->ops->common->update_wol(edev->cdev, wol_requested);
+ if (!rc)
+ edev->wol_enabled = wol_requested;
+
+ return rc;
+}
+
static u32 qede_get_msglevel(struct net_device *ndev)
{
struct qede_dev *edev = netdev_priv(ndev);
@@ -1420,6 +1459,8 @@ static int qede_get_tunable(struct net_device *dev,
.get_drvinfo = qede_get_drvinfo,
.get_regs_len = qede_get_regs_len,
.get_regs = qede_get_regs,
+ .get_wol = qede_get_wol,
+ .set_wol = qede_set_wol,
.get_msglevel = qede_get_msglevel,
.set_msglevel = qede_set_msglevel,
.nway_reset = qede_nway_reset,
diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c
index 5cded8f..e25168a 100644
--- a/drivers/net/ethernet/qlogic/qede/qede_main.c
+++ b/drivers/net/ethernet/qlogic/qede/qede_main.c
@@ -95,6 +95,7 @@ enum qede_pci_private {
#define TX_TIMEOUT (5 * HZ)
static void qede_remove(struct pci_dev *pdev);
+static void qede_shutdown(struct pci_dev *pdev);
static int qede_alloc_rx_buffer(struct qede_dev *edev,
struct qede_rx_queue *rxq);
static void qede_link_update(void *dev, struct qed_link_output *link);
@@ -166,6 +167,7 @@ static int qede_sriov_configure(struct pci_dev *pdev, int num_vfs_param)
.id_table = qede_pci_tbl,
.probe = qede_probe,
.remove = qede_remove,
+ .shutdown = qede_shutdown,
#ifdef CONFIG_QED_SRIOV
.sriov_configure = qede_sriov_configure,
#endif
@@ -2706,6 +2708,8 @@ static void __qede_remove(struct pci_dev *pdev, enum qede_remove_mode mode)
/* Use global ops since we've freed edev */
qed_ops->common->slowpath_stop(cdev);
+ if (system_state == SYSTEM_POWER_OFF)
+ return;
qed_ops->common->remove(cdev);
dev_info(&pdev->dev, "Ending qede_remove successfully\n");
@@ -2716,6 +2720,11 @@ static void qede_remove(struct pci_dev *pdev)
__qede_remove(pdev, QEDE_REMOVE_NORMAL);
}
+static void qede_shutdown(struct pci_dev *pdev)
+{
+ __qede_remove(pdev, QEDE_REMOVE_NORMAL);
+}
+
/* -------------------------------------------------------------------------
* START OF LOAD / UNLOAD
* -------------------------------------------------------------------------
diff --git a/include/linux/qed/qed_if.h b/include/linux/qed/qed_if.h
index 696d22d..57a5f44 100644
--- a/include/linux/qed/qed_if.h
+++ b/include/linux/qed/qed_if.h
@@ -267,6 +267,8 @@ struct qed_dev_info {
bool tx_switching;
bool rdma_supported;
u16 mtu;
+
+ bool wol_support;
};
enum qed_sb_type {
@@ -590,6 +592,14 @@ struct qed_common_ops {
*
*/
int (*update_mtu)(struct qed_dev *cdev, u16 mtu);
+
+/**
+ * @brief update_wol - update of changes in the WoL configuration
+ *
+ * @param cdev
+ * @param enabled - true iff WoL should be enabled.
+ */
+ int (*update_wol) (struct qed_dev *cdev, bool enabled);
};
#define MASK_FIELD(_name, _value) \
--
1.9.3
^ permalink raw reply related
* [PATCH net-next 1/7] qed*: Management firmware - notifications and defaults
From: Yuval Mintz @ 2016-10-30 16:38 UTC (permalink / raw)
To: davem, netdev; +Cc: Sudarsana Kalluru, Yuval Mintz
In-Reply-To: <1477845517-17228-1-git-send-email-Yuval.Mintz@cavium.com>
From: Sudarsana Kalluru <Sudarsana.Kalluru@cavium.com>
Management firmware is interested in various tidbits about
the driver - including the driver state & several configuration
related fields [MTU, primtary MAC, etc.].
This adds the necessray logic to update MFW with such configurations,
some of which are passed directly via qed while for others APIs
are provide so that qede would be able to later configure if needed.
This also introduces a new default configuration for MTU which would
replace the default inherited by being an ethernet device.
Signed-off-by: Sudarsana Kalluru <Sudarsana.Kalluru@cavium.com>
Signed-off-by: Yuval Mintz <Yuval.Mintz@cavium.com>
---
drivers/net/ethernet/qlogic/qed/qed.h | 1 +
drivers/net/ethernet/qlogic/qed/qed_dev.c | 52 +++++++-
drivers/net/ethernet/qlogic/qed/qed_hsi.h | 59 ++++++++-
drivers/net/ethernet/qlogic/qed/qed_main.c | 75 +++++++++++
drivers/net/ethernet/qlogic/qed/qed_mcp.c | 163 ++++++++++++++++++++++++
drivers/net/ethernet/qlogic/qed/qed_mcp.h | 102 +++++++++++++++
drivers/net/ethernet/qlogic/qede/qede_ethtool.c | 2 +
drivers/net/ethernet/qlogic/qede/qede_main.c | 8 ++
include/linux/qed/qed_if.h | 28 ++++
9 files changed, 487 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h
index 653bb57..f20243c 100644
--- a/drivers/net/ethernet/qlogic/qed/qed.h
+++ b/drivers/net/ethernet/qlogic/qed/qed.h
@@ -226,6 +226,7 @@ struct qed_hw_info {
u32 port_mode;
u32 hw_mode;
unsigned long device_capabilities;
+ u16 mtu;
};
struct qed_hw_cid_data {
diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c
index 754f6a9..9ef6dfd 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_dev.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c
@@ -1056,8 +1056,10 @@ int qed_hw_init(struct qed_dev *cdev,
bool allow_npar_tx_switch,
const u8 *bin_fw_data)
{
- u32 load_code, param;
- int rc, mfw_rc, i;
+ u32 load_code, param, drv_mb_param;
+ bool b_default_mtu = true;
+ struct qed_hwfn *p_hwfn;
+ int rc = 0, mfw_rc, i;
if ((int_mode == QED_INT_MODE_MSI) && (cdev->num_hwfns > 1)) {
DP_NOTICE(cdev, "MSI mode is not supported for CMT devices\n");
@@ -1073,6 +1075,12 @@ int qed_hw_init(struct qed_dev *cdev,
for_each_hwfn(cdev, i) {
struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
+ /* If management didn't provide a default, set one of our own */
+ if (!p_hwfn->hw_info.mtu) {
+ p_hwfn->hw_info.mtu = 1500;
+ b_default_mtu = false;
+ }
+
if (IS_VF(cdev)) {
p_hwfn->b_int_enabled = 1;
continue;
@@ -1156,6 +1164,38 @@ int qed_hw_init(struct qed_dev *cdev,
p_hwfn->hw_init_done = true;
}
+ if (IS_PF(cdev)) {
+ p_hwfn = QED_LEADING_HWFN(cdev);
+ drv_mb_param = (FW_MAJOR_VERSION << 24) |
+ (FW_MINOR_VERSION << 16) |
+ (FW_REVISION_VERSION << 8) |
+ (FW_ENGINEERING_VERSION);
+ rc = qed_mcp_cmd(p_hwfn, p_hwfn->p_main_ptt,
+ DRV_MSG_CODE_OV_UPDATE_STORM_FW_VER,
+ drv_mb_param, &load_code, ¶m);
+ if (rc)
+ DP_INFO(p_hwfn, "Failed to update firmware version\n");
+
+ if (!b_default_mtu) {
+ rc = qed_mcp_ov_update_mtu(p_hwfn, p_hwfn->p_main_ptt,
+ p_hwfn->hw_info.mtu);
+ if (rc)
+ DP_INFO(p_hwfn,
+ "Failed to update default mtu\n");
+ }
+
+ rc = qed_mcp_ov_update_driver_state(p_hwfn,
+ p_hwfn->p_main_ptt,
+ QED_OV_DRIVER_STATE_DISABLED);
+ if (rc)
+ DP_INFO(p_hwfn, "Failed to update driver state\n");
+
+ rc = qed_mcp_ov_update_eswitch(p_hwfn, p_hwfn->p_main_ptt,
+ QED_OV_ESWITCH_VEB);
+ if (rc)
+ DP_INFO(p_hwfn, "Failed to update eswitch mode\n");
+ }
+
return 0;
}
@@ -1800,6 +1840,9 @@ static void qed_get_num_funcs(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
qed_get_num_funcs(p_hwfn, p_ptt);
+ if (qed_mcp_is_init(p_hwfn))
+ p_hwfn->hw_info.mtu = p_hwfn->mcp_info->func_info.mtu;
+
return qed_hw_get_resc(p_hwfn);
}
@@ -1974,8 +2017,13 @@ int qed_hw_prepare(struct qed_dev *cdev,
void qed_hw_remove(struct qed_dev *cdev)
{
+ struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
int i;
+ if (IS_PF(cdev))
+ qed_mcp_ov_update_driver_state(p_hwfn, p_hwfn->p_main_ptt,
+ QED_OV_DRIVER_STATE_NOT_LOADED);
+
for_each_hwfn(cdev, i) {
struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
diff --git a/drivers/net/ethernet/qlogic/qed/qed_hsi.h b/drivers/net/ethernet/qlogic/qed/qed_hsi.h
index 72eee29..36de87a 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_hsi.h
+++ b/drivers/net/ethernet/qlogic/qed/qed_hsi.h
@@ -8564,6 +8564,15 @@ struct public_drv_mb {
#define DRV_MSG_CODE_INIT_PHY 0x22000000
#define DRV_MSG_CODE_LINK_RESET 0x23000000
#define DRV_MSG_CODE_SET_DCBX 0x25000000
+#define DRV_MSG_CODE_OV_UPDATE_CURR_CFG 0x26000000
+#define DRV_MSG_CODE_OV_UPDATE_BUS_NUM 0x27000000
+#define DRV_MSG_CODE_OV_UPDATE_BOOT_PROGRESS 0x28000000
+#define DRV_MSG_CODE_OV_UPDATE_STORM_FW_VER 0x29000000
+#define DRV_MSG_CODE_OV_UPDATE_DRIVER_STATE 0x31000000
+#define DRV_MSG_CODE_BW_UPDATE_ACK 0x32000000
+#define DRV_MSG_CODE_OV_UPDATE_MTU 0x33000000
+#define DRV_MSG_CODE_OV_UPDATE_WOL 0x38000000
+#define DRV_MSG_CODE_OV_UPDATE_ESWITCH_MODE 0x39000000
#define DRV_MSG_CODE_BW_UPDATE_ACK 0x32000000
#define DRV_MSG_CODE_NIG_DRAIN 0x30000000
@@ -8574,6 +8583,13 @@ struct public_drv_mb {
#define DRV_MSG_CODE_MCP_RESET 0x00090000
#define DRV_MSG_CODE_SET_VERSION 0x000f0000
#define DRV_MSG_CODE_MCP_HALT 0x00100000
+#define DRV_MSG_CODE_SET_VMAC 0x00110000
+#define DRV_MSG_CODE_GET_VMAC 0x00120000
+#define DRV_MSG_CODE_VMAC_TYPE_SHIFT 4
+#define DRV_MSG_CODE_VMAC_TYPE_MASK 0x30
+#define DRV_MSG_CODE_VMAC_TYPE_MAC 1
+#define DRV_MSG_CODE_VMAC_TYPE_WWNN 2
+#define DRV_MSG_CODE_VMAC_TYPE_WWPN 3
#define DRV_MSG_CODE_GET_STATS 0x00130000
#define DRV_MSG_CODE_STATS_TYPE_LAN 1
@@ -8589,7 +8605,10 @@ struct public_drv_mb {
#define DRV_MSG_SEQ_NUMBER_MASK 0x0000ffff
u32 drv_mb_param;
-#define DRV_MB_PARAM_UNLOAD_WOL_MCP 0x00000001
+#define DRV_MB_PARAM_UNLOAD_WOL_UNKNOWN 0x00000000
+#define DRV_MB_PARAM_UNLOAD_WOL_MCP 0x00000001
+#define DRV_MB_PARAM_UNLOAD_WOL_DISABLED 0x00000002
+#define DRV_MB_PARAM_UNLOAD_WOL_ENABLED 0x00000003
#define DRV_MB_PARAM_DCBX_NOTIFY_MASK 0x000000FF
#define DRV_MB_PARAM_DCBX_NOTIFY_SHIFT 3
@@ -8602,6 +8621,44 @@ struct public_drv_mb {
#define DRV_MB_PARAM_LLDP_SEND_MASK 0x00000001
#define DRV_MB_PARAM_LLDP_SEND_SHIFT 0
+#define DRV_MB_PARAM_OV_CURR_CFG_SHIFT 0
+#define DRV_MB_PARAM_OV_CURR_CFG_MASK 0x0000000F
+#define DRV_MB_PARAM_OV_CURR_CFG_NONE 0
+#define DRV_MB_PARAM_OV_CURR_CFG_OS 1
+#define DRV_MB_PARAM_OV_CURR_CFG_VENDOR_SPEC 2
+#define DRV_MB_PARAM_OV_CURR_CFG_OTHER 3
+
+#define DRV_MB_PARAM_OV_STORM_FW_VER_SHIFT 0
+#define DRV_MB_PARAM_OV_STORM_FW_VER_MASK 0xFFFFFFFF
+#define DRV_MB_PARAM_OV_STORM_FW_VER_MAJOR_MASK 0xFF000000
+#define DRV_MB_PARAM_OV_STORM_FW_VER_MINOR_MASK 0x00FF0000
+#define DRV_MB_PARAM_OV_STORM_FW_VER_BUILD_MASK 0x0000FF00
+#define DRV_MB_PARAM_OV_STORM_FW_VER_DROP_MASK 0x000000FF
+
+#define DRV_MSG_CODE_OV_UPDATE_DRIVER_STATE_SHIFT 0
+#define DRV_MSG_CODE_OV_UPDATE_DRIVER_STATE_MASK 0xF
+#define DRV_MSG_CODE_OV_UPDATE_DRIVER_STATE_UNKNOWN 0x1
+#define DRV_MSG_CODE_OV_UPDATE_DRIVER_STATE_NOT_LOADED 0x2
+#define DRV_MSG_CODE_OV_UPDATE_DRIVER_STATE_LOADING 0x3
+#define DRV_MSG_CODE_OV_UPDATE_DRIVER_STATE_DISABLED 0x4
+#define DRV_MSG_CODE_OV_UPDATE_DRIVER_STATE_ACTIVE 0x5
+
+#define DRV_MB_PARAM_OV_MTU_SIZE_SHIFT 0
+#define DRV_MB_PARAM_OV_MTU_SIZE_MASK 0xFFFFFFFF
+
+#define DRV_MB_PARAM_WOL_MASK (DRV_MB_PARAM_WOL_DEFAULT | \
+ DRV_MB_PARAM_WOL_DISABLED | \
+ DRV_MB_PARAM_WOL_ENABLED)
+#define DRV_MB_PARAM_WOL_DEFAULT DRV_MB_PARAM_UNLOAD_WOL_MCP
+#define DRV_MB_PARAM_WOL_DISABLED DRV_MB_PARAM_UNLOAD_WOL_DISABLED
+#define DRV_MB_PARAM_WOL_ENABLED DRV_MB_PARAM_UNLOAD_WOL_ENABLED
+
+#define DRV_MB_PARAM_ESWITCH_MODE_MASK (DRV_MB_PARAM_ESWITCH_MODE_NONE | \
+ DRV_MB_PARAM_ESWITCH_MODE_VEB | \
+ DRV_MB_PARAM_ESWITCH_MODE_VEPA)
+#define DRV_MB_PARAM_ESWITCH_MODE_NONE 0x0
+#define DRV_MB_PARAM_ESWITCH_MODE_VEB 0x1
+#define DRV_MB_PARAM_ESWITCH_MODE_VEPA 0x2
#define DRV_MB_PARAM_SET_LED_MODE_OPER 0x0
#define DRV_MB_PARAM_SET_LED_MODE_ON 0x1
diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c
index 4ee3151..937968b 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_main.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_main.c
@@ -245,6 +245,8 @@ int qed_fill_dev_info(struct qed_dev *cdev,
&dev_info->mfw_rev, NULL);
}
+ dev_info->mtu = QED_LEADING_HWFN(cdev)->hw_info.mtu;
+
return 0;
}
@@ -1432,6 +1434,76 @@ static int qed_set_led(struct qed_dev *cdev, enum qed_led_mode mode)
return status;
}
+static int qed_update_drv_state(struct qed_dev *cdev, bool active)
+{
+ struct qed_hwfn *hwfn = QED_LEADING_HWFN(cdev);
+ struct qed_ptt *ptt;
+ int status = 0;
+
+ if (IS_VF(cdev))
+ return 0;
+
+ ptt = qed_ptt_acquire(hwfn);
+ if (!ptt)
+ return -EAGAIN;
+
+ status = qed_mcp_ov_update_driver_state(hwfn, ptt, active ?
+ QED_OV_DRIVER_STATE_ACTIVE :
+ QED_OV_DRIVER_STATE_DISABLED);
+
+ qed_ptt_release(hwfn, ptt);
+
+ return status;
+}
+
+static int qed_update_mac(struct qed_dev *cdev, u8 *mac)
+{
+ struct qed_hwfn *hwfn = QED_LEADING_HWFN(cdev);
+ struct qed_ptt *ptt;
+ int status = 0;
+
+ if (IS_VF(cdev))
+ return 0;
+
+ ptt = qed_ptt_acquire(hwfn);
+ if (!ptt)
+ return -EAGAIN;
+
+ status = qed_mcp_ov_update_mac(hwfn, ptt, mac);
+ if (status)
+ goto out;
+
+ status = qed_mcp_ov_update_current_config(hwfn, ptt, QED_OV_CLIENT_DRV);
+
+out:
+ qed_ptt_release(hwfn, ptt);
+ return status;
+}
+
+static int qed_update_mtu(struct qed_dev *cdev, u16 mtu)
+{
+ struct qed_hwfn *hwfn = QED_LEADING_HWFN(cdev);
+ struct qed_ptt *ptt;
+ int status = 0;
+
+ if (IS_VF(cdev))
+ return 0;
+
+ ptt = qed_ptt_acquire(hwfn);
+ if (!ptt)
+ return -EAGAIN;
+
+ status = qed_mcp_ov_update_mtu(hwfn, ptt, mtu);
+ if (status)
+ goto out;
+
+ status = qed_mcp_ov_update_current_config(hwfn, ptt, QED_OV_CLIENT_DRV);
+
+out:
+ qed_ptt_release(hwfn, ptt);
+ return status;
+}
+
struct qed_selftest_ops qed_selftest_ops_pass = {
.selftest_memory = &qed_selftest_memory,
.selftest_interrupt = &qed_selftest_interrupt,
@@ -1466,6 +1538,9 @@ struct qed_selftest_ops qed_selftest_ops_pass = {
.get_coalesce = &qed_get_coalesce,
.set_coalesce = &qed_set_coalesce,
.set_led = &qed_set_led,
+ .update_drv_state = &qed_update_drv_state,
+ .update_mac = &qed_update_mac,
+ .update_mtu = &qed_update_mtu,
};
void qed_get_protocol_stats(struct qed_dev *cdev,
diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c
index bdc9ba9..98dc913 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c
@@ -14,6 +14,7 @@
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/string.h>
+#include <linux/etherdevice.h>
#include "qed.h"
#include "qed_dcbx.h"
#include "qed_hsi.h"
@@ -1068,6 +1069,8 @@ int qed_mcp_fill_shmem_func_info(struct qed_hwfn *p_hwfn,
info->ovlan = (u16)(shmem_info.ovlan_stag & FUNC_MF_CFG_OV_STAG_MASK);
+ info->mtu = (u16)shmem_info.mtu_size;
+
DP_VERBOSE(p_hwfn, (QED_MSG_SP | NETIF_MSG_IFUP),
"Read configuration from shmem: pause_on_host %02x protocol %02x BW [%02x - %02x] MAC %02x:%02x:%02x:%02x:%02x:%02x wwn port %llx node %llx ovlan %04x\n",
info->pause_on_host, info->protocol,
@@ -1223,6 +1226,166 @@ int qed_mcp_resume(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
return (cpu_mode & MCP_REG_CPU_MODE_SOFT_HALT) ? -EAGAIN : 0;
}
+int qed_mcp_ov_update_current_config(struct qed_hwfn *p_hwfn,
+ struct qed_ptt *p_ptt,
+ enum qed_ov_client client)
+{
+ u32 resp = 0, param = 0;
+ u32 drv_mb_param;
+ int rc;
+
+ switch (client) {
+ case QED_OV_CLIENT_DRV:
+ drv_mb_param = DRV_MB_PARAM_OV_CURR_CFG_OS;
+ break;
+ case QED_OV_CLIENT_USER:
+ drv_mb_param = DRV_MB_PARAM_OV_CURR_CFG_OTHER;
+ break;
+ case QED_OV_CLIENT_VENDOR_SPEC:
+ drv_mb_param = DRV_MB_PARAM_OV_CURR_CFG_VENDOR_SPEC;
+ break;
+ default:
+ DP_NOTICE(p_hwfn, "Invalid client type %d\n", client);
+ return -EINVAL;
+ }
+
+ rc = qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_OV_UPDATE_CURR_CFG,
+ drv_mb_param, &resp, ¶m);
+ if (rc)
+ DP_ERR(p_hwfn, "MCP response failure, aborting\n");
+
+ return rc;
+}
+
+int qed_mcp_ov_update_driver_state(struct qed_hwfn *p_hwfn,
+ struct qed_ptt *p_ptt,
+ enum qed_ov_driver_state drv_state)
+{
+ u32 resp = 0, param = 0;
+ u32 drv_mb_param;
+ int rc;
+
+ switch (drv_state) {
+ case QED_OV_DRIVER_STATE_NOT_LOADED:
+ drv_mb_param = DRV_MSG_CODE_OV_UPDATE_DRIVER_STATE_NOT_LOADED;
+ break;
+ case QED_OV_DRIVER_STATE_DISABLED:
+ drv_mb_param = DRV_MSG_CODE_OV_UPDATE_DRIVER_STATE_DISABLED;
+ break;
+ case QED_OV_DRIVER_STATE_ACTIVE:
+ drv_mb_param = DRV_MSG_CODE_OV_UPDATE_DRIVER_STATE_ACTIVE;
+ break;
+ default:
+ DP_NOTICE(p_hwfn, "Invalid driver state %d\n", drv_state);
+ return -EINVAL;
+ }
+
+ rc = qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_OV_UPDATE_DRIVER_STATE,
+ drv_mb_param, &resp, ¶m);
+ if (rc)
+ DP_ERR(p_hwfn, "Failed to send driver state\n");
+
+ return rc;
+}
+
+int qed_mcp_ov_update_mtu(struct qed_hwfn *p_hwfn,
+ struct qed_ptt *p_ptt, u16 mtu)
+{
+ u32 resp = 0, param = 0;
+ u32 drv_mb_param;
+ int rc;
+
+ drv_mb_param = (u32)mtu << DRV_MB_PARAM_OV_MTU_SIZE_SHIFT;
+ rc = qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_OV_UPDATE_MTU,
+ drv_mb_param, &resp, ¶m);
+ if (rc)
+ DP_ERR(p_hwfn, "Failed to send mtu value, rc = %d\n", rc);
+
+ return rc;
+}
+
+int qed_mcp_ov_update_mac(struct qed_hwfn *p_hwfn,
+ struct qed_ptt *p_ptt, u8 *mac)
+{
+ struct qed_mcp_mb_params mb_params;
+ union drv_union_data union_data;
+ int rc;
+
+ memset(&mb_params, 0, sizeof(mb_params));
+ mb_params.cmd = DRV_MSG_CODE_SET_VMAC;
+ mb_params.param = DRV_MSG_CODE_VMAC_TYPE_MAC <<
+ DRV_MSG_CODE_VMAC_TYPE_SHIFT;
+ mb_params.param |= MCP_PF_ID(p_hwfn);
+ ether_addr_copy(&union_data.raw_data[0], mac);
+ mb_params.p_data_src = &union_data;
+ rc = qed_mcp_cmd_and_union(p_hwfn, p_ptt, &mb_params);
+ if (rc)
+ DP_ERR(p_hwfn, "Failed to send mac address, rc = %d\n", rc);
+
+ return rc;
+}
+
+int qed_mcp_ov_update_wol(struct qed_hwfn *p_hwfn,
+ struct qed_ptt *p_ptt, enum qed_ov_wol wol)
+{
+ u32 resp = 0, param = 0;
+ u32 drv_mb_param;
+ int rc;
+
+ switch (wol) {
+ case QED_OV_WOL_DEFAULT:
+ drv_mb_param = DRV_MB_PARAM_WOL_DEFAULT;
+ break;
+ case QED_OV_WOL_DISABLED:
+ drv_mb_param = DRV_MB_PARAM_WOL_DISABLED;
+ break;
+ case QED_OV_WOL_ENABLED:
+ drv_mb_param = DRV_MB_PARAM_WOL_ENABLED;
+ break;
+ default:
+ DP_ERR(p_hwfn, "Invalid wol state %d\n", wol);
+ return -EINVAL;
+ }
+
+ rc = qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_OV_UPDATE_WOL,
+ drv_mb_param, &resp, ¶m);
+ if (rc)
+ DP_ERR(p_hwfn, "Failed to send wol mode, rc = %d\n", rc);
+
+ return rc;
+}
+
+int qed_mcp_ov_update_eswitch(struct qed_hwfn *p_hwfn,
+ struct qed_ptt *p_ptt,
+ enum qed_ov_eswitch eswitch)
+{
+ u32 resp = 0, param = 0;
+ u32 drv_mb_param;
+ int rc;
+
+ switch (eswitch) {
+ case QED_OV_ESWITCH_NONE:
+ drv_mb_param = DRV_MB_PARAM_ESWITCH_MODE_NONE;
+ break;
+ case QED_OV_ESWITCH_VEB:
+ drv_mb_param = DRV_MB_PARAM_ESWITCH_MODE_VEB;
+ break;
+ case QED_OV_ESWITCH_VEPA:
+ drv_mb_param = DRV_MB_PARAM_ESWITCH_MODE_VEPA;
+ break;
+ default:
+ DP_ERR(p_hwfn, "Invalid eswitch mode %d\n", eswitch);
+ return -EINVAL;
+ }
+
+ rc = qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_OV_UPDATE_ESWITCH_MODE,
+ drv_mb_param, &resp, ¶m);
+ if (rc)
+ DP_ERR(p_hwfn, "Failed to send eswitch mode, rc = %d\n", rc);
+
+ return rc;
+}
+
int qed_mcp_set_led(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt, enum qed_led_mode mode)
{
diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.h b/drivers/net/ethernet/qlogic/qed/qed_mcp.h
index dff520e..8950719 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_mcp.h
+++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.h
@@ -92,6 +92,8 @@ struct qed_mcp_function_info {
#define QED_MCP_VLAN_UNSET (0xffff)
u16 ovlan;
+
+ u16 mtu;
};
struct qed_mcp_nvm_common {
@@ -147,6 +149,30 @@ enum qed_mcp_protocol_type {
struct qed_mcp_rdma_stats rdma_stats;
};
+enum qed_ov_eswitch {
+ QED_OV_ESWITCH_NONE,
+ QED_OV_ESWITCH_VEB,
+ QED_OV_ESWITCH_VEPA
+};
+
+enum qed_ov_client {
+ QED_OV_CLIENT_DRV,
+ QED_OV_CLIENT_USER,
+ QED_OV_CLIENT_VENDOR_SPEC
+};
+
+enum qed_ov_driver_state {
+ QED_OV_DRIVER_STATE_NOT_LOADED,
+ QED_OV_DRIVER_STATE_DISABLED,
+ QED_OV_DRIVER_STATE_ACTIVE
+};
+
+enum qed_ov_wol {
+ QED_OV_WOL_DEFAULT,
+ QED_OV_WOL_DISABLED,
+ QED_OV_WOL_ENABLED
+};
+
/**
* @brief - returns the link params of the hw function
*
@@ -278,6 +304,69 @@ int qed_mcp_get_flash_size(struct qed_hwfn *p_hwfn,
struct qed_mcp_drv_version *p_ver);
/**
+ * @brief Notify MFW about the change in base device properties
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ * @param client - qed client type
+ *
+ * @return int - 0 - operation was successful.
+ */
+int qed_mcp_ov_update_current_config(struct qed_hwfn *p_hwfn,
+ struct qed_ptt *p_ptt,
+ enum qed_ov_client client);
+
+/**
+ * @brief Notify MFW about the driver state
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ * @param drv_state - Driver state
+ *
+ * @return int - 0 - operation was successful.
+ */
+int qed_mcp_ov_update_driver_state(struct qed_hwfn *p_hwfn,
+ struct qed_ptt *p_ptt,
+ enum qed_ov_driver_state drv_state);
+
+/**
+ * @brief Send MTU size to MFW
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ * @param mtu - MTU size
+ *
+ * @return int - 0 - operation was successful.
+ */
+int qed_mcp_ov_update_mtu(struct qed_hwfn *p_hwfn,
+ struct qed_ptt *p_ptt, u16 mtu);
+
+/**
+ * @brief Send MAC address to MFW
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ * @param mac - MAC address
+ *
+ * @return int - 0 - operation was successful.
+ */
+int qed_mcp_ov_update_mac(struct qed_hwfn *p_hwfn,
+ struct qed_ptt *p_ptt, u8 *mac);
+
+/**
+ * @brief Send WOL mode to MFW
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ * @param wol - WOL mode
+ *
+ * @return int - 0 - operation was successful.
+ */
+int qed_mcp_ov_update_wol(struct qed_hwfn *p_hwfn,
+ struct qed_ptt *p_ptt,
+ enum qed_ov_wol wol);
+
+/**
* @brief Set LED status
*
* @param p_hwfn
@@ -546,4 +635,17 @@ int __qed_configure_pf_min_bandwidth(struct qed_hwfn *p_hwfn,
int qed_mcp_mask_parities(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt, u32 mask_parities);
+/**
+ * @brief Send eswitch mode to MFW
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ * @param eswitch - eswitch mode
+ *
+ * @return int - 0 - operation was successful.
+ */
+int qed_mcp_ov_update_eswitch(struct qed_hwfn *p_hwfn,
+ struct qed_ptt *p_ptt,
+ enum qed_ov_eswitch eswitch);
+
#endif
diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c
index b7dbb44..3e477734 100644
--- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c
+++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c
@@ -739,6 +739,8 @@ int qede_change_mtu(struct net_device *ndev, int new_mtu)
qede_update_mtu(edev, &args);
+ edev->ops->common->update_mtu(edev->cdev, args.mtu);
+
return 0;
}
diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c
index 4f29865..5cded8f 100644
--- a/drivers/net/ethernet/qlogic/qede/qede_main.c
+++ b/drivers/net/ethernet/qlogic/qede/qede_main.c
@@ -2397,6 +2397,8 @@ static void qede_init_ndev(struct qede_dev *edev)
/* Set network device HW mac */
ether_addr_copy(edev->ndev->dev_addr, edev->dev_info.common.hw_mac);
+
+ ndev->mtu = edev->dev_info.common.mtu;
}
/* This function converts from 32b param to two params of level and module
@@ -3752,6 +3754,8 @@ static int qede_open(struct net_device *ndev)
udp_tunnel_get_rx_info(ndev);
+ edev->ops->common->update_drv_state(edev->cdev, true);
+
return 0;
}
@@ -3761,6 +3765,8 @@ static int qede_close(struct net_device *ndev)
qede_unload(edev, QEDE_UNLOAD_NORMAL);
+ edev->ops->common->update_drv_state(edev->cdev, false);
+
return 0;
}
@@ -3821,6 +3827,8 @@ static int qede_set_mac_addr(struct net_device *ndev, void *p)
if (rc)
return rc;
+ edev->ops->common->update_mac(edev->cdev, addr->sa_data);
+
/* Add MAC filter according to the new unicast HW MAC address */
ether_addr_copy(edev->primary_mac, ndev->dev_addr);
return qede_set_ucast_rx_mac(edev, QED_FILTER_XCAST_TYPE_ADD,
diff --git a/include/linux/qed/qed_if.h b/include/linux/qed/qed_if.h
index f9ae903..634ae6f 100644
--- a/include/linux/qed/qed_if.h
+++ b/include/linux/qed/qed_if.h
@@ -266,6 +266,7 @@ struct qed_dev_info {
u8 mf_mode;
bool tx_switching;
bool rdma_supported;
+ u16 mtu;
};
enum qed_sb_type {
@@ -553,6 +554,33 @@ struct qed_common_ops {
*/
int (*set_led)(struct qed_dev *cdev,
enum qed_led_mode mode);
+
+/**
+ * @brief update_drv_state - API to inform the change in the driver state.
+ *
+ * @param cdev
+ * @param active
+ *
+ */
+ int (*update_drv_state)(struct qed_dev *cdev, bool active);
+
+/**
+ * @brief update_mac - API to inform the change in the mac address
+ *
+ * @param cdev
+ * @param mac
+ *
+ */
+ int (*update_mac)(struct qed_dev *cdev, u8 *mac);
+
+/**
+ * @brief update_mtu - API to inform the change in the mtu
+ *
+ * @param cdev
+ * @param mtu
+ *
+ */
+ int (*update_mtu)(struct qed_dev *cdev, u16 mtu);
};
#define MASK_FIELD(_name, _value) \
--
1.9.3
^ permalink raw reply related
* [PATCH net-next 0/7] qed*: Patch series
From: Yuval Mintz @ 2016-10-30 16:38 UTC (permalink / raw)
To: davem, netdev; +Cc: Yuval Mintz
This series does several things. The bigger changes:
- Add new notification APIs [& Defaults] for various fields.
The series then utilizes some of those qed <-> qede APIs to bass WoL
support upon.
- Change the resource allocation scheme to receive the values from
management firmware, instead of equally sharing resources between
functions [that might not need those]. That would, e.g., allow us to
configure additional filters to network interfaces in presence of
storage [PCI] functions from same adapter.
Dave,
Please consider applying this series to `net-next'.
Thanks,
Yuval
Sudarsana Kalluru (1):
qed*: Management firmware - notifications and defaults
Tomer Tayar (1):
qed: Learn resources from management firmware
Yuval Mintz (5):
qed: Add nvram selftest
qed*: Add support for WoL
qede: Decouple ethtool caps from qed
qed: Learn of RDMA capabilities per-device
qed: Use VF-queue feature
drivers/net/ethernet/qlogic/qed/qed.h | 19 +-
drivers/net/ethernet/qlogic/qed/qed_dev.c | 382 +++++++++++++++++----
drivers/net/ethernet/qlogic/qed/qed_hsi.h | 120 ++++++-
drivers/net/ethernet/qlogic/qed/qed_int.c | 32 +-
drivers/net/ethernet/qlogic/qed/qed_l2.c | 2 +-
drivers/net/ethernet/qlogic/qed/qed_main.c | 105 ++++++
drivers/net/ethernet/qlogic/qed/qed_mcp.c | 433 +++++++++++++++++++++++-
drivers/net/ethernet/qlogic/qed/qed_mcp.h | 158 +++++++++
drivers/net/ethernet/qlogic/qed/qed_selftest.c | 101 ++++++
drivers/net/ethernet/qlogic/qed/qed_selftest.h | 10 +
drivers/net/ethernet/qlogic/qed/qed_sriov.c | 17 +-
drivers/net/ethernet/qlogic/qede/qede.h | 2 +
drivers/net/ethernet/qlogic/qede/qede_ethtool.c | 54 ++-
drivers/net/ethernet/qlogic/qede/qede_main.c | 17 +
include/linux/qed/qed_eth_if.h | 2 +-
include/linux/qed/qed_if.h | 47 +++
16 files changed, 1404 insertions(+), 97 deletions(-)
--
1.9.3
^ permalink raw reply
* Re: Let's do P4
From: Jiri Pirko @ 2016-10-30 16:38 UTC (permalink / raw)
To: Thomas Graf
Cc: John Fastabend, Jakub Kicinski, netdev, davem, jhs, roopa,
simon.horman, ast, daniel, prem, hannes, jbenc, tom, mattyk,
idosch, eladr, yotamg, nogahf, ogerlitz, linville, andy,
f.fainelli, dsa, vivien.didelot, andrew, ivecera,
Maciej Żenczykowski
In-Reply-To: <20161030102649.GE1810@pox.localdomain>
Sun, Oct 30, 2016 at 11:26:49AM CET, tgraf@suug.ch wrote:
>On 10/30/16 at 08:44am, Jiri Pirko wrote:
>> Sat, Oct 29, 2016 at 06:46:21PM CEST, john.fastabend@gmail.com wrote:
>> >On 16-10-29 07:49 AM, Jakub Kicinski wrote:
>> >> On Sat, 29 Oct 2016 09:53:28 +0200, Jiri Pirko wrote:
>> >>> Hi all.
>> >>>
>> >>> The network world is divided into 2 general types of hw:
>> >>> 1) network ASICs - network specific silicon, containing things like TCAM
>> >>> These ASICs are suitable to be programmed by P4.
>> >>> 2) network processors - basically a general purpose CPUs
>> >>> These processors are suitable to be programmed by eBPF.
>> >>>
>> >>> I believe that by now, the most people came to a conclusion that it is
>> >>> very difficult to handle both types by either P4 or eBPF. And since
>> >>> eBPF is part of the kernel, I would like to introduce P4 into kernel
>> >>> as well. Here's a plan:
>> >>>
>> >>> 1) Define P4 intermediate representation
>> >>> I cannot imagine loading P4 program (c-like syntax text file) into
>> >>> kernel as is. That means that as the first step, we need find some
>> >>> intermediate representation. I can imagine someting in a form of AST,
>> >>> call it "p4ast". I don't really know how to do this exactly though,
>> >>> it's just an idea.
>> >>>
>> >>> In the end there would be a userspace precompiler for this:
>> >>> $ makep4ast example.p4 example.ast
>> >>
>> >> Maybe stating the obvious, but IMHO defining the IR is the hardest part.
>> >> eBPF *is* the IR, we can compile C, P4 or even JIT Lua to eBPF. The
>> >> AST/IR for switch pipelines should allow for similar flexibility.
>> >> Looser coupling would also protect us from changes in spec of the high
>> >> level language.
>
>My assumption was that a new IR is defined which is easier to parse than
>eBPF which is targeted at execution on a CPU and not indented for pattern
>matching. Just looking at how llvm creates different patterns and reorders
>instructions, I'm not seeing how eBPF can serve as a general purpose IR
>if the objective is to allow fairly flexible generation of the bytecode.
>Hence the alternative IR serving as additional metadata complementing the
>eBPF program.
Agreed.
[...]
>> >... And merging threads here with Jiri's email ...
>> >
>> >> If you do p4>ebpf in userspace, you have 2 apis:
>> >> 1) to setup sw (in-kernel) p4 datapath, you push bpf.o to kernel
>> >> 2) to setup hw p4 datapath, you push program.p4ast to kernel
>> >>
>> >> Those are 2 apis. Both wrapped up by TC, but still 2 apis.
>> >>
>> >> What I believe is correct is to have one api:
>> >> 1) to setup sw (in-kernel) p4 datapath, you push program.p4ast to kernel
>> >> 2) to setup hw p4 datapath, you push program.p4ast to kernel
>
>I understand what you mean with two APIs now. You want a single IR
>block and divide the SW/HW part in the kernel rather than let llvm or
>something else do it.
Exactly. Following drawing shows p4 pipeline setup for SW and Hw:
|
| +--> ebpf engine
| |
| |
| compilerB
| ^
| |
p4src --> compilerA --> p4ast --TCNL--> cls_p4 --+-> driver -> compilerC -> HW
|
userspace | kernel
|
Now please consider runtime API for rule insertion/removal/stats/etc.
Also, the single API is cls_p4 here:
|
|
|
|
| ebpf map fillup
| ^
| |
p4 rule --TCNL--> cls_p4 --+-> driver -> HW table fillup
|
userspace | kernel
>
>> >Couple comments around this, first adding yet another IR in the kernel
>> >and another JIT engine to map that IR on to eBPF or hardware vendor X
>> >doesn't get me excited. Its really much easier to write these as backend
>> >objects in LLVM. Not saying it can't be done just saying it is easier
>> >in LLVM. Also we already have the LLVM code for P4 to LLVM-IR to eBPF.
>> >In the end this would be a reasonably complex bit of code in
>> >the kernel only for hardware offload. I have doubts that folks would
>> >ever use it for software only cases. I'm happy to admit I'm wrong here
>> >though.
>>
>> Well for hw offload, every driver has to parse the IR (whatever will it
>> be in) and program HW accordingly. Similar parsing and translation would
>> be needed for SW path, to translate into eBPF. I don't think it would be
>> more complex than in the drivers. Should be fine.
>
>I'm not sure I see why anyone would ever want to use an IR for SW
>purposes which is restricted to the lowest common denominator of HW.
>A good example here is OpenFlow and how some of its SW consumers
>have evolved with extensions which cannot be mappepd to HW easily.
>The same seems to happen with P4 as it introduces the concept of
>state and other concepts which are hard to map for dumb HW. P4 doesn't
>magically solve this problem, the fundamental difference in
>capabilities between HW and SW remain.
>
>> >So yes using llvm backends creates two paths a hardware mgmt and sw
>> >path but in the hardware + software case typical on the edge the
>> >orchestration and management planes have started to manage the hardware
>> >and software as two blocks of logic for performance SLA logic. Even on
>> >the edge it seems in most cases folks are selling SR-IOV ports and
>> >can't fall back to software and charge for the port. But this is just
>> >one use case I suspect others where it does make sense.
>> >
>> >> In case of 1), the program.p4ast will be either interpreted by new p4
>> >> interpreter, of translated to bpf and interpreted by that. But this
>> >> translation code is part of kernel.
>> >
>> >Finally a couple historic bits. The Flow-API proposed in Ottawa was
>> >mechanically generated from an original P4 draft. At the time I was
>> >working fairly closely with both the hardware and compiler folks. If
>> >there is interest we could use that as a base IR for hardware. It has
>> >a simple mapping to/from the original P4 spec. The newer P4 specs are
>> >significantly more complex by the way.
>>
>> Yeah, I was also thinking about something similar to your Flow-API,
>> but we need something more generic I believe.
>>
>> >We also have an emulated path also auto-generated from compiler tools
>> >that creates eBPF code from the IR so this would give you the software
>> >fall-back.
>>
>> Btw, Flow-API was rejected because it was a clean kernel-bypass. In case
>> of p4, if we do what Thomas is suggesting, having x.bpf for SW and
>> x.p4ast for HW, that would be the very same kernel-bypass. Therefore I
>> strongly believe there should be a single kernel API for p4 SW+HW - for
>> both p4 program insertion and runtime configuration.
>
>I think you misunderstand me. This is not what I'm proposing at all.
>In either model, the kernel receives the same IR and can reject.
>
>The rule is very clear: we can't allow to program anything that the
>kernel is not capable of doing in SW, right? That was the key take
>away from that discussion.
***
Exactly. But if you treat p4ast as a "metadata" of ebpf program destined
solely to setup HW, that in my opinion is a bypass. Because the ebpf part
and p4ast part could have no relacionship with each other. So I see it as
2 independent APIs. One for SW, one for HW. And having this kind od API
for hw only is a bypass.
Plus the thing I cannot imagine in the model you propose is table fillup.
For ebpf, you use maps. For p4 you would have to have a separate HW-only
API. This is very similar to the original John's Flow-API. And therefore
a kernel bypass.
>
>Let's assume we do cls_p4ast HW+SW with an eBPF translator for SW. As a
>user of this, as my program becomes more complex I will hit the wall of
>HW capabilities at some point and either the IR is not expressive
>enough or the driver will reject the program.
That can certainly happen, no matter what model we choose.
>
>I have at least three options now:
>
> 1) I move everything to SW and forget about HW programmability
>
> 2) I let HW bail out when HW can't support it and start parsing from
> scratch in SW. I don't really care how much of it has been done
> in HW, it's a best effort optimization. A use case for this might
> be dropping of packets. This is easy to do with flow based
> offloads as it can be best effort but already difficult when
> programming based on a IR.
>
> 3) I let HW bail out but carry some metadata trying to preserve
> some of the work done. A use case for this would be a router type
> of work where the lookup itself can be expensive and the majority
> of actions taken on packets are simple forwards but a subset of
> actions performed are too complex for HW. You still want to
> preserve the savings from the expensive lookup already performed.
>
You still have a choice to do this:
use cls_bpf SKIP_HW for SW processing
use cls_p4 SKIP_SW for HW processing
That gives you flexibility to program the pipelines separatelly. If a driver
is not capable of handling the selected p4 program, it will refuse to
program the HW. Then it is up to the user to change the program according
to the HW features.
>Even for the simpler 2) I can't just put everything I need into my
>p4ast program because the program will either load in its entirety or
>it won't.
>
>What I would likely end up doing is to write any number of subsets of
>my program which only contain various levels of pieces that are very
>likely to load on my target HW. I then load my full program with tc
>and want a notification if it also loaded into HW. If it HW failed,
>then I want tc to load subset programs with SKIP_SW starting from the
>one with most complexity. I need SKIP_SW because I already have the
>full program loaded and I don't want to go through both the partial
>and full program in case of a HW bail out. Is your proposal to not
>allow for the SKIP_SW?
Definitelly not. User should be able to pass SKIP_SW and SKIP_HW as he is
now able to do it for cls_u32, cls_flower and others.
>
>A more evolved form of this would be to expose capabilities of the HW
>and have the program writer include logic which results in the split
>based on the capabilities of the hardware.
I wonder why p4 does not handle the HW capabilities. At least I did
not find it. It would be certainly nice to have it.
>
>I I understand you correctly, you propose to make this split
>automatically in the kernel somehow.
>
>In either model the kernel receives the same new IR which it can
>reject. No difference. None of the models allow more or less.
>
>In either model, the program can be loaded with SKIP_SW to load a valid
>program into HW only.
>
>In either model, an eBPF program can be loaded at cls_bpf, or a new IR
>can be loaded with SKIP_HW to do SW only.
>
>The only difference I see between the models is whether to include a
>new IR => eBPF compiler in the kernel or not which is going to be
>optional anyway.
The main dirrefence I see is the single API/kernel bypass problem
I described earlier in this email (***)
^ permalink raw reply
* Re: [PATCH net-next 0/2] mlx4 XDP TX refactor
From: Tariq Toukan @ 2016-10-30 16:21 UTC (permalink / raw)
To: Tariq Toukan, David S. Miller; +Cc: netdev, Eran Ben Elisha
In-Reply-To: <1477579924-32737-1-git-send-email-tariqt@mellanox.com>
Hi Dave,
This series makes Brenden's fix unneeded:
958b3d396d7f ("net/mlx4_en: fixup xdp tx irq to match rx")
The fix got into net, but yet to be in net-next.
Should I wait with this series and send a re-spin, with a revert of the
fix, once it gets into net-next?
Regards,
Tariq
On 27/10/2016 5:52 PM, Tariq Toukan wrote:
> Hi Dave,
>
> This patchset refactors the XDP forwarding case, so that
> its dedicated transmit queues are managed in a complete
> separation from the other regular ones.
>
> Series generated against net-next commit:
> 6edf10173a1f "devlink: Prevent port_type_set() callback when it's not needed"
>
> Thanks,
> Tariq.
>
> Tariq Toukan (2):
> net/mlx4_en: Add TX_XDP for CQ types
> net/mlx4_en: Refactor the XDP forwarding rings scheme
>
> drivers/net/ethernet/mellanox/mlx4/en_cq.c | 18 +-
> drivers/net/ethernet/mellanox/mlx4/en_ethtool.c | 76 +++--
> drivers/net/ethernet/mellanox/mlx4/en_main.c | 2 +-
> drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 378 ++++++++++++++----------
> drivers/net/ethernet/mellanox/mlx4/en_port.c | 4 +-
> drivers/net/ethernet/mellanox/mlx4/en_rx.c | 8 +-
> drivers/net/ethernet/mellanox/mlx4/en_tx.c | 9 +-
> drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 18 +-
> 8 files changed, 305 insertions(+), 208 deletions(-)
>
^ 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