Commit 3b0b3bee authored by Kumar Sanghvi's avatar Kumar Sanghvi Committed by David S. Miller

cxgb4: add support to delete hash filter

Use a combined ulptx work-request to send hash filter deletion
request to hw. Hash filter deletion reply is processed on
getting cpl_abort_rpl_rss.

Release any L2T/SMT/CLIP entries on filter deletion.
Also, free up the corresponding filter entry.
Signed-off-by: default avatarKumar Sanghvi <kumaras@chelsio.com>
Signed-off-by: default avatarRahul Lakkireddy <rahul.lakkireddy@chelsio.com>
Signed-off-by: default avatarGanesh Goudar <ganeshgr@chelsio.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 12b276fb
......@@ -87,6 +87,59 @@ static int set_tcb_tflag(struct adapter *adap, struct filter_entry *f,
(unsigned long long)val << bit_pos, no_reply);
}
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 = htonl(ULPTX_CMD_V(ULP_TX_PKT) | ULP_TXPKT_DEST_V(0));
txpkt->len = htonl(DIV_ROUND_UP(sizeof(*abort_req), 16));
sc->cmd_more = htonl(ULPTX_CMD_V(ULP_TX_SC_IMM));
sc->len = htonl(sizeof(*abort_req) - sizeof(struct work_request_hdr));
OPCODE_TID(abort_req) = htonl(MK_OPCODE_TID(CPL_ABORT_REQ, tid));
abort_req->rsvd0 = htonl(0);
abort_req->rsvd1 = 0;
abort_req->cmd = CPL_ABORT_NO_RST;
}
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 = htonl(ULPTX_CMD_V(ULP_TX_PKT) | ULP_TXPKT_DEST_V(0));
txpkt->len = htonl(DIV_ROUND_UP(sizeof(*abort_rpl), 16));
sc->cmd_more = htonl(ULPTX_CMD_V(ULP_TX_SC_IMM));
sc->len = htonl(sizeof(*abort_rpl) - sizeof(struct work_request_hdr));
OPCODE_TID(abort_rpl) = htonl(MK_OPCODE_TID(CPL_ABORT_RPL, tid));
abort_rpl->rsvd0 = htonl(0);
abort_rpl->rsvd1 = 0;
abort_rpl->cmd = CPL_ABORT_NO_RST;
}
static void mk_set_tcb_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 = htonl(ULPTX_CMD_V(ULP_TX_PKT) | ULP_TXPKT_DEST_V(0));
txpkt->len = htonl(DIV_ROUND_UP(sizeof(*req), 16));
sc->cmd_more = htonl(ULPTX_CMD_V(ULP_TX_SC_IMM));
sc->len = htonl(sizeof(*req) - sizeof(struct work_request_hdr));
OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_SET_TCB_FIELD, f->tid));
req->reply_ctrl = htons(NO_REPLY_V(no_reply) | REPLY_CHAN_V(0) |
QUEUENO_V(0));
req->word_cookie = htons(TCB_WORD_V(word) | TCB_COOKIE_V(cookie));
req->mask = cpu_to_be64(mask);
req->val = cpu_to_be64(val);
sc = (struct ulptx_idata *)(req + 1);
sc->cmd_more = htonl(ULPTX_CMD_V(ULP_TX_SC_NOOP));
sc->len = htonl(0);
}
static int configure_filter_smac(struct adapter *adap, struct filter_entry *f)
{
int err;
......@@ -1110,12 +1163,74 @@ int __cxgb4_set_filter(struct net_device *dev, int filter_id,
return ret;
}
static int cxgb4_del_hash_filter(struct net_device *dev, int filter_id,
struct filter_ctx *ctx)
{
struct adapter *adapter = netdev2adap(dev);
struct tid_info *t = &adapter->tids;
struct cpl_abort_req *abort_req;
struct cpl_abort_rpl *abort_rpl;
struct cpl_set_tcb_field *req;
struct ulptx_idata *aligner;
struct work_request_hdr *wr;
struct filter_entry *f;
struct sk_buff *skb;
unsigned int wrlen;
int ret;
netdev_dbg(dev, "%s: filter_id = %d ; nftids = %d\n",
__func__, filter_id, adapter->tids.nftids);
if (filter_id > adapter->tids.ntids)
return -E2BIG;
f = lookup_tid(t, filter_id);
if (!f) {
netdev_err(dev, "%s: no filter entry for filter_id = %d",
__func__, filter_id);
return -EINVAL;
}
ret = writable_filter(f);
if (ret)
return ret;
if (!f->valid)
return -EINVAL;
f->ctx = ctx;
f->pending = 1;
wrlen = roundup(sizeof(*wr) + (sizeof(*req) + sizeof(*aligner))
+ sizeof(*abort_req) + sizeof(*abort_rpl), 16);
skb = alloc_skb(wrlen, GFP_KERNEL);
if (!skb) {
netdev_err(dev, "%s: could not allocate skb ..\n", __func__);
return -ENOMEM;
}
set_wr_txq(skb, CPL_PRIORITY_CONTROL, f->fs.val.iport & 0x3);
req = (struct cpl_set_tcb_field *)__skb_put(skb, wrlen);
INIT_ULPTX_WR(req, wrlen, 0, 0);
wr = (struct work_request_hdr *)req;
wr++;
req = (struct cpl_set_tcb_field *)wr;
mk_set_tcb_ulp(f, req, TCB_RSS_INFO_W, TCB_RSS_INFO_V(TCB_RSS_INFO_M),
TCB_RSS_INFO_V(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_ofld_send(adapter, skb);
return 0;
}
/* 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 __cxgb4_del_filter(struct net_device *dev, int filter_id,
struct ch_filter_specification *fs,
struct filter_ctx *ctx)
{
struct adapter *adapter = netdev2adap(dev);
......@@ -1123,6 +1238,14 @@ int __cxgb4_del_filter(struct net_device *dev, int filter_id,
unsigned int max_fidx;
int ret;
if (fs && fs->hash) {
if (is_hashfilter(adapter))
return cxgb4_del_hash_filter(dev, filter_id, ctx);
netdev_err(dev, "%s: Exact-match filters only supported with Hash Filter configuration\n",
__func__);
return -EINVAL;
}
max_fidx = adapter->tids.nftids;
if (filter_id != (max_fidx + adapter->tids.nsftids - 1) &&
filter_id >= max_fidx)
......@@ -1173,18 +1296,19 @@ int cxgb4_set_filter(struct net_device *dev, int filter_id,
return ret;
}
int cxgb4_del_filter(struct net_device *dev, int filter_id)
int cxgb4_del_filter(struct net_device *dev, int filter_id,
struct ch_filter_specification *fs)
{
struct filter_ctx ctx;
int ret;
/* If we are shutting down the adapter do not wait for completion */
if (netdev2adap(dev)->flags & SHUTTING_DOWN)
return __cxgb4_del_filter(dev, filter_id, NULL);
return __cxgb4_del_filter(dev, filter_id, fs, NULL);
init_completion(&ctx.completion);
ret = __cxgb4_del_filter(dev, filter_id, &ctx);
ret = __cxgb4_del_filter(dev, filter_id, fs, &ctx);
if (ret)
goto out;
......@@ -1258,6 +1382,35 @@ static int configure_filter_tcb(struct adapter *adap, unsigned int tid,
return 0;
}
void hash_del_filter_rpl(struct adapter *adap,
const struct cpl_abort_rpl_rss *rpl)
{
unsigned int status = rpl->status;
struct tid_info *t = &adap->tids;
unsigned int tid = GET_TID(rpl);
struct filter_ctx *ctx = NULL;
struct filter_entry *f;
dev_dbg(adap->pdev_dev, "%s: status = %u; tid = %u\n",
__func__, status, tid);
f = lookup_tid(t, tid);
if (!f) {
dev_err(adap->pdev_dev, "%s:could not find filter entry",
__func__);
return;
}
ctx = f->ctx;
f->ctx = NULL;
clear_filter(adap, f);
cxgb4_remove_tid(t, 0, tid, 0);
kfree(f);
if (ctx) {
ctx->result = 0;
complete(&ctx->completion);
}
}
void hash_filter_rpl(struct adapter *adap, const struct cpl_act_open_rpl *rpl)
{
unsigned int ftid = TID_TID_G(AOPEN_ATID_G(ntohl(rpl->atid_status)));
......
......@@ -41,6 +41,8 @@
void filter_rpl(struct adapter *adap, const struct cpl_set_tcb_rpl *rpl);
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 clear_filter(struct adapter *adap, struct filter_entry *f);
int set_filter_wr(struct adapter *adapter, int fidx);
......
......@@ -576,6 +576,10 @@ static int fwevtq_handler(struct sge_rspq *q, const __be64 *rsp,
const struct cpl_act_open_rpl *p = (void *)rsp;
hash_filter_rpl(q->adap, p);
} else if (opcode == CPL_ABORT_RPL_RSS) {
const struct cpl_abort_rpl_rss *p = (void *)rsp;
hash_del_filter_rpl(q->adap, p);
} else
dev_err(q->adap->pdev_dev,
"unexpected CPL %#x on FW event queue\n", opcode);
......
......@@ -730,7 +730,7 @@ int cxgb4_tc_flower_destroy(struct net_device *dev,
if (!ch_flower)
return -ENOENT;
ret = cxgb4_del_filter(dev, ch_flower->filter_id);
ret = cxgb4_del_filter(dev, ch_flower->filter_id, &ch_flower->fs);
if (ret)
goto err;
......
......@@ -380,7 +380,7 @@ int cxgb4_delete_knode(struct net_device *dev, struct tc_cls_u32_offload *cls)
return -EINVAL;
}
ret = cxgb4_del_filter(dev, filter_id);
ret = cxgb4_del_filter(dev, filter_id, NULL);
if (ret)
goto out;
......@@ -399,7 +399,7 @@ int cxgb4_delete_knode(struct net_device *dev, struct tc_cls_u32_offload *cls)
if (!test_bit(j, link->tid_map))
continue;
ret = __cxgb4_del_filter(dev, j, NULL);
ret = __cxgb4_del_filter(dev, j, NULL, NULL);
if (ret)
goto out;
......
......@@ -217,10 +217,12 @@ int __cxgb4_set_filter(struct net_device *dev, int filter_id,
struct ch_filter_specification *fs,
struct filter_ctx *ctx);
int __cxgb4_del_filter(struct net_device *dev, int filter_id,
struct ch_filter_specification *fs,
struct filter_ctx *ctx);
int cxgb4_set_filter(struct net_device *dev, int filter_id,
struct ch_filter_specification *fs);
int cxgb4_del_filter(struct net_device *dev, int filter_id);
int cxgb4_del_filter(struct net_device *dev, int filter_id,
struct ch_filter_specification *fs);
int cxgb4_get_filter_counters(struct net_device *dev, unsigned int fidx,
u64 *hitcnt, u64 *bytecnt);
......
......@@ -46,6 +46,11 @@
#define TF_CCTRL_CWR_S 61
#define TF_CCTRL_RFR_S 62
#define TCB_RSS_INFO_W 3
#define TCB_RSS_INFO_S 0
#define TCB_RSS_INFO_M 0x3ffULL
#define TCB_RSS_INFO_V(x) ((x) << TCB_RSS_INFO_S)
#define TCB_TIMESTAMP_W 5
#define TCB_TIMESTAMP_S 0
#define TCB_TIMESTAMP_M 0xffffffffULL
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment