Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright(c) 2018 Chelsio Communications.
3 : : * All rights reserved.
4 : : */
5 : : #include <rte_net.h>
6 : :
7 : : #include "base/common.h"
8 : : #include "base/t4_tcb.h"
9 : : #include "base/t4_regs.h"
10 : : #include "cxgbe_filter.h"
11 : : #include "mps_tcam.h"
12 : : #include "clip_tbl.h"
13 : : #include "l2t.h"
14 : : #include "smt.h"
15 : : #include "cxgbe_pfvf.h"
16 : :
17 : : /**
18 : : * Initialize Hash Filters
19 : : */
20 : 0 : int cxgbe_init_hash_filter(struct adapter *adap)
21 : : {
22 : : unsigned int user_filter_perc, n_user_filters;
23 : : u32 param, val;
24 : : int ret;
25 : :
26 [ # # ]: 0 : if (CHELSIO_CHIP_VERSION(adap->params.chip) > CHELSIO_T5) {
27 : 0 : val = t4_read_reg(adap, A_LE_DB_RSP_CODE_0);
28 [ # # ]: 0 : if (G_TCAM_ACTV_HIT(val) != 4) {
29 : 0 : adap->params.hash_filter = 0;
30 : 0 : return 0;
31 : : }
32 : :
33 : 0 : val = t4_read_reg(adap, A_LE_DB_RSP_CODE_1);
34 [ # # ]: 0 : if (G_HASH_ACTV_HIT(val) != 4) {
35 : 0 : adap->params.hash_filter = 0;
36 : 0 : return 0;
37 : : }
38 : : }
39 : :
40 : 0 : param = CXGBE_FW_PARAM_DEV(NTID);
41 : 0 : ret = t4_query_params(adap, adap->mbox, adap->pf, 0, 1,
42 : : ¶m, &val);
43 [ # # ]: 0 : if (ret < 0)
44 : : return ret;
45 : 0 : adap->tids.ntids = val;
46 : 0 : adap->tids.natids = min(adap->tids.ntids / 2, MAX_ATIDS);
47 : :
48 : : user_filter_perc = 100;
49 : 0 : n_user_filters = mult_frac(adap->tids.nftids,
50 : : user_filter_perc,
51 : : 100);
52 : :
53 : 0 : adap->tids.nftids = n_user_filters;
54 : 0 : adap->params.hash_filter = 1;
55 : 0 : return 0;
56 : : }
57 : :
58 : : /**
59 : : * Validate if the requested filter specification can be set by checking
60 : : * if the requested features have been enabled
61 : : */
62 : 0 : int cxgbe_validate_filter(struct adapter *adapter,
63 : : struct ch_filter_specification *fs)
64 : : {
65 : : u32 fconf, iconf;
66 : :
67 : : /*
68 : : * Check for unconfigured fields being used.
69 : : */
70 [ # # ]: 0 : fconf = fs->cap ? adapter->params.tp.filter_mask :
71 : : adapter->params.tp.vlan_pri_map;
72 : :
73 : 0 : iconf = adapter->params.tp.ingress_config;
74 : :
75 : : #define S(_field) \
76 : : (fs->val._field || fs->mask._field)
77 : : #define U(_mask, _field) \
78 : : (!(fconf & (_mask)) && S(_field))
79 : :
80 [ # # # # : 0 : if (U(F_PORT, iport) || U(F_ETHERTYPE, ethtype) ||
# # # # #
# # # ]
81 [ # # # # : 0 : U(F_PROTOCOL, proto) || U(F_MACMATCH, macidx) ||
# # # # #
# # # ]
82 [ # # # # : 0 : U(F_VLAN, ivlan_vld) || U(F_VNIC_ID, ovlan_vld) ||
# # # # #
# # # ]
83 [ # # # # : 0 : U(F_TOS, tos) || U(F_VNIC_ID, pfvf_vld))
# # # # #
# # # ]
84 : : return -EOPNOTSUPP;
85 : :
86 : : /* Either OVLAN or PFVF match is enabled in hardware, but not both */
87 [ # # # # : 0 : if ((S(pfvf_vld) && !(iconf & F_VNIC)) ||
# # ]
88 [ # # # # : 0 : (S(ovlan_vld) && (iconf & F_VNIC)))
# # ]
89 : : return -EOPNOTSUPP;
90 : :
91 : : /* To use OVLAN or PFVF, L4 encapsulation match must not be enabled */
92 [ # # # # : 0 : if ((S(ovlan_vld) && (iconf & F_USE_ENC_IDX)) ||
# # # # ]
93 [ # # ]: 0 : (S(pfvf_vld) && (iconf & F_USE_ENC_IDX)))
94 : : return -EOPNOTSUPP;
95 : :
96 : : #undef S
97 : : #undef U
98 : :
99 : : /*
100 : : * If the user is requesting that the filter action loop
101 : : * matching packets back out one of our ports, make sure that
102 : : * the egress port is in range.
103 : : */
104 [ # # ]: 0 : if (fs->action == FILTER_SWITCH &&
105 [ # # ]: 0 : fs->eport >= adapter->params.nports)
106 : : return -ERANGE;
107 : :
108 : : /*
109 : : * Don't allow various trivially obvious bogus out-of-range
110 : : * values ...
111 : : */
112 [ # # ]: 0 : if (fs->val.iport >= adapter->params.nports)
113 : : return -ERANGE;
114 : :
115 [ # # # # : 0 : if (!fs->cap && fs->nat_mode && !adapter->params.filter2_wr_support)
# # ]
116 : : return -EOPNOTSUPP;
117 : :
118 [ # # # # ]: 0 : if (!fs->cap && fs->swapmac && !adapter->params.filter2_wr_support)
119 : 0 : return -EOPNOTSUPP;
120 : :
121 : : return 0;
122 : : }
123 : :
124 : : /**
125 : : * Get the queue to which the traffic must be steered to.
126 : : */
127 : : static unsigned int get_filter_steerq(struct rte_eth_dev *dev,
128 : : struct ch_filter_specification *fs)
129 : : {
130 : : struct port_info *pi = ethdev2pinfo(dev);
131 : 0 : struct adapter *adapter = pi->adapter;
132 : : unsigned int iq;
133 : :
134 : : /*
135 : : * If the user has requested steering matching Ingress Packets
136 : : * to a specific Queue Set, we need to make sure it's in range
137 : : * for the port and map that into the Absolute Queue ID of the
138 : : * Queue Set's Response Queue.
139 : : */
140 [ # # # # ]: 0 : if (!fs->dirsteer) {
141 : : iq = 0;
142 : : } else {
143 : : /*
144 : : * If the iq id is greater than the number of qsets,
145 : : * then assume it is an absolute qid.
146 : : */
147 [ # # # # ]: 0 : if (fs->iq < pi->n_rx_qsets)
148 : 0 : iq = adapter->sge.ethrxq[pi->first_rxqset +
149 : 0 : fs->iq].rspq.abs_id;
150 : : else
151 : 0 : iq = fs->iq;
152 : : }
153 : :
154 : : return iq;
155 : : }
156 : :
157 : : /* Return an error number if the indicated filter isn't writable ... */
158 : : static int writable_filter(struct filter_entry *f)
159 : : {
160 [ # # # # ]: 0 : if (f->locked)
161 : : return -EPERM;
162 [ # # # # : 0 : if (f->pending)
# # ]
163 : : return -EBUSY;
164 : :
165 : : return 0;
166 : : }
167 : :
168 : : /**
169 : : * Send CPL_SET_TCB_FIELD message
170 : : */
171 : 0 : static void set_tcb_field(struct adapter *adapter, unsigned int ftid,
172 : : u16 word, u64 mask, u64 val, int no_reply)
173 : : {
174 : : struct rte_mbuf *mbuf;
175 : : struct cpl_set_tcb_field *req;
176 : : struct sge_ctrl_txq *ctrlq;
177 : :
178 : 0 : ctrlq = &adapter->sge.ctrlq[0];
179 : 0 : mbuf = rte_pktmbuf_alloc(ctrlq->mb_pool);
180 [ # # ]: 0 : WARN_ON(!mbuf);
181 : :
182 : 0 : mbuf->data_len = sizeof(*req);
183 : 0 : mbuf->pkt_len = mbuf->data_len;
184 : :
185 [ # # ]: 0 : req = rte_pktmbuf_mtod(mbuf, struct cpl_set_tcb_field *);
186 : : memset(req, 0, sizeof(*req));
187 [ # # # # ]: 0 : INIT_TP_WR_MIT_CPL(req, CPL_SET_TCB_FIELD, ftid);
188 [ # # ]: 0 : req->reply_ctrl = cpu_to_be16(V_REPLY_CHAN(0) |
189 : : V_QUEUENO(adapter->sge.fw_evtq.abs_id) |
190 : : V_NO_REPLY(no_reply));
191 [ # # ]: 0 : req->word_cookie = cpu_to_be16(V_WORD(word) | V_COOKIE(ftid));
192 [ # # ]: 0 : req->mask = cpu_to_be64(mask);
193 [ # # ]: 0 : req->val = cpu_to_be64(val);
194 : :
195 : 0 : t4_mgmt_tx(ctrlq, mbuf);
196 : 0 : }
197 : :
198 : : /**
199 : : * Set one of the t_flags bits in the TCB.
200 : : */
201 : : static void set_tcb_tflag(struct adapter *adap, unsigned int ftid,
202 : : unsigned int bit_pos, unsigned int val, int no_reply)
203 : : {
204 : 0 : set_tcb_field(adap, ftid, W_TCB_T_FLAGS, 1ULL << bit_pos,
205 : : (unsigned long long)val << bit_pos, no_reply);
206 : 0 : }
207 : :
208 : : /**
209 : : * Build a CPL_SET_TCB_FIELD message as payload of a ULP_TX_PKT command.
210 : : */
211 : 0 : static inline void mk_set_tcb_field_ulp(struct filter_entry *f,
212 : : struct cpl_set_tcb_field *req,
213 : : unsigned int word,
214 : : u64 mask, u64 val, u8 cookie,
215 : : int no_reply)
216 : : {
217 : : struct ulp_txpkt *txpkt = (struct ulp_txpkt *)req;
218 : : struct ulptx_idata *sc = (struct ulptx_idata *)(txpkt + 1);
219 : :
220 : 0 : txpkt->cmd_dest = cpu_to_be32(V_ULPTX_CMD(ULP_TX_PKT) |
221 : : V_ULP_TXPKT_DEST(0));
222 : 0 : txpkt->len = cpu_to_be32(DIV_ROUND_UP(sizeof(*req), 16));
223 : 0 : sc->cmd_more = cpu_to_be32(V_ULPTX_CMD(ULP_TX_SC_IMM));
224 : 0 : sc->len = cpu_to_be32(sizeof(*req) - sizeof(struct work_request_hdr));
225 [ # # ]: 0 : OPCODE_TID(req) = cpu_to_be32(MK_OPCODE_TID(CPL_SET_TCB_FIELD, f->tid));
226 [ # # ]: 0 : req->reply_ctrl = cpu_to_be16(V_NO_REPLY(no_reply) | V_REPLY_CHAN(0) |
227 : : V_QUEUENO(0));
228 [ # # ]: 0 : req->word_cookie = cpu_to_be16(V_WORD(word) | V_COOKIE(cookie));
229 [ # # ]: 0 : req->mask = cpu_to_be64(mask);
230 [ # # ]: 0 : req->val = cpu_to_be64(val);
231 : : sc = (struct ulptx_idata *)(req + 1);
232 : 0 : sc->cmd_more = cpu_to_be32(V_ULPTX_CMD(ULP_TX_SC_NOOP));
233 : 0 : sc->len = cpu_to_be32(0);
234 : 0 : }
235 : :
236 : : /**
237 : : * IPv6 requires 2 slots on T6 and 4 slots for cards below T6.
238 : : * IPv4 requires only 1 slot on all cards.
239 : : */
240 : 0 : u8 cxgbe_filter_slots(struct adapter *adap, u8 family)
241 : : {
242 [ # # ]: 0 : if (family == FILTER_TYPE_IPV6) {
243 [ # # ]: 0 : if (CHELSIO_CHIP_VERSION(adap->params.chip) < CHELSIO_T6)
244 : : return 4;
245 : :
246 : 0 : return 2;
247 : : }
248 : :
249 : : return 1;
250 : : }
251 : :
252 : : /**
253 : : * Check if entries are already filled.
254 : : */
255 : 0 : bool cxgbe_is_filter_set(struct tid_info *t, u32 fidx, u8 nentries)
256 : : {
257 : : bool result = FALSE;
258 : : u32 i;
259 : :
260 : : /* Ensure there's enough slots available. */
261 : 0 : t4_os_lock(&t->ftid_lock);
262 [ # # ]: 0 : for (i = fidx; i < fidx + nentries; i++) {
263 [ # # ]: 0 : if (rte_bitmap_get(t->ftid_bmap, i)) {
264 : : result = TRUE;
265 : : break;
266 : : }
267 : : }
268 : : t4_os_unlock(&t->ftid_lock);
269 : 0 : return result;
270 : : }
271 : :
272 : : /**
273 : : * Allocate available free entries.
274 : : */
275 : 0 : int cxgbe_alloc_ftid(struct adapter *adap, u8 nentries)
276 : : {
277 : : struct tid_info *t = &adap->tids;
278 : : int pos;
279 : 0 : int size = t->nftids;
280 : :
281 : 0 : t4_os_lock(&t->ftid_lock);
282 [ # # ]: 0 : if (nentries > 1)
283 : 0 : pos = cxgbe_bitmap_find_free_region(t->ftid_bmap, size,
284 : : nentries);
285 : : else
286 : 0 : pos = cxgbe_find_first_zero_bit(t->ftid_bmap, size);
287 : : t4_os_unlock(&t->ftid_lock);
288 : :
289 [ # # ]: 0 : return pos < size ? pos : -1;
290 : : }
291 : :
292 : : /**
293 : : * Clear a filter and release any of its resources that we own. This also
294 : : * clears the filter's "pending" status.
295 : : */
296 : 0 : static void clear_filter(struct filter_entry *f)
297 : : {
298 [ # # ]: 0 : struct port_info *pi = ethdev2pinfo(f->dev);
299 : :
300 [ # # ]: 0 : if (f->clipt)
301 : 0 : cxgbe_clip_release(f->dev, f->clipt);
302 : :
303 [ # # ]: 0 : if (f->l2t)
304 : 0 : cxgbe_l2t_release(f->l2t);
305 : :
306 [ # # ]: 0 : if (f->fs.mask.macidx)
307 : 0 : cxgbe_mpstcam_remove(pi, f->fs.val.macidx);
308 : :
309 [ # # ]: 0 : if (f->smt)
310 : 0 : cxgbe_smt_release(f->smt);
311 : :
312 : : /* The zeroing of the filter rule below clears the filter valid,
313 : : * pending, locked flags etc. so it's all we need for
314 : : * this operation.
315 : : */
316 : : memset(f, 0, sizeof(*f));
317 : 0 : }
318 : :
319 : : /**
320 : : * Construct hash filter ntuple.
321 : : */
322 : 0 : static u64 hash_filter_ntuple(const struct filter_entry *f)
323 : : {
324 [ # # ]: 0 : struct adapter *adap = ethdev2adap(f->dev);
325 : : struct tp_params *tp = &adap->params.tp;
326 : : u64 ntuple = 0;
327 : : u16 tcp_proto = IPPROTO_TCP; /* TCP Protocol Number */
328 : :
329 [ # # # # ]: 0 : if (tp->port_shift >= 0 && f->fs.mask.iport)
330 : 0 : ntuple |= (u64)f->fs.val.iport << tp->port_shift;
331 : :
332 [ # # ]: 0 : if (tp->protocol_shift >= 0) {
333 [ # # ]: 0 : if (!f->fs.val.proto)
334 : 0 : ntuple |= (u64)tcp_proto << tp->protocol_shift;
335 : : else
336 : 0 : ntuple |= (u64)f->fs.val.proto << tp->protocol_shift;
337 : : }
338 : :
339 [ # # # # ]: 0 : if (tp->ethertype_shift >= 0 && f->fs.mask.ethtype)
340 : 0 : ntuple |= (u64)(f->fs.val.ethtype) << tp->ethertype_shift;
341 [ # # # # ]: 0 : if (tp->macmatch_shift >= 0 && f->fs.mask.macidx)
342 : 0 : ntuple |= (u64)(f->fs.val.macidx) << tp->macmatch_shift;
343 [ # # # # ]: 0 : if (tp->vlan_shift >= 0 && f->fs.mask.ivlan)
344 : 0 : ntuple |= (u64)(F_FT_VLAN_VLD | f->fs.val.ivlan) <<
345 : : tp->vlan_shift;
346 [ # # ]: 0 : if (tp->vnic_shift >= 0) {
347 [ # # # # ]: 0 : if ((adap->params.tp.ingress_config & F_VNIC) &&
348 : : f->fs.mask.pfvf_vld)
349 : 0 : ntuple |= (u64)(f->fs.val.pfvf_vld << 16 |
350 : 0 : f->fs.val.pf << 13 | f->fs.val.vf) <<
351 : : tp->vnic_shift;
352 [ # # # # ]: 0 : else if (!(adap->params.tp.ingress_config & F_VNIC) &&
353 : : f->fs.mask.ovlan_vld)
354 : 0 : ntuple |= (u64)(f->fs.val.ovlan_vld << 16 |
355 : 0 : f->fs.val.ovlan) << tp->vnic_shift;
356 : : }
357 [ # # # # ]: 0 : if (tp->tos_shift >= 0 && f->fs.mask.tos)
358 : 0 : ntuple |= (u64)f->fs.val.tos << tp->tos_shift;
359 : :
360 : 0 : return ntuple;
361 : : }
362 : :
363 : : /**
364 : : * Build a CPL_ABORT_REQ message as payload of a ULP_TX_PKT command.
365 : : */
366 : 0 : static void mk_abort_req_ulp(struct cpl_abort_req *abort_req,
367 : : unsigned int tid)
368 : : {
369 : : struct ulp_txpkt *txpkt = (struct ulp_txpkt *)abort_req;
370 : : struct ulptx_idata *sc = (struct ulptx_idata *)(txpkt + 1);
371 : :
372 : 0 : txpkt->cmd_dest = cpu_to_be32(V_ULPTX_CMD(ULP_TX_PKT) |
373 : : V_ULP_TXPKT_DEST(0));
374 : 0 : txpkt->len = cpu_to_be32(DIV_ROUND_UP(sizeof(*abort_req), 16));
375 : 0 : sc->cmd_more = cpu_to_be32(V_ULPTX_CMD(ULP_TX_SC_IMM));
376 : 0 : sc->len = cpu_to_be32(sizeof(*abort_req) -
377 : : sizeof(struct work_request_hdr));
378 [ # # ]: 0 : OPCODE_TID(abort_req) = cpu_to_be32(MK_OPCODE_TID(CPL_ABORT_REQ, tid));
379 : 0 : abort_req->rsvd0 = cpu_to_be32(0);
380 : 0 : abort_req->rsvd1 = 0;
381 : 0 : abort_req->cmd = CPL_ABORT_NO_RST;
382 : : sc = (struct ulptx_idata *)(abort_req + 1);
383 : 0 : sc->cmd_more = cpu_to_be32(V_ULPTX_CMD(ULP_TX_SC_NOOP));
384 : 0 : sc->len = cpu_to_be32(0);
385 : 0 : }
386 : :
387 : : /**
388 : : * Build a CPL_ABORT_RPL message as payload of a ULP_TX_PKT command.
389 : : */
390 : 0 : static void mk_abort_rpl_ulp(struct cpl_abort_rpl *abort_rpl,
391 : : unsigned int tid)
392 : : {
393 : : struct ulp_txpkt *txpkt = (struct ulp_txpkt *)abort_rpl;
394 : : struct ulptx_idata *sc = (struct ulptx_idata *)(txpkt + 1);
395 : :
396 : 0 : txpkt->cmd_dest = cpu_to_be32(V_ULPTX_CMD(ULP_TX_PKT) |
397 : : V_ULP_TXPKT_DEST(0));
398 : 0 : txpkt->len = cpu_to_be32(DIV_ROUND_UP(sizeof(*abort_rpl), 16));
399 : 0 : sc->cmd_more = cpu_to_be32(V_ULPTX_CMD(ULP_TX_SC_IMM));
400 : 0 : sc->len = cpu_to_be32(sizeof(*abort_rpl) -
401 : : sizeof(struct work_request_hdr));
402 [ # # ]: 0 : OPCODE_TID(abort_rpl) = cpu_to_be32(MK_OPCODE_TID(CPL_ABORT_RPL, tid));
403 : 0 : abort_rpl->rsvd0 = cpu_to_be32(0);
404 : 0 : abort_rpl->rsvd1 = 0;
405 : 0 : abort_rpl->cmd = CPL_ABORT_NO_RST;
406 : : sc = (struct ulptx_idata *)(abort_rpl + 1);
407 : 0 : sc->cmd_more = cpu_to_be32(V_ULPTX_CMD(ULP_TX_SC_NOOP));
408 : 0 : sc->len = cpu_to_be32(0);
409 : 0 : }
410 : :
411 : : /**
412 : : * Delete the specified hash filter.
413 : : */
414 [ # # ]: 0 : static int cxgbe_del_hash_filter(struct rte_eth_dev *dev,
415 : : unsigned int filter_id,
416 : : struct filter_ctx *ctx)
417 : : {
418 : : struct adapter *adapter = ethdev2adap(dev);
419 : : struct tid_info *t = &adapter->tids;
420 : : struct filter_entry *f;
421 : : struct sge_ctrl_txq *ctrlq;
422 : 0 : unsigned int port_id = ethdev2pinfo(dev)->port_id;
423 : : int ret;
424 : :
425 [ # # ]: 0 : if (filter_id > adapter->tids.ntids)
426 : : return -E2BIG;
427 : :
428 : : f = lookup_tid(t, filter_id);
429 [ # # ]: 0 : if (!f) {
430 : 0 : dev_err(adapter, "%s: no filter entry for filter_id = %d\n",
431 : : __func__, filter_id);
432 : 0 : return -EINVAL;
433 : : }
434 : :
435 : : ret = writable_filter(f);
436 : : if (ret)
437 : 0 : return ret;
438 : :
439 [ # # ]: 0 : if (f->valid) {
440 : : unsigned int wrlen;
441 : : struct rte_mbuf *mbuf;
442 : : struct work_request_hdr *wr;
443 : : struct ulptx_idata *aligner;
444 : : struct cpl_set_tcb_field *req;
445 : : struct cpl_abort_req *abort_req;
446 : : struct cpl_abort_rpl *abort_rpl;
447 : :
448 : 0 : f->ctx = ctx;
449 : 0 : f->pending = 1;
450 : :
451 : : wrlen = cxgbe_roundup(sizeof(*wr) +
452 : : (sizeof(*req) + sizeof(*aligner)) +
453 : : sizeof(*abort_req) + sizeof(*abort_rpl),
454 : : 16);
455 : :
456 : 0 : ctrlq = &adapter->sge.ctrlq[port_id];
457 : 0 : mbuf = rte_pktmbuf_alloc(ctrlq->mb_pool);
458 [ # # ]: 0 : if (!mbuf) {
459 : 0 : dev_err(adapter, "%s: could not allocate skb ..\n",
460 : : __func__);
461 : 0 : goto out_err;
462 : : }
463 : :
464 : 0 : mbuf->data_len = wrlen;
465 : 0 : mbuf->pkt_len = mbuf->data_len;
466 : :
467 : 0 : req = rte_pktmbuf_mtod(mbuf, struct cpl_set_tcb_field *);
468 : 0 : INIT_ULPTX_WR(req, wrlen, 0, 0);
469 : : wr = (struct work_request_hdr *)req;
470 : 0 : wr++;
471 : : req = (struct cpl_set_tcb_field *)wr;
472 : 0 : mk_set_tcb_field_ulp(f, req, W_TCB_RSS_INFO,
473 : : V_TCB_RSS_INFO(M_TCB_RSS_INFO),
474 : 0 : V_TCB_RSS_INFO(adapter->sge.fw_evtq.abs_id),
475 : : 0, 1);
476 : : aligner = (struct ulptx_idata *)(req + 1);
477 : 0 : abort_req = (struct cpl_abort_req *)(aligner + 1);
478 : 0 : mk_abort_req_ulp(abort_req, f->tid);
479 : 0 : abort_rpl = (struct cpl_abort_rpl *)(abort_req + 1);
480 : 0 : mk_abort_rpl_ulp(abort_rpl, f->tid);
481 : 0 : t4_mgmt_tx(ctrlq, mbuf);
482 : : }
483 : : return 0;
484 : :
485 : : out_err:
486 : 0 : return -ENOMEM;
487 : : }
488 : :
489 : : /**
490 : : * Build a ACT_OPEN_REQ6 message for setting IPv6 hash filter.
491 : : */
492 : 0 : static void mk_act_open_req6(struct filter_entry *f, struct rte_mbuf *mbuf,
493 : : unsigned int qid_filterid, struct adapter *adap)
494 : : {
495 : : struct cpl_t6_act_open_req6 *req = NULL;
496 : : u64 local_lo, local_hi, peer_lo, peer_hi;
497 : : u32 *lip = (u32 *)f->fs.val.lip;
498 : : u32 *fip = (u32 *)f->fs.val.fip;
499 : :
500 [ # # ]: 0 : switch (CHELSIO_CHIP_VERSION(adap->params.chip)) {
501 : 0 : case CHELSIO_T6:
502 : 0 : req = rte_pktmbuf_mtod(mbuf, struct cpl_t6_act_open_req6 *);
503 : :
504 : 0 : INIT_TP_WR(req, 0);
505 : : break;
506 : 0 : default:
507 : 0 : dev_err(adap, "%s: unsupported chip type!\n", __func__);
508 : 0 : return;
509 : : }
510 : :
511 : 0 : local_hi = ((u64)lip[1]) << 32 | lip[0];
512 : 0 : local_lo = ((u64)lip[3]) << 32 | lip[2];
513 : 0 : peer_hi = ((u64)fip[1]) << 32 | fip[0];
514 : 0 : peer_lo = ((u64)fip[3]) << 32 | fip[2];
515 : :
516 [ # # ]: 0 : OPCODE_TID(req) = cpu_to_be32(MK_OPCODE_TID(CPL_ACT_OPEN_REQ6,
517 : : qid_filterid));
518 [ # # ]: 0 : req->local_port = cpu_to_be16(f->fs.val.lport);
519 [ # # ]: 0 : req->peer_port = cpu_to_be16(f->fs.val.fport);
520 : 0 : req->local_ip_hi = local_hi;
521 : 0 : req->local_ip_lo = local_lo;
522 : 0 : req->peer_ip_hi = peer_hi;
523 : 0 : req->peer_ip_lo = peer_lo;
524 [ # # ]: 0 : req->opt0 = cpu_to_be64(V_NAGLE(f->fs.newvlan == VLAN_REMOVE ||
525 : : f->fs.newvlan == VLAN_REWRITE) |
526 : : V_DELACK(f->fs.hitcnts) |
527 : : V_L2T_IDX(f->l2t ? f->l2t->idx : 0) |
528 : : V_SMAC_SEL((cxgbe_port_viid(f->dev) & 0x7F)
529 : : << 1) |
530 : : V_TX_CHAN(f->fs.eport) |
531 : : V_ULP_MODE(ULP_MODE_NONE) |
532 : : F_TCAM_BYPASS | F_NON_OFFLOAD);
533 : 0 : req->params = cpu_to_be64(V_FILTER_TUPLE(hash_filter_ntuple(f)));
534 [ # # # # ]: 0 : req->opt2 = cpu_to_be32(F_RSS_QUEUE_VALID |
535 : : V_RSS_QUEUE(f->fs.iq) |
536 : : F_T5_OPT_2_VALID |
537 : : F_RX_CHANNEL |
538 : : V_SACK_EN(f->fs.swapmac) |
539 : : V_CONG_CNTRL((f->fs.action == FILTER_DROP) |
540 : : (f->fs.dirsteer << 1)) |
541 : : V_CCTRL_ECN(f->fs.action == FILTER_SWITCH));
542 : : }
543 : :
544 : : /**
545 : : * Build a ACT_OPEN_REQ message for setting IPv4 hash filter.
546 : : */
547 : 0 : static void mk_act_open_req(struct filter_entry *f, struct rte_mbuf *mbuf,
548 : : unsigned int qid_filterid, struct adapter *adap)
549 : : {
550 : : struct cpl_t6_act_open_req *req = NULL;
551 : :
552 [ # # ]: 0 : switch (CHELSIO_CHIP_VERSION(adap->params.chip)) {
553 : 0 : case CHELSIO_T6:
554 : 0 : req = rte_pktmbuf_mtod(mbuf, struct cpl_t6_act_open_req *);
555 : :
556 : 0 : INIT_TP_WR(req, 0);
557 : : break;
558 : 0 : default:
559 : 0 : dev_err(adap, "%s: unsupported chip type!\n", __func__);
560 : 0 : return;
561 : : }
562 : :
563 [ # # ]: 0 : OPCODE_TID(req) = cpu_to_be32(MK_OPCODE_TID(CPL_ACT_OPEN_REQ,
564 : : qid_filterid));
565 [ # # ]: 0 : req->local_port = cpu_to_be16(f->fs.val.lport);
566 [ # # ]: 0 : req->peer_port = cpu_to_be16(f->fs.val.fport);
567 : 0 : req->local_ip = f->fs.val.lip[0] | f->fs.val.lip[1] << 8 |
568 : 0 : f->fs.val.lip[2] << 16 | f->fs.val.lip[3] << 24;
569 : 0 : req->peer_ip = f->fs.val.fip[0] | f->fs.val.fip[1] << 8 |
570 : 0 : f->fs.val.fip[2] << 16 | f->fs.val.fip[3] << 24;
571 [ # # ]: 0 : req->opt0 = cpu_to_be64(V_NAGLE(f->fs.newvlan == VLAN_REMOVE ||
572 : : f->fs.newvlan == VLAN_REWRITE) |
573 : : V_DELACK(f->fs.hitcnts) |
574 : : V_L2T_IDX(f->l2t ? f->l2t->idx : 0) |
575 : : V_SMAC_SEL((cxgbe_port_viid(f->dev) & 0x7F)
576 : : << 1) |
577 : : V_TX_CHAN(f->fs.eport) |
578 : : V_ULP_MODE(ULP_MODE_NONE) |
579 : : F_TCAM_BYPASS | F_NON_OFFLOAD);
580 : 0 : req->params = cpu_to_be64(V_FILTER_TUPLE(hash_filter_ntuple(f)));
581 [ # # # # ]: 0 : req->opt2 = cpu_to_be32(F_RSS_QUEUE_VALID |
582 : : V_RSS_QUEUE(f->fs.iq) |
583 : : F_T5_OPT_2_VALID |
584 : : F_RX_CHANNEL |
585 : : V_SACK_EN(f->fs.swapmac) |
586 : : V_CONG_CNTRL((f->fs.action == FILTER_DROP) |
587 : : (f->fs.dirsteer << 1)) |
588 : : V_CCTRL_ECN(f->fs.action == FILTER_SWITCH));
589 : : }
590 : :
591 : : /**
592 : : * Set the specified hash filter.
593 : : */
594 : 0 : static int cxgbe_set_hash_filter(struct rte_eth_dev *dev,
595 : : struct ch_filter_specification *fs,
596 : : struct filter_ctx *ctx)
597 : : {
598 : : struct port_info *pi = ethdev2pinfo(dev);
599 : 0 : struct adapter *adapter = pi->adapter;
600 : 0 : struct tid_info *t = &adapter->tids;
601 : : struct filter_entry *f;
602 : : struct rte_mbuf *mbuf;
603 : : struct sge_ctrl_txq *ctrlq;
604 : : unsigned int iq;
605 : : int atid, size;
606 : : int ret = 0;
607 : :
608 : 0 : ret = cxgbe_validate_filter(adapter, fs);
609 [ # # ]: 0 : if (ret)
610 : : return ret;
611 : :
612 : : iq = get_filter_steerq(dev, fs);
613 : :
614 : 0 : ctrlq = &adapter->sge.ctrlq[pi->port_id];
615 : :
616 : 0 : f = t4_os_alloc(sizeof(*f));
617 [ # # ]: 0 : if (!f)
618 : : return -ENOMEM;
619 : :
620 : 0 : f->fs = *fs;
621 : 0 : f->ctx = ctx;
622 : 0 : f->dev = dev;
623 : 0 : f->fs.iq = iq;
624 : :
625 : : /* Allocate MPS TCAM entry to match Destination MAC. */
626 [ # # ]: 0 : if (f->fs.mask.macidx) {
627 : : int idx;
628 : :
629 : 0 : idx = cxgbe_mpstcam_alloc(pi, f->fs.val.dmac, f->fs.mask.dmac);
630 [ # # ]: 0 : if (idx <= 0) {
631 : : ret = -ENOMEM;
632 : 0 : goto out_err;
633 : : }
634 : :
635 : 0 : f->fs.val.macidx = idx;
636 : : }
637 : :
638 : : /*
639 : : * If the new filter requires loopback Destination MAC and/or VLAN
640 : : * rewriting then we need to allocate a Layer 2 Table (L2T) entry for
641 : : * the filter.
642 : : */
643 [ # # # # : 0 : if (f->fs.newdmac || f->fs.newvlan == VLAN_INSERT ||
# # ]
644 : : f->fs.newvlan == VLAN_REWRITE) {
645 : : /* allocate L2T entry for new filter */
646 : 0 : f->l2t = cxgbe_l2t_alloc_switching(dev, f->fs.vlan,
647 : 0 : f->fs.eport, f->fs.dmac);
648 [ # # ]: 0 : if (!f->l2t) {
649 : : ret = -ENOMEM;
650 : 0 : goto out_err;
651 : : }
652 : : }
653 : :
654 : : /* If the new filter requires Source MAC rewriting then we need to
655 : : * allocate a SMT entry for the filter
656 : : */
657 [ # # ]: 0 : if (f->fs.newsmac) {
658 : 0 : f->smt = cxgbe_smt_alloc_switching(f->dev, f->fs.smac);
659 [ # # ]: 0 : if (!f->smt) {
660 : : ret = -EAGAIN;
661 : 0 : goto out_err;
662 : : }
663 : : }
664 : :
665 : 0 : atid = cxgbe_alloc_atid(t, f);
666 [ # # ]: 0 : if (atid < 0)
667 : 0 : goto out_err;
668 : :
669 [ # # ]: 0 : if (f->fs.type == FILTER_TYPE_IPV6) {
670 : : /* IPv6 hash filter */
671 : 0 : f->clipt = cxgbe_clip_alloc(f->dev, (u32 *)&f->fs.val.lip);
672 [ # # ]: 0 : if (!f->clipt)
673 : 0 : goto free_atid;
674 : :
675 : : size = sizeof(struct cpl_t6_act_open_req6);
676 : 0 : mbuf = rte_pktmbuf_alloc(ctrlq->mb_pool);
677 [ # # ]: 0 : if (!mbuf) {
678 : : ret = -ENOMEM;
679 : 0 : goto free_atid;
680 : : }
681 : :
682 : 0 : mbuf->data_len = size;
683 : 0 : mbuf->pkt_len = mbuf->data_len;
684 : :
685 : 0 : mk_act_open_req6(f, mbuf,
686 : 0 : ((adapter->sge.fw_evtq.abs_id << 14) | atid),
687 : : adapter);
688 : : } else {
689 : : /* IPv4 hash filter */
690 : : size = sizeof(struct cpl_t6_act_open_req);
691 : 0 : mbuf = rte_pktmbuf_alloc(ctrlq->mb_pool);
692 [ # # ]: 0 : if (!mbuf) {
693 : : ret = -ENOMEM;
694 : 0 : goto free_atid;
695 : : }
696 : :
697 : 0 : mbuf->data_len = size;
698 : 0 : mbuf->pkt_len = mbuf->data_len;
699 : :
700 : 0 : mk_act_open_req(f, mbuf,
701 : 0 : ((adapter->sge.fw_evtq.abs_id << 14) | atid),
702 : : adapter);
703 : : }
704 : :
705 : 0 : f->pending = 1;
706 : 0 : t4_mgmt_tx(ctrlq, mbuf);
707 : 0 : return 0;
708 : :
709 : 0 : free_atid:
710 : 0 : cxgbe_free_atid(t, atid);
711 : :
712 : 0 : out_err:
713 : 0 : clear_filter(f);
714 : 0 : t4_os_free(f);
715 : 0 : return ret;
716 : : }
717 : :
718 : : /**
719 : : * t4_mk_filtdelwr - create a delete filter WR
720 : : * @adap: adapter context
721 : : * @ftid: the filter ID
722 : : * @wr: the filter work request to populate
723 : : * @qid: ingress queue to receive the delete notification
724 : : *
725 : : * Creates a filter work request to delete the supplied filter. If @qid is
726 : : * negative the delete notification is suppressed.
727 : : */
728 [ # # ]: 0 : static void t4_mk_filtdelwr(struct adapter *adap, unsigned int ftid,
729 : : struct fw_filter2_wr *wr, int qid)
730 : : {
731 : : memset(wr, 0, sizeof(*wr));
732 [ # # ]: 0 : if (adap->params.filter2_wr_support)
733 : 0 : wr->op_pkd = cpu_to_be32(V_FW_WR_OP(FW_FILTER2_WR));
734 : : else
735 : 0 : wr->op_pkd = cpu_to_be32(V_FW_WR_OP(FW_FILTER_WR));
736 : 0 : wr->len16_pkd = cpu_to_be32(V_FW_WR_LEN16(sizeof(*wr) / 16));
737 [ # # ]: 0 : wr->tid_to_iq = cpu_to_be32(V_FW_FILTER_WR_TID(ftid) |
738 : : V_FW_FILTER_WR_NOREPLY(qid < 0));
739 : 0 : wr->del_filter_to_l2tix = cpu_to_be32(F_FW_FILTER_WR_DEL_FILTER);
740 [ # # ]: 0 : if (qid >= 0)
741 : 0 : wr->rx_chan_rx_rpl_iq =
742 [ # # ]: 0 : cpu_to_be16(V_FW_FILTER_WR_RX_RPL_IQ(qid));
743 : 0 : }
744 : :
745 : : /**
746 : : * Create FW work request to delete the filter at a specified index
747 : : */
748 : 0 : static int del_filter_wr(struct rte_eth_dev *dev, unsigned int fidx)
749 : : {
750 : : struct adapter *adapter = ethdev2adap(dev);
751 : 0 : struct filter_entry *f = &adapter->tids.ftid_tab[fidx];
752 : : struct rte_mbuf *mbuf;
753 : : struct fw_filter2_wr *fwr;
754 : : struct sge_ctrl_txq *ctrlq;
755 : 0 : unsigned int port_id = ethdev2pinfo(dev)->port_id;
756 : :
757 : 0 : ctrlq = &adapter->sge.ctrlq[port_id];
758 : 0 : mbuf = rte_pktmbuf_alloc(ctrlq->mb_pool);
759 [ # # ]: 0 : if (!mbuf)
760 : : return -ENOMEM;
761 : :
762 : 0 : mbuf->data_len = sizeof(*fwr);
763 : 0 : mbuf->pkt_len = mbuf->data_len;
764 : :
765 : 0 : fwr = rte_pktmbuf_mtod(mbuf, struct fw_filter2_wr *);
766 : 0 : t4_mk_filtdelwr(adapter, f->tid, fwr, adapter->sge.fw_evtq.abs_id);
767 : :
768 : : /*
769 : : * Mark the filter as "pending" and ship off the Filter Work Request.
770 : : * When we get the Work Request Reply we'll clear the pending status.
771 : : */
772 : 0 : f->pending = 1;
773 : 0 : t4_mgmt_tx(ctrlq, mbuf);
774 : 0 : return 0;
775 : : }
776 : :
777 : 0 : static int set_filter_wr(struct rte_eth_dev *dev, unsigned int fidx)
778 : : {
779 : : struct adapter *adapter = ethdev2adap(dev);
780 : 0 : struct filter_entry *f = &adapter->tids.ftid_tab[fidx];
781 : : struct rte_mbuf *mbuf;
782 : : struct fw_filter2_wr *fwr;
783 : : struct sge_ctrl_txq *ctrlq;
784 : 0 : unsigned int port_id = ethdev2pinfo(dev)->port_id;
785 : : int ret;
786 : :
787 : 0 : ctrlq = &adapter->sge.ctrlq[port_id];
788 : 0 : mbuf = rte_pktmbuf_alloc(ctrlq->mb_pool);
789 [ # # ]: 0 : if (!mbuf) {
790 : : ret = -ENOMEM;
791 : 0 : goto out;
792 : : }
793 : :
794 : 0 : mbuf->data_len = sizeof(*fwr);
795 : 0 : mbuf->pkt_len = mbuf->data_len;
796 : :
797 [ # # ]: 0 : fwr = rte_pktmbuf_mtod(mbuf, struct fw_filter2_wr *);
798 : : memset(fwr, 0, sizeof(*fwr));
799 : :
800 : : /*
801 : : * Construct the work request to set the filter.
802 : : */
803 [ # # ]: 0 : if (adapter->params.filter2_wr_support)
804 : 0 : fwr->op_pkd = cpu_to_be32(V_FW_WR_OP(FW_FILTER2_WR));
805 : : else
806 : 0 : fwr->op_pkd = cpu_to_be32(V_FW_WR_OP(FW_FILTER_WR));
807 : 0 : fwr->len16_pkd = cpu_to_be32(V_FW_WR_LEN16(sizeof(*fwr) / 16));
808 : 0 : fwr->tid_to_iq =
809 [ # # ]: 0 : cpu_to_be32(V_FW_FILTER_WR_TID(f->tid) |
810 : : V_FW_FILTER_WR_RQTYPE(f->fs.type) |
811 : : V_FW_FILTER_WR_NOREPLY(0) |
812 : : V_FW_FILTER_WR_IQ(f->fs.iq));
813 : 0 : fwr->del_filter_to_l2tix =
814 [ # # # # : 0 : cpu_to_be32(V_FW_FILTER_WR_DROP(f->fs.action == FILTER_DROP) |
# # ]
815 : : V_FW_FILTER_WR_DIRSTEER(f->fs.dirsteer) |
816 : : V_FW_FILTER_WR_LPBK(f->fs.action == FILTER_SWITCH) |
817 : : V_FW_FILTER_WR_SMAC(f->fs.newsmac) |
818 : : V_FW_FILTER_WR_DMAC(f->fs.newdmac) |
819 : : V_FW_FILTER_WR_INSVLAN
820 : : (f->fs.newvlan == VLAN_INSERT ||
821 : : f->fs.newvlan == VLAN_REWRITE) |
822 : : V_FW_FILTER_WR_RMVLAN
823 : : (f->fs.newvlan == VLAN_REMOVE ||
824 : : f->fs.newvlan == VLAN_REWRITE) |
825 : : V_FW_FILTER_WR_HITCNTS(f->fs.hitcnts) |
826 : : V_FW_FILTER_WR_TXCHAN(f->fs.eport) |
827 : : V_FW_FILTER_WR_PRIO(f->fs.prio) |
828 : : V_FW_FILTER_WR_L2TIX(f->l2t ? f->l2t->idx : 0));
829 [ # # ]: 0 : fwr->ethtype = cpu_to_be16(f->fs.val.ethtype);
830 [ # # ]: 0 : fwr->ethtypem = cpu_to_be16(f->fs.mask.ethtype);
831 : 0 : fwr->frag_to_ovlan_vldm =
832 : 0 : (V_FW_FILTER_WR_IVLAN_VLD(f->fs.val.ivlan_vld) |
833 : 0 : V_FW_FILTER_WR_IVLAN_VLDM(f->fs.mask.ivlan_vld) |
834 : 0 : V_FW_FILTER_WR_OVLAN_VLD(f->fs.val.ovlan_vld) |
835 : 0 : V_FW_FILTER_WR_OVLAN_VLDM(f->fs.mask.ovlan_vld));
836 [ # # ]: 0 : fwr->smac_sel = f->smt ? f->smt->hw_idx : 0;
837 : 0 : fwr->rx_chan_rx_rpl_iq =
838 [ # # ]: 0 : cpu_to_be16(V_FW_FILTER_WR_RX_CHAN(0) |
839 : : V_FW_FILTER_WR_RX_RPL_IQ(adapter->sge.fw_evtq.abs_id
840 : : ));
841 : 0 : fwr->maci_to_matchtypem =
842 [ # # ]: 0 : cpu_to_be32(V_FW_FILTER_WR_MACI(f->fs.val.macidx) |
843 : : V_FW_FILTER_WR_MACIM(f->fs.mask.macidx) |
844 : : V_FW_FILTER_WR_PORT(f->fs.val.iport) |
845 : : V_FW_FILTER_WR_PORTM(f->fs.mask.iport));
846 : 0 : fwr->ptcl = f->fs.val.proto;
847 : 0 : fwr->ptclm = f->fs.mask.proto;
848 : 0 : fwr->ttyp = f->fs.val.tos;
849 : 0 : fwr->ttypm = f->fs.mask.tos;
850 [ # # ]: 0 : fwr->ivlan = cpu_to_be16(f->fs.val.ivlan);
851 [ # # ]: 0 : fwr->ivlanm = cpu_to_be16(f->fs.mask.ivlan);
852 [ # # ]: 0 : fwr->ovlan = cpu_to_be16(f->fs.val.ovlan);
853 [ # # ]: 0 : fwr->ovlanm = cpu_to_be16(f->fs.mask.ovlan);
854 [ # # ]: 0 : rte_memcpy(fwr->lip, f->fs.val.lip, sizeof(fwr->lip));
855 [ # # ]: 0 : rte_memcpy(fwr->lipm, f->fs.mask.lip, sizeof(fwr->lipm));
856 [ # # ]: 0 : rte_memcpy(fwr->fip, f->fs.val.fip, sizeof(fwr->fip));
857 [ # # ]: 0 : rte_memcpy(fwr->fipm, f->fs.mask.fip, sizeof(fwr->fipm));
858 [ # # ]: 0 : fwr->lp = cpu_to_be16(f->fs.val.lport);
859 [ # # ]: 0 : fwr->lpm = cpu_to_be16(f->fs.mask.lport);
860 [ # # ]: 0 : fwr->fp = cpu_to_be16(f->fs.val.fport);
861 [ # # ]: 0 : fwr->fpm = cpu_to_be16(f->fs.mask.fport);
862 : :
863 [ # # ]: 0 : if (adapter->params.filter2_wr_support) {
864 : 0 : fwr->filter_type_swapmac =
865 : 0 : V_FW_FILTER2_WR_SWAPMAC(f->fs.swapmac);
866 : 0 : fwr->natmode_to_ulp_type =
867 : 0 : V_FW_FILTER2_WR_ULP_TYPE(f->fs.nat_mode ?
868 : : ULP_MODE_TCPDDP :
869 [ # # ]: 0 : ULP_MODE_NONE) |
870 : 0 : V_FW_FILTER2_WR_NATMODE(f->fs.nat_mode);
871 [ # # ]: 0 : memcpy(fwr->newlip, f->fs.nat_lip, sizeof(fwr->newlip));
872 : 0 : memcpy(fwr->newfip, f->fs.nat_fip, sizeof(fwr->newfip));
873 [ # # ]: 0 : fwr->newlport = cpu_to_be16(f->fs.nat_lport);
874 [ # # ]: 0 : fwr->newfport = cpu_to_be16(f->fs.nat_fport);
875 : : }
876 : :
877 : : /*
878 : : * Mark the filter as "pending" and ship off the Filter Work Request.
879 : : * When we get the Work Request Reply we'll clear the pending status.
880 : : */
881 : 0 : f->pending = 1;
882 : 0 : t4_mgmt_tx(ctrlq, mbuf);
883 : 0 : return 0;
884 : :
885 : : out:
886 : 0 : return ret;
887 : : }
888 : :
889 : : /**
890 : : * Set the corresponding entries in the bitmap.
891 : : */
892 : 0 : static int cxgbe_set_ftid(struct tid_info *t, u32 fidx, u8 nentries)
893 : : {
894 : : u32 i;
895 : :
896 : 0 : t4_os_lock(&t->ftid_lock);
897 [ # # ]: 0 : if (rte_bitmap_get(t->ftid_bmap, fidx)) {
898 : : t4_os_unlock(&t->ftid_lock);
899 : 0 : return -EBUSY;
900 : : }
901 : :
902 [ # # ]: 0 : for (i = fidx; i < fidx + nentries; i++)
903 : 0 : rte_bitmap_set(t->ftid_bmap, i);
904 : : t4_os_unlock(&t->ftid_lock);
905 : 0 : return 0;
906 : : }
907 : :
908 : : /**
909 : : * Clear the corresponding entries in the bitmap.
910 : : */
911 : 0 : static void cxgbe_clear_ftid(struct tid_info *t, u32 fidx, u8 nentries)
912 : : {
913 : : u32 i;
914 : :
915 : 0 : t4_os_lock(&t->ftid_lock);
916 [ # # ]: 0 : for (i = fidx; i < fidx + nentries; i++)
917 : 0 : rte_bitmap_clear(t->ftid_bmap, i);
918 : : t4_os_unlock(&t->ftid_lock);
919 : 0 : }
920 : :
921 : : /**
922 : : * Check a delete filter request for validity and send it to the hardware.
923 : : * Return 0 on success, an error number otherwise. We attach any provided
924 : : * filter operation context to the internal filter specification in order to
925 : : * facilitate signaling completion of the operation.
926 : : */
927 : 0 : int cxgbe_del_filter(struct rte_eth_dev *dev, unsigned int filter_id,
928 : : struct ch_filter_specification *fs,
929 : : struct filter_ctx *ctx)
930 : : {
931 : 0 : struct port_info *pi = dev->data->dev_private;
932 [ # # ]: 0 : struct adapter *adapter = pi->adapter;
933 : : struct filter_entry *f;
934 : : unsigned int chip_ver;
935 : : u8 nentries;
936 : : int ret;
937 : :
938 [ # # # # ]: 0 : if (is_hashfilter(adapter) && fs->cap)
939 : 0 : return cxgbe_del_hash_filter(dev, filter_id, ctx);
940 : :
941 [ # # ]: 0 : if (filter_id >= adapter->tids.nftids)
942 : : return -ERANGE;
943 : :
944 : 0 : chip_ver = CHELSIO_CHIP_VERSION(adapter->params.chip);
945 : :
946 : : /*
947 : : * Ensure IPv6 filter id is aligned on the 2 slot boundary for T6,
948 : : * and 4 slot boundary for cards below T6.
949 : : */
950 [ # # ]: 0 : if (fs->type == FILTER_TYPE_IPV6) {
951 [ # # ]: 0 : if (chip_ver < CHELSIO_T6)
952 : 0 : filter_id &= ~(0x3);
953 : : else
954 : 0 : filter_id &= ~(0x1);
955 : : }
956 : :
957 : 0 : nentries = cxgbe_filter_slots(adapter, fs->type);
958 : 0 : ret = cxgbe_is_filter_set(&adapter->tids, filter_id, nentries);
959 [ # # ]: 0 : if (!ret) {
960 : 0 : dev_warn(adap, "%s: could not find filter entry: %u\n",
961 : : __func__, filter_id);
962 : 0 : return -EINVAL;
963 : : }
964 : :
965 [ # # ]: 0 : f = &adapter->tids.ftid_tab[filter_id];
966 : : ret = writable_filter(f);
967 : : if (ret)
968 : 0 : return ret;
969 : :
970 [ # # ]: 0 : if (f->valid) {
971 : 0 : f->ctx = ctx;
972 : 0 : cxgbe_clear_ftid(&adapter->tids,
973 : 0 : f->tid - adapter->tids.ftid_base,
974 : : nentries);
975 : 0 : return del_filter_wr(dev, filter_id);
976 : : }
977 : :
978 : : /*
979 : : * If the caller has passed in a Completion Context then we need to
980 : : * mark it as a successful completion so they don't stall waiting
981 : : * for it.
982 : : */
983 [ # # ]: 0 : if (ctx) {
984 : 0 : ctx->result = 0;
985 : : t4_complete(&ctx->completion);
986 : : }
987 : :
988 : : return 0;
989 : : }
990 : :
991 : : /**
992 : : * Check a Chelsio Filter Request for validity, convert it into our internal
993 : : * format and send it to the hardware. Return 0 on success, an error number
994 : : * otherwise. We attach any provided filter operation context to the internal
995 : : * filter specification in order to facilitate signaling completion of the
996 : : * operation.
997 : : */
998 [ # # ]: 0 : int cxgbe_set_filter(struct rte_eth_dev *dev, unsigned int filter_id,
999 : : struct ch_filter_specification *fs,
1000 : : struct filter_ctx *ctx)
1001 : : {
1002 : : struct port_info *pi = ethdev2pinfo(dev);
1003 : 0 : struct adapter *adapter = pi->adapter;
1004 [ # # ]: 0 : u8 nentries, bitoff[16] = {0};
1005 : : struct filter_entry *f;
1006 : : unsigned int chip_ver;
1007 : : unsigned int fidx, iq;
1008 : : u32 iconf;
1009 : : int ret;
1010 : :
1011 [ # # # # ]: 0 : if (is_hashfilter(adapter) && fs->cap)
1012 : 0 : return cxgbe_set_hash_filter(dev, fs, ctx);
1013 : :
1014 [ # # ]: 0 : if (filter_id >= adapter->tids.nftids)
1015 : : return -ERANGE;
1016 : :
1017 : 0 : chip_ver = CHELSIO_CHIP_VERSION(adapter->params.chip);
1018 : :
1019 : 0 : ret = cxgbe_validate_filter(adapter, fs);
1020 [ # # ]: 0 : if (ret)
1021 : : return ret;
1022 : :
1023 : : /*
1024 : : * IPv6 filters occupy four slots and must be aligned on four-slot
1025 : : * boundaries for T5. On T6, IPv6 filters occupy two-slots and
1026 : : * must be aligned on two-slot boundaries.
1027 : : *
1028 : : * IPv4 filters only occupy a single slot and have no alignment
1029 : : * requirements.
1030 : : */
1031 : : fidx = filter_id;
1032 [ # # ]: 0 : if (fs->type == FILTER_TYPE_IPV6) {
1033 [ # # ]: 0 : if (chip_ver < CHELSIO_T6)
1034 : 0 : fidx &= ~(0x3);
1035 : : else
1036 : 0 : fidx &= ~(0x1);
1037 : : }
1038 : :
1039 [ # # ]: 0 : if (fidx != filter_id)
1040 : : return -EINVAL;
1041 : :
1042 : 0 : nentries = cxgbe_filter_slots(adapter, fs->type);
1043 : 0 : ret = cxgbe_is_filter_set(&adapter->tids, filter_id, nentries);
1044 [ # # ]: 0 : if (ret)
1045 : : return -EBUSY;
1046 : :
1047 : : iq = get_filter_steerq(dev, fs);
1048 : :
1049 : : /*
1050 : : * Check to make sure that provided filter index is not
1051 : : * already in use by someone else
1052 : : */
1053 : 0 : f = &adapter->tids.ftid_tab[filter_id];
1054 [ # # ]: 0 : if (f->valid)
1055 : : return -EBUSY;
1056 : :
1057 : 0 : fidx = adapter->tids.ftid_base + filter_id;
1058 : 0 : ret = cxgbe_set_ftid(&adapter->tids, filter_id, nentries);
1059 [ # # ]: 0 : if (ret)
1060 : : return ret;
1061 : :
1062 : : /*
1063 : : * Check to make sure the filter requested is writable ...
1064 : : */
1065 : : ret = writable_filter(f);
1066 : : if (ret) {
1067 : : /* Clear the bits we have set above */
1068 : 0 : cxgbe_clear_ftid(&adapter->tids, filter_id, nentries);
1069 : 0 : return ret;
1070 : : }
1071 : :
1072 : : /*
1073 : : * Convert the filter specification into our internal format.
1074 : : * We copy the PF/VF specification into the Outer VLAN field
1075 : : * here so the rest of the code -- including the interface to
1076 : : * the firmware -- doesn't have to constantly do these checks.
1077 : : */
1078 : 0 : f->fs = *fs;
1079 : 0 : f->fs.iq = iq;
1080 : 0 : f->dev = dev;
1081 : :
1082 : : /* Allocate MPS TCAM entry to match Destination MAC. */
1083 [ # # ]: 0 : if (f->fs.mask.macidx) {
1084 : : int idx;
1085 : :
1086 : 0 : idx = cxgbe_mpstcam_alloc(pi, f->fs.val.dmac, f->fs.mask.dmac);
1087 [ # # ]: 0 : if (idx <= 0) {
1088 : : ret = -ENOMEM;
1089 : 0 : goto free_tid;
1090 : : }
1091 : :
1092 : 0 : f->fs.val.macidx = idx;
1093 : : }
1094 : :
1095 : : /* Allocate a clip table entry only if we have non-zero IPv6 address. */
1096 [ # # # # ]: 0 : if (chip_ver > CHELSIO_T5 && f->fs.type &&
1097 [ # # ]: 0 : memcmp(f->fs.val.lip, bitoff, sizeof(bitoff))) {
1098 : 0 : f->clipt = cxgbe_clip_alloc(dev, (u32 *)&f->fs.val.lip);
1099 [ # # ]: 0 : if (!f->clipt) {
1100 : : ret = -ENOMEM;
1101 : 0 : goto free_tid;
1102 : : }
1103 : : }
1104 : :
1105 : : /* If the new filter requires loopback Destination MAC and/or VLAN
1106 : : * rewriting then we need to allocate a Layer 2 Table (L2T) entry for
1107 : : * the filter.
1108 : : */
1109 [ # # ]: 0 : if (f->fs.newvlan || f->fs.newdmac) {
1110 : 0 : f->l2t = cxgbe_l2t_alloc_switching(f->dev, f->fs.vlan,
1111 : 0 : f->fs.eport, f->fs.dmac);
1112 [ # # ]: 0 : if (!f->l2t) {
1113 : : ret = -ENOMEM;
1114 : 0 : goto free_tid;
1115 : : }
1116 : : }
1117 : :
1118 : : /* If the new filter requires Source MAC rewriting then we need to
1119 : : * allocate a SMT entry for the filter
1120 : : */
1121 [ # # ]: 0 : if (f->fs.newsmac) {
1122 : 0 : f->smt = cxgbe_smt_alloc_switching(f->dev, f->fs.smac);
1123 [ # # ]: 0 : if (!f->smt) {
1124 : : ret = -ENOMEM;
1125 : 0 : goto free_tid;
1126 : : }
1127 : : }
1128 : :
1129 : 0 : iconf = adapter->params.tp.ingress_config;
1130 : :
1131 : : /* Either PFVF or OVLAN can be active, but not both
1132 : : * So, if PFVF is enabled, then overwrite the OVLAN
1133 : : * fields with PFVF fields before writing the spec
1134 : : * to hardware.
1135 : : */
1136 [ # # ]: 0 : if (iconf & F_VNIC) {
1137 : 0 : f->fs.val.ovlan = fs->val.pf << 13 | fs->val.vf;
1138 : 0 : f->fs.mask.ovlan = fs->mask.pf << 13 | fs->mask.vf;
1139 : 0 : f->fs.val.ovlan_vld = fs->val.pfvf_vld;
1140 : 0 : f->fs.mask.ovlan_vld = fs->mask.pfvf_vld;
1141 : : }
1142 : :
1143 : : /*
1144 : : * Attempt to set the filter. If we don't succeed, we clear
1145 : : * it and return the failure.
1146 : : */
1147 : 0 : f->ctx = ctx;
1148 : 0 : f->tid = fidx; /* Save the actual tid */
1149 : 0 : ret = set_filter_wr(dev, filter_id);
1150 [ # # ]: 0 : if (ret)
1151 : 0 : goto free_tid;
1152 : :
1153 : : return ret;
1154 : :
1155 : 0 : free_tid:
1156 : 0 : cxgbe_clear_ftid(&adapter->tids, filter_id, nentries);
1157 : 0 : clear_filter(f);
1158 : 0 : return ret;
1159 : : }
1160 : :
1161 : : /**
1162 : : * Handle a Hash filter write reply.
1163 : : */
1164 : 0 : void cxgbe_hash_filter_rpl(struct adapter *adap,
1165 : : const struct cpl_act_open_rpl *rpl)
1166 : : {
1167 : 0 : struct tid_info *t = &adap->tids;
1168 : : struct filter_entry *f;
1169 : : struct filter_ctx *ctx = NULL;
1170 [ # # ]: 0 : unsigned int tid = GET_TID(rpl);
1171 [ # # ]: 0 : unsigned int ftid = G_TID_TID(G_AOPEN_ATID
1172 : : (be32_to_cpu(rpl->atid_status)));
1173 [ # # ]: 0 : unsigned int status = G_AOPEN_STATUS(be32_to_cpu(rpl->atid_status));
1174 : :
1175 : : f = lookup_atid(t, ftid);
1176 [ # # ]: 0 : if (!f) {
1177 : 0 : dev_warn(adap, "%s: could not find filter entry: %d\n",
1178 : : __func__, ftid);
1179 : 0 : return;
1180 : : }
1181 : :
1182 : 0 : ctx = f->ctx;
1183 : 0 : f->ctx = NULL;
1184 : :
1185 [ # # ]: 0 : switch (status) {
1186 : 0 : case CPL_ERR_NONE: {
1187 : 0 : f->tid = tid;
1188 : 0 : f->pending = 0; /* asynchronous setup completed */
1189 : 0 : f->valid = 1;
1190 : :
1191 : 0 : cxgbe_insert_tid(t, f, f->tid, 0);
1192 : 0 : cxgbe_free_atid(t, ftid);
1193 [ # # ]: 0 : if (ctx) {
1194 : 0 : ctx->tid = f->tid;
1195 : 0 : ctx->result = 0;
1196 : : }
1197 [ # # ]: 0 : if (f->fs.hitcnts)
1198 : 0 : set_tcb_field(adap, tid,
1199 : : W_TCB_TIMESTAMP,
1200 : : V_TCB_TIMESTAMP(M_TCB_TIMESTAMP) |
1201 : : V_TCB_T_RTT_TS_RECENT_AGE
1202 : : (M_TCB_T_RTT_TS_RECENT_AGE),
1203 : : V_TCB_TIMESTAMP(0ULL) |
1204 : : V_TCB_T_RTT_TS_RECENT_AGE(0ULL),
1205 : : 1);
1206 [ # # ]: 0 : if (f->fs.newdmac)
1207 : : set_tcb_tflag(adap, tid, S_TF_CCTRL_ECE, 1, 1);
1208 [ # # ]: 0 : if (f->fs.newvlan == VLAN_INSERT ||
1209 : : f->fs.newvlan == VLAN_REWRITE)
1210 : : set_tcb_tflag(adap, tid, S_TF_CCTRL_RFR, 1, 1);
1211 [ # # ]: 0 : if (f->fs.newsmac) {
1212 : : set_tcb_tflag(adap, tid, S_TF_CCTRL_CWR, 1, 1);
1213 : 0 : set_tcb_field(adap, tid, W_TCB_SMAC_SEL,
1214 : : V_TCB_SMAC_SEL(M_TCB_SMAC_SEL),
1215 : 0 : V_TCB_SMAC_SEL(f->smt->hw_idx), 1);
1216 : : }
1217 : : break;
1218 : : }
1219 : 0 : default:
1220 : 0 : dev_warn(adap, "%s: filter creation failed with status = %u\n",
1221 : : __func__, status);
1222 : :
1223 [ # # ]: 0 : if (ctx) {
1224 [ # # ]: 0 : if (status == CPL_ERR_TCAM_FULL)
1225 : 0 : ctx->result = -EAGAIN;
1226 : : else
1227 : 0 : ctx->result = -EINVAL;
1228 : : }
1229 : :
1230 : 0 : cxgbe_free_atid(t, ftid);
1231 : 0 : clear_filter(f);
1232 : 0 : t4_os_free(f);
1233 : : }
1234 : :
1235 [ # # ]: 0 : if (ctx)
1236 : : t4_complete(&ctx->completion);
1237 : : }
1238 : :
1239 : : /**
1240 : : * Handle a LE-TCAM filter write/deletion reply.
1241 : : */
1242 : 0 : void cxgbe_filter_rpl(struct adapter *adap, const struct cpl_set_tcb_rpl *rpl)
1243 : : {
1244 : : struct filter_entry *f = NULL;
1245 [ # # ]: 0 : unsigned int tid = GET_TID(rpl);
1246 : 0 : int idx, max_fidx = adap->tids.nftids;
1247 : :
1248 : : /* Get the corresponding filter entry for this tid */
1249 [ # # ]: 0 : if (adap->tids.ftid_tab) {
1250 : : /* Check this in normal filter region */
1251 : 0 : idx = tid - adap->tids.ftid_base;
1252 [ # # ]: 0 : if (idx >= max_fidx)
1253 : : return;
1254 : :
1255 : 0 : f = &adap->tids.ftid_tab[idx];
1256 [ # # ]: 0 : if (f->tid != tid)
1257 : : return;
1258 : : }
1259 : :
1260 : : /* We found the filter entry for this tid */
1261 : : if (f) {
1262 : 0 : unsigned int ret = G_COOKIE(rpl->cookie);
1263 : : struct filter_ctx *ctx;
1264 : :
1265 : : /*
1266 : : * Pull off any filter operation context attached to the
1267 : : * filter.
1268 : : */
1269 : 0 : ctx = f->ctx;
1270 : 0 : f->ctx = NULL;
1271 : :
1272 [ # # ]: 0 : if (ret == FW_FILTER_WR_FLT_ADDED) {
1273 : 0 : f->pending = 0; /* asynchronous setup completed */
1274 : 0 : f->valid = 1;
1275 [ # # ]: 0 : if (ctx) {
1276 : 0 : ctx->tid = f->tid;
1277 : 0 : ctx->result = 0;
1278 : : }
1279 [ # # ]: 0 : } else if (ret == FW_FILTER_WR_FLT_DELETED) {
1280 : : /*
1281 : : * Clear the filter when we get confirmation from the
1282 : : * hardware that the filter has been deleted.
1283 : : */
1284 : 0 : clear_filter(f);
1285 [ # # ]: 0 : if (ctx)
1286 : 0 : ctx->result = 0;
1287 : : } else {
1288 : : /*
1289 : : * Something went wrong. Issue a warning about the
1290 : : * problem and clear everything out.
1291 : : */
1292 : 0 : dev_warn(adap, "filter %u setup failed with error %u\n",
1293 : : idx, ret);
1294 : 0 : clear_filter(f);
1295 [ # # ]: 0 : if (ctx)
1296 : 0 : ctx->result = -EINVAL;
1297 : : }
1298 : :
1299 [ # # ]: 0 : if (ctx)
1300 : : t4_complete(&ctx->completion);
1301 : : }
1302 : : }
1303 : :
1304 : : /*
1305 : : * Retrieve the packet count for the specified filter.
1306 : : */
1307 : 0 : int cxgbe_get_filter_count(struct adapter *adapter, unsigned int fidx,
1308 : : u64 *c, int hash, bool get_byte)
1309 : : {
1310 : : struct filter_entry *f;
1311 : : unsigned int tcb_base, tcbaddr;
1312 : : int ret;
1313 : :
1314 : : tcb_base = t4_read_reg(adapter, A_TP_CMM_TCB_BASE);
1315 [ # # # # ]: 0 : if (is_hashfilter(adapter) && hash) {
1316 [ # # ]: 0 : if (fidx < adapter->tids.ntids) {
1317 : 0 : f = adapter->tids.tid_tab[fidx];
1318 [ # # ]: 0 : if (!f)
1319 : : return -EINVAL;
1320 : :
1321 [ # # ]: 0 : if (is_t5(adapter->params.chip)) {
1322 : 0 : *c = 0;
1323 : 0 : return 0;
1324 : : }
1325 : 0 : tcbaddr = tcb_base + (fidx * TCB_SIZE);
1326 : 0 : goto get_count;
1327 : : } else {
1328 : : return -ERANGE;
1329 : : }
1330 : : } else {
1331 [ # # ]: 0 : if (fidx >= adapter->tids.nftids)
1332 : : return -ERANGE;
1333 : :
1334 : 0 : f = &adapter->tids.ftid_tab[fidx];
1335 [ # # ]: 0 : if (!f->valid)
1336 : : return -EINVAL;
1337 : :
1338 : 0 : tcbaddr = tcb_base + f->tid * TCB_SIZE;
1339 : : }
1340 : :
1341 : : f = &adapter->tids.ftid_tab[fidx];
1342 : : if (!f->valid)
1343 : : return -EINVAL;
1344 : :
1345 : 0 : get_count:
1346 [ # # # # ]: 0 : if (is_t5(adapter->params.chip) || is_t6(adapter->params.chip)) {
1347 : : /*
1348 : : * For T5, the Filter Packet Hit Count is maintained as a
1349 : : * 32-bit Big Endian value in the TCB field {timestamp}.
1350 : : * Similar to the craziness above, instead of the filter hit
1351 : : * count showing up at offset 20 ((W_TCB_TIMESTAMP == 5) *
1352 : : * sizeof(u32)), it actually shows up at offset 24. Whacky.
1353 : : */
1354 [ # # ]: 0 : if (get_byte) {
1355 : : unsigned int word_offset = 4;
1356 : : __be64 be64_byte_count;
1357 : :
1358 : 0 : t4_os_lock(&adapter->win0_lock);
1359 : 0 : ret = t4_memory_rw(adapter, MEMWIN_NIC, MEM_EDC0,
1360 : : tcbaddr +
1361 : : (word_offset * sizeof(__be32)),
1362 : : sizeof(be64_byte_count),
1363 : : &be64_byte_count,
1364 : : T4_MEMORY_READ);
1365 : : t4_os_unlock(&adapter->win0_lock);
1366 [ # # ]: 0 : if (ret < 0)
1367 : 0 : return ret;
1368 [ # # ]: 0 : *c = be64_to_cpu(be64_byte_count);
1369 : : } else {
1370 : : unsigned int word_offset = 6;
1371 : : __be32 be32_count;
1372 : :
1373 : 0 : t4_os_lock(&adapter->win0_lock);
1374 : 0 : ret = t4_memory_rw(adapter, MEMWIN_NIC, MEM_EDC0,
1375 : : tcbaddr +
1376 : : (word_offset * sizeof(__be32)),
1377 : : sizeof(be32_count), &be32_count,
1378 : : T4_MEMORY_READ);
1379 : : t4_os_unlock(&adapter->win0_lock);
1380 [ # # ]: 0 : if (ret < 0)
1381 : 0 : return ret;
1382 [ # # ]: 0 : *c = (u64)be32_to_cpu(be32_count);
1383 : : }
1384 : : }
1385 : : return 0;
1386 : : }
1387 : :
1388 : : /*
1389 : : * Clear the packet count for the specified filter.
1390 : : */
1391 [ # # ]: 0 : int cxgbe_clear_filter_count(struct adapter *adapter, unsigned int fidx,
1392 : : int hash, bool clear_byte)
1393 : : {
1394 : : u64 tcb_mask = 0, tcb_val = 0;
1395 : : struct filter_entry *f = NULL;
1396 : : u16 tcb_word = 0;
1397 : :
1398 [ # # # # ]: 0 : if (is_hashfilter(adapter) && hash) {
1399 [ # # ]: 0 : if (fidx >= adapter->tids.ntids)
1400 : : return -ERANGE;
1401 : :
1402 : : /* No hitcounts supported for T5 hashfilters */
1403 [ # # ]: 0 : if (is_t5(adapter->params.chip))
1404 : : return 0;
1405 : :
1406 : 0 : f = adapter->tids.tid_tab[fidx];
1407 : : } else {
1408 [ # # ]: 0 : if (fidx >= adapter->tids.nftids)
1409 : : return -ERANGE;
1410 : :
1411 : 0 : f = &adapter->tids.ftid_tab[fidx];
1412 : : }
1413 : :
1414 [ # # # # ]: 0 : if (!f || !f->valid)
1415 : : return -EINVAL;
1416 : :
1417 : : tcb_word = W_TCB_TIMESTAMP;
1418 : : tcb_mask = V_TCB_TIMESTAMP(M_TCB_TIMESTAMP);
1419 : : tcb_val = V_TCB_TIMESTAMP(0ULL);
1420 : :
1421 : 0 : set_tcb_field(adapter, f->tid, tcb_word, tcb_mask, tcb_val, 1);
1422 : :
1423 [ # # ]: 0 : if (clear_byte) {
1424 : : tcb_word = W_TCB_T_RTT_TS_RECENT_AGE;
1425 : : tcb_mask =
1426 : : V_TCB_T_RTT_TS_RECENT_AGE(M_TCB_T_RTT_TS_RECENT_AGE) |
1427 : : V_TCB_T_RTSEQ_RECENT(M_TCB_T_RTSEQ_RECENT);
1428 : : tcb_val = V_TCB_T_RTT_TS_RECENT_AGE(0ULL) |
1429 : : V_TCB_T_RTSEQ_RECENT(0ULL);
1430 : :
1431 : 0 : set_tcb_field(adapter, f->tid, tcb_word, tcb_mask, tcb_val, 1);
1432 : : }
1433 : :
1434 : : return 0;
1435 : : }
1436 : :
1437 : : /**
1438 : : * Handle a Hash filter delete reply.
1439 : : */
1440 : 0 : void cxgbe_hash_del_filter_rpl(struct adapter *adap,
1441 : : const struct cpl_abort_rpl_rss *rpl)
1442 : : {
1443 : 0 : struct tid_info *t = &adap->tids;
1444 : : struct filter_entry *f;
1445 : : struct filter_ctx *ctx = NULL;
1446 [ # # ]: 0 : unsigned int tid = GET_TID(rpl);
1447 : :
1448 : : f = lookup_tid(t, tid);
1449 [ # # ]: 0 : if (!f) {
1450 : 0 : dev_warn(adap, "%s: could not find filter entry: %u\n",
1451 : : __func__, tid);
1452 : 0 : return;
1453 : : }
1454 : :
1455 : 0 : ctx = f->ctx;
1456 : :
1457 : 0 : clear_filter(f);
1458 : 0 : cxgbe_remove_tid(t, 0, tid, 0);
1459 : 0 : t4_os_free(f);
1460 : :
1461 [ # # ]: 0 : if (ctx) {
1462 : 0 : ctx->result = 0;
1463 : : t4_complete(&ctx->completion);
1464 : : }
1465 : : }
|