[PATCH 09/10] cxgbe: add HASH filtering support
From: Rahul Lakkireddy <hidden>
Date: 2016-02-03 08:33:07
Subsystem:
networking drivers, the rest · Maintainers:
Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
Add support for setting HASH (Maskless) filters. Both IPv4 and IPv6 occupy only one index per filter. Also, the index returned is a hash computed by the hardware based on value in the fields being matched. During matching, the hardware computes a hash of the relevant fields in the incoming packet and compares it against the hashed indices. If a match is found, then the filter's action is taken immediately. HASH filters have higher priority over LE-TCAM filters if a packet matches rules in both the HASH filter region and the LE-TCAM filter region. This can be changed by setting the prio bit in the filter specification. Signed-off-by: Rahul Lakkireddy <redacted> Signed-off-by: Kumar Sanghvi <redacted> --- drivers/net/cxgbe/base/adapter.h | 11 + drivers/net/cxgbe/base/common.h | 7 + drivers/net/cxgbe/base/t4_msg.h | 198 ++++++++ drivers/net/cxgbe/base/t4_regs.h | 9 + drivers/net/cxgbe/base/t4_regs_values.h | 25 + drivers/net/cxgbe/base/t4_tcb.h | 21 + drivers/net/cxgbe/base/t4fw_interface.h | 19 + drivers/net/cxgbe/cxgbe_compat.h | 12 + drivers/net/cxgbe/cxgbe_filter.c | 812 ++++++++++++++++++++++++++++++++ drivers/net/cxgbe/cxgbe_filter.h | 7 + drivers/net/cxgbe/cxgbe_main.c | 135 +++++- drivers/net/cxgbe/cxgbe_ofld.h | 26 + 12 files changed, 1280 insertions(+), 2 deletions(-)
diff --git a/drivers/net/cxgbe/base/adapter.h b/drivers/net/cxgbe/base/adapter.h
index a866993..a64571d 100644
--- a/drivers/net/cxgbe/base/adapter.h
+++ b/drivers/net/cxgbe/base/adapter.h@@ -629,6 +629,17 @@ static inline struct adapter *ethdev2adap(const struct rte_eth_dev *dev) return ethdev2pinfo(dev)->adapter; } +/** + * cxgbe_port_viid - get the VI id of a port + * @dev: the device for the port + * + * Return the VI id of the given port. + */ +static inline unsigned int cxgbe_port_viid(const struct rte_eth_dev *dev) +{ + return ethdev2pinfo(dev)->viid; +} + 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/common.h b/drivers/net/cxgbe/base/common.h
index 21dca32..bbeca75 100644
--- a/drivers/net/cxgbe/base/common.h
+++ b/drivers/net/cxgbe/base/common.h@@ -220,6 +220,8 @@ struct adapter_params { unsigned char nports; /* # of ethernet ports */ unsigned char portvec; + unsigned char hash_filter; + enum chip_type chip; /* chip code */ struct arch_specific_params arch; /* chip specific params */
@@ -255,6 +257,11 @@ static inline int t4_wait_op_done(struct adapter *adapter, int reg, u32 mask, #define for_each_port(adapter, iter) \ for (iter = 0; iter < (adapter)->params.nports; ++iter) +static inline int is_hashfilter(const struct adapter *adap) +{ + return adap->params.hash_filter; +} + void t4_read_mtu_tbl(struct adapter *adap, u16 *mtus, u8 *mtu_log); void t4_tp_wr_bits_indirect(struct adapter *adap, unsigned int addr, unsigned int mask, unsigned int val);
diff --git a/drivers/net/cxgbe/base/t4_msg.h b/drivers/net/cxgbe/base/t4_msg.h
index 57534f0..bffea8b 100644
--- a/drivers/net/cxgbe/base/t4_msg.h
+++ b/drivers/net/cxgbe/base/t4_msg.h@@ -35,12 +35,19 @@ #define T4_MSG_H enum { + CPL_ACT_OPEN_REQ = 0x3, CPL_SET_TCB_FIELD = 0x5, + CPL_ABORT_REQ = 0xA, + CPL_ABORT_RPL = 0xB, CPL_L2T_WRITE_REQ = 0x12, CPL_SMT_WRITE_REQ = 0x14, + CPL_TID_RELEASE = 0x1A, CPL_L2T_WRITE_RPL = 0x23, + CPL_ACT_OPEN_RPL = 0x25, + CPL_ABORT_RPL_RSS = 0x2D, CPL_SMT_WRITE_RPL = 0x2E, CPL_SET_TCB_RPL = 0x3A, + CPL_ACT_OPEN_REQ6 = 0x83, CPL_SGE_EGR_UPDATE = 0xA5, CPL_FW4_MSG = 0xC0, CPL_FW6_MSG = 0xE0,
@@ -53,6 +60,16 @@ enum CPL_error { CPL_ERR_TCAM_FULL = 3, }; +enum { + ULP_MODE_NONE = 0, + ULP_MODE_TCPDDP = 5, +}; + +enum { + CPL_ABORT_SEND_RST = 0, + CPL_ABORT_NO_RST, +}; + enum { /* TX_PKT_XT checksum types */ TX_CSUM_TCPIP = 8, TX_CSUM_UDPIP = 9,
@@ -127,6 +144,148 @@ struct work_request_hdr { #define WR_HDR_SIZE 0 #endif +/* option 0 fields */ +#define S_TX_CHAN 2 +#define V_TX_CHAN(x) ((x) << S_TX_CHAN) + +#define S_NO_CONG 4 +#define V_NO_CONG(x) ((x) << S_NO_CONG) + +#define S_DELACK 5 +#define V_DELACK(x) ((x) << S_DELACK) + +#define S_NON_OFFLOAD 7 +#define V_NON_OFFLOAD(x) ((x) << S_NON_OFFLOAD) +#define F_NON_OFFLOAD V_NON_OFFLOAD(1U) + +#define S_ULP_MODE 8 +#define V_ULP_MODE(x) ((x) << S_ULP_MODE) + +#define S_SMAC_SEL 28 +#define V_SMAC_SEL(x) ((__u64)(x) << S_SMAC_SEL) + +#define S_L2T_IDX 36 +#define V_L2T_IDX(x) ((__u64)(x) << S_L2T_IDX) + +#define S_TCAM_BYPASS 48 +#define V_TCAM_BYPASS(x) ((__u64)(x) << S_TCAM_BYPASS) +#define F_TCAM_BYPASS V_TCAM_BYPASS(1ULL) + +#define S_NAGLE 49 +#define V_NAGLE(x) ((__u64)(x) << S_NAGLE) + +/* option 2 fields */ +#define S_RSS_QUEUE 0 +#define V_RSS_QUEUE(x) ((x) << S_RSS_QUEUE) + +#define S_RSS_QUEUE_VALID 10 +#define V_RSS_QUEUE_VALID(x) ((x) << S_RSS_QUEUE_VALID) +#define F_RSS_QUEUE_VALID V_RSS_QUEUE_VALID(1U) + +#define S_CONG_CNTRL 14 +#define V_CONG_CNTRL(x) ((x) << S_CONG_CNTRL) + +#define S_PACE 16 +#define V_PACE(x) ((x) << S_PACE) + +#define S_RX_FC_DISABLE 20 +#define V_RX_FC_DISABLE(x) ((x) << S_RX_FC_DISABLE) + +#define S_TX_QUEUE 23 +#define V_TX_QUEUE(x) ((x) << S_TX_QUEUE) + +#define S_RX_CHANNEL 26 +#define V_RX_CHANNEL(x) ((x) << S_RX_CHANNEL) +#define F_RX_CHANNEL V_RX_CHANNEL(1U) + +#define S_CCTRL_ECN 27 +#define V_CCTRL_ECN(x) ((x) << S_CCTRL_ECN) + +#define S_WND_SCALE_EN 28 +#define V_WND_SCALE_EN(x) ((x) << S_WND_SCALE_EN) + +#define S_SACK_EN 30 +#define V_SACK_EN(x) ((x) << S_SACK_EN) + +#define S_T5_OPT_2_VALID 31 +#define V_T5_OPT_2_VALID(x) ((x) << S_T5_OPT_2_VALID) +#define F_T5_OPT_2_VALID V_T5_OPT_2_VALID(1U) + +struct cpl_act_open_req { + WR_HDR; + union opcode_tid ot; + __be16 local_port; + __be16 peer_port; + __be32 local_ip; + __be32 peer_ip; + __be64 opt0; + __be32 params; + __be32 opt2; +}; + +#define S_FILTER_TUPLE 24 +#define V_FILTER_TUPLE(x) ((x) << S_FILTER_TUPLE) + +struct cpl_t5_act_open_req { + WR_HDR; + union opcode_tid ot; + __be16 local_port; + __be16 peer_port; + __be32 local_ip; + __be32 peer_ip; + __be64 opt0; + __be32 rsvd; + __be32 opt2; + __be64 params; +}; + +/* cpl_{t5,t6}_act_open_req.params field */ +struct cpl_act_open_req6 { + WR_HDR; + union opcode_tid ot; + __be16 local_port; + __be16 peer_port; + __be64 local_ip_hi; + __be64 local_ip_lo; + __be64 peer_ip_hi; + __be64 peer_ip_lo; + __be64 opt0; + __be32 params; + __be32 opt2; +}; + +struct cpl_t5_act_open_req6 { + WR_HDR; + union opcode_tid ot; + __be16 local_port; + __be16 peer_port; + __be64 local_ip_hi; + __be64 local_ip_lo; + __be64 peer_ip_hi; + __be64 peer_ip_lo; + __be64 opt0; + __be32 rsvd; + __be32 opt2; + __be64 params; +}; + +struct cpl_act_open_rpl { + RSS_HDR + union opcode_tid ot; + __be32 atid_status; +}; + +/* cpl_act_open_rpl.atid_status fields */ +#define S_AOPEN_STATUS 0 +#define M_AOPEN_STATUS 0xFF +#define V_AOPEN_STATUS(x) ((x) << S_AOPEN_STATUS) +#define G_AOPEN_STATUS(x) (((x) >> S_AOPEN_STATUS) & M_AOPEN_STATUS) + +#define S_AOPEN_ATID 8 +#define M_AOPEN_ATID 0xFFFFFF +#define V_AOPEN_ATID(x) ((x) << S_AOPEN_ATID) +#define G_AOPEN_ATID(x) (((x) >> S_AOPEN_ATID) & M_AOPEN_ATID) + /* cpl_get_tcb.reply_ctrl fields */ #define S_QUEUENO 0 #define V_QUEUENO(x) ((x) << S_QUEUENO)
@@ -164,6 +323,39 @@ struct cpl_set_tcb_rpl { __be64 oldval; }; +/* cpl_abort_req status command code + */ +struct cpl_abort_req { + WR_HDR; + union opcode_tid ot; + __be32 rsvd0; + __u8 rsvd1; + __u8 cmd; + __u8 rsvd2[6]; +}; + +struct cpl_abort_rpl_rss { + RSS_HDR + union opcode_tid ot; + __u8 rsvd[3]; + __u8 status; +}; + +struct cpl_abort_rpl { + WR_HDR; + union opcode_tid ot; + __be32 rsvd0; + __u8 rsvd1; + __u8 cmd; + __u8 rsvd2[6]; +}; + +struct cpl_tid_release { + WR_HDR; + union opcode_tid ot; + __be32 rsvd; +}; + struct cpl_tx_data { union opcode_tid ot; __be32 len;
@@ -411,7 +603,13 @@ struct cpl_fw6_msg { __be64 data[4]; }; +/* ULP_TX opcodes */ +enum { + ULP_TX_PKT = 4 +}; + enum { + ULP_TX_SC_NOOP = 0x80, ULP_TX_SC_IMM = 0x81, ULP_TX_SC_DSGL = 0x82, ULP_TX_SC_ISGL = 0x83
diff --git a/drivers/net/cxgbe/base/t4_regs.h b/drivers/net/cxgbe/base/t4_regs.h
index 9057e40..1a7b6df 100644
--- a/drivers/net/cxgbe/base/t4_regs.h
+++ b/drivers/net/cxgbe/base/t4_regs.h@@ -793,3 +793,12 @@ #define M_REV 0xfU #define V_REV(x) ((x) << S_REV) #define G_REV(x) (((x) >> S_REV) & M_REV) + +/* registers for module LE */ +#define A_LE_DB_CONFIG 0x19c04 + +#define S_HASHEN 20 +#define V_HASHEN(x) ((x) << S_HASHEN) +#define F_HASHEN V_HASHEN(1U) + +#define A_LE_DB_TID_HASHBASE 0x19df8
diff --git a/drivers/net/cxgbe/base/t4_regs_values.h b/drivers/net/cxgbe/base/t4_regs_values.h
index d7d3144..8e1f6f3 100644
--- a/drivers/net/cxgbe/base/t4_regs_values.h
+++ b/drivers/net/cxgbe/base/t4_regs_values.h@@ -155,6 +155,9 @@ * selects for a particular field being present. These fields, when present * in the Compressed Filter Tuple, have the following widths in bits. */ +#define S_FT_FIRST S_FCOE +#define S_FT_LAST S_FRAGMENTATION + #define W_FT_FCOE 1 #define W_FT_PORT 3 #define W_FT_VNIC_ID 17
@@ -166,4 +169,26 @@ #define W_FT_MPSHITTYPE 3 #define W_FT_FRAGMENTATION 1 +/* + * Some of the Compressed Filter Tuple fields have internal structure. These + * bit shifts/masks describe those structures. All shifts are relative to the + * base position of the fields within the Compressed Filter Tuple + */ +#define S_FT_VLAN_VLD 16 +#define V_FT_VLAN_VLD(x) ((x) << S_FT_VLAN_VLD) +#define F_FT_VLAN_VLD V_FT_VLAN_VLD(1U) + +#define S_FT_VNID_ID_VF 0 +#define M_FT_VNID_ID_VF 0x7fU +#define V_FT_VNID_ID_VF(x) ((x) << S_FT_VNID_ID_VF) +#define G_FT_VNID_ID_VF(x) (((x) >> S_FT_VNID_ID_VF) & M_FT_VNID_ID_VF) + +#define S_FT_VNID_ID_PF 7 +#define M_FT_VNID_ID_PF 0x7U +#define V_FT_VNID_ID_PF(x) ((x) << S_FT_VNID_ID_PF) +#define G_FT_VNID_ID_PF(x) (((x) >> S_FT_VNID_ID_PF) & M_FT_VNID_ID_PF) + +#define S_FT_VNID_ID_VLD 16 +#define V_FT_VNID_ID_VLD(x) ((x) << S_FT_VNID_ID_VLD) +#define F_FT_VNID_ID_VLD(x) V_FT_VNID_ID_VLD(1U) #endif /* __T4_REGS_VALUES_H__ */
diff --git a/drivers/net/cxgbe/base/t4_tcb.h b/drivers/net/cxgbe/base/t4_tcb.h
index 36afd56..1a076f3 100644
--- a/drivers/net/cxgbe/base/t4_tcb.h
+++ b/drivers/net/cxgbe/base/t4_tcb.h@@ -60,6 +60,27 @@ #define M_TCB_T_RTT_TS_RECENT_AGE 0xffffffffULL #define V_TCB_T_RTT_TS_RECENT_AGE(x) ((x) << S_TCB_T_RTT_TS_RECENT_AGE) +/* 347:320 */ +#define W_TCB_SND_UNA_RAW 10 + +/* 553:522 */ +#define W_TCB_RCV_NXT 16 +#define S_TCB_RCV_NXT 10 +#define M_TCB_RCV_NXT 0xffffffffULL +#define V_TCB_RCV_NXT(x) ((__u64)(x) << S_TCB_RCV_NXT) + +/* 891:875 */ +#define W_TCB_RX_FRAG2_PTR_RAW 27 + +/* 964:937 */ +#define W_TCB_RX_FRAG3_LEN_RAW 29 + +/* 992:965 */ +#define W_TCB_RX_FRAG3_START_IDX_OFFSET_RAW 30 + +/* 1000:993 */ +#define W_TCB_PDU_HDR_LEN 31 + #define S_TF_MIGRATING 0 #define V_TF_MIGRATING(x) ((x) << S_TF_MIGRATING)
diff --git a/drivers/net/cxgbe/base/t4fw_interface.h b/drivers/net/cxgbe/base/t4fw_interface.h
index d3e4de5..d9278ff 100644
--- a/drivers/net/cxgbe/base/t4fw_interface.h
+++ b/drivers/net/cxgbe/base/t4fw_interface.h@@ -83,6 +83,7 @@ enum fw_memtype { enum fw_wr_opcodes { FW_FILTER_WR = 0x02, + FW_ULPTX_WR = 0x04, FW_TP_WR = 0x05, FW_ETH_TX_PKT_WR = 0x08, FW_ETH_TX_PKTS_WR = 0x09,
@@ -1009,6 +1010,24 @@ struct fw_eq_ctrl_cmd { #define S_FW_EQ_CTRL_CMD_EQSIZE 0 #define V_FW_EQ_CTRL_CMD_EQSIZE(x) ((x) << S_FW_EQ_CTRL_CMD_EQSIZE) +/* Macros for VIID parsing: + * VIID - [10:8] PFN, [7] VI Valid, [6:0] VI number + */ +#define S_FW_VIID_PFN 8 +#define M_FW_VIID_PFN 0x7 +#define V_FW_VIID_PFN(x) ((x) << S_FW_VIID_PFN) +#define G_FW_VIID_PFN(x) (((x) >> S_FW_VIID_PFN) & M_FW_VIID_PFN) + +#define S_FW_VIID_VIVLD 7 +#define M_FW_VIID_VIVLD 0x1 +#define V_FW_VIID_VIVLD(x) ((x) << S_FW_VIID_VIVLD) +#define G_FW_VIID_VIVLD(x) (((x) >> S_FW_VIID_VIVLD) & M_FW_VIID_VIVLD) + +#define S_FW_VIID_VIN 0 +#define M_FW_VIID_VIN 0x7F +#define V_FW_VIID_VIN(x) ((x) << S_FW_VIID_VIN) +#define G_FW_VIID_VIN(x) (((x) >> S_FW_VIID_VIN) & M_FW_VIID_VIN) + enum fw_vi_func { FW_VI_FUNC_ETH, };
diff --git a/drivers/net/cxgbe/cxgbe_compat.h b/drivers/net/cxgbe/cxgbe_compat.h
index e68f8f5..6649afc 100644
--- a/drivers/net/cxgbe/cxgbe_compat.h
+++ b/drivers/net/cxgbe/cxgbe_compat.h@@ -263,4 +263,16 @@ static inline void writeq(u64 val, volatile void __iomem *addr) writel(val >> 32, (void *)((uintptr_t)addr + 4)); } +/* + * Multiplies an integer by a fraction, while avoiding unnecessary + * overflow or loss of precision. + */ +#define mult_frac(x, numer, denom)( \ +{ \ + typeof(x) quot = (x) / (denom); \ + typeof(x) rem = (x) % (denom); \ + (quot * (numer)) + ((rem * (numer)) / (denom)); \ +} \ +) + #endif /* _CXGBE_COMPAT_H_ */
diff --git a/drivers/net/cxgbe/cxgbe_filter.c b/drivers/net/cxgbe/cxgbe_filter.c
index d4e32b1..285381b 100644
--- a/drivers/net/cxgbe/cxgbe_filter.c
+++ b/drivers/net/cxgbe/cxgbe_filter.c@@ -41,6 +41,44 @@ #include "cxgbe_filter.h" /** + * Initialize Hash Filters + */ +int init_hash_filter(struct adapter *adap) +{ + unsigned int n_user_filters; + unsigned int user_filter_perc; + int ret; + u32 params[7], val[7]; + +#define FW_PARAM_DEV(param) \ + (V_FW_PARAMS_MNEM(FW_PARAMS_MNEM_DEV) | \ + V_FW_PARAMS_PARAM_X(FW_PARAMS_PARAM_DEV_##param)) + +#define FW_PARAM_PFVF(param) \ + (V_FW_PARAMS_MNEM(FW_PARAMS_MNEM_PFVF) | \ + V_FW_PARAMS_PARAM_X(FW_PARAMS_PARAM_PFVF_##param) | \ + V_FW_PARAMS_PARAM_Y(0) | \ + V_FW_PARAMS_PARAM_Z(0)) + + params[0] = FW_PARAM_DEV(NTID); + ret = t4_query_params(adap, adap->mbox, adap->pf, 0, 1, + params, val); + if (ret < 0) + return ret; + adap->tids.ntids = val[0]; + adap->tids.natids = min(adap->tids.ntids / 2, MAX_ATIDS); + + user_filter_perc = 100; + n_user_filters = mult_frac(adap->tids.nftids, + user_filter_perc, + 100); + + adap->tids.nftids = n_user_filters; + adap->params.hash_filter = 1; + return 0; +} + +/** * Validate if the requested filter specification can be set by checking * if the requested features have been enabled */
@@ -190,6 +228,556 @@ static void set_tcb_tflag(struct adapter *adap, unsigned int ftid, } /** + * Build a CPL_SET_TCB_FIELD message as payload of a ULP_TX_PKT command. + */ +static inline void mk_set_tcb_field_ulp(struct filter_entry *f, + struct cpl_set_tcb_field *req, + unsigned int word, + u64 mask, u64 val, u8 cookie, + int no_reply) +{ + struct ulp_txpkt *txpkt = (struct ulp_txpkt *)req; + struct ulptx_idata *sc = (struct ulptx_idata *)(txpkt + 1); + + txpkt->cmd_dest = cpu_to_be32(V_ULPTX_CMD(ULP_TX_PKT) | + V_ULP_TXPKT_DEST(0)); + txpkt->len = cpu_to_be32(DIV_ROUND_UP(sizeof(*req), 16)); + sc->cmd_more = cpu_to_be32(V_ULPTX_CMD(ULP_TX_SC_IMM)); + sc->len = cpu_to_be32(sizeof(*req) - sizeof(struct work_request_hdr)); + OPCODE_TID(req) = cpu_to_be32(MK_OPCODE_TID(CPL_SET_TCB_FIELD, f->tid)); + req->reply_ctrl = cpu_to_be16(V_NO_REPLY(no_reply) | V_REPLY_CHAN(0) | + V_QUEUENO(0)); + req->word_cookie = cpu_to_be16(V_WORD(word) | V_COOKIE(cookie)); + req->mask = cpu_to_be64(mask); + req->val = cpu_to_be64(val); + sc = (struct ulptx_idata *)(req + 1); + sc->cmd_more = cpu_to_be32(V_ULPTX_CMD(ULP_TX_SC_NOOP)); + sc->len = cpu_to_be32(0); +} + +/** + * Set NAT parameters + */ +static void set_nat_params(struct adapter *adap, struct filter_entry *f, + unsigned int tid, bool dip, bool sip, + bool dp, bool sp) +{ + if (dip) { + if (f->fs.type) { + set_tcb_field(adap, tid, W_TCB_SND_UNA_RAW, + WORD_MASK, f->fs.nat_lip[15] | + f->fs.nat_lip[14] << 8 | + f->fs.nat_lip[13] << 16 | + f->fs.nat_lip[12] << 24, 1); + + set_tcb_field(adap, tid, W_TCB_SND_UNA_RAW + 1, + WORD_MASK, f->fs.nat_lip[11] | + f->fs.nat_lip[10] << 8 | + f->fs.nat_lip[9] << 16 | + f->fs.nat_lip[8] << 24, 1); + + set_tcb_field(adap, tid, W_TCB_SND_UNA_RAW + 2, + WORD_MASK, f->fs.nat_lip[7] | + f->fs.nat_lip[6] << 8 | + f->fs.nat_lip[5] << 16 | + f->fs.nat_lip[4] << 24, 1); + + set_tcb_field(adap, tid, W_TCB_SND_UNA_RAW + 3, + WORD_MASK, f->fs.nat_lip[3] | + f->fs.nat_lip[2] << 8 | + f->fs.nat_lip[1] << 16 | + f->fs.nat_lip[0] << 24, 1); + } else { + set_tcb_field(adap, tid, W_TCB_RX_FRAG3_LEN_RAW, + WORD_MASK, f->fs.nat_lip[3] | + f->fs.nat_lip[2] << 8 | + f->fs.nat_lip[1] << 16 | + f->fs.nat_lip[0] << 24, 1); + } + } + + if (sip) { + if (f->fs.type) { + set_tcb_field(adap, tid, W_TCB_RX_FRAG2_PTR_RAW, + WORD_MASK, f->fs.nat_fip[15] | + f->fs.nat_fip[14] << 8 | + f->fs.nat_fip[13] << 16 | + f->fs.nat_fip[12] << 24, 1); + + set_tcb_field(adap, tid, W_TCB_RX_FRAG2_PTR_RAW + 1, + WORD_MASK, f->fs.nat_fip[11] | + f->fs.nat_fip[10] << 8 | + f->fs.nat_fip[9] << 16 | + f->fs.nat_fip[8] << 24, 1); + + set_tcb_field(adap, tid, W_TCB_RX_FRAG2_PTR_RAW + 2, + WORD_MASK, f->fs.nat_fip[7] | + f->fs.nat_fip[6] << 8 | + f->fs.nat_fip[5] << 16 | + f->fs.nat_fip[4] << 24, 1); + + set_tcb_field(adap, tid, W_TCB_RX_FRAG2_PTR_RAW + 3, + WORD_MASK, f->fs.nat_fip[3] | + f->fs.nat_fip[2] << 8 | + f->fs.nat_fip[1] << 16 | + f->fs.nat_fip[0] << 24, 1); + + } else { + set_tcb_field(adap, tid, + W_TCB_RX_FRAG3_START_IDX_OFFSET_RAW, + WORD_MASK, f->fs.nat_fip[3] | + f->fs.nat_fip[2] << 8 | + f->fs.nat_fip[1] << 16 | + f->fs.nat_fip[0] << 24, 1); + } + } + + set_tcb_field(adap, tid, W_TCB_PDU_HDR_LEN, WORD_MASK, + (dp ? f->fs.nat_lport : 0) | + (sp ? f->fs.nat_fport << 16 : 0), 1); +} + +/** + * Build a CPL_ABORT_REQ message as payload of a ULP_TX_PKT command. + */ +static void mk_abort_req_ulp(struct cpl_abort_req *abort_req, + unsigned int tid) +{ + struct ulp_txpkt *txpkt = (struct ulp_txpkt *)abort_req; + struct ulptx_idata *sc = (struct ulptx_idata *)(txpkt + 1); + + txpkt->cmd_dest = cpu_to_be32(V_ULPTX_CMD(ULP_TX_PKT) | + V_ULP_TXPKT_DEST(0)); + txpkt->len = cpu_to_be32(DIV_ROUND_UP(sizeof(*abort_req), 16)); + sc->cmd_more = cpu_to_be32(V_ULPTX_CMD(ULP_TX_SC_IMM)); + sc->len = cpu_to_be32(sizeof(*abort_req) - + sizeof(struct work_request_hdr)); + OPCODE_TID(abort_req) = cpu_to_be32(MK_OPCODE_TID(CPL_ABORT_REQ, tid)); + abort_req->rsvd0 = cpu_to_be32(0); + abort_req->rsvd1 = 0; + abort_req->cmd = CPL_ABORT_NO_RST; + sc = (struct ulptx_idata *)(abort_req + 1); + sc->cmd_more = cpu_to_be32(V_ULPTX_CMD(ULP_TX_SC_NOOP)); + sc->len = cpu_to_be32(0); +} + +/** + * Build a CPL_ABORT_RPL message as payload of a ULP_TX_PKT command. + */ +static void mk_abort_rpl_ulp(struct cpl_abort_rpl *abort_rpl, + unsigned int tid) +{ + struct ulp_txpkt *txpkt = (struct ulp_txpkt *)abort_rpl; + struct ulptx_idata *sc = (struct ulptx_idata *)(txpkt + 1); + + txpkt->cmd_dest = cpu_to_be32(V_ULPTX_CMD(ULP_TX_PKT) | + V_ULP_TXPKT_DEST(0)); + txpkt->len = cpu_to_be32(DIV_ROUND_UP(sizeof(*abort_rpl), 16)); + sc->cmd_more = cpu_to_be32(V_ULPTX_CMD(ULP_TX_SC_IMM)); + sc->len = cpu_to_be32(sizeof(*abort_rpl) - + sizeof(struct work_request_hdr)); + OPCODE_TID(abort_rpl) = cpu_to_be32(MK_OPCODE_TID(CPL_ABORT_RPL, tid)); + abort_rpl->rsvd0 = cpu_to_be32(0); + abort_rpl->rsvd1 = 0; + abort_rpl->cmd = CPL_ABORT_NO_RST; + sc = (struct ulptx_idata *)(abort_rpl + 1); + sc->cmd_more = cpu_to_be32(V_ULPTX_CMD(ULP_TX_SC_NOOP)); + sc->len = cpu_to_be32(0); +} + +/** + * Delete the specified hash filter. + */ +static int cxgbe_del_hash_filter(struct rte_eth_dev *dev, + unsigned int filter_id, + struct filter_ctx *ctx) +{ + struct adapter *adapter = ethdev2adap(dev); + struct tid_info *t = &adapter->tids; + struct filter_entry *f; + struct sge_ctrl_txq *ctrlq; + unsigned int port_id = ethdev2pinfo(dev)->port_id; + int ret; + + if (filter_id > adapter->tids.ntids) + return -E2BIG; + + f = lookup_tid(t, filter_id); + if (!f) { + dev_err(adapter, "%s: no filter entry for filter_id = %d\n", + __func__, filter_id); + return -EINVAL; + } + + ret = writable_filter(f); + if (ret) + return ret; + + if (f->valid) { + unsigned int wrlen; + struct rte_mbuf *mbuf; + struct work_request_hdr *wr; + struct ulptx_idata *aligner; + struct cpl_set_tcb_field *req; + struct cpl_abort_req *abort_req; + struct cpl_abort_rpl *abort_rpl; + + f->ctx = ctx; + f->pending = 1; + + wrlen = cxgbe_roundup(sizeof(*wr) + + (sizeof(*req) + sizeof(*aligner)) + + sizeof(*abort_req) + sizeof(*abort_rpl), + 16); + + ctrlq = &adapter->sge.ctrlq[port_id]; + mbuf = rte_pktmbuf_alloc(ctrlq->mb_pool); + if (!mbuf) { + dev_err(adapter, "%s: could not allocate skb ..\n", + __func__); + goto out_err; + } + + mbuf->data_len = wrlen; + mbuf->pkt_len = mbuf->data_len; + + req = rte_pktmbuf_mtod(mbuf, struct cpl_set_tcb_field *); + INIT_ULPTX_WR(req, wrlen, 0, 0); + wr = (struct work_request_hdr *)req; + wr++; + req = (struct cpl_set_tcb_field *)wr; + mk_set_tcb_field_ulp(f, req, W_TCB_RSS_INFO, + V_TCB_RSS_INFO(M_TCB_RSS_INFO), + V_TCB_RSS_INFO( + adapter->sge.fw_evtq.abs_id), + 0, 1); + aligner = (struct ulptx_idata *)(req + 1); + abort_req = (struct cpl_abort_req *)(aligner + 1); + mk_abort_req_ulp(abort_req, f->tid); + abort_rpl = (struct cpl_abort_rpl *)(abort_req + 1); + mk_abort_rpl_ulp(abort_rpl, f->tid); + t4_mgmt_tx(ctrlq, mbuf); + } + return 0; + +out_err: + return -ENOMEM; +} + +/** + * Construct hash filter ntuple. + */ +static u64 hash_filter_ntuple(const struct filter_entry *f) +{ + struct adapter *adap = ethdev2adap(f->dev); + struct tp_params *tp = &adap->params.tp; + u64 ntuple = 0; + u16 tcp_proto = 6; /* TCP Protocol Number */ + + /* + * Initialize each of the fields which we care about which are present + * in the Compressed Filter Tuple. + */ + if (tp->vlan_shift >= 0 && f->fs.mask.ivlan) + ntuple |= (F_FT_VLAN_VLD | f->fs.val.ivlan) << tp->vlan_shift; + + if (tp->port_shift >= 0 && f->fs.mask.iport) + ntuple |= (u64)f->fs.val.iport << tp->port_shift; + + if (tp->protocol_shift >= 0) { + if (!f->fs.val.proto) + ntuple |= (u64)tcp_proto << tp->protocol_shift; + else + ntuple |= (u64)f->fs.val.proto << tp->protocol_shift; + } + + if (tp->tos_shift >= 0 && f->fs.mask.tos) + ntuple |= (u64)(f->fs.val.tos) << tp->tos_shift; + + if (tp->vnic_shift >= 0 && + (f->fs.mask.ovlan || f->fs.mask.pf || f->fs.mask.vf)) { + u32 viid = cxgbe_port_viid(f->dev); + u32 vf = G_FW_VIID_VIN(viid); + u32 pf = G_FW_VIID_PFN(viid); + u32 vld = G_FW_VIID_VIVLD(viid); + + ntuple |= (u64)(V_FT_VNID_ID_VF(vf) | + V_FT_VNID_ID_PF(pf) | + V_FT_VNID_ID_VLD(vld)) << tp->vnic_shift; + } + + if (tp->ethertype_shift >= 0 && f->fs.mask.ethtype) + ntuple |= (u64)(f->fs.val.ethtype) << tp->ethertype_shift; + + return ntuple; +} + +/** + * Build a ACT_OPEN_REQ6 message for setting IPv6 hash filter. + */ +static void mk_act_open_req6(struct filter_entry *f, struct rte_mbuf *mbuf, + unsigned int qid_filterid, struct adapter *adap) +{ + struct cpl_act_open_req6 *req = NULL; + struct cpl_t5_act_open_req6 *t5req = NULL; + u64 local_lo, local_hi, peer_lo, peer_hi; + u32 *lip = (u32 *)f->fs.val.lip; + u32 *fip = (u32 *)f->fs.val.fip; + + switch (CHELSIO_CHIP_VERSION(adap->params.chip)) { + case CHELSIO_T5: + t5req = rte_pktmbuf_mtod(mbuf, struct cpl_t5_act_open_req6 *); + + INIT_TP_WR(t5req, 0); + req = (struct cpl_act_open_req6 *)t5req; + break; + default: + dev_err(adap, "%s: unsupported chip type!\n", __func__); + return; + } + + local_hi = ((u64)lip[1]) << 32 | lip[0]; + local_lo = ((u64)lip[3]) << 32 | lip[2]; + peer_hi = ((u64)fip[1]) << 32 | fip[0]; + peer_lo = ((u64)fip[3]) << 32 | fip[2]; + + OPCODE_TID(req) = cpu_to_be32(MK_OPCODE_TID(CPL_ACT_OPEN_REQ6, + qid_filterid)); + req->local_port = cpu_to_be16(f->fs.val.lport); + req->peer_port = cpu_to_be16(f->fs.val.fport); + req->local_ip_hi = local_hi; + req->local_ip_lo = local_lo; + req->peer_ip_hi = peer_hi; + req->peer_ip_lo = peer_lo; + req->opt0 = cpu_to_be64(V_NAGLE(f->fs.newvlan == VLAN_REMOVE || + f->fs.newvlan == VLAN_REWRITE) | + V_DELACK(f->fs.hitcnts) | + V_L2T_IDX(f->l2t ? f->l2t->idx : 0) | + V_SMAC_SEL( + (cxgbe_port_viid(f->dev) & 0x7F) << 1) | + V_TX_CHAN(f->fs.eport) | + V_NO_CONG(f->fs.rpttid) | + V_ULP_MODE(f->fs.nat_mode ? + ULP_MODE_TCPDDP : ULP_MODE_NONE) | + F_TCAM_BYPASS | F_NON_OFFLOAD); + + if (is_t5(adap->params.chip)) { + t5req->params = cpu_to_be64( + V_FILTER_TUPLE(hash_filter_ntuple(f))); + t5req->opt2 = cpu_to_be32(F_RSS_QUEUE_VALID | + V_RSS_QUEUE(f->fs.iq) | + V_TX_QUEUE(f->fs.nat_mode) | + V_WND_SCALE_EN(f->fs.nat_flag_chk) | + V_RX_FC_DISABLE( + f->fs.nat_seq_chk ? 1 : 0) | + F_T5_OPT_2_VALID | + F_RX_CHANNEL | + V_SACK_EN(f->fs.swapmac) | + V_CONG_CNTRL( + (f->fs.action == FILTER_DROP) | + (f->fs.dirsteer << 1)) | + V_PACE((f->fs.maskhash) | + (f->fs.dirsteerhash << 1)) | + V_CCTRL_ECN( + f->fs.action == FILTER_SWITCH)); + } +} + +/** + * Build a ACT_OPEN_REQ message for setting IPv4 hash filter. + */ +static void mk_act_open_req(struct filter_entry *f, struct rte_mbuf *mbuf, + unsigned int qid_filterid, struct adapter *adap) +{ + struct cpl_act_open_req *req = NULL; + struct cpl_t5_act_open_req *t5req = NULL; + + switch (CHELSIO_CHIP_VERSION(adap->params.chip)) { + case CHELSIO_T5: + t5req = rte_pktmbuf_mtod(mbuf, struct cpl_t5_act_open_req *); + + INIT_TP_WR(t5req, 0); + req = (struct cpl_act_open_req *)t5req; + break; + default: + dev_err(adap, "%s: unsupported chip type!\n", __func__); + return; + } + + OPCODE_TID(req) = cpu_to_be32(MK_OPCODE_TID(CPL_ACT_OPEN_REQ, + qid_filterid)); + req->local_port = cpu_to_be16(f->fs.val.lport); + req->peer_port = cpu_to_be16(f->fs.val.fport); + req->local_ip = f->fs.val.lip[0] | f->fs.val.lip[1] << 8 | + f->fs.val.lip[2] << 16 | f->fs.val.lip[3] << 24; + req->peer_ip = f->fs.val.fip[0] | f->fs.val.fip[1] << 8 | + f->fs.val.fip[2] << 16 | f->fs.val.fip[3] << 24; + req->opt0 = cpu_to_be64(V_NAGLE(f->fs.newvlan == VLAN_REMOVE || + f->fs.newvlan == VLAN_REWRITE) | + V_DELACK(f->fs.hitcnts) | + V_L2T_IDX(f->l2t ? f->l2t->idx : 0) | + V_SMAC_SEL( + (cxgbe_port_viid(f->dev) & 0x7F) << 1) | + V_TX_CHAN(f->fs.eport) | + V_NO_CONG(f->fs.rpttid) | + V_ULP_MODE(f->fs.nat_mode ? + ULP_MODE_TCPDDP : ULP_MODE_NONE) | + F_TCAM_BYPASS | F_NON_OFFLOAD); + + if (is_t5(adap->params.chip)) { + t5req->params = cpu_to_be64( + V_FILTER_TUPLE(hash_filter_ntuple(f))); + t5req->opt2 = cpu_to_be32(F_RSS_QUEUE_VALID | + V_RSS_QUEUE(f->fs.iq) | + V_TX_QUEUE(f->fs.nat_mode) | + V_WND_SCALE_EN(f->fs.nat_flag_chk) | + V_RX_FC_DISABLE( + f->fs.nat_seq_chk ? 1 : 0) | + F_T5_OPT_2_VALID | + F_RX_CHANNEL | + V_SACK_EN(f->fs.swapmac) | + V_CONG_CNTRL( + (f->fs.action == FILTER_DROP) | + (f->fs.dirsteer << 1)) | + V_PACE((f->fs.maskhash) | + (f->fs.dirsteerhash << 1)) | + V_CCTRL_ECN( + f->fs.action == FILTER_SWITCH)); + } +} + +/** + * Set the specified hash filter. + */ +static int cxgbe_set_hash_filter(struct rte_eth_dev *dev, + struct ch_filter_specification *fs, + struct filter_ctx *ctx) +{ + struct port_info *pi = ethdev2pinfo(dev); + struct adapter *adapter = pi->adapter; + struct tid_info *t = &adapter->tids; + struct filter_entry *f; + struct rte_mbuf *mbuf; + struct sge_ctrl_txq *ctrlq; + unsigned int iq; + int atid, size; + int ret = 0; + + ret = validate_filter(adapter, fs); + if (ret) + return ret; + + iq = get_filter_steerq(dev, fs); + + ctrlq = &adapter->sge.ctrlq[pi->port_id]; + + f = t4_os_alloc(sizeof(*f)); + if (!f) + goto out_err; + + f->fs = *fs; + f->ctx = ctx; + f->dev = dev; + f->fs.iq = iq; + + /* + * If the new filter requires loopback Destination MAC and/or VLAN + * rewriting then we need to allocate a Layer 2 Table (L2T) entry for + * the filter. + */ + if (f->fs.newdmac || + ((f->fs.newvlan == VLAN_INSERT) || + (f->fs.newvlan == VLAN_REWRITE))) { + /* allocate L2T entry for new filter */ + f->l2t = cxgbe_l2t_alloc_switching(dev, f->fs.vlan, + f->fs.eport, f->fs.dmac); + if (!f->l2t) { + ret = -ENOMEM; + goto out_err; + } + } + + /* + * If the new filter requires loopback Source MAC rewriting then + * we need to allocate a SMT entry for the filter. + */ + if (f->fs.newsmac) { + f->smt = cxgbe_smt_alloc_switching(dev, f->fs.smac); + if (!f->smt) { + ret = -EAGAIN; + goto free_l2t; + } + f->smtidx = f->smt->idx; + } + + atid = cxgbe_alloc_atid(t, f); + if (atid < 0) + goto free_smt; + + if (f->fs.type) { + /* IPv6 hash filter */ + f->clipt = cxgbe_clip_alloc(f->dev, (u32 *)&f->fs.val.lip); + if (!f->clipt) + goto free_atid; + + size = sizeof(struct cpl_t5_act_open_req6); + mbuf = rte_pktmbuf_alloc(ctrlq->mb_pool); + if (!mbuf) { + ret = -ENOMEM; + goto free_clip; + } + + mbuf->data_len = size; + mbuf->pkt_len = mbuf->data_len; + + mk_act_open_req6(f, mbuf, + ((adapter->sge.fw_evtq.abs_id << 14) | atid), + adapter); + } else { + /* IPv4 hash filter */ + size = sizeof(struct cpl_t5_act_open_req); + mbuf = rte_pktmbuf_alloc(ctrlq->mb_pool); + if (!mbuf) { + ret = -ENOMEM; + goto free_atid; + } + + mbuf->data_len = size; + mbuf->pkt_len = mbuf->data_len; + + mk_act_open_req(f, mbuf, + ((adapter->sge.fw_evtq.abs_id << 14) | atid), + adapter); + } + + f->pending = 1; + t4_mgmt_tx(ctrlq, mbuf); + return 0; + +free_clip: + cxgbe_clip_release(f->dev, f->clipt); + +free_atid: + cxgbe_free_atid(t, atid); + +free_smt: + if (f->smt) { + cxgbe_smt_release(f->smt); + f->smt = NULL; + } + +free_l2t: + if (f->l2t) { + cxgbe_l2t_release(f->l2t); + f->l2t = NULL; + } + +out_err: + t4_os_free(f); + return ret; +} + +/** * Clear a filter and release any of its resources that we own. This also * clears the filter's "pending" status. */
@@ -229,6 +817,18 @@ void cxgbe_clear_all_filters(struct adapter *adapter) if (f->valid || f->pending) clear_filter(f); } + + if (is_hashfilter(adapter) && adapter->tids.tid_tab) { + struct filter_entry *f; + + for (i = adapter->tids.hash_base; i <= adapter->tids.ntids; + i++) { + f = (struct filter_entry *)adapter->tids.tid_tab[i]; + + if (f && (f->valid || f->pending)) + t4_os_free(f); + } + } } /**
@@ -536,6 +1136,9 @@ int cxgbe_del_filter(struct rte_eth_dev *dev, unsigned int filter_id, struct filter_entry *f; int ret; + if (is_hashfilter(adapter) && fs->cap) + return cxgbe_del_hash_filter(dev, filter_id, ctx); + if (filter_id >= adapter->tids.nftids) return -ERANGE;
@@ -591,6 +1194,9 @@ int cxgbe_set_filter(struct rte_eth_dev *dev, unsigned int filter_id, struct filter_entry *f; int ret; + if (is_hashfilter(adapter) && fs->cap) + return cxgbe_set_hash_filter(dev, fs, ctx); + if (filter_id >= adapter->tids.nftids) return -ERANGE;
@@ -800,3 +1406,209 @@ void filter_rpl(struct adapter *adap, const struct cpl_set_tcb_rpl *rpl) t4_complete(&ctx->completion); } } + +/** + * Handle a Hash filter write reply. + */ +void hash_filter_rpl(struct adapter *adap, const struct cpl_act_open_rpl *rpl) +{ + struct tid_info *t = &adap->tids; + struct filter_entry *f; + struct filter_ctx *ctx = NULL; + unsigned int tid = GET_TID(rpl); + unsigned int ftid = G_TID_TID( + G_AOPEN_ATID(be32_to_cpu(rpl->atid_status))); + unsigned int status = G_AOPEN_STATUS(be32_to_cpu(rpl->atid_status)); + + f = lookup_atid(t, ftid); + if (!f) { + dev_warn(adap, "%s: could not find filter entry: %d\n", + __func__, ftid); + return; + } + + ctx = f->ctx; + f->ctx = NULL; + + switch (status) { + case CPL_ERR_NONE: { + f->tid = tid; + f->pending = 0; /* asynchronous setup completed */ + f->valid = 1; + + cxgbe_insert_tid(t, f, f->tid, 0); + cxgbe_free_atid(t, ftid); + if (ctx) { + ctx->tid = f->tid; + ctx->result = 0; + } + + if (f->fs.hitcnts) + set_tcb_field(adap, tid, + W_TCB_TIMESTAMP, + V_TCB_TIMESTAMP(M_TCB_TIMESTAMP) | + V_TCB_T_RTT_TS_RECENT_AGE( + M_TCB_T_RTT_TS_RECENT_AGE), + V_TCB_TIMESTAMP(0ULL) | + V_TCB_T_RTT_TS_RECENT_AGE(0ULL), + 1); + + if (f->fs.newdmac) + set_tcb_tflag(adap, tid, S_TF_CCTRL_ECE, 1, 1); + + if ((f->fs.newvlan == VLAN_INSERT) || + (f->fs.newvlan == VLAN_REWRITE)) + set_tcb_tflag(adap, tid, S_TF_CCTRL_RFR, 1, 1); + + if (f->fs.newsmac) { + set_tcb_tflag(adap, tid, S_TF_CCTRL_CWR, 1, 1); + set_tcb_field(adap, tid, W_TCB_SMAC_SEL, + V_TCB_SMAC_SEL(M_TCB_SMAC_SEL), + V_TCB_SMAC_SEL(f->smtidx), 1); + } + + if (f->fs.nat_mode) { + switch (f->fs.nat_mode) { + case NAT_MODE_DIP: + set_nat_params(adap, f, tid, true, + false, false, false); + break; + + case NAT_MODE_DIP_DP: + set_nat_params(adap, f, tid, true, + false, true, false); + break; + + case NAT_MODE_DIP_DP_SIP: + set_nat_params(adap, f, tid, true, + true, true, false); + break; + + case NAT_MODE_DIP_DP_SP: + set_nat_params(adap, f, tid, true, + false, true, true); + break; + + case NAT_MODE_SIP_SP: + set_nat_params(adap, f, tid, false, + true, false, true); + break; + + case NAT_MODE_DIP_SIP_SP: + set_nat_params(adap, f, tid, true, + true, false, true); + break; + + case NAT_MODE_ALL: + set_nat_params(adap, f, tid, true, + true, true, true); + break; + + default: + dev_err(adap, "%s: Invalid NAT mode: %d\n", + __func__, f->fs.nat_mode); + + if (f->l2t) + cxgbe_l2t_release(f->l2t); + + if (f->smt) + cxgbe_smt_release(f->smt); + + t4_os_free(f); + + if (ctx) { + ctx->result = -EINVAL; + t4_complete(&ctx->completion); + } + return; + } + } + + if (f->fs.nat_seq_chk) { + set_tcb_field(adap, tid, W_TCB_RCV_NXT, + V_TCB_RCV_NXT(M_TCB_RCV_NXT), + V_TCB_RCV_NXT(f->fs.nat_seq_chk), 1); + } + + if (is_t5(adap->params.chip)) { + if (f->fs.action == FILTER_DROP) { + /* + * Set Migrating bit to 1, and + * set Non-offload bit to 0 - to achieve + * Drop action with Hash filters + */ + set_tcb_field(adap, tid, + W_TCB_T_FLAGS, + V_TF_NON_OFFLOAD(1) | + V_TF_MIGRATING(1), + V_TF_MIGRATING(1), 1); + } + } + + break; + } + default: + dev_warn(adap, "%s: filter creation failed with status = %u\n", + __func__, status); + + if (ctx) { + if (status == CPL_ERR_TCAM_FULL) + ctx->result = -EAGAIN; + else + ctx->result = -EINVAL; + } + + if (f->l2t) + cxgbe_l2t_release(f->l2t); + + if (f->smt) + cxgbe_smt_release(f->smt); + + cxgbe_free_atid(t, ftid); + t4_os_free(f); + } + + if (ctx) + t4_complete(&ctx->completion); +} + +/** + * Handle a Hash filter delete reply. + */ +void hash_del_filter_rpl(struct adapter *adap, + const struct cpl_abort_rpl_rss *rpl) +{ + struct tid_info *t = &adap->tids; + struct filter_entry *f; + struct filter_ctx *ctx = NULL; + unsigned int tid = GET_TID(rpl); + + f = lookup_tid(t, tid); + if (!f) { + dev_warn(adap, "%s: could not find filter entry: %u\n", + __func__, tid); + return; + } + + ctx = f->ctx; + f->ctx = NULL; + + f->valid = 0; + + if (f->clipt) + cxgbe_clip_release(f->dev, f->clipt); + + if (f->l2t) + cxgbe_l2t_release(f->l2t); + + if (f->smt) + cxgbe_smt_release(f->smt); + + cxgbe_remove_tid(t, 0, tid, 0); + t4_os_free(f); + + if (ctx) { + ctx->result = 0; + t4_complete(&ctx->completion); + } +}
diff --git a/drivers/net/cxgbe/cxgbe_filter.h b/drivers/net/cxgbe/cxgbe_filter.h
index 96c15d2..cde74fc 100644
--- a/drivers/net/cxgbe/cxgbe_filter.h
+++ b/drivers/net/cxgbe/cxgbe_filter.h@@ -235,6 +235,8 @@ struct filter_entry { struct ch_filter_specification fs; }; +#define WORD_MASK 0xffffffff + struct adapter; void filter_rpl(struct adapter *adap, const struct cpl_set_tcb_rpl *rpl);
@@ -249,5 +251,10 @@ int cxgbe_del_filter(struct rte_eth_dev *dev, unsigned int filter_id, struct ch_filter_specification *fs, struct filter_ctx *ctx); +int init_hash_filter(struct adapter *adap); +void hash_filter_rpl(struct adapter *adap, const struct cpl_act_open_rpl *rpl); +void hash_del_filter_rpl(struct adapter *adap, + const struct cpl_abort_rpl_rss *rpl); + void cxgbe_clear_all_filters(struct adapter *adapter); #endif /* _CXGBE_FILTER_H_ */
diff --git a/drivers/net/cxgbe/cxgbe_main.c b/drivers/net/cxgbe/cxgbe_main.c
index dfb6567..1f79ba3 100644
--- a/drivers/net/cxgbe/cxgbe_main.c
+++ b/drivers/net/cxgbe/cxgbe_main.c@@ -123,6 +123,14 @@ static int fwevtq_handler(struct sge_rspq *q, const __be64 *rsp, const struct cpl_set_tcb_rpl *p = (const void *)rsp; filter_rpl(q->adapter, p); + } else if (opcode == CPL_ACT_OPEN_RPL) { + const struct cpl_act_open_rpl *p = (const void *)rsp; + + hash_filter_rpl(q->adapter, p); + } else if (opcode == CPL_ABORT_RPL_RSS) { + const struct cpl_abort_rpl_rss *p = (const void *)rsp; + + hash_del_filter_rpl(q->adapter, p); } else if (opcode == CPL_SMT_WRITE_RPL) { const struct cpl_smt_write_rpl *p = (const void *)rsp;
@@ -272,6 +280,110 @@ int cxgb4_set_rspq_intr_params(struct sge_rspq *q, unsigned int us, } /** + * Allocate an active-open TID and set it to the supplied value. + */ +int cxgbe_alloc_atid(struct tid_info *t, void *data) +{ + int atid = -1; + + t4_os_lock(&t->atid_lock); + if (t->afree) { + union aopen_entry *p = t->afree; + + atid = p - t->atid_tab; + t->afree = p->next; + p->data = data; + t->atids_in_use++; + } + t4_os_unlock(&t->atid_lock); + return atid; +} + +/** + * Release an active-open TID. + */ +void cxgbe_free_atid(struct tid_info *t, unsigned int atid) +{ + union aopen_entry *p = &t->atid_tab[atid]; + + t4_os_lock(&t->atid_lock); + p->next = t->afree; + t->afree = p; + t->atids_in_use--; + t4_os_unlock(&t->atid_lock); +} + +/** + * Populate a TID_RELEASE WR. Caller must properly size the skb. + */ +static void mk_tid_release(struct rte_mbuf *mbuf, unsigned int tid) +{ + struct cpl_tid_release *req; + + req = rte_pktmbuf_mtod(mbuf, struct cpl_tid_release *); + INIT_TP_WR_MIT_CPL(req, CPL_TID_RELEASE, tid); +} + +/** + * Release a TID and inform HW. If we are unable to allocate the release + * message we defer to a work queue. + */ +void cxgbe_remove_tid(struct tid_info *t, unsigned int chan, unsigned int tid, + unsigned short family) +{ + struct rte_mbuf *mbuf; + struct adapter *adap = container_of(t, struct adapter, tids); + + WARN_ON(tid >= t->ntids); + + if (t->tid_tab[tid]) { + t->tid_tab[tid] = NULL; + rte_atomic32_dec(&t->conns_in_use); + if (t->hash_base && (tid >= t->hash_base)) { + if (family == FILTER_TYPE_IPV6) + rte_atomic32_sub(&t->hash_tids_in_use, 2); + else + rte_atomic32_dec(&t->hash_tids_in_use); + } else { + if (family == FILTER_TYPE_IPV6) + rte_atomic32_sub(&t->tids_in_use, 2); + else + rte_atomic32_dec(&t->tids_in_use); + } + } + + mbuf = rte_pktmbuf_alloc((&adap->sge.ctrlq[chan])->mb_pool); + if (mbuf) { + mbuf->data_len = sizeof(struct cpl_tid_release); + mbuf->pkt_len = mbuf->data_len; + mk_tid_release(mbuf, tid); + t4_mgmt_tx(&adap->sge.ctrlq[chan], mbuf); + } +} + +/** + * Insert a TID. + */ +void cxgbe_insert_tid(struct tid_info *t, void *data, unsigned int tid, + unsigned short family) +{ + t->tid_tab[tid] = data; + if (t->hash_base && (tid >= t->hash_base)) { + if (family == FILTER_TYPE_IPV6) + rte_atomic32_add(&t->hash_tids_in_use, 2); + else + rte_atomic32_inc(&t->hash_tids_in_use); + } else { + if (family == FILTER_TYPE_IPV6) + rte_atomic32_add(&t->tids_in_use, 2); + else + rte_atomic32_inc(&t->tids_in_use); + } + + rte_atomic32_inc(&t->conns_in_use); +} + +/** * Free TID tables. */ static void tid_free(struct tid_info *t)
@@ -675,8 +787,7 @@ static int adap_init0_config(struct adapter *adapter, int reset) * This will allow the firmware to optimize aspects of the hardware * configuration which will result in improved performance. */ - caps_cmd.niccaps &= cpu_to_be16(~(FW_CAPS_CONFIG_NIC_HASHFILTER | - FW_CAPS_CONFIG_NIC_ETHOFLD)); + caps_cmd.niccaps &= cpu_to_be16(~FW_CAPS_CONFIG_NIC_ETHOFLD); caps_cmd.toecaps = 0; caps_cmd.iscsicaps = 0; caps_cmd.rdmacaps = 0;
@@ -908,6 +1019,12 @@ static int adap_init0(struct adapter *adap) if (ret < 0) goto bye; + if ((caps_cmd.niccaps & cpu_to_be16(FW_CAPS_CONFIG_NIC_HASHFILTER)) && + is_t5(adap->params.chip)) { + if (init_hash_filter(adap) < 0) + goto bye; + } + /* query tid-related parameters */ params[0] = FW_PARAM_DEV(NTID); ret = t4_query_params(adap, adap->mbox, adap->pf, 0, 1,
@@ -1411,6 +1528,20 @@ allocate_mac: "filter support disabled. Continuing\n"); } + if (is_hashfilter(adapter)) { + if (t4_read_reg(adapter, A_LE_DB_CONFIG) & F_HASHEN) { + u32 hash_base, hash_reg; + + hash_reg = A_LE_DB_TID_HASHBASE; + hash_base = t4_read_reg(adapter, hash_reg); + adapter->tids.hash_base = hash_base / 4; + } + } else { + /* Disable hash filtering support */ + dev_warn(adapter, + "Maskless 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
index 115472e..0cddf8d 100644
--- a/drivers/net/cxgbe/cxgbe_ofld.h
+++ b/drivers/net/cxgbe/cxgbe_ofld.h@@ -52,6 +52,14 @@ OPCODE_TID(w) = cpu_to_be32(MK_OPCODE_TID(cpl, tid)); \ } while (0) +#define INIT_ULPTX_WR(w, wrlen, atomic, tid) do { \ + (w)->wr.wr_hi = cpu_to_be32(V_FW_WR_OP(FW_ULPTX_WR) | \ + V_FW_WR_ATOMIC(atomic)); \ + (w)->wr.wr_mid = cpu_to_be32(V_FW_WR_LEN16(DIV_ROUND_UP(wrlen, 16)) | \ + V_FW_WR_FLOWID(tid)); \ + (w)->wr.wr_lo = cpu_to_be64(0); \ +} while (0) + /* * Max # of ATIDs. The absolute HW max is 16K but we keep it lower. */
@@ -97,4 +105,22 @@ struct tid_info { rte_atomic32_t conns_in_use; rte_spinlock_t ftid_lock; }; + +static inline void *lookup_tid(const struct tid_info *t, unsigned int tid) +{ + return tid < t->ntids ? t->tid_tab[tid] : NULL; +} + +static inline void *lookup_atid(const struct tid_info *t, unsigned int atid) +{ + return atid < t->natids ? t->atid_tab[atid].data : NULL; +} + +int cxgbe_alloc_atid(struct tid_info *t, void *data); +void cxgbe_free_atid(struct tid_info *t, unsigned int atid); + +void cxgbe_remove_tid(struct tid_info *t, unsigned int qid, unsigned int tid, + unsigned short family); +void cxgbe_insert_tid(struct tid_info *t, void *data, unsigned int tid, + unsigned short family); #endif /* _CXGBE_OFLD_H_ */
--
2.5.3