diff options
Diffstat (limited to 'drivers/net/ethernet/netronome')
44 files changed, 4418 insertions, 781 deletions
| diff --git a/drivers/net/ethernet/netronome/nfp/Makefile b/drivers/net/ethernet/netronome/nfp/Makefile index 24c4408b5734..d5866d708dfa 100644 --- a/drivers/net/ethernet/netronome/nfp/Makefile +++ b/drivers/net/ethernet/netronome/nfp/Makefile @@ -22,6 +22,8 @@ nfp-objs := \  	    nfp_hwmon.o \  	    nfp_main.o \  	    nfp_net_common.o \ +	    nfp_net_ctrl.o \ +	    nfp_net_debugdump.o \  	    nfp_net_ethtool.o \  	    nfp_net_main.o \  	    nfp_net_repr.o \ @@ -43,6 +45,7 @@ endif  ifeq ($(CONFIG_BPF_SYSCALL),y)  nfp-objs += \ +	    bpf/cmsg.o \  	    bpf/main.o \  	    bpf/offload.o \  	    bpf/verifier.o \ diff --git a/drivers/net/ethernet/netronome/nfp/bpf/cmsg.c b/drivers/net/ethernet/netronome/nfp/bpf/cmsg.c new file mode 100644 index 000000000000..80d3aa0fc9d3 --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/bpf/cmsg.c @@ -0,0 +1,453 @@ +/* + * Copyright (C) 2017 Netronome Systems, Inc. + * + * This software is dual licensed under the GNU General License Version 2, + * June 1991 as shown in the file COPYING in the top-level directory of this + * source tree or the BSD 2-Clause License provided below.  You have the + * option to license this software under the complete terms of either license. + * + * The BSD 2-Clause License: + * + *     Redistribution and use in source and binary forms, with or + *     without modification, are permitted provided that the following + *     conditions are met: + * + *      1. Redistributions of source code must retain the above + *         copyright notice, this list of conditions and the following + *         disclaimer. + * + *      2. Redistributions in binary form must reproduce the above + *         copyright notice, this list of conditions and the following + *         disclaimer in the documentation and/or other materials + *         provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/bpf.h> +#include <linux/bitops.h> +#include <linux/bug.h> +#include <linux/jiffies.h> +#include <linux/skbuff.h> +#include <linux/wait.h> + +#include "../nfp_app.h" +#include "../nfp_net.h" +#include "fw.h" +#include "main.h" + +#define cmsg_warn(bpf, msg...)	nn_dp_warn(&(bpf)->app->ctrl->dp, msg) + +#define NFP_BPF_TAG_ALLOC_SPAN	(U16_MAX / 4) + +static bool nfp_bpf_all_tags_busy(struct nfp_app_bpf *bpf) +{ +	u16 used_tags; + +	used_tags = bpf->tag_alloc_next - bpf->tag_alloc_last; + +	return used_tags > NFP_BPF_TAG_ALLOC_SPAN; +} + +static int nfp_bpf_alloc_tag(struct nfp_app_bpf *bpf) +{ +	/* All FW communication for BPF is request-reply.  To make sure we +	 * don't reuse the message ID too early after timeout - limit the +	 * number of requests in flight. +	 */ +	if (nfp_bpf_all_tags_busy(bpf)) { +		cmsg_warn(bpf, "all FW request contexts busy!\n"); +		return -EAGAIN; +	} + +	WARN_ON(__test_and_set_bit(bpf->tag_alloc_next, bpf->tag_allocator)); +	return bpf->tag_alloc_next++; +} + +static void nfp_bpf_free_tag(struct nfp_app_bpf *bpf, u16 tag) +{ +	WARN_ON(!__test_and_clear_bit(tag, bpf->tag_allocator)); + +	while (!test_bit(bpf->tag_alloc_last, bpf->tag_allocator) && +	       bpf->tag_alloc_last != bpf->tag_alloc_next) +		bpf->tag_alloc_last++; +} + +static struct sk_buff * +nfp_bpf_cmsg_alloc(struct nfp_app_bpf *bpf, unsigned int size) +{ +	struct sk_buff *skb; + +	skb = nfp_app_ctrl_msg_alloc(bpf->app, size, GFP_KERNEL); +	skb_put(skb, size); + +	return skb; +} + +static struct sk_buff * +nfp_bpf_cmsg_map_req_alloc(struct nfp_app_bpf *bpf, unsigned int n) +{ +	unsigned int size; + +	size = sizeof(struct cmsg_req_map_op); +	size += sizeof(struct cmsg_key_value_pair) * n; + +	return nfp_bpf_cmsg_alloc(bpf, size); +} + +static unsigned int nfp_bpf_cmsg_get_tag(struct sk_buff *skb) +{ +	struct cmsg_hdr *hdr; + +	hdr = (struct cmsg_hdr *)skb->data; + +	return be16_to_cpu(hdr->tag); +} + +static struct sk_buff *__nfp_bpf_reply(struct nfp_app_bpf *bpf, u16 tag) +{ +	unsigned int msg_tag; +	struct sk_buff *skb; + +	skb_queue_walk(&bpf->cmsg_replies, skb) { +		msg_tag = nfp_bpf_cmsg_get_tag(skb); +		if (msg_tag == tag) { +			nfp_bpf_free_tag(bpf, tag); +			__skb_unlink(skb, &bpf->cmsg_replies); +			return skb; +		} +	} + +	return NULL; +} + +static struct sk_buff *nfp_bpf_reply(struct nfp_app_bpf *bpf, u16 tag) +{ +	struct sk_buff *skb; + +	nfp_ctrl_lock(bpf->app->ctrl); +	skb = __nfp_bpf_reply(bpf, tag); +	nfp_ctrl_unlock(bpf->app->ctrl); + +	return skb; +} + +static struct sk_buff *nfp_bpf_reply_drop_tag(struct nfp_app_bpf *bpf, u16 tag) +{ +	struct sk_buff *skb; + +	nfp_ctrl_lock(bpf->app->ctrl); +	skb = __nfp_bpf_reply(bpf, tag); +	if (!skb) +		nfp_bpf_free_tag(bpf, tag); +	nfp_ctrl_unlock(bpf->app->ctrl); + +	return skb; +} + +static struct sk_buff * +nfp_bpf_cmsg_wait_reply(struct nfp_app_bpf *bpf, enum nfp_bpf_cmsg_type type, +			int tag) +{ +	struct sk_buff *skb; +	int i, err; + +	for (i = 0; i < 50; i++) { +		udelay(4); +		skb = nfp_bpf_reply(bpf, tag); +		if (skb) +			return skb; +	} + +	err = wait_event_interruptible_timeout(bpf->cmsg_wq, +					       skb = nfp_bpf_reply(bpf, tag), +					       msecs_to_jiffies(5000)); +	/* We didn't get a response - try last time and atomically drop +	 * the tag even if no response is matched. +	 */ +	if (!skb) +		skb = nfp_bpf_reply_drop_tag(bpf, tag); +	if (err < 0) { +		cmsg_warn(bpf, "%s waiting for response to 0x%02x: %d\n", +			  err == ERESTARTSYS ? "interrupted" : "error", +			  type, err); +		return ERR_PTR(err); +	} +	if (!skb) { +		cmsg_warn(bpf, "timeout waiting for response to 0x%02x\n", +			  type); +		return ERR_PTR(-ETIMEDOUT); +	} + +	return skb; +} + +static struct sk_buff * +nfp_bpf_cmsg_communicate(struct nfp_app_bpf *bpf, struct sk_buff *skb, +			 enum nfp_bpf_cmsg_type type, unsigned int reply_size) +{ +	struct cmsg_hdr *hdr; +	int tag; + +	nfp_ctrl_lock(bpf->app->ctrl); +	tag = nfp_bpf_alloc_tag(bpf); +	if (tag < 0) { +		nfp_ctrl_unlock(bpf->app->ctrl); +		dev_kfree_skb_any(skb); +		return ERR_PTR(tag); +	} + +	hdr = (void *)skb->data; +	hdr->ver = CMSG_MAP_ABI_VERSION; +	hdr->type = type; +	hdr->tag = cpu_to_be16(tag); + +	__nfp_app_ctrl_tx(bpf->app, skb); + +	nfp_ctrl_unlock(bpf->app->ctrl); + +	skb = nfp_bpf_cmsg_wait_reply(bpf, type, tag); +	if (IS_ERR(skb)) +		return skb; + +	hdr = (struct cmsg_hdr *)skb->data; +	/* 0 reply_size means caller will do the validation */ +	if (reply_size && skb->len != reply_size) { +		cmsg_warn(bpf, "cmsg drop - wrong size %d != %d!\n", +			  skb->len, reply_size); +		goto err_free; +	} +	if (hdr->type != __CMSG_REPLY(type)) { +		cmsg_warn(bpf, "cmsg drop - wrong type 0x%02x != 0x%02lx!\n", +			  hdr->type, __CMSG_REPLY(type)); +		goto err_free; +	} + +	return skb; +err_free: +	dev_kfree_skb_any(skb); +	return ERR_PTR(-EIO); +} + +static int +nfp_bpf_ctrl_rc_to_errno(struct nfp_app_bpf *bpf, +			 struct cmsg_reply_map_simple *reply) +{ +	static const int res_table[] = { +		[CMSG_RC_SUCCESS]	= 0, +		[CMSG_RC_ERR_MAP_FD]	= -EBADFD, +		[CMSG_RC_ERR_MAP_NOENT]	= -ENOENT, +		[CMSG_RC_ERR_MAP_ERR]	= -EINVAL, +		[CMSG_RC_ERR_MAP_PARSE]	= -EIO, +		[CMSG_RC_ERR_MAP_EXIST]	= -EEXIST, +		[CMSG_RC_ERR_MAP_NOMEM]	= -ENOMEM, +		[CMSG_RC_ERR_MAP_E2BIG]	= -E2BIG, +	}; +	u32 rc; + +	rc = be32_to_cpu(reply->rc); +	if (rc >= ARRAY_SIZE(res_table)) { +		cmsg_warn(bpf, "FW responded with invalid status: %u\n", rc); +		return -EIO; +	} + +	return res_table[rc]; +} + +long long int +nfp_bpf_ctrl_alloc_map(struct nfp_app_bpf *bpf, struct bpf_map *map) +{ +	struct cmsg_reply_map_alloc_tbl *reply; +	struct cmsg_req_map_alloc_tbl *req; +	struct sk_buff *skb; +	u32 tid; +	int err; + +	skb = nfp_bpf_cmsg_alloc(bpf, sizeof(*req)); +	if (!skb) +		return -ENOMEM; + +	req = (void *)skb->data; +	req->key_size = cpu_to_be32(map->key_size); +	req->value_size = cpu_to_be32(map->value_size); +	req->max_entries = cpu_to_be32(map->max_entries); +	req->map_type = cpu_to_be32(map->map_type); +	req->map_flags = 0; + +	skb = nfp_bpf_cmsg_communicate(bpf, skb, CMSG_TYPE_MAP_ALLOC, +				       sizeof(*reply)); +	if (IS_ERR(skb)) +		return PTR_ERR(skb); + +	reply = (void *)skb->data; +	err = nfp_bpf_ctrl_rc_to_errno(bpf, &reply->reply_hdr); +	if (err) +		goto err_free; + +	tid = be32_to_cpu(reply->tid); +	dev_consume_skb_any(skb); + +	return tid; +err_free: +	dev_kfree_skb_any(skb); +	return err; +} + +void nfp_bpf_ctrl_free_map(struct nfp_app_bpf *bpf, struct nfp_bpf_map *nfp_map) +{ +	struct cmsg_reply_map_free_tbl *reply; +	struct cmsg_req_map_free_tbl *req; +	struct sk_buff *skb; +	int err; + +	skb = nfp_bpf_cmsg_alloc(bpf, sizeof(*req)); +	if (!skb) { +		cmsg_warn(bpf, "leaking map - failed to allocate msg\n"); +		return; +	} + +	req = (void *)skb->data; +	req->tid = cpu_to_be32(nfp_map->tid); + +	skb = nfp_bpf_cmsg_communicate(bpf, skb, CMSG_TYPE_MAP_FREE, +				       sizeof(*reply)); +	if (IS_ERR(skb)) { +		cmsg_warn(bpf, "leaking map - I/O error\n"); +		return; +	} + +	reply = (void *)skb->data; +	err = nfp_bpf_ctrl_rc_to_errno(bpf, &reply->reply_hdr); +	if (err) +		cmsg_warn(bpf, "leaking map - FW responded with: %d\n", err); + +	dev_consume_skb_any(skb); +} + +static int +nfp_bpf_ctrl_entry_op(struct bpf_offloaded_map *offmap, +		      enum nfp_bpf_cmsg_type op, +		      u8 *key, u8 *value, u64 flags, u8 *out_key, u8 *out_value) +{ +	struct nfp_bpf_map *nfp_map = offmap->dev_priv; +	struct nfp_app_bpf *bpf = nfp_map->bpf; +	struct bpf_map *map = &offmap->map; +	struct cmsg_reply_map_op *reply; +	struct cmsg_req_map_op *req; +	struct sk_buff *skb; +	int err; + +	/* FW messages have no space for more than 32 bits of flags */ +	if (flags >> 32) +		return -EOPNOTSUPP; + +	skb = nfp_bpf_cmsg_map_req_alloc(bpf, 1); +	if (!skb) +		return -ENOMEM; + +	req = (void *)skb->data; +	req->tid = cpu_to_be32(nfp_map->tid); +	req->count = cpu_to_be32(1); +	req->flags = cpu_to_be32(flags); + +	/* Copy inputs */ +	if (key) +		memcpy(&req->elem[0].key, key, map->key_size); +	if (value) +		memcpy(&req->elem[0].value, value, map->value_size); + +	skb = nfp_bpf_cmsg_communicate(bpf, skb, op, +				       sizeof(*reply) + sizeof(*reply->elem)); +	if (IS_ERR(skb)) +		return PTR_ERR(skb); + +	reply = (void *)skb->data; +	err = nfp_bpf_ctrl_rc_to_errno(bpf, &reply->reply_hdr); +	if (err) +		goto err_free; + +	/* Copy outputs */ +	if (out_key) +		memcpy(out_key, &reply->elem[0].key, map->key_size); +	if (out_value) +		memcpy(out_value, &reply->elem[0].value, map->value_size); + +	dev_consume_skb_any(skb); + +	return 0; +err_free: +	dev_kfree_skb_any(skb); +	return err; +} + +int nfp_bpf_ctrl_update_entry(struct bpf_offloaded_map *offmap, +			      void *key, void *value, u64 flags) +{ +	return nfp_bpf_ctrl_entry_op(offmap, CMSG_TYPE_MAP_UPDATE, +				     key, value, flags, NULL, NULL); +} + +int nfp_bpf_ctrl_del_entry(struct bpf_offloaded_map *offmap, void *key) +{ +	return nfp_bpf_ctrl_entry_op(offmap, CMSG_TYPE_MAP_DELETE, +				     key, NULL, 0, NULL, NULL); +} + +int nfp_bpf_ctrl_lookup_entry(struct bpf_offloaded_map *offmap, +			      void *key, void *value) +{ +	return nfp_bpf_ctrl_entry_op(offmap, CMSG_TYPE_MAP_LOOKUP, +				     key, NULL, 0, NULL, value); +} + +int nfp_bpf_ctrl_getfirst_entry(struct bpf_offloaded_map *offmap, +				void *next_key) +{ +	return nfp_bpf_ctrl_entry_op(offmap, CMSG_TYPE_MAP_GETFIRST, +				     NULL, NULL, 0, next_key, NULL); +} + +int nfp_bpf_ctrl_getnext_entry(struct bpf_offloaded_map *offmap, +			       void *key, void *next_key) +{ +	return nfp_bpf_ctrl_entry_op(offmap, CMSG_TYPE_MAP_GETNEXT, +				     key, NULL, 0, next_key, NULL); +} + +void nfp_bpf_ctrl_msg_rx(struct nfp_app *app, struct sk_buff *skb) +{ +	struct nfp_app_bpf *bpf = app->priv; +	unsigned int tag; + +	if (unlikely(skb->len < sizeof(struct cmsg_reply_map_simple))) { +		cmsg_warn(bpf, "cmsg drop - too short %d!\n", skb->len); +		goto err_free; +	} + +	nfp_ctrl_lock(bpf->app->ctrl); + +	tag = nfp_bpf_cmsg_get_tag(skb); +	if (unlikely(!test_bit(tag, bpf->tag_allocator))) { +		cmsg_warn(bpf, "cmsg drop - no one is waiting for tag %u!\n", +			  tag); +		goto err_unlock; +	} + +	__skb_queue_tail(&bpf->cmsg_replies, skb); +	wake_up_interruptible_all(&bpf->cmsg_wq); + +	nfp_ctrl_unlock(bpf->app->ctrl); + +	return; +err_unlock: +	nfp_ctrl_unlock(bpf->app->ctrl); +err_free: +	dev_kfree_skb_any(skb); +} diff --git a/drivers/net/ethernet/netronome/nfp/bpf/fw.h b/drivers/net/ethernet/netronome/nfp/bpf/fw.h new file mode 100644 index 000000000000..cfcc7bcb2c67 --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/bpf/fw.h @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2017 Netronome Systems, Inc. + * + * This software is dual licensed under the GNU General License Version 2, + * June 1991 as shown in the file COPYING in the top-level directory of this + * source tree or the BSD 2-Clause License provided below.  You have the + * option to license this software under the complete terms of either license. + * + * The BSD 2-Clause License: + * + *     Redistribution and use in source and binary forms, with or + *     without modification, are permitted provided that the following + *     conditions are met: + * + *      1. Redistributions of source code must retain the above + *         copyright notice, this list of conditions and the following + *         disclaimer. + * + *      2. Redistributions in binary form must reproduce the above + *         copyright notice, this list of conditions and the following + *         disclaimer in the documentation and/or other materials + *         provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef NFP_BPF_FW_H +#define NFP_BPF_FW_H 1 + +#include <linux/bitops.h> +#include <linux/types.h> + +enum bpf_cap_tlv_type { +	NFP_BPF_CAP_TYPE_FUNC		= 1, +	NFP_BPF_CAP_TYPE_ADJUST_HEAD	= 2, +	NFP_BPF_CAP_TYPE_MAPS		= 3, +}; + +struct nfp_bpf_cap_tlv_func { +	__le32 func_id; +	__le32 func_addr; +}; + +struct nfp_bpf_cap_tlv_adjust_head { +	__le32 flags; +	__le32 off_min; +	__le32 off_max; +	__le32 guaranteed_sub; +	__le32 guaranteed_add; +}; + +#define NFP_BPF_ADJUST_HEAD_NO_META	BIT(0) + +struct nfp_bpf_cap_tlv_maps { +	__le32 types; +	__le32 max_maps; +	__le32 max_elems; +	__le32 max_key_sz; +	__le32 max_val_sz; +	__le32 max_elem_sz; +}; + +/* + * Types defined for map related control messages + */ +#define CMSG_MAP_ABI_VERSION		1 + +enum nfp_bpf_cmsg_type { +	CMSG_TYPE_MAP_ALLOC	= 1, +	CMSG_TYPE_MAP_FREE	= 2, +	CMSG_TYPE_MAP_LOOKUP	= 3, +	CMSG_TYPE_MAP_UPDATE	= 4, +	CMSG_TYPE_MAP_DELETE	= 5, +	CMSG_TYPE_MAP_GETNEXT	= 6, +	CMSG_TYPE_MAP_GETFIRST	= 7, +	__CMSG_TYPE_MAP_MAX, +}; + +#define CMSG_TYPE_MAP_REPLY_BIT		7 +#define __CMSG_REPLY(req)		(BIT(CMSG_TYPE_MAP_REPLY_BIT) | (req)) + +#define CMSG_MAP_KEY_LW			16 +#define CMSG_MAP_VALUE_LW		16 + +enum nfp_bpf_cmsg_status { +	CMSG_RC_SUCCESS			= 0, +	CMSG_RC_ERR_MAP_FD		= 1, +	CMSG_RC_ERR_MAP_NOENT		= 2, +	CMSG_RC_ERR_MAP_ERR		= 3, +	CMSG_RC_ERR_MAP_PARSE		= 4, +	CMSG_RC_ERR_MAP_EXIST		= 5, +	CMSG_RC_ERR_MAP_NOMEM		= 6, +	CMSG_RC_ERR_MAP_E2BIG		= 7, +}; + +struct cmsg_hdr { +	u8 type; +	u8 ver; +	__be16 tag; +}; + +struct cmsg_reply_map_simple { +	struct cmsg_hdr hdr; +	__be32 rc; +}; + +struct cmsg_req_map_alloc_tbl { +	struct cmsg_hdr hdr; +	__be32 key_size;		/* in bytes */ +	__be32 value_size;		/* in bytes */ +	__be32 max_entries; +	__be32 map_type; +	__be32 map_flags;		/* reserved */ +}; + +struct cmsg_reply_map_alloc_tbl { +	struct cmsg_reply_map_simple reply_hdr; +	__be32 tid; +}; + +struct cmsg_req_map_free_tbl { +	struct cmsg_hdr hdr; +	__be32 tid; +}; + +struct cmsg_reply_map_free_tbl { +	struct cmsg_reply_map_simple reply_hdr; +	__be32 count; +}; + +struct cmsg_key_value_pair { +	__be32 key[CMSG_MAP_KEY_LW]; +	__be32 value[CMSG_MAP_VALUE_LW]; +}; + +struct cmsg_req_map_op { +	struct cmsg_hdr hdr; +	__be32 tid; +	__be32 count; +	__be32 flags; +	struct cmsg_key_value_pair elem[0]; +}; + +struct cmsg_reply_map_op { +	struct cmsg_reply_map_simple reply_hdr; +	__be32 count; +	__be32 resv; +	struct cmsg_key_value_pair elem[0]; +}; +#endif diff --git a/drivers/net/ethernet/netronome/nfp/bpf/jit.c b/drivers/net/ethernet/netronome/nfp/bpf/jit.c index 995e95410b11..56451edf01c2 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/jit.c +++ b/drivers/net/ethernet/netronome/nfp/bpf/jit.c @@ -1,5 +1,5 @@  /* - * Copyright (C) 2016 Netronome Systems, Inc. + * Copyright (C) 2016-2017 Netronome Systems, Inc.   *   * This software is dual licensed under the GNU General License Version 2,   * June 1991 as shown in the file COPYING in the top-level directory of this @@ -33,6 +33,7 @@  #define pr_fmt(fmt)	"NFP net bpf: " fmt +#include <linux/bug.h>  #include <linux/kernel.h>  #include <linux/bpf.h>  #include <linux/filter.h> @@ -66,12 +67,6 @@  	     next2 = nfp_meta_next(next))  static bool -nfp_meta_has_next(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) -{ -	return meta->l.next != &nfp_prog->insns; -} - -static bool  nfp_meta_has_prev(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)  {  	return meta->l.prev != &nfp_prog->insns; @@ -90,19 +85,25 @@ static void nfp_prog_push(struct nfp_prog *nfp_prog, u64 insn)  static unsigned int nfp_prog_current_offset(struct nfp_prog *nfp_prog)  { -	return nfp_prog->start_off + nfp_prog->prog_len; +	return nfp_prog->prog_len;  } -static unsigned int -nfp_prog_offset_to_index(struct nfp_prog *nfp_prog, unsigned int offset) +static bool +nfp_prog_confirm_current_offset(struct nfp_prog *nfp_prog, unsigned int off)  { -	return offset - nfp_prog->start_off; +	/* If there is a recorded error we may have dropped instructions; +	 * that doesn't have to be due to translator bug, and the translation +	 * will fail anyway, so just return OK. +	 */ +	if (nfp_prog->error) +		return true; +	return !WARN_ON_ONCE(nfp_prog_current_offset(nfp_prog) != off);  }  /* --- Emitters --- */  static void  __emit_cmd(struct nfp_prog *nfp_prog, enum cmd_tgt_map op, -	   u8 mode, u8 xfer, u8 areg, u8 breg, u8 size, bool sync) +	   u8 mode, u8 xfer, u8 areg, u8 breg, u8 size, bool sync, bool indir)  {  	enum cmd_ctx_swap ctx;  	u64 insn; @@ -120,14 +121,15 @@ __emit_cmd(struct nfp_prog *nfp_prog, enum cmd_tgt_map op,  		FIELD_PREP(OP_CMD_CNT, size) |  		FIELD_PREP(OP_CMD_SIG, sync) |  		FIELD_PREP(OP_CMD_TGT_CMD, cmd_tgt_act[op].tgt_cmd) | +		FIELD_PREP(OP_CMD_INDIR, indir) |  		FIELD_PREP(OP_CMD_MODE, mode);  	nfp_prog_push(nfp_prog, insn);  }  static void -emit_cmd(struct nfp_prog *nfp_prog, enum cmd_tgt_map op, -	 u8 mode, u8 xfer, swreg lreg, swreg rreg, u8 size, bool sync) +emit_cmd_any(struct nfp_prog *nfp_prog, enum cmd_tgt_map op, u8 mode, u8 xfer, +	     swreg lreg, swreg rreg, u8 size, bool sync, bool indir)  {  	struct nfp_insn_re_regs reg;  	int err; @@ -148,7 +150,22 @@ emit_cmd(struct nfp_prog *nfp_prog, enum cmd_tgt_map op,  		return;  	} -	__emit_cmd(nfp_prog, op, mode, xfer, reg.areg, reg.breg, size, sync); +	__emit_cmd(nfp_prog, op, mode, xfer, reg.areg, reg.breg, size, sync, +		   indir); +} + +static void +emit_cmd(struct nfp_prog *nfp_prog, enum cmd_tgt_map op, u8 mode, u8 xfer, +	 swreg lreg, swreg rreg, u8 size, bool sync) +{ +	emit_cmd_any(nfp_prog, op, mode, xfer, lreg, rreg, size, sync, false); +} + +static void +emit_cmd_indir(struct nfp_prog *nfp_prog, enum cmd_tgt_map op, u8 mode, u8 xfer, +	       swreg lreg, swreg rreg, u8 size, bool sync) +{ +	emit_cmd_any(nfp_prog, op, mode, xfer, lreg, rreg, size, sync, true);  }  static void @@ -172,22 +189,28 @@ __emit_br(struct nfp_prog *nfp_prog, enum br_mask mask, enum br_ev_pip ev_pip,  	nfp_prog_push(nfp_prog, insn);  } -static void emit_br_def(struct nfp_prog *nfp_prog, u16 addr, u8 defer) +static void +emit_br_relo(struct nfp_prog *nfp_prog, enum br_mask mask, u16 addr, u8 defer, +	     enum nfp_relo_type relo)  { -	if (defer > 2) { +	if (mask == BR_UNC && defer > 2) {  		pr_err("BUG: branch defer out of bounds %d\n", defer);  		nfp_prog->error = -EFAULT;  		return;  	} -	__emit_br(nfp_prog, BR_UNC, BR_EV_PIP_UNCOND, BR_CSS_NONE, addr, defer); + +	__emit_br(nfp_prog, mask, +		  mask != BR_UNC ? BR_EV_PIP_COND : BR_EV_PIP_UNCOND, +		  BR_CSS_NONE, addr, defer); + +	nfp_prog->prog[nfp_prog->prog_len - 1] |= +		FIELD_PREP(OP_RELO_TYPE, relo);  }  static void  emit_br(struct nfp_prog *nfp_prog, enum br_mask mask, u16 addr, u8 defer)  { -	__emit_br(nfp_prog, mask, -		  mask != BR_UNC ? BR_EV_PIP_COND : BR_EV_PIP_UNCOND, -		  BR_CSS_NONE, addr, defer); +	emit_br_relo(nfp_prog, mask, addr, defer, RELO_BR_REL);  }  static void @@ -230,9 +253,11 @@ emit_immed(struct nfp_prog *nfp_prog, swreg dst, u16 imm,  		return;  	} -	__emit_immed(nfp_prog, reg.areg, reg.breg, imm >> 8, width, -		     invert, shift, reg.wr_both, -		     reg.dst_lmextn, reg.src_lmextn); +	/* Use reg.dst when destination is No-Dest. */ +	__emit_immed(nfp_prog, +		     swreg_type(dst) == NN_REG_NONE ? reg.dst : reg.areg, +		     reg.breg, imm >> 8, width, invert, shift, +		     reg.wr_both, reg.dst_lmextn, reg.src_lmextn);  }  static void @@ -458,6 +483,21 @@ static void wrp_immed(struct nfp_prog *nfp_prog, swreg dst, u32 imm)  	}  } +static void +wrp_immed_relo(struct nfp_prog *nfp_prog, swreg dst, u32 imm, +	       enum nfp_relo_type relo) +{ +	if (imm > 0xffff) { +		pr_err("relocation of a large immediate!\n"); +		nfp_prog->error = -EFAULT; +		return; +	} +	emit_immed(nfp_prog, dst, imm, IMMED_WIDTH_ALL, false, IMMED_SHIFT_0B); + +	nfp_prog->prog[nfp_prog->prog_len - 1] |= +		FIELD_PREP(OP_RELO_TYPE, relo); +} +  /* ur_load_imm_any() - encode immediate or use tmp register (unrestricted)   * If the @imm is small enough encode it directly in operand and return   * otherwise load @imm to a spare register and return its encoding. @@ -490,24 +530,179 @@ static void wrp_nops(struct nfp_prog *nfp_prog, unsigned int count)  		emit_nop(nfp_prog);  } +static void wrp_mov(struct nfp_prog *nfp_prog, swreg dst, swreg src) +{ +	emit_alu(nfp_prog, dst, reg_none(), ALU_OP_NONE, src); +} + +static void wrp_reg_mov(struct nfp_prog *nfp_prog, u16 dst, u16 src) +{ +	wrp_mov(nfp_prog, reg_both(dst), reg_b(src)); +} + +/* wrp_reg_subpart() - load @field_len bytes from @offset of @src, write the + * result to @dst from low end. + */  static void -wrp_br_special(struct nfp_prog *nfp_prog, enum br_mask mask, -	       enum br_special special) +wrp_reg_subpart(struct nfp_prog *nfp_prog, swreg dst, swreg src, u8 field_len, +		u8 offset)  { -	emit_br(nfp_prog, mask, 0, 0); +	enum shf_sc sc = offset ? SHF_SC_R_SHF : SHF_SC_NONE; +	u8 mask = (1 << field_len) - 1; -	nfp_prog->prog[nfp_prog->prog_len - 1] |= -		FIELD_PREP(OP_BR_SPECIAL, special); +	emit_ld_field_any(nfp_prog, dst, mask, src, sc, offset * 8, true);  } -static void wrp_mov(struct nfp_prog *nfp_prog, swreg dst, swreg src) +static void +addr40_offset(struct nfp_prog *nfp_prog, u8 src_gpr, swreg offset, +	      swreg *rega, swreg *regb)  { -	emit_alu(nfp_prog, dst, reg_none(), ALU_OP_NONE, src); +	if (offset == reg_imm(0)) { +		*rega = reg_a(src_gpr); +		*regb = reg_b(src_gpr + 1); +		return; +	} + +	emit_alu(nfp_prog, imm_a(nfp_prog), reg_a(src_gpr), ALU_OP_ADD, offset); +	emit_alu(nfp_prog, imm_b(nfp_prog), reg_b(src_gpr + 1), ALU_OP_ADD_C, +		 reg_imm(0)); +	*rega = imm_a(nfp_prog); +	*regb = imm_b(nfp_prog);  } -static void wrp_reg_mov(struct nfp_prog *nfp_prog, u16 dst, u16 src) +/* NFP has Command Push Pull bus which supports bluk memory operations. */ +static int nfp_cpp_memcpy(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)  { -	wrp_mov(nfp_prog, reg_both(dst), reg_b(src)); +	bool descending_seq = meta->ldst_gather_len < 0; +	s16 len = abs(meta->ldst_gather_len); +	swreg src_base, off; +	bool src_40bit_addr; +	unsigned int i; +	u8 xfer_num; + +	off = re_load_imm_any(nfp_prog, meta->insn.off, imm_b(nfp_prog)); +	src_40bit_addr = meta->ptr.type == PTR_TO_MAP_VALUE; +	src_base = reg_a(meta->insn.src_reg * 2); +	xfer_num = round_up(len, 4) / 4; + +	if (src_40bit_addr) +		addr40_offset(nfp_prog, meta->insn.src_reg, off, &src_base, +			      &off); + +	/* Setup PREV_ALU fields to override memory read length. */ +	if (len > 32) +		wrp_immed(nfp_prog, reg_none(), +			  CMD_OVE_LEN | FIELD_PREP(CMD_OV_LEN, xfer_num - 1)); + +	/* Memory read from source addr into transfer-in registers. */ +	emit_cmd_any(nfp_prog, CMD_TGT_READ32_SWAP, +		     src_40bit_addr ? CMD_MODE_40b_BA : CMD_MODE_32b, 0, +		     src_base, off, xfer_num - 1, true, len > 32); + +	/* Move from transfer-in to transfer-out. */ +	for (i = 0; i < xfer_num; i++) +		wrp_mov(nfp_prog, reg_xfer(i), reg_xfer(i)); + +	off = re_load_imm_any(nfp_prog, meta->paired_st->off, imm_b(nfp_prog)); + +	if (len <= 8) { +		/* Use single direct_ref write8. */ +		emit_cmd(nfp_prog, CMD_TGT_WRITE8_SWAP, CMD_MODE_32b, 0, +			 reg_a(meta->paired_st->dst_reg * 2), off, len - 1, +			 true); +	} else if (len <= 32 && IS_ALIGNED(len, 4)) { +		/* Use single direct_ref write32. */ +		emit_cmd(nfp_prog, CMD_TGT_WRITE32_SWAP, CMD_MODE_32b, 0, +			 reg_a(meta->paired_st->dst_reg * 2), off, xfer_num - 1, +			 true); +	} else if (len <= 32) { +		/* Use single indirect_ref write8. */ +		wrp_immed(nfp_prog, reg_none(), +			  CMD_OVE_LEN | FIELD_PREP(CMD_OV_LEN, len - 1)); +		emit_cmd_indir(nfp_prog, CMD_TGT_WRITE8_SWAP, CMD_MODE_32b, 0, +			       reg_a(meta->paired_st->dst_reg * 2), off, +			       len - 1, true); +	} else if (IS_ALIGNED(len, 4)) { +		/* Use single indirect_ref write32. */ +		wrp_immed(nfp_prog, reg_none(), +			  CMD_OVE_LEN | FIELD_PREP(CMD_OV_LEN, xfer_num - 1)); +		emit_cmd_indir(nfp_prog, CMD_TGT_WRITE32_SWAP, CMD_MODE_32b, 0, +			       reg_a(meta->paired_st->dst_reg * 2), off, +			       xfer_num - 1, true); +	} else if (len <= 40) { +		/* Use one direct_ref write32 to write the first 32-bytes, then +		 * another direct_ref write8 to write the remaining bytes. +		 */ +		emit_cmd(nfp_prog, CMD_TGT_WRITE32_SWAP, CMD_MODE_32b, 0, +			 reg_a(meta->paired_st->dst_reg * 2), off, 7, +			 true); + +		off = re_load_imm_any(nfp_prog, meta->paired_st->off + 32, +				      imm_b(nfp_prog)); +		emit_cmd(nfp_prog, CMD_TGT_WRITE8_SWAP, CMD_MODE_32b, 8, +			 reg_a(meta->paired_st->dst_reg * 2), off, len - 33, +			 true); +	} else { +		/* Use one indirect_ref write32 to write 4-bytes aligned length, +		 * then another direct_ref write8 to write the remaining bytes. +		 */ +		u8 new_off; + +		wrp_immed(nfp_prog, reg_none(), +			  CMD_OVE_LEN | FIELD_PREP(CMD_OV_LEN, xfer_num - 2)); +		emit_cmd_indir(nfp_prog, CMD_TGT_WRITE32_SWAP, CMD_MODE_32b, 0, +			       reg_a(meta->paired_st->dst_reg * 2), off, +			       xfer_num - 2, true); +		new_off = meta->paired_st->off + (xfer_num - 1) * 4; +		off = re_load_imm_any(nfp_prog, new_off, imm_b(nfp_prog)); +		emit_cmd(nfp_prog, CMD_TGT_WRITE8_SWAP, CMD_MODE_32b, +			 xfer_num - 1, reg_a(meta->paired_st->dst_reg * 2), off, +			 (len & 0x3) - 1, true); +	} + +	/* TODO: The following extra load is to make sure data flow be identical +	 *  before and after we do memory copy optimization. +	 * +	 *  The load destination register is not guaranteed to be dead, so we +	 *  need to make sure it is loaded with the value the same as before +	 *  this transformation. +	 * +	 *  These extra loads could be removed once we have accurate register +	 *  usage information. +	 */ +	if (descending_seq) +		xfer_num = 0; +	else if (BPF_SIZE(meta->insn.code) != BPF_DW) +		xfer_num = xfer_num - 1; +	else +		xfer_num = xfer_num - 2; + +	switch (BPF_SIZE(meta->insn.code)) { +	case BPF_B: +		wrp_reg_subpart(nfp_prog, reg_both(meta->insn.dst_reg * 2), +				reg_xfer(xfer_num), 1, +				IS_ALIGNED(len, 4) ? 3 : (len & 3) - 1); +		break; +	case BPF_H: +		wrp_reg_subpart(nfp_prog, reg_both(meta->insn.dst_reg * 2), +				reg_xfer(xfer_num), 2, (len & 3) ^ 2); +		break; +	case BPF_W: +		wrp_mov(nfp_prog, reg_both(meta->insn.dst_reg * 2), +			reg_xfer(0)); +		break; +	case BPF_DW: +		wrp_mov(nfp_prog, reg_both(meta->insn.dst_reg * 2), +			reg_xfer(xfer_num)); +		wrp_mov(nfp_prog, reg_both(meta->insn.dst_reg * 2 + 1), +			reg_xfer(xfer_num + 1)); +		break; +	} + +	if (BPF_SIZE(meta->insn.code) != BPF_DW) +		wrp_immed(nfp_prog, reg_both(meta->insn.dst_reg * 2 + 1), 0); + +	return 0;  }  static int @@ -540,20 +735,20 @@ data_ld(struct nfp_prog *nfp_prog, swreg offset, u8 dst_gpr, int size)  }  static int -data_ld_host_order(struct nfp_prog *nfp_prog, u8 src_gpr, swreg offset, -		   u8 dst_gpr, int size) +data_ld_host_order(struct nfp_prog *nfp_prog, u8 dst_gpr, +		   swreg lreg, swreg rreg, int size, enum cmd_mode mode)  {  	unsigned int i;  	u8 mask, sz; -	/* We load the value from the address indicated in @offset and then +	/* We load the value from the address indicated in rreg + lreg and then  	 * mask out the data we don't need.  Note: this is little endian!  	 */  	sz = max(size, 4);  	mask = size < 4 ? GENMASK(size - 1, 0) : 0; -	emit_cmd(nfp_prog, CMD_TGT_READ32_SWAP, CMD_MODE_32b, 0, -		 reg_a(src_gpr), offset, sz / 4 - 1, true); +	emit_cmd(nfp_prog, CMD_TGT_READ32_SWAP, mode, 0, +		 lreg, rreg, sz / 4 - 1, true);  	i = 0;  	if (mask) @@ -570,6 +765,26 @@ data_ld_host_order(struct nfp_prog *nfp_prog, u8 src_gpr, swreg offset,  }  static int +data_ld_host_order_addr32(struct nfp_prog *nfp_prog, u8 src_gpr, swreg offset, +			  u8 dst_gpr, u8 size) +{ +	return data_ld_host_order(nfp_prog, dst_gpr, reg_a(src_gpr), offset, +				  size, CMD_MODE_32b); +} + +static int +data_ld_host_order_addr40(struct nfp_prog *nfp_prog, u8 src_gpr, swreg offset, +			  u8 dst_gpr, u8 size) +{ +	swreg rega, regb; + +	addr40_offset(nfp_prog, src_gpr, offset, ®a, ®b); + +	return data_ld_host_order(nfp_prog, dst_gpr, rega, regb, +				  size, CMD_MODE_40b_BA); +} + +static int  construct_data_ind_ld(struct nfp_prog *nfp_prog, u16 offset, u16 src, u8 size)  {  	swreg tmp_reg; @@ -583,7 +798,7 @@ construct_data_ind_ld(struct nfp_prog *nfp_prog, u16 offset, u16 src, u8 size)  		 imm_a(nfp_prog), ALU_OP_ADD, reg_imm(size));  	emit_alu(nfp_prog, reg_none(),  		 plen_reg(nfp_prog), ALU_OP_SUB, imm_a(nfp_prog)); -	wrp_br_special(nfp_prog, BR_BLO, OP_BR_GO_ABORT); +	emit_br_relo(nfp_prog, BR_BLO, BR_OFF_RELO, 0, RELO_BR_GO_ABORT);  	/* Load data */  	return data_ld(nfp_prog, imm_b(nfp_prog), 0, size); @@ -596,7 +811,7 @@ static int construct_data_ld(struct nfp_prog *nfp_prog, u16 offset, u8 size)  	/* Check packet length */  	tmp_reg = ur_load_imm_any(nfp_prog, offset + size, imm_a(nfp_prog));  	emit_alu(nfp_prog, reg_none(), plen_reg(nfp_prog), ALU_OP_SUB, tmp_reg); -	wrp_br_special(nfp_prog, BR_BLO, OP_BR_GO_ABORT); +	emit_br_relo(nfp_prog, BR_BLO, BR_OFF_RELO, 0, RELO_BR_GO_ABORT);  	/* Load data */  	tmp_reg = re_load_imm_any(nfp_prog, offset, imm_b(nfp_prog)); @@ -975,9 +1190,6 @@ wrp_test_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta,  {  	const struct bpf_insn *insn = &meta->insn; -	if (insn->off < 0) /* TODO */ -		return -EOPNOTSUPP; -  	wrp_test_reg_one(nfp_prog, insn->dst_reg * 2, alu_op,  			 insn->src_reg * 2, br_mask, insn->off);  	wrp_test_reg_one(nfp_prog, insn->dst_reg * 2 + 1, alu_op, @@ -995,9 +1207,6 @@ wrp_cmp_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta,  	u8 reg = insn->dst_reg * 2;  	swreg tmp_reg; -	if (insn->off < 0) /* TODO */ -		return -EOPNOTSUPP; -  	tmp_reg = ur_load_imm_any(nfp_prog, imm & ~0U, imm_b(nfp_prog));  	if (!swap)  		emit_alu(nfp_prog, reg_none(), reg_a(reg), ALU_OP_SUB, tmp_reg); @@ -1027,9 +1236,6 @@ wrp_cmp_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta,  	areg = insn->dst_reg * 2;  	breg = insn->src_reg * 2; -	if (insn->off < 0) /* TODO */ -		return -EOPNOTSUPP; -  	if (swap) {  		areg ^= breg;  		breg ^= areg; @@ -1052,6 +1258,136 @@ static void wrp_end32(struct nfp_prog *nfp_prog, swreg reg_in, u8 gpr_out)  		      SHF_SC_R_ROT, 16);  } +static int adjust_head(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) +{ +	swreg tmp = imm_a(nfp_prog), tmp_len = imm_b(nfp_prog); +	struct nfp_bpf_cap_adjust_head *adjust_head; +	u32 ret_einval, end; + +	adjust_head = &nfp_prog->bpf->adjust_head; + +	/* Optimized version - 5 vs 14 cycles */ +	if (nfp_prog->adjust_head_location != UINT_MAX) { +		if (WARN_ON_ONCE(nfp_prog->adjust_head_location != meta->n)) +			return -EINVAL; + +		emit_alu(nfp_prog, pptr_reg(nfp_prog), +			 reg_a(2 * 2), ALU_OP_ADD, pptr_reg(nfp_prog)); +		emit_alu(nfp_prog, plen_reg(nfp_prog), +			 plen_reg(nfp_prog), ALU_OP_SUB, reg_a(2 * 2)); +		emit_alu(nfp_prog, pv_len(nfp_prog), +			 pv_len(nfp_prog), ALU_OP_SUB, reg_a(2 * 2)); + +		wrp_immed(nfp_prog, reg_both(0), 0); +		wrp_immed(nfp_prog, reg_both(1), 0); + +		/* TODO: when adjust head is guaranteed to succeed we can +		 * also eliminate the following if (r0 == 0) branch. +		 */ + +		return 0; +	} + +	ret_einval = nfp_prog_current_offset(nfp_prog) + 14; +	end = ret_einval + 2; + +	/* We need to use a temp because offset is just a part of the pkt ptr */ +	emit_alu(nfp_prog, tmp, +		 reg_a(2 * 2), ALU_OP_ADD_2B, pptr_reg(nfp_prog)); + +	/* Validate result will fit within FW datapath constraints */ +	emit_alu(nfp_prog, reg_none(), +		 tmp, ALU_OP_SUB, reg_imm(adjust_head->off_min)); +	emit_br(nfp_prog, BR_BLO, ret_einval, 0); +	emit_alu(nfp_prog, reg_none(), +		 reg_imm(adjust_head->off_max), ALU_OP_SUB, tmp); +	emit_br(nfp_prog, BR_BLO, ret_einval, 0); + +	/* Validate the length is at least ETH_HLEN */ +	emit_alu(nfp_prog, tmp_len, +		 plen_reg(nfp_prog), ALU_OP_SUB, reg_a(2 * 2)); +	emit_alu(nfp_prog, reg_none(), +		 tmp_len, ALU_OP_SUB, reg_imm(ETH_HLEN)); +	emit_br(nfp_prog, BR_BMI, ret_einval, 0); + +	/* Load the ret code */ +	wrp_immed(nfp_prog, reg_both(0), 0); +	wrp_immed(nfp_prog, reg_both(1), 0); + +	/* Modify the packet metadata */ +	emit_ld_field(nfp_prog, pptr_reg(nfp_prog), 0x3, tmp, SHF_SC_NONE, 0); + +	/* Skip over the -EINVAL ret code (defer 2) */ +	emit_br(nfp_prog, BR_UNC, end, 2); + +	emit_alu(nfp_prog, plen_reg(nfp_prog), +		 plen_reg(nfp_prog), ALU_OP_SUB, reg_a(2 * 2)); +	emit_alu(nfp_prog, pv_len(nfp_prog), +		 pv_len(nfp_prog), ALU_OP_SUB, reg_a(2 * 2)); + +	/* return -EINVAL target */ +	if (!nfp_prog_confirm_current_offset(nfp_prog, ret_einval)) +		return -EINVAL; + +	wrp_immed(nfp_prog, reg_both(0), -22); +	wrp_immed(nfp_prog, reg_both(1), ~0); + +	if (!nfp_prog_confirm_current_offset(nfp_prog, end)) +		return -EINVAL; + +	return 0; +} + +static int +map_lookup_stack(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) +{ +	struct bpf_offloaded_map *offmap; +	struct nfp_bpf_map *nfp_map; +	bool load_lm_ptr; +	u32 ret_tgt; +	s64 lm_off; +	swreg tid; + +	offmap = (struct bpf_offloaded_map *)meta->arg1.map_ptr; +	nfp_map = offmap->dev_priv; + +	/* We only have to reload LM0 if the key is not at start of stack */ +	lm_off = nfp_prog->stack_depth; +	lm_off += meta->arg2.var_off.value + meta->arg2.off; +	load_lm_ptr = meta->arg2_var_off || lm_off; + +	/* Set LM0 to start of key */ +	if (load_lm_ptr) +		emit_csr_wr(nfp_prog, reg_b(2 * 2), NFP_CSR_ACT_LM_ADDR0); + +	/* Load map ID into a register, it should actually fit as an immediate +	 * but in case it doesn't deal with it here, not in the delay slots. +	 */ +	tid = ur_load_imm_any(nfp_prog, nfp_map->tid, imm_a(nfp_prog)); + +	emit_br_relo(nfp_prog, BR_UNC, BR_OFF_RELO + BPF_FUNC_map_lookup_elem, +		     2, RELO_BR_HELPER); +	ret_tgt = nfp_prog_current_offset(nfp_prog) + 2; + +	/* Load map ID into A0 */ +	wrp_mov(nfp_prog, reg_a(0), tid); + +	/* Load the return address into B0 */ +	wrp_immed_relo(nfp_prog, reg_b(0), ret_tgt, RELO_IMMED_REL); + +	if (!nfp_prog_confirm_current_offset(nfp_prog, ret_tgt)) +		return -EINVAL; + +	/* Reset the LM0 pointer */ +	if (!load_lm_ptr) +		return 0; + +	emit_csr_wr(nfp_prog, stack_reg(nfp_prog),  NFP_CSR_ACT_LM_ADDR0); +	wrp_nops(nfp_prog, 3); + +	return 0; +} +  /* --- Callbacks --- */  static int mov_reg64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)  { @@ -1486,14 +1822,29 @@ mem_ldx_data(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta,  	tmp_reg = re_load_imm_any(nfp_prog, meta->insn.off, imm_b(nfp_prog)); -	return data_ld_host_order(nfp_prog, meta->insn.src_reg * 2, tmp_reg, -				  meta->insn.dst_reg * 2, size); +	return data_ld_host_order_addr32(nfp_prog, meta->insn.src_reg * 2, +					 tmp_reg, meta->insn.dst_reg * 2, size); +} + +static int +mem_ldx_emem(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, +	     unsigned int size) +{ +	swreg tmp_reg; + +	tmp_reg = re_load_imm_any(nfp_prog, meta->insn.off, imm_b(nfp_prog)); + +	return data_ld_host_order_addr40(nfp_prog, meta->insn.src_reg * 2, +					 tmp_reg, meta->insn.dst_reg * 2, size);  }  static int  mem_ldx(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta,  	unsigned int size)  { +	if (meta->ldst_gather_len) +		return nfp_cpp_memcpy(nfp_prog, meta); +  	if (meta->ptr.type == PTR_TO_CTX) {  		if (nfp_prog->type == BPF_PROG_TYPE_XDP)  			return mem_ldx_xdp(nfp_prog, meta, size); @@ -1508,6 +1859,9 @@ mem_ldx(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta,  		return mem_ldx_stack(nfp_prog, meta, size,  				     meta->ptr.off + meta->ptr.var_off.value); +	if (meta->ptr.type == PTR_TO_MAP_VALUE) +		return mem_ldx_emem(nfp_prog, meta, size); +  	return -EOPNOTSUPP;  } @@ -1630,8 +1984,6 @@ static int mem_stx8(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)  static int jump(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)  { -	if (meta->insn.off < 0) /* TODO */ -		return -EOPNOTSUPP;  	emit_br(nfp_prog, BR_UNC, meta->insn.off, 0);  	return 0; @@ -1646,9 +1998,6 @@ static int jeq_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)  	or1 = reg_a(insn->dst_reg * 2);  	or2 = reg_b(insn->dst_reg * 2 + 1); -	if (insn->off < 0) /* TODO */ -		return -EOPNOTSUPP; -  	if (imm & ~0U) {  		tmp_reg = ur_load_imm_any(nfp_prog, imm & ~0U, imm_b(nfp_prog));  		emit_alu(nfp_prog, imm_a(nfp_prog), @@ -1689,15 +2038,32 @@ static int jle_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)  	return wrp_cmp_imm(nfp_prog, meta, BR_BHS, true);  } +static int jsgt_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) +{ +	return wrp_cmp_imm(nfp_prog, meta, BR_BLT, true); +} + +static int jsge_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) +{ +	return wrp_cmp_imm(nfp_prog, meta, BR_BGE, false); +} + +static int jslt_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) +{ +	return wrp_cmp_imm(nfp_prog, meta, BR_BLT, false); +} + +static int jsle_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) +{ +	return wrp_cmp_imm(nfp_prog, meta, BR_BGE, true); +} +  static int jset_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)  {  	const struct bpf_insn *insn = &meta->insn;  	u64 imm = insn->imm; /* sign extend */  	swreg tmp_reg; -	if (insn->off < 0) /* TODO */ -		return -EOPNOTSUPP; -  	if (!imm) {  		meta->skip = true;  		return 0; @@ -1726,9 +2092,6 @@ static int jne_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)  	u64 imm = insn->imm; /* sign extend */  	swreg tmp_reg; -	if (insn->off < 0) /* TODO */ -		return -EOPNOTSUPP; -  	if (!imm) {  		emit_alu(nfp_prog, reg_none(), reg_a(insn->dst_reg * 2),  			 ALU_OP_OR, reg_b(insn->dst_reg * 2 + 1)); @@ -1753,9 +2116,6 @@ static int jeq_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)  {  	const struct bpf_insn *insn = &meta->insn; -	if (insn->off < 0) /* TODO */ -		return -EOPNOTSUPP; -  	emit_alu(nfp_prog, imm_a(nfp_prog), reg_a(insn->dst_reg * 2),  		 ALU_OP_XOR, reg_b(insn->src_reg * 2));  	emit_alu(nfp_prog, imm_b(nfp_prog), reg_a(insn->dst_reg * 2 + 1), @@ -1787,6 +2147,26 @@ static int jle_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)  	return wrp_cmp_reg(nfp_prog, meta, BR_BHS, true);  } +static int jsgt_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) +{ +	return wrp_cmp_reg(nfp_prog, meta, BR_BLT, true); +} + +static int jsge_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) +{ +	return wrp_cmp_reg(nfp_prog, meta, BR_BGE, false); +} + +static int jslt_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) +{ +	return wrp_cmp_reg(nfp_prog, meta, BR_BLT, false); +} + +static int jsle_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) +{ +	return wrp_cmp_reg(nfp_prog, meta, BR_BGE, true); +} +  static int jset_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)  {  	return wrp_test_reg(nfp_prog, meta, ALU_OP_AND, BR_BNE); @@ -1797,9 +2177,22 @@ static int jne_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)  	return wrp_test_reg(nfp_prog, meta, ALU_OP_XOR, BR_BNE);  } +static int call(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) +{ +	switch (meta->insn.imm) { +	case BPF_FUNC_xdp_adjust_head: +		return adjust_head(nfp_prog, meta); +	case BPF_FUNC_map_lookup_elem: +		return map_lookup_stack(nfp_prog, meta); +	default: +		WARN_ONCE(1, "verifier allowed unsupported function\n"); +		return -EOPNOTSUPP; +	} +} +  static int goto_out(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)  { -	wrp_br_special(nfp_prog, BR_UNC, OP_BR_GO_OUT); +	emit_br_relo(nfp_prog, BR_UNC, BR_OFF_RELO, 0, RELO_BR_GO_OUT);  	return 0;  } @@ -1860,6 +2253,10 @@ static const instr_cb_t instr_cb[256] = {  	[BPF_JMP | BPF_JGE | BPF_K] =	jge_imm,  	[BPF_JMP | BPF_JLT | BPF_K] =	jlt_imm,  	[BPF_JMP | BPF_JLE | BPF_K] =	jle_imm, +	[BPF_JMP | BPF_JSGT | BPF_K] =  jsgt_imm, +	[BPF_JMP | BPF_JSGE | BPF_K] =  jsge_imm, +	[BPF_JMP | BPF_JSLT | BPF_K] =  jslt_imm, +	[BPF_JMP | BPF_JSLE | BPF_K] =  jsle_imm,  	[BPF_JMP | BPF_JSET | BPF_K] =	jset_imm,  	[BPF_JMP | BPF_JNE | BPF_K] =	jne_imm,  	[BPF_JMP | BPF_JEQ | BPF_X] =	jeq_reg, @@ -1867,99 +2264,64 @@ static const instr_cb_t instr_cb[256] = {  	[BPF_JMP | BPF_JGE | BPF_X] =	jge_reg,  	[BPF_JMP | BPF_JLT | BPF_X] =	jlt_reg,  	[BPF_JMP | BPF_JLE | BPF_X] =	jle_reg, +	[BPF_JMP | BPF_JSGT | BPF_X] =  jsgt_reg, +	[BPF_JMP | BPF_JSGE | BPF_X] =  jsge_reg, +	[BPF_JMP | BPF_JSLT | BPF_X] =  jslt_reg, +	[BPF_JMP | BPF_JSLE | BPF_X] =  jsle_reg,  	[BPF_JMP | BPF_JSET | BPF_X] =	jset_reg,  	[BPF_JMP | BPF_JNE | BPF_X] =	jne_reg, +	[BPF_JMP | BPF_CALL] =		call,  	[BPF_JMP | BPF_EXIT] =		goto_out,  }; -/* --- Misc code --- */ -static void br_set_offset(u64 *instr, u16 offset) -{ -	u16 addr_lo, addr_hi; - -	addr_lo = offset & (OP_BR_ADDR_LO >> __bf_shf(OP_BR_ADDR_LO)); -	addr_hi = offset != addr_lo; -	*instr &= ~(OP_BR_ADDR_HI | OP_BR_ADDR_LO); -	*instr |= FIELD_PREP(OP_BR_ADDR_HI, addr_hi); -	*instr |= FIELD_PREP(OP_BR_ADDR_LO, addr_lo); -} -  /* --- Assembler logic --- */  static int nfp_fixup_branches(struct nfp_prog *nfp_prog)  { -	struct nfp_insn_meta *meta, *next; -	u32 off, br_idx; -	u32 idx; +	struct nfp_insn_meta *meta, *jmp_dst; +	u32 idx, br_idx; -	nfp_for_each_insn_walk2(nfp_prog, meta, next) { +	list_for_each_entry(meta, &nfp_prog->insns, l) {  		if (meta->skip)  			continue; +		if (meta->insn.code == (BPF_JMP | BPF_CALL)) +			continue;  		if (BPF_CLASS(meta->insn.code) != BPF_JMP)  			continue; -		br_idx = nfp_prog_offset_to_index(nfp_prog, next->off) - 1; +		if (list_is_last(&meta->l, &nfp_prog->insns)) +			br_idx = nfp_prog->last_bpf_off; +		else +			br_idx = list_next_entry(meta, l)->off - 1; +  		if (!nfp_is_br(nfp_prog->prog[br_idx])) {  			pr_err("Fixup found block not ending in branch %d %02x %016llx!!\n",  			       br_idx, meta->insn.code, nfp_prog->prog[br_idx]);  			return -ELOOP;  		}  		/* Leave special branches for later */ -		if (FIELD_GET(OP_BR_SPECIAL, nfp_prog->prog[br_idx])) +		if (FIELD_GET(OP_RELO_TYPE, nfp_prog->prog[br_idx]) != +		    RELO_BR_REL)  			continue; -		/* Find the target offset in assembler realm */ -		off = meta->insn.off; -		if (!off) { -			pr_err("Fixup found zero offset!!\n"); +		if (!meta->jmp_dst) { +			pr_err("Non-exit jump doesn't have destination info recorded!!\n");  			return -ELOOP;  		} -		while (off && nfp_meta_has_next(nfp_prog, next)) { -			next = nfp_meta_next(next); -			off--; -		} -		if (off) { -			pr_err("Fixup found too large jump!! %d\n", off); -			return -ELOOP; -		} +		jmp_dst = meta->jmp_dst; -		if (next->skip) { +		if (jmp_dst->skip) {  			pr_err("Branch landing on removed instruction!!\n");  			return -ELOOP;  		} -		for (idx = nfp_prog_offset_to_index(nfp_prog, meta->off); -		     idx <= br_idx; idx++) { +		for (idx = meta->off; idx <= br_idx; idx++) {  			if (!nfp_is_br(nfp_prog->prog[idx]))  				continue; -			br_set_offset(&nfp_prog->prog[idx], next->off); +			br_set_offset(&nfp_prog->prog[idx], jmp_dst->off);  		}  	} -	/* Fixup 'goto out's separately, they can be scattered around */ -	for (br_idx = 0; br_idx < nfp_prog->prog_len; br_idx++) { -		enum br_special special; - -		if ((nfp_prog->prog[br_idx] & OP_BR_BASE_MASK) != OP_BR_BASE) -			continue; - -		special = FIELD_GET(OP_BR_SPECIAL, nfp_prog->prog[br_idx]); -		switch (special) { -		case OP_BR_NORMAL: -			break; -		case OP_BR_GO_OUT: -			br_set_offset(&nfp_prog->prog[br_idx], -				      nfp_prog->tgt_out); -			break; -		case OP_BR_GO_ABORT: -			br_set_offset(&nfp_prog->prog[br_idx], -				      nfp_prog->tgt_abort); -			break; -		} - -		nfp_prog->prog[br_idx] &= ~OP_BR_SPECIAL; -	} -  	return 0;  } @@ -1987,7 +2349,7 @@ static void nfp_outro_tc_da(struct nfp_prog *nfp_prog)  	/* Target for aborts */  	nfp_prog->tgt_abort = nfp_prog_current_offset(nfp_prog); -	emit_br_def(nfp_prog, nfp_prog->tgt_done, 2); +	emit_br_relo(nfp_prog, BR_UNC, BR_OFF_RELO, 2, RELO_BR_NEXT_PKT);  	wrp_mov(nfp_prog, reg_a(0), NFP_BPF_ABI_FLAGS);  	emit_ld_field(nfp_prog, reg_a(0), 0xc, reg_imm(0x11), SHF_SC_L_SHF, 16); @@ -2014,7 +2376,7 @@ static void nfp_outro_tc_da(struct nfp_prog *nfp_prog)  	emit_shf(nfp_prog, reg_b(2),  		 reg_imm(0xf), SHF_OP_AND, reg_b(3), SHF_SC_R_SHF, 0); -	emit_br_def(nfp_prog, nfp_prog->tgt_done, 2); +	emit_br_relo(nfp_prog, BR_UNC, BR_OFF_RELO, 2, RELO_BR_NEXT_PKT);  	emit_shf(nfp_prog, reg_b(2),  		 reg_a(2), SHF_OP_OR, reg_b(2), SHF_SC_L_SHF, 4); @@ -2033,7 +2395,7 @@ static void nfp_outro_xdp(struct nfp_prog *nfp_prog)  	/* Target for aborts */  	nfp_prog->tgt_abort = nfp_prog_current_offset(nfp_prog); -	emit_br_def(nfp_prog, nfp_prog->tgt_done, 2); +	emit_br_relo(nfp_prog, BR_UNC, BR_OFF_RELO, 2, RELO_BR_NEXT_PKT);  	wrp_mov(nfp_prog, reg_a(0), NFP_BPF_ABI_FLAGS);  	emit_ld_field(nfp_prog, reg_a(0), 0xc, reg_imm(0x82), SHF_SC_L_SHF, 16); @@ -2054,7 +2416,7 @@ static void nfp_outro_xdp(struct nfp_prog *nfp_prog)  	emit_shf(nfp_prog, reg_b(2),  		 reg_imm(0xff), SHF_OP_AND, reg_b(2), SHF_SC_R_SHF, 0); -	emit_br_def(nfp_prog, nfp_prog->tgt_done, 2); +	emit_br_relo(nfp_prog, BR_UNC, BR_OFF_RELO, 2, RELO_BR_NEXT_PKT);  	wrp_mov(nfp_prog, reg_a(0), NFP_BPF_ABI_FLAGS);  	emit_ld_field(nfp_prog, reg_a(0), 0xc, reg_b(2), SHF_SC_L_SHF, 16); @@ -2105,6 +2467,8 @@ static int nfp_translate(struct nfp_prog *nfp_prog)  		nfp_prog->n_translated++;  	} +	nfp_prog->last_bpf_off = nfp_prog_current_offset(nfp_prog) - 1; +  	nfp_outro(nfp_prog);  	if (nfp_prog->error)  		return nfp_prog->error; @@ -2173,6 +2537,9 @@ static void nfp_bpf_opt_ld_mask(struct nfp_prog *nfp_prog)  		if (next.src_reg || next.dst_reg)  			continue; +		if (meta2->flags & FLAG_INSN_IS_JUMP_DST) +			continue; +  		meta2->skip = true;  	}  } @@ -2209,40 +2576,294 @@ static void nfp_bpf_opt_ld_shift(struct nfp_prog *nfp_prog)  		if (next1.imm != 0x20 || next2.imm != 0x20)  			continue; +		if (meta2->flags & FLAG_INSN_IS_JUMP_DST || +		    meta3->flags & FLAG_INSN_IS_JUMP_DST) +			continue; +  		meta2->skip = true;  		meta3->skip = true;  	}  } +/* load/store pair that forms memory copy sould look like the following: + * + *   ld_width R, [addr_src + offset_src] + *   st_width [addr_dest + offset_dest], R + * + * The destination register of load and source register of store should + * be the same, load and store should also perform at the same width. + * If either of addr_src or addr_dest is stack pointer, we don't do the + * CPP optimization as stack is modelled by registers on NFP. + */ +static bool +curr_pair_is_memcpy(struct nfp_insn_meta *ld_meta, +		    struct nfp_insn_meta *st_meta) +{ +	struct bpf_insn *ld = &ld_meta->insn; +	struct bpf_insn *st = &st_meta->insn; + +	if (!is_mbpf_load(ld_meta) || !is_mbpf_store(st_meta)) +		return false; + +	if (ld_meta->ptr.type != PTR_TO_PACKET) +		return false; + +	if (st_meta->ptr.type != PTR_TO_PACKET) +		return false; + +	if (BPF_SIZE(ld->code) != BPF_SIZE(st->code)) +		return false; + +	if (ld->dst_reg != st->src_reg) +		return false; + +	/* There is jump to the store insn in this pair. */ +	if (st_meta->flags & FLAG_INSN_IS_JUMP_DST) +		return false; + +	return true; +} + +/* Currently, we only support chaining load/store pairs if: + * + *  - Their address base registers are the same. + *  - Their address offsets are in the same order. + *  - They operate at the same memory width. + *  - There is no jump into the middle of them. + */ +static bool +curr_pair_chain_with_previous(struct nfp_insn_meta *ld_meta, +			      struct nfp_insn_meta *st_meta, +			      struct bpf_insn *prev_ld, +			      struct bpf_insn *prev_st) +{ +	u8 prev_size, curr_size, prev_ld_base, prev_st_base, prev_ld_dst; +	struct bpf_insn *ld = &ld_meta->insn; +	struct bpf_insn *st = &st_meta->insn; +	s16 prev_ld_off, prev_st_off; + +	/* This pair is the start pair. */ +	if (!prev_ld) +		return true; + +	prev_size = BPF_LDST_BYTES(prev_ld); +	curr_size = BPF_LDST_BYTES(ld); +	prev_ld_base = prev_ld->src_reg; +	prev_st_base = prev_st->dst_reg; +	prev_ld_dst = prev_ld->dst_reg; +	prev_ld_off = prev_ld->off; +	prev_st_off = prev_st->off; + +	if (ld->dst_reg != prev_ld_dst) +		return false; + +	if (ld->src_reg != prev_ld_base || st->dst_reg != prev_st_base) +		return false; + +	if (curr_size != prev_size) +		return false; + +	/* There is jump to the head of this pair. */ +	if (ld_meta->flags & FLAG_INSN_IS_JUMP_DST) +		return false; + +	/* Both in ascending order. */ +	if (prev_ld_off + prev_size == ld->off && +	    prev_st_off + prev_size == st->off) +		return true; + +	/* Both in descending order. */ +	if (ld->off + curr_size == prev_ld_off && +	    st->off + curr_size == prev_st_off) +		return true; + +	return false; +} + +/* Return TRUE if cross memory access happens. Cross memory access means + * store area is overlapping with load area that a later load might load + * the value from previous store, for this case we can't treat the sequence + * as an memory copy. + */ +static bool +cross_mem_access(struct bpf_insn *ld, struct nfp_insn_meta *head_ld_meta, +		 struct nfp_insn_meta *head_st_meta) +{ +	s16 head_ld_off, head_st_off, ld_off; + +	/* Different pointer types does not overlap. */ +	if (head_ld_meta->ptr.type != head_st_meta->ptr.type) +		return false; + +	/* load and store are both PTR_TO_PACKET, check ID info.  */ +	if (head_ld_meta->ptr.id != head_st_meta->ptr.id) +		return true; + +	/* Canonicalize the offsets. Turn all of them against the original +	 * base register. +	 */ +	head_ld_off = head_ld_meta->insn.off + head_ld_meta->ptr.off; +	head_st_off = head_st_meta->insn.off + head_st_meta->ptr.off; +	ld_off = ld->off + head_ld_meta->ptr.off; + +	/* Ascending order cross. */ +	if (ld_off > head_ld_off && +	    head_ld_off < head_st_off && ld_off >= head_st_off) +		return true; + +	/* Descending order cross. */ +	if (ld_off < head_ld_off && +	    head_ld_off > head_st_off && ld_off <= head_st_off) +		return true; + +	return false; +} + +/* This pass try to identify the following instructoin sequences. + * + *   load R, [regA + offA] + *   store [regB + offB], R + *   load R, [regA + offA + const_imm_A] + *   store [regB + offB + const_imm_A], R + *   load R, [regA + offA + 2 * const_imm_A] + *   store [regB + offB + 2 * const_imm_A], R + *   ... + * + * Above sequence is typically generated by compiler when lowering + * memcpy. NFP prefer using CPP instructions to accelerate it. + */ +static void nfp_bpf_opt_ldst_gather(struct nfp_prog *nfp_prog) +{ +	struct nfp_insn_meta *head_ld_meta = NULL; +	struct nfp_insn_meta *head_st_meta = NULL; +	struct nfp_insn_meta *meta1, *meta2; +	struct bpf_insn *prev_ld = NULL; +	struct bpf_insn *prev_st = NULL; +	u8 count = 0; + +	nfp_for_each_insn_walk2(nfp_prog, meta1, meta2) { +		struct bpf_insn *ld = &meta1->insn; +		struct bpf_insn *st = &meta2->insn; + +		/* Reset record status if any of the following if true: +		 *   - The current insn pair is not load/store. +		 *   - The load/store pair doesn't chain with previous one. +		 *   - The chained load/store pair crossed with previous pair. +		 *   - The chained load/store pair has a total size of memory +		 *     copy beyond 128 bytes which is the maximum length a +		 *     single NFP CPP command can transfer. +		 */ +		if (!curr_pair_is_memcpy(meta1, meta2) || +		    !curr_pair_chain_with_previous(meta1, meta2, prev_ld, +						   prev_st) || +		    (head_ld_meta && (cross_mem_access(ld, head_ld_meta, +						       head_st_meta) || +				      head_ld_meta->ldst_gather_len >= 128))) { +			if (!count) +				continue; + +			if (count > 1) { +				s16 prev_ld_off = prev_ld->off; +				s16 prev_st_off = prev_st->off; +				s16 head_ld_off = head_ld_meta->insn.off; + +				if (prev_ld_off < head_ld_off) { +					head_ld_meta->insn.off = prev_ld_off; +					head_st_meta->insn.off = prev_st_off; +					head_ld_meta->ldst_gather_len = +						-head_ld_meta->ldst_gather_len; +				} + +				head_ld_meta->paired_st = &head_st_meta->insn; +				head_st_meta->skip = true; +			} else { +				head_ld_meta->ldst_gather_len = 0; +			} + +			/* If the chain is ended by an load/store pair then this +			 * could serve as the new head of the the next chain. +			 */ +			if (curr_pair_is_memcpy(meta1, meta2)) { +				head_ld_meta = meta1; +				head_st_meta = meta2; +				head_ld_meta->ldst_gather_len = +					BPF_LDST_BYTES(ld); +				meta1 = nfp_meta_next(meta1); +				meta2 = nfp_meta_next(meta2); +				prev_ld = ld; +				prev_st = st; +				count = 1; +			} else { +				head_ld_meta = NULL; +				head_st_meta = NULL; +				prev_ld = NULL; +				prev_st = NULL; +				count = 0; +			} + +			continue; +		} + +		if (!head_ld_meta) { +			head_ld_meta = meta1; +			head_st_meta = meta2; +		} else { +			meta1->skip = true; +			meta2->skip = true; +		} + +		head_ld_meta->ldst_gather_len += BPF_LDST_BYTES(ld); +		meta1 = nfp_meta_next(meta1); +		meta2 = nfp_meta_next(meta2); +		prev_ld = ld; +		prev_st = st; +		count++; +	} +} +  static int nfp_bpf_optimize(struct nfp_prog *nfp_prog)  {  	nfp_bpf_opt_reg_init(nfp_prog);  	nfp_bpf_opt_ld_mask(nfp_prog);  	nfp_bpf_opt_ld_shift(nfp_prog); +	nfp_bpf_opt_ldst_gather(nfp_prog);  	return 0;  } -static int nfp_bpf_ustore_calc(struct nfp_prog *nfp_prog, __le64 *ustore) +static int nfp_bpf_ustore_calc(u64 *prog, unsigned int len)  { +	__le64 *ustore = (__force __le64 *)prog;  	int i; -	for (i = 0; i < nfp_prog->prog_len; i++) { +	for (i = 0; i < len; i++) {  		int err; -		err = nfp_ustore_check_valid_no_ecc(nfp_prog->prog[i]); +		err = nfp_ustore_check_valid_no_ecc(prog[i]);  		if (err)  			return err; -		nfp_prog->prog[i] = nfp_ustore_calc_ecc_insn(nfp_prog->prog[i]); - -		ustore[i] = cpu_to_le64(nfp_prog->prog[i]); +		ustore[i] = cpu_to_le64(nfp_ustore_calc_ecc_insn(prog[i]));  	}  	return 0;  } +static void nfp_bpf_prog_trim(struct nfp_prog *nfp_prog) +{ +	void *prog; + +	prog = kvmalloc_array(nfp_prog->prog_len, sizeof(u64), GFP_KERNEL); +	if (!prog) +		return; + +	nfp_prog->__prog_alloc_len = nfp_prog->prog_len * sizeof(u64); +	memcpy(prog, nfp_prog->prog, nfp_prog->__prog_alloc_len); +	kvfree(nfp_prog->prog); +	nfp_prog->prog = prog; +} +  int nfp_bpf_jit(struct nfp_prog *nfp_prog)  {  	int ret; @@ -2258,5 +2879,102 @@ int nfp_bpf_jit(struct nfp_prog *nfp_prog)  		return -EINVAL;  	} -	return nfp_bpf_ustore_calc(nfp_prog, (__force __le64 *)nfp_prog->prog); +	nfp_bpf_prog_trim(nfp_prog); + +	return ret; +} + +void nfp_bpf_jit_prepare(struct nfp_prog *nfp_prog, unsigned int cnt) +{ +	struct nfp_insn_meta *meta; + +	/* Another pass to record jump information. */ +	list_for_each_entry(meta, &nfp_prog->insns, l) { +		u64 code = meta->insn.code; + +		if (BPF_CLASS(code) == BPF_JMP && BPF_OP(code) != BPF_EXIT && +		    BPF_OP(code) != BPF_CALL) { +			struct nfp_insn_meta *dst_meta; +			unsigned short dst_indx; + +			dst_indx = meta->n + 1 + meta->insn.off; +			dst_meta = nfp_bpf_goto_meta(nfp_prog, meta, dst_indx, +						     cnt); + +			meta->jmp_dst = dst_meta; +			dst_meta->flags |= FLAG_INSN_IS_JUMP_DST; +		} +	} +} + +bool nfp_bpf_supported_opcode(u8 code) +{ +	return !!instr_cb[code]; +} + +void *nfp_bpf_relo_for_vnic(struct nfp_prog *nfp_prog, struct nfp_bpf_vnic *bv) +{ +	unsigned int i; +	u64 *prog; +	int err; + +	prog = kmemdup(nfp_prog->prog, nfp_prog->prog_len * sizeof(u64), +		       GFP_KERNEL); +	if (!prog) +		return ERR_PTR(-ENOMEM); + +	for (i = 0; i < nfp_prog->prog_len; i++) { +		enum nfp_relo_type special; +		u32 val; + +		special = FIELD_GET(OP_RELO_TYPE, prog[i]); +		switch (special) { +		case RELO_NONE: +			continue; +		case RELO_BR_REL: +			br_add_offset(&prog[i], bv->start_off); +			break; +		case RELO_BR_GO_OUT: +			br_set_offset(&prog[i], +				      nfp_prog->tgt_out + bv->start_off); +			break; +		case RELO_BR_GO_ABORT: +			br_set_offset(&prog[i], +				      nfp_prog->tgt_abort + bv->start_off); +			break; +		case RELO_BR_NEXT_PKT: +			br_set_offset(&prog[i], bv->tgt_done); +			break; +		case RELO_BR_HELPER: +			val = br_get_offset(prog[i]); +			val -= BR_OFF_RELO; +			switch (val) { +			case BPF_FUNC_map_lookup_elem: +				val = nfp_prog->bpf->helpers.map_lookup; +				break; +			default: +				pr_err("relocation of unknown helper %d\n", +				       val); +				err = -EINVAL; +				goto err_free_prog; +			} +			br_set_offset(&prog[i], val); +			break; +		case RELO_IMMED_REL: +			immed_add_value(&prog[i], bv->start_off); +			break; +		} + +		prog[i] &= ~OP_RELO_TYPE; +	} + +	err = nfp_bpf_ustore_calc(prog, nfp_prog->prog_len); +	if (err) +		goto err_free_prog; + +	return prog; + +err_free_prog: +	kfree(prog); +	return ERR_PTR(err);  } diff --git a/drivers/net/ethernet/netronome/nfp/bpf/main.c b/drivers/net/ethernet/netronome/nfp/bpf/main.c index 13190aa09faf..34e98aa6b956 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/main.c +++ b/drivers/net/ethernet/netronome/nfp/bpf/main.c @@ -34,10 +34,13 @@  #include <net/pkt_cls.h>  #include "../nfpcore/nfp_cpp.h" +#include "../nfpcore/nfp_nffw.h" +#include "../nfpcore/nfp_nsp.h"  #include "../nfp_app.h"  #include "../nfp_main.h"  #include "../nfp_net.h"  #include "../nfp_port.h" +#include "fw.h"  #include "main.h"  static bool nfp_net_ebpf_capable(struct nfp_net *nn) @@ -52,7 +55,7 @@ static bool nfp_net_ebpf_capable(struct nfp_net *nn)  static int  nfp_bpf_xdp_offload(struct nfp_app *app, struct nfp_net *nn, -		    struct bpf_prog *prog) +		    struct bpf_prog *prog, struct netlink_ext_ack *extack)  {  	bool running, xdp_running;  	int ret; @@ -68,10 +71,10 @@ nfp_bpf_xdp_offload(struct nfp_app *app, struct nfp_net *nn,  	if (prog && running && !xdp_running)  		return -EBUSY; -	ret = nfp_net_bpf_offload(nn, prog, running); +	ret = nfp_net_bpf_offload(nn, prog, running, extack);  	/* Stop offload if replace not possible */  	if (ret && prog) -		nfp_bpf_xdp_offload(app, nn, NULL); +		nfp_bpf_xdp_offload(app, nn, NULL, extack);  	nn->dp.bpf_offload_xdp = prog && !ret;  	return ret; @@ -85,16 +88,32 @@ static const char *nfp_bpf_extra_cap(struct nfp_app *app, struct nfp_net *nn)  static int  nfp_bpf_vnic_alloc(struct nfp_app *app, struct nfp_net *nn, unsigned int id)  { +	struct nfp_pf *pf = app->pf; +	struct nfp_bpf_vnic *bv;  	int err; -	nn->app_priv = kzalloc(sizeof(struct nfp_bpf_vnic), GFP_KERNEL); -	if (!nn->app_priv) +	if (!pf->eth_tbl) { +		nfp_err(pf->cpp, "No ETH table\n"); +		return -EINVAL; +	} +	if (pf->max_data_vnics != pf->eth_tbl->count) { +		nfp_err(pf->cpp, "ETH entries don't match vNICs (%d vs %d)\n", +			pf->max_data_vnics, pf->eth_tbl->count); +		return -EINVAL; +	} + +	bv = kzalloc(sizeof(*bv), GFP_KERNEL); +	if (!bv)  		return -ENOMEM; +	nn->app_priv = bv;  	err = nfp_app_nic_vnic_alloc(app, nn, id);  	if (err)  		goto err_free_priv; +	bv->start_off = nn_readw(nn, NFP_NET_CFG_BPF_START); +	bv->tgt_done = nn_readw(nn, NFP_NET_CFG_BPF_DONE); +  	return 0;  err_free_priv:  	kfree(nn->app_priv); @@ -105,8 +124,6 @@ static void nfp_bpf_vnic_free(struct nfp_app *app, struct nfp_net *nn)  {  	struct nfp_bpf_vnic *bv = nn->app_priv; -	if (nn->dp.bpf_offload_xdp) -		nfp_bpf_xdp_offload(app, nn, NULL);  	WARN_ON(bv->tc_prog);  	kfree(bv);  } @@ -120,17 +137,29 @@ static int nfp_bpf_setup_tc_block_cb(enum tc_setup_type type,  	struct nfp_bpf_vnic *bv;  	int err; -	if (type != TC_SETUP_CLSBPF || -	    !tc_can_offload(nn->dp.netdev) || -	    !nfp_net_ebpf_capable(nn) || -	    cls_bpf->common.protocol != htons(ETH_P_ALL) || -	    cls_bpf->common.chain_index) +	if (type != TC_SETUP_CLSBPF) { +		NL_SET_ERR_MSG_MOD(cls_bpf->common.extack, +				   "only offload of BPF classifiers supported"); +		return -EOPNOTSUPP; +	} +	if (!tc_cls_can_offload_and_chain0(nn->dp.netdev, &cls_bpf->common)) +		return -EOPNOTSUPP; +	if (!nfp_net_ebpf_capable(nn)) { +		NL_SET_ERR_MSG_MOD(cls_bpf->common.extack, +				   "NFP firmware does not support eBPF offload"); +		return -EOPNOTSUPP; +	} +	if (cls_bpf->common.protocol != htons(ETH_P_ALL)) { +		NL_SET_ERR_MSG_MOD(cls_bpf->common.extack, +				   "only ETH_P_ALL supported as filter protocol");  		return -EOPNOTSUPP; +	}  	/* Only support TC direct action */  	if (!cls_bpf->exts_integrated ||  	    tcf_exts_has_actions(cls_bpf->exts)) { -		nn_err(nn, "only direct action with no legacy actions supported\n"); +		NL_SET_ERR_MSG_MOD(cls_bpf->common.extack, +				   "only direct action with no legacy actions supported");  		return -EOPNOTSUPP;  	} @@ -147,11 +176,13 @@ static int nfp_bpf_setup_tc_block_cb(enum tc_setup_type type,  			return 0;  	} -	err = nfp_net_bpf_offload(nn, cls_bpf->prog, oldprog); +	err = nfp_net_bpf_offload(nn, cls_bpf->prog, oldprog, +				  cls_bpf->common.extack);  	if (err)  		return err;  	bv->tc_prog = cls_bpf->prog; +	nn->port->tc_offload_cnt = !!bv->tc_prog;  	return 0;  } @@ -189,25 +220,209 @@ static int nfp_bpf_setup_tc(struct nfp_app *app, struct net_device *netdev,  	}  } -static bool nfp_bpf_tc_busy(struct nfp_app *app, struct nfp_net *nn) +static int +nfp_bpf_change_mtu(struct nfp_app *app, struct net_device *netdev, int new_mtu) +{ +	struct nfp_net *nn = netdev_priv(netdev); +	unsigned int max_mtu; + +	if (~nn->dp.ctrl & NFP_NET_CFG_CTRL_BPF) +		return 0; + +	max_mtu = nn_readb(nn, NFP_NET_CFG_BPF_INL_MTU) * 64 - 32; +	if (new_mtu > max_mtu) { +		nn_info(nn, "BPF offload active, MTU over %u not supported\n", +			max_mtu); +		return -EBUSY; +	} +	return 0; +} + +static int +nfp_bpf_parse_cap_adjust_head(struct nfp_app_bpf *bpf, void __iomem *value, +			      u32 length) +{ +	struct nfp_bpf_cap_tlv_adjust_head __iomem *cap = value; +	struct nfp_cpp *cpp = bpf->app->pf->cpp; + +	if (length < sizeof(*cap)) { +		nfp_err(cpp, "truncated adjust_head TLV: %d\n", length); +		return -EINVAL; +	} + +	bpf->adjust_head.flags = readl(&cap->flags); +	bpf->adjust_head.off_min = readl(&cap->off_min); +	bpf->adjust_head.off_max = readl(&cap->off_max); +	bpf->adjust_head.guaranteed_sub = readl(&cap->guaranteed_sub); +	bpf->adjust_head.guaranteed_add = readl(&cap->guaranteed_add); + +	if (bpf->adjust_head.off_min > bpf->adjust_head.off_max) { +		nfp_err(cpp, "invalid adjust_head TLV: min > max\n"); +		return -EINVAL; +	} +	if (!FIELD_FIT(UR_REG_IMM_MAX, bpf->adjust_head.off_min) || +	    !FIELD_FIT(UR_REG_IMM_MAX, bpf->adjust_head.off_max)) { +		nfp_warn(cpp, "disabling adjust_head - driver expects min/max to fit in as immediates\n"); +		memset(&bpf->adjust_head, 0, sizeof(bpf->adjust_head)); +		return 0; +	} + +	return 0; +} + +static int +nfp_bpf_parse_cap_func(struct nfp_app_bpf *bpf, void __iomem *value, u32 length) +{ +	struct nfp_bpf_cap_tlv_func __iomem *cap = value; + +	if (length < sizeof(*cap)) { +		nfp_err(bpf->app->cpp, "truncated function TLV: %d\n", length); +		return -EINVAL; +	} + +	switch (readl(&cap->func_id)) { +	case BPF_FUNC_map_lookup_elem: +		bpf->helpers.map_lookup = readl(&cap->func_addr); +		break; +	} + +	return 0; +} + +static int +nfp_bpf_parse_cap_maps(struct nfp_app_bpf *bpf, void __iomem *value, u32 length) +{ +	struct nfp_bpf_cap_tlv_maps __iomem *cap = value; + +	if (length < sizeof(*cap)) { +		nfp_err(bpf->app->cpp, "truncated maps TLV: %d\n", length); +		return -EINVAL; +	} + +	bpf->maps.types = readl(&cap->types); +	bpf->maps.max_maps = readl(&cap->max_maps); +	bpf->maps.max_elems = readl(&cap->max_elems); +	bpf->maps.max_key_sz = readl(&cap->max_key_sz); +	bpf->maps.max_val_sz = readl(&cap->max_val_sz); +	bpf->maps.max_elem_sz = readl(&cap->max_elem_sz); + +	return 0; +} + +static int nfp_bpf_parse_capabilities(struct nfp_app *app) +{ +	struct nfp_cpp *cpp = app->pf->cpp; +	struct nfp_cpp_area *area; +	u8 __iomem *mem, *start; + +	mem = nfp_rtsym_map(app->pf->rtbl, "_abi_bpf_capabilities", "bpf.cap", +			    8, &area); +	if (IS_ERR(mem)) +		return PTR_ERR(mem) == -ENOENT ? 0 : PTR_ERR(mem); + +	start = mem; +	while (mem - start + 8 < nfp_cpp_area_size(area)) { +		u8 __iomem *value; +		u32 type, length; + +		type = readl(mem); +		length = readl(mem + 4); +		value = mem + 8; + +		mem += 8 + length; +		if (mem - start > nfp_cpp_area_size(area)) +			goto err_release_free; + +		switch (type) { +		case NFP_BPF_CAP_TYPE_FUNC: +			if (nfp_bpf_parse_cap_func(app->priv, value, length)) +				goto err_release_free; +			break; +		case NFP_BPF_CAP_TYPE_ADJUST_HEAD: +			if (nfp_bpf_parse_cap_adjust_head(app->priv, value, +							  length)) +				goto err_release_free; +			break; +		case NFP_BPF_CAP_TYPE_MAPS: +			if (nfp_bpf_parse_cap_maps(app->priv, value, length)) +				goto err_release_free; +			break; +		default: +			nfp_dbg(cpp, "unknown BPF capability: %d\n", type); +			break; +		} +	} +	if (mem - start != nfp_cpp_area_size(area)) { +		nfp_err(cpp, "BPF capabilities left after parsing, parsed:%zd total length:%zu\n", +			mem - start, nfp_cpp_area_size(area)); +		goto err_release_free; +	} + +	nfp_cpp_area_release_free(area); + +	return 0; + +err_release_free: +	nfp_err(cpp, "invalid BPF capabilities at offset:%zd\n", mem - start); +	nfp_cpp_area_release_free(area); +	return -EINVAL; +} + +static int nfp_bpf_init(struct nfp_app *app) +{ +	struct nfp_app_bpf *bpf; +	int err; + +	bpf = kzalloc(sizeof(*bpf), GFP_KERNEL); +	if (!bpf) +		return -ENOMEM; +	bpf->app = app; +	app->priv = bpf; + +	skb_queue_head_init(&bpf->cmsg_replies); +	init_waitqueue_head(&bpf->cmsg_wq); +	INIT_LIST_HEAD(&bpf->map_list); + +	err = nfp_bpf_parse_capabilities(app); +	if (err) +		goto err_free_bpf; + +	return 0; + +err_free_bpf: +	kfree(bpf); +	return err; +} + +static void nfp_bpf_clean(struct nfp_app *app)  { -	return nn->dp.ctrl & NFP_NET_CFG_CTRL_BPF; +	struct nfp_app_bpf *bpf = app->priv; + +	WARN_ON(!skb_queue_empty(&bpf->cmsg_replies)); +	WARN_ON(!list_empty(&bpf->map_list)); +	WARN_ON(bpf->maps_in_use || bpf->map_elems_in_use); +	kfree(bpf);  }  const struct nfp_app_type app_bpf = {  	.id		= NFP_APP_BPF_NIC,  	.name		= "ebpf", +	.ctrl_cap_mask	= 0, + +	.init		= nfp_bpf_init, +	.clean		= nfp_bpf_clean, + +	.change_mtu	= nfp_bpf_change_mtu, +  	.extra_cap	= nfp_bpf_extra_cap,  	.vnic_alloc	= nfp_bpf_vnic_alloc,  	.vnic_free	= nfp_bpf_vnic_free, +	.ctrl_msg_rx	= nfp_bpf_ctrl_msg_rx, +  	.setup_tc	= nfp_bpf_setup_tc, -	.tc_busy	= nfp_bpf_tc_busy, +	.bpf		= nfp_ndo_bpf,  	.xdp_offload	= nfp_bpf_xdp_offload, - -	.bpf_verifier_prep	= nfp_bpf_verifier_prep, -	.bpf_translate		= nfp_bpf_translate, -	.bpf_destroy		= nfp_bpf_destroy,  }; diff --git a/drivers/net/ethernet/netronome/nfp/bpf/main.h b/drivers/net/ethernet/netronome/nfp/bpf/main.h index 57b6043177a3..054df3dc0698 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/main.h +++ b/drivers/net/ethernet/netronome/nfp/bpf/main.h @@ -1,5 +1,5 @@  /* - * Copyright (C) 2016 Netronome Systems, Inc. + * Copyright (C) 2016-2017 Netronome Systems, Inc.   *   * This software is dual licensed under the GNU General License Version 2,   * June 1991 as shown in the file COPYING in the top-level directory of this @@ -37,22 +37,40 @@  #include <linux/bitfield.h>  #include <linux/bpf.h>  #include <linux/bpf_verifier.h> +#include <linux/kernel.h>  #include <linux/list.h> +#include <linux/skbuff.h>  #include <linux/types.h> +#include <linux/wait.h>  #include "../nfp_asm.h" +#include "fw.h" -/* For branch fixup logic use up-most byte of branch instruction as scratch +/* For relocation logic use up-most byte of branch instruction as scratch   * area.  Remember to clear this before sending instructions to HW!   */ -#define OP_BR_SPECIAL	0xff00000000000000ULL - -enum br_special { -	OP_BR_NORMAL = 0, -	OP_BR_GO_OUT, -	OP_BR_GO_ABORT, +#define OP_RELO_TYPE	0xff00000000000000ULL + +enum nfp_relo_type { +	RELO_NONE = 0, +	/* standard internal jumps */ +	RELO_BR_REL, +	/* internal jumps to parts of the outro */ +	RELO_BR_GO_OUT, +	RELO_BR_GO_ABORT, +	/* external jumps to fixed addresses */ +	RELO_BR_NEXT_PKT, +	RELO_BR_HELPER, +	/* immediate relocation against load address */ +	RELO_IMMED_REL,  }; +/* To make absolute relocated branches (branches other than RELO_BR_REL) + * distinguishable in user space dumps from normal jumps, add a large offset + * to them. + */ +#define BR_OFF_RELO		15000 +  enum static_regs {  	STATIC_REG_IMM		= 21, /* Bank AB */  	STATIC_REG_STACK	= 22, /* Bank A */ @@ -78,6 +96,89 @@ enum pkt_vec {  #define NFP_BPF_ABI_FLAGS	reg_imm(0)  #define   NFP_BPF_ABI_FLAG_MARK	1 +/** + * struct nfp_app_bpf - bpf app priv structure + * @app:		backpointer to the app + * + * @tag_allocator:	bitmap of control message tags in use + * @tag_alloc_next:	next tag bit to allocate + * @tag_alloc_last:	next tag bit to be freed + * + * @cmsg_replies:	received cmsg replies waiting to be consumed + * @cmsg_wq:		work queue for waiting for cmsg replies + * + * @map_list:		list of offloaded maps + * @maps_in_use:	number of currently offloaded maps + * @map_elems_in_use:	number of elements allocated to offloaded maps + * + * @adjust_head:	adjust head capability + * @adjust_head.flags:		extra flags for adjust head + * @adjust_head.off_min:	minimal packet offset within buffer required + * @adjust_head.off_max:	maximum packet offset within buffer required + * @adjust_head.guaranteed_sub:	negative adjustment guaranteed possible + * @adjust_head.guaranteed_add:	positive adjustment guaranteed possible + * + * @maps:		map capability + * @maps.types:			supported map types + * @maps.max_maps:		max number of maps supported + * @maps.max_elems:		max number of entries in each map + * @maps.max_key_sz:		max size of map key + * @maps.max_val_sz:		max size of map value + * @maps.max_elem_sz:		max size of map entry (key + value) + * + * @helpers:		helper addressess for various calls + * @helpers.map_lookup:		map lookup helper address + */ +struct nfp_app_bpf { +	struct nfp_app *app; + +	DECLARE_BITMAP(tag_allocator, U16_MAX + 1); +	u16 tag_alloc_next; +	u16 tag_alloc_last; + +	struct sk_buff_head cmsg_replies; +	struct wait_queue_head cmsg_wq; + +	struct list_head map_list; +	unsigned int maps_in_use; +	unsigned int map_elems_in_use; + +	struct nfp_bpf_cap_adjust_head { +		u32 flags; +		int off_min; +		int off_max; +		int guaranteed_sub; +		int guaranteed_add; +	} adjust_head; + +	struct { +		u32 types; +		u32 max_maps; +		u32 max_elems; +		u32 max_key_sz; +		u32 max_val_sz; +		u32 max_elem_sz; +	} maps; + +	struct { +		u32 map_lookup; +	} helpers; +}; + +/** + * struct nfp_bpf_map - private per-map data attached to BPF maps for offload + * @offmap:	pointer to the offloaded BPF map + * @bpf:	back pointer to bpf app private structure + * @tid:	table id identifying map on datapath + * @l:		link on the nfp_app_bpf->map_list list + */ +struct nfp_bpf_map { +	struct bpf_offloaded_map *offmap; +	struct nfp_app_bpf *bpf; +	u32 tid; +	struct list_head l; +}; +  struct nfp_prog;  struct nfp_insn_meta;  typedef int (*instr_cb_t)(struct nfp_prog *, struct nfp_insn_meta *); @@ -89,23 +190,47 @@ typedef int (*instr_cb_t)(struct nfp_prog *, struct nfp_insn_meta *);  #define nfp_meta_next(meta)	list_next_entry(meta, l)  #define nfp_meta_prev(meta)	list_prev_entry(meta, l) +#define FLAG_INSN_IS_JUMP_DST	BIT(0) +  /**   * struct nfp_insn_meta - BPF instruction wrapper   * @insn: BPF instruction   * @ptr: pointer type for memory operations + * @ldst_gather_len: memcpy length gathered from load/store sequence + * @paired_st: the paired store insn at the head of the sequence   * @ptr_not_const: pointer is not always constant + * @jmp_dst: destination info for jump instructions + * @func_id: function id for call instructions + * @arg1: arg1 for call instructions + * @arg2: arg2 for call instructions + * @arg2_var_off: arg2 changes stack offset on different paths   * @off: index of first generated machine instruction (in nfp_prog.prog)   * @n: eBPF instruction number + * @flags: eBPF instruction extra optimization flags   * @skip: skip this instruction (optimized out)   * @double_cb: callback for second part of the instruction   * @l: link on nfp_prog->insns list   */  struct nfp_insn_meta {  	struct bpf_insn insn; -	struct bpf_reg_state ptr; -	bool ptr_not_const; +	union { +		struct { +			struct bpf_reg_state ptr; +			struct bpf_insn *paired_st; +			s16 ldst_gather_len; +			bool ptr_not_const; +		}; +		struct nfp_insn_meta *jmp_dst; +		struct { +			u32 func_id; +			struct bpf_reg_state arg1; +			struct bpf_reg_state arg2; +			bool arg2_var_off; +		}; +	};  	unsigned int off;  	unsigned short n; +	unsigned short flags;  	bool skip;  	instr_cb_t double_cb; @@ -134,23 +259,36 @@ static inline u8 mbpf_mode(const struct nfp_insn_meta *meta)  	return BPF_MODE(meta->insn.code);  } +static inline bool is_mbpf_load(const struct nfp_insn_meta *meta) +{ +	return (meta->insn.code & ~BPF_SIZE_MASK) == (BPF_LDX | BPF_MEM); +} + +static inline bool is_mbpf_store(const struct nfp_insn_meta *meta) +{ +	return (meta->insn.code & ~BPF_SIZE_MASK) == (BPF_STX | BPF_MEM); +} +  /**   * struct nfp_prog - nfp BPF program + * @bpf: backpointer to the bpf app priv structure   * @prog: machine code   * @prog_len: number of valid instructions in @prog array   * @__prog_alloc_len: alloc size of @prog array   * @verifier_meta: temporary storage for verifier's insn meta   * @type: BPF program type - * @start_off: address of the first instruction in the memory + * @last_bpf_off: address of the last instruction translated from BPF   * @tgt_out: jump target for normal exit   * @tgt_abort: jump target for abort (e.g. access outside of packet buffer) - * @tgt_done: jump target to get the next packet   * @n_translated: number of successfully translated instructions (for errors)   * @error: error code if something went wrong   * @stack_depth: max stack depth from the verifier + * @adjust_head_location: if program has single adjust head call - the insn no.   * @insns: list of BPF instruction wrappers (struct nfp_insn_meta)   */  struct nfp_prog { +	struct nfp_app_bpf *bpf; +  	u64 *prog;  	unsigned int prog_len;  	unsigned int __prog_alloc_len; @@ -159,15 +297,15 @@ struct nfp_prog {  	enum bpf_prog_type type; -	unsigned int start_off; +	unsigned int last_bpf_off;  	unsigned int tgt_out;  	unsigned int tgt_abort; -	unsigned int tgt_done;  	unsigned int n_translated;  	int error;  	unsigned int stack_depth; +	unsigned int adjust_head_location;  	struct list_head insns;  }; @@ -175,26 +313,49 @@ struct nfp_prog {  /**   * struct nfp_bpf_vnic - per-vNIC BPF priv structure   * @tc_prog:	currently loaded cls_bpf program + * @start_off:	address of the first instruction in the memory + * @tgt_done:	jump target to get the next packet   */  struct nfp_bpf_vnic {  	struct bpf_prog *tc_prog; +	unsigned int start_off; +	unsigned int tgt_done;  }; +void nfp_bpf_jit_prepare(struct nfp_prog *nfp_prog, unsigned int cnt);  int nfp_bpf_jit(struct nfp_prog *prog); +bool nfp_bpf_supported_opcode(u8 code); -extern const struct bpf_ext_analyzer_ops nfp_bpf_analyzer_ops; +extern const struct bpf_prog_offload_ops nfp_bpf_analyzer_ops;  struct netdev_bpf;  struct nfp_app;  struct nfp_net; +int nfp_ndo_bpf(struct nfp_app *app, struct nfp_net *nn, +		struct netdev_bpf *bpf);  int nfp_net_bpf_offload(struct nfp_net *nn, struct bpf_prog *prog, -			bool old_prog); - -int nfp_bpf_verifier_prep(struct nfp_app *app, struct nfp_net *nn, -			  struct netdev_bpf *bpf); -int nfp_bpf_translate(struct nfp_app *app, struct nfp_net *nn, -		      struct bpf_prog *prog); -int nfp_bpf_destroy(struct nfp_app *app, struct nfp_net *nn, -		    struct bpf_prog *prog); +			bool old_prog, struct netlink_ext_ack *extack); + +struct nfp_insn_meta * +nfp_bpf_goto_meta(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, +		  unsigned int insn_idx, unsigned int n_insns); + +void *nfp_bpf_relo_for_vnic(struct nfp_prog *nfp_prog, struct nfp_bpf_vnic *bv); + +long long int +nfp_bpf_ctrl_alloc_map(struct nfp_app_bpf *bpf, struct bpf_map *map); +void +nfp_bpf_ctrl_free_map(struct nfp_app_bpf *bpf, struct nfp_bpf_map *nfp_map); +int nfp_bpf_ctrl_getfirst_entry(struct bpf_offloaded_map *offmap, +				void *next_key); +int nfp_bpf_ctrl_update_entry(struct bpf_offloaded_map *offmap, +			      void *key, void *value, u64 flags); +int nfp_bpf_ctrl_del_entry(struct bpf_offloaded_map *offmap, void *key); +int nfp_bpf_ctrl_lookup_entry(struct bpf_offloaded_map *offmap, +			      void *key, void *value); +int nfp_bpf_ctrl_getnext_entry(struct bpf_offloaded_map *offmap, +			       void *key, void *next_key); + +void nfp_bpf_ctrl_msg_rx(struct nfp_app *app, struct sk_buff *skb);  #endif diff --git a/drivers/net/ethernet/netronome/nfp/bpf/offload.c b/drivers/net/ethernet/netronome/nfp/bpf/offload.c index bc879aeb62d4..0a7732385469 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/offload.c +++ b/drivers/net/ethernet/netronome/nfp/bpf/offload.c @@ -1,5 +1,5 @@  /* - * Copyright (C) 2016 Netronome Systems, Inc. + * Copyright (C) 2016-2017 Netronome Systems, Inc.   *   * This software is dual licensed under the GNU General License Version 2,   * June 1991 as shown in the file COPYING in the top-level directory of this @@ -36,18 +36,23 @@   * Netronome network device driver: TC offload functions for PF and VF   */ +#define pr_fmt(fmt)	"NFP net bpf: " fmt + +#include <linux/bpf.h>  #include <linux/kernel.h>  #include <linux/netdevice.h>  #include <linux/pci.h>  #include <linux/jiffies.h>  #include <linux/timer.h>  #include <linux/list.h> +#include <linux/mm.h>  #include <net/pkt_cls.h>  #include <net/tc_act/tc_gact.h>  #include <net/tc_act/tc_mirred.h>  #include "main.h" +#include "../nfp_app.h"  #include "../nfp_net_ctrl.h"  #include "../nfp_net.h" @@ -55,11 +60,10 @@ static int  nfp_prog_prepare(struct nfp_prog *nfp_prog, const struct bpf_insn *prog,  		 unsigned int cnt)  { +	struct nfp_insn_meta *meta;  	unsigned int i;  	for (i = 0; i < cnt; i++) { -		struct nfp_insn_meta *meta; -  		meta = kzalloc(sizeof(*meta), GFP_KERNEL);  		if (!meta)  			return -ENOMEM; @@ -70,6 +74,8 @@ nfp_prog_prepare(struct nfp_prog *nfp_prog, const struct bpf_insn *prog,  		list_add_tail(&meta->l, &nfp_prog->insns);  	} +	nfp_bpf_jit_prepare(nfp_prog, cnt); +  	return 0;  } @@ -84,8 +90,9 @@ static void nfp_prog_free(struct nfp_prog *nfp_prog)  	kfree(nfp_prog);  } -int nfp_bpf_verifier_prep(struct nfp_app *app, struct nfp_net *nn, -			  struct netdev_bpf *bpf) +static int +nfp_bpf_verifier_prep(struct nfp_app *app, struct nfp_net *nn, +		      struct netdev_bpf *bpf)  {  	struct bpf_prog *prog = bpf->verifier.prog;  	struct nfp_prog *nfp_prog; @@ -98,6 +105,7 @@ int nfp_bpf_verifier_prep(struct nfp_app *app, struct nfp_net *nn,  	INIT_LIST_HEAD(&nfp_prog->insns);  	nfp_prog->type = prog->type; +	nfp_prog->bpf = app->priv;  	ret = nfp_prog_prepare(nfp_prog, prog->insnsi, prog->len);  	if (ret) @@ -114,12 +122,12 @@ err_free:  	return ret;  } -int nfp_bpf_translate(struct nfp_app *app, struct nfp_net *nn, -		      struct bpf_prog *prog) +static int nfp_bpf_translate(struct nfp_net *nn, struct bpf_prog *prog)  {  	struct nfp_prog *nfp_prog = prog->aux->offload->dev_priv;  	unsigned int stack_size;  	unsigned int max_instr; +	int err;  	stack_size = nn_readb(nn, NFP_NET_CFG_BPF_STACK_SZ) * 64;  	if (prog->aux->stack_depth > stack_size) { @@ -127,50 +135,179 @@ int nfp_bpf_translate(struct nfp_app *app, struct nfp_net *nn,  			prog->aux->stack_depth, stack_size);  		return -EOPNOTSUPP;  	} - -	nfp_prog->stack_depth = prog->aux->stack_depth; -	nfp_prog->start_off = nn_readw(nn, NFP_NET_CFG_BPF_START); -	nfp_prog->tgt_done = nn_readw(nn, NFP_NET_CFG_BPF_DONE); +	nfp_prog->stack_depth = round_up(prog->aux->stack_depth, 4);  	max_instr = nn_readw(nn, NFP_NET_CFG_BPF_MAX_LEN);  	nfp_prog->__prog_alloc_len = max_instr * sizeof(u64); -	nfp_prog->prog = kmalloc(nfp_prog->__prog_alloc_len, GFP_KERNEL); +	nfp_prog->prog = kvmalloc(nfp_prog->__prog_alloc_len, GFP_KERNEL);  	if (!nfp_prog->prog)  		return -ENOMEM; -	return nfp_bpf_jit(nfp_prog); +	err = nfp_bpf_jit(nfp_prog); +	if (err) +		return err; + +	prog->aux->offload->jited_len = nfp_prog->prog_len * sizeof(u64); +	prog->aux->offload->jited_image = nfp_prog->prog; + +	return 0;  } -int nfp_bpf_destroy(struct nfp_app *app, struct nfp_net *nn, -		    struct bpf_prog *prog) +static int nfp_bpf_destroy(struct nfp_net *nn, struct bpf_prog *prog)  {  	struct nfp_prog *nfp_prog = prog->aux->offload->dev_priv; -	kfree(nfp_prog->prog); +	kvfree(nfp_prog->prog);  	nfp_prog_free(nfp_prog);  	return 0;  } -static int nfp_net_bpf_load(struct nfp_net *nn, struct bpf_prog *prog) +static int +nfp_bpf_map_get_next_key(struct bpf_offloaded_map *offmap, +			 void *key, void *next_key) +{ +	if (!key) +		return nfp_bpf_ctrl_getfirst_entry(offmap, next_key); +	return nfp_bpf_ctrl_getnext_entry(offmap, key, next_key); +} + +static int +nfp_bpf_map_delete_elem(struct bpf_offloaded_map *offmap, void *key) +{ +	if (offmap->map.map_type == BPF_MAP_TYPE_ARRAY) +		return -EINVAL; +	return nfp_bpf_ctrl_del_entry(offmap, key); +} + +static const struct bpf_map_dev_ops nfp_bpf_map_ops = { +	.map_get_next_key	= nfp_bpf_map_get_next_key, +	.map_lookup_elem	= nfp_bpf_ctrl_lookup_entry, +	.map_update_elem	= nfp_bpf_ctrl_update_entry, +	.map_delete_elem	= nfp_bpf_map_delete_elem, +}; + +static int +nfp_bpf_map_alloc(struct nfp_app_bpf *bpf, struct bpf_offloaded_map *offmap) +{ +	struct nfp_bpf_map *nfp_map; +	long long int res; + +	if (!bpf->maps.types) +		return -EOPNOTSUPP; + +	if (offmap->map.map_flags || +	    offmap->map.numa_node != NUMA_NO_NODE) { +		pr_info("map flags are not supported\n"); +		return -EINVAL; +	} + +	if (!(bpf->maps.types & 1 << offmap->map.map_type)) { +		pr_info("map type not supported\n"); +		return -EOPNOTSUPP; +	} +	if (bpf->maps.max_maps == bpf->maps_in_use) { +		pr_info("too many maps for a device\n"); +		return -ENOMEM; +	} +	if (bpf->maps.max_elems - bpf->map_elems_in_use < +	    offmap->map.max_entries) { +		pr_info("map with too many elements: %u, left: %u\n", +			offmap->map.max_entries, +			bpf->maps.max_elems - bpf->map_elems_in_use); +		return -ENOMEM; +	} +	if (offmap->map.key_size > bpf->maps.max_key_sz || +	    offmap->map.value_size > bpf->maps.max_val_sz || +	    round_up(offmap->map.key_size, 8) + +	    round_up(offmap->map.value_size, 8) > bpf->maps.max_elem_sz) { +		pr_info("elements don't fit in device constraints\n"); +		return -ENOMEM; +	} + +	nfp_map = kzalloc(sizeof(*nfp_map), GFP_USER); +	if (!nfp_map) +		return -ENOMEM; + +	offmap->dev_priv = nfp_map; +	nfp_map->offmap = offmap; +	nfp_map->bpf = bpf; + +	res = nfp_bpf_ctrl_alloc_map(bpf, &offmap->map); +	if (res < 0) { +		kfree(nfp_map); +		return res; +	} + +	nfp_map->tid = res; +	offmap->dev_ops = &nfp_bpf_map_ops; +	bpf->maps_in_use++; +	bpf->map_elems_in_use += offmap->map.max_entries; +	list_add_tail(&nfp_map->l, &bpf->map_list); + +	return 0; +} + +static int +nfp_bpf_map_free(struct nfp_app_bpf *bpf, struct bpf_offloaded_map *offmap) +{ +	struct nfp_bpf_map *nfp_map = offmap->dev_priv; + +	nfp_bpf_ctrl_free_map(bpf, nfp_map); +	list_del_init(&nfp_map->l); +	bpf->map_elems_in_use -= offmap->map.max_entries; +	bpf->maps_in_use--; +	kfree(nfp_map); + +	return 0; +} + +int nfp_ndo_bpf(struct nfp_app *app, struct nfp_net *nn, struct netdev_bpf *bpf) +{ +	switch (bpf->command) { +	case BPF_OFFLOAD_VERIFIER_PREP: +		return nfp_bpf_verifier_prep(app, nn, bpf); +	case BPF_OFFLOAD_TRANSLATE: +		return nfp_bpf_translate(nn, bpf->offload.prog); +	case BPF_OFFLOAD_DESTROY: +		return nfp_bpf_destroy(nn, bpf->offload.prog); +	case BPF_OFFLOAD_MAP_ALLOC: +		return nfp_bpf_map_alloc(app->priv, bpf->offmap); +	case BPF_OFFLOAD_MAP_FREE: +		return nfp_bpf_map_free(app->priv, bpf->offmap); +	default: +		return -EINVAL; +	} +} + +static int +nfp_net_bpf_load(struct nfp_net *nn, struct bpf_prog *prog, +		 struct netlink_ext_ack *extack)  {  	struct nfp_prog *nfp_prog = prog->aux->offload->dev_priv;  	unsigned int max_mtu;  	dma_addr_t dma_addr; +	void *img;  	int err;  	max_mtu = nn_readb(nn, NFP_NET_CFG_BPF_INL_MTU) * 64 - 32;  	if (max_mtu < nn->dp.netdev->mtu) { -		nn_info(nn, "BPF offload not supported with MTU larger than HW packet split boundary\n"); +		NL_SET_ERR_MSG_MOD(extack, "BPF offload not supported with MTU larger than HW packet split boundary");  		return -EOPNOTSUPP;  	} -	dma_addr = dma_map_single(nn->dp.dev, nfp_prog->prog, +	img = nfp_bpf_relo_for_vnic(nfp_prog, nn->app_priv); +	if (IS_ERR(img)) +		return PTR_ERR(img); + +	dma_addr = dma_map_single(nn->dp.dev, img,  				  nfp_prog->prog_len * sizeof(u64),  				  DMA_TO_DEVICE); -	if (dma_mapping_error(nn->dp.dev, dma_addr)) +	if (dma_mapping_error(nn->dp.dev, dma_addr)) { +		kfree(img);  		return -ENOMEM; +	}  	nn_writew(nn, NFP_NET_CFG_BPF_SIZE, nfp_prog->prog_len);  	nn_writeq(nn, NFP_NET_CFG_BPF_ADDR, dma_addr); @@ -178,15 +315,18 @@ static int nfp_net_bpf_load(struct nfp_net *nn, struct bpf_prog *prog)  	/* Load up the JITed code */  	err = nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_BPF);  	if (err) -		nn_err(nn, "FW command error while loading BPF: %d\n", err); +		NL_SET_ERR_MSG_MOD(extack, +				   "FW command error while loading BPF");  	dma_unmap_single(nn->dp.dev, dma_addr, nfp_prog->prog_len * sizeof(u64),  			 DMA_TO_DEVICE); +	kfree(img);  	return err;  } -static void nfp_net_bpf_start(struct nfp_net *nn) +static void +nfp_net_bpf_start(struct nfp_net *nn, struct netlink_ext_ack *extack)  {  	int err; @@ -195,7 +335,8 @@ static void nfp_net_bpf_start(struct nfp_net *nn)  	nn_writel(nn, NFP_NET_CFG_CTRL, nn->dp.ctrl);  	err = nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_GEN);  	if (err) -		nn_err(nn, "FW command error while enabling BPF: %d\n", err); +		NL_SET_ERR_MSG_MOD(extack, +				   "FW command error while enabling BPF");  }  static int nfp_net_bpf_stop(struct nfp_net *nn) @@ -210,12 +351,12 @@ static int nfp_net_bpf_stop(struct nfp_net *nn)  }  int nfp_net_bpf_offload(struct nfp_net *nn, struct bpf_prog *prog, -			bool old_prog) +			bool old_prog, struct netlink_ext_ack *extack)  {  	int err;  	if (prog) { -		struct bpf_dev_offload *offload = prog->aux->offload; +		struct bpf_prog_offload *offload = prog->aux->offload;  		if (!offload)  			return -EINVAL; @@ -228,7 +369,8 @@ int nfp_net_bpf_offload(struct nfp_net *nn, struct bpf_prog *prog,  		cap = nn_readb(nn, NFP_NET_CFG_BPF_CAP);  		if (!(cap & NFP_NET_BPF_CAP_RELO)) { -			nn_err(nn, "FW does not support live reload\n"); +			NL_SET_ERR_MSG_MOD(extack, +					   "FW does not support live reload");  			return -EBUSY;  		}  	} @@ -240,12 +382,12 @@ int nfp_net_bpf_offload(struct nfp_net *nn, struct bpf_prog *prog,  	if (old_prog && !prog)  		return nfp_net_bpf_stop(nn); -	err = nfp_net_bpf_load(nn, prog); +	err = nfp_net_bpf_load(nn, prog, extack);  	if (err)  		return err;  	if (!old_prog) -		nfp_net_bpf_start(nn); +		nfp_net_bpf_start(nn, extack);  	return 0;  } diff --git a/drivers/net/ethernet/netronome/nfp/bpf/verifier.c b/drivers/net/ethernet/netronome/nfp/bpf/verifier.c index 8d43491ddd6b..479f602887e9 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/verifier.c +++ b/drivers/net/ethernet/netronome/nfp/bpf/verifier.c @@ -1,5 +1,5 @@  /* - * Copyright (C) 2016 Netronome Systems, Inc. + * Copyright (C) 2016-2017 Netronome Systems, Inc.   *   * This software is dual licensed under the GNU General License Version 2,   * June 1991 as shown in the file COPYING in the top-level directory of this @@ -31,16 +31,18 @@   * SOFTWARE.   */ -#define pr_fmt(fmt)	"NFP net bpf: " fmt -  #include <linux/bpf.h>  #include <linux/bpf_verifier.h>  #include <linux/kernel.h>  #include <linux/pkt_cls.h> +#include "fw.h"  #include "main.h" -static struct nfp_insn_meta * +#define pr_vlog(env, fmt, ...)	\ +	bpf_verifier_log_write(env, "[nfp] " fmt, ##__VA_ARGS__) + +struct nfp_insn_meta *  nfp_bpf_goto_meta(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta,  		  unsigned int insn_idx, unsigned int n_insns)  { @@ -68,6 +70,114 @@ nfp_bpf_goto_meta(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta,  	return meta;  } +static void +nfp_record_adjust_head(struct nfp_app_bpf *bpf, struct nfp_prog *nfp_prog, +		       struct nfp_insn_meta *meta, +		       const struct bpf_reg_state *reg2) +{ +	unsigned int location =	UINT_MAX; +	int imm; + +	/* Datapath usually can give us guarantees on how much adjust head +	 * can be done without the need for any checks.  Optimize the simple +	 * case where there is only one adjust head by a constant. +	 */ +	if (reg2->type != SCALAR_VALUE || !tnum_is_const(reg2->var_off)) +		goto exit_set_location; +	imm = reg2->var_off.value; +	/* Translator will skip all checks, we need to guarantee min pkt len */ +	if (imm > ETH_ZLEN - ETH_HLEN) +		goto exit_set_location; +	if (imm > (int)bpf->adjust_head.guaranteed_add || +	    imm < -bpf->adjust_head.guaranteed_sub) +		goto exit_set_location; + +	if (nfp_prog->adjust_head_location) { +		/* Only one call per program allowed */ +		if (nfp_prog->adjust_head_location != meta->n) +			goto exit_set_location; + +		if (meta->arg2.var_off.value != imm) +			goto exit_set_location; +	} + +	location = meta->n; +exit_set_location: +	nfp_prog->adjust_head_location = location; +} + +static int +nfp_bpf_check_call(struct nfp_prog *nfp_prog, struct bpf_verifier_env *env, +		   struct nfp_insn_meta *meta) +{ +	const struct bpf_reg_state *reg1 = cur_regs(env) + BPF_REG_1; +	const struct bpf_reg_state *reg2 = cur_regs(env) + BPF_REG_2; +	struct nfp_app_bpf *bpf = nfp_prog->bpf; +	u32 func_id = meta->insn.imm; +	s64 off, old_off; + +	switch (func_id) { +	case BPF_FUNC_xdp_adjust_head: +		if (!bpf->adjust_head.off_max) { +			pr_vlog(env, "adjust_head not supported by FW\n"); +			return -EOPNOTSUPP; +		} +		if (!(bpf->adjust_head.flags & NFP_BPF_ADJUST_HEAD_NO_META)) { +			pr_vlog(env, "adjust_head: FW requires shifting metadata, not supported by the driver\n"); +			return -EOPNOTSUPP; +		} + +		nfp_record_adjust_head(bpf, nfp_prog, meta, reg2); +		break; + +	case BPF_FUNC_map_lookup_elem: +		if (!bpf->helpers.map_lookup) { +			pr_vlog(env, "map_lookup: not supported by FW\n"); +			return -EOPNOTSUPP; +		} +		if (reg2->type != PTR_TO_STACK) { +			pr_vlog(env, +				"map_lookup: unsupported key ptr type %d\n", +				reg2->type); +			return -EOPNOTSUPP; +		} +		if (!tnum_is_const(reg2->var_off)) { +			pr_vlog(env, "map_lookup: variable key pointer\n"); +			return -EOPNOTSUPP; +		} + +		off = reg2->var_off.value + reg2->off; +		if (-off % 4) { +			pr_vlog(env, +				"map_lookup: unaligned stack pointer %lld\n", +				-off); +			return -EOPNOTSUPP; +		} + +		/* Rest of the checks is only if we re-parse the same insn */ +		if (!meta->func_id) +			break; + +		old_off = meta->arg2.var_off.value + meta->arg2.off; +		meta->arg2_var_off |= off != old_off; + +		if (meta->arg1.map_ptr != reg1->map_ptr) { +			pr_vlog(env, "map_lookup: called for different map\n"); +			return -EOPNOTSUPP; +		} +		break; +	default: +		pr_vlog(env, "unsupported function id: %d\n", func_id); +		return -EOPNOTSUPP; +	} + +	meta->func_id = func_id; +	meta->arg1 = *reg1; +	meta->arg2 = *reg2; + +	return 0; +} +  static int  nfp_bpf_check_exit(struct nfp_prog *nfp_prog,  		   struct bpf_verifier_env *env) @@ -82,7 +192,7 @@ nfp_bpf_check_exit(struct nfp_prog *nfp_prog,  		char tn_buf[48];  		tnum_strn(tn_buf, sizeof(tn_buf), reg0->var_off); -		pr_info("unsupported exit state: %d, var_off: %s\n", +		pr_vlog(env, "unsupported exit state: %d, var_off: %s\n",  			reg0->type, tn_buf);  		return -EINVAL;  	} @@ -92,7 +202,7 @@ nfp_bpf_check_exit(struct nfp_prog *nfp_prog,  	    imm <= TC_ACT_REDIRECT &&  	    imm != TC_ACT_SHOT && imm != TC_ACT_STOLEN &&  	    imm != TC_ACT_QUEUED) { -		pr_info("unsupported exit state: %d, imm: %llx\n", +		pr_vlog(env, "unsupported exit state: %d, imm: %llx\n",  			reg0->type, imm);  		return -EINVAL;  	} @@ -103,12 +213,13 @@ nfp_bpf_check_exit(struct nfp_prog *nfp_prog,  static int  nfp_bpf_check_stack_access(struct nfp_prog *nfp_prog,  			   struct nfp_insn_meta *meta, -			   const struct bpf_reg_state *reg) +			   const struct bpf_reg_state *reg, +			   struct bpf_verifier_env *env)  {  	s32 old_off, new_off;  	if (!tnum_is_const(reg->var_off)) { -		pr_info("variable ptr stack access\n"); +		pr_vlog(env, "variable ptr stack access\n");  		return -EINVAL;  	} @@ -126,7 +237,7 @@ nfp_bpf_check_stack_access(struct nfp_prog *nfp_prog,  	if (old_off % 4 == new_off % 4)  		return 0; -	pr_info("stack access changed location was:%d is:%d\n", +	pr_vlog(env, "stack access changed location was:%d is:%d\n",  		old_off, new_off);  	return -EINVAL;  } @@ -140,19 +251,27 @@ nfp_bpf_check_ptr(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta,  	if (reg->type != PTR_TO_CTX &&  	    reg->type != PTR_TO_STACK && +	    reg->type != PTR_TO_MAP_VALUE &&  	    reg->type != PTR_TO_PACKET) { -		pr_info("unsupported ptr type: %d\n", reg->type); +		pr_vlog(env, "unsupported ptr type: %d\n", reg->type);  		return -EINVAL;  	}  	if (reg->type == PTR_TO_STACK) { -		err = nfp_bpf_check_stack_access(nfp_prog, meta, reg); +		err = nfp_bpf_check_stack_access(nfp_prog, meta, reg, env);  		if (err)  			return err;  	} +	if (reg->type == PTR_TO_MAP_VALUE) { +		if (is_mbpf_store(meta)) { +			pr_vlog(env, "map writes not supported\n"); +			return -EOPNOTSUPP; +		} +	} +  	if (meta->ptr.type != NOT_INIT && meta->ptr.type != reg->type) { -		pr_info("ptr type changed for instruction %d -> %d\n", +		pr_vlog(env, "ptr type changed for instruction %d -> %d\n",  			meta->ptr.type, reg->type);  		return -EINVAL;  	} @@ -171,25 +290,33 @@ nfp_verify_insn(struct bpf_verifier_env *env, int insn_idx, int prev_insn_idx)  	meta = nfp_bpf_goto_meta(nfp_prog, meta, insn_idx, env->prog->len);  	nfp_prog->verifier_meta = meta; +	if (!nfp_bpf_supported_opcode(meta->insn.code)) { +		pr_vlog(env, "instruction %#02x not supported\n", +			meta->insn.code); +		return -EINVAL; +	} +  	if (meta->insn.src_reg >= MAX_BPF_REG ||  	    meta->insn.dst_reg >= MAX_BPF_REG) { -		pr_err("program uses extended registers - jit hardening?\n"); +		pr_vlog(env, "program uses extended registers - jit hardening?\n");  		return -EINVAL;  	} +	if (meta->insn.code == (BPF_JMP | BPF_CALL)) +		return nfp_bpf_check_call(nfp_prog, env, meta);  	if (meta->insn.code == (BPF_JMP | BPF_EXIT))  		return nfp_bpf_check_exit(nfp_prog, env); -	if ((meta->insn.code & ~BPF_SIZE_MASK) == (BPF_LDX | BPF_MEM)) +	if (is_mbpf_load(meta))  		return nfp_bpf_check_ptr(nfp_prog, meta, env,  					 meta->insn.src_reg); -	if ((meta->insn.code & ~BPF_SIZE_MASK) == (BPF_STX | BPF_MEM)) +	if (is_mbpf_store(meta))  		return nfp_bpf_check_ptr(nfp_prog, meta, env,  					 meta->insn.dst_reg);  	return 0;  } -const struct bpf_ext_analyzer_ops nfp_bpf_analyzer_ops = { +const struct bpf_prog_offload_ops nfp_bpf_analyzer_ops = {  	.insn_hook = nfp_verify_insn,  }; diff --git a/drivers/net/ethernet/netronome/nfp/flower/action.c b/drivers/net/ethernet/netronome/nfp/flower/action.c index c1c595f8bb87..b3567a596fc1 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/action.c +++ b/drivers/net/ethernet/netronome/nfp/flower/action.c @@ -81,6 +81,9 @@ static bool nfp_fl_netdev_is_tunnel_type(struct net_device *out_dev,  	if (!strcmp(out_dev->rtnl_link_ops->kind, "vxlan"))  		return tun_type == NFP_FL_TUNNEL_VXLAN; +	if (!strcmp(out_dev->rtnl_link_ops->kind, "geneve")) +		return tun_type == NFP_FL_TUNNEL_GENEVE; +  	return false;  } @@ -93,13 +96,11 @@ nfp_fl_output(struct nfp_fl_output *output, const struct tc_action *action,  	size_t act_size = sizeof(struct nfp_fl_output);  	struct net_device *out_dev;  	u16 tmp_flags; -	int ifindex;  	output->head.jump_id = NFP_FL_ACTION_OPCODE_OUTPUT;  	output->head.len_lw = act_size >> NFP_FL_LW_SIZ; -	ifindex = tcf_mirred_ifindex(action); -	out_dev = __dev_get_by_index(dev_net(in_dev), ifindex); +	out_dev = tcf_mirred_dev(action);  	if (!out_dev)  		return -EOPNOTSUPP; @@ -138,11 +139,23 @@ nfp_fl_output(struct nfp_fl_output *output, const struct tc_action *action,  	return 0;  } -static bool nfp_fl_supported_tun_port(const struct tc_action *action) +static enum nfp_flower_tun_type +nfp_fl_get_tun_from_act_l4_port(struct nfp_app *app, +				const struct tc_action *action)  {  	struct ip_tunnel_info *tun = tcf_tunnel_info(action); - -	return tun->key.tp_dst == htons(NFP_FL_VXLAN_PORT); +	struct nfp_flower_priv *priv = app->priv; + +	switch (tun->key.tp_dst) { +	case htons(NFP_FL_VXLAN_PORT): +		return NFP_FL_TUNNEL_VXLAN; +	case htons(NFP_FL_GENEVE_PORT): +		if (priv->flower_ext_feats & NFP_FL_FEATS_GENEVE) +			return NFP_FL_TUNNEL_GENEVE; +		/* FALLTHROUGH */ +	default: +		return NFP_FL_TUNNEL_NONE; +	}  }  static struct nfp_fl_pre_tunnel *nfp_fl_pre_tunnel(char *act_data, int act_len) @@ -167,38 +180,33 @@ static struct nfp_fl_pre_tunnel *nfp_fl_pre_tunnel(char *act_data, int act_len)  }  static int -nfp_fl_set_vxlan(struct nfp_fl_set_vxlan *set_vxlan, -		 const struct tc_action *action, -		 struct nfp_fl_pre_tunnel *pre_tun) +nfp_fl_set_ipv4_udp_tun(struct nfp_fl_set_ipv4_udp_tun *set_tun, +			const struct tc_action *action, +			struct nfp_fl_pre_tunnel *pre_tun, +			enum nfp_flower_tun_type tun_type)  { -	struct ip_tunnel_info *vxlan = tcf_tunnel_info(action); -	size_t act_size = sizeof(struct nfp_fl_set_vxlan); -	u32 tmp_set_vxlan_type_index = 0; +	size_t act_size = sizeof(struct nfp_fl_set_ipv4_udp_tun); +	struct ip_tunnel_info *ip_tun = tcf_tunnel_info(action); +	u32 tmp_set_ip_tun_type_index = 0;  	/* Currently support one pre-tunnel so index is always 0. */  	int pretun_idx = 0; -	if (vxlan->options_len) { -		/* Do not support options e.g. vxlan gpe. */ +	if (ip_tun->options_len)  		return -EOPNOTSUPP; -	} -	set_vxlan->head.jump_id = NFP_FL_ACTION_OPCODE_SET_IPV4_TUNNEL; -	set_vxlan->head.len_lw = act_size >> NFP_FL_LW_SIZ; +	set_tun->head.jump_id = NFP_FL_ACTION_OPCODE_SET_IPV4_TUNNEL; +	set_tun->head.len_lw = act_size >> NFP_FL_LW_SIZ;  	/* Set tunnel type and pre-tunnel index. */ -	tmp_set_vxlan_type_index |= -		FIELD_PREP(NFP_FL_IPV4_TUNNEL_TYPE, NFP_FL_TUNNEL_VXLAN) | +	tmp_set_ip_tun_type_index |= +		FIELD_PREP(NFP_FL_IPV4_TUNNEL_TYPE, tun_type) |  		FIELD_PREP(NFP_FL_IPV4_PRE_TUN_INDEX, pretun_idx); -	set_vxlan->tun_type_index = cpu_to_be32(tmp_set_vxlan_type_index); - -	set_vxlan->tun_id = vxlan->key.tun_id; -	set_vxlan->tun_flags = vxlan->key.tun_flags; -	set_vxlan->ipv4_ttl = vxlan->key.ttl; -	set_vxlan->ipv4_tos = vxlan->key.tos; +	set_tun->tun_type_index = cpu_to_be32(tmp_set_ip_tun_type_index); +	set_tun->tun_id = ip_tun->key.tun_id;  	/* Complete pre_tunnel action. */ -	pre_tun->ipv4_dst = vxlan->key.u.ipv4.dst; +	pre_tun->ipv4_dst = ip_tun->key.u.ipv4.dst;  	return 0;  } @@ -435,8 +443,8 @@ nfp_flower_loop_action(const struct tc_action *a,  		       struct net_device *netdev,  		       enum nfp_flower_tun_type *tun_type, int *tun_out_cnt)  { +	struct nfp_fl_set_ipv4_udp_tun *set_tun;  	struct nfp_fl_pre_tunnel *pre_tun; -	struct nfp_fl_set_vxlan *s_vxl;  	struct nfp_fl_push_vlan *psh_v;  	struct nfp_fl_pop_vlan *pop_v;  	struct nfp_fl_output *output; @@ -484,26 +492,29 @@ nfp_flower_loop_action(const struct tc_action *a,  		nfp_fl_push_vlan(psh_v, a);  		*a_len += sizeof(struct nfp_fl_push_vlan); -	} else if (is_tcf_tunnel_set(a) && nfp_fl_supported_tun_port(a)) { +	} else if (is_tcf_tunnel_set(a)) { +		struct nfp_repr *repr = netdev_priv(netdev); +		*tun_type = nfp_fl_get_tun_from_act_l4_port(repr->app, a); +		if (*tun_type == NFP_FL_TUNNEL_NONE) +			return -EOPNOTSUPP; +  		/* Pre-tunnel action is required for tunnel encap.  		 * This checks for next hop entries on NFP.  		 * If none, the packet falls back before applying other actions.  		 */  		if (*a_len + sizeof(struct nfp_fl_pre_tunnel) + -		    sizeof(struct nfp_fl_set_vxlan) > NFP_FL_MAX_A_SIZ) +		    sizeof(struct nfp_fl_set_ipv4_udp_tun) > NFP_FL_MAX_A_SIZ)  			return -EOPNOTSUPP; -		*tun_type = NFP_FL_TUNNEL_VXLAN;  		pre_tun = nfp_fl_pre_tunnel(nfp_fl->action_data, *a_len);  		nfp_fl->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_NULL);  		*a_len += sizeof(struct nfp_fl_pre_tunnel); -		s_vxl = (struct nfp_fl_set_vxlan *)&nfp_fl->action_data[*a_len]; -		err = nfp_fl_set_vxlan(s_vxl, a, pre_tun); +		set_tun = (void *)&nfp_fl->action_data[*a_len]; +		err = nfp_fl_set_ipv4_udp_tun(set_tun, a, pre_tun, *tun_type);  		if (err)  			return err; - -		*a_len += sizeof(struct nfp_fl_set_vxlan); +		*a_len += sizeof(struct nfp_fl_set_ipv4_udp_tun);  	} else if (is_tcf_tunnel_release(a)) {  		/* Tunnel decap is handled by default so accept action. */  		return 0; diff --git a/drivers/net/ethernet/netronome/nfp/flower/cmsg.c b/drivers/net/ethernet/netronome/nfp/flower/cmsg.c index e98bb9cdb6a3..baaea6f1a9d8 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/cmsg.c +++ b/drivers/net/ethernet/netronome/nfp/flower/cmsg.c @@ -125,6 +125,27 @@ int nfp_flower_cmsg_portmod(struct nfp_repr *repr, bool carrier_ok)  	return 0;  } +int nfp_flower_cmsg_portreify(struct nfp_repr *repr, bool exists) +{ +	struct nfp_flower_cmsg_portreify *msg; +	struct sk_buff *skb; + +	skb = nfp_flower_cmsg_alloc(repr->app, sizeof(*msg), +				    NFP_FLOWER_CMSG_TYPE_PORT_REIFY, +				    GFP_KERNEL); +	if (!skb) +		return -ENOMEM; + +	msg = nfp_flower_cmsg_get_data(skb); +	msg->portnum = cpu_to_be32(repr->dst->u.port_info.port_id); +	msg->reserved = 0; +	msg->info = cpu_to_be16(exists); + +	nfp_ctrl_tx(repr->app->ctrl, skb); + +	return 0; +} +  static void  nfp_flower_cmsg_portmod_rx(struct nfp_app *app, struct sk_buff *skb)  { @@ -161,6 +182,28 @@ nfp_flower_cmsg_portmod_rx(struct nfp_app *app, struct sk_buff *skb)  }  static void +nfp_flower_cmsg_portreify_rx(struct nfp_app *app, struct sk_buff *skb) +{ +	struct nfp_flower_priv *priv = app->priv; +	struct nfp_flower_cmsg_portreify *msg; +	bool exists; + +	msg = nfp_flower_cmsg_get_data(skb); + +	rcu_read_lock(); +	exists = !!nfp_app_repr_get(app, be32_to_cpu(msg->portnum)); +	rcu_read_unlock(); +	if (!exists) { +		nfp_flower_cmsg_warn(app, "ctrl msg for unknown port 0x%08x\n", +				     be32_to_cpu(msg->portnum)); +		return; +	} + +	atomic_inc(&priv->reify_replies); +	wake_up_interruptible(&priv->reify_wait_queue); +} + +static void  nfp_flower_cmsg_process_one_rx(struct nfp_app *app, struct sk_buff *skb)  {  	struct nfp_flower_cmsg_hdr *cmsg_hdr; @@ -168,20 +211,14 @@ nfp_flower_cmsg_process_one_rx(struct nfp_app *app, struct sk_buff *skb)  	cmsg_hdr = nfp_flower_cmsg_get_hdr(skb); -	if (unlikely(cmsg_hdr->version != NFP_FLOWER_CMSG_VER1)) { -		nfp_flower_cmsg_warn(app, "Cannot handle repr control version %u\n", -				     cmsg_hdr->version); -		goto out; -	} -  	type = cmsg_hdr->type;  	switch (type) { +	case NFP_FLOWER_CMSG_TYPE_PORT_REIFY: +		nfp_flower_cmsg_portreify_rx(app, skb); +		break;  	case NFP_FLOWER_CMSG_TYPE_PORT_MOD:  		nfp_flower_cmsg_portmod_rx(app, skb);  		break; -	case NFP_FLOWER_CMSG_TYPE_FLOW_STATS: -		nfp_flower_rx_flow_stats(app, skb); -		break;  	case NFP_FLOWER_CMSG_TYPE_NO_NEIGH:  		nfp_tunnel_request_route(app, skb);  		break; @@ -217,7 +254,23 @@ void nfp_flower_cmsg_process_rx(struct work_struct *work)  void nfp_flower_cmsg_rx(struct nfp_app *app, struct sk_buff *skb)  {  	struct nfp_flower_priv *priv = app->priv; +	struct nfp_flower_cmsg_hdr *cmsg_hdr; + +	cmsg_hdr = nfp_flower_cmsg_get_hdr(skb); -	skb_queue_tail(&priv->cmsg_skbs, skb); -	schedule_work(&priv->cmsg_work); +	if (unlikely(cmsg_hdr->version != NFP_FLOWER_CMSG_VER1)) { +		nfp_flower_cmsg_warn(app, "Cannot handle repr control version %u\n", +				     cmsg_hdr->version); +		dev_kfree_skb_any(skb); +		return; +	} + +	if (cmsg_hdr->type == NFP_FLOWER_CMSG_TYPE_FLOW_STATS) { +		/* We need to deal with stats updates from HW asap */ +		nfp_flower_rx_flow_stats(app, skb); +		dev_consume_skb_any(skb); +	} else { +		skb_queue_tail(&priv->cmsg_skbs, skb); +		schedule_work(&priv->cmsg_work); +	}  } diff --git a/drivers/net/ethernet/netronome/nfp/flower/cmsg.h b/drivers/net/ethernet/netronome/nfp/flower/cmsg.h index 66070741d55f..adfe474c2cf0 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/cmsg.h +++ b/drivers/net/ethernet/netronome/nfp/flower/cmsg.h @@ -41,7 +41,7 @@  #include "../nfp_app.h"  #include "../nfpcore/nfp_cpp.h" -#define NFP_FLOWER_LAYER_META		BIT(0) +#define NFP_FLOWER_LAYER_EXT_META	BIT(0)  #define NFP_FLOWER_LAYER_PORT		BIT(1)  #define NFP_FLOWER_LAYER_MAC		BIT(2)  #define NFP_FLOWER_LAYER_TP		BIT(3) @@ -50,8 +50,7 @@  #define NFP_FLOWER_LAYER_CT		BIT(6)  #define NFP_FLOWER_LAYER_VXLAN		BIT(7) -#define NFP_FLOWER_LAYER_ETHER		BIT(3) -#define NFP_FLOWER_LAYER_ARP		BIT(4) +#define NFP_FLOWER_LAYER2_GENEVE	BIT(5)  #define NFP_FLOWER_MASK_VLAN_PRIO	GENMASK(15, 13)  #define NFP_FLOWER_MASK_VLAN_CFI	BIT(12) @@ -108,6 +107,7 @@  enum nfp_flower_tun_type {  	NFP_FL_TUNNEL_NONE =	0,  	NFP_FL_TUNNEL_VXLAN =	2, +	NFP_FL_TUNNEL_GENEVE =	4,  };  struct nfp_fl_act_head { @@ -165,20 +165,6 @@ struct nfp_fl_pop_vlan {  	__be16 reserved;  }; -/* Metadata without L2 (1W/4B) - * ---------------------------------------------------------------- - *    3                   2                   1 - *  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * |  key_layers   |    mask_id    |           reserved            | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ -struct nfp_flower_meta_one { -	u8 nfp_flow_key_layer; -	u8 mask_id; -	u16 reserved; -}; -  struct nfp_fl_pre_tunnel {  	struct nfp_fl_act_head head;  	__be16 reserved; @@ -187,16 +173,13 @@ struct nfp_fl_pre_tunnel {  	__be32 extra[3];  }; -struct nfp_fl_set_vxlan { +struct nfp_fl_set_ipv4_udp_tun {  	struct nfp_fl_act_head head;  	__be16 reserved; -	__be64 tun_id; +	__be64 tun_id __packed;  	__be32 tun_type_index; -	__be16 tun_flags; -	u8 ipv4_ttl; -	u8 ipv4_tos; -	__be32 extra[2]; -} __packed; +	__be32 extra[3]; +};  /* Metadata with L2 (1W/4B)   * ---------------------------------------------------------------- @@ -209,12 +192,24 @@ struct nfp_fl_set_vxlan {   *                           NOTE: |             TCI               |   *                                 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+   */ -struct nfp_flower_meta_two { +struct nfp_flower_meta_tci {  	u8 nfp_flow_key_layer;  	u8 mask_id;  	__be16 tci;  }; +/* Extended metadata for additional key_layers (1W/4B) + * ---------------------------------------------------------------- + *    3                   2                   1 + *  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |                      nfp_flow_key_layer2                      | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +struct nfp_flower_ext_meta { +	__be32 nfp_flow_key_layer2; +}; +  /* Port details (1W/4B)   * ----------------------------------------------------------------   *    3                   2                   1 @@ -313,7 +308,7 @@ struct nfp_flower_ipv6 {  	struct in6_addr ipv6_dst;  }; -/* Flow Frame VXLAN --> Tunnel details (4W/16B) +/* Flow Frame IPv4 UDP TUNNEL --> Tunnel details (4W/16B)   * -----------------------------------------------------------------   *    3                   2                   1   *  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 @@ -322,22 +317,17 @@ struct nfp_flower_ipv6 {   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+   * |                         ipv4_addr_dst                         |   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * |           tun_flags           |       tos     |       ttl     | + * |                            Reserved                           |   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * |   gpe_flags   |            Reserved           | Next Protocol | + * |                            Reserved                           |   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+   * |                     VNI                       |   Reserved    |   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+   */ -struct nfp_flower_vxlan { +struct nfp_flower_ipv4_udp_tun {  	__be32 ip_src;  	__be32 ip_dst; -	__be16 tun_flags; -	u8 tos; -	u8 ttl; -	u8 gpe_flags; -	u8 reserved[2]; -	u8 nxt_proto; +	__be32 reserved[2];  	__be32 tun_id;  }; @@ -360,6 +350,7 @@ struct nfp_flower_cmsg_hdr {  enum nfp_flower_cmsg_type_port {  	NFP_FLOWER_CMSG_TYPE_FLOW_ADD =		0,  	NFP_FLOWER_CMSG_TYPE_FLOW_DEL =		2, +	NFP_FLOWER_CMSG_TYPE_PORT_REIFY =	6,  	NFP_FLOWER_CMSG_TYPE_MAC_REPR =		7,  	NFP_FLOWER_CMSG_TYPE_PORT_MOD =		8,  	NFP_FLOWER_CMSG_TYPE_NO_NEIGH =		10, @@ -396,6 +387,15 @@ struct nfp_flower_cmsg_portmod {  #define NFP_FLOWER_CMSG_PORTMOD_INFO_LINK	BIT(0) +/* NFP_FLOWER_CMSG_TYPE_PORT_REIFY */ +struct nfp_flower_cmsg_portreify { +	__be32 portnum; +	u16 reserved; +	__be16 info; +}; + +#define NFP_FLOWER_CMSG_PORTREIFY_INFO_EXIST	BIT(0) +  enum nfp_flower_cmsg_port_type {  	NFP_FLOWER_CMSG_PORT_TYPE_UNSPEC =	0x0,  	NFP_FLOWER_CMSG_PORT_TYPE_PHYS_PORT =	0x1, @@ -454,6 +454,7 @@ nfp_flower_cmsg_mac_repr_add(struct sk_buff *skb, unsigned int idx,  			     unsigned int nbi, unsigned int nbi_port,  			     unsigned int phys_port);  int nfp_flower_cmsg_portmod(struct nfp_repr *repr, bool carrier_ok); +int nfp_flower_cmsg_portreify(struct nfp_repr *repr, bool exists);  void nfp_flower_cmsg_process_rx(struct work_struct *work);  void nfp_flower_cmsg_rx(struct nfp_app *app, struct sk_buff *skb);  struct sk_buff * diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.c b/drivers/net/ethernet/netronome/nfp/flower/main.c index 8fcc90c0d2d3..742d6f1575b5 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/main.c +++ b/drivers/net/ethernet/netronome/nfp/flower/main.c @@ -32,6 +32,7 @@   */  #include <linux/etherdevice.h> +#include <linux/lockdep.h>  #include <linux/pci.h>  #include <linux/skbuff.h>  #include <linux/vmalloc.h> @@ -98,7 +99,57 @@ nfp_flower_repr_get(struct nfp_app *app, u32 port_id)  	if (port >= reprs->num_reprs)  		return NULL; -	return reprs->reprs[port]; +	return rcu_dereference(reprs->reprs[port]); +} + +static int +nfp_flower_reprs_reify(struct nfp_app *app, enum nfp_repr_type type, +		       bool exists) +{ +	struct nfp_reprs *reprs; +	int i, err, count = 0; + +	reprs = rcu_dereference_protected(app->reprs[type], +					  lockdep_is_held(&app->pf->lock)); +	if (!reprs) +		return 0; + +	for (i = 0; i < reprs->num_reprs; i++) { +		struct net_device *netdev; + +		netdev = nfp_repr_get_locked(app, reprs, i); +		if (netdev) { +			struct nfp_repr *repr = netdev_priv(netdev); + +			err = nfp_flower_cmsg_portreify(repr, exists); +			if (err) +				return err; +			count++; +		} +	} + +	return count; +} + +static int +nfp_flower_wait_repr_reify(struct nfp_app *app, atomic_t *replies, int tot_repl) +{ +	struct nfp_flower_priv *priv = app->priv; +	int err; + +	if (!tot_repl) +		return 0; + +	lockdep_assert_held(&app->pf->lock); +	err = wait_event_interruptible_timeout(priv->reify_wait_queue, +					       atomic_read(replies) >= tot_repl, +					       msecs_to_jiffies(10)); +	if (err <= 0) { +		nfp_warn(app->cpp, "Not all reprs responded to reify\n"); +		return -EIO; +	} + +	return 0;  }  static int @@ -110,7 +161,6 @@ nfp_flower_repr_netdev_open(struct nfp_app *app, struct nfp_repr *repr)  	if (err)  		return err; -	netif_carrier_on(repr->netdev);  	netif_tx_wake_all_queues(repr->netdev);  	return 0; @@ -119,7 +169,6 @@ nfp_flower_repr_netdev_open(struct nfp_app *app, struct nfp_repr *repr)  static int  nfp_flower_repr_netdev_stop(struct nfp_app *app, struct nfp_repr *repr)  { -	netif_carrier_off(repr->netdev);  	netif_tx_disable(repr->netdev);  	return nfp_flower_cmsg_portmod(repr, false); @@ -140,6 +189,24 @@ nfp_flower_repr_netdev_clean(struct nfp_app *app, struct net_device *netdev)  				     netdev_priv(netdev));  } +static void +nfp_flower_repr_netdev_preclean(struct nfp_app *app, struct net_device *netdev) +{ +	struct nfp_repr *repr = netdev_priv(netdev); +	struct nfp_flower_priv *priv = app->priv; +	atomic_t *replies = &priv->reify_replies; +	int err; + +	atomic_set(replies, 0); +	err = nfp_flower_cmsg_portreify(repr, false); +	if (err) { +		nfp_warn(app->cpp, "Failed to notify firmware about repr destruction\n"); +		return; +	} + +	nfp_flower_wait_repr_reify(app, replies, 1); +} +  static void nfp_flower_sriov_disable(struct nfp_app *app)  {  	struct nfp_flower_priv *priv = app->priv; @@ -157,10 +224,11 @@ nfp_flower_spawn_vnic_reprs(struct nfp_app *app,  {  	u8 nfp_pcie = nfp_cppcore_pcie_unit(app->pf->cpp);  	struct nfp_flower_priv *priv = app->priv; +	atomic_t *replies = &priv->reify_replies;  	enum nfp_port_type port_type;  	struct nfp_reprs *reprs; +	int i, err, reify_cnt;  	const u8 queue = 0; -	int i, err;  	port_type = repr_type == NFP_REPR_TYPE_PF ? NFP_PORT_PF_PORT :  						    NFP_PORT_VF_PORT; @@ -170,19 +238,21 @@ nfp_flower_spawn_vnic_reprs(struct nfp_app *app,  		return -ENOMEM;  	for (i = 0; i < cnt; i++) { +		struct net_device *repr;  		struct nfp_port *port;  		u32 port_id; -		reprs->reprs[i] = nfp_repr_alloc(app); -		if (!reprs->reprs[i]) { +		repr = nfp_repr_alloc(app); +		if (!repr) {  			err = -ENOMEM;  			goto err_reprs_clean;  		} +		RCU_INIT_POINTER(reprs->reprs[i], repr);  		/* For now we only support 1 PF */  		WARN_ON(repr_type == NFP_REPR_TYPE_PF && i); -		port = nfp_port_alloc(app, port_type, reprs->reprs[i]); +		port = nfp_port_alloc(app, port_type, repr);  		if (repr_type == NFP_REPR_TYPE_PF) {  			port->pf_id = i;  			port->vnic = priv->nn->dp.ctrl_bar; @@ -193,11 +263,11 @@ nfp_flower_spawn_vnic_reprs(struct nfp_app *app,  				app->pf->vf_cfg_mem + i * NFP_NET_CFG_BAR_SZ;  		} -		eth_hw_addr_random(reprs->reprs[i]); +		eth_hw_addr_random(repr);  		port_id = nfp_flower_cmsg_pcie_port(nfp_pcie, vnic_type,  						    i, queue); -		err = nfp_repr_init(app, reprs->reprs[i], +		err = nfp_repr_init(app, repr,  				    port_id, port, priv->nn->dp.netdev);  		if (err) {  			nfp_port_free(port); @@ -206,14 +276,28 @@ nfp_flower_spawn_vnic_reprs(struct nfp_app *app,  		nfp_info(app->cpp, "%s%d Representor(%s) created\n",  			 repr_type == NFP_REPR_TYPE_PF ? "PF" : "VF", i, -			 reprs->reprs[i]->name); +			 repr->name);  	}  	nfp_app_reprs_set(app, repr_type, reprs); +	atomic_set(replies, 0); +	reify_cnt = nfp_flower_reprs_reify(app, repr_type, true); +	if (reify_cnt < 0) { +		err = reify_cnt; +		nfp_warn(app->cpp, "Failed to notify firmware about repr creation\n"); +		goto err_reprs_remove; +	} + +	err = nfp_flower_wait_repr_reify(app, replies, reify_cnt); +	if (err) +		goto err_reprs_remove; +  	return 0; +err_reprs_remove: +	reprs = nfp_app_reprs_set(app, repr_type, NULL);  err_reprs_clean: -	nfp_reprs_clean_and_free(reprs); +	nfp_reprs_clean_and_free(app, reprs);  	return err;  } @@ -233,10 +317,11 @@ static int  nfp_flower_spawn_phy_reprs(struct nfp_app *app, struct nfp_flower_priv *priv)  {  	struct nfp_eth_table *eth_tbl = app->pf->eth_tbl; +	atomic_t *replies = &priv->reify_replies;  	struct sk_buff *ctrl_skb;  	struct nfp_reprs *reprs; +	int err, reify_cnt;  	unsigned int i; -	int err;  	ctrl_skb = nfp_flower_cmsg_mac_repr_start(app, eth_tbl->count);  	if (!ctrl_skb) @@ -250,17 +335,18 @@ nfp_flower_spawn_phy_reprs(struct nfp_app *app, struct nfp_flower_priv *priv)  	for (i = 0; i < eth_tbl->count; i++) {  		unsigned int phys_port = eth_tbl->ports[i].index; +		struct net_device *repr;  		struct nfp_port *port;  		u32 cmsg_port_id; -		reprs->reprs[phys_port] = nfp_repr_alloc(app); -		if (!reprs->reprs[phys_port]) { +		repr = nfp_repr_alloc(app); +		if (!repr) {  			err = -ENOMEM;  			goto err_reprs_clean;  		} +		RCU_INIT_POINTER(reprs->reprs[phys_port], repr); -		port = nfp_port_alloc(app, NFP_PORT_PHYS_PORT, -				      reprs->reprs[phys_port]); +		port = nfp_port_alloc(app, NFP_PORT_PHYS_PORT, repr);  		if (IS_ERR(port)) {  			err = PTR_ERR(port);  			goto err_reprs_clean; @@ -271,11 +357,11 @@ nfp_flower_spawn_phy_reprs(struct nfp_app *app, struct nfp_flower_priv *priv)  			goto err_reprs_clean;  		} -		SET_NETDEV_DEV(reprs->reprs[phys_port], &priv->nn->pdev->dev); +		SET_NETDEV_DEV(repr, &priv->nn->pdev->dev);  		nfp_net_get_mac_addr(app->pf, port);  		cmsg_port_id = nfp_flower_cmsg_phys_port(phys_port); -		err = nfp_repr_init(app, reprs->reprs[phys_port], +		err = nfp_repr_init(app, repr,  				    cmsg_port_id, port, priv->nn->dp.netdev);  		if (err) {  			nfp_port_free(port); @@ -288,23 +374,37 @@ nfp_flower_spawn_phy_reprs(struct nfp_app *app, struct nfp_flower_priv *priv)  					     phys_port);  		nfp_info(app->cpp, "Phys Port %d Representor(%s) created\n", -			 phys_port, reprs->reprs[phys_port]->name); +			 phys_port, repr->name);  	}  	nfp_app_reprs_set(app, NFP_REPR_TYPE_PHYS_PORT, reprs); -	/* The MAC_REPR control message should be sent after the MAC +	/* The REIFY/MAC_REPR control messages should be sent after the MAC  	 * representors are registered using nfp_app_reprs_set().  This is  	 * because the firmware may respond with control messages for the  	 * MAC representors, f.e. to provide the driver with information  	 * about their state, and without registration the driver will drop  	 * any such messages.  	 */ +	atomic_set(replies, 0); +	reify_cnt = nfp_flower_reprs_reify(app, NFP_REPR_TYPE_PHYS_PORT, true); +	if (reify_cnt < 0) { +		err = reify_cnt; +		nfp_warn(app->cpp, "Failed to notify firmware about repr creation\n"); +		goto err_reprs_remove; +	} + +	err = nfp_flower_wait_repr_reify(app, replies, reify_cnt); +	if (err) +		goto err_reprs_remove; +  	nfp_ctrl_tx(app->ctrl, ctrl_skb);  	return 0; +err_reprs_remove: +	reprs = nfp_app_reprs_set(app, NFP_REPR_TYPE_PHYS_PORT, NULL);  err_reprs_clean: -	nfp_reprs_clean_and_free(reprs); +	nfp_reprs_clean_and_free(app, reprs);  err_free_ctrl_skb:  	kfree_skb(ctrl_skb);  	return err; @@ -381,7 +481,7 @@ static int nfp_flower_init(struct nfp_app *app)  {  	const struct nfp_pf *pf = app->pf;  	struct nfp_flower_priv *app_priv; -	u64 version; +	u64 version, features;  	int err;  	if (!pf->eth_tbl) { @@ -419,11 +519,20 @@ static int nfp_flower_init(struct nfp_app *app)  	app_priv->app = app;  	skb_queue_head_init(&app_priv->cmsg_skbs);  	INIT_WORK(&app_priv->cmsg_work, nfp_flower_cmsg_process_rx); +	init_waitqueue_head(&app_priv->reify_wait_queue);  	err = nfp_flower_metadata_init(app);  	if (err)  		goto err_free_app_priv; +	/* Extract the extra features supported by the firmware. */ +	features = nfp_rtsym_read_le(app->pf->rtbl, +				     "_abi_flower_extra_features", &err); +	if (err) +		app_priv->flower_ext_feats = 0; +	else +		app_priv->flower_ext_feats = features; +  	return 0;  err_free_app_priv: @@ -456,6 +565,8 @@ static void nfp_flower_stop(struct nfp_app *app)  const struct nfp_app_type app_flower = {  	.id		= NFP_APP_FLOWER_NIC,  	.name		= "flower", + +	.ctrl_cap_mask	= ~0U,  	.ctrl_has_meta	= true,  	.extra_cap	= nfp_flower_extra_cap, @@ -468,6 +579,7 @@ const struct nfp_app_type app_flower = {  	.vnic_clean	= nfp_flower_vnic_clean,  	.repr_init	= nfp_flower_repr_netdev_init, +	.repr_preclean	= nfp_flower_repr_netdev_preclean,  	.repr_clean	= nfp_flower_repr_netdev_clean,  	.repr_open	= nfp_flower_repr_netdev_open, diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.h b/drivers/net/ethernet/netronome/nfp/flower/main.h index e6b26c5ae6e0..332ff0fdc038 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/main.h +++ b/drivers/net/ethernet/netronome/nfp/flower/main.h @@ -34,6 +34,8 @@  #ifndef __NFP_FLOWER_H__  #define __NFP_FLOWER_H__ 1 +#include "cmsg.h" +  #include <linux/circ_buf.h>  #include <linux/hashtable.h>  #include <linux/time64.h> @@ -58,6 +60,10 @@ struct nfp_app;  #define NFP_FL_MASK_ID_LOCATION		1  #define NFP_FL_VXLAN_PORT		4789 +#define NFP_FL_GENEVE_PORT		6081 + +/* Extra features bitmap. */ +#define NFP_FL_FEATS_GENEVE		BIT(0)  struct nfp_fl_mask_id {  	struct circ_buf mask_id_free_list; @@ -77,6 +83,7 @@ struct nfp_fl_stats_id {   * @nn:			Pointer to vNIC   * @mask_id_seed:	Seed used for mask hash table   * @flower_version:	HW version of flower + * @flower_ext_feats:	Bitmap of extra features the HW supports   * @stats_ids:		List of free stats ids   * @mask_ids:		List of free mask ids   * @mask_table:		Hash table used to store masks @@ -95,12 +102,16 @@ struct nfp_fl_stats_id {   * @nfp_mac_off_count:	Number of MACs in address list   * @nfp_tun_mac_nb:	Notifier to monitor link state   * @nfp_tun_neigh_nb:	Notifier to monitor neighbour state + * @reify_replies:	atomically stores the number of replies received + *			from firmware for repr reify + * @reify_wait_queue:	wait queue for repr reify response counting   */  struct nfp_flower_priv {  	struct nfp_app *app;  	struct nfp_net *nn;  	u32 mask_id_seed;  	u64 flower_version; +	u64 flower_ext_feats;  	struct nfp_fl_stats_id stats_ids;  	struct nfp_fl_mask_id mask_ids;  	DECLARE_HASHTABLE(mask_table, NFP_FLOWER_MASK_HASH_BITS); @@ -119,6 +130,8 @@ struct nfp_flower_priv {  	int nfp_mac_off_count;  	struct notifier_block nfp_tun_mac_nb;  	struct notifier_block nfp_tun_neigh_nb; +	atomic_t reify_replies; +	wait_queue_head_t reify_wait_queue;  };  struct nfp_fl_key_ls { @@ -172,7 +185,8 @@ int nfp_flower_setup_tc(struct nfp_app *app, struct net_device *netdev,  int nfp_flower_compile_flow_match(struct tc_cls_flower_offload *flow,  				  struct nfp_fl_key_ls *key_ls,  				  struct net_device *netdev, -				  struct nfp_fl_payload *nfp_flow); +				  struct nfp_fl_payload *nfp_flow, +				  enum nfp_flower_tun_type tun_type);  int nfp_flower_compile_action(struct tc_cls_flower_offload *flow,  			      struct net_device *netdev,  			      struct nfp_fl_payload *nfp_flow); diff --git a/drivers/net/ethernet/netronome/nfp/flower/match.c b/drivers/net/ethernet/netronome/nfp/flower/match.c index 60614d4f0e22..37c2ecae2a7a 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/match.c +++ b/drivers/net/ethernet/netronome/nfp/flower/match.c @@ -38,7 +38,7 @@  #include "main.h"  static void -nfp_flower_compile_meta_tci(struct nfp_flower_meta_two *frame, +nfp_flower_compile_meta_tci(struct nfp_flower_meta_tci *frame,  			    struct tc_cls_flower_offload *flow, u8 key_type,  			    bool mask_version)  { @@ -46,7 +46,7 @@ nfp_flower_compile_meta_tci(struct nfp_flower_meta_two *frame,  	struct flow_dissector_key_vlan *flow_vlan;  	u16 tmp_tci; -	memset(frame, 0, sizeof(struct nfp_flower_meta_two)); +	memset(frame, 0, sizeof(struct nfp_flower_meta_tci));  	/* Populate the metadata frame. */  	frame->nfp_flow_key_layer = key_type;  	frame->mask_id = ~0; @@ -68,11 +68,9 @@ nfp_flower_compile_meta_tci(struct nfp_flower_meta_two *frame,  }  static void -nfp_flower_compile_meta(struct nfp_flower_meta_one *frame, u8 key_type) +nfp_flower_compile_ext_meta(struct nfp_flower_ext_meta *frame, u32 key_ext)  { -	frame->nfp_flow_key_layer = key_type; -	frame->mask_id = 0; -	frame->reserved = 0; +	frame->nfp_flow_key_layer2 = cpu_to_be32(key_ext);  }  static int @@ -224,16 +222,15 @@ nfp_flower_compile_ipv6(struct nfp_flower_ipv6 *frame,  }  static void -nfp_flower_compile_vxlan(struct nfp_flower_vxlan *frame, -			 struct tc_cls_flower_offload *flow, -			 bool mask_version, __be32 *tun_dst) +nfp_flower_compile_ipv4_udp_tun(struct nfp_flower_ipv4_udp_tun *frame, +				struct tc_cls_flower_offload *flow, +				bool mask_version)  {  	struct fl_flow_key *target = mask_version ? flow->mask : flow->key; -	struct flow_dissector_key_ipv4_addrs *vxlan_ips; +	struct flow_dissector_key_ipv4_addrs *tun_ips;  	struct flow_dissector_key_keyid *vni; -	/* Wildcard TOS/TTL/GPE_FLAGS/NXT_PROTO for now. */ -	memset(frame, 0, sizeof(struct nfp_flower_vxlan)); +	memset(frame, 0, sizeof(struct nfp_flower_ipv4_udp_tun));  	if (dissector_uses_key(flow->dissector,  			       FLOW_DISSECTOR_KEY_ENC_KEYID)) { @@ -248,80 +245,68 @@ nfp_flower_compile_vxlan(struct nfp_flower_vxlan *frame,  	if (dissector_uses_key(flow->dissector,  			       FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS)) { -		vxlan_ips = +		tun_ips =  		   skb_flow_dissector_target(flow->dissector,  					     FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS,  					     target); -		frame->ip_src = vxlan_ips->src; -		frame->ip_dst = vxlan_ips->dst; -		*tun_dst = vxlan_ips->dst; +		frame->ip_src = tun_ips->src; +		frame->ip_dst = tun_ips->dst;  	}  }  int nfp_flower_compile_flow_match(struct tc_cls_flower_offload *flow,  				  struct nfp_fl_key_ls *key_ls,  				  struct net_device *netdev, -				  struct nfp_fl_payload *nfp_flow) +				  struct nfp_fl_payload *nfp_flow, +				  enum nfp_flower_tun_type tun_type)  { -	enum nfp_flower_tun_type tun_type = NFP_FL_TUNNEL_NONE; -	__be32 tun_dst, tun_dst_mask = 0;  	struct nfp_repr *netdev_repr;  	int err;  	u8 *ext;  	u8 *msk; -	if (key_ls->key_layer & NFP_FLOWER_LAYER_VXLAN) -		tun_type = NFP_FL_TUNNEL_VXLAN; -  	memset(nfp_flow->unmasked_data, 0, key_ls->key_size);  	memset(nfp_flow->mask_data, 0, key_ls->key_size);  	ext = nfp_flow->unmasked_data;  	msk = nfp_flow->mask_data; -	if (NFP_FLOWER_LAYER_PORT & key_ls->key_layer) { -		/* Populate Exact Metadata. */ -		nfp_flower_compile_meta_tci((struct nfp_flower_meta_two *)ext, -					    flow, key_ls->key_layer, false); -		/* Populate Mask Metadata. */ -		nfp_flower_compile_meta_tci((struct nfp_flower_meta_two *)msk, -					    flow, key_ls->key_layer, true); -		ext += sizeof(struct nfp_flower_meta_two); -		msk += sizeof(struct nfp_flower_meta_two); - -		/* Populate Exact Port data. */ -		err = nfp_flower_compile_port((struct nfp_flower_in_port *)ext, -					      nfp_repr_get_port_id(netdev), -					      false, tun_type); -		if (err) -			return err; - -		/* Populate Mask Port Data. */ -		err = nfp_flower_compile_port((struct nfp_flower_in_port *)msk, -					      nfp_repr_get_port_id(netdev), -					      true, tun_type); -		if (err) -			return err; - -		ext += sizeof(struct nfp_flower_in_port); -		msk += sizeof(struct nfp_flower_in_port); -	} else { -		/* Populate Exact Metadata. */ -		nfp_flower_compile_meta((struct nfp_flower_meta_one *)ext, -					key_ls->key_layer); -		/* Populate Mask Metadata. */ -		nfp_flower_compile_meta((struct nfp_flower_meta_one *)msk, -					key_ls->key_layer); -		ext += sizeof(struct nfp_flower_meta_one); -		msk += sizeof(struct nfp_flower_meta_one); -	} -	if (NFP_FLOWER_LAYER_META & key_ls->key_layer) { -		/* Additional Metadata Fields. -		 * Currently unsupported. -		 */ -		return -EOPNOTSUPP; +	/* Populate Exact Metadata. */ +	nfp_flower_compile_meta_tci((struct nfp_flower_meta_tci *)ext, +				    flow, key_ls->key_layer, false); +	/* Populate Mask Metadata. */ +	nfp_flower_compile_meta_tci((struct nfp_flower_meta_tci *)msk, +				    flow, key_ls->key_layer, true); +	ext += sizeof(struct nfp_flower_meta_tci); +	msk += sizeof(struct nfp_flower_meta_tci); + +	/* Populate Extended Metadata if Required. */ +	if (NFP_FLOWER_LAYER_EXT_META & key_ls->key_layer) { +		nfp_flower_compile_ext_meta((struct nfp_flower_ext_meta *)ext, +					    key_ls->key_layer_two); +		nfp_flower_compile_ext_meta((struct nfp_flower_ext_meta *)msk, +					    key_ls->key_layer_two); +		ext += sizeof(struct nfp_flower_ext_meta); +		msk += sizeof(struct nfp_flower_ext_meta);  	} +	/* Populate Exact Port data. */ +	err = nfp_flower_compile_port((struct nfp_flower_in_port *)ext, +				      nfp_repr_get_port_id(netdev), +				      false, tun_type); +	if (err) +		return err; + +	/* Populate Mask Port Data. */ +	err = nfp_flower_compile_port((struct nfp_flower_in_port *)msk, +				      nfp_repr_get_port_id(netdev), +				      true, tun_type); +	if (err) +		return err; + +	ext += sizeof(struct nfp_flower_in_port); +	msk += sizeof(struct nfp_flower_in_port); +  	if (NFP_FLOWER_LAYER_MAC & key_ls->key_layer) {  		/* Populate Exact MAC Data. */  		nfp_flower_compile_mac((struct nfp_flower_mac_mpls *)ext, @@ -366,15 +351,17 @@ int nfp_flower_compile_flow_match(struct tc_cls_flower_offload *flow,  		msk += sizeof(struct nfp_flower_ipv6);  	} -	if (key_ls->key_layer & NFP_FLOWER_LAYER_VXLAN) { +	if (key_ls->key_layer & NFP_FLOWER_LAYER_VXLAN || +	    key_ls->key_layer_two & NFP_FLOWER_LAYER2_GENEVE) { +		__be32 tun_dst; +  		/* Populate Exact VXLAN Data. */ -		nfp_flower_compile_vxlan((struct nfp_flower_vxlan *)ext, -					 flow, false, &tun_dst); +		nfp_flower_compile_ipv4_udp_tun((void *)ext, flow, false);  		/* Populate Mask VXLAN Data. */ -		nfp_flower_compile_vxlan((struct nfp_flower_vxlan *)msk, -					 flow, true, &tun_dst_mask); -		ext += sizeof(struct nfp_flower_vxlan); -		msk += sizeof(struct nfp_flower_vxlan); +		nfp_flower_compile_ipv4_udp_tun((void *)msk, flow, true); +		tun_dst = ((struct nfp_flower_ipv4_udp_tun *)ext)->ip_dst; +		ext += sizeof(struct nfp_flower_ipv4_udp_tun); +		msk += sizeof(struct nfp_flower_ipv4_udp_tun);  		/* Configure tunnel end point MAC. */  		if (nfp_netdev_is_nfp_repr(netdev)) { diff --git a/drivers/net/ethernet/netronome/nfp/flower/offload.c b/drivers/net/ethernet/netronome/nfp/flower/offload.c index 553f94f55dce..eb5c13dea8f5 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/offload.c +++ b/drivers/net/ethernet/netronome/nfp/flower/offload.c @@ -130,12 +130,15 @@ static bool nfp_flower_check_higher_than_mac(struct tc_cls_flower_offload *f)  }  static int -nfp_flower_calculate_key_layers(struct nfp_fl_key_ls *ret_key_ls, +nfp_flower_calculate_key_layers(struct nfp_app *app, +				struct nfp_fl_key_ls *ret_key_ls,  				struct tc_cls_flower_offload *flow, -				bool egress) +				bool egress, +				enum nfp_flower_tun_type *tun_type)  {  	struct flow_dissector_key_basic *mask_basic = NULL;  	struct flow_dissector_key_basic *key_basic = NULL; +	struct nfp_flower_priv *priv = app->priv;  	u32 key_layer_two;  	u8 key_layer;  	int key_size; @@ -150,10 +153,15 @@ nfp_flower_calculate_key_layers(struct nfp_fl_key_ls *ret_key_ls,  		return -EOPNOTSUPP;  	key_layer_two = 0; -	key_layer = NFP_FLOWER_LAYER_PORT | NFP_FLOWER_LAYER_MAC; -	key_size = sizeof(struct nfp_flower_meta_one) + -		   sizeof(struct nfp_flower_in_port) + -		   sizeof(struct nfp_flower_mac_mpls); +	key_layer = NFP_FLOWER_LAYER_PORT; +	key_size = sizeof(struct nfp_flower_meta_tci) + +		   sizeof(struct nfp_flower_in_port); + +	if (dissector_uses_key(flow->dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS) || +	    dissector_uses_key(flow->dissector, FLOW_DISSECTOR_KEY_MPLS)) { +		key_layer |= NFP_FLOWER_LAYER_MAC; +		key_size += sizeof(struct nfp_flower_mac_mpls); +	}  	if (dissector_uses_key(flow->dissector,  			       FLOW_DISSECTOR_KEY_ENC_CONTROL)) { @@ -192,12 +200,27 @@ nfp_flower_calculate_key_layers(struct nfp_fl_key_ls *ret_key_ls,  						  FLOW_DISSECTOR_KEY_ENC_PORTS,  						  flow->key); -		if (mask_enc_ports->dst != cpu_to_be16(~0) || -		    enc_ports->dst != htons(NFP_FL_VXLAN_PORT)) +		if (mask_enc_ports->dst != cpu_to_be16(~0))  			return -EOPNOTSUPP; -		key_layer |= NFP_FLOWER_LAYER_VXLAN; -		key_size += sizeof(struct nfp_flower_vxlan); +		switch (enc_ports->dst) { +		case htons(NFP_FL_VXLAN_PORT): +			*tun_type = NFP_FL_TUNNEL_VXLAN; +			key_layer |= NFP_FLOWER_LAYER_VXLAN; +			key_size += sizeof(struct nfp_flower_ipv4_udp_tun); +			break; +		case htons(NFP_FL_GENEVE_PORT): +			if (!(priv->flower_ext_feats & NFP_FL_FEATS_GENEVE)) +				return -EOPNOTSUPP; +			*tun_type = NFP_FL_TUNNEL_GENEVE; +			key_layer |= NFP_FLOWER_LAYER_EXT_META; +			key_size += sizeof(struct nfp_flower_ext_meta); +			key_layer_two |= NFP_FLOWER_LAYER2_GENEVE; +			key_size += sizeof(struct nfp_flower_ipv4_udp_tun); +			break; +		default: +			return -EOPNOTSUPP; +		}  	} else if (egress) {  		/* Reject non tunnel matches offloaded to egress repr. */  		return -EOPNOTSUPP; @@ -325,6 +348,8 @@ static int  nfp_flower_add_offload(struct nfp_app *app, struct net_device *netdev,  		       struct tc_cls_flower_offload *flow, bool egress)  { +	enum nfp_flower_tun_type tun_type = NFP_FL_TUNNEL_NONE; +	struct nfp_port *port = nfp_port_from_netdev(netdev);  	struct nfp_flower_priv *priv = app->priv;  	struct nfp_fl_payload *flow_pay;  	struct nfp_fl_key_ls *key_layer; @@ -334,7 +359,8 @@ nfp_flower_add_offload(struct nfp_app *app, struct net_device *netdev,  	if (!key_layer)  		return -ENOMEM; -	err = nfp_flower_calculate_key_layers(key_layer, flow, egress); +	err = nfp_flower_calculate_key_layers(app, key_layer, flow, egress, +					      &tun_type);  	if (err)  		goto err_free_key_ls; @@ -344,7 +370,8 @@ nfp_flower_add_offload(struct nfp_app *app, struct net_device *netdev,  		goto err_free_key_ls;  	} -	err = nfp_flower_compile_flow_match(flow, key_layer, netdev, flow_pay); +	err = nfp_flower_compile_flow_match(flow, key_layer, netdev, flow_pay, +					    tun_type);  	if (err)  		goto err_destroy_flow; @@ -364,6 +391,7 @@ nfp_flower_add_offload(struct nfp_app *app, struct net_device *netdev,  	INIT_HLIST_NODE(&flow_pay->link);  	flow_pay->tc_flower_cookie = flow->cookie;  	hash_add_rcu(priv->flow_table, &flow_pay->link, flow->cookie); +	port->tc_offload_cnt++;  	/* Deallocate flow payload when flower rule has been destroyed. */  	kfree(key_layer); @@ -395,6 +423,7 @@ static int  nfp_flower_del_offload(struct nfp_app *app, struct net_device *netdev,  		       struct tc_cls_flower_offload *flow)  { +	struct nfp_port *port = nfp_port_from_netdev(netdev);  	struct nfp_fl_payload *nfp_flow;  	int err; @@ -416,6 +445,7 @@ nfp_flower_del_offload(struct nfp_app *app, struct net_device *netdev,  err_free_flow:  	hash_del_rcu(&nfp_flow->link); +	port->tc_offload_cnt--;  	kfree(nfp_flow->action_data);  	kfree(nfp_flow->mask_data);  	kfree(nfp_flow->unmasked_data); @@ -457,8 +487,7 @@ static int  nfp_flower_repr_offload(struct nfp_app *app, struct net_device *netdev,  			struct tc_cls_flower_offload *flower, bool egress)  { -	if (!eth_proto_is_802_3(flower->common.protocol) || -	    flower->common.chain_index) +	if (!eth_proto_is_802_3(flower->common.protocol))  		return -EOPNOTSUPP;  	switch (flower->command) { @@ -478,7 +507,7 @@ int nfp_flower_setup_tc_egress_cb(enum tc_setup_type type, void *type_data,  {  	struct nfp_repr *repr = cb_priv; -	if (!tc_can_offload(repr->netdev)) +	if (!tc_cls_can_offload_and_chain0(repr->netdev, type_data))  		return -EOPNOTSUPP;  	switch (type) { @@ -495,7 +524,7 @@ static int nfp_flower_setup_tc_block_cb(enum tc_setup_type type,  {  	struct nfp_repr *repr = cb_priv; -	if (!tc_can_offload(repr->netdev)) +	if (!tc_cls_can_offload_and_chain0(repr->netdev, type_data))  		return -EOPNOTSUPP;  	switch (type) { diff --git a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c index b03f22f29612..ec524d97869d 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c +++ b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c @@ -50,9 +50,9 @@   * @seq:		sequence number of the message   * @count:		number of tunnels report in message   * @flags:		options part of the request - * @ipv4:		dest IPv4 address of active route - * @egress_port:	port the encapsulated packet egressed - * @extra:		reserved for future use + * @tun_info.ipv4:		dest IPv4 address of active route + * @tun_info.egress_port:	port the encapsulated packet egressed + * @tun_info.extra:		reserved for future use   * @tun_info:		tunnels that have sent traffic in reported period   */  struct nfp_tun_active_tuns { @@ -132,8 +132,8 @@ struct nfp_ipv4_addr_entry {   * struct nfp_tun_mac_addr - configure MAC address of tunnel EP on NFP   * @reserved:	reserved for future use   * @count:	number of MAC addresses in the message - * @index:	index of MAC address in the lookup table - * @addr:	interface MAC address + * @addresses.index:	index of MAC address in the lookup table + * @addresses.addr:	interface MAC address   * @addresses:	series of MACs to offload   */  struct nfp_tun_mac_addr { diff --git a/drivers/net/ethernet/netronome/nfp/nfp_app.c b/drivers/net/ethernet/netronome/nfp/nfp_app.c index 955a9f44d244..6aedef0ad433 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_app.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_app.c @@ -32,6 +32,8 @@   */  #include <linux/bug.h> +#include <linux/lockdep.h> +#include <linux/rcupdate.h>  #include <linux/skbuff.h>  #include <linux/slab.h> @@ -99,13 +101,19 @@ nfp_app_ctrl_msg_alloc(struct nfp_app *app, unsigned int size, gfp_t priority)  }  struct nfp_reprs * +nfp_reprs_get_locked(struct nfp_app *app, enum nfp_repr_type type) +{ +	return rcu_dereference_protected(app->reprs[type], +					 lockdep_is_held(&app->pf->lock)); +} + +struct nfp_reprs *  nfp_app_reprs_set(struct nfp_app *app, enum nfp_repr_type type,  		  struct nfp_reprs *reprs)  {  	struct nfp_reprs *old; -	old = rcu_dereference_protected(app->reprs[type], -					lockdep_is_held(&app->pf->lock)); +	old = nfp_reprs_get_locked(app, type);  	rcu_assign_pointer(app->reprs[type], reprs);  	return old; @@ -116,7 +124,7 @@ struct nfp_app *nfp_app_alloc(struct nfp_pf *pf, enum nfp_app_id id)  	struct nfp_app *app;  	if (id >= ARRAY_SIZE(apps) || !apps[id]) { -		nfp_err(pf->cpp, "failed to find app with ID 0x%02hhx\n", id); +		nfp_err(pf->cpp, "unknown FW app ID 0x%02hhx, driver too old or support for FW not built in\n", id);  		return ERR_PTR(-EINVAL);  	} diff --git a/drivers/net/ethernet/netronome/nfp/nfp_app.h b/drivers/net/ethernet/netronome/nfp/nfp_app.h index 0e5e0305ad1c..20546ae67909 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_app.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_app.h @@ -43,6 +43,7 @@  struct bpf_prog;  struct net_device;  struct netdev_bpf; +struct netlink_ext_ack;  struct pci_dev;  struct sk_buff;  struct sk_buff; @@ -66,6 +67,9 @@ extern const struct nfp_app_type app_flower;   * struct nfp_app_type - application definition   * @id:		application ID   * @name:	application name + * @ctrl_cap_mask:  ctrl vNIC capability mask, allows disabling features like + *		    IRQMOD which are on by default but counter-productive for + *		    control messages which are often latency-sensitive   * @ctrl_has_meta:  control messages have prepend of type:5/port:CTRL   *   * Callbacks @@ -77,18 +81,19 @@ extern const struct nfp_app_type app_flower;   * @vnic_init:	vNIC netdev was registered   * @vnic_clean:	vNIC netdev about to be unregistered   * @repr_init:	representor about to be registered + * @repr_preclean:	representor about to unregistered, executed before app + *			reference to the it is removed   * @repr_clean:	representor about to be unregistered   * @repr_open:	representor netdev open callback   * @repr_stop:	representor netdev stop callback + * @change_mtu:	MTU change on a netdev has been requested (veto-only, change + *		is not guaranteed to be committed)   * @start:	start application logic   * @stop:	stop application logic   * @ctrl_msg_rx:    control message handler   * @setup_tc:	setup TC ndo - * @tc_busy:	TC HW offload busy (rules loaded) + * @bpf:	BPF ndo offload-related calls   * @xdp_offload:    offload an XDP program - * @bpf_verifier_prep:	verifier prep for dev-specific BPF programs - * @bpf_translate:	translate call for dev-specific BPF programs - * @bpf_destroy:	destroy for dev-specific BPF programs   * @eswitch_mode_get:    get SR-IOV eswitch mode   * @sriov_enable: app-specific sriov initialisation   * @sriov_disable: app-specific sriov clean-up @@ -98,6 +103,7 @@ struct nfp_app_type {  	enum nfp_app_id id;  	const char *name; +	u32 ctrl_cap_mask;  	bool ctrl_has_meta;  	int (*init)(struct nfp_app *app); @@ -112,11 +118,15 @@ struct nfp_app_type {  	void (*vnic_clean)(struct nfp_app *app, struct nfp_net *nn);  	int (*repr_init)(struct nfp_app *app, struct net_device *netdev); +	void (*repr_preclean)(struct nfp_app *app, struct net_device *netdev);  	void (*repr_clean)(struct nfp_app *app, struct net_device *netdev);  	int (*repr_open)(struct nfp_app *app, struct nfp_repr *repr);  	int (*repr_stop)(struct nfp_app *app, struct nfp_repr *repr); +	int (*change_mtu)(struct nfp_app *app, struct net_device *netdev, +			  int new_mtu); +  	int (*start)(struct nfp_app *app);  	void (*stop)(struct nfp_app *app); @@ -124,15 +134,11 @@ struct nfp_app_type {  	int (*setup_tc)(struct nfp_app *app, struct net_device *netdev,  			enum tc_setup_type type, void *type_data); -	bool (*tc_busy)(struct nfp_app *app, struct nfp_net *nn); +	int (*bpf)(struct nfp_app *app, struct nfp_net *nn, +		   struct netdev_bpf *xdp);  	int (*xdp_offload)(struct nfp_app *app, struct nfp_net *nn, -			   struct bpf_prog *prog); -	int (*bpf_verifier_prep)(struct nfp_app *app, struct nfp_net *nn, -				 struct netdev_bpf *bpf); -	int (*bpf_translate)(struct nfp_app *app, struct nfp_net *nn, -			     struct bpf_prog *prog); -	int (*bpf_destroy)(struct nfp_app *app, struct nfp_net *nn, -			   struct bpf_prog *prog); +			   struct bpf_prog *prog, +			   struct netlink_ext_ack *extack);  	int (*sriov_enable)(struct nfp_app *app, int num_vfs);  	void (*sriov_disable)(struct nfp_app *app); @@ -163,6 +169,7 @@ struct nfp_app {  	void *priv;  }; +bool __nfp_ctrl_tx(struct nfp_net *nn, struct sk_buff *skb);  bool nfp_ctrl_tx(struct nfp_net *nn, struct sk_buff *skb);  static inline int nfp_app_init(struct nfp_app *app) @@ -226,12 +233,27 @@ nfp_app_repr_init(struct nfp_app *app, struct net_device *netdev)  }  static inline void +nfp_app_repr_preclean(struct nfp_app *app, struct net_device *netdev) +{ +	if (app->type->repr_preclean) +		app->type->repr_preclean(app, netdev); +} + +static inline void  nfp_app_repr_clean(struct nfp_app *app, struct net_device *netdev)  {  	if (app->type->repr_clean)  		app->type->repr_clean(app, netdev);  } +static inline int +nfp_app_change_mtu(struct nfp_app *app, struct net_device *netdev, int new_mtu) +{ +	if (!app || !app->type->change_mtu) +		return 0; +	return app->type->change_mtu(app, netdev, new_mtu); +} +  static inline int nfp_app_start(struct nfp_app *app, struct nfp_net *ctrl)  {  	app->ctrl = ctrl; @@ -277,13 +299,6 @@ static inline bool nfp_app_has_tc(struct nfp_app *app)  	return app && app->type->setup_tc;  } -static inline bool nfp_app_tc_busy(struct nfp_app *app, struct nfp_net *nn) -{ -	if (!app || !app->type->tc_busy) -		return false; -	return app->type->tc_busy(app, nn); -} -  static inline int nfp_app_setup_tc(struct nfp_app *app,  				   struct net_device *netdev,  				   enum tc_setup_type type, void *type_data) @@ -293,39 +308,29 @@ static inline int nfp_app_setup_tc(struct nfp_app *app,  	return app->type->setup_tc(app, netdev, type, type_data);  } -static inline int nfp_app_xdp_offload(struct nfp_app *app, struct nfp_net *nn, -				      struct bpf_prog *prog) +static inline int nfp_app_bpf(struct nfp_app *app, struct nfp_net *nn, +			      struct netdev_bpf *bpf)  { -	if (!app || !app->type->xdp_offload) -		return -EOPNOTSUPP; -	return app->type->xdp_offload(app, nn, prog); +	if (!app || !app->type->bpf) +		return -EINVAL; +	return app->type->bpf(app, nn, bpf);  } -static inline int -nfp_app_bpf_verifier_prep(struct nfp_app *app, struct nfp_net *nn, -			  struct netdev_bpf *bpf) +static inline int nfp_app_xdp_offload(struct nfp_app *app, struct nfp_net *nn, +				      struct bpf_prog *prog, +				      struct netlink_ext_ack *extack)  { -	if (!app || !app->type->bpf_verifier_prep) +	if (!app || !app->type->xdp_offload)  		return -EOPNOTSUPP; -	return app->type->bpf_verifier_prep(app, nn, bpf); +	return app->type->xdp_offload(app, nn, prog, extack);  } -static inline int -nfp_app_bpf_translate(struct nfp_app *app, struct nfp_net *nn, -		      struct bpf_prog *prog) +static inline bool __nfp_app_ctrl_tx(struct nfp_app *app, struct sk_buff *skb)  { -	if (!app || !app->type->bpf_translate) -		return -EOPNOTSUPP; -	return app->type->bpf_translate(app, nn, prog); -} +	trace_devlink_hwmsg(priv_to_devlink(app->pf), false, 0, +			    skb->data, skb->len); -static inline int -nfp_app_bpf_destroy(struct nfp_app *app, struct nfp_net *nn, -		    struct bpf_prog *prog) -{ -	if (!app || !app->type->bpf_destroy) -		return -EOPNOTSUPP; -	return app->type->bpf_destroy(app, nn, prog); +	return __nfp_ctrl_tx(app->ctrl, skb);  }  static inline bool nfp_app_ctrl_tx(struct nfp_app *app, struct sk_buff *skb) @@ -378,6 +383,8 @@ static inline struct net_device *nfp_app_repr_get(struct nfp_app *app, u32 id)  struct nfp_app *nfp_app_from_netdev(struct net_device *netdev);  struct nfp_reprs * +nfp_reprs_get_locked(struct nfp_app *app, enum nfp_repr_type type); +struct nfp_reprs *  nfp_app_reprs_set(struct nfp_app *app, enum nfp_repr_type type,  		  struct nfp_reprs *reprs); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_asm.c b/drivers/net/ethernet/netronome/nfp/nfp_asm.c index 830f6de25f47..1e597600c693 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_asm.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_asm.c @@ -41,6 +41,7 @@  const struct cmd_tgt_act cmd_tgt_act[__CMD_TGT_MAP_SIZE] = {  	[CMD_TGT_WRITE8_SWAP] =		{ 0x02, 0x42 }, +	[CMD_TGT_WRITE32_SWAP] =	{ 0x02, 0x5f },  	[CMD_TGT_READ8] =		{ 0x01, 0x43 },  	[CMD_TGT_READ32] =		{ 0x00, 0x5c },  	[CMD_TGT_READ32_LE] =		{ 0x01, 0x5c }, @@ -49,6 +50,94 @@ const struct cmd_tgt_act cmd_tgt_act[__CMD_TGT_MAP_SIZE] = {  	[CMD_TGT_READ_SWAP_LE] =	{ 0x03, 0x40 },  }; +static bool unreg_is_imm(u16 reg) +{ +	return (reg & UR_REG_IMM) == UR_REG_IMM; +} + +u16 br_get_offset(u64 instr) +{ +	u16 addr_lo, addr_hi; + +	addr_lo = FIELD_GET(OP_BR_ADDR_LO, instr); +	addr_hi = FIELD_GET(OP_BR_ADDR_HI, instr); + +	return (addr_hi * ((OP_BR_ADDR_LO >> __bf_shf(OP_BR_ADDR_LO)) + 1)) | +		addr_lo; +} + +void br_set_offset(u64 *instr, u16 offset) +{ +	u16 addr_lo, addr_hi; + +	addr_lo = offset & (OP_BR_ADDR_LO >> __bf_shf(OP_BR_ADDR_LO)); +	addr_hi = offset != addr_lo; +	*instr &= ~(OP_BR_ADDR_HI | OP_BR_ADDR_LO); +	*instr |= FIELD_PREP(OP_BR_ADDR_HI, addr_hi); +	*instr |= FIELD_PREP(OP_BR_ADDR_LO, addr_lo); +} + +void br_add_offset(u64 *instr, u16 offset) +{ +	u16 addr; + +	addr = br_get_offset(*instr); +	br_set_offset(instr, addr + offset); +} + +static bool immed_can_modify(u64 instr) +{ +	if (FIELD_GET(OP_IMMED_INV, instr) || +	    FIELD_GET(OP_IMMED_SHIFT, instr) || +	    FIELD_GET(OP_IMMED_WIDTH, instr) != IMMED_WIDTH_ALL) { +		pr_err("Can't decode/encode immed!\n"); +		return false; +	} +	return true; +} + +u16 immed_get_value(u64 instr) +{ +	u16 reg; + +	if (!immed_can_modify(instr)) +		return 0; + +	reg = FIELD_GET(OP_IMMED_A_SRC, instr); +	if (!unreg_is_imm(reg)) +		reg = FIELD_GET(OP_IMMED_B_SRC, instr); + +	return (reg & 0xff) | FIELD_GET(OP_IMMED_IMM, instr) << 8; +} + +void immed_set_value(u64 *instr, u16 immed) +{ +	if (!immed_can_modify(*instr)) +		return; + +	if (unreg_is_imm(FIELD_GET(OP_IMMED_A_SRC, *instr))) { +		*instr &= ~FIELD_PREP(OP_IMMED_A_SRC, 0xff); +		*instr |= FIELD_PREP(OP_IMMED_A_SRC, immed & 0xff); +	} else { +		*instr &= ~FIELD_PREP(OP_IMMED_B_SRC, 0xff); +		*instr |= FIELD_PREP(OP_IMMED_B_SRC, immed & 0xff); +	} + +	*instr &= ~OP_IMMED_IMM; +	*instr |= FIELD_PREP(OP_IMMED_IMM, immed >> 8); +} + +void immed_add_value(u64 *instr, u16 offset) +{ +	u16 val; + +	if (!immed_can_modify(*instr)) +		return; + +	val = immed_get_value(*instr); +	immed_set_value(instr, val + offset); +} +  static u16 nfp_swreg_to_unreg(swreg reg, bool is_dst)  {  	bool lm_id, lm_dec = false; @@ -120,7 +209,8 @@ int swreg_to_unrestricted(swreg dst, swreg lreg, swreg rreg,  	reg->dst = nfp_swreg_to_unreg(dst, true);  	/* Decode source operands */ -	if (swreg_type(lreg) == swreg_type(rreg)) +	if (swreg_type(lreg) == swreg_type(rreg) && +	    swreg_type(lreg) != NN_REG_NONE)  		return -EFAULT;  	if (swreg_type(lreg) == NN_REG_GPR_B || @@ -200,7 +290,8 @@ int swreg_to_restricted(swreg dst, swreg lreg, swreg rreg,  	reg->dst = nfp_swreg_to_rereg(dst, true, false, NULL);  	/* Decode source operands */ -	if (swreg_type(lreg) == swreg_type(rreg)) +	if (swreg_type(lreg) == swreg_type(rreg) && +	    swreg_type(lreg) != NN_REG_NONE)  		return -EFAULT;  	if (swreg_type(lreg) == NN_REG_GPR_B || diff --git a/drivers/net/ethernet/netronome/nfp/nfp_asm.h b/drivers/net/ethernet/netronome/nfp/nfp_asm.h index 74d0c11ab2f9..5f9291db98e0 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_asm.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_asm.h @@ -1,5 +1,5 @@  /* - * Copyright (C) 2016 Netronome Systems, Inc. + * Copyright (C) 2016-2017 Netronome Systems, Inc.   *   * This software is dual licensed under the GNU General License Version 2,   * June 1991 as shown in the file COPYING in the top-level directory of this @@ -77,9 +77,11 @@  enum br_mask {  	BR_BEQ = 0x00,  	BR_BNE = 0x01, +	BR_BMI = 0x02,  	BR_BHS = 0x04,  	BR_BLO = 0x05,  	BR_BGE = 0x08, +	BR_BLT = 0x09,  	BR_UNC = 0x18,  }; @@ -92,6 +94,10 @@ enum br_ctx_signal_state {  	BR_CSS_NONE = 2,  }; +u16 br_get_offset(u64 instr); +void br_set_offset(u64 *instr, u16 offset); +void br_add_offset(u64 *instr, u16 offset); +  #define OP_BBYTE_BASE		0x0c800000000ULL  #define OP_BB_A_SRC		0x000000000ffULL  #define OP_BB_BYTE		0x00000000300ULL @@ -132,6 +138,10 @@ enum immed_shift {  	IMMED_SHIFT_2B = 2,  }; +u16 immed_get_value(u64 instr); +void immed_set_value(u64 *instr, u16 immed); +void immed_add_value(u64 *instr, u16 offset); +  #define OP_SHF_BASE		0x08000000000ULL  #define OP_SHF_A_SRC		0x000000000ffULL  #define OP_SHF_SC		0x00000000300ULL @@ -175,6 +185,7 @@ enum alu_op {  	ALU_OP_NONE	= 0x00,  	ALU_OP_ADD	= 0x01,  	ALU_OP_NOT	= 0x04, +	ALU_OP_ADD_2B	= 0x05,  	ALU_OP_AND	= 0x08,  	ALU_OP_SUB_C	= 0x0d,  	ALU_OP_ADD_C	= 0x11, @@ -209,6 +220,7 @@ enum alu_dst_ab {  #define OP_CMD_CNT		0x0000e000000ULL  #define OP_CMD_SIG		0x000f0000000ULL  #define OP_CMD_TGT_CMD		0x07f00000000ULL +#define OP_CMD_INDIR		0x20000000000ULL  #define OP_CMD_MODE	       0x1c0000000000ULL  struct cmd_tgt_act { @@ -219,6 +231,7 @@ struct cmd_tgt_act {  enum cmd_tgt_map {  	CMD_TGT_READ8,  	CMD_TGT_WRITE8_SWAP, +	CMD_TGT_WRITE32_SWAP,  	CMD_TGT_READ32,  	CMD_TGT_READ32_LE,  	CMD_TGT_READ32_SWAP, @@ -240,6 +253,9 @@ enum cmd_ctx_swap {  	CMD_CTX_NO_SWAP = 3,  }; +#define CMD_OVE_LEN	BIT(7) +#define CMD_OV_LEN	GENMASK(12, 8) +  #define OP_LCSR_BASE		0x0fc00000000ULL  #define OP_LCSR_A_SRC		0x000000003ffULL  #define OP_LCSR_B_SRC		0x000000ffc00ULL @@ -257,6 +273,7 @@ enum lcsr_wr_src {  #define OP_CARB_BASE		0x0e000000000ULL  #define OP_CARB_OR		0x00000010000ULL +#define NFP_CSR_CTX_PTR		0x20  #define NFP_CSR_ACT_LM_ADDR0	0x64  #define NFP_CSR_ACT_LM_ADDR1	0x6c  #define NFP_CSR_ACT_LM_ADDR2	0x94 @@ -377,4 +394,13 @@ int swreg_to_restricted(swreg dst, swreg lreg, swreg rreg,  int nfp_ustore_check_valid_no_ecc(u64 insn);  u64 nfp_ustore_calc_ecc_insn(u64 insn); +#define NFP_IND_ME_REFL_WR_SIG_INIT	3 +#define NFP_IND_ME_CTX_PTR_BASE_MASK	GENMASK(9, 0) +#define NFP_IND_NUM_CONTEXTS		8 + +static inline u32 nfp_get_ind_csr_ctx_ptr_offs(u32 read_offset) +{ +	return (read_offset & ~NFP_IND_ME_CTX_PTR_BASE_MASK) | NFP_CSR_CTX_PTR; +} +  #endif diff --git a/drivers/net/ethernet/netronome/nfp/nfp_devlink.c b/drivers/net/ethernet/netronome/nfp/nfp_devlink.c index 6c9f29c2e975..eb0fc614673d 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_devlink.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_devlink.c @@ -152,18 +152,8 @@ out:  static int nfp_devlink_eswitch_mode_get(struct devlink *devlink, u16 *mode)  {  	struct nfp_pf *pf = devlink_priv(devlink); -	int ret; - -	mutex_lock(&pf->lock); -	if (!pf->app) { -		ret = -EBUSY; -		goto out; -	} -	ret = nfp_app_eswitch_mode_get(pf->app, mode); -out: -	mutex_unlock(&pf->lock); -	return ret; +	return nfp_app_eswitch_mode_get(pf->app, mode);  }  const struct devlink_ops nfp_devlink_ops = { diff --git a/drivers/net/ethernet/netronome/nfp/nfp_main.c b/drivers/net/ethernet/netronome/nfp/nfp_main.c index 35eaccbece36..ab301d56430b 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_main.c @@ -45,6 +45,7 @@  #include <linux/pci.h>  #include <linux/firmware.h>  #include <linux/vermagic.h> +#include <linux/vmalloc.h>  #include <net/devlink.h>  #include "nfpcore/nfp.h" @@ -498,17 +499,16 @@ static int nfp_pci_probe(struct pci_dev *pdev,  	if (err)  		goto err_hwinfo_free; -	err = devlink_register(devlink, &pdev->dev); -	if (err) -		goto err_hwinfo_free; -  	err = nfp_nsp_init(pdev, pf);  	if (err) -		goto err_devlink_unreg; +		goto err_hwinfo_free;  	pf->mip = nfp_mip_open(pf->cpp);  	pf->rtbl = __nfp_rtsym_table_read(pf->cpp, pf->mip); +	pf->dump_flag = NFP_DUMP_NSP_DIAG; +	pf->dumpspec = nfp_net_dump_load_dumpspec(pf->cpp, pf->rtbl); +  	err = nfp_pcie_sriov_read_nfd_limit(pf);  	if (err)  		goto err_fw_unload; @@ -518,6 +518,7 @@ static int nfp_pci_probe(struct pci_dev *pdev,  		dev_err(&pdev->dev,  			"Error: %d VFs already enabled, but loaded FW can only support %d\n",  			pf->num_vfs, pf->limit_vfs); +		err = -EINVAL;  		goto err_fw_unload;  	} @@ -544,8 +545,7 @@ err_fw_unload:  		nfp_fw_unload(pf);  	kfree(pf->eth_tbl);  	kfree(pf->nspi); -err_devlink_unreg: -	devlink_unregister(devlink); +	vfree(pf->dumpspec);  err_hwinfo_free:  	kfree(pf->hwinfo);  	nfp_cpp_free(pf->cpp); @@ -566,19 +566,15 @@ err_pci_disable:  static void nfp_pci_remove(struct pci_dev *pdev)  {  	struct nfp_pf *pf = pci_get_drvdata(pdev); -	struct devlink *devlink;  	nfp_hwmon_unregister(pf); -	devlink = priv_to_devlink(pf); - -	nfp_net_pci_remove(pf); -  	nfp_pcie_sriov_disable(pdev);  	pci_sriov_set_totalvfs(pf->pdev, 0); -	devlink_unregister(devlink); +	nfp_net_pci_remove(pf); +	vfree(pf->dumpspec);  	kfree(pf->rtbl);  	nfp_mip_close(pf->mip);  	if (pf->fw_loaded) @@ -592,7 +588,7 @@ static void nfp_pci_remove(struct pci_dev *pdev)  	kfree(pf->eth_tbl);  	kfree(pf->nspi);  	mutex_destroy(&pf->lock); -	devlink_free(devlink); +	devlink_free(priv_to_devlink(pf));  	pci_release_regions(pdev);  	pci_disable_device(pdev);  } @@ -653,3 +649,4 @@ MODULE_FIRMWARE("netronome/nic_AMDA0099-0001_2x25.nffw");  MODULE_AUTHOR("Netronome Systems <[email protected]>");  MODULE_LICENSE("GPL");  MODULE_DESCRIPTION("The Netronome Flow Processor (NFP) driver."); +MODULE_VERSION(UTS_RELEASE); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_main.h b/drivers/net/ethernet/netronome/nfp/nfp_main.h index be0ee59f2eb9..add46e28212b 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_main.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_main.h @@ -39,6 +39,7 @@  #ifndef NFP_MAIN_H  #define NFP_MAIN_H +#include <linux/ethtool.h>  #include <linux/list.h>  #include <linux/types.h>  #include <linux/msi.h> @@ -62,6 +63,17 @@ struct nfp_port;  struct nfp_rtsym_table;  /** + * struct nfp_dumpspec - NFP FW dump specification structure + * @size:	Size of the data + * @data:	Sequence of TLVs, each being an instruction to dump some data + *		from FW + */ +struct nfp_dumpspec { +	u32 size; +	u8 data[0]; +}; + +/**   * struct nfp_pf - NFP PF-specific device structure   * @pdev:		Backpointer to PCI device   * @cpp:		Pointer to the CPP handle @@ -83,6 +95,9 @@ struct nfp_rtsym_table;   * @mip:		MIP handle   * @rtbl:		RTsym table   * @hwinfo:		HWInfo table + * @dumpspec:		Debug dump specification + * @dump_flag:		Store dump flag between set_dump and get_dump_flag + * @dump_len:		Store dump length between set_dump and get_dump_flag   * @eth_tbl:		NSP ETH table   * @nspi:		NSP identification info   * @hwmon_dev:		pointer to hwmon device @@ -124,6 +139,9 @@ struct nfp_pf {  	const struct nfp_mip *mip;  	struct nfp_rtsym_table *rtbl;  	struct nfp_hwinfo *hwinfo; +	struct nfp_dumpspec *dumpspec; +	u32 dump_flag; +	u32 dump_len;  	struct nfp_eth_table *eth_tbl;  	struct nfp_nsp_identify *nspi; @@ -157,4 +175,15 @@ void nfp_net_get_mac_addr(struct nfp_pf *pf, struct nfp_port *port);  bool nfp_ctrl_tx(struct nfp_net *nn, struct sk_buff *skb); +enum nfp_dump_diag { +	NFP_DUMP_NSP_DIAG = 0, +}; + +struct nfp_dumpspec * +nfp_net_dump_load_dumpspec(struct nfp_cpp *cpp, struct nfp_rtsym_table *rtbl); +s64 nfp_net_dump_calculate_size(struct nfp_pf *pf, struct nfp_dumpspec *spec, +				u32 flag); +int nfp_net_dump_populate_buffer(struct nfp_pf *pf, struct nfp_dumpspec *spec, +				 struct ethtool_dump *dump_param, void *dest); +  #endif /* NFP_MAIN_H */ diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index 7f9857c276b1..787df47ec430 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -47,6 +47,7 @@  #include <linux/netdevice.h>  #include <linux/pci.h>  #include <linux/io-64-nonatomic-hi-lo.h> +#include <net/xdp.h>  #include "nfp_net_ctrl.h" @@ -192,7 +193,8 @@ struct nfp_net_tx_desc {  /**   * struct nfp_net_tx_buf - software TX buffer descriptor - * @skb:	sk_buff associated with this buffer + * @skb:	normal ring, sk_buff associated with this buffer + * @frag:	XDP ring, page frag associated with this buffer   * @dma_addr:	DMA mapping address of the buffer   * @fidx:	Fragment index (-1 for the head and [0..nr_frags-1] for frags)   * @pkt_cnt:	Number of packets to be produced out of the skb associated @@ -350,6 +352,7 @@ struct nfp_net_rx_buf {   * @rxds:       Virtual address of FL/RX ring in host memory   * @dma:        DMA address of the FL/RX ring   * @size:       Size, in bytes, of the FL/RX ring (needed to free) + * @xdp_rxq:    RX-ring info avail for XDP   */  struct nfp_net_rx_ring {  	struct nfp_net_r_vector *r_vec; @@ -361,19 +364,23 @@ struct nfp_net_rx_ring {  	u32 idx;  	int fl_qcidx; +	unsigned int size;  	u8 __iomem *qcp_fl;  	struct nfp_net_rx_buf *rxbufs;  	struct nfp_net_rx_desc *rxds;  	dma_addr_t dma; -	unsigned int size; +	struct xdp_rxq_info xdp_rxq;  } ____cacheline_aligned;  /**   * struct nfp_net_r_vector - Per ring interrupt vector configuration   * @nfp_net:        Backpointer to nfp_net structure   * @napi:           NAPI structure for this ring vec + * @tasklet:        ctrl vNIC, tasklet for servicing the r_vec + * @queue:          ctrl vNIC, send queue + * @lock:           ctrl vNIC, r_vec lock protects @queue   * @tx_ring:        Pointer to TX ring   * @rx_ring:        Pointer to RX ring   * @xdp_ring:	    Pointer to an extra TX ring for XDP @@ -548,6 +555,8 @@ struct nfp_net_dp {   * @max_r_vecs:		Number of allocated interrupt vectors for RX/TX   * @max_tx_rings:       Maximum number of TX rings supported by the Firmware   * @max_rx_rings:       Maximum number of RX rings supported by the Firmware + * @stride_rx:		Queue controller RX queue spacing + * @stride_tx:		Queue controller TX queue spacing   * @r_vecs:             Pre-allocated array of ring vectors   * @irq_entries:        Pre-allocated array of MSI-X entries   * @lsc_handler:        Handler for Link State Change interrupt @@ -573,6 +582,7 @@ struct nfp_net_dp {   * @qcp_cfg:            Pointer to QCP queue used for configuration notification   * @tx_bar:             Pointer to mapped TX queues   * @rx_bar:             Pointer to mapped FL/RX queues + * @tlv_caps:		Parsed TLV capabilities   * @debugfs_dir:	Device directory in debugfs   * @vnic_list:		Entry on device vNIC list   * @pdev:		Backpointer to PCI device @@ -639,6 +649,8 @@ struct nfp_net {  	u8 __iomem *tx_bar;  	u8 __iomem *rx_bar; +	struct nfp_net_tlv_caps tlv_caps; +  	struct dentry *debugfs_dir;  	struct list_head vnic_list; @@ -834,6 +846,18 @@ static inline const char *nfp_net_name(struct nfp_net *nn)  	return nn->dp.netdev ? nn->dp.netdev->name : "ctrl";  } +static inline void nfp_ctrl_lock(struct nfp_net *nn) +	__acquires(&nn->r_vecs[0].lock) +{ +	spin_lock_bh(&nn->r_vecs[0].lock); +} + +static inline void nfp_ctrl_unlock(struct nfp_net *nn) +	__releases(&nn->r_vecs[0].lock) +{ +	spin_unlock_bh(&nn->r_vecs[0].lock); +} +  /* Globals */  extern const char nfp_driver_version[]; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 99b0487b6d82..a05be0ab2713 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -293,9 +293,15 @@ int nfp_net_reconfig(struct nfp_net *nn, u32 update)   */  static int nfp_net_reconfig_mbox(struct nfp_net *nn, u32 mbox_cmd)  { +	u32 mbox = nn->tlv_caps.mbox_off;  	int ret; -	nn_writeq(nn, NFP_NET_CFG_MBOX_CMD, mbox_cmd); +	if (!nfp_net_has_mbox(&nn->tlv_caps)) { +		nn_err(nn, "no mailbox present, command: %u\n", mbox_cmd); +		return -EIO; +	} + +	nn_writeq(nn, mbox + NFP_NET_CFG_MBOX_SIMPLE_CMD, mbox_cmd);  	ret = nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_MBOX);  	if (ret) { @@ -303,7 +309,7 @@ static int nfp_net_reconfig_mbox(struct nfp_net *nn, u32 mbox_cmd)  		return ret;  	} -	return -nn_readl(nn, NFP_NET_CFG_MBOX_RET); +	return -nn_readl(nn, mbox + NFP_NET_CFG_MBOX_SIMPLE_RET);  }  /* Interrupt configuration and handling @@ -1610,11 +1616,13 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget)  	unsigned int true_bufsz;  	struct sk_buff *skb;  	int pkts_polled = 0; +	struct xdp_buff xdp;  	int idx;  	rcu_read_lock();  	xdp_prog = READ_ONCE(dp->xdp_prog);  	true_bufsz = xdp_prog ? PAGE_SIZE : dp->fl_bufsz; +	xdp.rxq = &rx_ring->xdp_rxq;  	tx_ring = r_vec->xdp_ring;  	while (pkts_polled < budget) { @@ -1705,7 +1713,6 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget)  				  dp->bpf_offload_xdp) && !meta.portid) {  			void *orig_data = rxbuf->frag + pkt_off;  			unsigned int dma_off; -			struct xdp_buff xdp;  			int act;  			xdp.data_hard_start = rxbuf->frag + NFP_NET_RX_BUF_HEADROOM; @@ -1919,6 +1926,13 @@ err_free:  	return false;  } +bool __nfp_ctrl_tx(struct nfp_net *nn, struct sk_buff *skb) +{ +	struct nfp_net_r_vector *r_vec = &nn->r_vecs[0]; + +	return nfp_ctrl_tx_one(nn, r_vec, skb, false); +} +  bool nfp_ctrl_tx(struct nfp_net *nn, struct sk_buff *skb)  {  	struct nfp_net_r_vector *r_vec = &nn->r_vecs[0]; @@ -2254,6 +2268,8 @@ static void nfp_net_rx_ring_free(struct nfp_net_rx_ring *rx_ring)  	struct nfp_net_r_vector *r_vec = rx_ring->r_vec;  	struct nfp_net_dp *dp = &r_vec->nfp_net->dp; +	if (dp->netdev) +		xdp_rxq_info_unreg(&rx_ring->xdp_rxq);  	kfree(rx_ring->rxbufs);  	if (rx_ring->rxds) @@ -2277,7 +2293,14 @@ static void nfp_net_rx_ring_free(struct nfp_net_rx_ring *rx_ring)  static int  nfp_net_rx_ring_alloc(struct nfp_net_dp *dp, struct nfp_net_rx_ring *rx_ring)  { -	int sz; +	int sz, err; + +	if (dp->netdev) { +		err = xdp_rxq_info_reg(&rx_ring->xdp_rxq, dp->netdev, +				       rx_ring->idx); +		if (err < 0) +			return err; +	}  	rx_ring->cnt = dp->rxd_cnt;  	rx_ring->size = sizeof(*rx_ring->rxds) * rx_ring->cnt; @@ -2441,7 +2464,7 @@ void nfp_net_coalesce_write_cfg(struct nfp_net *nn)  	 * ME timestamp ticks.  There are 16 ME clock cycles for each timestamp  	 * count.  	 */ -	factor = nn->me_freq_mhz / 16; +	factor = nn->tlv_caps.me_freq_mhz / 16;  	/* copy RX interrupt coalesce parameters */  	value = (nn->rx_coalesce_max_frames << 16) | @@ -2852,6 +2875,11 @@ static void nfp_net_set_rx_mode(struct net_device *netdev)  	new_ctrl = nn->dp.ctrl; +	if (!netdev_mc_empty(netdev) || netdev->flags & IFF_ALLMULTI) +		new_ctrl |= nn->cap & NFP_NET_CFG_CTRL_L2MC; +	else +		new_ctrl &= ~NFP_NET_CFG_CTRL_L2MC; +  	if (netdev->flags & IFF_PROMISC) {  		if (nn->cap & NFP_NET_CFG_CTRL_PROMISC)  			new_ctrl |= NFP_NET_CFG_CTRL_PROMISC; @@ -3036,6 +3064,11 @@ static int nfp_net_change_mtu(struct net_device *netdev, int new_mtu)  {  	struct nfp_net *nn = netdev_priv(netdev);  	struct nfp_net_dp *dp; +	int err; + +	err = nfp_app_change_mtu(nn->app, netdev, new_mtu); +	if (err) +		return err;  	dp = nfp_net_clone_dp(nn);  	if (!dp) @@ -3057,8 +3090,9 @@ nfp_net_vlan_rx_add_vid(struct net_device *netdev, __be16 proto, u16 vid)  	if (!vid)  		return 0; -	nn_writew(nn, NFP_NET_CFG_VLAN_FILTER_VID, vid); -	nn_writew(nn, NFP_NET_CFG_VLAN_FILTER_PROTO, ETH_P_8021Q); +	nn_writew(nn, nn->tlv_caps.mbox_off + NFP_NET_CFG_VLAN_FILTER_VID, vid); +	nn_writew(nn, nn->tlv_caps.mbox_off + NFP_NET_CFG_VLAN_FILTER_PROTO, +		  ETH_P_8021Q);  	return nfp_net_reconfig_mbox(nn, NFP_NET_CFG_MBOX_CMD_CTAG_FILTER_ADD);  } @@ -3074,8 +3108,9 @@ nfp_net_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto, u16 vid)  	if (!vid)  		return 0; -	nn_writew(nn, NFP_NET_CFG_VLAN_FILTER_VID, vid); -	nn_writew(nn, NFP_NET_CFG_VLAN_FILTER_PROTO, ETH_P_8021Q); +	nn_writew(nn, nn->tlv_caps.mbox_off + NFP_NET_CFG_VLAN_FILTER_VID, vid); +	nn_writew(nn, nn->tlv_caps.mbox_off + NFP_NET_CFG_VLAN_FILTER_PROTO, +		  ETH_P_8021Q);  	return nfp_net_reconfig_mbox(nn, NFP_NET_CFG_MBOX_CMD_CTAG_FILTER_KILL);  } @@ -3175,10 +3210,9 @@ static int nfp_net_set_features(struct net_device *netdev,  			new_ctrl &= ~NFP_NET_CFG_CTRL_GATHER;  	} -	if (changed & NETIF_F_HW_TC && nfp_app_tc_busy(nn->app, nn)) { -		nn_err(nn, "Cannot disable HW TC offload while in use\n"); -		return -EBUSY; -	} +	err = nfp_port_set_features(netdev, features); +	if (err) +		return err;  	nn_dbg(nn, "Feature change 0x%llx -> 0x%llx (changed=0x%llx)\n",  	       netdev->features, features, changed); @@ -3368,7 +3402,7 @@ nfp_net_xdp_setup(struct nfp_net *nn, struct bpf_prog *prog, u32 flags,  	if (err)  		return err; -	err = nfp_app_xdp_offload(nn->app, nn, offload_prog); +	err = nfp_app_xdp_offload(nn->app, nn, offload_prog, extack);  	if (err && flags & XDP_FLAGS_HW_MODE)  		return err; @@ -3394,17 +3428,10 @@ static int nfp_net_xdp(struct net_device *netdev, struct netdev_bpf *xdp)  		if (nn->dp.bpf_offload_xdp)  			xdp->prog_attached = XDP_ATTACHED_HW;  		xdp->prog_id = nn->xdp_prog ? nn->xdp_prog->aux->id : 0; +		xdp->prog_flags = nn->xdp_prog ? nn->xdp_flags : 0;  		return 0; -	case BPF_OFFLOAD_VERIFIER_PREP: -		return nfp_app_bpf_verifier_prep(nn->app, nn, xdp); -	case BPF_OFFLOAD_TRANSLATE: -		return nfp_app_bpf_translate(nn->app, nn, -					     xdp->offload.prog); -	case BPF_OFFLOAD_DESTROY: -		return nfp_app_bpf_destroy(nn->app, nn, -					   xdp->offload.prog);  	default: -		return -EINVAL; +		return nfp_app_bpf(nn->app, nn, xdp);  	}  } @@ -3563,9 +3590,6 @@ struct nfp_net *nfp_net_alloc(struct pci_dev *pdev, bool needs_netdev,   */  void nfp_net_free(struct nfp_net *nn)  { -	if (nn->xdp_prog) -		bpf_prog_put(nn->xdp_prog); -  	if (nn->dp.netdev)  		free_netdev(nn->dp.netdev);  	else @@ -3709,7 +3733,7 @@ static void nfp_net_netdev_init(struct nfp_net *nn)  	netdev->features = netdev->hw_features; -	if (nfp_app_has_tc(nn->app)) +	if (nfp_app_has_tc(nn->app) && nn->port)  		netdev->hw_features |= NETIF_F_HW_TC;  	/* Advertise but disable TSO by default. */ @@ -3726,23 +3750,15 @@ static void nfp_net_netdev_init(struct nfp_net *nn)  	netdev->min_mtu = ETH_MIN_MTU;  	netdev->max_mtu = nn->max_mtu; +	netdev->gso_max_segs = NFP_NET_LSO_MAX_SEGS; +  	netif_carrier_off(netdev);  	nfp_net_set_ethtool_ops(netdev);  } -/** - * nfp_net_init() - Initialise/finalise the nfp_net structure - * @nn:		NFP Net device structure - * - * Return: 0 on success or negative errno on error. - */ -int nfp_net_init(struct nfp_net *nn) +static int nfp_net_read_caps(struct nfp_net *nn)  { -	int err; - -	nn->dp.rx_dma_dir = DMA_FROM_DEVICE; -  	/* Get some of the read-only fields from the BAR */  	nn->cap = nn_readl(nn, NFP_NET_CFG_CAP);  	nn->max_mtu = nn_readl(nn, NFP_NET_CFG_MAX_MTU); @@ -3775,6 +3791,29 @@ int nfp_net_init(struct nfp_net *nn)  		nn->dp.rx_offset = NFP_NET_RX_OFFSET;  	} +	/* For control vNICs mask out the capabilities app doesn't want. */ +	if (!nn->dp.netdev) +		nn->cap &= nn->app->type->ctrl_cap_mask; + +	return 0; +} + +/** + * nfp_net_init() - Initialise/finalise the nfp_net structure + * @nn:		NFP Net device structure + * + * Return: 0 on success or negative errno on error. + */ +int nfp_net_init(struct nfp_net *nn) +{ +	int err; + +	nn->dp.rx_dma_dir = DMA_FROM_DEVICE; + +	err = nfp_net_read_caps(nn); +	if (err) +		return err; +  	/* Set default MTU and Freelist buffer size */  	if (nn->max_mtu < NFP_NET_DEFAULT_MTU)  		nn->dp.mtu = nn->max_mtu; @@ -3791,8 +3830,6 @@ int nfp_net_init(struct nfp_net *nn)  	/* Allow L2 Broadcast and Multicast through by default, if supported */  	if (nn->cap & NFP_NET_CFG_CTRL_L2BC)  		nn->dp.ctrl |= NFP_NET_CFG_CTRL_L2BC; -	if (nn->cap & NFP_NET_CFG_CTRL_L2MC) -		nn->dp.ctrl |= NFP_NET_CFG_CTRL_L2MC;  	/* Allow IRQ moderation, if supported */  	if (nn->cap & NFP_NET_CFG_CTRL_IRQMOD) { @@ -3800,6 +3837,11 @@ int nfp_net_init(struct nfp_net *nn)  		nn->dp.ctrl |= NFP_NET_CFG_CTRL_IRQMOD;  	} +	err = nfp_net_tlv_caps_parse(&nn->pdev->dev, nn->dp.ctrl_bar, +				     &nn->tlv_caps); +	if (err) +		return err; +  	if (nn->dp.netdev)  		nfp_net_netdev_init(nn); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.c new file mode 100644 index 000000000000..1f9149bb2ae6 --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.c @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2018 Netronome Systems, Inc. + * + * This software is dual licensed under the GNU General License Version 2, + * June 1991 as shown in the file COPYING in the top-level directory of this + * source tree or the BSD 2-Clause License provided below.  You have the + * option to license this software under the complete terms of either license. + * + * The BSD 2-Clause License: + * + *     Redistribution and use in source and binary forms, with or + *     without modification, are permitted provided that the following + *     conditions are met: + * + *      1. Redistributions of source code must retain the above + *         copyright notice, this list of conditions and the following + *         disclaimer. + * + *      2. Redistributions in binary form must reproduce the above + *         copyright notice, this list of conditions and the following + *         disclaimer in the documentation and/or other materials + *         provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/bitfield.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/types.h> + +#include "nfp_net_ctrl.h" +#include "nfp_net.h" + +static void nfp_net_tlv_caps_reset(struct nfp_net_tlv_caps *caps) +{ +	memset(caps, 0, sizeof(*caps)); +	caps->me_freq_mhz = 1200; +	caps->mbox_off = NFP_NET_CFG_MBOX_BASE; +	caps->mbox_len = NFP_NET_CFG_MBOX_VAL_MAX_SZ; +} + +int nfp_net_tlv_caps_parse(struct device *dev, u8 __iomem *ctrl_mem, +			   struct nfp_net_tlv_caps *caps) +{ +	u8 __iomem *data = ctrl_mem + NFP_NET_CFG_TLV_BASE; +	u8 __iomem *end = ctrl_mem + NFP_NET_CFG_BAR_SZ; +	u32 hdr; + +	nfp_net_tlv_caps_reset(caps); + +	hdr = readl(data); +	if (!hdr) +		return 0; + +	while (true) { +		unsigned int length, offset; +		u32 hdr = readl(data); + +		length = FIELD_GET(NFP_NET_CFG_TLV_HEADER_LENGTH, hdr); +		offset = data - ctrl_mem; + +		/* Advance past the header */ +		data += 4; + +		if (length % NFP_NET_CFG_TLV_LENGTH_INC) { +			dev_err(dev, "TLV size not multiple of %u len:%u\n", +				NFP_NET_CFG_TLV_LENGTH_INC, length); +			return -EINVAL; +		} +		if (data + length > end) { +			dev_err(dev, "oversized TLV offset:%u len:%u\n", +				offset, length); +			return -EINVAL; +		} + +		switch (FIELD_GET(NFP_NET_CFG_TLV_HEADER_TYPE, hdr)) { +		case NFP_NET_CFG_TLV_TYPE_UNKNOWN: +			dev_err(dev, "NULL TLV at offset:%u\n", offset); +			return -EINVAL; +		case NFP_NET_CFG_TLV_TYPE_RESERVED: +			break; +		case NFP_NET_CFG_TLV_TYPE_END: +			if (!length) +				return 0; + +			dev_err(dev, "END TLV should be empty, has len:%d\n", +				length); +			return -EINVAL; +		case NFP_NET_CFG_TLV_TYPE_ME_FREQ: +			if (length != 4) { +				dev_err(dev, +					"ME FREQ TLV should be 4B, is %dB\n", +					length); +				return -EINVAL; +			} + +			caps->me_freq_mhz = readl(data); +			break; +		case NFP_NET_CFG_TLV_TYPE_MBOX: +			if (!length) { +				caps->mbox_off = 0; +				caps->mbox_len = 0; +			} else { +				caps->mbox_off = data - ctrl_mem; +				caps->mbox_len = length; +			} +			break; +		default: +			if (!FIELD_GET(NFP_NET_CFG_TLV_HEADER_REQUIRED, hdr)) +				break; + +			dev_err(dev, "unknown TLV type:%u offset:%u len:%u\n", +				FIELD_GET(NFP_NET_CFG_TLV_HEADER_TYPE, hdr), +				offset, length); +			return -EINVAL; +		} + +		data += length; +		if (data + 4 > end) { +			dev_err(dev, "reached end of BAR without END TLV\n"); +			return -EINVAL; +		} +	} + +	/* Not reached */ +	return -EINVAL; +} diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h index 782d452e0fc2..4499a7333078 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h @@ -43,9 +43,7 @@  #ifndef _NFP_NET_CTRL_H_  #define _NFP_NET_CTRL_H_ -/* IMPORTANT: This header file is shared with the FW, - *	      no OS specific constructs, please! - */ +#include <linux/types.h>  /**   * Configuration BAR size. @@ -61,9 +59,12 @@  #define NFP_NET_RX_OFFSET               32  /** - * Maximum header size supported for LSO frames + * LSO parameters + * %NFP_NET_LSO_MAX_HDR_SZ:	Maximum header size supported for LSO frames + * %NFP_NET_LSO_MAX_SEGS:	Maximum number of segments LSO frame can produce   */  #define NFP_NET_LSO_MAX_HDR_SZ		255 +#define NFP_NET_LSO_MAX_SEGS		64  /**   * Prepend field types @@ -91,23 +92,24 @@  #define NFP_NET_RSS_IPV6_EX_UDP         9  /** - * @NFP_NET_TXR_MAX:         Maximum number of TX rings - * @NFP_NET_RXR_MAX:         Maximum number of RX rings + * Ring counts + * %NFP_NET_TXR_MAX:         Maximum number of TX rings + * %NFP_NET_RXR_MAX:         Maximum number of RX rings   */  #define NFP_NET_TXR_MAX                 64  #define NFP_NET_RXR_MAX                 64  /**   * Read/Write config words (0x0000 - 0x002c) - * @NFP_NET_CFG_CTRL:        Global control - * @NFP_NET_CFG_UPDATE:      Indicate which fields are updated - * @NFP_NET_CFG_TXRS_ENABLE: Bitmask of enabled TX rings - * @NFP_NET_CFG_RXRS_ENABLE: Bitmask of enabled RX rings - * @NFP_NET_CFG_MTU:         Set MTU size - * @NFP_NET_CFG_FLBUFSZ:     Set freelist buffer size (must be larger than MTU) - * @NFP_NET_CFG_EXN:         MSI-X table entry for exceptions - * @NFP_NET_CFG_LSC:         MSI-X table entry for link state changes - * @NFP_NET_CFG_MACADDR:     MAC address + * %NFP_NET_CFG_CTRL:        Global control + * %NFP_NET_CFG_UPDATE:      Indicate which fields are updated + * %NFP_NET_CFG_TXRS_ENABLE: Bitmask of enabled TX rings + * %NFP_NET_CFG_RXRS_ENABLE: Bitmask of enabled RX rings + * %NFP_NET_CFG_MTU:         Set MTU size + * %NFP_NET_CFG_FLBUFSZ:     Set freelist buffer size (must be larger than MTU) + * %NFP_NET_CFG_EXN:         MSI-X table entry for exceptions + * %NFP_NET_CFG_LSC:         MSI-X table entry for link state changes + * %NFP_NET_CFG_MACADDR:     MAC address   *   * TODO:   * - define Error details in UPDATE @@ -176,14 +178,14 @@  /**   * Read-only words (0x0030 - 0x0050): - * @NFP_NET_CFG_VERSION:     Firmware version number - * @NFP_NET_CFG_STS:         Status - * @NFP_NET_CFG_CAP:         Capabilities (same bits as @NFP_NET_CFG_CTRL) - * @NFP_NET_CFG_MAX_TXRINGS: Maximum number of TX rings - * @NFP_NET_CFG_MAX_RXRINGS: Maximum number of RX rings - * @NFP_NET_CFG_MAX_MTU:     Maximum support MTU - * @NFP_NET_CFG_START_TXQ:   Start Queue Control Queue to use for TX (PF only) - * @NFP_NET_CFG_START_RXQ:   Start Queue Control Queue to use for RX (PF only) + * %NFP_NET_CFG_VERSION:     Firmware version number + * %NFP_NET_CFG_STS:         Status + * %NFP_NET_CFG_CAP:         Capabilities (same bits as %NFP_NET_CFG_CTRL) + * %NFP_NET_CFG_MAX_TXRINGS: Maximum number of TX rings + * %NFP_NET_CFG_MAX_RXRINGS: Maximum number of RX rings + * %NFP_NET_CFG_MAX_MTU:     Maximum support MTU + * %NFP_NET_CFG_START_TXQ:   Start Queue Control Queue to use for TX (PF only) + * %NFP_NET_CFG_START_RXQ:   Start Queue Control Queue to use for RX (PF only)   *   * TODO:   * - define more STS bits @@ -228,31 +230,37 @@  /**   * RSS capabilities - * @NFP_NET_CFG_RSS_CAP_HFUNC:	supported hash functions (same bits as - *				@NFP_NET_CFG_RSS_HFUNC) + * %NFP_NET_CFG_RSS_CAP_HFUNC:	supported hash functions (same bits as + *				%NFP_NET_CFG_RSS_HFUNC)   */  #define NFP_NET_CFG_RSS_CAP		0x0054  #define   NFP_NET_CFG_RSS_CAP_HFUNC	  0xff000000  /** + * TLV area start + * %NFP_NET_CFG_TLV_BASE:	start anchor of the TLV area + */ +#define NFP_NET_CFG_TLV_BASE		0x0058 + +/**   * VXLAN/UDP encap configuration - * @NFP_NET_CFG_VXLAN_PORT:	Base address of table of tunnels' UDP dst ports - * @NFP_NET_CFG_VXLAN_SZ:	Size of the UDP port table in bytes + * %NFP_NET_CFG_VXLAN_PORT:	Base address of table of tunnels' UDP dst ports + * %NFP_NET_CFG_VXLAN_SZ:	Size of the UDP port table in bytes   */  #define NFP_NET_CFG_VXLAN_PORT		0x0060  #define NFP_NET_CFG_VXLAN_SZ		  0x0008  /**   * BPF section - * @NFP_NET_CFG_BPF_ABI:	BPF ABI version - * @NFP_NET_CFG_BPF_CAP:	BPF capabilities - * @NFP_NET_CFG_BPF_MAX_LEN:	Maximum size of JITed BPF code in bytes - * @NFP_NET_CFG_BPF_START:	Offset at which BPF will be loaded - * @NFP_NET_CFG_BPF_DONE:	Offset to jump to on exit - * @NFP_NET_CFG_BPF_STACK_SZ:	Total size of stack area in 64B chunks - * @NFP_NET_CFG_BPF_INL_MTU:	Packet data split offset in 64B chunks - * @NFP_NET_CFG_BPF_SIZE:	Size of the JITed BPF code in instructions - * @NFP_NET_CFG_BPF_ADDR:	DMA address of the buffer with JITed BPF code + * %NFP_NET_CFG_BPF_ABI:	BPF ABI version + * %NFP_NET_CFG_BPF_CAP:	BPF capabilities + * %NFP_NET_CFG_BPF_MAX_LEN:	Maximum size of JITed BPF code in bytes + * %NFP_NET_CFG_BPF_START:	Offset at which BPF will be loaded + * %NFP_NET_CFG_BPF_DONE:	Offset to jump to on exit + * %NFP_NET_CFG_BPF_STACK_SZ:	Total size of stack area in 64B chunks + * %NFP_NET_CFG_BPF_INL_MTU:	Packet data split offset in 64B chunks + * %NFP_NET_CFG_BPF_SIZE:	Size of the JITed BPF code in instructions + * %NFP_NET_CFG_BPF_ADDR:	DMA address of the buffer with JITed BPF code   */  #define NFP_NET_CFG_BPF_ABI		0x0080  #define   NFP_NET_BPF_ABI		2 @@ -278,9 +286,9 @@  /**   * RSS configuration (0x0100 - 0x01ac):   * Used only when NFP_NET_CFG_CTRL_RSS is enabled - * @NFP_NET_CFG_RSS_CFG:     RSS configuration word - * @NFP_NET_CFG_RSS_KEY:     RSS "secret" key - * @NFP_NET_CFG_RSS_ITBL:    RSS indirection table + * %NFP_NET_CFG_RSS_CFG:     RSS configuration word + * %NFP_NET_CFG_RSS_KEY:     RSS "secret" key + * %NFP_NET_CFG_RSS_ITBL:    RSS indirection table   */  #define NFP_NET_CFG_RSS_BASE            0x0100  #define NFP_NET_CFG_RSS_CTRL            NFP_NET_CFG_RSS_BASE @@ -305,13 +313,13 @@  /**   * TX ring configuration (0x200 - 0x800) - * @NFP_NET_CFG_TXR_BASE:    Base offset for TX ring configuration - * @NFP_NET_CFG_TXR_ADDR:    Per TX ring DMA address (8B entries) - * @NFP_NET_CFG_TXR_WB_ADDR: Per TX ring write back DMA address (8B entries) - * @NFP_NET_CFG_TXR_SZ:      Per TX ring ring size (1B entries) - * @NFP_NET_CFG_TXR_VEC:     Per TX ring MSI-X table entry (1B entries) - * @NFP_NET_CFG_TXR_PRIO:    Per TX ring priority (1B entries) - * @NFP_NET_CFG_TXR_IRQ_MOD: Per TX ring interrupt moderation packet + * %NFP_NET_CFG_TXR_BASE:    Base offset for TX ring configuration + * %NFP_NET_CFG_TXR_ADDR:    Per TX ring DMA address (8B entries) + * %NFP_NET_CFG_TXR_WB_ADDR: Per TX ring write back DMA address (8B entries) + * %NFP_NET_CFG_TXR_SZ:      Per TX ring ring size (1B entries) + * %NFP_NET_CFG_TXR_VEC:     Per TX ring MSI-X table entry (1B entries) + * %NFP_NET_CFG_TXR_PRIO:    Per TX ring priority (1B entries) + * %NFP_NET_CFG_TXR_IRQ_MOD: Per TX ring interrupt moderation packet   */  #define NFP_NET_CFG_TXR_BASE            0x0200  #define NFP_NET_CFG_TXR_ADDR(_x)        (NFP_NET_CFG_TXR_BASE + ((_x) * 0x8)) @@ -325,12 +333,12 @@  /**   * RX ring configuration (0x0800 - 0x0c00) - * @NFP_NET_CFG_RXR_BASE:    Base offset for RX ring configuration - * @NFP_NET_CFG_RXR_ADDR:    Per RX ring DMA address (8B entries) - * @NFP_NET_CFG_RXR_SZ:      Per RX ring ring size (1B entries) - * @NFP_NET_CFG_RXR_VEC:     Per RX ring MSI-X table entry (1B entries) - * @NFP_NET_CFG_RXR_PRIO:    Per RX ring priority (1B entries) - * @NFP_NET_CFG_RXR_IRQ_MOD: Per RX ring interrupt moderation (4B entries) + * %NFP_NET_CFG_RXR_BASE:    Base offset for RX ring configuration + * %NFP_NET_CFG_RXR_ADDR:    Per RX ring DMA address (8B entries) + * %NFP_NET_CFG_RXR_SZ:      Per RX ring ring size (1B entries) + * %NFP_NET_CFG_RXR_VEC:     Per RX ring MSI-X table entry (1B entries) + * %NFP_NET_CFG_RXR_PRIO:    Per RX ring priority (1B entries) + * %NFP_NET_CFG_RXR_IRQ_MOD: Per RX ring interrupt moderation (4B entries)   */  #define NFP_NET_CFG_RXR_BASE            0x0800  #define NFP_NET_CFG_RXR_ADDR(_x)        (NFP_NET_CFG_RXR_BASE + ((_x) * 0x8)) @@ -343,7 +351,7 @@  /**   * Interrupt Control/Cause registers (0x0c00 - 0x0d00)   * These registers are only used when MSI-X auto-masking is not - * enabled (@NFP_NET_CFG_CTRL_MSIXAUTO not set).  The array is index + * enabled (%NFP_NET_CFG_CTRL_MSIXAUTO not set).  The array is index   * by MSI-X entry and are 1B in size.  If an entry is zero, the   * corresponding entry is enabled.  If the FW generates an interrupt,   * it writes a cause into the corresponding field.  This also masks @@ -393,8 +401,8 @@  /**   * Per ring stats (0x1000 - 0x1800)   * options, 64bit per entry - * @NFP_NET_CFG_TXR_STATS:   TX ring statistics (Packet and Byte count) - * @NFP_NET_CFG_RXR_STATS:   RX ring statistics (Packet and Byte count) + * %NFP_NET_CFG_TXR_STATS:   TX ring statistics (Packet and Byte count) + * %NFP_NET_CFG_RXR_STATS:   RX ring statistics (Packet and Byte count)   */  #define NFP_NET_CFG_TXR_STATS_BASE      0x1000  #define NFP_NET_CFG_TXR_STATS(_x)       (NFP_NET_CFG_TXR_STATS_BASE + \ @@ -408,24 +416,105 @@   * 4B used for update command and 4B return code   * followed by a max of 504B of variable length value   */ -#define NFP_NET_CFG_MBOX_CMD		0x1800 -#define NFP_NET_CFG_MBOX_RET		0x1804 -#define NFP_NET_CFG_MBOX_VAL		0x1808 +#define NFP_NET_CFG_MBOX_BASE		0x1800  #define NFP_NET_CFG_MBOX_VAL_MAX_SZ	0x1F8 +#define NFP_NET_CFG_MBOX_SIMPLE_CMD	0x0 +#define NFP_NET_CFG_MBOX_SIMPLE_RET	0x4 +#define NFP_NET_CFG_MBOX_SIMPLE_VAL	0x8 +#define NFP_NET_CFG_MBOX_SIMPLE_LEN	0x12 +  #define NFP_NET_CFG_MBOX_CMD_CTAG_FILTER_ADD 1  #define NFP_NET_CFG_MBOX_CMD_CTAG_FILTER_KILL 2  /**   * VLAN filtering using general use mailbox - * @NFP_NET_CFG_VLAN_FILTER:		Base address of VLAN filter mailbox - * @NFP_NET_CFG_VLAN_FILTER_VID:	VLAN ID to filter - * @NFP_NET_CFG_VLAN_FILTER_PROTO:	VLAN proto to filter - * @NFP_NET_CFG_VXLAN_SZ:		Size of the VLAN filter mailbox in bytes + * %NFP_NET_CFG_VLAN_FILTER:		Base address of VLAN filter mailbox + * %NFP_NET_CFG_VLAN_FILTER_VID:	VLAN ID to filter + * %NFP_NET_CFG_VLAN_FILTER_PROTO:	VLAN proto to filter + * %NFP_NET_CFG_VXLAN_SZ:		Size of the VLAN filter mailbox in bytes   */ -#define NFP_NET_CFG_VLAN_FILTER		NFP_NET_CFG_MBOX_VAL +#define NFP_NET_CFG_VLAN_FILTER		NFP_NET_CFG_MBOX_SIMPLE_VAL  #define  NFP_NET_CFG_VLAN_FILTER_VID	NFP_NET_CFG_VLAN_FILTER  #define  NFP_NET_CFG_VLAN_FILTER_PROTO	 (NFP_NET_CFG_VLAN_FILTER + 2)  #define NFP_NET_CFG_VLAN_FILTER_SZ	 0x0004 +/** + * TLV capabilities + * %NFP_NET_CFG_TLV_TYPE:	Offset of type within the TLV + * %NFP_NET_CFG_TLV_TYPE_REQUIRED: Driver must be able to parse the TLV + * %NFP_NET_CFG_TLV_LENGTH:	Offset of length within the TLV + * %NFP_NET_CFG_TLV_LENGTH_INC:	TLV length increments + * %NFP_NET_CFG_TLV_VALUE:	Offset of value with the TLV + * + * List of simple TLV structures, first one starts at %NFP_NET_CFG_TLV_BASE. + * Last structure must be of type %NFP_NET_CFG_TLV_TYPE_END.  Presence of TLVs + * is indicated by %NFP_NET_CFG_TLV_BASE being non-zero.  TLV structures may + * fill the entire remainder of the BAR or be shorter.  FW must make sure TLVs + * don't conflict with other features which allocate space beyond + * %NFP_NET_CFG_TLV_BASE.  %NFP_NET_CFG_TLV_TYPE_RESERVED should be used to wrap + * space used by such features. + * Note that the 4 byte TLV header is not counted in %NFP_NET_CFG_TLV_LENGTH. + */ +#define NFP_NET_CFG_TLV_TYPE		0x00 +#define   NFP_NET_CFG_TLV_TYPE_REQUIRED	  0x8000 +#define NFP_NET_CFG_TLV_LENGTH		0x02 +#define   NFP_NET_CFG_TLV_LENGTH_INC	  4 +#define NFP_NET_CFG_TLV_VALUE		0x04 + +#define NFP_NET_CFG_TLV_HEADER_REQUIRED	0x80000000 +#define NFP_NET_CFG_TLV_HEADER_TYPE	0x7fff0000 +#define NFP_NET_CFG_TLV_HEADER_LENGTH	0x0000ffff + +/** + * Capability TLV types + * + * %NFP_NET_CFG_TLV_TYPE_UNKNOWN: + * Special TLV type to catch bugs, should never be encountered.  Drivers should + * treat encountering this type as error and refuse to probe. + * + * %NFP_NET_CFG_TLV_TYPE_RESERVED: + * Reserved space, may contain legacy fixed-offset fields, or be used for + * padding.  The use of this type should be otherwise avoided. + * + * %NFP_NET_CFG_TLV_TYPE_END: + * Empty, end of TLV list.  Must be the last TLV.  Drivers will stop processing + * further TLVs when encountered. + * + * %NFP_NET_CFG_TLV_TYPE_ME_FREQ: + * Single word, ME frequency in MHz as used in calculation for + * %NFP_NET_CFG_RXR_IRQ_MOD and %NFP_NET_CFG_TXR_IRQ_MOD. + * + * %NFP_NET_CFG_TLV_TYPE_MBOX: + * Variable, mailbox area.  Overwrites the default location which is + * %NFP_NET_CFG_MBOX_BASE and length %NFP_NET_CFG_MBOX_VAL_MAX_SZ. + */ +#define NFP_NET_CFG_TLV_TYPE_UNKNOWN		0 +#define NFP_NET_CFG_TLV_TYPE_RESERVED		1 +#define NFP_NET_CFG_TLV_TYPE_END		2 +#define NFP_NET_CFG_TLV_TYPE_ME_FREQ		3 +#define NFP_NET_CFG_TLV_TYPE_MBOX		4 + +struct device; + +/** + * struct nfp_net_tlv_caps - parsed control BAR TLV capabilities + * @me_freq_mhz:	ME clock_freq (MHz) + * @mbox_off:		vNIC mailbox area offset + * @mbox_len:		vNIC mailbox area length + */ +struct nfp_net_tlv_caps { +	u32 me_freq_mhz; +	unsigned int mbox_off; +	unsigned int mbox_len; +}; + +int nfp_net_tlv_caps_parse(struct device *dev, u8 __iomem *ctrl_mem, +			   struct nfp_net_tlv_caps *caps); + +static inline bool nfp_net_has_mbox(struct nfp_net_tlv_caps *caps) +{ +	return caps->mbox_len >= NFP_NET_CFG_MBOX_SIMPLE_LEN; +} +  #endif /* _NFP_NET_CTRL_H_ */ diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_debugdump.c b/drivers/net/ethernet/netronome/nfp/nfp_net_debugdump.c new file mode 100644 index 000000000000..bb8ed460086e --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_debugdump.c @@ -0,0 +1,811 @@ +/* + * Copyright (C) 2017 Netronome Systems, Inc. + * + * This software is dual licensed under the GNU General License Version 2, + * June 1991 as shown in the file COPYING in the top-level directory of this + * source tree or the BSD 2-Clause License provided below.  You have the + * option to license this software under the complete terms of either license. + * + * The BSD 2-Clause License: + * + *     Redistribution and use in source and binary forms, with or + *     without modification, are permitted provided that the following + *     conditions are met: + * + *      1. Redistributions of source code must retain the above + *         copyright notice, this list of conditions and the following + *         disclaimer. + * + *      2. Redistributions in binary form must reproduce the above + *         copyright notice, this list of conditions and the following + *         disclaimer in the documentation and/or other materials + *         provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/ethtool.h> +#include <linux/vmalloc.h> + +#include "nfp_asm.h" +#include "nfp_main.h" +#include "nfpcore/nfp.h" +#include "nfpcore/nfp_nffw.h" +#include "nfpcore/nfp6000/nfp6000.h" + +#define NFP_DUMP_SPEC_RTSYM	"_abi_dump_spec" + +#define ALIGN8(x)	ALIGN(x, 8) + +enum nfp_dumpspec_type { +	NFP_DUMPSPEC_TYPE_CPP_CSR = 0, +	NFP_DUMPSPEC_TYPE_XPB_CSR = 1, +	NFP_DUMPSPEC_TYPE_ME_CSR = 2, +	NFP_DUMPSPEC_TYPE_INDIRECT_ME_CSR = 3, +	NFP_DUMPSPEC_TYPE_RTSYM = 4, +	NFP_DUMPSPEC_TYPE_HWINFO = 5, +	NFP_DUMPSPEC_TYPE_FWNAME = 6, +	NFP_DUMPSPEC_TYPE_HWINFO_FIELD = 7, +	NFP_DUMPSPEC_TYPE_PROLOG = 10000, +	NFP_DUMPSPEC_TYPE_ERROR = 10001, +}; + +/* The following structs must be carefully aligned so that they can be used to + * interpret the binary dumpspec and populate the dump data in a deterministic + * way. + */ + +/* generic type plus length */ +struct nfp_dump_tl { +	__be32 type; +	__be32 length;	/* chunk length to follow, aligned to 8 bytes */ +	char data[0]; +}; + +/* NFP CPP parameters */ +struct nfp_dumpspec_cpp_isl_id { +	u8 target; +	u8 action; +	u8 token; +	u8 island; +}; + +struct nfp_dump_common_cpp { +	struct nfp_dumpspec_cpp_isl_id cpp_id; +	__be32 offset;		/* address to start dump */ +	__be32 dump_length;	/* total bytes to dump, aligned to reg size */ +}; + +/* CSR dumpables */ +struct nfp_dumpspec_csr { +	struct nfp_dump_tl tl; +	struct nfp_dump_common_cpp cpp; +	__be32 register_width;	/* in bits */ +}; + +struct nfp_dumpspec_rtsym { +	struct nfp_dump_tl tl; +	char rtsym[0]; +}; + +/* header for register dumpable */ +struct nfp_dump_csr { +	struct nfp_dump_tl tl; +	struct nfp_dump_common_cpp cpp; +	__be32 register_width;	/* in bits */ +	__be32 error;		/* error code encountered while reading */ +	__be32 error_offset;	/* offset being read when error occurred */ +}; + +struct nfp_dump_rtsym { +	struct nfp_dump_tl tl; +	struct nfp_dump_common_cpp cpp; +	__be32 error;		/* error code encountered while reading */ +	u8 padded_name_length;	/* pad so data starts at 8 byte boundary */ +	char rtsym[0]; +	/* after padded_name_length, there is dump_length data */ +}; + +struct nfp_dump_prolog { +	struct nfp_dump_tl tl; +	__be32 dump_level; +}; + +struct nfp_dump_error { +	struct nfp_dump_tl tl; +	__be32 error; +	char padding[4]; +	char spec[0]; +}; + +/* to track state through debug size calculation TLV traversal */ +struct nfp_level_size { +	__be32 requested_level;	/* input */ +	u32 total_size;		/* output */ +}; + +/* to track state during debug dump creation TLV traversal */ +struct nfp_dump_state { +	__be32 requested_level;	/* input param */ +	u32 dumped_size;	/* adds up to size of dumped data */ +	u32 buf_size;		/* size of buffer pointer to by p */ +	void *p;		/* current point in dump buffer */ +}; + +typedef int (*nfp_tlv_visit)(struct nfp_pf *pf, struct nfp_dump_tl *tl, +			     void *param); + +static int +nfp_traverse_tlvs(struct nfp_pf *pf, void *data, u32 data_length, void *param, +		  nfp_tlv_visit tlv_visit) +{ +	long long remaining = data_length; +	struct nfp_dump_tl *tl; +	u32 total_tlv_size; +	void *p = data; +	int err; + +	while (remaining >= sizeof(*tl)) { +		tl = p; +		if (!tl->type && !tl->length) +			break; + +		if (be32_to_cpu(tl->length) > remaining - sizeof(*tl)) +			return -EINVAL; + +		total_tlv_size = sizeof(*tl) + be32_to_cpu(tl->length); + +		/* Spec TLVs should be aligned to 4 bytes. */ +		if (total_tlv_size % 4 != 0) +			return -EINVAL; + +		p += total_tlv_size; +		remaining -= total_tlv_size; +		err = tlv_visit(pf, tl, param); +		if (err) +			return err; +	} + +	return 0; +} + +static u32 nfp_get_numeric_cpp_id(struct nfp_dumpspec_cpp_isl_id *cpp_id) +{ +	return NFP_CPP_ISLAND_ID(cpp_id->target, cpp_id->action, cpp_id->token, +				 cpp_id->island); +} + +struct nfp_dumpspec * +nfp_net_dump_load_dumpspec(struct nfp_cpp *cpp, struct nfp_rtsym_table *rtbl) +{ +	const struct nfp_rtsym *specsym; +	struct nfp_dumpspec *dumpspec; +	int bytes_read; +	u32 cpp_id; + +	specsym = nfp_rtsym_lookup(rtbl, NFP_DUMP_SPEC_RTSYM); +	if (!specsym) +		return NULL; + +	/* expected size of this buffer is in the order of tens of kilobytes */ +	dumpspec = vmalloc(sizeof(*dumpspec) + specsym->size); +	if (!dumpspec) +		return NULL; + +	dumpspec->size = specsym->size; + +	cpp_id = NFP_CPP_ISLAND_ID(specsym->target, NFP_CPP_ACTION_RW, 0, +				   specsym->domain); + +	bytes_read = nfp_cpp_read(cpp, cpp_id, specsym->addr, dumpspec->data, +				  specsym->size); +	if (bytes_read != specsym->size) { +		vfree(dumpspec); +		nfp_warn(cpp, "Debug dump specification read failed.\n"); +		return NULL; +	} + +	return dumpspec; +} + +static int nfp_dump_error_tlv_size(struct nfp_dump_tl *spec) +{ +	return ALIGN8(sizeof(struct nfp_dump_error) + sizeof(*spec) + +		      be32_to_cpu(spec->length)); +} + +static int nfp_calc_fwname_tlv_size(struct nfp_pf *pf) +{ +	u32 fwname_len = strlen(nfp_mip_name(pf->mip)); + +	return sizeof(struct nfp_dump_tl) + ALIGN8(fwname_len + 1); +} + +static int nfp_calc_hwinfo_field_sz(struct nfp_pf *pf, struct nfp_dump_tl *spec) +{ +	u32 tl_len, key_len; +	const char *value; + +	tl_len = be32_to_cpu(spec->length); +	key_len = strnlen(spec->data, tl_len); +	if (key_len == tl_len) +		return nfp_dump_error_tlv_size(spec); + +	value = nfp_hwinfo_lookup(pf->hwinfo, spec->data); +	if (!value) +		return nfp_dump_error_tlv_size(spec); + +	return sizeof(struct nfp_dump_tl) + ALIGN8(key_len + strlen(value) + 2); +} + +static bool nfp_csr_spec_valid(struct nfp_dumpspec_csr *spec_csr) +{ +	u32 required_read_sz = sizeof(*spec_csr) - sizeof(spec_csr->tl); +	u32 available_sz = be32_to_cpu(spec_csr->tl.length); +	u32 reg_width; + +	if (available_sz < required_read_sz) +		return false; + +	reg_width = be32_to_cpu(spec_csr->register_width); + +	return reg_width == 32 || reg_width == 64; +} + +static int +nfp_calc_rtsym_dump_sz(struct nfp_pf *pf, struct nfp_dump_tl *spec) +{ +	struct nfp_rtsym_table *rtbl = pf->rtbl; +	struct nfp_dumpspec_rtsym *spec_rtsym; +	const struct nfp_rtsym *sym; +	u32 tl_len, key_len; +	u32 size; + +	spec_rtsym = (struct nfp_dumpspec_rtsym *)spec; +	tl_len = be32_to_cpu(spec->length); +	key_len = strnlen(spec_rtsym->rtsym, tl_len); +	if (key_len == tl_len) +		return nfp_dump_error_tlv_size(spec); + +	sym = nfp_rtsym_lookup(rtbl, spec_rtsym->rtsym); +	if (!sym) +		return nfp_dump_error_tlv_size(spec); + +	if (sym->type == NFP_RTSYM_TYPE_ABS) +		size = sizeof(sym->addr); +	else +		size = sym->size; + +	return ALIGN8(offsetof(struct nfp_dump_rtsym, rtsym) + key_len + 1) + +	       ALIGN8(size); +} + +static int +nfp_add_tlv_size(struct nfp_pf *pf, struct nfp_dump_tl *tl, void *param) +{ +	struct nfp_dumpspec_csr *spec_csr; +	u32 *size = param; +	u32 hwinfo_size; + +	switch (be32_to_cpu(tl->type)) { +	case NFP_DUMPSPEC_TYPE_FWNAME: +		*size += nfp_calc_fwname_tlv_size(pf); +		break; +	case NFP_DUMPSPEC_TYPE_CPP_CSR: +	case NFP_DUMPSPEC_TYPE_XPB_CSR: +	case NFP_DUMPSPEC_TYPE_ME_CSR: +		spec_csr = (struct nfp_dumpspec_csr *)tl; +		if (!nfp_csr_spec_valid(spec_csr)) +			*size += nfp_dump_error_tlv_size(tl); +		else +			*size += ALIGN8(sizeof(struct nfp_dump_csr)) + +				 ALIGN8(be32_to_cpu(spec_csr->cpp.dump_length)); +		break; +	case NFP_DUMPSPEC_TYPE_INDIRECT_ME_CSR: +		spec_csr = (struct nfp_dumpspec_csr *)tl; +		if (!nfp_csr_spec_valid(spec_csr)) +			*size += nfp_dump_error_tlv_size(tl); +		else +			*size += ALIGN8(sizeof(struct nfp_dump_csr)) + +				 ALIGN8(be32_to_cpu(spec_csr->cpp.dump_length) * +					NFP_IND_NUM_CONTEXTS); +		break; +	case NFP_DUMPSPEC_TYPE_RTSYM: +		*size += nfp_calc_rtsym_dump_sz(pf, tl); +		break; +	case NFP_DUMPSPEC_TYPE_HWINFO: +		hwinfo_size = nfp_hwinfo_get_packed_str_size(pf->hwinfo); +		*size += sizeof(struct nfp_dump_tl) + ALIGN8(hwinfo_size); +		break; +	case NFP_DUMPSPEC_TYPE_HWINFO_FIELD: +		*size += nfp_calc_hwinfo_field_sz(pf, tl); +		break; +	default: +		*size += nfp_dump_error_tlv_size(tl); +		break; +	} + +	return 0; +} + +static int +nfp_calc_specific_level_size(struct nfp_pf *pf, struct nfp_dump_tl *dump_level, +			     void *param) +{ +	struct nfp_level_size *lev_sz = param; + +	if (dump_level->type != lev_sz->requested_level) +		return 0; + +	return nfp_traverse_tlvs(pf, dump_level->data, +				 be32_to_cpu(dump_level->length), +				 &lev_sz->total_size, nfp_add_tlv_size); +} + +s64 nfp_net_dump_calculate_size(struct nfp_pf *pf, struct nfp_dumpspec *spec, +				u32 flag) +{ +	struct nfp_level_size lev_sz; +	int err; + +	lev_sz.requested_level = cpu_to_be32(flag); +	lev_sz.total_size = ALIGN8(sizeof(struct nfp_dump_prolog)); + +	err = nfp_traverse_tlvs(pf, spec->data, spec->size, &lev_sz, +				nfp_calc_specific_level_size); +	if (err) +		return err; + +	return lev_sz.total_size; +} + +static int nfp_add_tlv(u32 type, u32 total_tlv_sz, struct nfp_dump_state *dump) +{ +	struct nfp_dump_tl *tl = dump->p; + +	if (total_tlv_sz > dump->buf_size) +		return -ENOSPC; + +	if (dump->buf_size - total_tlv_sz < dump->dumped_size) +		return -ENOSPC; + +	tl->type = cpu_to_be32(type); +	tl->length = cpu_to_be32(total_tlv_sz - sizeof(*tl)); + +	dump->dumped_size += total_tlv_sz; +	dump->p += total_tlv_sz; + +	return 0; +} + +static int +nfp_dump_error_tlv(struct nfp_dump_tl *spec, int error, +		   struct nfp_dump_state *dump) +{ +	struct nfp_dump_error *dump_header = dump->p; +	u32 total_spec_size, total_size; +	int err; + +	total_spec_size = sizeof(*spec) + be32_to_cpu(spec->length); +	total_size = ALIGN8(sizeof(*dump_header) + total_spec_size); + +	err = nfp_add_tlv(NFP_DUMPSPEC_TYPE_ERROR, total_size, dump); +	if (err) +		return err; + +	dump_header->error = cpu_to_be32(error); +	memcpy(dump_header->spec, spec, total_spec_size); + +	return 0; +} + +static int nfp_dump_fwname(struct nfp_pf *pf, struct nfp_dump_state *dump) +{ +	struct nfp_dump_tl *dump_header = dump->p; +	u32 fwname_len, total_size; +	const char *fwname; +	int err; + +	fwname = nfp_mip_name(pf->mip); +	fwname_len = strlen(fwname); +	total_size = sizeof(*dump_header) + ALIGN8(fwname_len + 1); + +	err = nfp_add_tlv(NFP_DUMPSPEC_TYPE_FWNAME, total_size, dump); +	if (err) +		return err; + +	memcpy(dump_header->data, fwname, fwname_len); + +	return 0; +} + +static int +nfp_dump_hwinfo(struct nfp_pf *pf, struct nfp_dump_tl *spec, +		struct nfp_dump_state *dump) +{ +	struct nfp_dump_tl *dump_header = dump->p; +	u32 hwinfo_size, total_size; +	char *hwinfo; +	int err; + +	hwinfo = nfp_hwinfo_get_packed_strings(pf->hwinfo); +	hwinfo_size = nfp_hwinfo_get_packed_str_size(pf->hwinfo); +	total_size = sizeof(*dump_header) + ALIGN8(hwinfo_size); + +	err = nfp_add_tlv(NFP_DUMPSPEC_TYPE_HWINFO, total_size, dump); +	if (err) +		return err; + +	memcpy(dump_header->data, hwinfo, hwinfo_size); + +	return 0; +} + +static int nfp_dump_hwinfo_field(struct nfp_pf *pf, struct nfp_dump_tl *spec, +				 struct nfp_dump_state *dump) +{ +	struct nfp_dump_tl *dump_header = dump->p; +	u32 tl_len, key_len, val_len; +	const char *key, *value; +	u32 total_size; +	int err; + +	tl_len = be32_to_cpu(spec->length); +	key_len = strnlen(spec->data, tl_len); +	if (key_len == tl_len) +		return nfp_dump_error_tlv(spec, -EINVAL, dump); + +	key = spec->data; +	value = nfp_hwinfo_lookup(pf->hwinfo, key); +	if (!value) +		return nfp_dump_error_tlv(spec, -ENOENT, dump); + +	val_len = strlen(value); +	total_size = sizeof(*dump_header) + ALIGN8(key_len + val_len + 2); +	err = nfp_add_tlv(NFP_DUMPSPEC_TYPE_HWINFO_FIELD, total_size, dump); +	if (err) +		return err; + +	memcpy(dump_header->data, key, key_len + 1); +	memcpy(dump_header->data + key_len + 1, value, val_len + 1); + +	return 0; +} + +static bool is_xpb_read(struct nfp_dumpspec_cpp_isl_id *cpp_id) +{ +	return cpp_id->target == NFP_CPP_TARGET_ISLAND_XPB && +	       cpp_id->action == 0 && cpp_id->token == 0; +} + +static int +nfp_dump_csr_range(struct nfp_pf *pf, struct nfp_dumpspec_csr *spec_csr, +		   struct nfp_dump_state *dump) +{ +	struct nfp_dump_csr *dump_header = dump->p; +	u32 reg_sz, header_size, total_size; +	u32 cpp_rd_addr, max_rd_addr; +	int bytes_read; +	void *dest; +	u32 cpp_id; +	int err; + +	if (!nfp_csr_spec_valid(spec_csr)) +		return nfp_dump_error_tlv(&spec_csr->tl, -EINVAL, dump); + +	reg_sz = be32_to_cpu(spec_csr->register_width) / BITS_PER_BYTE; +	header_size = ALIGN8(sizeof(*dump_header)); +	total_size = header_size + +		     ALIGN8(be32_to_cpu(spec_csr->cpp.dump_length)); +	dest = dump->p + header_size; + +	err = nfp_add_tlv(be32_to_cpu(spec_csr->tl.type), total_size, dump); +	if (err) +		return err; + +	dump_header->cpp = spec_csr->cpp; +	dump_header->register_width = spec_csr->register_width; + +	cpp_id = nfp_get_numeric_cpp_id(&spec_csr->cpp.cpp_id); +	cpp_rd_addr = be32_to_cpu(spec_csr->cpp.offset); +	max_rd_addr = cpp_rd_addr + be32_to_cpu(spec_csr->cpp.dump_length); + +	while (cpp_rd_addr < max_rd_addr) { +		if (is_xpb_read(&spec_csr->cpp.cpp_id)) { +			err = nfp_xpb_readl(pf->cpp, cpp_rd_addr, (u32 *)dest); +		} else { +			bytes_read = nfp_cpp_read(pf->cpp, cpp_id, cpp_rd_addr, +						  dest, reg_sz); +			err = bytes_read == reg_sz ? 0 : -EIO; +		} +		if (err) { +			dump_header->error = cpu_to_be32(err); +			dump_header->error_offset = cpu_to_be32(cpp_rd_addr); +			break; +		} +		cpp_rd_addr += reg_sz; +		dest += reg_sz; +	} + +	return 0; +} + +/* Write context to CSRCtxPtr, then read from it. Then the value can be read + * from IndCtxStatus. + */ +static int +nfp_read_indirect_csr(struct nfp_cpp *cpp, +		      struct nfp_dumpspec_cpp_isl_id cpp_params, u32 offset, +		      u32 reg_sz, u32 context, void *dest) +{ +	u32 csr_ctx_ptr_offs; +	u32 cpp_id; +	int result; + +	csr_ctx_ptr_offs = nfp_get_ind_csr_ctx_ptr_offs(offset); +	cpp_id = NFP_CPP_ISLAND_ID(cpp_params.target, +				   NFP_IND_ME_REFL_WR_SIG_INIT, +				   cpp_params.token, cpp_params.island); +	result = nfp_cpp_writel(cpp, cpp_id, csr_ctx_ptr_offs, context); +	if (result) +		return result; + +	cpp_id = nfp_get_numeric_cpp_id(&cpp_params); +	result = nfp_cpp_read(cpp, cpp_id, csr_ctx_ptr_offs, dest, reg_sz); +	if (result != reg_sz) +		return result < 0 ? result : -EIO; + +	result = nfp_cpp_read(cpp, cpp_id, offset, dest, reg_sz); +	if (result != reg_sz) +		return result < 0 ? result : -EIO; + +	return 0; +} + +static int +nfp_read_all_indirect_csr_ctx(struct nfp_cpp *cpp, +			      struct nfp_dumpspec_csr *spec_csr, u32 address, +			      u32 reg_sz, void *dest) +{ +	u32 ctx; +	int err; + +	for (ctx = 0; ctx < NFP_IND_NUM_CONTEXTS; ctx++) { +		err = nfp_read_indirect_csr(cpp, spec_csr->cpp.cpp_id, address, +					    reg_sz, ctx, dest + ctx * reg_sz); +		if (err) +			return err; +	} + +	return 0; +} + +static int +nfp_dump_indirect_csr_range(struct nfp_pf *pf, +			    struct nfp_dumpspec_csr *spec_csr, +			    struct nfp_dump_state *dump) +{ +	struct nfp_dump_csr *dump_header = dump->p; +	u32 reg_sz, header_size, total_size; +	u32 cpp_rd_addr, max_rd_addr; +	u32 reg_data_length; +	void *dest; +	int err; + +	if (!nfp_csr_spec_valid(spec_csr)) +		return nfp_dump_error_tlv(&spec_csr->tl, -EINVAL, dump); + +	reg_sz = be32_to_cpu(spec_csr->register_width) / BITS_PER_BYTE; +	header_size = ALIGN8(sizeof(*dump_header)); +	reg_data_length = be32_to_cpu(spec_csr->cpp.dump_length) * +			  NFP_IND_NUM_CONTEXTS; +	total_size = header_size + ALIGN8(reg_data_length); +	dest = dump->p + header_size; + +	err = nfp_add_tlv(be32_to_cpu(spec_csr->tl.type), total_size, dump); +	if (err) +		return err; + +	dump_header->cpp = spec_csr->cpp; +	dump_header->register_width = spec_csr->register_width; + +	cpp_rd_addr = be32_to_cpu(spec_csr->cpp.offset); +	max_rd_addr = cpp_rd_addr + be32_to_cpu(spec_csr->cpp.dump_length); +	while (cpp_rd_addr < max_rd_addr) { +		err = nfp_read_all_indirect_csr_ctx(pf->cpp, spec_csr, +						    cpp_rd_addr, reg_sz, dest); +		if (err) { +			dump_header->error = cpu_to_be32(err); +			dump_header->error_offset = cpu_to_be32(cpp_rd_addr); +			break; +		} +		cpp_rd_addr += reg_sz; +		dest += reg_sz * NFP_IND_NUM_CONTEXTS; +	} + +	return 0; +} + +static int +nfp_dump_single_rtsym(struct nfp_pf *pf, struct nfp_dumpspec_rtsym *spec, +		      struct nfp_dump_state *dump) +{ +	struct nfp_dump_rtsym *dump_header = dump->p; +	struct nfp_dumpspec_cpp_isl_id cpp_params; +	struct nfp_rtsym_table *rtbl = pf->rtbl; +	u32 header_size, total_size, sym_size; +	const struct nfp_rtsym *sym; +	u32 tl_len, key_len; +	int bytes_read; +	u32 cpp_id; +	void *dest; +	int err; + +	tl_len = be32_to_cpu(spec->tl.length); +	key_len = strnlen(spec->rtsym, tl_len); +	if (key_len == tl_len) +		return nfp_dump_error_tlv(&spec->tl, -EINVAL, dump); + +	sym = nfp_rtsym_lookup(rtbl, spec->rtsym); +	if (!sym) +		return nfp_dump_error_tlv(&spec->tl, -ENOENT, dump); + +	if (sym->type == NFP_RTSYM_TYPE_ABS) +		sym_size = sizeof(sym->addr); +	else +		sym_size = sym->size; + +	header_size = +		ALIGN8(offsetof(struct nfp_dump_rtsym, rtsym) + key_len + 1); +	total_size = header_size + ALIGN8(sym_size); +	dest = dump->p + header_size; + +	err = nfp_add_tlv(be32_to_cpu(spec->tl.type), total_size, dump); +	if (err) +		return err; + +	dump_header->padded_name_length = +		header_size - offsetof(struct nfp_dump_rtsym, rtsym); +	memcpy(dump_header->rtsym, spec->rtsym, key_len + 1); +	dump_header->cpp.dump_length = cpu_to_be32(sym_size); + +	if (sym->type == NFP_RTSYM_TYPE_ABS) { +		*(u64 *)dest = sym->addr; +	} else { +		cpp_params.target = sym->target; +		cpp_params.action = NFP_CPP_ACTION_RW; +		cpp_params.token  = 0; +		cpp_params.island = sym->domain; +		cpp_id = nfp_get_numeric_cpp_id(&cpp_params); +		dump_header->cpp.cpp_id = cpp_params; +		dump_header->cpp.offset = cpu_to_be32(sym->addr); +		bytes_read = nfp_cpp_read(pf->cpp, cpp_id, sym->addr, dest, +					  sym_size); +		if (bytes_read != sym_size) { +			if (bytes_read >= 0) +				bytes_read = -EIO; +			dump_header->error = cpu_to_be32(bytes_read); +		} +	} + +	return 0; +} + +static int +nfp_dump_for_tlv(struct nfp_pf *pf, struct nfp_dump_tl *tl, void *param) +{ +	struct nfp_dumpspec_rtsym *spec_rtsym; +	struct nfp_dump_state *dump = param; +	struct nfp_dumpspec_csr *spec_csr; +	int err; + +	switch (be32_to_cpu(tl->type)) { +	case NFP_DUMPSPEC_TYPE_FWNAME: +		err = nfp_dump_fwname(pf, dump); +		if (err) +			return err; +		break; +	case NFP_DUMPSPEC_TYPE_CPP_CSR: +	case NFP_DUMPSPEC_TYPE_XPB_CSR: +	case NFP_DUMPSPEC_TYPE_ME_CSR: +		spec_csr = (struct nfp_dumpspec_csr *)tl; +		err = nfp_dump_csr_range(pf, spec_csr, dump); +		if (err) +			return err; +		break; +	case NFP_DUMPSPEC_TYPE_INDIRECT_ME_CSR: +		spec_csr = (struct nfp_dumpspec_csr *)tl; +		err = nfp_dump_indirect_csr_range(pf, spec_csr, dump); +		if (err) +			return err; +		break; +	case NFP_DUMPSPEC_TYPE_RTSYM: +		spec_rtsym = (struct nfp_dumpspec_rtsym *)tl; +		err = nfp_dump_single_rtsym(pf, spec_rtsym, dump); +		if (err) +			return err; +		break; +	case NFP_DUMPSPEC_TYPE_HWINFO: +		err = nfp_dump_hwinfo(pf, tl, dump); +		if (err) +			return err; +		break; +	case NFP_DUMPSPEC_TYPE_HWINFO_FIELD: +		err = nfp_dump_hwinfo_field(pf, tl, dump); +		if (err) +			return err; +		break; +	default: +		err = nfp_dump_error_tlv(tl, -EOPNOTSUPP, dump); +		if (err) +			return err; +	} + +	return 0; +} + +static int +nfp_dump_specific_level(struct nfp_pf *pf, struct nfp_dump_tl *dump_level, +			void *param) +{ +	struct nfp_dump_state *dump = param; + +	if (dump_level->type != dump->requested_level) +		return 0; + +	return nfp_traverse_tlvs(pf, dump_level->data, +				 be32_to_cpu(dump_level->length), dump, +				 nfp_dump_for_tlv); +} + +static int nfp_dump_populate_prolog(struct nfp_dump_state *dump) +{ +	struct nfp_dump_prolog *prolog = dump->p; +	u32 total_size; +	int err; + +	total_size = ALIGN8(sizeof(*prolog)); + +	err = nfp_add_tlv(NFP_DUMPSPEC_TYPE_PROLOG, total_size, dump); +	if (err) +		return err; + +	prolog->dump_level = dump->requested_level; + +	return 0; +} + +int nfp_net_dump_populate_buffer(struct nfp_pf *pf, struct nfp_dumpspec *spec, +				 struct ethtool_dump *dump_param, void *dest) +{ +	struct nfp_dump_state dump; +	int err; + +	dump.requested_level = cpu_to_be32(dump_param->flag); +	dump.dumped_size = 0; +	dump.p = dest; +	dump.buf_size = dump_param->len; + +	err = nfp_dump_populate_prolog(&dump); +	if (err) +		return err; + +	err = nfp_traverse_tlvs(pf, spec->data, spec->size, &dump, +				nfp_dump_specific_level); +	if (err) +		return err; + +	/* Set size of actual dump, to trigger warning if different from +	 * calculated size. +	 */ +	dump_param->len = dump.dumped_size; + +	return 0; +} diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index 6c02b2d6ba06..e1dae0616f52 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -47,18 +47,16 @@  #include <linux/interrupt.h>  #include <linux/pci.h>  #include <linux/ethtool.h> +#include <linux/firmware.h>  #include "nfpcore/nfp.h"  #include "nfpcore/nfp_nsp.h"  #include "nfp_app.h" +#include "nfp_main.h"  #include "nfp_net_ctrl.h"  #include "nfp_net.h"  #include "nfp_port.h" -enum nfp_dump_diag { -	NFP_DUMP_NSP_DIAG = 0, -}; -  struct nfp_et_stat {  	char name[ETH_GSTRING_LEN];  	int off; @@ -1066,15 +1064,34 @@ exit_release:  	return ret;  } +/* Set the dump flag/level. Calculate the dump length for flag > 0 only (new TLV + * based dumps), since flag 0 (default) calculates the length in + * nfp_app_get_dump_flag(), and we need to support triggering a level 0 dump + * without setting the flag first, for backward compatibility. + */  static int nfp_app_set_dump(struct net_device *netdev, struct ethtool_dump *val)  {  	struct nfp_app *app = nfp_app_from_netdev(netdev); +	s64 len;  	if (!app)  		return -EOPNOTSUPP; -	if (val->flag != NFP_DUMP_NSP_DIAG) -		return -EINVAL; +	if (val->flag == NFP_DUMP_NSP_DIAG) { +		app->pf->dump_flag = val->flag; +		return 0; +	} + +	if (!app->pf->dumpspec) +		return -EOPNOTSUPP; + +	len = nfp_net_dump_calculate_size(app->pf, app->pf->dumpspec, +					  val->flag); +	if (len < 0) +		return len; + +	app->pf->dump_flag = val->flag; +	app->pf->dump_len = len;  	return 0;  } @@ -1082,14 +1099,37 @@ static int nfp_app_set_dump(struct net_device *netdev, struct ethtool_dump *val)  static int  nfp_app_get_dump_flag(struct net_device *netdev, struct ethtool_dump *dump)  { -	return nfp_dump_nsp_diag(nfp_app_from_netdev(netdev), dump, NULL); +	struct nfp_app *app = nfp_app_from_netdev(netdev); + +	if (!app) +		return -EOPNOTSUPP; + +	if (app->pf->dump_flag == NFP_DUMP_NSP_DIAG) +		return nfp_dump_nsp_diag(app, dump, NULL); + +	dump->flag = app->pf->dump_flag; +	dump->len = app->pf->dump_len; + +	return 0;  }  static int  nfp_app_get_dump_data(struct net_device *netdev, struct ethtool_dump *dump,  		      void *buffer)  { -	return nfp_dump_nsp_diag(nfp_app_from_netdev(netdev), dump, buffer); +	struct nfp_app *app = nfp_app_from_netdev(netdev); + +	if (!app) +		return -EOPNOTSUPP; + +	if (app->pf->dump_flag == NFP_DUMP_NSP_DIAG) +		return nfp_dump_nsp_diag(app, dump, buffer); + +	dump->flag = app->pf->dump_flag; +	dump->len = app->pf->dump_len; + +	return nfp_net_dump_populate_buffer(app->pf, app->pf->dumpspec, dump, +					    buffer);  }  static int nfp_net_set_coalesce(struct net_device *netdev, @@ -1230,6 +1270,57 @@ static int nfp_net_set_channels(struct net_device *netdev,  	return nfp_net_set_num_rings(nn, total_rx, total_tx);  } +static int +nfp_net_flash_device(struct net_device *netdev, struct ethtool_flash *flash) +{ +	const struct firmware *fw; +	struct nfp_app *app; +	struct nfp_nsp *nsp; +	struct device *dev; +	int err; + +	if (flash->region != ETHTOOL_FLASH_ALL_REGIONS) +		return -EOPNOTSUPP; + +	app = nfp_app_from_netdev(netdev); +	if (!app) +		return -EOPNOTSUPP; + +	dev = &app->pdev->dev; + +	nsp = nfp_nsp_open(app->cpp); +	if (IS_ERR(nsp)) { +		err = PTR_ERR(nsp); +		dev_err(dev, "Failed to access the NSP: %d\n", err); +		return err; +	} + +	err = request_firmware_direct(&fw, flash->data, dev); +	if (err) +		goto exit_close_nsp; + +	dev_info(dev, "Please be patient while writing flash image: %s\n", +		 flash->data); +	dev_hold(netdev); +	rtnl_unlock(); + +	err = nfp_nsp_write_flash(nsp, fw); +	if (err < 0) { +		dev_err(dev, "Flash write failed: %d\n", err); +		goto exit_rtnl_lock; +	} +	dev_info(dev, "Finished writing flash image\n"); + +exit_rtnl_lock: +	rtnl_lock(); +	dev_put(netdev); +	release_firmware(fw); + +exit_close_nsp: +	nfp_nsp_close(nsp); +	return err; +} +  static const struct ethtool_ops nfp_net_ethtool_ops = {  	.get_drvinfo		= nfp_net_get_drvinfo,  	.get_link		= ethtool_op_get_link, @@ -1240,6 +1331,7 @@ static const struct ethtool_ops nfp_net_ethtool_ops = {  	.get_sset_count		= nfp_net_get_sset_count,  	.get_rxnfc		= nfp_net_get_rxnfc,  	.set_rxnfc		= nfp_net_set_rxnfc, +	.flash_device		= nfp_net_flash_device,  	.get_rxfh_indir_size	= nfp_net_get_rxfh_indir_size,  	.get_rxfh_key_size	= nfp_net_get_rxfh_key_size,  	.get_rxfh		= nfp_net_get_rxfh, @@ -1265,6 +1357,7 @@ const struct ethtool_ops nfp_port_ethtool_ops = {  	.get_strings		= nfp_port_get_strings,  	.get_ethtool_stats	= nfp_port_get_stats,  	.get_sset_count		= nfp_port_get_sset_count, +	.flash_device		= nfp_net_flash_device,  	.set_dump		= nfp_app_set_dump,  	.get_dump_flag		= nfp_app_get_dump_flag,  	.get_dump_data		= nfp_app_get_dump_data, diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c index c505014121c4..15fa47f622aa 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c @@ -208,12 +208,6 @@ nfp_net_pf_init_vnic(struct nfp_pf *pf, struct nfp_net *nn, unsigned int id)  {  	int err; -	/* Get ME clock frequency from ctrl BAR -	 * XXX for now frequency is hardcoded until we figure out how -	 * to get the value from nfp-hwinfo into ctrl bar -	 */ -	nn->me_freq_mhz = 1200; -  	err = nfp_net_init(nn);  	if (err)  		return err; @@ -373,7 +367,9 @@ nfp_net_pf_app_init(struct nfp_pf *pf, u8 __iomem *qc_bar, unsigned int stride)  	if (IS_ERR(pf->app))  		return PTR_ERR(pf->app); +	mutex_lock(&pf->lock);  	err = nfp_app_init(pf->app); +	mutex_unlock(&pf->lock);  	if (err)  		goto err_free; @@ -401,7 +397,9 @@ nfp_net_pf_app_init(struct nfp_pf *pf, u8 __iomem *qc_bar, unsigned int stride)  err_unmap:  	nfp_cpp_area_release_free(pf->ctrl_vnic_bar);  err_app_clean: +	mutex_lock(&pf->lock);  	nfp_app_clean(pf->app); +	mutex_unlock(&pf->lock);  err_free:  	nfp_app_free(pf->app);  	pf->app = NULL; @@ -414,7 +412,11 @@ static void nfp_net_pf_app_clean(struct nfp_pf *pf)  		nfp_net_pf_free_vnic(pf, pf->ctrl_vnic);  		nfp_cpp_area_release_free(pf->ctrl_vnic_bar);  	} + +	mutex_lock(&pf->lock);  	nfp_app_clean(pf->app); +	mutex_unlock(&pf->lock); +  	nfp_app_free(pf->app);  	pf->app = NULL;  } @@ -570,17 +572,6 @@ err_unmap_ctrl:  	return err;  } -static void nfp_net_pci_remove_finish(struct nfp_pf *pf) -{ -	nfp_net_pf_app_stop(pf); -	/* stop app first, to avoid double free of ctrl vNIC's ddir */ -	nfp_net_debugfs_dir_clean(&pf->ddir); - -	nfp_net_pf_free_irqs(pf); -	nfp_net_pf_app_clean(pf); -	nfp_net_pci_unmap_mem(pf); -} -  static int  nfp_net_eth_port_update(struct nfp_cpp *cpp, struct nfp_port *port,  			struct nfp_eth_table *eth_table) @@ -655,9 +646,6 @@ int nfp_net_refresh_port_table_sync(struct nfp_pf *pf)  		nfp_net_pf_free_vnic(pf, nn);  	} -	if (list_empty(&pf->vnics)) -		nfp_net_pci_remove_finish(pf); -  	return 0;  } @@ -707,6 +695,7 @@ int nfp_net_refresh_eth_port(struct nfp_port *port)   */  int nfp_net_pci_probe(struct nfp_pf *pf)  { +	struct devlink *devlink = priv_to_devlink(pf);  	struct nfp_net_fw_version fw_ver;  	u8 __iomem *ctrl_bar, *qc_bar;  	int stride; @@ -720,16 +709,13 @@ int nfp_net_pci_probe(struct nfp_pf *pf)  		return -EINVAL;  	} -	mutex_lock(&pf->lock);  	pf->max_data_vnics = nfp_net_pf_get_num_ports(pf); -	if ((int)pf->max_data_vnics < 0) { -		err = pf->max_data_vnics; -		goto err_unlock; -	} +	if ((int)pf->max_data_vnics < 0) +		return pf->max_data_vnics;  	err = nfp_net_pci_map_mem(pf);  	if (err) -		goto err_unlock; +		return err;  	ctrl_bar = nfp_cpp_area_iomem(pf->data_vnic_bar);  	qc_bar = nfp_cpp_area_iomem(pf->qc_area); @@ -768,6 +754,11 @@ int nfp_net_pci_probe(struct nfp_pf *pf)  	if (err)  		goto err_unmap; +	err = devlink_register(devlink, &pf->pdev->dev); +	if (err) +		goto err_app_clean; + +	mutex_lock(&pf->lock);  	pf->ddir = nfp_net_debugfs_device_add(pf->pdev);  	/* Allocate the vnics and do basic init */ @@ -799,32 +790,39 @@ err_free_vnics:  	nfp_net_pf_free_vnics(pf);  err_clean_ddir:  	nfp_net_debugfs_dir_clean(&pf->ddir); +	mutex_unlock(&pf->lock); +	cancel_work_sync(&pf->port_refresh_work); +	devlink_unregister(devlink); +err_app_clean:  	nfp_net_pf_app_clean(pf);  err_unmap:  	nfp_net_pci_unmap_mem(pf); -err_unlock: -	mutex_unlock(&pf->lock); -	cancel_work_sync(&pf->port_refresh_work);  	return err;  }  void nfp_net_pci_remove(struct nfp_pf *pf)  { -	struct nfp_net *nn; +	struct nfp_net *nn, *next;  	mutex_lock(&pf->lock); -	if (list_empty(&pf->vnics)) -		goto out; - -	list_for_each_entry(nn, &pf->vnics, vnic_list) -		if (nfp_net_is_data_vnic(nn)) -			nfp_net_pf_clean_vnic(pf, nn); +	list_for_each_entry_safe(nn, next, &pf->vnics, vnic_list) { +		if (!nfp_net_is_data_vnic(nn)) +			continue; +		nfp_net_pf_clean_vnic(pf, nn); +		nfp_net_pf_free_vnic(pf, nn); +	} -	nfp_net_pf_free_vnics(pf); +	nfp_net_pf_app_stop(pf); +	/* stop app first, to avoid double free of ctrl vNIC's ddir */ +	nfp_net_debugfs_dir_clean(&pf->ddir); -	nfp_net_pci_remove_finish(pf); -out:  	mutex_unlock(&pf->lock); +	devlink_unregister(priv_to_devlink(pf)); + +	nfp_net_pf_free_irqs(pf); +	nfp_net_pf_app_clean(pf); +	nfp_net_pci_unmap_mem(pf); +  	cancel_work_sync(&pf->port_refresh_work);  } diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c b/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c index 78b36c67c232..619570524d2a 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c @@ -46,6 +46,13 @@  #include "nfp_net_sriov.h"  #include "nfp_port.h" +struct net_device * +nfp_repr_get_locked(struct nfp_app *app, struct nfp_reprs *set, unsigned int id) +{ +	return rcu_dereference_protected(set->reprs[id], +					 lockdep_is_held(&app->pf->lock)); +} +  static void  nfp_repr_inc_tx_stats(struct net_device *netdev, unsigned int len,  		      int tx_status) @@ -186,6 +193,13 @@ nfp_repr_get_offload_stats(int attr_id, const struct net_device *dev,  	return -EINVAL;  } +static int nfp_repr_change_mtu(struct net_device *netdev, int new_mtu) +{ +	struct nfp_repr *repr = netdev_priv(netdev); + +	return nfp_app_change_mtu(repr->app, netdev, new_mtu); +} +  static netdev_tx_t nfp_repr_xmit(struct sk_buff *skb, struct net_device *netdev)  {  	struct nfp_repr *repr = netdev_priv(netdev); @@ -240,6 +254,7 @@ const struct net_device_ops nfp_repr_netdev_ops = {  	.ndo_open		= nfp_repr_open,  	.ndo_stop		= nfp_repr_stop,  	.ndo_start_xmit		= nfp_repr_xmit, +	.ndo_change_mtu		= nfp_repr_change_mtu,  	.ndo_get_stats64	= nfp_repr_get_stats64,  	.ndo_has_offload_stats	= nfp_repr_has_offload_stats,  	.ndo_get_offload_stats	= nfp_repr_get_offload_stats, @@ -250,6 +265,7 @@ const struct net_device_ops nfp_repr_netdev_ops = {  	.ndo_set_vf_spoofchk	= nfp_app_set_vf_spoofchk,  	.ndo_get_vf_config	= nfp_app_get_vf_config,  	.ndo_set_vf_link_state	= nfp_app_set_vf_link_state, +	.ndo_set_features	= nfp_port_set_features,  };  static void nfp_repr_clean(struct nfp_repr *repr) @@ -336,6 +352,8 @@ struct net_device *nfp_repr_alloc(struct nfp_app *app)  	if (!netdev)  		return NULL; +	netif_carrier_off(netdev); +  	repr = netdev_priv(netdev);  	repr->netdev = netdev;  	repr->app = app; @@ -359,29 +377,45 @@ static void nfp_repr_clean_and_free(struct nfp_repr *repr)  	nfp_repr_free(repr);  } -void nfp_reprs_clean_and_free(struct nfp_reprs *reprs) +void nfp_reprs_clean_and_free(struct nfp_app *app, struct nfp_reprs *reprs)  { +	struct net_device *netdev;  	unsigned int i; -	for (i = 0; i < reprs->num_reprs; i++) -		if (reprs->reprs[i]) -			nfp_repr_clean_and_free(netdev_priv(reprs->reprs[i])); +	for (i = 0; i < reprs->num_reprs; i++) { +		netdev = nfp_repr_get_locked(app, reprs, i); +		if (netdev) +			nfp_repr_clean_and_free(netdev_priv(netdev)); +	}  	kfree(reprs);  }  void -nfp_reprs_clean_and_free_by_type(struct nfp_app *app, -				 enum nfp_repr_type type) +nfp_reprs_clean_and_free_by_type(struct nfp_app *app, enum nfp_repr_type type)  { +	struct net_device *netdev;  	struct nfp_reprs *reprs; +	int i; -	reprs = nfp_app_reprs_set(app, type, NULL); +	reprs = rcu_dereference_protected(app->reprs[type], +					  lockdep_is_held(&app->pf->lock));  	if (!reprs)  		return; +	/* Preclean must happen before we remove the reprs reference from the +	 * app below. +	 */ +	for (i = 0; i < reprs->num_reprs; i++) { +		netdev = nfp_repr_get_locked(app, reprs, i); +		if (netdev) +			nfp_app_repr_preclean(app, netdev); +	} + +	reprs = nfp_app_reprs_set(app, type, NULL); +  	synchronize_rcu(); -	nfp_reprs_clean_and_free(reprs); +	nfp_reprs_clean_and_free(app, reprs);  }  struct nfp_reprs *nfp_reprs_alloc(unsigned int num_reprs) @@ -399,47 +433,29 @@ struct nfp_reprs *nfp_reprs_alloc(unsigned int num_reprs)  int nfp_reprs_resync_phys_ports(struct nfp_app *app)  { -	struct nfp_reprs *reprs, *old_reprs; +	struct net_device *netdev; +	struct nfp_reprs *reprs;  	struct nfp_repr *repr;  	int i; -	old_reprs = -		rcu_dereference_protected(app->reprs[NFP_REPR_TYPE_PHYS_PORT], -					  lockdep_is_held(&app->pf->lock)); -	if (!old_reprs) -		return 0; - -	reprs = nfp_reprs_alloc(old_reprs->num_reprs); +	reprs = nfp_reprs_get_locked(app, NFP_REPR_TYPE_PHYS_PORT);  	if (!reprs) -		return -ENOMEM; - -	for (i = 0; i < old_reprs->num_reprs; i++) { -		if (!old_reprs->reprs[i]) -			continue; - -		repr = netdev_priv(old_reprs->reprs[i]); -		if (repr->port->type == NFP_PORT_INVALID) -			continue; - -		reprs->reprs[i] = old_reprs->reprs[i]; -	} - -	old_reprs = nfp_app_reprs_set(app, NFP_REPR_TYPE_PHYS_PORT, reprs); -	synchronize_rcu(); +		return 0; -	/* Now we free up removed representors */ -	for (i = 0; i < old_reprs->num_reprs; i++) { -		if (!old_reprs->reprs[i]) +	for (i = 0; i < reprs->num_reprs; i++) { +		netdev = nfp_repr_get_locked(app, reprs, i); +		if (!netdev)  			continue; -		repr = netdev_priv(old_reprs->reprs[i]); +		repr = netdev_priv(netdev);  		if (repr->port->type != NFP_PORT_INVALID)  			continue; -		nfp_app_repr_stop(app, repr); +		nfp_app_repr_preclean(app, netdev); +		rcu_assign_pointer(reprs->reprs[i], NULL); +		synchronize_rcu();  		nfp_repr_clean(repr);  	} -	kfree(old_reprs);  	return 0;  } diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_repr.h b/drivers/net/ethernet/netronome/nfp/nfp_net_repr.h index 5d4d897bc9c6..a621e8ff528e 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_repr.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_repr.h @@ -35,6 +35,7 @@  #define NFP_NET_REPR_H  struct metadata_dst; +struct nfp_app;  struct nfp_net;  struct nfp_port; @@ -47,7 +48,7 @@ struct nfp_port;   */  struct nfp_reprs {  	unsigned int num_reprs; -	struct net_device *reprs[0]; +	struct net_device __rcu *reprs[0];  };  /** @@ -89,6 +90,7 @@ struct nfp_repr {   * @NFP_REPR_TYPE_PHYS_PORT:	external NIC port   * @NFP_REPR_TYPE_PF:		physical function   * @NFP_REPR_TYPE_VF:		virtual function + * @__NFP_REPR_TYPE_MAX:	number of representor types   */  enum nfp_repr_type {  	NFP_REPR_TYPE_PHYS_PORT, @@ -113,16 +115,18 @@ static inline int nfp_repr_get_port_id(struct net_device *netdev)  	return priv->dst->u.port_info.port_id;  } +struct net_device * +nfp_repr_get_locked(struct nfp_app *app, struct nfp_reprs *set, +		    unsigned int id); +  void nfp_repr_inc_rx_stats(struct net_device *netdev, unsigned int len);  int nfp_repr_init(struct nfp_app *app, struct net_device *netdev,  		  u32 cmsg_port_id, struct nfp_port *port,  		  struct net_device *pf_netdev);  struct net_device *nfp_repr_alloc(struct nfp_app *app); -void -nfp_reprs_clean_and_free(struct nfp_reprs *reprs); -void -nfp_reprs_clean_and_free_by_type(struct nfp_app *app, -				 enum nfp_repr_type type); +void nfp_reprs_clean_and_free(struct nfp_app *app, struct nfp_reprs *reprs); +void nfp_reprs_clean_and_free_by_type(struct nfp_app *app, +				      enum nfp_repr_type type);  struct nfp_reprs *nfp_reprs_alloc(unsigned int num_reprs);  int nfp_reprs_resync_phys_ports(struct nfp_app *app); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c b/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c index c879626e035b..b802a1d55449 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c @@ -277,12 +277,6 @@ static int nfp_netvf_pci_probe(struct pci_dev *pdev,  	}  	nfp_net_irqs_assign(nn, vf->irq_entries, num_irqs); -	/* Get ME clock frequency from ctrl BAR -	 * XXX for now frequency is hardcoded until we figure out how -	 * to get the value from nfp-hwinfo into ctrl bar -	 */ -	nn->me_freq_mhz = 1200; -  	err = nfp_net_init(nn);  	if (err)  		goto err_irqs_disable; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_port.c b/drivers/net/ethernet/netronome/nfp/nfp_port.c index 34a6e035fe9a..7bd8be5c833b 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_port.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_port.c @@ -32,6 +32,7 @@   */  #include <linux/lockdep.h> +#include <linux/netdevice.h>  #include <net/switchdev.h>  #include "nfpcore/nfp_cpp.h" @@ -100,6 +101,23 @@ int nfp_port_setup_tc(struct net_device *netdev, enum tc_setup_type type,  	return nfp_app_setup_tc(port->app, netdev, type, type_data);  } +int nfp_port_set_features(struct net_device *netdev, netdev_features_t features) +{ +	struct nfp_port *port; + +	port = nfp_port_from_netdev(netdev); +	if (!port) +		return 0; + +	if ((netdev->features & NETIF_F_HW_TC) > (features & NETIF_F_HW_TC) && +	    port->tc_offload_cnt) { +		netdev_err(netdev, "Cannot disable HW TC offload while offloads active\n"); +		return -EBUSY; +	} + +	return 0; +} +  struct nfp_port *  nfp_port_from_id(struct nfp_pf *pf, enum nfp_port_type type, unsigned int id)  { diff --git a/drivers/net/ethernet/netronome/nfp/nfp_port.h b/drivers/net/ethernet/netronome/nfp/nfp_port.h index 21bd4aa32646..fa7e669a969c 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_port.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_port.h @@ -72,6 +72,8 @@ enum nfp_port_flags {   * @netdev:	backpointer to associated netdev   * @type:	what port type does the entity represent   * @flags:	port flags + * @tc_offload_cnt:	number of active TC offloads, how offloads are counted + *			is not defined, use as a boolean   * @app:	backpointer to the app structure   * @dl_port:	devlink port structure   * @eth_id:	for %NFP_PORT_PHYS_PORT port ID in NFP enumeration scheme @@ -87,6 +89,7 @@ struct nfp_port {  	enum nfp_port_type type;  	unsigned long flags; +	unsigned long tc_offload_cnt;  	struct nfp_app *app; @@ -121,6 +124,9 @@ static inline bool nfp_port_is_vnic(const struct nfp_port *port)  	return port->type == NFP_PORT_PF_PORT || port->type == NFP_PORT_VF_PORT;  } +int +nfp_port_set_features(struct net_device *netdev, netdev_features_t features); +  struct nfp_port *nfp_port_from_netdev(struct net_device *netdev);  struct nfp_port *  nfp_port_from_id(struct nfp_pf *pf, enum nfp_port_type type, unsigned int id); diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h index 3ce51f03126f..ced62d112aa2 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h @@ -49,6 +49,8 @@  struct nfp_hwinfo;  struct nfp_hwinfo *nfp_hwinfo_read(struct nfp_cpp *cpp);  const char *nfp_hwinfo_lookup(struct nfp_hwinfo *hwinfo, const char *lookup); +char *nfp_hwinfo_get_packed_strings(struct nfp_hwinfo *hwinfo); +u32 nfp_hwinfo_get_packed_str_size(struct nfp_hwinfo *hwinfo);  /* Implemented in nfp_nsp.c, low level functions */ diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cpp.h b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cpp.h index 5798adc57cbc..c8f2c064cce3 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cpp.h +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cpp.h @@ -242,6 +242,7 @@ int nfp_cpp_area_read(struct nfp_cpp_area *area, unsigned long offset,  		      void *buffer, size_t length);  int nfp_cpp_area_write(struct nfp_cpp_area *area, unsigned long offset,  		       const void *buffer, size_t length); +size_t nfp_cpp_area_size(struct nfp_cpp_area *area);  const char *nfp_cpp_area_name(struct nfp_cpp_area *cpp_area);  void *nfp_cpp_area_priv(struct nfp_cpp_area *cpp_area);  struct nfp_cpp *nfp_cpp_area_cpp(struct nfp_cpp_area *cpp_area); diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c index 04dd5758ecf5..ef30597aa319 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c @@ -372,8 +372,7 @@ nfp_cpp_area_alloc(struct nfp_cpp *cpp, u32 dest,   * that it can be accessed directly.   *   * NOTE: @address and @size must be 32-bit aligned values. - * - * NOTE: The area must also be 'released' when the structure is freed. + * The area must also be 'released' when the structure is freed.   *   * Return: NFP CPP Area handle, or NULL   */ @@ -536,8 +535,7 @@ void nfp_cpp_area_release_free(struct nfp_cpp_area *area)   * Read data from indicated CPP region.   *   * NOTE: @offset and @length must be 32-bit aligned values. - * - * NOTE: Area must have been locked down with an 'acquire'. + * Area must have been locked down with an 'acquire'.   *   * Return: length of io, or -ERRNO   */ @@ -558,8 +556,7 @@ int nfp_cpp_area_read(struct nfp_cpp_area *area,   * Write data to indicated CPP region.   *   * NOTE: @offset and @length must be 32-bit aligned values. - * - * NOTE: Area must have been locked down with an 'acquire'. + * Area must have been locked down with an 'acquire'.   *   * Return: length of io, or -ERRNO   */ @@ -571,6 +568,17 @@ int nfp_cpp_area_write(struct nfp_cpp_area *area,  }  /** + * nfp_cpp_area_size() - return size of a CPP area + * @cpp_area:	CPP area handle + * + * Return: Size of the area + */ +size_t nfp_cpp_area_size(struct nfp_cpp_area *cpp_area) +{ +	return cpp_area->size; +} + +/**   * nfp_cpp_area_name() - return name of a CPP area   * @cpp_area:	CPP area handle   * @@ -666,18 +674,20 @@ void __iomem *nfp_cpp_area_iomem(struct nfp_cpp_area *area)   * @offset:	Offset into area   * @value:	Pointer to read buffer   * - * Return: length of the io, or -ERRNO + * Return: 0 on success, or -ERRNO   */  int nfp_cpp_area_readl(struct nfp_cpp_area *area,  		       unsigned long offset, u32 *value)  {  	u8 tmp[4]; -	int err; +	int n; -	err = nfp_cpp_area_read(area, offset, &tmp, sizeof(tmp)); -	*value = get_unaligned_le32(tmp); +	n = nfp_cpp_area_read(area, offset, &tmp, sizeof(tmp)); +	if (n != sizeof(tmp)) +		return n < 0 ? n : -EIO; -	return err; +	*value = get_unaligned_le32(tmp); +	return 0;  }  /** @@ -686,16 +696,18 @@ int nfp_cpp_area_readl(struct nfp_cpp_area *area,   * @offset:	Offset into area   * @value:	Value to write   * - * Return: length of the io, or -ERRNO + * Return: 0 on success, or -ERRNO   */  int nfp_cpp_area_writel(struct nfp_cpp_area *area,  			unsigned long offset, u32 value)  {  	u8 tmp[4]; +	int n;  	put_unaligned_le32(value, tmp); +	n = nfp_cpp_area_write(area, offset, &tmp, sizeof(tmp)); -	return nfp_cpp_area_write(area, offset, &tmp, sizeof(tmp)); +	return n == sizeof(tmp) ? 0 : n < 0 ? n : -EIO;  }  /** @@ -704,18 +716,20 @@ int nfp_cpp_area_writel(struct nfp_cpp_area *area,   * @offset:	Offset into area   * @value:	Pointer to read buffer   * - * Return: length of the io, or -ERRNO + * Return: 0 on success, or -ERRNO   */  int nfp_cpp_area_readq(struct nfp_cpp_area *area,  		       unsigned long offset, u64 *value)  {  	u8 tmp[8]; -	int err; +	int n; -	err = nfp_cpp_area_read(area, offset, &tmp, sizeof(tmp)); -	*value = get_unaligned_le64(tmp); +	n = nfp_cpp_area_read(area, offset, &tmp, sizeof(tmp)); +	if (n != sizeof(tmp)) +		return n < 0 ? n : -EIO; -	return err; +	*value = get_unaligned_le64(tmp); +	return 0;  }  /** @@ -724,16 +738,18 @@ int nfp_cpp_area_readq(struct nfp_cpp_area *area,   * @offset:	Offset into area   * @value:	Value to write   * - * Return: length of the io, or -ERRNO + * Return: 0 on success, or -ERRNO   */  int nfp_cpp_area_writeq(struct nfp_cpp_area *area,  			unsigned long offset, u64 value)  {  	u8 tmp[8]; +	int n;  	put_unaligned_le64(value, tmp); +	n = nfp_cpp_area_write(area, offset, &tmp, sizeof(tmp)); -	return nfp_cpp_area_write(area, offset, &tmp, sizeof(tmp)); +	return n == sizeof(tmp) ? 0 : n < 0 ? n : -EIO;  }  /** @@ -1072,7 +1088,7 @@ static u32 nfp_xpb_to_cpp(struct nfp_cpp *cpp, u32 *xpb_addr)   * @xpb_addr:	Address for operation   * @value:	Pointer to read buffer   * - * Return: length of the io, or -ERRNO + * Return: 0 on success, or -ERRNO   */  int nfp_xpb_readl(struct nfp_cpp *cpp, u32 xpb_addr, u32 *value)  { @@ -1087,7 +1103,7 @@ int nfp_xpb_readl(struct nfp_cpp *cpp, u32 xpb_addr, u32 *value)   * @xpb_addr:	Address for operation   * @value:	Value to write   * - * Return: length of the io, or -ERRNO + * Return: 0 on success, or -ERRNO   */  int nfp_xpb_writel(struct nfp_cpp *cpp, u32 xpb_addr, u32 value)  { @@ -1105,7 +1121,7 @@ int nfp_xpb_writel(struct nfp_cpp *cpp, u32 xpb_addr, u32 value)   *   * KERNEL: This operation is safe to call in interrupt or softirq context.   * - * Return: length of the io, or -ERRNO + * Return: 0 on success, or -ERRNO   */  int nfp_xpb_writelm(struct nfp_cpp *cpp, u32 xpb_tgt,  		    u32 mask, u32 value) diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cpplib.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cpplib.c index ab86bceb93f2..20bad05e2e92 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cpplib.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cpplib.c @@ -64,18 +64,20 @@   * @address:	Address for operation   * @value:	Pointer to read buffer   * - * Return: length of the io, or -ERRNO + * Return: 0 on success, or -ERRNO   */  int nfp_cpp_readl(struct nfp_cpp *cpp, u32 cpp_id,  		  unsigned long long address, u32 *value)  {  	u8 tmp[4]; -	int err; +	int n; -	err = nfp_cpp_read(cpp, cpp_id, address, tmp, sizeof(tmp)); -	*value = get_unaligned_le32(tmp); +	n = nfp_cpp_read(cpp, cpp_id, address, tmp, sizeof(tmp)); +	if (n != sizeof(tmp)) +		return n < 0 ? n : -EIO; -	return err; +	*value = get_unaligned_le32(tmp); +	return 0;  }  /** @@ -85,15 +87,18 @@ int nfp_cpp_readl(struct nfp_cpp *cpp, u32 cpp_id,   * @address:	Address for operation   * @value:	Value to write   * - * Return: length of the io, or -ERRNO + * Return: 0 on success, or -ERRNO   */  int nfp_cpp_writel(struct nfp_cpp *cpp, u32 cpp_id,  		   unsigned long long address, u32 value)  {  	u8 tmp[4]; +	int n;  	put_unaligned_le32(value, tmp); -	return nfp_cpp_write(cpp, cpp_id, address, tmp, sizeof(tmp)); +	n = nfp_cpp_write(cpp, cpp_id, address, tmp, sizeof(tmp)); + +	return n == sizeof(tmp) ? 0 : n < 0 ? n : -EIO;  }  /** @@ -103,18 +108,20 @@ int nfp_cpp_writel(struct nfp_cpp *cpp, u32 cpp_id,   * @address:	Address for operation   * @value:	Pointer to read buffer   * - * Return: length of the io, or -ERRNO + * Return: 0 on success, or -ERRNO   */  int nfp_cpp_readq(struct nfp_cpp *cpp, u32 cpp_id,  		  unsigned long long address, u64 *value)  {  	u8 tmp[8]; -	int err; +	int n; -	err = nfp_cpp_read(cpp, cpp_id, address, tmp, sizeof(tmp)); -	*value = get_unaligned_le64(tmp); +	n = nfp_cpp_read(cpp, cpp_id, address, tmp, sizeof(tmp)); +	if (n != sizeof(tmp)) +		return n < 0 ? n : -EIO; -	return err; +	*value = get_unaligned_le64(tmp); +	return 0;  }  /** @@ -124,15 +131,18 @@ int nfp_cpp_readq(struct nfp_cpp *cpp, u32 cpp_id,   * @address:	Address for operation   * @value:	Value to write   * - * Return: length of the io, or -ERRNO + * Return: 0 on success, or -ERRNO   */  int nfp_cpp_writeq(struct nfp_cpp *cpp, u32 cpp_id,  		   unsigned long long address, u64 value)  {  	u8 tmp[8]; +	int n;  	put_unaligned_le64(value, tmp); -	return nfp_cpp_write(cpp, cpp_id, address, tmp, sizeof(tmp)); +	n = nfp_cpp_write(cpp, cpp_id, address, tmp, sizeof(tmp)); + +	return n == sizeof(tmp) ? 0 : n < 0 ? n : -EIO;  }  /* NOTE: This code should not use nfp_xpb_* functions, diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_hwinfo.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_hwinfo.c index 4f24aff1e772..063a9a6243d6 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_hwinfo.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_hwinfo.c @@ -302,3 +302,13 @@ const char *nfp_hwinfo_lookup(struct nfp_hwinfo *hwinfo, const char *lookup)  	return NULL;  } + +char *nfp_hwinfo_get_packed_strings(struct nfp_hwinfo *hwinfo) +{ +	return hwinfo->data; +} + +u32 nfp_hwinfo_get_packed_str_size(struct nfp_hwinfo *hwinfo) +{ +	return le32_to_cpu(hwinfo->size) - sizeof(u32); +} diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c index 14a6d1ba51a9..39abac678b71 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c @@ -51,6 +51,9 @@  #include "nfp_cpp.h"  #include "nfp_nsp.h" +#define NFP_NSP_TIMEOUT_DEFAULT	30 +#define NFP_NSP_TIMEOUT_BOOT	30 +  /* Offsets relative to the CSR base */  #define NSP_STATUS		0x00  #define   NSP_STATUS_MAGIC	GENMASK_ULL(63, 48) @@ -93,6 +96,7 @@ enum nfp_nsp_cmd {  	SPCODE_FW_LOAD		= 6, /* Load fw from buffer, len in option */  	SPCODE_ETH_RESCAN	= 7, /* Rescan ETHs, write ETH_TABLE to buf */  	SPCODE_ETH_CONTROL	= 8, /* Update media config from buffer */ +	SPCODE_NSP_WRITE_FLASH	= 11, /* Load and flash image from buffer */  	SPCODE_NSP_SENSORS	= 12, /* Read NSP sensor(s) */  	SPCODE_NSP_IDENTIFY	= 13, /* Read NSP version */  }; @@ -260,10 +264,10 @@ u16 nfp_nsp_get_abi_ver_minor(struct nfp_nsp *state)  }  static int -nfp_nsp_wait_reg(struct nfp_cpp *cpp, u64 *reg, -		 u32 nsp_cpp, u64 addr, u64 mask, u64 val) +nfp_nsp_wait_reg(struct nfp_cpp *cpp, u64 *reg, u32 nsp_cpp, u64 addr, +		 u64 mask, u64 val, u32 timeout_sec)  { -	const unsigned long wait_until = jiffies + 30 * HZ; +	const unsigned long wait_until = jiffies + timeout_sec * HZ;  	int err;  	for (;;) { @@ -285,12 +289,13 @@ nfp_nsp_wait_reg(struct nfp_cpp *cpp, u64 *reg,  }  /** - * nfp_nsp_command() - Execute a command on the NFP Service Processor + * __nfp_nsp_command() - Execute a command on the NFP Service Processor   * @state:	NFP SP state   * @code:	NFP SP Command Code   * @option:	NFP SP Command Argument   * @buff_cpp:	NFP SP Buffer CPP Address info   * @buff_addr:	NFP SP Buffer Host address + * @timeout_sec:Timeout value to wait for completion in seconds   *   * Return: 0 for success with no result   * @@ -300,10 +305,11 @@ nfp_nsp_wait_reg(struct nfp_cpp *cpp, u64 *reg,   *	-ENODEV if the NSP is not a supported model   *	-EBUSY if the NSP is stuck   *	-EINTR if interrupted while waiting for completion - *	-ETIMEDOUT if the NSP took longer than 30 seconds to complete + *	-ETIMEDOUT if the NSP took longer than @timeout_sec seconds to complete   */ -static int nfp_nsp_command(struct nfp_nsp *state, u16 code, u32 option, -			   u32 buff_cpp, u64 buff_addr) +static int +__nfp_nsp_command(struct nfp_nsp *state, u16 code, u32 option, u32 buff_cpp, +		  u64 buff_addr, u32 timeout_sec)  {  	u64 reg, ret_val, nsp_base, nsp_buffer, nsp_status, nsp_command;  	struct nfp_cpp *cpp = state->cpp; @@ -341,8 +347,8 @@ static int nfp_nsp_command(struct nfp_nsp *state, u16 code, u32 option,  		return err;  	/* Wait for NSP_COMMAND_START to go to 0 */ -	err = nfp_nsp_wait_reg(cpp, ®, -			       nsp_cpp, nsp_command, NSP_COMMAND_START, 0); +	err = nfp_nsp_wait_reg(cpp, ®, nsp_cpp, nsp_command, +			       NSP_COMMAND_START, 0, NFP_NSP_TIMEOUT_DEFAULT);  	if (err) {  		nfp_err(cpp, "Error %d waiting for code 0x%04x to start\n",  			err, code); @@ -350,8 +356,8 @@ static int nfp_nsp_command(struct nfp_nsp *state, u16 code, u32 option,  	}  	/* Wait for NSP_STATUS_BUSY to go to 0 */ -	err = nfp_nsp_wait_reg(cpp, ®, -			       nsp_cpp, nsp_status, NSP_STATUS_BUSY, 0); +	err = nfp_nsp_wait_reg(cpp, ®, nsp_cpp, nsp_status, NSP_STATUS_BUSY, +			       0, timeout_sec);  	if (err) {  		nfp_err(cpp, "Error %d waiting for code 0x%04x to complete\n",  			err, code); @@ -374,9 +380,18 @@ static int nfp_nsp_command(struct nfp_nsp *state, u16 code, u32 option,  	return ret_val;  } -static int nfp_nsp_command_buf(struct nfp_nsp *nsp, u16 code, u32 option, -			       const void *in_buf, unsigned int in_size, -			       void *out_buf, unsigned int out_size) +static int +nfp_nsp_command(struct nfp_nsp *state, u16 code, u32 option, u32 buff_cpp, +		u64 buff_addr) +{ +	return __nfp_nsp_command(state, code, option, buff_cpp, buff_addr, +				 NFP_NSP_TIMEOUT_DEFAULT); +} + +static int +__nfp_nsp_command_buf(struct nfp_nsp *nsp, u16 code, u32 option, +		      const void *in_buf, unsigned int in_size, void *out_buf, +		      unsigned int out_size, u32 timeout_sec)  {  	struct nfp_cpp *cpp = nsp->cpp;  	unsigned int max_size; @@ -429,7 +444,8 @@ static int nfp_nsp_command_buf(struct nfp_nsp *nsp, u16 code, u32 option,  			return err;  	} -	ret = nfp_nsp_command(nsp, code, option, cpp_id, cpp_buf); +	ret = __nfp_nsp_command(nsp, code, option, cpp_id, cpp_buf, +				timeout_sec);  	if (ret < 0)  		return ret; @@ -442,12 +458,23 @@ static int nfp_nsp_command_buf(struct nfp_nsp *nsp, u16 code, u32 option,  	return ret;  } +static int +nfp_nsp_command_buf(struct nfp_nsp *nsp, u16 code, u32 option, +		    const void *in_buf, unsigned int in_size, void *out_buf, +		    unsigned int out_size) +{ +	return __nfp_nsp_command_buf(nsp, code, option, in_buf, in_size, +				     out_buf, out_size, +				     NFP_NSP_TIMEOUT_DEFAULT); +} +  int nfp_nsp_wait(struct nfp_nsp *state)  { -	const unsigned long wait_until = jiffies + 30 * HZ; +	const unsigned long wait_until = jiffies + NFP_NSP_TIMEOUT_BOOT * HZ;  	int err; -	nfp_dbg(state->cpp, "Waiting for NSP to respond (30 sec max).\n"); +	nfp_dbg(state->cpp, "Waiting for NSP to respond (%u sec max).\n", +		NFP_NSP_TIMEOUT_BOOT);  	for (;;) {  		const unsigned long start_time = jiffies; @@ -488,6 +515,17 @@ int nfp_nsp_load_fw(struct nfp_nsp *state, const struct firmware *fw)  				   fw->size, NULL, 0);  } +int nfp_nsp_write_flash(struct nfp_nsp *state, const struct firmware *fw) +{ +	/* The flash time is specified to take a maximum of 70s so we add an +	 * additional factor to this spec time. +	 */ +	u32 timeout_sec = 2.5 * 70; + +	return __nfp_nsp_command_buf(state, SPCODE_NSP_WRITE_FLASH, fw->size, +				     fw->data, fw->size, NULL, 0, timeout_sec); +} +  int nfp_nsp_read_eth_table(struct nfp_nsp *state, void *buf, unsigned int size)  {  	return nfp_nsp_command_buf(state, SPCODE_ETH_RESCAN, size, NULL, 0, diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h index 650ca1a5bd21..f23d9e06f097 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h @@ -48,6 +48,7 @@ u16 nfp_nsp_get_abi_ver_minor(struct nfp_nsp *state);  int nfp_nsp_wait(struct nfp_nsp *state);  int nfp_nsp_device_soft_reset(struct nfp_nsp *state);  int nfp_nsp_load_fw(struct nfp_nsp *state, const struct firmware *fw); +int nfp_nsp_write_flash(struct nfp_nsp *state, const struct firmware *fw);  int nfp_nsp_mac_reinit(struct nfp_nsp *state);  static inline bool nfp_nsp_has_mac_reinit(struct nfp_nsp *state) @@ -97,28 +98,29 @@ enum nfp_eth_fec {   * @max_index:	max of @index fields of all @ports   * @ports:	table of ports   * - * @eth_index:	port index according to legacy ethX numbering - * @index:	chip-wide first channel index - * @nbi:	NBI index - * @base:	first channel index (within NBI) - * @lanes:	number of channels - * @speed:	interface speed (in Mbps) - * @interface:	interface (module) plugged in - * @media:	media type of the @interface - * @fec:	forward error correction mode - * @aneg:	auto negotiation mode - * @mac_addr:	interface MAC address - * @label_port:	port id - * @label_subport:  id of interface within port (for split ports) - * @enabled:	is enabled? - * @tx_enabled:	is TX enabled? - * @rx_enabled:	is RX enabled? - * @override_changed: is media reconfig pending? + * @ports.eth_index:	port index according to legacy ethX numbering + * @ports.index:	chip-wide first channel index + * @ports.nbi:		NBI index + * @ports.base:		first channel index (within NBI) + * @ports.lanes:	number of channels + * @ports.speed:	interface speed (in Mbps) + * @ports.interface:	interface (module) plugged in + * @ports.media:	media type of the @interface + * @ports.fec:		forward error correction mode + * @ports.aneg:		auto negotiation mode + * @ports.mac_addr:	interface MAC address + * @ports.label_port:	port id + * @ports.label_subport:  id of interface within port (for split ports) + * @ports.enabled:	is enabled? + * @ports.tx_enabled:	is TX enabled? + * @ports.rx_enabled:	is RX enabled? + * @ports.override_changed: is media reconfig pending?   * - * @port_type:	one of %PORT_* defines for ethtool - * @port_lanes:	total number of lanes on the port (sum of lanes of all subports) - * @is_split:	is interface part of a split port - * @fec_modes_supported:	bitmap of FEC modes supported + * @ports.port_type:	one of %PORT_* defines for ethtool + * @ports.port_lanes:	total number of lanes on the port (sum of lanes of all + *			subports) + * @ports.is_split:	is interface part of a split port + * @ports.fec_modes_supported:	bitmap of FEC modes supported   */  struct nfp_eth_table {  	unsigned int count; diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_resource.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_resource.c index b1dd13ff282b..7e14725055c7 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_resource.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_resource.c @@ -56,16 +56,17 @@  /**   * struct nfp_resource_entry - Resource table entry - * @owner:		NFP CPP Lock, interface owner - * @key:		NFP CPP Lock, posix_crc32(name, 8) - * @region:		Memory region descriptor - * @name:		ASCII, zero padded name - * @reserved - * @cpp_action:		CPP Action - * @cpp_token:		CPP Token - * @cpp_target:		CPP Target ID - * @page_offset:	256-byte page offset into target's CPP address - * @page_size:		size, in 256-byte pages + * @mutex:	NFP CPP Lock + * @mutex.owner:	NFP CPP Lock, interface owner + * @mutex.key:		NFP CPP Lock, posix_crc32(name, 8) + * @region:	Memory region descriptor + * @region.name:	ASCII, zero padded name + * @region.reserved:	padding + * @region.cpp_action:	CPP Action + * @region.cpp_token:	CPP Token + * @region.cpp_target:	CPP Target ID + * @region.page_offset:	256-byte page offset into target's CPP address + * @region.page_size:	size, in 256-byte pages   */  struct nfp_resource_entry {  	struct nfp_resource_entry_mutex { diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_rtsym.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_rtsym.c index ecda474ac7c3..46107aefad1c 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_rtsym.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_rtsym.c @@ -277,10 +277,6 @@ u64 nfp_rtsym_read_le(struct nfp_rtsym_table *rtbl, const char *name,  		break;  	} -	if (err == sym->size) -		err = 0; -	else if (err >= 0) -		err = -EIO;  exit:  	if (error)  		*error = err; |