From patchwork Fri Jun 8 17:58:11 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rahul Lakkireddy X-Patchwork-Id: 40926 X-Patchwork-Delegate: ferruh.yigit@amd.com Return-Path: X-Original-To: patchwork@dpdk.org Delivered-To: patchwork@dpdk.org Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 969471CFF8; Fri, 8 Jun 2018 19:59:12 +0200 (CEST) Received: from stargate.chelsio.com (stargate.chelsio.com [12.32.117.8]) by dpdk.org (Postfix) with ESMTP id CC7C11CFF7 for ; Fri, 8 Jun 2018 19:59:11 +0200 (CEST) Received: from localhost (scalar.blr.asicdesigners.com [10.193.185.94]) by stargate.chelsio.com (8.13.8/8.13.8) with ESMTP id w58Hx8FF017288; Fri, 8 Jun 2018 10:59:08 -0700 From: Rahul Lakkireddy To: dev@dpdk.org Cc: shaguna@chelsio.com, kumaras@chelsio.com, indranil@chelsio.com, nirranjan@chelsio.com Date: Fri, 8 Jun 2018 23:28:11 +0530 Message-Id: X-Mailer: git-send-email 2.5.3 In-Reply-To: References: In-Reply-To: References: Subject: [dpdk-dev] [PATCH 1/7] net/cxgbe: query firmware for filter resources X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" From: Shagun Agrawal Fetch available filter resources from firmware and allocate table for book-keeping and managing filters in hardware. Also define the hardware filter specification (ch_filter_specification) used to describe each filter rule. Signed-off-by: Shagun Agrawal Signed-off-by: Kumar Sanghvi Signed-off-by: Rahul Lakkireddy --- drivers/net/cxgbe/base/adapter.h | 4 ++ drivers/net/cxgbe/base/t4fw_interface.h | 6 ++ drivers/net/cxgbe/cxgbe_filter.h | 97 +++++++++++++++++++++++++++++ drivers/net/cxgbe/cxgbe_main.c | 106 ++++++++++++++++++++++++++++++++ drivers/net/cxgbe/cxgbe_ofld.h | 27 ++++++++ 5 files changed, 240 insertions(+) create mode 100644 drivers/net/cxgbe/cxgbe_filter.h create mode 100644 drivers/net/cxgbe/cxgbe_ofld.h diff --git a/drivers/net/cxgbe/base/adapter.h b/drivers/net/cxgbe/base/adapter.h index 55cb2e91c..1a0f96e40 100644 --- a/drivers/net/cxgbe/base/adapter.h +++ b/drivers/net/cxgbe/base/adapter.h @@ -11,9 +11,11 @@ #include #include #include +#include #include "cxgbe_compat.h" #include "t4_regs_values.h" +#include "cxgbe_ofld.h" enum { MAX_ETH_QSETS = 64, /* # of Ethernet Tx/Rx queue sets */ @@ -306,6 +308,8 @@ struct adapter { unsigned int vpd_flag; int use_unpacked_mode; /* unpacked rx mode state */ + + struct tid_info tids; /* Info used to access TID related tables */ }; /** diff --git a/drivers/net/cxgbe/base/t4fw_interface.h b/drivers/net/cxgbe/base/t4fw_interface.h index 852e8f3c7..95b2aec48 100644 --- a/drivers/net/cxgbe/base/t4fw_interface.h +++ b/drivers/net/cxgbe/base/t4fw_interface.h @@ -489,6 +489,10 @@ enum fw_params_mnem { enum fw_params_param_dev { FW_PARAMS_PARAM_DEV_CCLK = 0x00, /* chip core clock in khz */ FW_PARAMS_PARAM_DEV_PORTVEC = 0x01, /* the port vector */ + FW_PARAMS_PARAM_DEV_NTID = 0x02, /* reads the number of TIDs + * allocated by the device's + * Lookup Engine + */ FW_PARAMS_PARAM_DEV_FWREV = 0x0B, /* fw version */ FW_PARAMS_PARAM_DEV_TPREV = 0x0C, /* tp version */ FW_PARAMS_PARAM_DEV_ULPTX_MEMWRITE_DSGL = 0x17, @@ -498,6 +502,8 @@ enum fw_params_param_dev { * physical and virtual function parameters */ enum fw_params_param_pfvf { + FW_PARAMS_PARAM_PFVF_FILTER_START = 0x05, + FW_PARAMS_PARAM_PFVF_FILTER_END = 0x06, FW_PARAMS_PARAM_PFVF_CPLFW4MSG_ENCAP = 0x31, FW_PARAMS_PARAM_PFVF_PORT_CAPS32 = 0x3A }; diff --git a/drivers/net/cxgbe/cxgbe_filter.h b/drivers/net/cxgbe/cxgbe_filter.h new file mode 100644 index 000000000..d69c79e80 --- /dev/null +++ b/drivers/net/cxgbe/cxgbe_filter.h @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2014-2018 Chelsio Communications. + * All rights reserved. + */ + +#ifndef _CXGBE_FILTER_H_ +#define _CXGBE_FILTER_H_ + +#include "t4_msg.h" +/* + * Defined bit width of user definable filter tuples + */ +#define ETHTYPE_BITWIDTH 16 +#define FRAG_BITWIDTH 1 +#define MACIDX_BITWIDTH 9 +#define FCOE_BITWIDTH 1 +#define IPORT_BITWIDTH 3 +#define MATCHTYPE_BITWIDTH 3 +#define PROTO_BITWIDTH 8 +#define TOS_BITWIDTH 8 +#define PF_BITWIDTH 8 +#define VF_BITWIDTH 8 +#define IVLAN_BITWIDTH 16 +#define OVLAN_BITWIDTH 16 + +/* + * Filter matching rules. These consist of a set of ingress packet field + * (value, mask) tuples. The associated ingress packet field matches the + * tuple when ((field & mask) == value). (Thus a wildcard "don't care" field + * rule can be constructed by specifying a tuple of (0, 0).) A filter rule + * matches an ingress packet when all of the individual individual field + * matching rules are true. + * + * Partial field masks are always valid, however, while it may be easy to + * understand their meanings for some fields (e.g. IP address to match a + * subnet), for others making sensible partial masks is less intuitive (e.g. + * MPS match type) ... + */ +struct ch_filter_tuple { + /* + * Compressed header matching field rules. The TP_VLAN_PRI_MAP + * register selects which of these fields will participate in the + * filter match rules -- up to a maximum of 36 bits. Because + * TP_VLAN_PRI_MAP is a global register, all filters must use the same + * set of fields. + */ + uint32_t ethtype:ETHTYPE_BITWIDTH; /* Ethernet type */ + uint32_t frag:FRAG_BITWIDTH; /* IP fragmentation header */ + uint32_t ivlan_vld:1; /* inner VLAN valid */ + uint32_t ovlan_vld:1; /* outer VLAN valid */ + uint32_t pfvf_vld:1; /* PF/VF valid */ + uint32_t macidx:MACIDX_BITWIDTH; /* exact match MAC index */ + uint32_t fcoe:FCOE_BITWIDTH; /* FCoE packet */ + uint32_t iport:IPORT_BITWIDTH; /* ingress port */ + uint32_t matchtype:MATCHTYPE_BITWIDTH; /* MPS match type */ + uint32_t proto:PROTO_BITWIDTH; /* protocol type */ + uint32_t tos:TOS_BITWIDTH; /* TOS/Traffic Type */ + uint32_t pf:PF_BITWIDTH; /* PCI-E PF ID */ + uint32_t vf:VF_BITWIDTH; /* PCI-E VF ID */ + uint32_t ivlan:IVLAN_BITWIDTH; /* inner VLAN */ + uint32_t ovlan:OVLAN_BITWIDTH; /* outer VLAN */ + + /* + * Uncompressed header matching field rules. These are always + * available for field rules. + */ + uint8_t lip[16]; /* local IP address (IPv4 in [3:0]) */ + uint8_t fip[16]; /* foreign IP address (IPv4 in [3:0]) */ + uint16_t lport; /* local port */ + uint16_t fport; /* foreign port */ + + /* reservations for future additions */ + uint8_t rsvd[12]; +}; + +/* + * Filter specification + */ +struct ch_filter_specification { + /* Filter rule value/mask pairs. */ + struct ch_filter_tuple val; + struct ch_filter_tuple mask; +}; + +/* + * Host shadow copy of ingress filter entry. This is in host native format + * and doesn't match the ordering or bit order, etc. of the hardware or the + * firmware command. + */ +struct filter_entry { + /* + * The filter itself. + */ + struct ch_filter_specification fs; +}; + +#endif /* _CXGBE_FILTER_H_ */ diff --git a/drivers/net/cxgbe/cxgbe_main.c b/drivers/net/cxgbe/cxgbe_main.c index 54eb23dfb..9880257d2 100644 --- a/drivers/net/cxgbe/cxgbe_main.c +++ b/drivers/net/cxgbe/cxgbe_main.c @@ -38,6 +38,22 @@ #include "t4_msg.h" #include "cxgbe.h" +/** + * Allocate a chunk of memory. The allocated memory is cleared. + */ +void *t4_alloc_mem(size_t size) +{ + return rte_zmalloc(NULL, size, 0); +} + +/** + * Free memory allocated through t4_alloc_mem(). + */ +void t4_free_mem(void *addr) +{ + rte_free(addr); +} + /* * Response queue handler for the FW event queue. */ @@ -169,6 +185,59 @@ int cxgb4_set_rspq_intr_params(struct sge_rspq *q, unsigned int us, return 0; } +/** + * Free TID tables. + */ +static void tid_free(struct tid_info *t) +{ + if (t->tid_tab) { + if (t->ftid_bmap) + rte_bitmap_free(t->ftid_bmap); + + if (t->ftid_bmap_array) + t4_os_free(t->ftid_bmap_array); + + t4_os_free(t->tid_tab); + } + + memset(t, 0, sizeof(struct tid_info)); +} + +/** + * Allocate and initialize the TID tables. Returns 0 on success. + */ +static int tid_init(struct tid_info *t) +{ + size_t size; + unsigned int ftid_bmap_size; + unsigned int max_ftids = t->nftids; + + ftid_bmap_size = rte_bitmap_get_memory_footprint(t->nftids); + size = t->ntids * sizeof(*t->tid_tab) + + max_ftids * sizeof(*t->ftid_tab); + + t->tid_tab = t4_os_alloc(size); + if (!t->tid_tab) + return -ENOMEM; + + t->ftid_tab = (struct filter_entry *)&t->tid_tab[t->ntids]; + t->ftid_bmap_array = t4_os_alloc(ftid_bmap_size); + if (!t->ftid_bmap_array) { + tid_free(t); + return -ENOMEM; + } + + t4_os_lock_init(&t->ftid_lock); + t->ftid_bmap = rte_bitmap_init(t->nftids, t->ftid_bmap_array, + ftid_bmap_size); + if (!t->ftid_bmap) { + tid_free(t); + return -ENOMEM; + } + + return 0; +} + static inline bool is_x_1g_port(const struct link_config *lc) { return (lc->pcaps & FW_PORT_CAP32_SPEED_1G) != 0; @@ -706,6 +775,7 @@ static int adap_init0_config(struct adapter *adapter, int reset) static int adap_init0(struct adapter *adap) { + struct fw_caps_config_cmd caps_cmd; int ret = 0; u32 v, port_vec; enum dev_state state; @@ -822,6 +892,35 @@ static int adap_init0(struct adapter *adap) V_FW_PARAMS_PARAM_Y(0) | \ V_FW_PARAMS_PARAM_Z(0)) + params[0] = FW_PARAM_PFVF(FILTER_START); + params[1] = FW_PARAM_PFVF(FILTER_END); + ret = t4_query_params(adap, adap->mbox, adap->pf, 0, 2, params, val); + if (ret < 0) + goto bye; + adap->tids.ftid_base = val[0]; + adap->tids.nftids = val[1] - val[0] + 1; + + /* + * Get device capabilities so we can determine what resources we need + * to manage. + */ + memset(&caps_cmd, 0, sizeof(caps_cmd)); + caps_cmd.op_to_write = htonl(V_FW_CMD_OP(FW_CAPS_CONFIG_CMD) | + F_FW_CMD_REQUEST | F_FW_CMD_READ); + caps_cmd.cfvalid_to_len16 = htonl(FW_LEN16(caps_cmd)); + ret = t4_wr_mbox(adap, adap->mbox, &caps_cmd, sizeof(caps_cmd), + &caps_cmd); + if (ret < 0) + goto bye; + + /* query tid-related parameters */ + params[0] = FW_PARAM_DEV(NTID); + ret = t4_query_params(adap, adap->mbox, adap->pf, 0, 1, + params, val); + if (ret < 0) + goto bye; + adap->tids.ntids = val[0]; + /* If we're running on newer firmware, let it know that we're * prepared to deal with encapsulated CPL messages. Older * firmware won't understand this and we'll just get @@ -1307,6 +1406,7 @@ void cxgbe_close(struct adapter *adapter) if (adapter->flags & FULL_INIT_DONE) { if (is_pf4(adapter)) t4_intr_disable(adapter); + tid_free(&adapter->tids); t4_sge_tx_monitor_stop(adapter); t4_free_sge_resources(adapter); for_each_port(adapter, i) { @@ -1469,6 +1569,12 @@ int cxgbe_probe(struct adapter *adapter) print_adapter_info(adapter); print_port_info(adapter); + if (tid_init(&adapter->tids) < 0) { + /* Disable filtering support */ + dev_warn(adapter, "could not allocate TID table, " + "filter support disabled. Continuing\n"); + } + err = init_rss(adapter); if (err) goto out_free; diff --git a/drivers/net/cxgbe/cxgbe_ofld.h b/drivers/net/cxgbe/cxgbe_ofld.h new file mode 100644 index 000000000..57b4eb15b --- /dev/null +++ b/drivers/net/cxgbe/cxgbe_ofld.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2014-2018 Chelsio Communications. + * All rights reserved. + */ + +#ifndef _CXGBE_OFLD_H_ +#define _CXGBE_OFLD_H_ + +#include + +#include "cxgbe_filter.h" + +/* + * Holds the size, base address, free list start, etc of filter TID. + * The tables themselves are allocated dynamically. + */ +struct tid_info { + void **tid_tab; + unsigned int ntids; + struct filter_entry *ftid_tab; /* Normal filters */ + struct rte_bitmap *ftid_bmap; + uint8_t *ftid_bmap_array; + unsigned int nftids; + unsigned int ftid_base; + rte_spinlock_t ftid_lock; +}; +#endif /* _CXGBE_OFLD_H_ */ From patchwork Fri Jun 8 17:58:12 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rahul Lakkireddy X-Patchwork-Id: 40927 X-Patchwork-Delegate: ferruh.yigit@amd.com Return-Path: X-Original-To: patchwork@dpdk.org Delivered-To: patchwork@dpdk.org Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 85AA11CFFD; Fri, 8 Jun 2018 19:59:16 +0200 (CEST) Received: from stargate.chelsio.com (stargate.chelsio.com [12.32.117.8]) by dpdk.org (Postfix) with ESMTP id 19CF81CFF0 for ; Fri, 8 Jun 2018 19:59:14 +0200 (CEST) Received: from localhost (scalar.blr.asicdesigners.com [10.193.185.94]) by stargate.chelsio.com (8.13.8/8.13.8) with ESMTP id w58HxBkY017291; Fri, 8 Jun 2018 10:59:12 -0700 From: Rahul Lakkireddy To: dev@dpdk.org Cc: shaguna@chelsio.com, kumaras@chelsio.com, indranil@chelsio.com, nirranjan@chelsio.com Date: Fri, 8 Jun 2018 23:28:12 +0530 Message-Id: <6f9b34d75940a654ee79b96918e127212d9f5239.1528469677.git.rahul.lakkireddy@chelsio.com> X-Mailer: git-send-email 2.5.3 In-Reply-To: References: In-Reply-To: References: Subject: [dpdk-dev] [PATCH 2/7] net/cxgbe: parse and validate flows X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" From: Shagun Agrawal Introduce rte_flow skeleton and implement validate operation. Parse and convert , , into hardware specification. Perform validation, including basic sanity tests and underlying device's supported filter capability checks. Currently add support for: : IPv4, IPv6, TCP, and UDP. : Drop, Queue, and Count. Also add sanity checks to ensure filters are created at specified index in LE-TCAM region. The index in LE-TCAM region indicates the filter rule's priority with index 0 having the highest priority. If no index is specified, filters are created at closest available free index. Signed-off-by: Shagun Agrawal Signed-off-by: Kumar Sanghvi Signed-off-by: Rahul Lakkireddy --- doc/guides/nics/cxgbe.rst | 1 + doc/guides/nics/features/cxgbe.ini | 1 + doc/guides/rel_notes/release_18_08.rst | 5 + drivers/net/cxgbe/Makefile | 2 + drivers/net/cxgbe/base/adapter.h | 22 ++ drivers/net/cxgbe/cxgbe_ethdev.c | 2 + drivers/net/cxgbe/cxgbe_filter.c | 77 ++++++ drivers/net/cxgbe/cxgbe_filter.h | 91 +++++++ drivers/net/cxgbe/cxgbe_flow.c | 473 +++++++++++++++++++++++++++++++++ drivers/net/cxgbe/cxgbe_flow.h | 38 +++ 10 files changed, 712 insertions(+) create mode 100644 drivers/net/cxgbe/cxgbe_filter.c create mode 100644 drivers/net/cxgbe/cxgbe_flow.c create mode 100644 drivers/net/cxgbe/cxgbe_flow.h diff --git a/doc/guides/nics/cxgbe.rst b/doc/guides/nics/cxgbe.rst index 78e391473..124022cfc 100644 --- a/doc/guides/nics/cxgbe.rst +++ b/doc/guides/nics/cxgbe.rst @@ -30,6 +30,7 @@ CXGBE and CXGBEVF PMD has support for: - All multicast mode - Port hardware statistics - Jumbo frames +- Flow API Limitations ----------- diff --git a/doc/guides/nics/features/cxgbe.ini b/doc/guides/nics/features/cxgbe.ini index 6cf5c13f5..88f2f92b7 100644 --- a/doc/guides/nics/features/cxgbe.ini +++ b/doc/guides/nics/features/cxgbe.ini @@ -16,6 +16,7 @@ Allmulticast mode = Y RSS hash = Y RSS key update = Y Flow control = Y +Flow API = Y CRC offload = Y VLAN offload = Y L3 checksum offload = Y diff --git a/doc/guides/rel_notes/release_18_08.rst b/doc/guides/rel_notes/release_18_08.rst index 5bc23c537..bc0124295 100644 --- a/doc/guides/rel_notes/release_18_08.rst +++ b/doc/guides/rel_notes/release_18_08.rst @@ -41,6 +41,11 @@ New Features Also, make sure to start the actual text at the margin. ========================================================= +* **Added Flow API support for CXGBE PMD.** + + Flow API support has been added to CXGBE Poll Mode Driver to offload + flows to Chelsio T5/T6 NICs. + API Changes ----------- diff --git a/drivers/net/cxgbe/Makefile b/drivers/net/cxgbe/Makefile index 79fdb6f06..edc5d8188 100644 --- a/drivers/net/cxgbe/Makefile +++ b/drivers/net/cxgbe/Makefile @@ -49,6 +49,8 @@ SRCS-$(CONFIG_RTE_LIBRTE_CXGBE_PMD) += cxgbevf_ethdev.c SRCS-$(CONFIG_RTE_LIBRTE_CXGBE_PMD) += cxgbe_main.c SRCS-$(CONFIG_RTE_LIBRTE_CXGBE_PMD) += cxgbevf_main.c SRCS-$(CONFIG_RTE_LIBRTE_CXGBE_PMD) += sge.c +SRCS-$(CONFIG_RTE_LIBRTE_CXGBE_PMD) += cxgbe_filter.c +SRCS-$(CONFIG_RTE_LIBRTE_CXGBE_PMD) += cxgbe_flow.c SRCS-$(CONFIG_RTE_LIBRTE_CXGBE_PMD) += t4_hw.c SRCS-$(CONFIG_RTE_LIBRTE_CXGBE_PMD) += t4vf_hw.c diff --git a/drivers/net/cxgbe/base/adapter.h b/drivers/net/cxgbe/base/adapter.h index 1a0f96e40..f3434d28a 100644 --- a/drivers/net/cxgbe/base/adapter.h +++ b/drivers/net/cxgbe/base/adapter.h @@ -312,6 +312,17 @@ struct adapter { struct tid_info tids; /* Info used to access TID related tables */ }; +/** + * ethdev2pinfo - return the port_info structure associated with a rte_eth_dev + * @dev: the rte_eth_dev + * + * Return the struct port_info associated with a rte_eth_dev + */ +static inline struct port_info *ethdev2pinfo(const struct rte_eth_dev *dev) +{ + return (struct port_info *)dev->data->dev_private; +} + /** * adap2pinfo - return the port_info of a port * @adap: the adapter @@ -324,6 +335,17 @@ static inline struct port_info *adap2pinfo(const struct adapter *adap, int idx) return adap->port[idx]; } +/** + * ethdev2adap - return the adapter structure associated with a rte_eth_dev + * @dev: the rte_eth_dev + * + * Return the struct adapter associated with a rte_eth_dev + */ +static inline struct adapter *ethdev2adap(const struct rte_eth_dev *dev) +{ + return ethdev2pinfo(dev)->adapter; +} + #define CXGBE_PCI_REG(reg) rte_read32(reg) static inline uint64_t cxgbe_read_addr64(volatile void *addr) diff --git a/drivers/net/cxgbe/cxgbe_ethdev.c b/drivers/net/cxgbe/cxgbe_ethdev.c index 32450915c..1adb8e41f 100644 --- a/drivers/net/cxgbe/cxgbe_ethdev.c +++ b/drivers/net/cxgbe/cxgbe_ethdev.c @@ -36,6 +36,7 @@ #include "cxgbe.h" #include "cxgbe_pfvf.h" +#include "cxgbe_flow.h" /* * Macros needed to support the PCI Device ID Table ... @@ -1036,6 +1037,7 @@ static const struct eth_dev_ops cxgbe_eth_dev_ops = { .rx_queue_start = cxgbe_dev_rx_queue_start, .rx_queue_stop = cxgbe_dev_rx_queue_stop, .rx_queue_release = cxgbe_dev_rx_queue_release, + .filter_ctrl = cxgbe_dev_filter_ctrl, .stats_get = cxgbe_dev_stats_get, .stats_reset = cxgbe_dev_stats_reset, .flow_ctrl_get = cxgbe_flow_ctrl_get, diff --git a/drivers/net/cxgbe/cxgbe_filter.c b/drivers/net/cxgbe/cxgbe_filter.c new file mode 100644 index 000000000..6b10a8be1 --- /dev/null +++ b/drivers/net/cxgbe/cxgbe_filter.c @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2014-2018 Chelsio Communications. + * All rights reserved. + */ + +#include "common.h" +#include "t4_regs.h" +#include "cxgbe_filter.h" + +/** + * Validate if the requested filter specification can be set by checking + * if the requested features have been enabled + */ +int validate_filter(struct adapter *adapter, struct ch_filter_specification *fs) +{ + u32 fconf; + + /* + * Check for unconfigured fields being used. + */ + fconf = adapter->params.tp.vlan_pri_map; + +#define S(_field) \ + (fs->val._field || fs->mask._field) +#define U(_mask, _field) \ + (!(fconf & (_mask)) && S(_field)) + + if (U(F_ETHERTYPE, ethtype) || U(F_PROTOCOL, proto)) + return -EOPNOTSUPP; + +#undef S +#undef U + return 0; +} + +/** + * Check if entry already filled. + */ +bool is_filter_set(struct tid_info *t, int fidx, int family) +{ + bool result = FALSE; + int i, max; + + /* IPv6 requires four slots and IPv4 requires only 1 slot. + * Ensure, there's enough slots available. + */ + max = family == FILTER_TYPE_IPV6 ? fidx + 3 : fidx; + + t4_os_lock(&t->ftid_lock); + for (i = fidx; i <= max; i++) { + if (rte_bitmap_get(t->ftid_bmap, i)) { + result = TRUE; + break; + } + } + t4_os_unlock(&t->ftid_lock); + return result; +} + +/** + * Allocate a available free entry + */ +int cxgbe_alloc_ftid(struct adapter *adap, unsigned int family) +{ + struct tid_info *t = &adap->tids; + int pos; + int size = t->nftids; + + t4_os_lock(&t->ftid_lock); + if (family == FILTER_TYPE_IPV6) + pos = cxgbe_bitmap_find_free_region(t->ftid_bmap, size, 4); + else + pos = cxgbe_find_first_zero_bit(t->ftid_bmap, size); + t4_os_unlock(&t->ftid_lock); + + return pos < size ? pos : -1; +} diff --git a/drivers/net/cxgbe/cxgbe_filter.h b/drivers/net/cxgbe/cxgbe_filter.h index d69c79e80..a9d2d3d39 100644 --- a/drivers/net/cxgbe/cxgbe_filter.h +++ b/drivers/net/cxgbe/cxgbe_filter.h @@ -77,21 +77,112 @@ struct ch_filter_tuple { * Filter specification */ struct ch_filter_specification { + /* Administrative fields for filter. */ + uint32_t hitcnts:1; /* count filter hits in TCB */ + uint32_t prio:1; /* filter has priority over active/server */ + + /* + * Fundamental filter typing. This is the one element of filter + * matching that doesn't exist as a (value, mask) tuple. + */ + uint32_t type:1; /* 0 => IPv4, 1 => IPv6 */ + + /* + * Packet dispatch information. Ingress packets which match the + * filter rules will be dropped, passed to the host or switched back + * out as egress packets. + */ + uint32_t action:2; /* drop, pass, switch */ + + uint32_t dirsteer:1; /* 0 => RSS, 1 => steer to iq */ + uint32_t iq:10; /* ingress queue */ + /* Filter rule value/mask pairs. */ struct ch_filter_tuple val; struct ch_filter_tuple mask; }; +enum { + FILTER_PASS = 0, /* default */ + FILTER_DROP +}; + +enum filter_type { + FILTER_TYPE_IPV4 = 0, + FILTER_TYPE_IPV6, +}; + /* * Host shadow copy of ingress filter entry. This is in host native format * and doesn't match the ordering or bit order, etc. of the hardware or the * firmware command. */ struct filter_entry { + struct rte_eth_dev *dev; /* Port's rte eth device */ + /* * The filter itself. */ struct ch_filter_specification fs; }; +#define FILTER_ID_MAX (~0U) + +struct tid_info; +struct adapter; + +/** + * Find first clear bit in the bitmap. + */ +static inline unsigned int cxgbe_find_first_zero_bit(struct rte_bitmap *bmap, + unsigned int size) +{ + unsigned int idx; + + for (idx = 0; idx < size; idx++) + if (!rte_bitmap_get(bmap, idx)) + break; + + return idx; +} + +/** + * Find a free region of 'num' consecutive entries. + */ +static inline unsigned int +cxgbe_bitmap_find_free_region(struct rte_bitmap *bmap, unsigned int size, + unsigned int num) +{ + unsigned int idx, j, free = 0; + + if (num > size) + return size; + + for (idx = 0; idx < size; idx += num) { + for (j = 0; j < num; j++) { + if (!rte_bitmap_get(bmap, idx + j)) { + free++; + } else { + free = 0; + break; + } + } + + /* Found the Region */ + if (free == num) + break; + + /* Reached the end and still no region found */ + if ((idx + num) > size) { + idx = size; + break; + } + } + + return idx; +} + +bool is_filter_set(struct tid_info *, int fidx, int family); +int cxgbe_alloc_ftid(struct adapter *adap, unsigned int family); +int validate_filter(struct adapter *adap, struct ch_filter_specification *fs); #endif /* _CXGBE_FILTER_H_ */ diff --git a/drivers/net/cxgbe/cxgbe_flow.c b/drivers/net/cxgbe/cxgbe_flow.c new file mode 100644 index 000000000..a01708e70 --- /dev/null +++ b/drivers/net/cxgbe/cxgbe_flow.c @@ -0,0 +1,473 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2014-2018 Chelsio Communications. + * All rights reserved. + */ +#include "common.h" +#include "cxgbe_flow.h" + +#define __CXGBE_FILL_FS(__v, __m, fs, elem, e) \ +do { \ + if (!((fs)->val.elem || (fs)->mask.elem)) { \ + (fs)->val.elem = (__v); \ + (fs)->mask.elem = (__m); \ + } else { \ + return rte_flow_error_set(e, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM, \ + NULL, "a filter can be specified" \ + " only once"); \ + } \ +} while (0) + +#define __CXGBE_FILL_FS_MEMCPY(__v, __m, fs, elem) \ +do { \ + memcpy(&(fs)->val.elem, &(__v), sizeof(__v)); \ + memcpy(&(fs)->mask.elem, &(__m), sizeof(__m)); \ +} while (0) + +#define CXGBE_FILL_FS(v, m, elem) \ + __CXGBE_FILL_FS(v, m, fs, elem, e) + +#define CXGBE_FILL_FS_MEMCPY(v, m, elem) \ + __CXGBE_FILL_FS_MEMCPY(v, m, fs, elem) + +static int +cxgbe_validate_item(const struct rte_flow_item *i, struct rte_flow_error *e) +{ + /* rte_flow specification does not allow it. */ + if (!i->spec && (i->mask || i->last)) + return rte_flow_error_set(e, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM, + i, "last or mask given without spec"); + /* + * We don't support it. + * Although, we can support values in last as 0's or last == spec. + * But this will not provide user with any additional functionality + * and will only increase the complexity for us. + */ + if (i->last) + return rte_flow_error_set(e, ENOTSUP, RTE_FLOW_ERROR_TYPE_ITEM, + i, "last is not supported by chelsio pmd"); + return 0; +} + +static int +ch_rte_parsetype_udp(const void *dmask, const struct rte_flow_item *item, + struct ch_filter_specification *fs, + struct rte_flow_error *e) +{ + const struct rte_flow_item_udp *val = item->spec; + const struct rte_flow_item_udp *umask = item->mask; + const struct rte_flow_item_udp *mask; + + mask = umask ? umask : (const struct rte_flow_item_udp *)dmask; + + if (mask->hdr.dgram_len || mask->hdr.dgram_cksum) + return rte_flow_error_set(e, ENOTSUP, RTE_FLOW_ERROR_TYPE_ITEM, + item, + "udp: only src/dst port supported"); + + CXGBE_FILL_FS(IPPROTO_UDP, 0xff, proto); + if (!val) + return 0; + CXGBE_FILL_FS(be16_to_cpu(val->hdr.src_port), + be16_to_cpu(mask->hdr.src_port), fport); + CXGBE_FILL_FS(be16_to_cpu(val->hdr.dst_port), + be16_to_cpu(mask->hdr.dst_port), lport); + return 0; +} + +static int +ch_rte_parsetype_tcp(const void *dmask, const struct rte_flow_item *item, + struct ch_filter_specification *fs, + struct rte_flow_error *e) +{ + const struct rte_flow_item_tcp *val = item->spec; + const struct rte_flow_item_tcp *umask = item->mask; + const struct rte_flow_item_tcp *mask; + + mask = umask ? umask : (const struct rte_flow_item_tcp *)dmask; + + if (mask->hdr.sent_seq || mask->hdr.recv_ack || mask->hdr.data_off || + mask->hdr.tcp_flags || mask->hdr.rx_win || mask->hdr.cksum || + mask->hdr.tcp_urp) + return rte_flow_error_set(e, ENOTSUP, RTE_FLOW_ERROR_TYPE_ITEM, + item, + "tcp: only src/dst port supported"); + + CXGBE_FILL_FS(IPPROTO_TCP, 0xff, proto); + if (!val) + return 0; + CXGBE_FILL_FS(be16_to_cpu(val->hdr.src_port), + be16_to_cpu(mask->hdr.src_port), fport); + CXGBE_FILL_FS(be16_to_cpu(val->hdr.dst_port), + be16_to_cpu(mask->hdr.dst_port), lport); + return 0; +} + +static int +ch_rte_parsetype_ipv4(const void *dmask, const struct rte_flow_item *item, + struct ch_filter_specification *fs, + struct rte_flow_error *e) +{ + const struct rte_flow_item_ipv4 *val = item->spec; + const struct rte_flow_item_ipv4 *umask = item->mask; + const struct rte_flow_item_ipv4 *mask; + + mask = umask ? umask : (const struct rte_flow_item_ipv4 *)dmask; + + if (mask->hdr.time_to_live || mask->hdr.type_of_service) + return rte_flow_error_set(e, ENOTSUP, RTE_FLOW_ERROR_TYPE_ITEM, + item, "ttl/tos are not supported"); + + fs->type = FILTER_TYPE_IPV4; + CXGBE_FILL_FS(ETHER_TYPE_IPv4, 0xffff, ethtype); + if (!val) + return 0; /* ipv4 wild card */ + + CXGBE_FILL_FS(val->hdr.next_proto_id, mask->hdr.next_proto_id, proto); + CXGBE_FILL_FS_MEMCPY(val->hdr.dst_addr, mask->hdr.dst_addr, lip); + CXGBE_FILL_FS_MEMCPY(val->hdr.src_addr, mask->hdr.src_addr, fip); + + return 0; +} + +static int +ch_rte_parsetype_ipv6(const void *dmask, const struct rte_flow_item *item, + struct ch_filter_specification *fs, + struct rte_flow_error *e) +{ + const struct rte_flow_item_ipv6 *val = item->spec; + const struct rte_flow_item_ipv6 *umask = item->mask; + const struct rte_flow_item_ipv6 *mask; + + mask = umask ? umask : (const struct rte_flow_item_ipv6 *)dmask; + + if (mask->hdr.vtc_flow || + mask->hdr.payload_len || mask->hdr.hop_limits) + return rte_flow_error_set(e, ENOTSUP, RTE_FLOW_ERROR_TYPE_ITEM, + item, + "tc/flow/hop are not supported"); + + fs->type = FILTER_TYPE_IPV6; + CXGBE_FILL_FS(ETHER_TYPE_IPv6, 0xffff, ethtype); + if (!val) + return 0; /* ipv6 wild card */ + + CXGBE_FILL_FS(val->hdr.proto, mask->hdr.proto, proto); + CXGBE_FILL_FS_MEMCPY(val->hdr.dst_addr, mask->hdr.dst_addr, lip); + CXGBE_FILL_FS_MEMCPY(val->hdr.src_addr, mask->hdr.src_addr, fip); + + return 0; +} + +static int +cxgbe_rtef_parse_attr(struct rte_flow *flow, const struct rte_flow_attr *attr, + struct rte_flow_error *e) +{ + if (attr->egress) + return rte_flow_error_set(e, ENOTSUP, RTE_FLOW_ERROR_TYPE_ATTR, + attr, "attribute: is" + " not supported !"); + if (attr->group > 0) + return rte_flow_error_set(e, ENOTSUP, RTE_FLOW_ERROR_TYPE_ATTR, + attr, "group parameter is" + " not supported."); + + flow->fidx = attr->priority ? attr->priority - 1 : FILTER_ID_MAX; + + return 0; +} + +static inline int check_rxq(struct rte_eth_dev *dev, uint16_t rxq) +{ + struct port_info *pi = ethdev2pinfo(dev); + + if (rxq > pi->n_rx_qsets) + return -EINVAL; + return 0; +} + +static int cxgbe_validate_fidxondel(struct filter_entry *f, unsigned int fidx) +{ + struct adapter *adap = ethdev2adap(f->dev); + struct ch_filter_specification fs = f->fs; + + if (fidx >= adap->tids.nftids) { + dev_err(adap, "invalid flow index %d.\n", fidx); + return -EINVAL; + } + if (!is_filter_set(&adap->tids, fidx, fs.type)) { + dev_err(adap, "Already free fidx:%d f:%p\n", fidx, f); + return -EINVAL; + } + + return 0; +} + +static int +cxgbe_validate_fidxonadd(struct ch_filter_specification *fs, + struct adapter *adap, unsigned int fidx) +{ + if (is_filter_set(&adap->tids, fidx, fs->type)) { + dev_err(adap, "filter index: %d is busy.\n", fidx); + return -EBUSY; + } + if (fidx >= adap->tids.nftids) { + dev_err(adap, "filter index (%u) >= max(%u)\n", + fidx, adap->tids.nftids); + return -ERANGE; + } + + return 0; +} + +static int +cxgbe_verify_fidx(struct rte_flow *flow, unsigned int fidx, uint8_t del) +{ + return del ? cxgbe_validate_fidxondel(flow->f, fidx) : + cxgbe_validate_fidxonadd(&flow->fs, + ethdev2adap(flow->dev), fidx); +} + +static int cxgbe_get_fidx(struct rte_flow *flow, unsigned int *fidx) +{ + struct ch_filter_specification *fs = &flow->fs; + struct adapter *adap = ethdev2adap(flow->dev); + + /* For tcam get the next available slot, if default value specified */ + if (flow->fidx == FILTER_ID_MAX) { + int idx; + + idx = cxgbe_alloc_ftid(adap, fs->type); + if (idx < 0) { + dev_err(adap, "unable to get a filter index in tcam\n"); + return -ENOMEM; + } + *fidx = (unsigned int)idx; + } else { + *fidx = flow->fidx; + } + + return 0; +} + +static int +cxgbe_rtef_parse_actions(struct rte_flow *flow, + const struct rte_flow_action action[], + struct rte_flow_error *e) +{ + struct ch_filter_specification *fs = &flow->fs; + const struct rte_flow_action_queue *q; + const struct rte_flow_action *a; + char abit = 0; + + for (a = action; a->type != RTE_FLOW_ACTION_TYPE_END; a++) { + switch (a->type) { + case RTE_FLOW_ACTION_TYPE_VOID: + continue; + case RTE_FLOW_ACTION_TYPE_DROP: + if (abit++) + return rte_flow_error_set(e, EINVAL, + RTE_FLOW_ERROR_TYPE_ACTION, a, + "specify only 1 pass/drop"); + fs->action = FILTER_DROP; + break; + case RTE_FLOW_ACTION_TYPE_QUEUE: + q = (const struct rte_flow_action_queue *)a->conf; + if (!q) + return rte_flow_error_set(e, EINVAL, + RTE_FLOW_ERROR_TYPE_ACTION, q, + "specify rx queue index"); + if (check_rxq(flow->dev, q->index)) + return rte_flow_error_set(e, EINVAL, + RTE_FLOW_ERROR_TYPE_ACTION, q, + "Invalid rx queue"); + if (abit++) + return rte_flow_error_set(e, EINVAL, + RTE_FLOW_ERROR_TYPE_ACTION, a, + "specify only 1 pass/drop"); + fs->action = FILTER_PASS; + fs->dirsteer = 1; + fs->iq = q->index; + break; + case RTE_FLOW_ACTION_TYPE_COUNT: + fs->hitcnts = 1; + break; + default: + /* Not supported action : return error */ + return rte_flow_error_set(e, ENOTSUP, + RTE_FLOW_ERROR_TYPE_ACTION, + a, "Action not supported"); + } + } + + return 0; +} + +struct chrte_fparse parseitem[] = { + [RTE_FLOW_ITEM_TYPE_IPV4] = { + .fptr = ch_rte_parsetype_ipv4, + .dmask = &rte_flow_item_ipv4_mask, + }, + + [RTE_FLOW_ITEM_TYPE_IPV6] = { + .fptr = ch_rte_parsetype_ipv6, + .dmask = &rte_flow_item_ipv6_mask, + }, + + [RTE_FLOW_ITEM_TYPE_UDP] = { + .fptr = ch_rte_parsetype_udp, + .dmask = &rte_flow_item_udp_mask, + }, + + [RTE_FLOW_ITEM_TYPE_TCP] = { + .fptr = ch_rte_parsetype_tcp, + .dmask = &rte_flow_item_tcp_mask, + }, +}; + +static int +cxgbe_rtef_parse_items(struct rte_flow *flow, + const struct rte_flow_item items[], + struct rte_flow_error *e) +{ + const struct rte_flow_item *i; + char repeat[ARRAY_SIZE(parseitem)] = {0}; + + for (i = items; i->type != RTE_FLOW_ITEM_TYPE_END; i++) { + struct chrte_fparse *idx = &flow->item_parser[i->type]; + int ret; + + if (i->type > ARRAY_SIZE(parseitem)) + return rte_flow_error_set(e, ENOTSUP, + RTE_FLOW_ERROR_TYPE_ITEM, + i, "Item not supported"); + + switch (i->type) { + case RTE_FLOW_ITEM_TYPE_VOID: + continue; + default: + /* check if item is repeated */ + if (repeat[i->type]) + return rte_flow_error_set(e, ENOTSUP, + RTE_FLOW_ERROR_TYPE_ITEM, i, + "parse items cannot be repeated (except void)"); + repeat[i->type] = 1; + + /* validate the item */ + ret = cxgbe_validate_item(i, e); + if (ret) + return ret; + + if (!idx || !idx->fptr) { + return rte_flow_error_set(e, ENOTSUP, + RTE_FLOW_ERROR_TYPE_ITEM, i, + "Item not supported"); + } else { + ret = idx->fptr(idx->dmask, i, &flow->fs, e); + if (ret) + return ret; + } + } + } + + return 0; +} + +static int +cxgbe_flow_parse(struct rte_flow *flow, + const struct rte_flow_attr *attr, + const struct rte_flow_item item[], + const struct rte_flow_action action[], + struct rte_flow_error *e) +{ + int ret; + + /* parse user request into ch_filter_specification */ + ret = cxgbe_rtef_parse_attr(flow, attr, e); + if (ret) + return ret; + ret = cxgbe_rtef_parse_items(flow, item, e); + if (ret) + return ret; + return cxgbe_rtef_parse_actions(flow, action, e); +} + +static int +cxgbe_flow_validate(struct rte_eth_dev *dev, + const struct rte_flow_attr *attr, + const struct rte_flow_item item[], + const struct rte_flow_action action[], + struct rte_flow_error *e) +{ + struct adapter *adap = ethdev2adap(dev); + struct rte_flow *flow; + unsigned int fidx; + int ret; + + flow = t4_os_alloc(sizeof(struct rte_flow)); + if (!flow) + return rte_flow_error_set(e, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE, + NULL, + "Unable to allocate memory for filter_entry"); + + flow->item_parser = parseitem; + flow->dev = dev; + + ret = cxgbe_flow_parse(flow, attr, item, action, e); + if (ret) { + t4_os_free(flow); + return ret; + } + + if (validate_filter(adap, &flow->fs)) { + t4_os_free(flow); + return rte_flow_error_set(e, EINVAL, RTE_FLOW_ERROR_TYPE_HANDLE, + NULL, + "validation failed. Check f/w config file."); + } + + if (cxgbe_get_fidx(flow, &fidx)) { + t4_os_free(flow); + return rte_flow_error_set(e, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE, + NULL, "no memory in tcam."); + } + + if (cxgbe_verify_fidx(flow, fidx, 0)) { + t4_os_free(flow); + return rte_flow_error_set(e, EINVAL, RTE_FLOW_ERROR_TYPE_HANDLE, + NULL, "validation failed"); + } + + t4_os_free(flow); + return 0; +} + +static const struct rte_flow_ops cxgbe_flow_ops = { + .validate = cxgbe_flow_validate, + .create = NULL, + .destroy = NULL, + .flush = NULL, + .query = NULL, + .isolate = NULL, +}; + +int +cxgbe_dev_filter_ctrl(struct rte_eth_dev *dev, + enum rte_filter_type filter_type, + enum rte_filter_op filter_op, + void *arg) +{ + int ret = 0; + + RTE_SET_USED(dev); + switch (filter_type) { + case RTE_ETH_FILTER_GENERIC: + if (filter_op != RTE_ETH_FILTER_GET) + return -EINVAL; + *(const void **)arg = &cxgbe_flow_ops; + break; + default: + ret = -ENOTSUP; + break; + } + return ret; +} diff --git a/drivers/net/cxgbe/cxgbe_flow.h b/drivers/net/cxgbe/cxgbe_flow.h new file mode 100644 index 000000000..45bc37082 --- /dev/null +++ b/drivers/net/cxgbe/cxgbe_flow.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2014-2018 Chelsio Communications. + * All rights reserved. + */ +#ifndef _CXGBE_FLOW_H_ +#define _CXGBE_FLOW_H_ + +#include +#include "cxgbe_filter.h" + +struct chrte_fparse { + int (*fptr)(const void *mask, /* currently supported mask */ + const struct rte_flow_item *item, /* user input */ + struct ch_filter_specification *fs, /* where to parse */ + struct rte_flow_error *e); + const void *dmask; /* Specify what is supported by chelsio by default*/ +}; + +struct rte_flow { + struct filter_entry *f; + struct ch_filter_specification fs; /* temp, to create filter */ + struct chrte_fparse *item_parser; + /* + * filter_entry doesn't store user priority. + * Post creation of filter this will indicate the + * flow index (fidx) for both hash and tcam filters + */ + unsigned int fidx; + struct rte_eth_dev *dev; +}; + +int +cxgbe_dev_filter_ctrl(struct rte_eth_dev *dev, + enum rte_filter_type filter_type, + enum rte_filter_op filter_op, + void *arg); + +#endif /* _CXGBE_FLOW_H_ */ From patchwork Fri Jun 8 17:58:13 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rahul Lakkireddy X-Patchwork-Id: 40928 X-Patchwork-Delegate: ferruh.yigit@amd.com Return-Path: X-Original-To: patchwork@dpdk.org Delivered-To: patchwork@dpdk.org Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id F0C781D00A; Fri, 8 Jun 2018 19:59:19 +0200 (CEST) Received: from stargate.chelsio.com (stargate.chelsio.com [12.32.117.8]) by dpdk.org (Postfix) with ESMTP id 646AC1D003 for ; Fri, 8 Jun 2018 19:59:18 +0200 (CEST) Received: from localhost (scalar.blr.asicdesigners.com [10.193.185.94]) by stargate.chelsio.com (8.13.8/8.13.8) with ESMTP id w58HxF8Z017294; Fri, 8 Jun 2018 10:59:16 -0700 From: Rahul Lakkireddy To: dev@dpdk.org Cc: shaguna@chelsio.com, kumaras@chelsio.com, indranil@chelsio.com, nirranjan@chelsio.com Date: Fri, 8 Jun 2018 23:28:13 +0530 Message-Id: <1be701875ffb3a27247a076b771993545d0513b6.1528469677.git.rahul.lakkireddy@chelsio.com> X-Mailer: git-send-email 2.5.3 In-Reply-To: References: In-Reply-To: References: Subject: [dpdk-dev] [PATCH 3/7] net/cxgbe: add control queue to communicate filter requests X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" From: Shagun Agrawal Add control queue to communicate filter creation/deletion requests with firmware. This API will be used by subsequent patches. Signed-off-by: Shagun Agrawal Signed-off-by: Kumar Sanghvi Signed-off-by: Rahul Lakkireddy --- drivers/net/cxgbe/base/adapter.h | 15 +++ drivers/net/cxgbe/base/common.h | 2 + drivers/net/cxgbe/base/t4_hw.c | 25 ++++ drivers/net/cxgbe/base/t4fw_interface.h | 70 ++++++++++++ drivers/net/cxgbe/cxgbe.h | 1 + drivers/net/cxgbe/cxgbe_ethdev.c | 3 + drivers/net/cxgbe/cxgbe_main.c | 41 +++++++ drivers/net/cxgbe/sge.c | 197 +++++++++++++++++++++++++++++++- 8 files changed, 353 insertions(+), 1 deletion(-) diff --git a/drivers/net/cxgbe/base/adapter.h b/drivers/net/cxgbe/base/adapter.h index f3434d28a..9a66a4a99 100644 --- a/drivers/net/cxgbe/base/adapter.h +++ b/drivers/net/cxgbe/base/adapter.h @@ -19,6 +19,7 @@ enum { MAX_ETH_QSETS = 64, /* # of Ethernet Tx/Rx queue sets */ + MAX_CTRL_QUEUES = NCHAN, /* # of control Tx queues */ }; struct adapter; @@ -256,10 +257,20 @@ struct sge_eth_txq { /* state for an SGE Ethernet Tx queue */ unsigned int flags; /* flags for state of the queue */ } __rte_cache_aligned; +struct sge_ctrl_txq { /* State for an SGE control Tx queue */ + struct sge_txq q; /* txq */ + struct adapter *adapter; /* adapter associated with this queue */ + rte_spinlock_t ctrlq_lock; /* control queue lock */ + u8 full; /* the Tx ring is full */ + u64 txp; /* number of transmits */ + struct rte_mempool *mb_pool; /* mempool to generate ctrl pkts */ +} __rte_cache_aligned; + struct sge { struct sge_eth_txq ethtxq[MAX_ETH_QSETS]; struct sge_eth_rxq ethrxq[MAX_ETH_QSETS]; struct sge_rspq fw_evtq __rte_cache_aligned; + struct sge_ctrl_txq ctrlq[MAX_CTRL_QUEUES]; u16 max_ethqsets; /* # of available Ethernet queue sets */ u32 stat_len; /* length of status page at ring end */ @@ -720,6 +731,7 @@ void t4_sge_tx_monitor_start(struct adapter *adap); void t4_sge_tx_monitor_stop(struct adapter *adap); int t4_eth_xmit(struct sge_eth_txq *txq, struct rte_mbuf *mbuf, uint16_t nb_pkts); +int t4_mgmt_tx(struct sge_ctrl_txq *txq, struct rte_mbuf *mbuf); int t4_ethrx_handler(struct sge_rspq *q, const __be64 *rsp, const struct pkt_gl *gl); int t4_sge_init(struct adapter *adap); @@ -727,6 +739,9 @@ int t4vf_sge_init(struct adapter *adap); int t4_sge_alloc_eth_txq(struct adapter *adap, struct sge_eth_txq *txq, struct rte_eth_dev *eth_dev, uint16_t queue_id, unsigned int iqid, int socket_id); +int t4_sge_alloc_ctrl_txq(struct adapter *adap, struct sge_ctrl_txq *txq, + struct rte_eth_dev *eth_dev, uint16_t queue_id, + unsigned int iqid, int socket_id); int t4_sge_alloc_rxq(struct adapter *adap, struct sge_rspq *rspq, bool fwevtq, struct rte_eth_dev *eth_dev, int intr_idx, struct sge_fl *fl, rspq_handler_t handler, diff --git a/drivers/net/cxgbe/base/common.h b/drivers/net/cxgbe/base/common.h index 155a30288..c80304b24 100644 --- a/drivers/net/cxgbe/base/common.h +++ b/drivers/net/cxgbe/base/common.h @@ -378,6 +378,8 @@ int t4_iq_free(struct adapter *adap, unsigned int mbox, unsigned int pf, unsigned int fl0id, unsigned int fl1id); int t4_eth_eq_free(struct adapter *adap, unsigned int mbox, unsigned int pf, unsigned int vf, unsigned int eqid); +int t4_ctrl_eq_free(struct adapter *adap, unsigned int mbox, unsigned int pf, + unsigned int vf, unsigned int eqid); static inline unsigned int core_ticks_per_usec(const struct adapter *adap) { diff --git a/drivers/net/cxgbe/base/t4_hw.c b/drivers/net/cxgbe/base/t4_hw.c index e5ef73b67..c146c911e 100644 --- a/drivers/net/cxgbe/base/t4_hw.c +++ b/drivers/net/cxgbe/base/t4_hw.c @@ -4490,6 +4490,31 @@ static void t4_handle_get_port_info(struct port_info *pi, const __be64 *rpl) } } +/** + * t4_ctrl_eq_free - free a control egress queue + * @adap: the adapter + * @mbox: mailbox to use for the FW command + * @pf: the PF owning the queue + * @vf: the VF owning the queue + * @eqid: egress queue id + * + * Frees a control egress queue. + */ +int t4_ctrl_eq_free(struct adapter *adap, unsigned int mbox, unsigned int pf, + unsigned int vf, unsigned int eqid) +{ + struct fw_eq_ctrl_cmd c; + + memset(&c, 0, sizeof(c)); + c.op_to_vfn = cpu_to_be32(V_FW_CMD_OP(FW_EQ_CTRL_CMD) | + F_FW_CMD_REQUEST | F_FW_CMD_EXEC | + V_FW_EQ_CTRL_CMD_PFN(pf) | + V_FW_EQ_CTRL_CMD_VFN(vf)); + c.alloc_to_len16 = cpu_to_be32(F_FW_EQ_CTRL_CMD_FREE | FW_LEN16(c)); + c.cmpliqid_eqid = cpu_to_be32(V_FW_EQ_CTRL_CMD_EQID(eqid)); + return t4_wr_mbox(adap, mbox, &c, sizeof(c), NULL); +} + /** * t4_handle_fw_rpl - process a FW reply message * @adap: the adapter diff --git a/drivers/net/cxgbe/base/t4fw_interface.h b/drivers/net/cxgbe/base/t4fw_interface.h index 95b2aec48..44b6f6dac 100644 --- a/drivers/net/cxgbe/base/t4fw_interface.h +++ b/drivers/net/cxgbe/base/t4fw_interface.h @@ -178,6 +178,7 @@ enum fw_cmd_opcodes { FW_PFVF_CMD = 0x09, FW_IQ_CMD = 0x10, FW_EQ_ETH_CMD = 0x12, + FW_EQ_CTRL_CMD = 0x13, FW_VI_CMD = 0x14, FW_VI_MAC_CMD = 0x15, FW_VI_RXMODE_CMD = 0x16, @@ -960,6 +961,75 @@ struct fw_eq_eth_cmd { #define G_FW_EQ_ETH_CMD_VIID(x) \ (((x) >> S_FW_EQ_ETH_CMD_VIID) & M_FW_EQ_ETH_CMD_VIID) +struct fw_eq_ctrl_cmd { + __be32 op_to_vfn; + __be32 alloc_to_len16; + __be32 cmpliqid_eqid; + __be32 physeqid_pkd; + __be32 fetchszm_to_iqid; + __be32 dcaen_to_eqsize; + __be64 eqaddr; +}; + +#define S_FW_EQ_CTRL_CMD_PFN 8 +#define V_FW_EQ_CTRL_CMD_PFN(x) ((x) << S_FW_EQ_CTRL_CMD_PFN) + +#define S_FW_EQ_CTRL_CMD_VFN 0 +#define V_FW_EQ_CTRL_CMD_VFN(x) ((x) << S_FW_EQ_CTRL_CMD_VFN) + +#define S_FW_EQ_CTRL_CMD_ALLOC 31 +#define V_FW_EQ_CTRL_CMD_ALLOC(x) ((x) << S_FW_EQ_CTRL_CMD_ALLOC) +#define F_FW_EQ_CTRL_CMD_ALLOC V_FW_EQ_CTRL_CMD_ALLOC(1U) + +#define S_FW_EQ_CTRL_CMD_FREE 30 +#define V_FW_EQ_CTRL_CMD_FREE(x) ((x) << S_FW_EQ_CTRL_CMD_FREE) +#define F_FW_EQ_CTRL_CMD_FREE V_FW_EQ_CTRL_CMD_FREE(1U) + +#define S_FW_EQ_CTRL_CMD_EQSTART 28 +#define V_FW_EQ_CTRL_CMD_EQSTART(x) ((x) << S_FW_EQ_CTRL_CMD_EQSTART) +#define F_FW_EQ_CTRL_CMD_EQSTART V_FW_EQ_CTRL_CMD_EQSTART(1U) + +#define S_FW_EQ_CTRL_CMD_CMPLIQID 20 +#define V_FW_EQ_CTRL_CMD_CMPLIQID(x) ((x) << S_FW_EQ_CTRL_CMD_CMPLIQID) + +#define S_FW_EQ_CTRL_CMD_EQID 0 +#define M_FW_EQ_CTRL_CMD_EQID 0xfffff +#define V_FW_EQ_CTRL_CMD_EQID(x) ((x) << S_FW_EQ_CTRL_CMD_EQID) +#define G_FW_EQ_CTRL_CMD_EQID(x) \ + (((x) >> S_FW_EQ_CTRL_CMD_EQID) & M_FW_EQ_CTRL_CMD_EQID) + +#define S_FW_EQ_CTRL_CMD_PHYSEQID 0 +#define M_FW_EQ_CTRL_CMD_PHYSEQID 0xfffff +#define V_FW_EQ_CTRL_CMD_PHYSEQID(x) ((x) << S_FW_EQ_CTRL_CMD_PHYSEQID) +#define G_FW_EQ_CTRL_CMD_PHYSEQID(x) \ + (((x) >> S_FW_EQ_CTRL_CMD_PHYSEQID) & M_FW_EQ_CTRL_CMD_PHYSEQID) + +#define S_FW_EQ_CTRL_CMD_FETCHRO 22 +#define V_FW_EQ_CTRL_CMD_FETCHRO(x) ((x) << S_FW_EQ_CTRL_CMD_FETCHRO) +#define F_FW_EQ_CTRL_CMD_FETCHRO V_FW_EQ_CTRL_CMD_FETCHRO(1U) + +#define S_FW_EQ_CTRL_CMD_HOSTFCMODE 20 +#define M_FW_EQ_CTRL_CMD_HOSTFCMODE 0x3 +#define V_FW_EQ_CTRL_CMD_HOSTFCMODE(x) ((x) << S_FW_EQ_CTRL_CMD_HOSTFCMODE) + +#define S_FW_EQ_CTRL_CMD_PCIECHN 16 +#define V_FW_EQ_CTRL_CMD_PCIECHN(x) ((x) << S_FW_EQ_CTRL_CMD_PCIECHN) + +#define S_FW_EQ_CTRL_CMD_IQID 0 +#define V_FW_EQ_CTRL_CMD_IQID(x) ((x) << S_FW_EQ_CTRL_CMD_IQID) + +#define S_FW_EQ_CTRL_CMD_FBMIN 23 +#define V_FW_EQ_CTRL_CMD_FBMIN(x) ((x) << S_FW_EQ_CTRL_CMD_FBMIN) + +#define S_FW_EQ_CTRL_CMD_FBMAX 20 +#define V_FW_EQ_CTRL_CMD_FBMAX(x) ((x) << S_FW_EQ_CTRL_CMD_FBMAX) + +#define S_FW_EQ_CTRL_CMD_CIDXFTHRESH 16 +#define V_FW_EQ_CTRL_CMD_CIDXFTHRESH(x) ((x) << S_FW_EQ_CTRL_CMD_CIDXFTHRESH) + +#define S_FW_EQ_CTRL_CMD_EQSIZE 0 +#define V_FW_EQ_CTRL_CMD_EQSIZE(x) ((x) << S_FW_EQ_CTRL_CMD_EQSIZE) + enum fw_vi_func { FW_VI_FUNC_ETH, }; diff --git a/drivers/net/cxgbe/cxgbe.h b/drivers/net/cxgbe/cxgbe.h index e4a525607..44f5934d1 100644 --- a/drivers/net/cxgbe/cxgbe.h +++ b/drivers/net/cxgbe/cxgbe.h @@ -42,6 +42,7 @@ int link_start(struct port_info *pi); void init_rspq(struct adapter *adap, struct sge_rspq *q, unsigned int us, unsigned int cnt, unsigned int size, unsigned int iqe_size); int setup_sge_fwevtq(struct adapter *adapter); +int setup_sge_ctrl_txq(struct adapter *adapter); void cfg_queues(struct rte_eth_dev *eth_dev); int cfg_queue_count(struct rte_eth_dev *eth_dev); int init_rss(struct adapter *adap); diff --git a/drivers/net/cxgbe/cxgbe_ethdev.c b/drivers/net/cxgbe/cxgbe_ethdev.c index 1adb8e41f..713dc8fae 100644 --- a/drivers/net/cxgbe/cxgbe_ethdev.c +++ b/drivers/net/cxgbe/cxgbe_ethdev.c @@ -365,6 +365,9 @@ int cxgbe_dev_configure(struct rte_eth_dev *eth_dev) if (err) return err; adapter->flags |= FW_QUEUE_BOUND; + err = setup_sge_ctrl_txq(adapter); + if (err) + return err; } err = cfg_queue_count(eth_dev); diff --git a/drivers/net/cxgbe/cxgbe_main.c b/drivers/net/cxgbe/cxgbe_main.c index 9880257d2..5416800de 100644 --- a/drivers/net/cxgbe/cxgbe_main.c +++ b/drivers/net/cxgbe/cxgbe_main.c @@ -94,6 +94,47 @@ static int fwevtq_handler(struct sge_rspq *q, const __be64 *rsp, return 0; } +/** + * Setup sge control queues to pass control information. + */ +int setup_sge_ctrl_txq(struct adapter *adapter) +{ + struct sge *s = &adapter->sge; + int err = 0, i = 0; + + for_each_port(adapter, i) { + char name[RTE_ETH_NAME_MAX_LEN]; + struct sge_ctrl_txq *q = &s->ctrlq[i]; + + q->q.size = 1024; + err = t4_sge_alloc_ctrl_txq(adapter, q, + adapter->eth_dev, i, + s->fw_evtq.cntxt_id, + rte_socket_id()); + if (err) { + dev_err(adapter, "Failed to alloc ctrl txq. Err: %d", + err); + goto out; + } + snprintf(name, sizeof(name), "cxgbe_ctrl_pool_%d", i); + q->mb_pool = rte_pktmbuf_pool_create(name, s->ctrlq[i].q.size, + RTE_CACHE_LINE_SIZE, + RTE_MBUF_PRIV_ALIGN, + RTE_MBUF_DEFAULT_BUF_SIZE, + SOCKET_ID_ANY); + if (!q->mb_pool) { + dev_err(adapter, "Can't create ctrl pool for port: %d", + i); + err = -ENOMEM; + goto out; + } + } + return 0; +out: + t4_free_sge_resources(adapter); + return err; +} + int setup_sge_fwevtq(struct adapter *adapter) { struct sge *s = &adapter->sge; diff --git a/drivers/net/cxgbe/sge.c b/drivers/net/cxgbe/sge.c index b5d3611da..357b4856d 100644 --- a/drivers/net/cxgbe/sge.c +++ b/drivers/net/cxgbe/sge.c @@ -54,6 +54,11 @@ static inline void ship_tx_pkt_coalesce_wr(struct adapter *adap, */ #define MAX_IMM_TX_PKT_LEN 256 +/* + * Max size of a WR sent through a control Tx queue. + */ +#define MAX_CTRL_WR_LEN SGE_MAX_WR_LEN + /* * Rx buffer sizes for "usembufs" Free List buffers (one ingress packet * per mbuf buffer). We currently only support two sizes for 1500- and @@ -1299,6 +1304,126 @@ int t4_eth_xmit(struct sge_eth_txq *txq, struct rte_mbuf *mbuf, return 0; } +/** + * reclaim_completed_tx_imm - reclaim completed control-queue Tx descs + * @q: the SGE control Tx queue + * + * This is a variant of reclaim_completed_tx() that is used for Tx queues + * that send only immediate data (presently just the control queues) and + * thus do not have any mbufs to release. + */ +static inline void reclaim_completed_tx_imm(struct sge_txq *q) +{ + int hw_cidx = ntohs(q->stat->cidx); + int reclaim = hw_cidx - q->cidx; + + if (reclaim < 0) + reclaim += q->size; + + q->in_use -= reclaim; + q->cidx = hw_cidx; +} + +/** + * is_imm - check whether a packet can be sent as immediate data + * @mbuf: the packet + * + * Returns true if a packet can be sent as a WR with immediate data. + */ +static inline int is_imm(const struct rte_mbuf *mbuf) +{ + return mbuf->pkt_len <= MAX_CTRL_WR_LEN; +} + +/** + * inline_tx_mbuf: inline a packet's data into TX descriptors + * @q: the TX queue where the packet will be inlined + * @from: pointer to data portion of packet + * @to: pointer after cpl where data has to be inlined + * @len: length of data to inline + * + * Inline a packet's contents directly to TX descriptors, starting at + * the given position within the TX DMA ring. + * Most of the complexity of this operation is dealing with wrap arounds + * in the middle of the packet we want to inline. + */ +static void inline_tx_mbuf(const struct sge_txq *q, caddr_t from, caddr_t *to, + int len) +{ + int left = RTE_PTR_DIFF(q->stat, *to); + + if (likely((uintptr_t)*to + len <= (uintptr_t)q->stat)) { + rte_memcpy(*to, from, len); + *to = RTE_PTR_ADD(*to, len); + } else { + rte_memcpy(*to, from, left); + from = RTE_PTR_ADD(from, left); + left = len - left; + rte_memcpy((void *)q->desc, from, left); + *to = RTE_PTR_ADD((void *)q->desc, left); + } +} + +/** + * ctrl_xmit - send a packet through an SGE control Tx queue + * @q: the control queue + * @mbuf: the packet + * + * Send a packet through an SGE control Tx queue. Packets sent through + * a control queue must fit entirely as immediate data. + */ +static int ctrl_xmit(struct sge_ctrl_txq *q, struct rte_mbuf *mbuf) +{ + unsigned int ndesc; + struct fw_wr_hdr *wr; + caddr_t dst; + + if (unlikely(!is_imm(mbuf))) { + WARN_ON(1); + rte_pktmbuf_free(mbuf); + return -1; + } + + reclaim_completed_tx_imm(&q->q); + ndesc = DIV_ROUND_UP(mbuf->pkt_len, sizeof(struct tx_desc)); + t4_os_lock(&q->ctrlq_lock); + + q->full = txq_avail(&q->q) < ndesc ? 1 : 0; + if (unlikely(q->full)) { + t4_os_unlock(&q->ctrlq_lock); + return -1; + } + + wr = (struct fw_wr_hdr *)&q->q.desc[q->q.pidx]; + dst = (void *)wr; + inline_tx_mbuf(&q->q, rte_pktmbuf_mtod(mbuf, caddr_t), + &dst, mbuf->data_len); + + txq_advance(&q->q, ndesc); + if (unlikely(txq_avail(&q->q) < 64)) + wr->lo |= htonl(F_FW_WR_EQUEQ); + + q->txp++; + + ring_tx_db(q->adapter, &q->q); + t4_os_unlock(&q->ctrlq_lock); + + rte_pktmbuf_free(mbuf); + return 0; +} + +/** + * t4_mgmt_tx - send a management message + * @q: the control queue + * @mbuf: the packet containing the management message + * + * Send a management message through control queue. + */ +int t4_mgmt_tx(struct sge_ctrl_txq *q, struct rte_mbuf *mbuf) +{ + return ctrl_xmit(q, mbuf); +} + /** * alloc_ring - allocate resources for an SGE descriptor ring * @dev: the PCI device's core device @@ -2080,6 +2205,64 @@ int t4_sge_alloc_eth_txq(struct adapter *adap, struct sge_eth_txq *txq, return 0; } +int t4_sge_alloc_ctrl_txq(struct adapter *adap, struct sge_ctrl_txq *txq, + struct rte_eth_dev *eth_dev, uint16_t queue_id, + unsigned int iqid, int socket_id) +{ + int ret, nentries; + struct fw_eq_ctrl_cmd c; + struct sge *s = &adap->sge; + struct port_info *pi = (struct port_info *)(eth_dev->data->dev_private); + char z_name[RTE_MEMZONE_NAMESIZE]; + char z_name_sw[RTE_MEMZONE_NAMESIZE]; + + /* Add status entries */ + nentries = txq->q.size + s->stat_len / sizeof(struct tx_desc); + + snprintf(z_name, sizeof(z_name), "%s_%s_%d_%d", + eth_dev->device->driver->name, "ctrl_tx_ring", + eth_dev->data->port_id, queue_id); + snprintf(z_name_sw, sizeof(z_name_sw), "%s_sw_ring", z_name); + + txq->q.desc = alloc_ring(txq->q.size, sizeof(struct tx_desc), + 0, &txq->q.phys_addr, + NULL, 0, queue_id, + socket_id, z_name, z_name_sw); + if (!txq->q.desc) + return -ENOMEM; + + memset(&c, 0, sizeof(c)); + c.op_to_vfn = htonl(V_FW_CMD_OP(FW_EQ_CTRL_CMD) | F_FW_CMD_REQUEST | + F_FW_CMD_WRITE | F_FW_CMD_EXEC | + V_FW_EQ_CTRL_CMD_PFN(adap->pf) | + V_FW_EQ_CTRL_CMD_VFN(0)); + c.alloc_to_len16 = htonl(F_FW_EQ_CTRL_CMD_ALLOC | + F_FW_EQ_CTRL_CMD_EQSTART | (sizeof(c) / 16)); + c.cmpliqid_eqid = htonl(V_FW_EQ_CTRL_CMD_CMPLIQID(0)); + c.physeqid_pkd = htonl(0); + c.fetchszm_to_iqid = + htonl(V_FW_EQ_CTRL_CMD_HOSTFCMODE(X_HOSTFCMODE_NONE) | + V_FW_EQ_CTRL_CMD_PCIECHN(pi->tx_chan) | + F_FW_EQ_CTRL_CMD_FETCHRO | V_FW_EQ_CTRL_CMD_IQID(iqid)); + c.dcaen_to_eqsize = + htonl(V_FW_EQ_CTRL_CMD_FBMIN(X_FETCHBURSTMIN_64B) | + V_FW_EQ_CTRL_CMD_FBMAX(X_FETCHBURSTMAX_512B) | + V_FW_EQ_CTRL_CMD_EQSIZE(nentries)); + c.eqaddr = cpu_to_be64(txq->q.phys_addr); + + ret = t4_wr_mbox(adap, adap->mbox, &c, sizeof(c), &c); + if (ret) { + txq->q.desc = NULL; + return ret; + } + + init_txq(adap, &txq->q, G_FW_EQ_CTRL_CMD_EQID(ntohl(c.cmpliqid_eqid)), + G_FW_EQ_CTRL_CMD_EQID(ntohl(c. physeqid_pkd))); + txq->adapter = adap; + txq->full = 0; + return 0; +} + static void free_txq(struct sge_txq *q) { q->cntxt_id = 0; @@ -2174,7 +2357,7 @@ void t4_sge_tx_monitor_stop(struct adapter *adap) */ void t4_free_sge_resources(struct adapter *adap) { - int i; + unsigned int i; struct sge_eth_rxq *rxq = &adap->sge.ethrxq[0]; struct sge_eth_txq *txq = &adap->sge.ethtxq[0]; @@ -2191,6 +2374,18 @@ void t4_free_sge_resources(struct adapter *adap) } } + /* clean up control Tx queues */ + for (i = 0; i < ARRAY_SIZE(adap->sge.ctrlq); i++) { + struct sge_ctrl_txq *cq = &adap->sge.ctrlq[i]; + + if (cq->q.desc) { + reclaim_completed_tx_imm(&cq->q); + t4_ctrl_eq_free(adap, adap->mbox, adap->pf, 0, + cq->q.cntxt_id); + free_txq(&cq->q); + } + } + if (adap->sge.fw_evtq.desc) free_rspq_fl(adap, &adap->sge.fw_evtq, NULL); } From patchwork Fri Jun 8 17:58:14 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rahul Lakkireddy X-Patchwork-Id: 40930 X-Patchwork-Delegate: ferruh.yigit@amd.com Return-Path: X-Original-To: patchwork@dpdk.org Delivered-To: patchwork@dpdk.org Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 7C4B81CFF9; Fri, 8 Jun 2018 19:59:36 +0200 (CEST) Received: from stargate.chelsio.com (stargate.chelsio.com [12.32.117.8]) by dpdk.org (Postfix) with ESMTP id 727D01BB35 for ; Fri, 8 Jun 2018 19:59:34 +0200 (CEST) Received: from localhost (scalar.blr.asicdesigners.com [10.193.185.94]) by stargate.chelsio.com (8.13.8/8.13.8) with ESMTP id w58HxLwv017297; Fri, 8 Jun 2018 10:59:22 -0700 From: Rahul Lakkireddy To: dev@dpdk.org Cc: shaguna@chelsio.com, kumaras@chelsio.com, indranil@chelsio.com, nirranjan@chelsio.com Date: Fri, 8 Jun 2018 23:28:14 +0530 Message-Id: X-Mailer: git-send-email 2.5.3 In-Reply-To: References: In-Reply-To: References: Subject: [dpdk-dev] [PATCH 4/7] net/cxgbe: implement flow create operation X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" From: Shagun Agrawal Define filter work request API used to construct filter operations to be communicated with firmware. These requests are sent via control queue and completions come asynchronously in firmware event queue. Implement flow create operation to create filters in LE-TCAM (maskfull) region at specified index. Signed-off-by: Shagun Agrawal Signed-off-by: Kumar Sanghvi Signed-off-by: Rahul Lakkireddy --- drivers/net/cxgbe/base/adapter.h | 21 ++ drivers/net/cxgbe/base/t4_msg.h | 22 ++ drivers/net/cxgbe/base/t4fw_interface.h | 145 +++++++++++++ drivers/net/cxgbe/cxgbe.h | 2 + drivers/net/cxgbe/cxgbe_filter.c | 356 ++++++++++++++++++++++++++++++++ drivers/net/cxgbe/cxgbe_filter.h | 32 +++ drivers/net/cxgbe/cxgbe_flow.c | 82 +++++++- drivers/net/cxgbe/cxgbe_flow.h | 4 + drivers/net/cxgbe/cxgbe_main.c | 36 ++++ 9 files changed, 699 insertions(+), 1 deletion(-) diff --git a/drivers/net/cxgbe/base/adapter.h b/drivers/net/cxgbe/base/adapter.h index 9a66a4a99..7f9ddae01 100644 --- a/drivers/net/cxgbe/base/adapter.h +++ b/drivers/net/cxgbe/base/adapter.h @@ -717,6 +717,27 @@ static inline void t4_os_atomic_list_del(struct mbox_entry *entry, t4_os_unlock(lock); } +/** + * t4_init_completion - initialize completion + * @c: the completion context + */ +static inline void t4_init_completion(struct t4_completion *c) +{ + c->done = 0; + t4_os_lock_init(&c->lock); +} + +/** + * t4_complete - set completion as done + * @c: the completion context + */ +static inline void t4_complete(struct t4_completion *c) +{ + t4_os_lock(&c->lock); + c->done = 1; + t4_os_unlock(&c->lock); +} + void *t4_alloc_mem(size_t size); void t4_free_mem(void *addr); #define t4_os_alloc(_size) t4_alloc_mem((_size)) diff --git a/drivers/net/cxgbe/base/t4_msg.h b/drivers/net/cxgbe/base/t4_msg.h index 74b4fc193..43d1cb66f 100644 --- a/drivers/net/cxgbe/base/t4_msg.h +++ b/drivers/net/cxgbe/base/t4_msg.h @@ -7,6 +7,7 @@ #define T4_MSG_H enum { + CPL_SET_TCB_RPL = 0x3A, CPL_SGE_EGR_UPDATE = 0xA5, CPL_FW4_MSG = 0xC0, CPL_FW6_MSG = 0xE0, @@ -25,6 +26,13 @@ union opcode_tid { __u8 opcode; }; +#define G_TID(x) ((x) & 0xFFFFFF) + +#define OPCODE_TID(cmd) ((cmd)->ot.opcode_tid) + +/* extract the TID from a CPL command */ +#define GET_TID(cmd) (G_TID(be32_to_cpu(OPCODE_TID(cmd)))) + struct rss_header { __u8 opcode; #if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN @@ -66,6 +74,20 @@ struct work_request_hdr { #define WR_HDR_SIZE 0 #endif +#define S_COOKIE 5 +#define M_COOKIE 0x7 +#define V_COOKIE(x) ((x) << S_COOKIE) +#define G_COOKIE(x) (((x) >> S_COOKIE) & M_COOKIE) + +struct cpl_set_tcb_rpl { + RSS_HDR + union opcode_tid ot; + __be16 rsvd; + __u8 cookie; + __u8 status; + __be64 oldval; +}; + struct cpl_tx_data { union opcode_tid ot; __be32 len; diff --git a/drivers/net/cxgbe/base/t4fw_interface.h b/drivers/net/cxgbe/base/t4fw_interface.h index 44b6f6dac..842aa1263 100644 --- a/drivers/net/cxgbe/base/t4fw_interface.h +++ b/drivers/net/cxgbe/base/t4fw_interface.h @@ -54,6 +54,7 @@ enum fw_memtype { ********************************/ enum fw_wr_opcodes { + FW_FILTER_WR = 0x02, FW_ETH_TX_PKT_WR = 0x08, FW_ETH_TX_PKTS_WR = 0x09, FW_ETH_TX_PKT_VM_WR = 0x11, @@ -143,6 +144,150 @@ struct fw_eth_tx_pkts_vm_wr { __be16 vlantci; }; +/* filter wr reply code in cookie in CPL_SET_TCB_RPL */ +enum fw_filter_wr_cookie { + FW_FILTER_WR_SUCCESS, + FW_FILTER_WR_FLT_ADDED, + FW_FILTER_WR_FLT_DELETED, + FW_FILTER_WR_SMT_TBL_FULL, + FW_FILTER_WR_EINVAL, +}; + +struct fw_filter_wr { + __be32 op_pkd; + __be32 len16_pkd; + __be64 r3; + __be32 tid_to_iq; + __be32 del_filter_to_l2tix; + __be16 ethtype; + __be16 ethtypem; + __u8 frag_to_ovlan_vldm; + __u8 smac_sel; + __be16 rx_chan_rx_rpl_iq; + __be32 maci_to_matchtypem; + __u8 ptcl; + __u8 ptclm; + __u8 ttyp; + __u8 ttypm; + __be16 ivlan; + __be16 ivlanm; + __be16 ovlan; + __be16 ovlanm; + __u8 lip[16]; + __u8 lipm[16]; + __u8 fip[16]; + __u8 fipm[16]; + __be16 lp; + __be16 lpm; + __be16 fp; + __be16 fpm; + __be16 r7; + __u8 sma[6]; +}; + +#define S_FW_FILTER_WR_TID 12 +#define V_FW_FILTER_WR_TID(x) ((x) << S_FW_FILTER_WR_TID) + +#define S_FW_FILTER_WR_RQTYPE 11 +#define V_FW_FILTER_WR_RQTYPE(x) ((x) << S_FW_FILTER_WR_RQTYPE) + +#define S_FW_FILTER_WR_NOREPLY 10 +#define V_FW_FILTER_WR_NOREPLY(x) ((x) << S_FW_FILTER_WR_NOREPLY) + +#define S_FW_FILTER_WR_IQ 0 +#define V_FW_FILTER_WR_IQ(x) ((x) << S_FW_FILTER_WR_IQ) + +#define S_FW_FILTER_WR_DEL_FILTER 31 +#define V_FW_FILTER_WR_DEL_FILTER(x) ((x) << S_FW_FILTER_WR_DEL_FILTER) +#define F_FW_FILTER_WR_DEL_FILTER V_FW_FILTER_WR_DEL_FILTER(1U) + +#define S_FW_FILTER_WR_RPTTID 25 +#define V_FW_FILTER_WR_RPTTID(x) ((x) << S_FW_FILTER_WR_RPTTID) + +#define S_FW_FILTER_WR_DROP 24 +#define V_FW_FILTER_WR_DROP(x) ((x) << S_FW_FILTER_WR_DROP) + +#define S_FW_FILTER_WR_DIRSTEER 23 +#define V_FW_FILTER_WR_DIRSTEER(x) ((x) << S_FW_FILTER_WR_DIRSTEER) + +#define S_FW_FILTER_WR_MASKHASH 22 +#define V_FW_FILTER_WR_MASKHASH(x) ((x) << S_FW_FILTER_WR_MASKHASH) + +#define S_FW_FILTER_WR_DIRSTEERHASH 21 +#define V_FW_FILTER_WR_DIRSTEERHASH(x) ((x) << S_FW_FILTER_WR_DIRSTEERHASH) + +#define S_FW_FILTER_WR_LPBK 20 +#define V_FW_FILTER_WR_LPBK(x) ((x) << S_FW_FILTER_WR_LPBK) + +#define S_FW_FILTER_WR_DMAC 19 +#define V_FW_FILTER_WR_DMAC(x) ((x) << S_FW_FILTER_WR_DMAC) + +#define S_FW_FILTER_WR_INSVLAN 17 +#define V_FW_FILTER_WR_INSVLAN(x) ((x) << S_FW_FILTER_WR_INSVLAN) + +#define S_FW_FILTER_WR_RMVLAN 16 +#define V_FW_FILTER_WR_RMVLAN(x) ((x) << S_FW_FILTER_WR_RMVLAN) + +#define S_FW_FILTER_WR_HITCNTS 15 +#define V_FW_FILTER_WR_HITCNTS(x) ((x) << S_FW_FILTER_WR_HITCNTS) + +#define S_FW_FILTER_WR_TXCHAN 13 +#define V_FW_FILTER_WR_TXCHAN(x) ((x) << S_FW_FILTER_WR_TXCHAN) + +#define S_FW_FILTER_WR_PRIO 12 +#define V_FW_FILTER_WR_PRIO(x) ((x) << S_FW_FILTER_WR_PRIO) + +#define S_FW_FILTER_WR_L2TIX 0 +#define V_FW_FILTER_WR_L2TIX(x) ((x) << S_FW_FILTER_WR_L2TIX) + +#define S_FW_FILTER_WR_FRAG 7 +#define V_FW_FILTER_WR_FRAG(x) ((x) << S_FW_FILTER_WR_FRAG) + +#define S_FW_FILTER_WR_FRAGM 6 +#define V_FW_FILTER_WR_FRAGM(x) ((x) << S_FW_FILTER_WR_FRAGM) + +#define S_FW_FILTER_WR_IVLAN_VLD 5 +#define V_FW_FILTER_WR_IVLAN_VLD(x) ((x) << S_FW_FILTER_WR_IVLAN_VLD) + +#define S_FW_FILTER_WR_OVLAN_VLD 4 +#define V_FW_FILTER_WR_OVLAN_VLD(x) ((x) << S_FW_FILTER_WR_OVLAN_VLD) + +#define S_FW_FILTER_WR_IVLAN_VLDM 3 +#define V_FW_FILTER_WR_IVLAN_VLDM(x) ((x) << S_FW_FILTER_WR_IVLAN_VLDM) + +#define S_FW_FILTER_WR_OVLAN_VLDM 2 +#define V_FW_FILTER_WR_OVLAN_VLDM(x) ((x) << S_FW_FILTER_WR_OVLAN_VLDM) + +#define S_FW_FILTER_WR_RX_CHAN 15 +#define V_FW_FILTER_WR_RX_CHAN(x) ((x) << S_FW_FILTER_WR_RX_CHAN) + +#define S_FW_FILTER_WR_RX_RPL_IQ 0 +#define V_FW_FILTER_WR_RX_RPL_IQ(x) ((x) << S_FW_FILTER_WR_RX_RPL_IQ) + +#define S_FW_FILTER_WR_MACI 23 +#define V_FW_FILTER_WR_MACI(x) ((x) << S_FW_FILTER_WR_MACI) + +#define S_FW_FILTER_WR_MACIM 14 +#define V_FW_FILTER_WR_MACIM(x) ((x) << S_FW_FILTER_WR_MACIM) + +#define S_FW_FILTER_WR_FCOE 13 +#define V_FW_FILTER_WR_FCOE(x) ((x) << S_FW_FILTER_WR_FCOE) + +#define S_FW_FILTER_WR_FCOEM 12 +#define V_FW_FILTER_WR_FCOEM(x) ((x) << S_FW_FILTER_WR_FCOEM) + +#define S_FW_FILTER_WR_PORT 9 +#define V_FW_FILTER_WR_PORT(x) ((x) << S_FW_FILTER_WR_PORT) + +#define S_FW_FILTER_WR_PORTM 6 +#define V_FW_FILTER_WR_PORTM(x) ((x) << S_FW_FILTER_WR_PORTM) + +#define S_FW_FILTER_WR_MATCHTYPE 3 +#define V_FW_FILTER_WR_MATCHTYPE(x) ((x) << S_FW_FILTER_WR_MATCHTYPE) + +#define S_FW_FILTER_WR_MATCHTYPEM 0 +#define V_FW_FILTER_WR_MATCHTYPEM(x) ((x) << S_FW_FILTER_WR_MATCHTYPEM) + /****************************************************************************** * C O M M A N D s *********************/ diff --git a/drivers/net/cxgbe/cxgbe.h b/drivers/net/cxgbe/cxgbe.h index 44f5934d1..27d6e2b84 100644 --- a/drivers/net/cxgbe/cxgbe.h +++ b/drivers/net/cxgbe/cxgbe.h @@ -38,6 +38,8 @@ void cxgbe_close(struct adapter *adapter); void cxgbe_stats_get(struct port_info *pi, struct port_stats *stats); void cxgbevf_stats_get(struct port_info *pi, struct port_stats *stats); void cxgbe_stats_reset(struct port_info *pi); +int cxgbe_poll_for_completion(struct sge_rspq *q, unsigned int us, + unsigned int cnt, struct t4_completion *c); int link_start(struct port_info *pi); void init_rspq(struct adapter *adap, struct sge_rspq *q, unsigned int us, unsigned int cnt, unsigned int size, unsigned int iqe_size); diff --git a/drivers/net/cxgbe/cxgbe_filter.c b/drivers/net/cxgbe/cxgbe_filter.c index 6b10a8be1..cf83ec9c0 100644 --- a/drivers/net/cxgbe/cxgbe_filter.c +++ b/drivers/net/cxgbe/cxgbe_filter.c @@ -33,6 +33,50 @@ int validate_filter(struct adapter *adapter, struct ch_filter_specification *fs) return 0; } +/** + * Get the queue to which the traffic must be steered to. + */ +static unsigned int get_filter_steerq(struct rte_eth_dev *dev, + struct ch_filter_specification *fs) +{ + struct port_info *pi = ethdev2pinfo(dev); + struct adapter *adapter = pi->adapter; + unsigned int iq; + + /* + * If the user has requested steering matching Ingress Packets + * to a specific Queue Set, we need to make sure it's in range + * for the port and map that into the Absolute Queue ID of the + * Queue Set's Response Queue. + */ + if (!fs->dirsteer) { + iq = 0; + } else { + /* + * If the iq id is greater than the number of qsets, + * then assume it is an absolute qid. + */ + if (fs->iq < pi->n_rx_qsets) + iq = adapter->sge.ethrxq[pi->first_qset + + fs->iq].rspq.abs_id; + else + iq = fs->iq; + } + + return iq; +} + +/* Return an error number if the indicated filter isn't writable ... */ +int writable_filter(struct filter_entry *f) +{ + if (f->locked) + return -EPERM; + if (f->pending) + return -EBUSY; + + return 0; +} + /** * Check if entry already filled. */ @@ -75,3 +119,315 @@ int cxgbe_alloc_ftid(struct adapter *adap, unsigned int family) return pos < size ? pos : -1; } + +/** + * Clear a filter and release any of its resources that we own. This also + * clears the filter's "pending" status. + */ +void clear_filter(struct filter_entry *f) +{ + /* + * The zeroing of the filter rule below clears the filter valid, + * pending, locked flags etc. so it's all we need for + * this operation. + */ + memset(f, 0, sizeof(*f)); +} + +int set_filter_wr(struct rte_eth_dev *dev, unsigned int fidx) +{ + struct adapter *adapter = ethdev2adap(dev); + struct filter_entry *f = &adapter->tids.ftid_tab[fidx]; + struct rte_mbuf *mbuf; + struct fw_filter_wr *fwr; + struct sge_ctrl_txq *ctrlq; + unsigned int port_id = ethdev2pinfo(dev)->port_id; + int ret; + + ctrlq = &adapter->sge.ctrlq[port_id]; + mbuf = rte_pktmbuf_alloc(ctrlq->mb_pool); + if (!mbuf) { + ret = -ENOMEM; + goto out; + } + + mbuf->data_len = sizeof(*fwr); + mbuf->pkt_len = mbuf->data_len; + + fwr = rte_pktmbuf_mtod(mbuf, struct fw_filter_wr *); + memset(fwr, 0, sizeof(*fwr)); + + /* + * Construct the work request to set the filter. + */ + fwr->op_pkd = cpu_to_be32(V_FW_WR_OP(FW_FILTER_WR)); + fwr->len16_pkd = cpu_to_be32(V_FW_WR_LEN16(sizeof(*fwr) / 16)); + fwr->tid_to_iq = + cpu_to_be32(V_FW_FILTER_WR_TID(f->tid) | + V_FW_FILTER_WR_RQTYPE(f->fs.type) | + V_FW_FILTER_WR_NOREPLY(0) | + V_FW_FILTER_WR_IQ(f->fs.iq)); + fwr->del_filter_to_l2tix = + cpu_to_be32(V_FW_FILTER_WR_DROP(f->fs.action == FILTER_DROP) | + V_FW_FILTER_WR_DIRSTEER(f->fs.dirsteer) | + V_FW_FILTER_WR_HITCNTS(f->fs.hitcnts) | + V_FW_FILTER_WR_PRIO(f->fs.prio)); + fwr->ethtype = cpu_to_be16(f->fs.val.ethtype); + fwr->ethtypem = cpu_to_be16(f->fs.mask.ethtype); + fwr->smac_sel = 0; + fwr->rx_chan_rx_rpl_iq = + cpu_to_be16(V_FW_FILTER_WR_RX_CHAN(0) | + V_FW_FILTER_WR_RX_RPL_IQ(adapter->sge.fw_evtq.abs_id + )); + fwr->ptcl = f->fs.val.proto; + fwr->ptclm = f->fs.mask.proto; + rte_memcpy(fwr->lip, f->fs.val.lip, sizeof(fwr->lip)); + rte_memcpy(fwr->lipm, f->fs.mask.lip, sizeof(fwr->lipm)); + rte_memcpy(fwr->fip, f->fs.val.fip, sizeof(fwr->fip)); + rte_memcpy(fwr->fipm, f->fs.mask.fip, sizeof(fwr->fipm)); + fwr->lp = cpu_to_be16(f->fs.val.lport); + fwr->lpm = cpu_to_be16(f->fs.mask.lport); + fwr->fp = cpu_to_be16(f->fs.val.fport); + fwr->fpm = cpu_to_be16(f->fs.mask.fport); + + /* + * Mark the filter as "pending" and ship off the Filter Work Request. + * When we get the Work Request Reply we'll clear the pending status. + */ + f->pending = 1; + t4_mgmt_tx(ctrlq, mbuf); + return 0; + +out: + return ret; +} + +/** + * Set the corresponding entry in the bitmap. 4 slots are + * marked for IPv6, whereas only 1 slot is marked for IPv4. + */ +static int cxgbe_set_ftid(struct tid_info *t, int fidx, int family) +{ + t4_os_lock(&t->ftid_lock); + if (rte_bitmap_get(t->ftid_bmap, fidx)) { + t4_os_unlock(&t->ftid_lock); + return -EBUSY; + } + + if (family == FILTER_TYPE_IPV4) { + rte_bitmap_set(t->ftid_bmap, fidx); + } else { + rte_bitmap_set(t->ftid_bmap, fidx); + rte_bitmap_set(t->ftid_bmap, fidx + 1); + rte_bitmap_set(t->ftid_bmap, fidx + 2); + rte_bitmap_set(t->ftid_bmap, fidx + 3); + } + t4_os_unlock(&t->ftid_lock); + return 0; +} + +/** + * Clear the corresponding entry in the bitmap. 4 slots are + * cleared for IPv6, whereas only 1 slot is cleared for IPv4. + */ +static void cxgbe_clear_ftid(struct tid_info *t, int fidx, int family) +{ + t4_os_lock(&t->ftid_lock); + if (family == FILTER_TYPE_IPV4) { + rte_bitmap_clear(t->ftid_bmap, fidx); + } else { + rte_bitmap_clear(t->ftid_bmap, fidx); + rte_bitmap_clear(t->ftid_bmap, fidx + 1); + rte_bitmap_clear(t->ftid_bmap, fidx + 2); + rte_bitmap_clear(t->ftid_bmap, fidx + 3); + } + t4_os_unlock(&t->ftid_lock); +} + +/** + * Check a Chelsio Filter Request for validity, convert it into our internal + * format and send it to the hardware. Return 0 on success, an error number + * otherwise. We attach any provided filter operation context to the internal + * filter specification in order to facilitate signaling completion of the + * operation. + */ +int cxgbe_set_filter(struct rte_eth_dev *dev, unsigned int filter_id, + struct ch_filter_specification *fs, + struct filter_ctx *ctx) +{ + struct port_info *pi = ethdev2pinfo(dev); + struct adapter *adapter = pi->adapter; + unsigned int fidx, iq, fid_bit = 0; + struct filter_entry *f; + int ret; + + if (filter_id >= adapter->tids.nftids) + return -ERANGE; + + ret = validate_filter(adapter, fs); + if (ret) + return ret; + + /* + * Ensure filter id is aligned on the 4 slot boundary for IPv6 + * maskfull filters. + */ + if (fs->type) + filter_id &= ~(0x3); + + ret = is_filter_set(&adapter->tids, filter_id, fs->type); + if (ret) + return -EBUSY; + + iq = get_filter_steerq(dev, fs); + + /* + * IPv6 filters occupy four slots and must be aligned on + * four-slot boundaries. IPv4 filters only occupy a single + * slot and have no alignment requirements but writing a new + * IPv4 filter into the middle of an existing IPv6 filter + * requires clearing the old IPv6 filter. + */ + if (fs->type == FILTER_TYPE_IPV4) { /* IPv4 */ + /* + * If our IPv4 filter isn't being written to a + * multiple of four filter index and there's an IPv6 + * filter at the multiple of 4 base slot, then we need + * to delete that IPv6 filter ... + */ + fidx = filter_id & ~0x3; + if (fidx != filter_id && adapter->tids.ftid_tab[fidx].fs.type) { + f = &adapter->tids.ftid_tab[fidx]; + if (f->valid) + return -EBUSY; + } + } else { /* IPv6 */ + /* + * Ensure that the IPv6 filter is aligned on a + * multiple of 4 boundary. + */ + if (filter_id & 0x3) + return -EINVAL; + + /* + * Check all except the base overlapping IPv4 filter + * slots. + */ + for (fidx = filter_id + 1; fidx < filter_id + 4; fidx++) { + f = &adapter->tids.ftid_tab[fidx]; + if (f->valid) + return -EBUSY; + } + } + + /* + * Check to make sure that provided filter index is not + * already in use by someone else + */ + f = &adapter->tids.ftid_tab[filter_id]; + if (f->valid) + return -EBUSY; + + fidx = adapter->tids.ftid_base + filter_id; + fid_bit = filter_id; + ret = cxgbe_set_ftid(&adapter->tids, fid_bit, + fs->type ? FILTER_TYPE_IPV6 : FILTER_TYPE_IPV4); + if (ret) + return ret; + + /* + * Check to make sure the filter requested is writable ... + */ + ret = writable_filter(f); + if (ret) { + /* Clear the bits we have set above */ + cxgbe_clear_ftid(&adapter->tids, fid_bit, + fs->type ? FILTER_TYPE_IPV6 : + FILTER_TYPE_IPV4); + return ret; + } + + /* + * Convert the filter specification into our internal format. + * We copy the PF/VF specification into the Outer VLAN field + * here so the rest of the code -- including the interface to + * the firmware -- doesn't have to constantly do these checks. + */ + f->fs = *fs; + f->fs.iq = iq; + f->dev = dev; + + /* + * Attempt to set the filter. If we don't succeed, we clear + * it and return the failure. + */ + f->ctx = ctx; + f->tid = fidx; /* Save the actual tid */ + ret = set_filter_wr(dev, filter_id); + if (ret) { + fid_bit = f->tid - adapter->tids.ftid_base; + cxgbe_clear_ftid(&adapter->tids, fid_bit, + fs->type ? FILTER_TYPE_IPV6 : + FILTER_TYPE_IPV4); + clear_filter(f); + } + + return ret; +} + +/** + * Handle a LE-TCAM filter write/deletion reply. + */ +void filter_rpl(struct adapter *adap, const struct cpl_set_tcb_rpl *rpl) +{ + struct filter_entry *f = NULL; + unsigned int tid = GET_TID(rpl); + int idx, max_fidx = adap->tids.nftids; + + /* Get the corresponding filter entry for this tid */ + if (adap->tids.ftid_tab) { + /* Check this in normal filter region */ + idx = tid - adap->tids.ftid_base; + if (idx >= max_fidx) + return; + + f = &adap->tids.ftid_tab[idx]; + if (f->tid != tid) + return; + } + + /* We found the filter entry for this tid */ + if (f) { + unsigned int ret = G_COOKIE(rpl->cookie); + struct filter_ctx *ctx; + + /* + * Pull off any filter operation context attached to the + * filter. + */ + ctx = f->ctx; + f->ctx = NULL; + + if (ret == FW_FILTER_WR_FLT_ADDED) { + f->pending = 0; /* asynchronous setup completed */ + f->valid = 1; + if (ctx) { + ctx->tid = f->tid; + ctx->result = 0; + } + } else { + /* + * Something went wrong. Issue a warning about the + * problem and clear everything out. + */ + dev_warn(adap, "filter %u setup failed with error %u\n", + idx, ret); + clear_filter(f); + if (ctx) + ctx->result = -EINVAL; + } + + if (ctx) + t4_complete(&ctx->completion); + } +} diff --git a/drivers/net/cxgbe/cxgbe_filter.h b/drivers/net/cxgbe/cxgbe_filter.h index a9d2d3d39..e12baa7f9 100644 --- a/drivers/net/cxgbe/cxgbe_filter.h +++ b/drivers/net/cxgbe/cxgbe_filter.h @@ -112,14 +112,39 @@ enum filter_type { FILTER_TYPE_IPV6, }; +struct t4_completion { + unsigned int done; /* completion done (0 - No, 1 - Yes) */ + rte_spinlock_t lock; /* completion lock */ +}; + +/* + * Filter operation context to allow callers to wait for + * an asynchronous completion. + */ +struct filter_ctx { + struct t4_completion completion; /* completion rendezvous */ + int result; /* result of operation */ + u32 tid; /* to store tid of hash filter */ +}; + /* * Host shadow copy of ingress filter entry. This is in host native format * and doesn't match the ordering or bit order, etc. of the hardware or the * firmware command. */ struct filter_entry { + /* + * Administrative fields for filter. + */ + u32 valid:1; /* filter allocated and valid */ + u32 locked:1; /* filter is administratively locked */ + u32 pending:1; /* filter action is pending FW reply */ + struct filter_ctx *ctx; /* caller's completion hook */ struct rte_eth_dev *dev; /* Port's rte eth device */ + /* This will store the actual tid */ + u32 tid; + /* * The filter itself. */ @@ -183,6 +208,13 @@ cxgbe_bitmap_find_free_region(struct rte_bitmap *bmap, unsigned int size, } bool is_filter_set(struct tid_info *, int fidx, int family); +void filter_rpl(struct adapter *adap, const struct cpl_set_tcb_rpl *rpl); +void clear_filter(struct filter_entry *f); +int set_filter_wr(struct rte_eth_dev *dev, unsigned int fidx); +int writable_filter(struct filter_entry *f); +int cxgbe_set_filter(struct rte_eth_dev *dev, unsigned int filter_id, + struct ch_filter_specification *fs, + struct filter_ctx *ctx); int cxgbe_alloc_ftid(struct adapter *adap, unsigned int family); int validate_filter(struct adapter *adap, struct ch_filter_specification *fs); #endif /* _CXGBE_FILTER_H_ */ diff --git a/drivers/net/cxgbe/cxgbe_flow.c b/drivers/net/cxgbe/cxgbe_flow.c index a01708e70..7fa3f5810 100644 --- a/drivers/net/cxgbe/cxgbe_flow.c +++ b/drivers/net/cxgbe/cxgbe_flow.c @@ -391,6 +391,86 @@ cxgbe_flow_parse(struct rte_flow *flow, return cxgbe_rtef_parse_actions(flow, action, e); } +static int __cxgbe_flow_create(struct rte_eth_dev *dev, struct rte_flow *flow) +{ + struct ch_filter_specification *fs = &flow->fs; + struct adapter *adap = ethdev2adap(dev); + struct filter_ctx ctx; + unsigned int fidx; + int err; + + if (cxgbe_get_fidx(flow, &fidx)) + return -ENOMEM; + if (cxgbe_verify_fidx(flow, fidx, 0)) + return -1; + + t4_init_completion(&ctx.completion); + /* go create the filter */ + err = cxgbe_set_filter(dev, fidx, fs, &ctx); + if (err) { + dev_err(adap, "Error %d while creating filter.\n", err); + return err; + } + + /* Poll the FW for reply */ + err = cxgbe_poll_for_completion(&adap->sge.fw_evtq, + CXGBE_FLOW_POLL_US, + CXGBE_FLOW_POLL_CNT, + &ctx.completion); + if (err) { + dev_err(adap, "Filter set operation timed out (%d)\n", err); + return err; + } + if (ctx.result) { + dev_err(adap, "Hardware error %d while creating the filter.\n", + ctx.result); + return ctx.result; + } + + flow->fidx = fidx; + flow->f = &adap->tids.ftid_tab[fidx]; + + return 0; +} + +static struct rte_flow * +cxgbe_flow_create(struct rte_eth_dev *dev, + const struct rte_flow_attr *attr, + const struct rte_flow_item item[], + const struct rte_flow_action action[], + struct rte_flow_error *e) +{ + struct rte_flow *flow; + int ret; + + flow = t4_os_alloc(sizeof(struct rte_flow)); + if (!flow) { + rte_flow_error_set(e, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE, + NULL, "Unable to allocate memory for" + " filter_entry"); + return NULL; + } + + flow->item_parser = parseitem; + flow->dev = dev; + + if (cxgbe_flow_parse(flow, attr, item, action, e)) { + t4_os_free(flow); + return NULL; + } + + /* go, interact with cxgbe_filter */ + ret = __cxgbe_flow_create(dev, flow); + if (ret) { + rte_flow_error_set(e, ret, RTE_FLOW_ERROR_TYPE_HANDLE, + NULL, "Unable to create flow rule"); + t4_os_free(flow); + return NULL; + } + + return flow; +} + static int cxgbe_flow_validate(struct rte_eth_dev *dev, const struct rte_flow_attr *attr, @@ -443,7 +523,7 @@ cxgbe_flow_validate(struct rte_eth_dev *dev, static const struct rte_flow_ops cxgbe_flow_ops = { .validate = cxgbe_flow_validate, - .create = NULL, + .create = cxgbe_flow_create, .destroy = NULL, .flush = NULL, .query = NULL, diff --git a/drivers/net/cxgbe/cxgbe_flow.h b/drivers/net/cxgbe/cxgbe_flow.h index 45bc37082..4456376aa 100644 --- a/drivers/net/cxgbe/cxgbe_flow.h +++ b/drivers/net/cxgbe/cxgbe_flow.h @@ -7,6 +7,10 @@ #include #include "cxgbe_filter.h" +#include "cxgbe.h" + +#define CXGBE_FLOW_POLL_US 10 +#define CXGBE_FLOW_POLL_CNT 10 struct chrte_fparse { int (*fptr)(const void *mask, /* currently supported mask */ diff --git a/drivers/net/cxgbe/cxgbe_main.c b/drivers/net/cxgbe/cxgbe_main.c index 5416800de..a00e0700d 100644 --- a/drivers/net/cxgbe/cxgbe_main.c +++ b/drivers/net/cxgbe/cxgbe_main.c @@ -86,6 +86,10 @@ static int fwevtq_handler(struct sge_rspq *q, const __be64 *rsp, const struct cpl_fw6_msg *msg = (const void *)rsp; t4_handle_fw_rpl(q->adapter, msg->data); + } else if (opcode == CPL_SET_TCB_RPL) { + const struct cpl_set_tcb_rpl *p = (const void *)rsp; + + filter_rpl(q->adapter, p); } else { dev_err(adapter, "unexpected CPL %#x on FW event queue\n", opcode); @@ -135,6 +139,38 @@ int setup_sge_ctrl_txq(struct adapter *adapter) return err; } +/** + * cxgbe_poll_for_completion: Poll rxq for completion + * @q: rxq to poll + * @us: microseconds to delay + * @cnt: number of times to poll + * @c: completion to check for 'done' status + * + * Polls the rxq for reples until completion is done or the count + * expires. + */ +int cxgbe_poll_for_completion(struct sge_rspq *q, unsigned int us, + unsigned int cnt, struct t4_completion *c) +{ + unsigned int i; + unsigned int work_done, budget = 4; + + if (!c) + return -EINVAL; + + for (i = 0; i < cnt; i++) { + cxgbe_poll(q, NULL, budget, &work_done); + t4_os_lock(&c->lock); + if (c->done) { + t4_os_unlock(&c->lock); + return 0; + } + t4_os_unlock(&c->lock); + udelay(us); + } + return -ETIMEDOUT; +} + int setup_sge_fwevtq(struct adapter *adapter) { struct sge *s = &adapter->sge; From patchwork Fri Jun 8 17:58:15 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rahul Lakkireddy X-Patchwork-Id: 40929 X-Patchwork-Delegate: ferruh.yigit@amd.com Return-Path: X-Original-To: patchwork@dpdk.org Delivered-To: patchwork@dpdk.org Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 5FECD1BB2F; Fri, 8 Jun 2018 19:59:34 +0200 (CEST) Received: from stargate.chelsio.com (stargate.chelsio.com [12.32.117.8]) by dpdk.org (Postfix) with ESMTP id BCE361BB2E for ; Fri, 8 Jun 2018 19:59:32 +0200 (CEST) Received: from localhost (scalar.blr.asicdesigners.com [10.193.185.94]) by stargate.chelsio.com (8.13.8/8.13.8) with ESMTP id w58HxTqS017300; Fri, 8 Jun 2018 10:59:30 -0700 From: Rahul Lakkireddy To: dev@dpdk.org Cc: shaguna@chelsio.com, kumaras@chelsio.com, indranil@chelsio.com, nirranjan@chelsio.com Date: Fri, 8 Jun 2018 23:28:15 +0530 Message-Id: <1e8a8ad0cda86e0a61ce42e82f01dce4f888b842.1528469677.git.rahul.lakkireddy@chelsio.com> X-Mailer: git-send-email 2.5.3 In-Reply-To: References: In-Reply-To: References: Subject: [dpdk-dev] [PATCH 5/7] net/cxgbe: implement flow destroy operation X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" From: Shagun Agrawal Add API to construct delete filter work request to remove filter at specified index in LE-TCAM (maskfull) region. Signed-off-by: Shagun Agrawal Signed-off-by: Kumar Sanghvi Signed-off-by: Rahul Lakkireddy --- drivers/net/cxgbe/cxgbe_filter.c | 114 +++++++++++++++++++++++++++++++++++++++ drivers/net/cxgbe/cxgbe_filter.h | 3 ++ drivers/net/cxgbe/cxgbe_flow.c | 53 +++++++++++++++++- 3 files changed, 169 insertions(+), 1 deletion(-) diff --git a/drivers/net/cxgbe/cxgbe_filter.c b/drivers/net/cxgbe/cxgbe_filter.c index cf83ec9c0..8129ed01f 100644 --- a/drivers/net/cxgbe/cxgbe_filter.c +++ b/drivers/net/cxgbe/cxgbe_filter.c @@ -134,6 +134,60 @@ void clear_filter(struct filter_entry *f) memset(f, 0, sizeof(*f)); } +/** + * t4_mk_filtdelwr - create a delete filter WR + * @ftid: the filter ID + * @wr: the filter work request to populate + * @qid: ingress queue to receive the delete notification + * + * Creates a filter work request to delete the supplied filter. If @qid is + * negative the delete notification is suppressed. + */ +static void t4_mk_filtdelwr(unsigned int ftid, struct fw_filter_wr *wr, int qid) +{ + memset(wr, 0, sizeof(*wr)); + wr->op_pkd = cpu_to_be32(V_FW_WR_OP(FW_FILTER_WR)); + wr->len16_pkd = cpu_to_be32(V_FW_WR_LEN16(sizeof(*wr) / 16)); + wr->tid_to_iq = cpu_to_be32(V_FW_FILTER_WR_TID(ftid) | + V_FW_FILTER_WR_NOREPLY(qid < 0)); + wr->del_filter_to_l2tix = cpu_to_be32(F_FW_FILTER_WR_DEL_FILTER); + if (qid >= 0) + wr->rx_chan_rx_rpl_iq = + cpu_to_be16(V_FW_FILTER_WR_RX_RPL_IQ(qid)); +} + +/** + * Create FW work request to delete the filter at a specified index + */ +static int del_filter_wr(struct rte_eth_dev *dev, unsigned int fidx) +{ + struct adapter *adapter = ethdev2adap(dev); + struct filter_entry *f = &adapter->tids.ftid_tab[fidx]; + struct rte_mbuf *mbuf; + struct fw_filter_wr *fwr; + struct sge_ctrl_txq *ctrlq; + unsigned int port_id = ethdev2pinfo(dev)->port_id; + + ctrlq = &adapter->sge.ctrlq[port_id]; + mbuf = rte_pktmbuf_alloc(ctrlq->mb_pool); + if (!mbuf) + return -ENOMEM; + + mbuf->data_len = sizeof(*fwr); + mbuf->pkt_len = mbuf->data_len; + + fwr = rte_pktmbuf_mtod(mbuf, struct fw_filter_wr *); + t4_mk_filtdelwr(f->tid, fwr, adapter->sge.fw_evtq.abs_id); + + /* + * Mark the filter as "pending" and ship off the Filter Work Request. + * When we get the Work Request Reply we'll clear the pending status. + */ + f->pending = 1; + t4_mgmt_tx(ctrlq, mbuf); + return 0; +} + int set_filter_wr(struct rte_eth_dev *dev, unsigned int fidx) { struct adapter *adapter = ethdev2adap(dev); @@ -244,6 +298,58 @@ static void cxgbe_clear_ftid(struct tid_info *t, int fidx, int family) t4_os_unlock(&t->ftid_lock); } +/** + * Check a delete filter request for validity and send it to the hardware. + * Return 0 on success, an error number otherwise. We attach any provided + * filter operation context to the internal filter specification in order to + * facilitate signaling completion of the operation. + */ +int cxgbe_del_filter(struct rte_eth_dev *dev, unsigned int filter_id, + struct ch_filter_specification *fs, + struct filter_ctx *ctx) +{ + struct port_info *pi = (struct port_info *)(dev->data->dev_private); + struct adapter *adapter = pi->adapter; + struct filter_entry *f; + int ret; + + if (filter_id >= adapter->tids.nftids) + return -ERANGE; + + ret = is_filter_set(&adapter->tids, filter_id, fs->type); + if (!ret) { + dev_warn(adap, "%s: could not find filter entry: %u\n", + __func__, filter_id); + return -EINVAL; + } + + f = &adapter->tids.ftid_tab[filter_id]; + ret = writable_filter(f); + if (ret) + return ret; + + if (f->valid) { + f->ctx = ctx; + cxgbe_clear_ftid(&adapter->tids, + f->tid - adapter->tids.ftid_base, + f->fs.type ? FILTER_TYPE_IPV6 : + FILTER_TYPE_IPV4); + return del_filter_wr(dev, filter_id); + } + + /* + * If the caller has passed in a Completion Context then we need to + * mark it as a successful completion so they don't stall waiting + * for it. + */ + if (ctx) { + ctx->result = 0; + t4_complete(&ctx->completion); + } + + return 0; +} + /** * Check a Chelsio Filter Request for validity, convert it into our internal * format and send it to the hardware. Return 0 on success, an error number @@ -415,6 +521,14 @@ void filter_rpl(struct adapter *adap, const struct cpl_set_tcb_rpl *rpl) ctx->tid = f->tid; ctx->result = 0; } + } else if (ret == FW_FILTER_WR_FLT_DELETED) { + /* + * Clear the filter when we get confirmation from the + * hardware that the filter has been deleted. + */ + clear_filter(f); + if (ctx) + ctx->result = 0; } else { /* * Something went wrong. Issue a warning about the diff --git a/drivers/net/cxgbe/cxgbe_filter.h b/drivers/net/cxgbe/cxgbe_filter.h index e12baa7f9..e0ba6a4d3 100644 --- a/drivers/net/cxgbe/cxgbe_filter.h +++ b/drivers/net/cxgbe/cxgbe_filter.h @@ -215,6 +215,9 @@ int writable_filter(struct filter_entry *f); int cxgbe_set_filter(struct rte_eth_dev *dev, unsigned int filter_id, struct ch_filter_specification *fs, struct filter_ctx *ctx); +int cxgbe_del_filter(struct rte_eth_dev *dev, unsigned int filter_id, + struct ch_filter_specification *fs, + struct filter_ctx *ctx); int cxgbe_alloc_ftid(struct adapter *adap, unsigned int family); int validate_filter(struct adapter *adap, struct ch_filter_specification *fs); #endif /* _CXGBE_FILTER_H_ */ diff --git a/drivers/net/cxgbe/cxgbe_flow.c b/drivers/net/cxgbe/cxgbe_flow.c index 7fa3f5810..1584df392 100644 --- a/drivers/net/cxgbe/cxgbe_flow.c +++ b/drivers/net/cxgbe/cxgbe_flow.c @@ -471,6 +471,57 @@ cxgbe_flow_create(struct rte_eth_dev *dev, return flow; } +static int __cxgbe_flow_destroy(struct rte_eth_dev *dev, struct rte_flow *flow) +{ + struct adapter *adap = ethdev2adap(dev); + struct filter_entry *f = flow->f; + struct ch_filter_specification *fs; + struct filter_ctx ctx; + int err; + + fs = &f->fs; + if (cxgbe_verify_fidx(flow, flow->fidx, 1)) + return -1; + + t4_init_completion(&ctx.completion); + err = cxgbe_del_filter(dev, flow->fidx, fs, &ctx); + if (err) { + dev_err(adap, "Error %d while deleting filter.\n", err); + return err; + } + + /* Poll the FW for reply */ + err = cxgbe_poll_for_completion(&adap->sge.fw_evtq, + CXGBE_FLOW_POLL_US, + CXGBE_FLOW_POLL_CNT, + &ctx.completion); + if (err) { + dev_err(adap, "Filter delete operation timed out (%d)\n", err); + return err; + } + if (ctx.result) { + dev_err(adap, "Hardware error %d while deleting the filter.\n", + ctx.result); + return ctx.result; + } + + return 0; +} + +static int +cxgbe_flow_destroy(struct rte_eth_dev *dev, struct rte_flow *flow, + struct rte_flow_error *e) +{ + int ret; + + ret = __cxgbe_flow_destroy(dev, flow); + if (ret) + return rte_flow_error_set(e, ret, RTE_FLOW_ERROR_TYPE_HANDLE, + flow, "error destroying filter."); + t4_os_free(flow); + return 0; +} + static int cxgbe_flow_validate(struct rte_eth_dev *dev, const struct rte_flow_attr *attr, @@ -524,7 +575,7 @@ cxgbe_flow_validate(struct rte_eth_dev *dev, static const struct rte_flow_ops cxgbe_flow_ops = { .validate = cxgbe_flow_validate, .create = cxgbe_flow_create, - .destroy = NULL, + .destroy = cxgbe_flow_destroy, .flush = NULL, .query = NULL, .isolate = NULL, From patchwork Fri Jun 8 17:58:16 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rahul Lakkireddy X-Patchwork-Id: 40931 X-Patchwork-Delegate: ferruh.yigit@amd.com Return-Path: X-Original-To: patchwork@dpdk.org Delivered-To: patchwork@dpdk.org Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 60F0C1D013; Fri, 8 Jun 2018 19:59:38 +0200 (CEST) Received: from stargate.chelsio.com (stargate.chelsio.com [12.32.117.8]) by dpdk.org (Postfix) with ESMTP id 7840E1BB89 for ; Fri, 8 Jun 2018 19:59:36 +0200 (CEST) Received: from localhost (scalar.blr.asicdesigners.com [10.193.185.94]) by stargate.chelsio.com (8.13.8/8.13.8) with ESMTP id w58HxXwU017303; Fri, 8 Jun 2018 10:59:34 -0700 From: Rahul Lakkireddy To: dev@dpdk.org Cc: shaguna@chelsio.com, kumaras@chelsio.com, indranil@chelsio.com, nirranjan@chelsio.com Date: Fri, 8 Jun 2018 23:28:16 +0530 Message-Id: X-Mailer: git-send-email 2.5.3 In-Reply-To: References: In-Reply-To: References: Subject: [dpdk-dev] [PATCH 6/7] net/cxgbe: implement flow query operation X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" From: Shagun Agrawal Add API to query filter hit and byte counts from hardware. Signed-off-by: Shagun Agrawal Signed-off-by: Kumar Sanghvi Signed-off-by: Rahul Lakkireddy --- drivers/net/cxgbe/base/adapter.h | 1 + drivers/net/cxgbe/base/common.h | 15 +++ drivers/net/cxgbe/base/t4_hw.c | 209 +++++++++++++++++++++++++++++++++++++++ drivers/net/cxgbe/base/t4_hw.h | 4 + drivers/net/cxgbe/base/t4_regs.h | 16 +++ drivers/net/cxgbe/cxgbe_filter.c | 62 ++++++++++++ drivers/net/cxgbe/cxgbe_filter.h | 2 + drivers/net/cxgbe/cxgbe_flow.c | 62 +++++++++++- drivers/net/cxgbe/cxgbe_main.c | 1 + 9 files changed, 371 insertions(+), 1 deletion(-) diff --git a/drivers/net/cxgbe/base/adapter.h b/drivers/net/cxgbe/base/adapter.h index 7f9ddae01..de46ecfe3 100644 --- a/drivers/net/cxgbe/base/adapter.h +++ b/drivers/net/cxgbe/base/adapter.h @@ -319,6 +319,7 @@ struct adapter { unsigned int vpd_flag; int use_unpacked_mode; /* unpacked rx mode state */ + rte_spinlock_t win0_lock; struct tid_info tids; /* Info used to access TID related tables */ }; diff --git a/drivers/net/cxgbe/base/common.h b/drivers/net/cxgbe/base/common.h index c80304b24..e524f7931 100644 --- a/drivers/net/cxgbe/base/common.h +++ b/drivers/net/cxgbe/base/common.h @@ -18,6 +18,9 @@ extern "C" { #define CXGBE_PAGE_SIZE RTE_PGSIZE_4K +#define T4_MEMORY_WRITE 0 +#define T4_MEMORY_READ 1 + enum { MAX_NPORTS = 4, /* max # of ports */ }; @@ -47,6 +50,8 @@ enum cc_fec { FEC_BASER_RS = 1 << 2, /* BaseR/Reed-Solomon */ }; +enum { MEM_EDC0, MEM_EDC1, MEM_MC, MEM_MC0 = MEM_MC, MEM_MC1 }; + struct port_stats { u64 tx_octets; /* total # of octets in good frames */ u64 tx_frames; /* all good frames */ @@ -502,5 +507,15 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size); int t4_seeprom_read(struct adapter *adapter, u32 addr, u32 *data); int t4_seeprom_write(struct adapter *adapter, u32 addr, u32 data); int t4_seeprom_wp(struct adapter *adapter, int enable); +int t4_memory_rw_addr(struct adapter *adap, int win, + u32 addr, u32 len, void *hbuf, int dir); +int t4_memory_rw_mtype(struct adapter *adap, int win, int mtype, u32 maddr, + u32 len, void *hbuf, int dir); +static inline int t4_memory_rw(struct adapter *adap, int win, + int mtype, u32 maddr, u32 len, + void *hbuf, int dir) +{ + return t4_memory_rw_mtype(adap, win, mtype, maddr, len, hbuf, dir); +} fw_port_cap32_t fwcaps16_to_caps32(fw_port_cap16_t caps16); #endif /* __CHELSIO_COMMON_H */ diff --git a/drivers/net/cxgbe/base/t4_hw.c b/drivers/net/cxgbe/base/t4_hw.c index c146c911e..66d080476 100644 --- a/drivers/net/cxgbe/base/t4_hw.c +++ b/drivers/net/cxgbe/base/t4_hw.c @@ -5215,3 +5215,212 @@ int t4_port_init(struct adapter *adap, int mbox, int pf, int vf) } return 0; } + +/** + * t4_memory_rw_addr - read/write adapter memory via PCIE memory window + * @adap: the adapter + * @win: PCI-E Memory Window to use + * @addr: address within adapter memory + * @len: amount of memory to transfer + * @hbuf: host memory buffer + * @dir: direction of transfer T4_MEMORY_READ (1) or T4_MEMORY_WRITE (0) + * + * Reads/writes an [almost] arbitrary memory region in the firmware: the + * firmware memory address and host buffer must be aligned on 32-bit + * boudaries; the length may be arbitrary. + * + * NOTES: + * 1. The memory is transferred as a raw byte sequence from/to the + * firmware's memory. If this memory contains data structures which + * contain multi-byte integers, it's the caller's responsibility to + * perform appropriate byte order conversions. + * + * 2. It is the Caller's responsibility to ensure that no other code + * uses the specified PCI-E Memory Window while this routine is + * using it. This is typically done via the use of OS-specific + * locks, etc. + */ +int t4_memory_rw_addr(struct adapter *adap, int win, u32 addr, + u32 len, void *hbuf, int dir) +{ + u32 pos, offset, resid; + u32 win_pf, mem_reg, mem_aperture, mem_base; + u32 *buf; + + /* Argument sanity checks ...*/ + if (addr & 0x3 || (uintptr_t)hbuf & 0x3) + return -EINVAL; + buf = (u32 *)hbuf; + + /* It's convenient to be able to handle lengths which aren't a + * multiple of 32-bits because we often end up transferring files to + * the firmware. So we'll handle that by normalizing the length here + * and then handling any residual transfer at the end. + */ + resid = len & 0x3; + len -= resid; + + /* Each PCI-E Memory Window is programmed with a window size -- or + * "aperture" -- which controls the granularity of its mapping onto + * adapter memory. We need to grab that aperture in order to know + * how to use the specified window. The window is also programmed + * with the base address of the Memory Window in BAR0's address + * space. For T4 this is an absolute PCI-E Bus Address. For T5 + * the address is relative to BAR0. + */ + mem_reg = t4_read_reg(adap, + PCIE_MEM_ACCESS_REG(A_PCIE_MEM_ACCESS_BASE_WIN, + win)); + mem_aperture = 1 << (G_WINDOW(mem_reg) + X_WINDOW_SHIFT); + mem_base = G_PCIEOFST(mem_reg) << X_PCIEOFST_SHIFT; + + win_pf = is_t4(adap->params.chip) ? 0 : V_PFNUM(adap->pf); + + /* Calculate our initial PCI-E Memory Window Position and Offset into + * that Window. + */ + pos = addr & ~(mem_aperture - 1); + offset = addr - pos; + + /* Set up initial PCI-E Memory Window to cover the start of our + * transfer. (Read it back to ensure that changes propagate before we + * attempt to use the new value.) + */ + t4_write_reg(adap, + PCIE_MEM_ACCESS_REG(A_PCIE_MEM_ACCESS_OFFSET, win), + pos | win_pf); + t4_read_reg(adap, + PCIE_MEM_ACCESS_REG(A_PCIE_MEM_ACCESS_OFFSET, win)); + + /* Transfer data to/from the adapter as long as there's an integral + * number of 32-bit transfers to complete. + * + * A note on Endianness issues: + * + * The "register" reads and writes below from/to the PCI-E Memory + * Window invoke the standard adapter Big-Endian to PCI-E Link + * Little-Endian "swizzel." As a result, if we have the following + * data in adapter memory: + * + * Memory: ... | b0 | b1 | b2 | b3 | ... + * Address: i+0 i+1 i+2 i+3 + * + * Then a read of the adapter memory via the PCI-E Memory Window + * will yield: + * + * x = readl(i) + * 31 0 + * [ b3 | b2 | b1 | b0 ] + * + * If this value is stored into local memory on a Little-Endian system + * it will show up correctly in local memory as: + * + * ( ..., b0, b1, b2, b3, ... ) + * + * But on a Big-Endian system, the store will show up in memory + * incorrectly swizzled as: + * + * ( ..., b3, b2, b1, b0, ... ) + * + * So we need to account for this in the reads and writes to the + * PCI-E Memory Window below by undoing the register read/write + * swizzels. + */ + while (len > 0) { + if (dir == T4_MEMORY_READ) + *buf++ = le32_to_cpu((__le32)t4_read_reg(adap, + mem_base + + offset)); + else + t4_write_reg(adap, mem_base + offset, + (u32)cpu_to_le32(*buf++)); + offset += sizeof(__be32); + len -= sizeof(__be32); + + /* If we've reached the end of our current window aperture, + * move the PCI-E Memory Window on to the next. Note that + * doing this here after "len" may be 0 allows us to set up + * the PCI-E Memory Window for a possible final residual + * transfer below ... + */ + if (offset == mem_aperture) { + pos += mem_aperture; + offset = 0; + t4_write_reg(adap, + PCIE_MEM_ACCESS_REG(A_PCIE_MEM_ACCESS_OFFSET, + win), pos | win_pf); + t4_read_reg(adap, + PCIE_MEM_ACCESS_REG(A_PCIE_MEM_ACCESS_OFFSET, + win)); + } + } + + /* If the original transfer had a length which wasn't a multiple of + * 32-bits, now's where we need to finish off the transfer of the + * residual amount. The PCI-E Memory Window has already been moved + * above (if necessary) to cover this final transfer. + */ + if (resid) { + union { + u32 word; + char byte[4]; + } last; + unsigned char *bp; + int i; + + if (dir == T4_MEMORY_READ) { + last.word = le32_to_cpu((__le32)t4_read_reg(adap, + mem_base + + offset)); + for (bp = (unsigned char *)buf, i = resid; i < 4; i++) + bp[i] = last.byte[i]; + } else { + last.word = *buf; + for (i = resid; i < 4; i++) + last.byte[i] = 0; + t4_write_reg(adap, mem_base + offset, + (u32)cpu_to_le32(last.word)); + } + } + + return 0; +} + +/** + * t4_memory_rw_mtype -read/write EDC 0, EDC 1 or MC via PCIE memory window + * @adap: the adapter + * @win: PCI-E Memory Window to use + * @mtype: memory type: MEM_EDC0, MEM_EDC1 or MEM_MC + * @maddr: address within indicated memory type + * @len: amount of memory to transfer + * @hbuf: host memory buffer + * @dir: direction of transfer T4_MEMORY_READ (1) or T4_MEMORY_WRITE (0) + * + * Reads/writes adapter memory using t4_memory_rw_addr(). This routine + * provides an (memory type, address within memory type) interface. + */ +int t4_memory_rw_mtype(struct adapter *adap, int win, int mtype, u32 maddr, + u32 len, void *hbuf, int dir) +{ + u32 mtype_offset; + u32 edc_size, mc_size; + + /* Offset into the region of memory which is being accessed + * MEM_EDC0 = 0 + * MEM_EDC1 = 1 + * MEM_MC = 2 -- MEM_MC for chips with only 1 memory controller + * MEM_MC1 = 3 -- for chips with 2 memory controllers (e.g. T5) + */ + edc_size = G_EDRAM0_SIZE(t4_read_reg(adap, A_MA_EDRAM0_BAR)); + if (mtype != MEM_MC1) { + mtype_offset = (mtype * (edc_size * 1024 * 1024)); + } else { + mc_size = G_EXT_MEM0_SIZE(t4_read_reg(adap, + A_MA_EXT_MEMORY0_BAR)); + mtype_offset = (MEM_MC0 * edc_size + mc_size) * 1024 * 1024; + } + + return t4_memory_rw_addr(adap, win, + mtype_offset + maddr, len, + hbuf, dir); +} diff --git a/drivers/net/cxgbe/base/t4_hw.h b/drivers/net/cxgbe/base/t4_hw.h index ac12afc04..e77563dfa 100644 --- a/drivers/net/cxgbe/base/t4_hw.h +++ b/drivers/net/cxgbe/base/t4_hw.h @@ -42,6 +42,10 @@ enum { SGE_MAX_WR_NDESC = SGE_MAX_WR_LEN / SGE_EQ_IDXSIZE, }; +enum { + TCB_SIZE = 128, /* TCB size */ +}; + struct sge_qstat { /* data written to SGE queue status entries */ __be32 qid; __be16 cidx; diff --git a/drivers/net/cxgbe/base/t4_regs.h b/drivers/net/cxgbe/base/t4_regs.h index c0d6ddcac..fd8f9cf27 100644 --- a/drivers/net/cxgbe/base/t4_regs.h +++ b/drivers/net/cxgbe/base/t4_regs.h @@ -458,6 +458,7 @@ #define F_CRXPKTENC V_CRXPKTENC(1U) #define TP_BASE_ADDR 0x7d00 +#define A_TP_CMM_TCB_BASE 0x7d10 #define A_TP_TIMER_RESOLUTION 0x7d90 @@ -574,6 +575,21 @@ #define S_RM_OVLAN 9 #define V_RM_OVLAN(x) ((x) << S_RM_OVLAN) +/* registers for module MA */ +#define A_MA_EDRAM0_BAR 0x77c0 + +#define S_EDRAM0_SIZE 0 +#define M_EDRAM0_SIZE 0xfffU +#define V_EDRAM0_SIZE(x) ((x) << S_EDRAM0_SIZE) +#define G_EDRAM0_SIZE(x) (((x) >> S_EDRAM0_SIZE) & M_EDRAM0_SIZE) + +#define A_MA_EXT_MEMORY0_BAR 0x77c8 + +#define S_EXT_MEM0_SIZE 0 +#define M_EXT_MEM0_SIZE 0xfffU +#define V_EXT_MEM0_SIZE(x) ((x) << S_EXT_MEM0_SIZE) +#define G_EXT_MEM0_SIZE(x) (((x) >> S_EXT_MEM0_SIZE) & M_EXT_MEM0_SIZE) + /* registers for module MPS */ #define MPS_BASE_ADDR 0x9000 #define T4VF_MPS_BASE_ADDR 0x0100 diff --git a/drivers/net/cxgbe/cxgbe_filter.c b/drivers/net/cxgbe/cxgbe_filter.c index 8129ed01f..2fc580e61 100644 --- a/drivers/net/cxgbe/cxgbe_filter.c +++ b/drivers/net/cxgbe/cxgbe_filter.c @@ -545,3 +545,65 @@ void filter_rpl(struct adapter *adap, const struct cpl_set_tcb_rpl *rpl) t4_complete(&ctx->completion); } } + +/* + * Retrieve the packet count for the specified filter. + */ +int cxgbe_get_filter_count(struct adapter *adapter, unsigned int fidx, + u64 *c, bool get_byte) +{ + struct filter_entry *f; + unsigned int tcb_base, tcbaddr; + int ret; + + tcb_base = t4_read_reg(adapter, A_TP_CMM_TCB_BASE); + if (fidx >= adapter->tids.nftids) + return -ERANGE; + + f = &adapter->tids.ftid_tab[fidx]; + if (!f->valid) + return -EINVAL; + + tcbaddr = tcb_base + f->tid * TCB_SIZE; + + if (is_t5(adapter->params.chip) || is_t6(adapter->params.chip)) { + /* + * For T5, the Filter Packet Hit Count is maintained as a + * 32-bit Big Endian value in the TCB field {timestamp}. + * Similar to the craziness above, instead of the filter hit + * count showing up at offset 20 ((W_TCB_TIMESTAMP == 5) * + * sizeof(u32)), it actually shows up at offset 24. Whacky. + */ + if (get_byte) { + unsigned int word_offset = 4; + __be64 be64_byte_count; + + t4_os_lock(&adapter->win0_lock); + ret = t4_memory_rw(adapter, MEMWIN_NIC, MEM_EDC0, + tcbaddr + + (word_offset * sizeof(__be32)), + sizeof(be64_byte_count), + &be64_byte_count, + T4_MEMORY_READ); + t4_os_unlock(&adapter->win0_lock); + if (ret < 0) + return ret; + *c = be64_to_cpu(be64_byte_count); + } else { + unsigned int word_offset = 6; + __be32 be32_count; + + t4_os_lock(&adapter->win0_lock); + ret = t4_memory_rw(adapter, MEMWIN_NIC, MEM_EDC0, + tcbaddr + + (word_offset * sizeof(__be32)), + sizeof(be32_count), &be32_count, + T4_MEMORY_READ); + t4_os_unlock(&adapter->win0_lock); + if (ret < 0) + return ret; + *c = (u64)be32_to_cpu(be32_count); + } + } + return 0; +} diff --git a/drivers/net/cxgbe/cxgbe_filter.h b/drivers/net/cxgbe/cxgbe_filter.h index e0ba6a4d3..3c81c1a64 100644 --- a/drivers/net/cxgbe/cxgbe_filter.h +++ b/drivers/net/cxgbe/cxgbe_filter.h @@ -220,4 +220,6 @@ int cxgbe_del_filter(struct rte_eth_dev *dev, unsigned int filter_id, struct filter_ctx *ctx); int cxgbe_alloc_ftid(struct adapter *adap, unsigned int family); int validate_filter(struct adapter *adap, struct ch_filter_specification *fs); +int cxgbe_get_filter_count(struct adapter *adapter, unsigned int fidx, + u64 *c, bool get_byte); #endif /* _CXGBE_FILTER_H_ */ diff --git a/drivers/net/cxgbe/cxgbe_flow.c b/drivers/net/cxgbe/cxgbe_flow.c index 1584df392..89490ecc2 100644 --- a/drivers/net/cxgbe/cxgbe_flow.c +++ b/drivers/net/cxgbe/cxgbe_flow.c @@ -522,6 +522,66 @@ cxgbe_flow_destroy(struct rte_eth_dev *dev, struct rte_flow *flow, return 0; } +static int __cxgbe_flow_query(struct rte_flow *flow, u64 *count, + u64 *byte_count) +{ + struct adapter *adap = ethdev2adap(flow->dev); + unsigned int fidx = flow->fidx; + int ret = 0; + + ret = cxgbe_get_filter_count(adap, fidx, count, 0); + if (ret) + return ret; + return cxgbe_get_filter_count(adap, fidx, byte_count, 1); +} + +static int +cxgbe_flow_query(struct rte_eth_dev *dev, struct rte_flow *flow, + const struct rte_flow_action *action, void *data, + struct rte_flow_error *e) +{ + struct ch_filter_specification fs; + struct rte_flow_query_count *c; + struct filter_entry *f; + int ret; + + RTE_SET_USED(dev); + + f = flow->f; + fs = f->fs; + + if (action->type != RTE_FLOW_ACTION_TYPE_COUNT) + return rte_flow_error_set(e, ENOTSUP, + RTE_FLOW_ERROR_TYPE_ACTION, NULL, + "only count supported for query"); + + /* + * This is a valid operation, Since we are allowed to do chelsio + * specific operations in rte side of our code but not vise-versa + * + * So, fs can be queried/modified here BUT rte_flow_query_count + * cannot be worked on by the lower layer since we want to maintain + * it as rte_flow agnostic. + */ + if (!fs.hitcnts) + return rte_flow_error_set(e, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION, + &fs, "filter hit counters were not" + " enabled during filter creation"); + + c = (struct rte_flow_query_count *)data; + ret = __cxgbe_flow_query(flow, &c->hits, &c->bytes); + if (ret) + return rte_flow_error_set(e, -ret, RTE_FLOW_ERROR_TYPE_ACTION, + f, "cxgbe pmd failed to" + " perform query"); + + /* Query was successful */ + c->bytes_set = 1; + c->hits_set = 1; + + return 0; /* success / partial_success */ +} + static int cxgbe_flow_validate(struct rte_eth_dev *dev, const struct rte_flow_attr *attr, @@ -577,7 +637,7 @@ static const struct rte_flow_ops cxgbe_flow_ops = { .create = cxgbe_flow_create, .destroy = cxgbe_flow_destroy, .flush = NULL, - .query = NULL, + .query = cxgbe_flow_query, .isolate = NULL, }; diff --git a/drivers/net/cxgbe/cxgbe_main.c b/drivers/net/cxgbe/cxgbe_main.c index a00e0700d..21ad380ae 100644 --- a/drivers/net/cxgbe/cxgbe_main.c +++ b/drivers/net/cxgbe/cxgbe_main.c @@ -1527,6 +1527,7 @@ int cxgbe_probe(struct adapter *adapter) t4_os_lock_init(&adapter->mbox_lock); TAILQ_INIT(&adapter->mbox_list); + t4_os_lock_init(&adapter->win0_lock); err = t4_prep_adapter(adapter); if (err) From patchwork Fri Jun 8 17:58:17 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rahul Lakkireddy X-Patchwork-Id: 40932 X-Patchwork-Delegate: ferruh.yigit@amd.com Return-Path: X-Original-To: patchwork@dpdk.org Delivered-To: patchwork@dpdk.org Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id B60E71D003; Fri, 8 Jun 2018 19:59:40 +0200 (CEST) Received: from stargate.chelsio.com (stargate.chelsio.com [12.32.117.8]) by dpdk.org (Postfix) with ESMTP id 2AF5D1D010 for ; Fri, 8 Jun 2018 19:59:39 +0200 (CEST) Received: from localhost (scalar.blr.asicdesigners.com [10.193.185.94]) by stargate.chelsio.com (8.13.8/8.13.8) with ESMTP id w58HxavR017306; Fri, 8 Jun 2018 10:59:36 -0700 From: Rahul Lakkireddy To: dev@dpdk.org Cc: shaguna@chelsio.com, kumaras@chelsio.com, indranil@chelsio.com, nirranjan@chelsio.com Date: Fri, 8 Jun 2018 23:28:17 +0530 Message-Id: <93202e988fd013bb7410ea1226c11c73b86a556a.1528469677.git.rahul.lakkireddy@chelsio.com> X-Mailer: git-send-email 2.5.3 In-Reply-To: References: In-Reply-To: References: Subject: [dpdk-dev] [PATCH 7/7] net/cxgbe: implement flow flush operation X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" From: Shagun Agrawal Add API to flush all the filters under specified port. Signed-off-by: Shagun Agrawal Signed-off-by: Kumar Sanghvi Signed-off-by: Rahul Lakkireddy --- drivers/net/cxgbe/cxgbe_filter.h | 1 + drivers/net/cxgbe/cxgbe_flow.c | 40 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/drivers/net/cxgbe/cxgbe_filter.h b/drivers/net/cxgbe/cxgbe_filter.h index 3c81c1a64..70443df4c 100644 --- a/drivers/net/cxgbe/cxgbe_filter.h +++ b/drivers/net/cxgbe/cxgbe_filter.h @@ -141,6 +141,7 @@ struct filter_entry { u32 pending:1; /* filter action is pending FW reply */ struct filter_ctx *ctx; /* caller's completion hook */ struct rte_eth_dev *dev; /* Port's rte eth device */ + void *private; /* For use by apps using filter_entry */ /* This will store the actual tid */ u32 tid; diff --git a/drivers/net/cxgbe/cxgbe_flow.c b/drivers/net/cxgbe/cxgbe_flow.c index 89490ecc2..061947dae 100644 --- a/drivers/net/cxgbe/cxgbe_flow.c +++ b/drivers/net/cxgbe/cxgbe_flow.c @@ -468,6 +468,8 @@ cxgbe_flow_create(struct rte_eth_dev *dev, return NULL; } + flow->f->private = flow; /* Will be used during flush */ + return flow; } @@ -632,11 +634,47 @@ cxgbe_flow_validate(struct rte_eth_dev *dev, return 0; } +/* + * @ret : > 0 filter destroyed succsesfully + * < 0 error destroying filter + * == 1 filter not active / not found + */ +static int +cxgbe_check_n_destroy(struct filter_entry *f, struct rte_eth_dev *dev, + struct rte_flow_error *e) +{ + if (f && (f->valid || f->pending) && + f->dev == dev && /* Only if user has asked for this port */ + f->private) /* We (rte_flow) created this filter */ + return cxgbe_flow_destroy(dev, (struct rte_flow *)f->private, + e); + return 1; +} + +static int cxgbe_flow_flush(struct rte_eth_dev *dev, struct rte_flow_error *e) +{ + struct adapter *adap = ethdev2adap(dev); + unsigned int i; + int ret = 0; + + if (adap->tids.ftid_tab) { + struct filter_entry *f = &adap->tids.ftid_tab[0]; + + for (i = 0; i < adap->tids.nftids; i++, f++) { + ret = cxgbe_check_n_destroy(f, dev, e); + if (ret < 0) + goto out; + } + } +out: + return ret >= 0 ? 0 : ret; +} + static const struct rte_flow_ops cxgbe_flow_ops = { .validate = cxgbe_flow_validate, .create = cxgbe_flow_create, .destroy = cxgbe_flow_destroy, - .flush = NULL, + .flush = cxgbe_flow_flush, .query = cxgbe_flow_query, .isolate = NULL, };