Commit | Line | Data |
---|---|---|
1f7ea1cd | 1 | // SPDX-License-Identifier: GPL-2.0 |
ba1124f5 | 2 | /* Copyright (C) 2021-2023, Intel Corporation. */ |
1f7ea1cd QZ |
3 | |
4 | #include "ice.h" | |
5 | #include "ice_base.h" | |
6 | #include "ice_lib.h" | |
7 | #include "ice_flow.h" | |
109aba47 | 8 | #include "ice_vf_lib_private.h" |
1f7ea1cd QZ |
9 | |
10 | #define to_fltr_conf_from_desc(p) \ | |
11 | container_of(p, struct virtchnl_fdir_fltr_conf, input) | |
12 | ||
ef9e4cc5 QZ |
13 | #define GTPU_TEID_OFFSET 4 |
14 | #define GTPU_EH_QFI_OFFSET 1 | |
15 | #define GTPU_EH_QFI_MASK 0x3F | |
213528fe QZ |
16 | #define PFCP_S_OFFSET 0 |
17 | #define PFCP_S_MASK 0x1 | |
18 | #define PFCP_PORT_NR 8805 | |
19 | ||
20 | #define FDIR_INSET_FLAG_ESP_S 0 | |
21 | #define FDIR_INSET_FLAG_ESP_M BIT_ULL(FDIR_INSET_FLAG_ESP_S) | |
22 | #define FDIR_INSET_FLAG_ESP_UDP BIT_ULL(FDIR_INSET_FLAG_ESP_S) | |
23 | #define FDIR_INSET_FLAG_ESP_IPSEC (0ULL << FDIR_INSET_FLAG_ESP_S) | |
ef9e4cc5 QZ |
24 | |
25 | enum ice_fdir_tunnel_type { | |
26 | ICE_FDIR_TUNNEL_TYPE_NONE = 0, | |
27 | ICE_FDIR_TUNNEL_TYPE_GTPU, | |
28 | ICE_FDIR_TUNNEL_TYPE_GTPU_EH, | |
29 | }; | |
30 | ||
1f7ea1cd QZ |
31 | struct virtchnl_fdir_fltr_conf { |
32 | struct ice_fdir_fltr input; | |
ef9e4cc5 | 33 | enum ice_fdir_tunnel_type ttype; |
213528fe | 34 | u64 inset_flag; |
d6218317 | 35 | u32 flow_id; |
1f7ea1cd QZ |
36 | }; |
37 | ||
38 | struct virtchnl_fdir_inset_map { | |
39 | enum virtchnl_proto_hdr_field field; | |
40 | enum ice_flow_field fld; | |
213528fe QZ |
41 | u64 flag; |
42 | u64 mask; | |
1f7ea1cd QZ |
43 | }; |
44 | ||
45 | static const struct virtchnl_fdir_inset_map fdir_inset_map[] = { | |
213528fe QZ |
46 | {VIRTCHNL_PROTO_HDR_ETH_ETHERTYPE, ICE_FLOW_FIELD_IDX_ETH_TYPE, 0, 0}, |
47 | {VIRTCHNL_PROTO_HDR_IPV4_SRC, ICE_FLOW_FIELD_IDX_IPV4_SA, 0, 0}, | |
48 | {VIRTCHNL_PROTO_HDR_IPV4_DST, ICE_FLOW_FIELD_IDX_IPV4_DA, 0, 0}, | |
49 | {VIRTCHNL_PROTO_HDR_IPV4_DSCP, ICE_FLOW_FIELD_IDX_IPV4_DSCP, 0, 0}, | |
50 | {VIRTCHNL_PROTO_HDR_IPV4_TTL, ICE_FLOW_FIELD_IDX_IPV4_TTL, 0, 0}, | |
51 | {VIRTCHNL_PROTO_HDR_IPV4_PROT, ICE_FLOW_FIELD_IDX_IPV4_PROT, 0, 0}, | |
52 | {VIRTCHNL_PROTO_HDR_IPV6_SRC, ICE_FLOW_FIELD_IDX_IPV6_SA, 0, 0}, | |
53 | {VIRTCHNL_PROTO_HDR_IPV6_DST, ICE_FLOW_FIELD_IDX_IPV6_DA, 0, 0}, | |
54 | {VIRTCHNL_PROTO_HDR_IPV6_TC, ICE_FLOW_FIELD_IDX_IPV6_DSCP, 0, 0}, | |
55 | {VIRTCHNL_PROTO_HDR_IPV6_HOP_LIMIT, ICE_FLOW_FIELD_IDX_IPV6_TTL, 0, 0}, | |
56 | {VIRTCHNL_PROTO_HDR_IPV6_PROT, ICE_FLOW_FIELD_IDX_IPV6_PROT, 0, 0}, | |
57 | {VIRTCHNL_PROTO_HDR_UDP_SRC_PORT, ICE_FLOW_FIELD_IDX_UDP_SRC_PORT, 0, 0}, | |
58 | {VIRTCHNL_PROTO_HDR_UDP_DST_PORT, ICE_FLOW_FIELD_IDX_UDP_DST_PORT, 0, 0}, | |
59 | {VIRTCHNL_PROTO_HDR_TCP_SRC_PORT, ICE_FLOW_FIELD_IDX_TCP_SRC_PORT, 0, 0}, | |
60 | {VIRTCHNL_PROTO_HDR_TCP_DST_PORT, ICE_FLOW_FIELD_IDX_TCP_DST_PORT, 0, 0}, | |
61 | {VIRTCHNL_PROTO_HDR_SCTP_SRC_PORT, ICE_FLOW_FIELD_IDX_SCTP_SRC_PORT, 0, 0}, | |
62 | {VIRTCHNL_PROTO_HDR_SCTP_DST_PORT, ICE_FLOW_FIELD_IDX_SCTP_DST_PORT, 0, 0}, | |
63 | {VIRTCHNL_PROTO_HDR_GTPU_IP_TEID, ICE_FLOW_FIELD_IDX_GTPU_IP_TEID, 0, 0}, | |
64 | {VIRTCHNL_PROTO_HDR_GTPU_EH_QFI, ICE_FLOW_FIELD_IDX_GTPU_EH_QFI, 0, 0}, | |
65 | {VIRTCHNL_PROTO_HDR_ESP_SPI, ICE_FLOW_FIELD_IDX_ESP_SPI, | |
66 | FDIR_INSET_FLAG_ESP_IPSEC, FDIR_INSET_FLAG_ESP_M}, | |
67 | {VIRTCHNL_PROTO_HDR_ESP_SPI, ICE_FLOW_FIELD_IDX_NAT_T_ESP_SPI, | |
68 | FDIR_INSET_FLAG_ESP_UDP, FDIR_INSET_FLAG_ESP_M}, | |
69 | {VIRTCHNL_PROTO_HDR_AH_SPI, ICE_FLOW_FIELD_IDX_AH_SPI, 0, 0}, | |
70 | {VIRTCHNL_PROTO_HDR_L2TPV3_SESS_ID, ICE_FLOW_FIELD_IDX_L2TPV3_SESS_ID, 0, 0}, | |
71 | {VIRTCHNL_PROTO_HDR_PFCP_S_FIELD, ICE_FLOW_FIELD_IDX_UDP_DST_PORT, 0, 0}, | |
1f7ea1cd QZ |
72 | }; |
73 | ||
74 | /** | |
75 | * ice_vc_fdir_param_check | |
76 | * @vf: pointer to the VF structure | |
77 | * @vsi_id: VF relative VSI ID | |
78 | * | |
79 | * Check for the valid VSI ID, PF's state and VF's state | |
80 | * | |
81 | * Return: 0 on success, and -EINVAL on error. | |
82 | */ | |
83 | static int | |
84 | ice_vc_fdir_param_check(struct ice_vf *vf, u16 vsi_id) | |
85 | { | |
86 | struct ice_pf *pf = vf->pf; | |
87 | ||
88 | if (!test_bit(ICE_FLAG_FD_ENA, pf->flags)) | |
89 | return -EINVAL; | |
90 | ||
91 | if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) | |
92 | return -EINVAL; | |
93 | ||
94 | if (!(vf->driver_caps & VIRTCHNL_VF_OFFLOAD_FDIR_PF)) | |
95 | return -EINVAL; | |
96 | ||
1f7ea1cd QZ |
97 | if (!ice_vc_isvalid_vsi_id(vf, vsi_id)) |
98 | return -EINVAL; | |
99 | ||
772dec64 | 100 | if (!ice_get_vf_vsi(vf)) |
1f7ea1cd QZ |
101 | return -EINVAL; |
102 | ||
103 | return 0; | |
104 | } | |
105 | ||
106 | /** | |
107 | * ice_vf_start_ctrl_vsi | |
108 | * @vf: pointer to the VF structure | |
109 | * | |
110 | * Allocate ctrl_vsi for the first time and open the ctrl_vsi port for VF | |
111 | * | |
112 | * Return: 0 on success, and other on error. | |
113 | */ | |
114 | static int ice_vf_start_ctrl_vsi(struct ice_vf *vf) | |
115 | { | |
116 | struct ice_pf *pf = vf->pf; | |
117 | struct ice_vsi *ctrl_vsi; | |
118 | struct device *dev; | |
119 | int err; | |
120 | ||
121 | dev = ice_pf_to_dev(pf); | |
122 | if (vf->ctrl_vsi_idx != ICE_NO_VSI) | |
123 | return -EEXIST; | |
124 | ||
125 | ctrl_vsi = ice_vf_ctrl_vsi_setup(vf); | |
126 | if (!ctrl_vsi) { | |
127 | dev_dbg(dev, "Could not setup control VSI for VF %d\n", | |
128 | vf->vf_id); | |
129 | return -ENOMEM; | |
130 | } | |
131 | ||
132 | err = ice_vsi_open_ctrl(ctrl_vsi); | |
133 | if (err) { | |
134 | dev_dbg(dev, "Could not open control VSI for VF %d\n", | |
135 | vf->vf_id); | |
136 | goto err_vsi_open; | |
137 | } | |
138 | ||
139 | return 0; | |
140 | ||
141 | err_vsi_open: | |
142 | ice_vsi_release(ctrl_vsi); | |
143 | if (vf->ctrl_vsi_idx != ICE_NO_VSI) { | |
144 | pf->vsi[vf->ctrl_vsi_idx] = NULL; | |
145 | vf->ctrl_vsi_idx = ICE_NO_VSI; | |
146 | } | |
147 | return err; | |
148 | } | |
149 | ||
150 | /** | |
151 | * ice_vc_fdir_alloc_prof - allocate profile for this filter flow type | |
152 | * @vf: pointer to the VF structure | |
153 | * @flow: filter flow type | |
154 | * | |
155 | * Return: 0 on success, and other on error. | |
156 | */ | |
157 | static int | |
158 | ice_vc_fdir_alloc_prof(struct ice_vf *vf, enum ice_fltr_ptype flow) | |
159 | { | |
160 | struct ice_vf_fdir *fdir = &vf->fdir; | |
161 | ||
162 | if (!fdir->fdir_prof) { | |
163 | fdir->fdir_prof = devm_kcalloc(ice_pf_to_dev(vf->pf), | |
164 | ICE_FLTR_PTYPE_MAX, | |
165 | sizeof(*fdir->fdir_prof), | |
166 | GFP_KERNEL); | |
167 | if (!fdir->fdir_prof) | |
168 | return -ENOMEM; | |
169 | } | |
170 | ||
171 | if (!fdir->fdir_prof[flow]) { | |
172 | fdir->fdir_prof[flow] = devm_kzalloc(ice_pf_to_dev(vf->pf), | |
173 | sizeof(**fdir->fdir_prof), | |
174 | GFP_KERNEL); | |
175 | if (!fdir->fdir_prof[flow]) | |
176 | return -ENOMEM; | |
177 | } | |
178 | ||
179 | return 0; | |
180 | } | |
181 | ||
182 | /** | |
183 | * ice_vc_fdir_free_prof - free profile for this filter flow type | |
184 | * @vf: pointer to the VF structure | |
185 | * @flow: filter flow type | |
186 | */ | |
187 | static void | |
188 | ice_vc_fdir_free_prof(struct ice_vf *vf, enum ice_fltr_ptype flow) | |
189 | { | |
190 | struct ice_vf_fdir *fdir = &vf->fdir; | |
191 | ||
192 | if (!fdir->fdir_prof) | |
193 | return; | |
194 | ||
195 | if (!fdir->fdir_prof[flow]) | |
196 | return; | |
197 | ||
198 | devm_kfree(ice_pf_to_dev(vf->pf), fdir->fdir_prof[flow]); | |
199 | fdir->fdir_prof[flow] = NULL; | |
200 | } | |
201 | ||
202 | /** | |
203 | * ice_vc_fdir_free_prof_all - free all the profile for this VF | |
204 | * @vf: pointer to the VF structure | |
205 | */ | |
206 | static void ice_vc_fdir_free_prof_all(struct ice_vf *vf) | |
207 | { | |
208 | struct ice_vf_fdir *fdir = &vf->fdir; | |
209 | enum ice_fltr_ptype flow; | |
210 | ||
211 | if (!fdir->fdir_prof) | |
212 | return; | |
213 | ||
214 | for (flow = ICE_FLTR_PTYPE_NONF_NONE; flow < ICE_FLTR_PTYPE_MAX; flow++) | |
215 | ice_vc_fdir_free_prof(vf, flow); | |
216 | ||
217 | devm_kfree(ice_pf_to_dev(vf->pf), fdir->fdir_prof); | |
218 | fdir->fdir_prof = NULL; | |
219 | } | |
220 | ||
221 | /** | |
222 | * ice_vc_fdir_parse_flow_fld | |
223 | * @proto_hdr: virtual channel protocol filter header | |
224 | * @conf: FDIR configuration for each filter | |
225 | * @fld: field type array | |
226 | * @fld_cnt: field counter | |
227 | * | |
228 | * Parse the virtual channel filter header and store them into field type array | |
229 | * | |
230 | * Return: 0 on success, and other on error. | |
231 | */ | |
232 | static int | |
233 | ice_vc_fdir_parse_flow_fld(struct virtchnl_proto_hdr *proto_hdr, | |
234 | struct virtchnl_fdir_fltr_conf *conf, | |
235 | enum ice_flow_field *fld, int *fld_cnt) | |
236 | { | |
237 | struct virtchnl_proto_hdr hdr; | |
238 | u32 i; | |
239 | ||
240 | memcpy(&hdr, proto_hdr, sizeof(hdr)); | |
241 | ||
242 | for (i = 0; (i < ARRAY_SIZE(fdir_inset_map)) && | |
243 | VIRTCHNL_GET_PROTO_HDR_FIELD(&hdr); i++) | |
244 | if (VIRTCHNL_TEST_PROTO_HDR(&hdr, fdir_inset_map[i].field)) { | |
213528fe QZ |
245 | if (fdir_inset_map[i].mask && |
246 | ((fdir_inset_map[i].mask & conf->inset_flag) != | |
247 | fdir_inset_map[i].flag)) | |
248 | continue; | |
249 | ||
1f7ea1cd QZ |
250 | fld[*fld_cnt] = fdir_inset_map[i].fld; |
251 | *fld_cnt += 1; | |
252 | if (*fld_cnt >= ICE_FLOW_FIELD_IDX_MAX) | |
253 | return -EINVAL; | |
254 | VIRTCHNL_DEL_PROTO_HDR_FIELD(&hdr, | |
255 | fdir_inset_map[i].field); | |
256 | } | |
257 | ||
258 | return 0; | |
259 | } | |
260 | ||
261 | /** | |
262 | * ice_vc_fdir_set_flow_fld | |
263 | * @vf: pointer to the VF structure | |
264 | * @fltr: virtual channel add cmd buffer | |
265 | * @conf: FDIR configuration for each filter | |
266 | * @seg: array of one or more packet segments that describe the flow | |
267 | * | |
268 | * Parse the virtual channel add msg buffer's field vector and store them into | |
269 | * flow's packet segment field | |
270 | * | |
271 | * Return: 0 on success, and other on error. | |
272 | */ | |
273 | static int | |
274 | ice_vc_fdir_set_flow_fld(struct ice_vf *vf, struct virtchnl_fdir_add *fltr, | |
275 | struct virtchnl_fdir_fltr_conf *conf, | |
276 | struct ice_flow_seg_info *seg) | |
277 | { | |
278 | struct virtchnl_fdir_rule *rule = &fltr->rule_cfg; | |
279 | enum ice_flow_field fld[ICE_FLOW_FIELD_IDX_MAX]; | |
280 | struct device *dev = ice_pf_to_dev(vf->pf); | |
281 | struct virtchnl_proto_hdrs *proto; | |
282 | int fld_cnt = 0; | |
283 | int i; | |
284 | ||
285 | proto = &rule->proto_hdrs; | |
286 | for (i = 0; i < proto->count; i++) { | |
287 | struct virtchnl_proto_hdr *hdr = &proto->proto_hdr[i]; | |
288 | int ret; | |
289 | ||
290 | ret = ice_vc_fdir_parse_flow_fld(hdr, conf, fld, &fld_cnt); | |
291 | if (ret) | |
292 | return ret; | |
293 | } | |
294 | ||
295 | if (fld_cnt == 0) { | |
296 | dev_dbg(dev, "Empty input set for VF %d\n", vf->vf_id); | |
297 | return -EINVAL; | |
298 | } | |
299 | ||
300 | for (i = 0; i < fld_cnt; i++) | |
301 | ice_flow_set_fld(seg, fld[i], | |
302 | ICE_FLOW_FLD_OFF_INVAL, | |
303 | ICE_FLOW_FLD_OFF_INVAL, | |
304 | ICE_FLOW_FLD_OFF_INVAL, false); | |
305 | ||
306 | return 0; | |
307 | } | |
308 | ||
309 | /** | |
310 | * ice_vc_fdir_set_flow_hdr - config the flow's packet segment header | |
311 | * @vf: pointer to the VF structure | |
312 | * @conf: FDIR configuration for each filter | |
313 | * @seg: array of one or more packet segments that describe the flow | |
314 | * | |
315 | * Return: 0 on success, and other on error. | |
316 | */ | |
317 | static int | |
318 | ice_vc_fdir_set_flow_hdr(struct ice_vf *vf, | |
319 | struct virtchnl_fdir_fltr_conf *conf, | |
320 | struct ice_flow_seg_info *seg) | |
321 | { | |
322 | enum ice_fltr_ptype flow = conf->input.flow_type; | |
ef9e4cc5 | 323 | enum ice_fdir_tunnel_type ttype = conf->ttype; |
1f7ea1cd QZ |
324 | struct device *dev = ice_pf_to_dev(vf->pf); |
325 | ||
326 | switch (flow) { | |
21606584 QZ |
327 | case ICE_FLTR_PTYPE_NON_IP_L2: |
328 | ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_ETH_NON_IP); | |
329 | break; | |
213528fe QZ |
330 | case ICE_FLTR_PTYPE_NONF_IPV4_L2TPV3: |
331 | ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_L2TPV3 | | |
332 | ICE_FLOW_SEG_HDR_IPV4 | | |
333 | ICE_FLOW_SEG_HDR_IPV_OTHER); | |
334 | break; | |
335 | case ICE_FLTR_PTYPE_NONF_IPV4_ESP: | |
336 | ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_ESP | | |
337 | ICE_FLOW_SEG_HDR_IPV4 | | |
338 | ICE_FLOW_SEG_HDR_IPV_OTHER); | |
339 | break; | |
340 | case ICE_FLTR_PTYPE_NONF_IPV4_AH: | |
341 | ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_AH | | |
342 | ICE_FLOW_SEG_HDR_IPV4 | | |
343 | ICE_FLOW_SEG_HDR_IPV_OTHER); | |
344 | break; | |
345 | case ICE_FLTR_PTYPE_NONF_IPV4_NAT_T_ESP: | |
346 | ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_NAT_T_ESP | | |
347 | ICE_FLOW_SEG_HDR_IPV4 | | |
348 | ICE_FLOW_SEG_HDR_IPV_OTHER); | |
349 | break; | |
350 | case ICE_FLTR_PTYPE_NONF_IPV4_PFCP_NODE: | |
351 | ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_PFCP_NODE | | |
352 | ICE_FLOW_SEG_HDR_IPV4 | | |
353 | ICE_FLOW_SEG_HDR_IPV_OTHER); | |
354 | break; | |
355 | case ICE_FLTR_PTYPE_NONF_IPV4_PFCP_SESSION: | |
356 | ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_PFCP_SESSION | | |
357 | ICE_FLOW_SEG_HDR_IPV4 | | |
358 | ICE_FLOW_SEG_HDR_IPV_OTHER); | |
359 | break; | |
1f7ea1cd QZ |
360 | case ICE_FLTR_PTYPE_NONF_IPV4_OTHER: |
361 | ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_IPV4 | | |
362 | ICE_FLOW_SEG_HDR_IPV_OTHER); | |
363 | break; | |
364 | case ICE_FLTR_PTYPE_NONF_IPV4_TCP: | |
365 | ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_TCP | | |
366 | ICE_FLOW_SEG_HDR_IPV4 | | |
367 | ICE_FLOW_SEG_HDR_IPV_OTHER); | |
368 | break; | |
369 | case ICE_FLTR_PTYPE_NONF_IPV4_UDP: | |
370 | ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_UDP | | |
371 | ICE_FLOW_SEG_HDR_IPV4 | | |
372 | ICE_FLOW_SEG_HDR_IPV_OTHER); | |
373 | break; | |
ef9e4cc5 QZ |
374 | case ICE_FLTR_PTYPE_NONF_IPV4_GTPU_IPV4_UDP: |
375 | case ICE_FLTR_PTYPE_NONF_IPV4_GTPU_IPV4_TCP: | |
376 | case ICE_FLTR_PTYPE_NONF_IPV4_GTPU_IPV4_ICMP: | |
377 | case ICE_FLTR_PTYPE_NONF_IPV4_GTPU_IPV4_OTHER: | |
378 | if (ttype == ICE_FDIR_TUNNEL_TYPE_GTPU) { | |
379 | ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_GTPU_IP | | |
380 | ICE_FLOW_SEG_HDR_IPV4 | | |
381 | ICE_FLOW_SEG_HDR_IPV_OTHER); | |
382 | } else if (ttype == ICE_FDIR_TUNNEL_TYPE_GTPU_EH) { | |
383 | ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_GTPU_EH | | |
384 | ICE_FLOW_SEG_HDR_GTPU_IP | | |
385 | ICE_FLOW_SEG_HDR_IPV4 | | |
386 | ICE_FLOW_SEG_HDR_IPV_OTHER); | |
387 | } else { | |
388 | dev_dbg(dev, "Invalid tunnel type 0x%x for VF %d\n", | |
389 | flow, vf->vf_id); | |
390 | return -EINVAL; | |
391 | } | |
392 | break; | |
1f7ea1cd QZ |
393 | case ICE_FLTR_PTYPE_NONF_IPV4_SCTP: |
394 | ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_SCTP | | |
395 | ICE_FLOW_SEG_HDR_IPV4 | | |
396 | ICE_FLOW_SEG_HDR_IPV_OTHER); | |
397 | break; | |
213528fe QZ |
398 | case ICE_FLTR_PTYPE_NONF_IPV6_L2TPV3: |
399 | ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_L2TPV3 | | |
400 | ICE_FLOW_SEG_HDR_IPV6 | | |
401 | ICE_FLOW_SEG_HDR_IPV_OTHER); | |
402 | break; | |
403 | case ICE_FLTR_PTYPE_NONF_IPV6_ESP: | |
404 | ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_ESP | | |
405 | ICE_FLOW_SEG_HDR_IPV6 | | |
406 | ICE_FLOW_SEG_HDR_IPV_OTHER); | |
407 | break; | |
408 | case ICE_FLTR_PTYPE_NONF_IPV6_AH: | |
409 | ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_AH | | |
410 | ICE_FLOW_SEG_HDR_IPV6 | | |
411 | ICE_FLOW_SEG_HDR_IPV_OTHER); | |
412 | break; | |
413 | case ICE_FLTR_PTYPE_NONF_IPV6_NAT_T_ESP: | |
414 | ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_NAT_T_ESP | | |
415 | ICE_FLOW_SEG_HDR_IPV6 | | |
416 | ICE_FLOW_SEG_HDR_IPV_OTHER); | |
417 | break; | |
418 | case ICE_FLTR_PTYPE_NONF_IPV6_PFCP_NODE: | |
419 | ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_PFCP_NODE | | |
420 | ICE_FLOW_SEG_HDR_IPV6 | | |
421 | ICE_FLOW_SEG_HDR_IPV_OTHER); | |
422 | break; | |
423 | case ICE_FLTR_PTYPE_NONF_IPV6_PFCP_SESSION: | |
424 | ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_PFCP_SESSION | | |
425 | ICE_FLOW_SEG_HDR_IPV6 | | |
426 | ICE_FLOW_SEG_HDR_IPV_OTHER); | |
427 | break; | |
1f7ea1cd QZ |
428 | case ICE_FLTR_PTYPE_NONF_IPV6_OTHER: |
429 | ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_IPV6 | | |
430 | ICE_FLOW_SEG_HDR_IPV_OTHER); | |
431 | break; | |
432 | case ICE_FLTR_PTYPE_NONF_IPV6_TCP: | |
433 | ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_TCP | | |
434 | ICE_FLOW_SEG_HDR_IPV6 | | |
435 | ICE_FLOW_SEG_HDR_IPV_OTHER); | |
436 | break; | |
437 | case ICE_FLTR_PTYPE_NONF_IPV6_UDP: | |
438 | ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_UDP | | |
439 | ICE_FLOW_SEG_HDR_IPV6 | | |
440 | ICE_FLOW_SEG_HDR_IPV_OTHER); | |
441 | break; | |
442 | case ICE_FLTR_PTYPE_NONF_IPV6_SCTP: | |
443 | ICE_FLOW_SET_HDRS(seg, ICE_FLOW_SEG_HDR_SCTP | | |
444 | ICE_FLOW_SEG_HDR_IPV6 | | |
445 | ICE_FLOW_SEG_HDR_IPV_OTHER); | |
446 | break; | |
447 | default: | |
448 | dev_dbg(dev, "Invalid flow type 0x%x for VF %d failed\n", | |
449 | flow, vf->vf_id); | |
450 | return -EINVAL; | |
451 | } | |
452 | ||
453 | return 0; | |
454 | } | |
455 | ||
456 | /** | |
457 | * ice_vc_fdir_rem_prof - remove profile for this filter flow type | |
458 | * @vf: pointer to the VF structure | |
459 | * @flow: filter flow type | |
460 | * @tun: 0 implies non-tunnel type filter, 1 implies tunnel type filter | |
461 | */ | |
462 | static void | |
463 | ice_vc_fdir_rem_prof(struct ice_vf *vf, enum ice_fltr_ptype flow, int tun) | |
464 | { | |
465 | struct ice_vf_fdir *fdir = &vf->fdir; | |
466 | struct ice_fd_hw_prof *vf_prof; | |
467 | struct ice_pf *pf = vf->pf; | |
468 | struct ice_vsi *vf_vsi; | |
469 | struct device *dev; | |
470 | struct ice_hw *hw; | |
471 | u64 prof_id; | |
472 | int i; | |
473 | ||
474 | dev = ice_pf_to_dev(pf); | |
475 | hw = &pf->hw; | |
476 | if (!fdir->fdir_prof || !fdir->fdir_prof[flow]) | |
477 | return; | |
478 | ||
479 | vf_prof = fdir->fdir_prof[flow]; | |
b1f5921a | 480 | prof_id = vf_prof->prof_id[tun]; |
1f7ea1cd | 481 | |
772dec64 | 482 | vf_vsi = ice_get_vf_vsi(vf); |
1f7ea1cd QZ |
483 | if (!vf_vsi) { |
484 | dev_dbg(dev, "NULL vf %d vsi pointer\n", vf->vf_id); | |
485 | return; | |
486 | } | |
487 | ||
488 | if (!fdir->prof_entry_cnt[flow][tun]) | |
489 | return; | |
490 | ||
1f7ea1cd QZ |
491 | for (i = 0; i < fdir->prof_entry_cnt[flow][tun]; i++) |
492 | if (vf_prof->entry_h[i][tun]) { | |
493 | u16 vsi_num = ice_get_hw_vsi_num(hw, vf_prof->vsi_h[i]); | |
494 | ||
495 | ice_rem_prof_id_flow(hw, ICE_BLK_FD, vsi_num, prof_id); | |
496 | ice_flow_rem_entry(hw, ICE_BLK_FD, | |
497 | vf_prof->entry_h[i][tun]); | |
498 | vf_prof->entry_h[i][tun] = 0; | |
499 | } | |
500 | ||
501 | ice_flow_rem_prof(hw, ICE_BLK_FD, prof_id); | |
502 | devm_kfree(dev, vf_prof->fdir_seg[tun]); | |
503 | vf_prof->fdir_seg[tun] = NULL; | |
504 | ||
505 | for (i = 0; i < vf_prof->cnt; i++) | |
506 | vf_prof->vsi_h[i] = 0; | |
507 | ||
508 | fdir->prof_entry_cnt[flow][tun] = 0; | |
509 | } | |
510 | ||
511 | /** | |
512 | * ice_vc_fdir_rem_prof_all - remove profile for this VF | |
513 | * @vf: pointer to the VF structure | |
514 | */ | |
515 | static void ice_vc_fdir_rem_prof_all(struct ice_vf *vf) | |
516 | { | |
517 | enum ice_fltr_ptype flow; | |
518 | ||
519 | for (flow = ICE_FLTR_PTYPE_NONF_NONE; | |
520 | flow < ICE_FLTR_PTYPE_MAX; flow++) { | |
521 | ice_vc_fdir_rem_prof(vf, flow, 0); | |
522 | ice_vc_fdir_rem_prof(vf, flow, 1); | |
523 | } | |
524 | } | |
525 | ||
83c911dc LL |
526 | /** |
527 | * ice_vc_fdir_reset_cnt_all - reset all FDIR counters for this VF FDIR | |
528 | * @fdir: pointer to the VF FDIR structure | |
529 | */ | |
530 | static void ice_vc_fdir_reset_cnt_all(struct ice_vf_fdir *fdir) | |
531 | { | |
532 | enum ice_fltr_ptype flow; | |
533 | ||
534 | for (flow = ICE_FLTR_PTYPE_NONF_NONE; | |
535 | flow < ICE_FLTR_PTYPE_MAX; flow++) { | |
536 | fdir->fdir_fltr_cnt[flow][0] = 0; | |
537 | fdir->fdir_fltr_cnt[flow][1] = 0; | |
538 | } | |
539 | } | |
540 | ||
29486b6d JG |
541 | /** |
542 | * ice_vc_fdir_has_prof_conflict | |
543 | * @vf: pointer to the VF structure | |
544 | * @conf: FDIR configuration for each filter | |
545 | * | |
546 | * Check if @conf has conflicting profile with existing profiles | |
547 | * | |
548 | * Return: true on success, and false on error. | |
549 | */ | |
550 | static bool | |
551 | ice_vc_fdir_has_prof_conflict(struct ice_vf *vf, | |
552 | struct virtchnl_fdir_fltr_conf *conf) | |
553 | { | |
554 | struct ice_fdir_fltr *desc; | |
555 | ||
556 | list_for_each_entry(desc, &vf->fdir.fdir_rule_list, fltr_node) { | |
557 | struct virtchnl_fdir_fltr_conf *existing_conf; | |
558 | enum ice_fltr_ptype flow_type_a, flow_type_b; | |
559 | struct ice_fdir_fltr *a, *b; | |
560 | ||
561 | existing_conf = to_fltr_conf_from_desc(desc); | |
562 | a = &existing_conf->input; | |
563 | b = &conf->input; | |
564 | flow_type_a = a->flow_type; | |
565 | flow_type_b = b->flow_type; | |
566 | ||
567 | /* No need to compare two rules with different tunnel types or | |
568 | * with the same protocol type. | |
569 | */ | |
570 | if (existing_conf->ttype != conf->ttype || | |
571 | flow_type_a == flow_type_b) | |
572 | continue; | |
573 | ||
574 | switch (flow_type_a) { | |
575 | case ICE_FLTR_PTYPE_NONF_IPV4_UDP: | |
576 | case ICE_FLTR_PTYPE_NONF_IPV4_TCP: | |
577 | case ICE_FLTR_PTYPE_NONF_IPV4_SCTP: | |
578 | if (flow_type_b == ICE_FLTR_PTYPE_NONF_IPV4_OTHER) | |
579 | return true; | |
580 | break; | |
581 | case ICE_FLTR_PTYPE_NONF_IPV4_OTHER: | |
582 | if (flow_type_b == ICE_FLTR_PTYPE_NONF_IPV4_UDP || | |
583 | flow_type_b == ICE_FLTR_PTYPE_NONF_IPV4_TCP || | |
584 | flow_type_b == ICE_FLTR_PTYPE_NONF_IPV4_SCTP) | |
585 | return true; | |
586 | break; | |
587 | case ICE_FLTR_PTYPE_NONF_IPV6_UDP: | |
588 | case ICE_FLTR_PTYPE_NONF_IPV6_TCP: | |
589 | case ICE_FLTR_PTYPE_NONF_IPV6_SCTP: | |
590 | if (flow_type_b == ICE_FLTR_PTYPE_NONF_IPV6_OTHER) | |
591 | return true; | |
592 | break; | |
593 | case ICE_FLTR_PTYPE_NONF_IPV6_OTHER: | |
594 | if (flow_type_b == ICE_FLTR_PTYPE_NONF_IPV6_UDP || | |
595 | flow_type_b == ICE_FLTR_PTYPE_NONF_IPV6_TCP || | |
596 | flow_type_b == ICE_FLTR_PTYPE_NONF_IPV6_SCTP) | |
597 | return true; | |
598 | break; | |
599 | default: | |
600 | break; | |
601 | } | |
602 | } | |
603 | ||
604 | return false; | |
605 | } | |
606 | ||
1f7ea1cd QZ |
607 | /** |
608 | * ice_vc_fdir_write_flow_prof | |
609 | * @vf: pointer to the VF structure | |
610 | * @flow: filter flow type | |
611 | * @seg: array of one or more packet segments that describe the flow | |
612 | * @tun: 0 implies non-tunnel type filter, 1 implies tunnel type filter | |
613 | * | |
614 | * Write the flow's profile config and packet segment into the hardware | |
615 | * | |
616 | * Return: 0 on success, and other on error. | |
617 | */ | |
618 | static int | |
619 | ice_vc_fdir_write_flow_prof(struct ice_vf *vf, enum ice_fltr_ptype flow, | |
620 | struct ice_flow_seg_info *seg, int tun) | |
621 | { | |
622 | struct ice_vf_fdir *fdir = &vf->fdir; | |
623 | struct ice_vsi *vf_vsi, *ctrl_vsi; | |
624 | struct ice_flow_seg_info *old_seg; | |
625 | struct ice_flow_prof *prof = NULL; | |
626 | struct ice_fd_hw_prof *vf_prof; | |
1f7ea1cd QZ |
627 | struct device *dev; |
628 | struct ice_pf *pf; | |
629 | struct ice_hw *hw; | |
630 | u64 entry1_h = 0; | |
631 | u64 entry2_h = 0; | |
1f7ea1cd QZ |
632 | int ret; |
633 | ||
634 | pf = vf->pf; | |
635 | dev = ice_pf_to_dev(pf); | |
636 | hw = &pf->hw; | |
772dec64 | 637 | vf_vsi = ice_get_vf_vsi(vf); |
1f7ea1cd QZ |
638 | if (!vf_vsi) |
639 | return -EINVAL; | |
640 | ||
641 | ctrl_vsi = pf->vsi[vf->ctrl_vsi_idx]; | |
642 | if (!ctrl_vsi) | |
643 | return -EINVAL; | |
644 | ||
645 | vf_prof = fdir->fdir_prof[flow]; | |
646 | old_seg = vf_prof->fdir_seg[tun]; | |
647 | if (old_seg) { | |
648 | if (!memcmp(old_seg, seg, sizeof(*seg))) { | |
649 | dev_dbg(dev, "Duplicated profile for VF %d!\n", | |
650 | vf->vf_id); | |
651 | return -EEXIST; | |
652 | } | |
653 | ||
654 | if (fdir->fdir_fltr_cnt[flow][tun]) { | |
655 | ret = -EINVAL; | |
656 | dev_dbg(dev, "Input set conflicts for VF %d\n", | |
657 | vf->vf_id); | |
658 | goto err_exit; | |
659 | } | |
660 | ||
661 | /* remove previously allocated profile */ | |
662 | ice_vc_fdir_rem_prof(vf, flow, tun); | |
663 | } | |
664 | ||
b1f5921a | 665 | ret = ice_flow_add_prof(hw, ICE_BLK_FD, ICE_FLOW_RX, seg, |
352e9bf2 | 666 | tun + 1, false, &prof); |
1f7ea1cd QZ |
667 | if (ret) { |
668 | dev_dbg(dev, "Could not add VSI flow 0x%x for VF %d\n", | |
669 | flow, vf->vf_id); | |
670 | goto err_exit; | |
671 | } | |
672 | ||
b1f5921a | 673 | ret = ice_flow_add_entry(hw, ICE_BLK_FD, prof->id, vf_vsi->idx, |
2ccc1c1c TN |
674 | vf_vsi->idx, ICE_FLOW_PRIO_NORMAL, |
675 | seg, &entry1_h); | |
1f7ea1cd QZ |
676 | if (ret) { |
677 | dev_dbg(dev, "Could not add flow 0x%x VSI entry for VF %d\n", | |
678 | flow, vf->vf_id); | |
679 | goto err_prof; | |
680 | } | |
681 | ||
b1f5921a | 682 | ret = ice_flow_add_entry(hw, ICE_BLK_FD, prof->id, vf_vsi->idx, |
2ccc1c1c TN |
683 | ctrl_vsi->idx, ICE_FLOW_PRIO_NORMAL, |
684 | seg, &entry2_h); | |
1f7ea1cd QZ |
685 | if (ret) { |
686 | dev_dbg(dev, | |
687 | "Could not add flow 0x%x Ctrl VSI entry for VF %d\n", | |
688 | flow, vf->vf_id); | |
689 | goto err_entry_1; | |
690 | } | |
691 | ||
692 | vf_prof->fdir_seg[tun] = seg; | |
693 | vf_prof->cnt = 0; | |
694 | fdir->prof_entry_cnt[flow][tun] = 0; | |
695 | ||
696 | vf_prof->entry_h[vf_prof->cnt][tun] = entry1_h; | |
697 | vf_prof->vsi_h[vf_prof->cnt] = vf_vsi->idx; | |
698 | vf_prof->cnt++; | |
699 | fdir->prof_entry_cnt[flow][tun]++; | |
700 | ||
701 | vf_prof->entry_h[vf_prof->cnt][tun] = entry2_h; | |
702 | vf_prof->vsi_h[vf_prof->cnt] = ctrl_vsi->idx; | |
703 | vf_prof->cnt++; | |
704 | fdir->prof_entry_cnt[flow][tun]++; | |
705 | ||
b1f5921a AZ |
706 | vf_prof->prof_id[tun] = prof->id; |
707 | ||
1f7ea1cd QZ |
708 | return 0; |
709 | ||
710 | err_entry_1: | |
711 | ice_rem_prof_id_flow(hw, ICE_BLK_FD, | |
b1f5921a | 712 | ice_get_hw_vsi_num(hw, vf_vsi->idx), prof->id); |
1f7ea1cd QZ |
713 | ice_flow_rem_entry(hw, ICE_BLK_FD, entry1_h); |
714 | err_prof: | |
b1f5921a | 715 | ice_flow_rem_prof(hw, ICE_BLK_FD, prof->id); |
1f7ea1cd QZ |
716 | err_exit: |
717 | return ret; | |
718 | } | |
719 | ||
720 | /** | |
721 | * ice_vc_fdir_config_input_set | |
722 | * @vf: pointer to the VF structure | |
723 | * @fltr: virtual channel add cmd buffer | |
724 | * @conf: FDIR configuration for each filter | |
725 | * @tun: 0 implies non-tunnel type filter, 1 implies tunnel type filter | |
726 | * | |
727 | * Config the input set type and value for virtual channel add msg buffer | |
728 | * | |
729 | * Return: 0 on success, and other on error. | |
730 | */ | |
731 | static int | |
732 | ice_vc_fdir_config_input_set(struct ice_vf *vf, struct virtchnl_fdir_add *fltr, | |
733 | struct virtchnl_fdir_fltr_conf *conf, int tun) | |
734 | { | |
735 | struct ice_fdir_fltr *input = &conf->input; | |
736 | struct device *dev = ice_pf_to_dev(vf->pf); | |
737 | struct ice_flow_seg_info *seg; | |
738 | enum ice_fltr_ptype flow; | |
739 | int ret; | |
740 | ||
29486b6d JG |
741 | ret = ice_vc_fdir_has_prof_conflict(vf, conf); |
742 | if (ret) { | |
743 | dev_dbg(dev, "Found flow profile conflict for VF %d\n", | |
744 | vf->vf_id); | |
745 | return ret; | |
746 | } | |
747 | ||
1f7ea1cd QZ |
748 | flow = input->flow_type; |
749 | ret = ice_vc_fdir_alloc_prof(vf, flow); | |
750 | if (ret) { | |
751 | dev_dbg(dev, "Alloc flow prof for VF %d failed\n", vf->vf_id); | |
752 | return ret; | |
753 | } | |
754 | ||
755 | seg = devm_kzalloc(dev, sizeof(*seg), GFP_KERNEL); | |
756 | if (!seg) | |
757 | return -ENOMEM; | |
758 | ||
759 | ret = ice_vc_fdir_set_flow_fld(vf, fltr, conf, seg); | |
760 | if (ret) { | |
761 | dev_dbg(dev, "Set flow field for VF %d failed\n", vf->vf_id); | |
762 | goto err_exit; | |
763 | } | |
764 | ||
765 | ret = ice_vc_fdir_set_flow_hdr(vf, conf, seg); | |
766 | if (ret) { | |
767 | dev_dbg(dev, "Set flow hdr for VF %d failed\n", vf->vf_id); | |
768 | goto err_exit; | |
769 | } | |
770 | ||
771 | ret = ice_vc_fdir_write_flow_prof(vf, flow, seg, tun); | |
772 | if (ret == -EEXIST) { | |
773 | devm_kfree(dev, seg); | |
774 | } else if (ret) { | |
775 | dev_dbg(dev, "Write flow profile for VF %d failed\n", | |
776 | vf->vf_id); | |
777 | goto err_exit; | |
778 | } | |
779 | ||
780 | return 0; | |
781 | ||
782 | err_exit: | |
783 | devm_kfree(dev, seg); | |
784 | return ret; | |
785 | } | |
786 | ||
0ce332fd QZ |
787 | /** |
788 | * ice_vc_fdir_parse_pattern | |
789 | * @vf: pointer to the VF info | |
790 | * @fltr: virtual channel add cmd buffer | |
791 | * @conf: FDIR configuration for each filter | |
792 | * | |
793 | * Parse the virtual channel filter's pattern and store them into conf | |
794 | * | |
795 | * Return: 0 on success, and other on error. | |
796 | */ | |
797 | static int | |
798 | ice_vc_fdir_parse_pattern(struct ice_vf *vf, struct virtchnl_fdir_add *fltr, | |
799 | struct virtchnl_fdir_fltr_conf *conf) | |
800 | { | |
801 | struct virtchnl_proto_hdrs *proto = &fltr->rule_cfg.proto_hdrs; | |
802 | enum virtchnl_proto_hdr_type l3 = VIRTCHNL_PROTO_HDR_NONE; | |
213528fe | 803 | enum virtchnl_proto_hdr_type l4 = VIRTCHNL_PROTO_HDR_NONE; |
0ce332fd QZ |
804 | struct device *dev = ice_pf_to_dev(vf->pf); |
805 | struct ice_fdir_fltr *input = &conf->input; | |
806 | int i; | |
807 | ||
808 | if (proto->count > VIRTCHNL_MAX_NUM_PROTO_HDRS) { | |
809 | dev_dbg(dev, "Invalid protocol count:0x%x for VF %d\n", | |
810 | proto->count, vf->vf_id); | |
811 | return -EINVAL; | |
812 | } | |
813 | ||
814 | for (i = 0; i < proto->count; i++) { | |
815 | struct virtchnl_proto_hdr *hdr = &proto->proto_hdr[i]; | |
213528fe QZ |
816 | struct ip_esp_hdr *esph; |
817 | struct ip_auth_hdr *ah; | |
0ce332fd QZ |
818 | struct sctphdr *sctph; |
819 | struct ipv6hdr *ip6h; | |
820 | struct udphdr *udph; | |
821 | struct tcphdr *tcph; | |
21606584 | 822 | struct ethhdr *eth; |
0ce332fd | 823 | struct iphdr *iph; |
213528fe | 824 | u8 s_field; |
ef9e4cc5 | 825 | u8 *rawh; |
0ce332fd QZ |
826 | |
827 | switch (hdr->type) { | |
828 | case VIRTCHNL_PROTO_HDR_ETH: | |
21606584 QZ |
829 | eth = (struct ethhdr *)hdr->buffer; |
830 | input->flow_type = ICE_FLTR_PTYPE_NON_IP_L2; | |
831 | ||
832 | if (hdr->field_selector) | |
833 | input->ext_data.ether_type = eth->h_proto; | |
0ce332fd QZ |
834 | break; |
835 | case VIRTCHNL_PROTO_HDR_IPV4: | |
836 | iph = (struct iphdr *)hdr->buffer; | |
837 | l3 = VIRTCHNL_PROTO_HDR_IPV4; | |
838 | input->flow_type = ICE_FLTR_PTYPE_NONF_IPV4_OTHER; | |
839 | ||
840 | if (hdr->field_selector) { | |
841 | input->ip.v4.src_ip = iph->saddr; | |
842 | input->ip.v4.dst_ip = iph->daddr; | |
843 | input->ip.v4.tos = iph->tos; | |
844 | input->ip.v4.proto = iph->protocol; | |
845 | } | |
846 | break; | |
847 | case VIRTCHNL_PROTO_HDR_IPV6: | |
848 | ip6h = (struct ipv6hdr *)hdr->buffer; | |
849 | l3 = VIRTCHNL_PROTO_HDR_IPV6; | |
850 | input->flow_type = ICE_FLTR_PTYPE_NONF_IPV6_OTHER; | |
851 | ||
852 | if (hdr->field_selector) { | |
853 | memcpy(input->ip.v6.src_ip, | |
854 | ip6h->saddr.in6_u.u6_addr8, | |
855 | sizeof(ip6h->saddr)); | |
856 | memcpy(input->ip.v6.dst_ip, | |
857 | ip6h->daddr.in6_u.u6_addr8, | |
858 | sizeof(ip6h->daddr)); | |
859 | input->ip.v6.tc = ((u8)(ip6h->priority) << 4) | | |
860 | (ip6h->flow_lbl[0] >> 4); | |
861 | input->ip.v6.proto = ip6h->nexthdr; | |
862 | } | |
863 | break; | |
864 | case VIRTCHNL_PROTO_HDR_TCP: | |
865 | tcph = (struct tcphdr *)hdr->buffer; | |
866 | if (l3 == VIRTCHNL_PROTO_HDR_IPV4) | |
867 | input->flow_type = ICE_FLTR_PTYPE_NONF_IPV4_TCP; | |
868 | else if (l3 == VIRTCHNL_PROTO_HDR_IPV6) | |
869 | input->flow_type = ICE_FLTR_PTYPE_NONF_IPV6_TCP; | |
870 | ||
871 | if (hdr->field_selector) { | |
872 | if (l3 == VIRTCHNL_PROTO_HDR_IPV4) { | |
873 | input->ip.v4.src_port = tcph->source; | |
874 | input->ip.v4.dst_port = tcph->dest; | |
875 | } else if (l3 == VIRTCHNL_PROTO_HDR_IPV6) { | |
876 | input->ip.v6.src_port = tcph->source; | |
877 | input->ip.v6.dst_port = tcph->dest; | |
878 | } | |
879 | } | |
880 | break; | |
881 | case VIRTCHNL_PROTO_HDR_UDP: | |
882 | udph = (struct udphdr *)hdr->buffer; | |
883 | if (l3 == VIRTCHNL_PROTO_HDR_IPV4) | |
884 | input->flow_type = ICE_FLTR_PTYPE_NONF_IPV4_UDP; | |
885 | else if (l3 == VIRTCHNL_PROTO_HDR_IPV6) | |
886 | input->flow_type = ICE_FLTR_PTYPE_NONF_IPV6_UDP; | |
887 | ||
888 | if (hdr->field_selector) { | |
889 | if (l3 == VIRTCHNL_PROTO_HDR_IPV4) { | |
890 | input->ip.v4.src_port = udph->source; | |
891 | input->ip.v4.dst_port = udph->dest; | |
892 | } else if (l3 == VIRTCHNL_PROTO_HDR_IPV6) { | |
893 | input->ip.v6.src_port = udph->source; | |
894 | input->ip.v6.dst_port = udph->dest; | |
895 | } | |
896 | } | |
897 | break; | |
898 | case VIRTCHNL_PROTO_HDR_SCTP: | |
899 | sctph = (struct sctphdr *)hdr->buffer; | |
900 | if (l3 == VIRTCHNL_PROTO_HDR_IPV4) | |
901 | input->flow_type = | |
902 | ICE_FLTR_PTYPE_NONF_IPV4_SCTP; | |
903 | else if (l3 == VIRTCHNL_PROTO_HDR_IPV6) | |
904 | input->flow_type = | |
905 | ICE_FLTR_PTYPE_NONF_IPV6_SCTP; | |
906 | ||
907 | if (hdr->field_selector) { | |
908 | if (l3 == VIRTCHNL_PROTO_HDR_IPV4) { | |
909 | input->ip.v4.src_port = sctph->source; | |
910 | input->ip.v4.dst_port = sctph->dest; | |
911 | } else if (l3 == VIRTCHNL_PROTO_HDR_IPV6) { | |
912 | input->ip.v6.src_port = sctph->source; | |
913 | input->ip.v6.dst_port = sctph->dest; | |
914 | } | |
915 | } | |
916 | break; | |
213528fe QZ |
917 | case VIRTCHNL_PROTO_HDR_L2TPV3: |
918 | if (l3 == VIRTCHNL_PROTO_HDR_IPV4) | |
919 | input->flow_type = ICE_FLTR_PTYPE_NONF_IPV4_L2TPV3; | |
920 | else if (l3 == VIRTCHNL_PROTO_HDR_IPV6) | |
921 | input->flow_type = ICE_FLTR_PTYPE_NONF_IPV6_L2TPV3; | |
922 | ||
923 | if (hdr->field_selector) | |
924 | input->l2tpv3_data.session_id = *((__be32 *)hdr->buffer); | |
925 | break; | |
926 | case VIRTCHNL_PROTO_HDR_ESP: | |
927 | esph = (struct ip_esp_hdr *)hdr->buffer; | |
928 | if (l3 == VIRTCHNL_PROTO_HDR_IPV4 && | |
929 | l4 == VIRTCHNL_PROTO_HDR_UDP) | |
930 | input->flow_type = ICE_FLTR_PTYPE_NONF_IPV4_NAT_T_ESP; | |
931 | else if (l3 == VIRTCHNL_PROTO_HDR_IPV6 && | |
932 | l4 == VIRTCHNL_PROTO_HDR_UDP) | |
933 | input->flow_type = ICE_FLTR_PTYPE_NONF_IPV6_NAT_T_ESP; | |
934 | else if (l3 == VIRTCHNL_PROTO_HDR_IPV4 && | |
935 | l4 == VIRTCHNL_PROTO_HDR_NONE) | |
936 | input->flow_type = ICE_FLTR_PTYPE_NONF_IPV4_ESP; | |
937 | else if (l3 == VIRTCHNL_PROTO_HDR_IPV6 && | |
938 | l4 == VIRTCHNL_PROTO_HDR_NONE) | |
939 | input->flow_type = ICE_FLTR_PTYPE_NONF_IPV6_ESP; | |
940 | ||
941 | if (l4 == VIRTCHNL_PROTO_HDR_UDP) | |
942 | conf->inset_flag |= FDIR_INSET_FLAG_ESP_UDP; | |
943 | else | |
944 | conf->inset_flag |= FDIR_INSET_FLAG_ESP_IPSEC; | |
945 | ||
946 | if (hdr->field_selector) { | |
947 | if (l3 == VIRTCHNL_PROTO_HDR_IPV4) | |
948 | input->ip.v4.sec_parm_idx = esph->spi; | |
949 | else if (l3 == VIRTCHNL_PROTO_HDR_IPV6) | |
950 | input->ip.v6.sec_parm_idx = esph->spi; | |
951 | } | |
952 | break; | |
953 | case VIRTCHNL_PROTO_HDR_AH: | |
954 | ah = (struct ip_auth_hdr *)hdr->buffer; | |
955 | if (l3 == VIRTCHNL_PROTO_HDR_IPV4) | |
956 | input->flow_type = ICE_FLTR_PTYPE_NONF_IPV4_AH; | |
957 | else if (l3 == VIRTCHNL_PROTO_HDR_IPV6) | |
958 | input->flow_type = ICE_FLTR_PTYPE_NONF_IPV6_AH; | |
959 | ||
960 | if (hdr->field_selector) { | |
961 | if (l3 == VIRTCHNL_PROTO_HDR_IPV4) | |
962 | input->ip.v4.sec_parm_idx = ah->spi; | |
963 | else if (l3 == VIRTCHNL_PROTO_HDR_IPV6) | |
964 | input->ip.v6.sec_parm_idx = ah->spi; | |
965 | } | |
966 | break; | |
967 | case VIRTCHNL_PROTO_HDR_PFCP: | |
968 | rawh = (u8 *)hdr->buffer; | |
969 | s_field = (rawh[0] >> PFCP_S_OFFSET) & PFCP_S_MASK; | |
970 | if (l3 == VIRTCHNL_PROTO_HDR_IPV4 && s_field == 0) | |
971 | input->flow_type = ICE_FLTR_PTYPE_NONF_IPV4_PFCP_NODE; | |
972 | else if (l3 == VIRTCHNL_PROTO_HDR_IPV4 && s_field == 1) | |
973 | input->flow_type = ICE_FLTR_PTYPE_NONF_IPV4_PFCP_SESSION; | |
974 | else if (l3 == VIRTCHNL_PROTO_HDR_IPV6 && s_field == 0) | |
975 | input->flow_type = ICE_FLTR_PTYPE_NONF_IPV6_PFCP_NODE; | |
976 | else if (l3 == VIRTCHNL_PROTO_HDR_IPV6 && s_field == 1) | |
977 | input->flow_type = ICE_FLTR_PTYPE_NONF_IPV6_PFCP_SESSION; | |
978 | ||
979 | if (hdr->field_selector) { | |
980 | if (l3 == VIRTCHNL_PROTO_HDR_IPV4) | |
981 | input->ip.v4.dst_port = cpu_to_be16(PFCP_PORT_NR); | |
982 | else if (l3 == VIRTCHNL_PROTO_HDR_IPV6) | |
983 | input->ip.v6.dst_port = cpu_to_be16(PFCP_PORT_NR); | |
984 | } | |
985 | break; | |
ef9e4cc5 QZ |
986 | case VIRTCHNL_PROTO_HDR_GTPU_IP: |
987 | rawh = (u8 *)hdr->buffer; | |
988 | input->flow_type = ICE_FLTR_PTYPE_NONF_IPV4_GTPU_IPV4_OTHER; | |
989 | ||
990 | if (hdr->field_selector) | |
991 | input->gtpu_data.teid = *(__be32 *)(&rawh[GTPU_TEID_OFFSET]); | |
992 | conf->ttype = ICE_FDIR_TUNNEL_TYPE_GTPU; | |
993 | break; | |
994 | case VIRTCHNL_PROTO_HDR_GTPU_EH: | |
995 | rawh = (u8 *)hdr->buffer; | |
996 | ||
997 | if (hdr->field_selector) | |
998 | input->gtpu_data.qfi = rawh[GTPU_EH_QFI_OFFSET] & GTPU_EH_QFI_MASK; | |
999 | conf->ttype = ICE_FDIR_TUNNEL_TYPE_GTPU_EH; | |
1000 | break; | |
0ce332fd QZ |
1001 | default: |
1002 | dev_dbg(dev, "Invalid header type 0x:%x for VF %d\n", | |
1003 | hdr->type, vf->vf_id); | |
1004 | return -EINVAL; | |
1005 | } | |
1006 | } | |
1007 | ||
1008 | return 0; | |
1009 | } | |
1010 | ||
1011 | /** | |
1012 | * ice_vc_fdir_parse_action | |
1013 | * @vf: pointer to the VF info | |
1014 | * @fltr: virtual channel add cmd buffer | |
1015 | * @conf: FDIR configuration for each filter | |
1016 | * | |
1017 | * Parse the virtual channel filter's action and store them into conf | |
1018 | * | |
1019 | * Return: 0 on success, and other on error. | |
1020 | */ | |
1021 | static int | |
1022 | ice_vc_fdir_parse_action(struct ice_vf *vf, struct virtchnl_fdir_add *fltr, | |
1023 | struct virtchnl_fdir_fltr_conf *conf) | |
1024 | { | |
1025 | struct virtchnl_filter_action_set *as = &fltr->rule_cfg.action_set; | |
1026 | struct device *dev = ice_pf_to_dev(vf->pf); | |
1027 | struct ice_fdir_fltr *input = &conf->input; | |
1028 | u32 dest_num = 0; | |
1029 | u32 mark_num = 0; | |
1030 | int i; | |
1031 | ||
1032 | if (as->count > VIRTCHNL_MAX_NUM_ACTIONS) { | |
1033 | dev_dbg(dev, "Invalid action numbers:0x%x for VF %d\n", | |
1034 | as->count, vf->vf_id); | |
1035 | return -EINVAL; | |
1036 | } | |
1037 | ||
1038 | for (i = 0; i < as->count; i++) { | |
1039 | struct virtchnl_filter_action *action = &as->actions[i]; | |
1040 | ||
1041 | switch (action->type) { | |
346bf250 QZ |
1042 | case VIRTCHNL_ACTION_PASSTHRU: |
1043 | dest_num++; | |
1044 | input->dest_ctl = ICE_FLTR_PRGM_DESC_DEST_DIRECT_PKT_OTHER; | |
1045 | break; | |
0ce332fd QZ |
1046 | case VIRTCHNL_ACTION_DROP: |
1047 | dest_num++; | |
1048 | input->dest_ctl = ICE_FLTR_PRGM_DESC_DEST_DROP_PKT; | |
1049 | break; | |
1050 | case VIRTCHNL_ACTION_QUEUE: | |
1051 | dest_num++; | |
1052 | input->dest_ctl = ICE_FLTR_PRGM_DESC_DEST_DIRECT_PKT_QINDEX; | |
1053 | input->q_index = action->act_conf.queue.index; | |
1054 | break; | |
346bf250 QZ |
1055 | case VIRTCHNL_ACTION_Q_REGION: |
1056 | dest_num++; | |
1057 | input->dest_ctl = ICE_FLTR_PRGM_DESC_DEST_DIRECT_PKT_QGROUP; | |
1058 | input->q_index = action->act_conf.queue.index; | |
1059 | input->q_region = action->act_conf.queue.region; | |
1060 | break; | |
0ce332fd QZ |
1061 | case VIRTCHNL_ACTION_MARK: |
1062 | mark_num++; | |
1063 | input->fltr_id = action->act_conf.mark_id; | |
1064 | input->fdid_prio = ICE_FXD_FLTR_QW1_FDID_PRI_THREE; | |
1065 | break; | |
1066 | default: | |
1067 | dev_dbg(dev, "Invalid action type:0x%x for VF %d\n", | |
1068 | action->type, vf->vf_id); | |
1069 | return -EINVAL; | |
1070 | } | |
1071 | } | |
1072 | ||
1073 | if (dest_num == 0 || dest_num >= 2) { | |
1074 | dev_dbg(dev, "Invalid destination action for VF %d\n", | |
1075 | vf->vf_id); | |
1076 | return -EINVAL; | |
1077 | } | |
1078 | ||
1079 | if (mark_num >= 2) { | |
1080 | dev_dbg(dev, "Too many mark actions for VF %d\n", vf->vf_id); | |
1081 | return -EINVAL; | |
1082 | } | |
1083 | ||
1084 | return 0; | |
1085 | } | |
1086 | ||
1f7ea1cd QZ |
1087 | /** |
1088 | * ice_vc_validate_fdir_fltr - validate the virtual channel filter | |
1089 | * @vf: pointer to the VF info | |
1090 | * @fltr: virtual channel add cmd buffer | |
1091 | * @conf: FDIR configuration for each filter | |
1092 | * | |
1093 | * Return: 0 on success, and other on error. | |
1094 | */ | |
1095 | static int | |
1096 | ice_vc_validate_fdir_fltr(struct ice_vf *vf, struct virtchnl_fdir_add *fltr, | |
1097 | struct virtchnl_fdir_fltr_conf *conf) | |
1098 | { | |
60f44fe4 | 1099 | struct virtchnl_proto_hdrs *proto = &fltr->rule_cfg.proto_hdrs; |
0ce332fd QZ |
1100 | int ret; |
1101 | ||
60f44fe4 JG |
1102 | if (!ice_vc_validate_pattern(vf, proto)) |
1103 | return -EINVAL; | |
0ce332fd QZ |
1104 | |
1105 | ret = ice_vc_fdir_parse_pattern(vf, fltr, conf); | |
1106 | if (ret) | |
1107 | return ret; | |
1108 | ||
1109 | return ice_vc_fdir_parse_action(vf, fltr, conf); | |
1f7ea1cd QZ |
1110 | } |
1111 | ||
1112 | /** | |
1113 | * ice_vc_fdir_comp_rules - compare if two filter rules have the same value | |
1114 | * @conf_a: FDIR configuration for filter a | |
1115 | * @conf_b: FDIR configuration for filter b | |
1116 | * | |
1117 | * Return: 0 on success, and other on error. | |
1118 | */ | |
1119 | static bool | |
1120 | ice_vc_fdir_comp_rules(struct virtchnl_fdir_fltr_conf *conf_a, | |
1121 | struct virtchnl_fdir_fltr_conf *conf_b) | |
1122 | { | |
1123 | struct ice_fdir_fltr *a = &conf_a->input; | |
1124 | struct ice_fdir_fltr *b = &conf_b->input; | |
1125 | ||
ef9e4cc5 QZ |
1126 | if (conf_a->ttype != conf_b->ttype) |
1127 | return false; | |
1f7ea1cd QZ |
1128 | if (a->flow_type != b->flow_type) |
1129 | return false; | |
1130 | if (memcmp(&a->ip, &b->ip, sizeof(a->ip))) | |
1131 | return false; | |
1132 | if (memcmp(&a->mask, &b->mask, sizeof(a->mask))) | |
1133 | return false; | |
ef9e4cc5 QZ |
1134 | if (memcmp(&a->gtpu_data, &b->gtpu_data, sizeof(a->gtpu_data))) |
1135 | return false; | |
1136 | if (memcmp(&a->gtpu_mask, &b->gtpu_mask, sizeof(a->gtpu_mask))) | |
1137 | return false; | |
213528fe QZ |
1138 | if (memcmp(&a->l2tpv3_data, &b->l2tpv3_data, sizeof(a->l2tpv3_data))) |
1139 | return false; | |
1140 | if (memcmp(&a->l2tpv3_mask, &b->l2tpv3_mask, sizeof(a->l2tpv3_mask))) | |
1141 | return false; | |
1f7ea1cd QZ |
1142 | if (memcmp(&a->ext_data, &b->ext_data, sizeof(a->ext_data))) |
1143 | return false; | |
1144 | if (memcmp(&a->ext_mask, &b->ext_mask, sizeof(a->ext_mask))) | |
1145 | return false; | |
1146 | ||
1147 | return true; | |
1148 | } | |
1149 | ||
1150 | /** | |
1151 | * ice_vc_fdir_is_dup_fltr | |
1152 | * @vf: pointer to the VF info | |
1153 | * @conf: FDIR configuration for each filter | |
1154 | * | |
1155 | * Check if there is duplicated rule with same conf value | |
1156 | * | |
1157 | * Return: 0 true success, and false on error. | |
1158 | */ | |
1159 | static bool | |
1160 | ice_vc_fdir_is_dup_fltr(struct ice_vf *vf, struct virtchnl_fdir_fltr_conf *conf) | |
1161 | { | |
1162 | struct ice_fdir_fltr *desc; | |
1163 | bool ret; | |
1164 | ||
1165 | list_for_each_entry(desc, &vf->fdir.fdir_rule_list, fltr_node) { | |
1166 | struct virtchnl_fdir_fltr_conf *node = | |
1167 | to_fltr_conf_from_desc(desc); | |
1168 | ||
1169 | ret = ice_vc_fdir_comp_rules(node, conf); | |
1170 | if (ret) | |
1171 | return true; | |
1172 | } | |
1173 | ||
1174 | return false; | |
1175 | } | |
1176 | ||
1177 | /** | |
1178 | * ice_vc_fdir_insert_entry | |
1179 | * @vf: pointer to the VF info | |
1180 | * @conf: FDIR configuration for each filter | |
1181 | * @id: pointer to ID value allocated by driver | |
1182 | * | |
1183 | * Insert FDIR conf entry into list and allocate ID for this filter | |
1184 | * | |
1185 | * Return: 0 true success, and other on error. | |
1186 | */ | |
1187 | static int | |
1188 | ice_vc_fdir_insert_entry(struct ice_vf *vf, | |
1189 | struct virtchnl_fdir_fltr_conf *conf, u32 *id) | |
1190 | { | |
1191 | struct ice_fdir_fltr *input = &conf->input; | |
1192 | int i; | |
1193 | ||
1194 | /* alloc ID corresponding with conf */ | |
1195 | i = idr_alloc(&vf->fdir.fdir_rule_idr, conf, 0, | |
1196 | ICE_FDIR_MAX_FLTRS, GFP_KERNEL); | |
1197 | if (i < 0) | |
1198 | return -EINVAL; | |
1199 | *id = i; | |
1200 | ||
1201 | list_add(&input->fltr_node, &vf->fdir.fdir_rule_list); | |
1202 | return 0; | |
1203 | } | |
1204 | ||
1205 | /** | |
1206 | * ice_vc_fdir_remove_entry - remove FDIR conf entry by ID value | |
1207 | * @vf: pointer to the VF info | |
1208 | * @conf: FDIR configuration for each filter | |
1209 | * @id: filter rule's ID | |
1210 | */ | |
1211 | static void | |
1212 | ice_vc_fdir_remove_entry(struct ice_vf *vf, | |
1213 | struct virtchnl_fdir_fltr_conf *conf, u32 id) | |
1214 | { | |
1215 | struct ice_fdir_fltr *input = &conf->input; | |
1216 | ||
1217 | idr_remove(&vf->fdir.fdir_rule_idr, id); | |
1218 | list_del(&input->fltr_node); | |
1219 | } | |
1220 | ||
1221 | /** | |
1222 | * ice_vc_fdir_lookup_entry - lookup FDIR conf entry by ID value | |
1223 | * @vf: pointer to the VF info | |
1224 | * @id: filter rule's ID | |
1225 | * | |
1226 | * Return: NULL on error, and other on success. | |
1227 | */ | |
1228 | static struct virtchnl_fdir_fltr_conf * | |
1229 | ice_vc_fdir_lookup_entry(struct ice_vf *vf, u32 id) | |
1230 | { | |
1231 | return idr_find(&vf->fdir.fdir_rule_idr, id); | |
1232 | } | |
1233 | ||
1234 | /** | |
1235 | * ice_vc_fdir_flush_entry - remove all FDIR conf entry | |
1236 | * @vf: pointer to the VF info | |
1237 | */ | |
1238 | static void ice_vc_fdir_flush_entry(struct ice_vf *vf) | |
1239 | { | |
1240 | struct virtchnl_fdir_fltr_conf *conf; | |
1241 | struct ice_fdir_fltr *desc, *temp; | |
1242 | ||
1243 | list_for_each_entry_safe(desc, temp, | |
1244 | &vf->fdir.fdir_rule_list, fltr_node) { | |
1245 | conf = to_fltr_conf_from_desc(desc); | |
1246 | list_del(&desc->fltr_node); | |
1247 | devm_kfree(ice_pf_to_dev(vf->pf), conf); | |
1248 | } | |
1249 | } | |
1250 | ||
1251 | /** | |
1252 | * ice_vc_fdir_write_fltr - write filter rule into hardware | |
1253 | * @vf: pointer to the VF info | |
1254 | * @conf: FDIR configuration for each filter | |
1255 | * @add: true implies add rule, false implies del rules | |
1256 | * @is_tun: false implies non-tunnel type filter, true implies tunnel filter | |
1257 | * | |
1258 | * Return: 0 on success, and other on error. | |
1259 | */ | |
1260 | static int ice_vc_fdir_write_fltr(struct ice_vf *vf, | |
1261 | struct virtchnl_fdir_fltr_conf *conf, | |
1262 | bool add, bool is_tun) | |
1263 | { | |
1264 | struct ice_fdir_fltr *input = &conf->input; | |
1265 | struct ice_vsi *vsi, *ctrl_vsi; | |
1266 | struct ice_fltr_desc desc; | |
1f7ea1cd QZ |
1267 | struct device *dev; |
1268 | struct ice_pf *pf; | |
1269 | struct ice_hw *hw; | |
1270 | int ret; | |
1271 | u8 *pkt; | |
1272 | ||
1273 | pf = vf->pf; | |
1274 | dev = ice_pf_to_dev(pf); | |
1275 | hw = &pf->hw; | |
772dec64 | 1276 | vsi = ice_get_vf_vsi(vf); |
1f7ea1cd QZ |
1277 | if (!vsi) { |
1278 | dev_dbg(dev, "Invalid vsi for VF %d\n", vf->vf_id); | |
1279 | return -EINVAL; | |
1280 | } | |
1281 | ||
1282 | input->dest_vsi = vsi->idx; | |
d6218317 | 1283 | input->comp_report = ICE_FXD_FLTR_QW0_COMP_REPORT_SW; |
1f7ea1cd QZ |
1284 | |
1285 | ctrl_vsi = pf->vsi[vf->ctrl_vsi_idx]; | |
1286 | if (!ctrl_vsi) { | |
1287 | dev_dbg(dev, "Invalid ctrl_vsi for VF %d\n", vf->vf_id); | |
1288 | return -EINVAL; | |
1289 | } | |
1290 | ||
1291 | pkt = devm_kzalloc(dev, ICE_FDIR_MAX_RAW_PKT_SIZE, GFP_KERNEL); | |
1292 | if (!pkt) | |
1293 | return -ENOMEM; | |
1294 | ||
1295 | ice_fdir_get_prgm_desc(hw, input, &desc, add); | |
2ccc1c1c | 1296 | ret = ice_fdir_get_gen_prgm_pkt(hw, input, pkt, false, is_tun); |
1f7ea1cd QZ |
1297 | if (ret) { |
1298 | dev_dbg(dev, "Gen training pkt for VF %d ptype %d failed\n", | |
1299 | vf->vf_id, input->flow_type); | |
1300 | goto err_free_pkt; | |
1301 | } | |
1302 | ||
1303 | ret = ice_prgm_fdir_fltr(ctrl_vsi, &desc, pkt); | |
1304 | if (ret) | |
1305 | goto err_free_pkt; | |
1306 | ||
1307 | return 0; | |
1308 | ||
1309 | err_free_pkt: | |
1310 | devm_kfree(dev, pkt); | |
1311 | return ret; | |
1312 | } | |
1313 | ||
d6218317 QZ |
1314 | /** |
1315 | * ice_vf_fdir_timer - FDIR program waiting timer interrupt handler | |
1316 | * @t: pointer to timer_list | |
1317 | */ | |
1318 | static void ice_vf_fdir_timer(struct timer_list *t) | |
1319 | { | |
1320 | struct ice_vf_fdir_ctx *ctx_irq = from_timer(ctx_irq, t, rx_tmr); | |
1321 | struct ice_vf_fdir_ctx *ctx_done; | |
1322 | struct ice_vf_fdir *fdir; | |
1323 | unsigned long flags; | |
1324 | struct ice_vf *vf; | |
1325 | struct ice_pf *pf; | |
1326 | ||
1327 | fdir = container_of(ctx_irq, struct ice_vf_fdir, ctx_irq); | |
1328 | vf = container_of(fdir, struct ice_vf, fdir); | |
1329 | ctx_done = &fdir->ctx_done; | |
1330 | pf = vf->pf; | |
1331 | spin_lock_irqsave(&fdir->ctx_lock, flags); | |
1332 | if (!(ctx_irq->flags & ICE_VF_FDIR_CTX_VALID)) { | |
1333 | spin_unlock_irqrestore(&fdir->ctx_lock, flags); | |
1334 | WARN_ON_ONCE(1); | |
1335 | return; | |
1336 | } | |
1337 | ||
1338 | ctx_irq->flags &= ~ICE_VF_FDIR_CTX_VALID; | |
1339 | ||
1340 | ctx_done->flags |= ICE_VF_FDIR_CTX_VALID; | |
1341 | ctx_done->conf = ctx_irq->conf; | |
1342 | ctx_done->stat = ICE_FDIR_CTX_TIMEOUT; | |
1343 | ctx_done->v_opcode = ctx_irq->v_opcode; | |
1344 | spin_unlock_irqrestore(&fdir->ctx_lock, flags); | |
1345 | ||
7e408e07 | 1346 | set_bit(ICE_FD_VF_FLUSH_CTX, pf->state); |
d6218317 QZ |
1347 | ice_service_task_schedule(pf); |
1348 | } | |
1349 | ||
1350 | /** | |
1351 | * ice_vc_fdir_irq_handler - ctrl_vsi Rx queue interrupt handler | |
1352 | * @ctrl_vsi: pointer to a VF's CTRL VSI | |
1353 | * @rx_desc: pointer to FDIR Rx queue descriptor | |
1354 | */ | |
1355 | void | |
1356 | ice_vc_fdir_irq_handler(struct ice_vsi *ctrl_vsi, | |
1357 | union ice_32b_rx_flex_desc *rx_desc) | |
1358 | { | |
1359 | struct ice_pf *pf = ctrl_vsi->back; | |
b03d519d | 1360 | struct ice_vf *vf = ctrl_vsi->vf; |
d6218317 QZ |
1361 | struct ice_vf_fdir_ctx *ctx_done; |
1362 | struct ice_vf_fdir_ctx *ctx_irq; | |
1363 | struct ice_vf_fdir *fdir; | |
1364 | unsigned long flags; | |
1365 | struct device *dev; | |
d6218317 QZ |
1366 | int ret; |
1367 | ||
b03d519d JK |
1368 | if (WARN_ON(!vf)) |
1369 | return; | |
d6218317 QZ |
1370 | |
1371 | fdir = &vf->fdir; | |
1372 | ctx_done = &fdir->ctx_done; | |
1373 | ctx_irq = &fdir->ctx_irq; | |
1374 | dev = ice_pf_to_dev(pf); | |
1375 | spin_lock_irqsave(&fdir->ctx_lock, flags); | |
1376 | if (!(ctx_irq->flags & ICE_VF_FDIR_CTX_VALID)) { | |
1377 | spin_unlock_irqrestore(&fdir->ctx_lock, flags); | |
1378 | WARN_ON_ONCE(1); | |
1379 | return; | |
1380 | } | |
1381 | ||
1382 | ctx_irq->flags &= ~ICE_VF_FDIR_CTX_VALID; | |
1383 | ||
1384 | ctx_done->flags |= ICE_VF_FDIR_CTX_VALID; | |
1385 | ctx_done->conf = ctx_irq->conf; | |
1386 | ctx_done->stat = ICE_FDIR_CTX_IRQ; | |
1387 | ctx_done->v_opcode = ctx_irq->v_opcode; | |
1388 | memcpy(&ctx_done->rx_desc, rx_desc, sizeof(*rx_desc)); | |
1389 | spin_unlock_irqrestore(&fdir->ctx_lock, flags); | |
1390 | ||
1391 | ret = del_timer(&ctx_irq->rx_tmr); | |
1392 | if (!ret) | |
1393 | dev_err(dev, "VF %d: Unexpected inactive timer!\n", vf->vf_id); | |
1394 | ||
7e408e07 | 1395 | set_bit(ICE_FD_VF_FLUSH_CTX, pf->state); |
d6218317 QZ |
1396 | ice_service_task_schedule(pf); |
1397 | } | |
1398 | ||
1399 | /** | |
1400 | * ice_vf_fdir_dump_info - dump FDIR information for diagnosis | |
1401 | * @vf: pointer to the VF info | |
1402 | */ | |
1403 | static void ice_vf_fdir_dump_info(struct ice_vf *vf) | |
1404 | { | |
ba1124f5 | 1405 | u32 fd_size, fd_cnt, fd_size_g, fd_cnt_g, fd_size_b, fd_cnt_b; |
d6218317 | 1406 | struct ice_vsi *vf_vsi; |
d6218317 QZ |
1407 | struct device *dev; |
1408 | struct ice_pf *pf; | |
1409 | struct ice_hw *hw; | |
1410 | u16 vsi_num; | |
1411 | ||
1412 | pf = vf->pf; | |
1413 | hw = &pf->hw; | |
1414 | dev = ice_pf_to_dev(pf); | |
baeb705f JK |
1415 | vf_vsi = ice_get_vf_vsi(vf); |
1416 | if (!vf_vsi) { | |
1417 | dev_dbg(dev, "VF %d: invalid VSI pointer\n", vf->vf_id); | |
1418 | return; | |
1419 | } | |
1420 | ||
d6218317 QZ |
1421 | vsi_num = ice_get_hw_vsi_num(hw, vf_vsi->idx); |
1422 | ||
1423 | fd_size = rd32(hw, VSIQF_FD_SIZE(vsi_num)); | |
1424 | fd_cnt = rd32(hw, VSIQF_FD_CNT(vsi_num)); | |
ba1124f5 PG |
1425 | switch (hw->mac_type) { |
1426 | case ICE_MAC_E830: | |
1427 | fd_size_g = FIELD_GET(E830_VSIQF_FD_CNT_FD_GCNT_M, fd_size); | |
1428 | fd_size_b = FIELD_GET(E830_VSIQF_FD_CNT_FD_BCNT_M, fd_size); | |
1429 | fd_cnt_g = FIELD_GET(E830_VSIQF_FD_CNT_FD_GCNT_M, fd_cnt); | |
1430 | fd_cnt_b = FIELD_GET(E830_VSIQF_FD_CNT_FD_BCNT_M, fd_cnt); | |
1431 | break; | |
1432 | case ICE_MAC_E810: | |
1433 | default: | |
1434 | fd_size_g = FIELD_GET(E800_VSIQF_FD_CNT_FD_GCNT_M, fd_size); | |
1435 | fd_size_b = FIELD_GET(E800_VSIQF_FD_CNT_FD_BCNT_M, fd_size); | |
1436 | fd_cnt_g = FIELD_GET(E800_VSIQF_FD_CNT_FD_GCNT_M, fd_cnt); | |
1437 | fd_cnt_b = FIELD_GET(E800_VSIQF_FD_CNT_FD_BCNT_M, fd_cnt); | |
1438 | } | |
1439 | ||
1440 | dev_dbg(dev, "VF %d: Size in the FD table: guaranteed:0x%x, best effort:0x%x\n", | |
1441 | vf->vf_id, fd_size_g, fd_size_b); | |
1442 | dev_dbg(dev, "VF %d: Filter counter in the FD table: guaranteed:0x%x, best effort:0x%x\n", | |
1443 | vf->vf_id, fd_cnt_g, fd_cnt_b); | |
d6218317 QZ |
1444 | } |
1445 | ||
1446 | /** | |
1447 | * ice_vf_verify_rx_desc - verify received FDIR programming status descriptor | |
1448 | * @vf: pointer to the VF info | |
1449 | * @ctx: FDIR context info for post processing | |
1450 | * @status: virtchnl FDIR program status | |
1451 | * | |
1452 | * Return: 0 on success, and other on error. | |
1453 | */ | |
1454 | static int | |
1455 | ice_vf_verify_rx_desc(struct ice_vf *vf, struct ice_vf_fdir_ctx *ctx, | |
1456 | enum virtchnl_fdir_prgm_status *status) | |
1457 | { | |
1458 | struct device *dev = ice_pf_to_dev(vf->pf); | |
1459 | u32 stat_err, error, prog_id; | |
1460 | int ret; | |
1461 | ||
1462 | stat_err = le16_to_cpu(ctx->rx_desc.wb.status_error0); | |
5a259f8e JB |
1463 | if (FIELD_GET(ICE_FXD_FLTR_WB_QW1_DD_M, stat_err) != |
1464 | ICE_FXD_FLTR_WB_QW1_DD_YES) { | |
d6218317 QZ |
1465 | *status = VIRTCHNL_FDIR_FAILURE_RULE_NORESOURCE; |
1466 | dev_err(dev, "VF %d: Desc Done not set\n", vf->vf_id); | |
1467 | ret = -EINVAL; | |
1468 | goto err_exit; | |
1469 | } | |
1470 | ||
5a259f8e | 1471 | prog_id = FIELD_GET(ICE_FXD_FLTR_WB_QW1_PROG_ID_M, stat_err); |
d6218317 QZ |
1472 | if (prog_id == ICE_FXD_FLTR_WB_QW1_PROG_ADD && |
1473 | ctx->v_opcode != VIRTCHNL_OP_ADD_FDIR_FILTER) { | |
1474 | dev_err(dev, "VF %d: Desc show add, but ctx not", | |
1475 | vf->vf_id); | |
1476 | *status = VIRTCHNL_FDIR_FAILURE_RULE_INVALID; | |
1477 | ret = -EINVAL; | |
1478 | goto err_exit; | |
1479 | } | |
1480 | ||
1481 | if (prog_id == ICE_FXD_FLTR_WB_QW1_PROG_DEL && | |
1482 | ctx->v_opcode != VIRTCHNL_OP_DEL_FDIR_FILTER) { | |
1483 | dev_err(dev, "VF %d: Desc show del, but ctx not", | |
1484 | vf->vf_id); | |
1485 | *status = VIRTCHNL_FDIR_FAILURE_RULE_INVALID; | |
1486 | ret = -EINVAL; | |
1487 | goto err_exit; | |
1488 | } | |
1489 | ||
5a259f8e | 1490 | error = FIELD_GET(ICE_FXD_FLTR_WB_QW1_FAIL_M, stat_err); |
d6218317 QZ |
1491 | if (error == ICE_FXD_FLTR_WB_QW1_FAIL_YES) { |
1492 | if (prog_id == ICE_FXD_FLTR_WB_QW1_PROG_ADD) { | |
1493 | dev_err(dev, "VF %d, Failed to add FDIR rule due to no space in the table", | |
1494 | vf->vf_id); | |
1495 | *status = VIRTCHNL_FDIR_FAILURE_RULE_NORESOURCE; | |
1496 | } else { | |
1497 | dev_err(dev, "VF %d, Failed to remove FDIR rule, attempt to remove non-existent entry", | |
1498 | vf->vf_id); | |
1499 | *status = VIRTCHNL_FDIR_FAILURE_RULE_NONEXIST; | |
1500 | } | |
1501 | ret = -EINVAL; | |
1502 | goto err_exit; | |
1503 | } | |
1504 | ||
5a259f8e | 1505 | error = FIELD_GET(ICE_FXD_FLTR_WB_QW1_FAIL_PROF_M, stat_err); |
d6218317 QZ |
1506 | if (error == ICE_FXD_FLTR_WB_QW1_FAIL_PROF_YES) { |
1507 | dev_err(dev, "VF %d: Profile matching error", vf->vf_id); | |
1508 | *status = VIRTCHNL_FDIR_FAILURE_RULE_NORESOURCE; | |
1509 | ret = -EINVAL; | |
1510 | goto err_exit; | |
1511 | } | |
1512 | ||
1513 | *status = VIRTCHNL_FDIR_SUCCESS; | |
1514 | ||
1515 | return 0; | |
1516 | ||
1517 | err_exit: | |
1518 | ice_vf_fdir_dump_info(vf); | |
1519 | return ret; | |
1520 | } | |
1521 | ||
1522 | /** | |
1523 | * ice_vc_add_fdir_fltr_post | |
1524 | * @vf: pointer to the VF structure | |
1525 | * @ctx: FDIR context info for post processing | |
1526 | * @status: virtchnl FDIR program status | |
1527 | * @success: true implies success, false implies failure | |
1528 | * | |
1529 | * Post process for flow director add command. If success, then do post process | |
1530 | * and send back success msg by virtchnl. Otherwise, do context reversion and | |
1531 | * send back failure msg by virtchnl. | |
1532 | * | |
1533 | * Return: 0 on success, and other on error. | |
1534 | */ | |
1535 | static int | |
1536 | ice_vc_add_fdir_fltr_post(struct ice_vf *vf, struct ice_vf_fdir_ctx *ctx, | |
1537 | enum virtchnl_fdir_prgm_status status, | |
1538 | bool success) | |
1539 | { | |
1540 | struct virtchnl_fdir_fltr_conf *conf = ctx->conf; | |
1541 | struct device *dev = ice_pf_to_dev(vf->pf); | |
1542 | enum virtchnl_status_code v_ret; | |
1543 | struct virtchnl_fdir_add *resp; | |
1544 | int ret, len, is_tun; | |
1545 | ||
1546 | v_ret = VIRTCHNL_STATUS_SUCCESS; | |
1547 | len = sizeof(*resp); | |
1548 | resp = kzalloc(len, GFP_KERNEL); | |
1549 | if (!resp) { | |
1550 | len = 0; | |
1551 | v_ret = VIRTCHNL_STATUS_ERR_NO_MEMORY; | |
1552 | dev_dbg(dev, "VF %d: Alloc resp buf fail", vf->vf_id); | |
1553 | goto err_exit; | |
1554 | } | |
1555 | ||
1556 | if (!success) | |
1557 | goto err_exit; | |
1558 | ||
1559 | is_tun = 0; | |
1560 | resp->status = status; | |
1561 | resp->flow_id = conf->flow_id; | |
1562 | vf->fdir.fdir_fltr_cnt[conf->input.flow_type][is_tun]++; | |
1563 | ||
1564 | ret = ice_vc_send_msg_to_vf(vf, ctx->v_opcode, v_ret, | |
1565 | (u8 *)resp, len); | |
1566 | kfree(resp); | |
1567 | ||
1568 | dev_dbg(dev, "VF %d: flow_id:0x%X, FDIR %s success!\n", | |
1569 | vf->vf_id, conf->flow_id, | |
1570 | (ctx->v_opcode == VIRTCHNL_OP_ADD_FDIR_FILTER) ? | |
1571 | "add" : "del"); | |
1572 | return ret; | |
1573 | ||
1574 | err_exit: | |
1575 | if (resp) | |
1576 | resp->status = status; | |
1577 | ice_vc_fdir_remove_entry(vf, conf, conf->flow_id); | |
1578 | devm_kfree(dev, conf); | |
1579 | ||
1580 | ret = ice_vc_send_msg_to_vf(vf, ctx->v_opcode, v_ret, | |
1581 | (u8 *)resp, len); | |
1582 | kfree(resp); | |
1583 | return ret; | |
1584 | } | |
1585 | ||
1586 | /** | |
1587 | * ice_vc_del_fdir_fltr_post | |
1588 | * @vf: pointer to the VF structure | |
1589 | * @ctx: FDIR context info for post processing | |
1590 | * @status: virtchnl FDIR program status | |
1591 | * @success: true implies success, false implies failure | |
1592 | * | |
1593 | * Post process for flow director del command. If success, then do post process | |
1594 | * and send back success msg by virtchnl. Otherwise, do context reversion and | |
1595 | * send back failure msg by virtchnl. | |
1596 | * | |
1597 | * Return: 0 on success, and other on error. | |
1598 | */ | |
1599 | static int | |
1600 | ice_vc_del_fdir_fltr_post(struct ice_vf *vf, struct ice_vf_fdir_ctx *ctx, | |
1601 | enum virtchnl_fdir_prgm_status status, | |
1602 | bool success) | |
1603 | { | |
1604 | struct virtchnl_fdir_fltr_conf *conf = ctx->conf; | |
1605 | struct device *dev = ice_pf_to_dev(vf->pf); | |
1606 | enum virtchnl_status_code v_ret; | |
1607 | struct virtchnl_fdir_del *resp; | |
1608 | int ret, len, is_tun; | |
1609 | ||
1610 | v_ret = VIRTCHNL_STATUS_SUCCESS; | |
1611 | len = sizeof(*resp); | |
1612 | resp = kzalloc(len, GFP_KERNEL); | |
1613 | if (!resp) { | |
1614 | len = 0; | |
1615 | v_ret = VIRTCHNL_STATUS_ERR_NO_MEMORY; | |
1616 | dev_dbg(dev, "VF %d: Alloc resp buf fail", vf->vf_id); | |
1617 | goto err_exit; | |
1618 | } | |
1619 | ||
1620 | if (!success) | |
1621 | goto err_exit; | |
1622 | ||
1623 | is_tun = 0; | |
1624 | resp->status = status; | |
1625 | ice_vc_fdir_remove_entry(vf, conf, conf->flow_id); | |
1626 | vf->fdir.fdir_fltr_cnt[conf->input.flow_type][is_tun]--; | |
1627 | ||
1628 | ret = ice_vc_send_msg_to_vf(vf, ctx->v_opcode, v_ret, | |
1629 | (u8 *)resp, len); | |
1630 | kfree(resp); | |
1631 | ||
1632 | dev_dbg(dev, "VF %d: flow_id:0x%X, FDIR %s success!\n", | |
1633 | vf->vf_id, conf->flow_id, | |
1634 | (ctx->v_opcode == VIRTCHNL_OP_ADD_FDIR_FILTER) ? | |
1635 | "add" : "del"); | |
1636 | devm_kfree(dev, conf); | |
1637 | return ret; | |
1638 | ||
1639 | err_exit: | |
1640 | if (resp) | |
1641 | resp->status = status; | |
1642 | if (success) | |
1643 | devm_kfree(dev, conf); | |
1644 | ||
1645 | ret = ice_vc_send_msg_to_vf(vf, ctx->v_opcode, v_ret, | |
1646 | (u8 *)resp, len); | |
1647 | kfree(resp); | |
1648 | return ret; | |
1649 | } | |
1650 | ||
1651 | /** | |
1652 | * ice_flush_fdir_ctx | |
1653 | * @pf: pointer to the PF structure | |
1654 | * | |
1655 | * Flush all the pending event on ctx_done list and process them. | |
1656 | */ | |
1657 | void ice_flush_fdir_ctx(struct ice_pf *pf) | |
1658 | { | |
c4c2c7db JK |
1659 | struct ice_vf *vf; |
1660 | unsigned int bkt; | |
d6218317 | 1661 | |
7e408e07 | 1662 | if (!test_and_clear_bit(ICE_FD_VF_FLUSH_CTX, pf->state)) |
d6218317 QZ |
1663 | return; |
1664 | ||
3d5985a1 | 1665 | mutex_lock(&pf->vfs.table_lock); |
c4c2c7db | 1666 | ice_for_each_vf(pf, bkt, vf) { |
d6218317 QZ |
1667 | struct device *dev = ice_pf_to_dev(pf); |
1668 | enum virtchnl_fdir_prgm_status status; | |
d6218317 QZ |
1669 | struct ice_vf_fdir_ctx *ctx; |
1670 | unsigned long flags; | |
1671 | int ret; | |
1672 | ||
1673 | if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) | |
1674 | continue; | |
1675 | ||
1676 | if (vf->ctrl_vsi_idx == ICE_NO_VSI) | |
1677 | continue; | |
1678 | ||
1679 | ctx = &vf->fdir.ctx_done; | |
1680 | spin_lock_irqsave(&vf->fdir.ctx_lock, flags); | |
1681 | if (!(ctx->flags & ICE_VF_FDIR_CTX_VALID)) { | |
1682 | spin_unlock_irqrestore(&vf->fdir.ctx_lock, flags); | |
1683 | continue; | |
1684 | } | |
1685 | spin_unlock_irqrestore(&vf->fdir.ctx_lock, flags); | |
1686 | ||
1687 | WARN_ON(ctx->stat == ICE_FDIR_CTX_READY); | |
1688 | if (ctx->stat == ICE_FDIR_CTX_TIMEOUT) { | |
1689 | status = VIRTCHNL_FDIR_FAILURE_RULE_TIMEOUT; | |
1690 | dev_err(dev, "VF %d: ctrl_vsi irq timeout\n", | |
1691 | vf->vf_id); | |
1692 | goto err_exit; | |
1693 | } | |
1694 | ||
1695 | ret = ice_vf_verify_rx_desc(vf, ctx, &status); | |
1696 | if (ret) | |
1697 | goto err_exit; | |
1698 | ||
1699 | if (ctx->v_opcode == VIRTCHNL_OP_ADD_FDIR_FILTER) | |
1700 | ice_vc_add_fdir_fltr_post(vf, ctx, status, true); | |
1701 | else if (ctx->v_opcode == VIRTCHNL_OP_DEL_FDIR_FILTER) | |
1702 | ice_vc_del_fdir_fltr_post(vf, ctx, status, true); | |
1703 | else | |
1704 | dev_err(dev, "VF %d: Unsupported opcode\n", vf->vf_id); | |
1705 | ||
1706 | spin_lock_irqsave(&vf->fdir.ctx_lock, flags); | |
1707 | ctx->flags &= ~ICE_VF_FDIR_CTX_VALID; | |
1708 | spin_unlock_irqrestore(&vf->fdir.ctx_lock, flags); | |
1709 | continue; | |
1710 | err_exit: | |
1711 | if (ctx->v_opcode == VIRTCHNL_OP_ADD_FDIR_FILTER) | |
1712 | ice_vc_add_fdir_fltr_post(vf, ctx, status, false); | |
1713 | else if (ctx->v_opcode == VIRTCHNL_OP_DEL_FDIR_FILTER) | |
1714 | ice_vc_del_fdir_fltr_post(vf, ctx, status, false); | |
1715 | else | |
1716 | dev_err(dev, "VF %d: Unsupported opcode\n", vf->vf_id); | |
1717 | ||
1718 | spin_lock_irqsave(&vf->fdir.ctx_lock, flags); | |
1719 | ctx->flags &= ~ICE_VF_FDIR_CTX_VALID; | |
1720 | spin_unlock_irqrestore(&vf->fdir.ctx_lock, flags); | |
1721 | } | |
3d5985a1 | 1722 | mutex_unlock(&pf->vfs.table_lock); |
d6218317 QZ |
1723 | } |
1724 | ||
1725 | /** | |
1726 | * ice_vc_fdir_set_irq_ctx - set FDIR context info for later IRQ handler | |
1727 | * @vf: pointer to the VF structure | |
1728 | * @conf: FDIR configuration for each filter | |
1729 | * @v_opcode: virtual channel operation code | |
1730 | * | |
1731 | * Return: 0 on success, and other on error. | |
1732 | */ | |
1733 | static int | |
1734 | ice_vc_fdir_set_irq_ctx(struct ice_vf *vf, struct virtchnl_fdir_fltr_conf *conf, | |
1735 | enum virtchnl_ops v_opcode) | |
1736 | { | |
1737 | struct device *dev = ice_pf_to_dev(vf->pf); | |
1738 | struct ice_vf_fdir_ctx *ctx; | |
1739 | unsigned long flags; | |
1740 | ||
1741 | ctx = &vf->fdir.ctx_irq; | |
1742 | spin_lock_irqsave(&vf->fdir.ctx_lock, flags); | |
1743 | if ((vf->fdir.ctx_irq.flags & ICE_VF_FDIR_CTX_VALID) || | |
1744 | (vf->fdir.ctx_done.flags & ICE_VF_FDIR_CTX_VALID)) { | |
1745 | spin_unlock_irqrestore(&vf->fdir.ctx_lock, flags); | |
1746 | dev_dbg(dev, "VF %d: Last request is still in progress\n", | |
1747 | vf->vf_id); | |
1748 | return -EBUSY; | |
1749 | } | |
1750 | ctx->flags |= ICE_VF_FDIR_CTX_VALID; | |
1751 | spin_unlock_irqrestore(&vf->fdir.ctx_lock, flags); | |
1752 | ||
1753 | ctx->conf = conf; | |
1754 | ctx->v_opcode = v_opcode; | |
1755 | ctx->stat = ICE_FDIR_CTX_READY; | |
1756 | timer_setup(&ctx->rx_tmr, ice_vf_fdir_timer, 0); | |
1757 | ||
1758 | mod_timer(&ctx->rx_tmr, round_jiffies(msecs_to_jiffies(10) + jiffies)); | |
1759 | ||
1760 | return 0; | |
1761 | } | |
1762 | ||
1763 | /** | |
1764 | * ice_vc_fdir_clear_irq_ctx - clear FDIR context info for IRQ handler | |
1765 | * @vf: pointer to the VF structure | |
1766 | * | |
1767 | * Return: 0 on success, and other on error. | |
1768 | */ | |
1769 | static void ice_vc_fdir_clear_irq_ctx(struct ice_vf *vf) | |
1770 | { | |
1771 | struct ice_vf_fdir_ctx *ctx = &vf->fdir.ctx_irq; | |
1772 | unsigned long flags; | |
1773 | ||
1774 | del_timer(&ctx->rx_tmr); | |
1775 | spin_lock_irqsave(&vf->fdir.ctx_lock, flags); | |
1776 | ctx->flags &= ~ICE_VF_FDIR_CTX_VALID; | |
1777 | spin_unlock_irqrestore(&vf->fdir.ctx_lock, flags); | |
1778 | } | |
1779 | ||
1f7ea1cd QZ |
1780 | /** |
1781 | * ice_vc_add_fdir_fltr - add a FDIR filter for VF by the msg buffer | |
1782 | * @vf: pointer to the VF info | |
1783 | * @msg: pointer to the msg buffer | |
1784 | * | |
1785 | * Return: 0 on success, and other on error. | |
1786 | */ | |
1787 | int ice_vc_add_fdir_fltr(struct ice_vf *vf, u8 *msg) | |
1788 | { | |
1789 | struct virtchnl_fdir_add *fltr = (struct virtchnl_fdir_add *)msg; | |
1790 | struct virtchnl_fdir_add *stat = NULL; | |
1791 | struct virtchnl_fdir_fltr_conf *conf; | |
1792 | enum virtchnl_status_code v_ret; | |
1793 | struct device *dev; | |
1794 | struct ice_pf *pf; | |
1795 | int is_tun = 0; | |
1796 | int len = 0; | |
1797 | int ret; | |
1798 | ||
1799 | pf = vf->pf; | |
1800 | dev = ice_pf_to_dev(pf); | |
1801 | ret = ice_vc_fdir_param_check(vf, fltr->vsi_id); | |
1802 | if (ret) { | |
1803 | v_ret = VIRTCHNL_STATUS_ERR_PARAM; | |
1804 | dev_dbg(dev, "Parameter check for VF %d failed\n", vf->vf_id); | |
1805 | goto err_exit; | |
1806 | } | |
1807 | ||
1808 | ret = ice_vf_start_ctrl_vsi(vf); | |
1809 | if (ret && (ret != -EEXIST)) { | |
1810 | v_ret = VIRTCHNL_STATUS_ERR_PARAM; | |
1811 | dev_err(dev, "Init FDIR for VF %d failed, ret:%d\n", | |
1812 | vf->vf_id, ret); | |
1813 | goto err_exit; | |
1814 | } | |
1815 | ||
1816 | stat = kzalloc(sizeof(*stat), GFP_KERNEL); | |
1817 | if (!stat) { | |
1818 | v_ret = VIRTCHNL_STATUS_ERR_NO_MEMORY; | |
1819 | dev_dbg(dev, "Alloc stat for VF %d failed\n", vf->vf_id); | |
1820 | goto err_exit; | |
1821 | } | |
1822 | ||
1823 | conf = devm_kzalloc(dev, sizeof(*conf), GFP_KERNEL); | |
1824 | if (!conf) { | |
1825 | v_ret = VIRTCHNL_STATUS_ERR_NO_MEMORY; | |
1826 | dev_dbg(dev, "Alloc conf for VF %d failed\n", vf->vf_id); | |
1827 | goto err_exit; | |
1828 | } | |
1829 | ||
1830 | len = sizeof(*stat); | |
1831 | ret = ice_vc_validate_fdir_fltr(vf, fltr, conf); | |
1832 | if (ret) { | |
1833 | v_ret = VIRTCHNL_STATUS_SUCCESS; | |
1834 | stat->status = VIRTCHNL_FDIR_FAILURE_RULE_INVALID; | |
1835 | dev_dbg(dev, "Invalid FDIR filter from VF %d\n", vf->vf_id); | |
1836 | goto err_free_conf; | |
1837 | } | |
1838 | ||
1839 | if (fltr->validate_only) { | |
1840 | v_ret = VIRTCHNL_STATUS_SUCCESS; | |
1841 | stat->status = VIRTCHNL_FDIR_SUCCESS; | |
1842 | devm_kfree(dev, conf); | |
1843 | ret = ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_ADD_FDIR_FILTER, | |
1844 | v_ret, (u8 *)stat, len); | |
1845 | goto exit; | |
1846 | } | |
1847 | ||
1848 | ret = ice_vc_fdir_config_input_set(vf, fltr, conf, is_tun); | |
1849 | if (ret) { | |
1850 | v_ret = VIRTCHNL_STATUS_SUCCESS; | |
1851 | stat->status = VIRTCHNL_FDIR_FAILURE_RULE_CONFLICT; | |
1852 | dev_err(dev, "VF %d: FDIR input set configure failed, ret:%d\n", | |
1853 | vf->vf_id, ret); | |
1854 | goto err_free_conf; | |
1855 | } | |
1856 | ||
1857 | ret = ice_vc_fdir_is_dup_fltr(vf, conf); | |
1858 | if (ret) { | |
1859 | v_ret = VIRTCHNL_STATUS_SUCCESS; | |
1860 | stat->status = VIRTCHNL_FDIR_FAILURE_RULE_EXIST; | |
1861 | dev_dbg(dev, "VF %d: duplicated FDIR rule detected\n", | |
1862 | vf->vf_id); | |
1863 | goto err_free_conf; | |
1864 | } | |
1865 | ||
d6218317 | 1866 | ret = ice_vc_fdir_insert_entry(vf, conf, &conf->flow_id); |
1f7ea1cd QZ |
1867 | if (ret) { |
1868 | v_ret = VIRTCHNL_STATUS_SUCCESS; | |
1869 | stat->status = VIRTCHNL_FDIR_FAILURE_RULE_NORESOURCE; | |
1870 | dev_dbg(dev, "VF %d: insert FDIR list failed\n", vf->vf_id); | |
1871 | goto err_free_conf; | |
1872 | } | |
1873 | ||
d6218317 QZ |
1874 | ret = ice_vc_fdir_set_irq_ctx(vf, conf, VIRTCHNL_OP_ADD_FDIR_FILTER); |
1875 | if (ret) { | |
1876 | v_ret = VIRTCHNL_STATUS_SUCCESS; | |
1877 | stat->status = VIRTCHNL_FDIR_FAILURE_RULE_NORESOURCE; | |
1878 | dev_dbg(dev, "VF %d: set FDIR context failed\n", vf->vf_id); | |
b4a01ace | 1879 | goto err_rem_entry; |
d6218317 QZ |
1880 | } |
1881 | ||
1f7ea1cd QZ |
1882 | ret = ice_vc_fdir_write_fltr(vf, conf, true, is_tun); |
1883 | if (ret) { | |
1884 | v_ret = VIRTCHNL_STATUS_SUCCESS; | |
1885 | stat->status = VIRTCHNL_FDIR_FAILURE_RULE_NORESOURCE; | |
1886 | dev_err(dev, "VF %d: writing FDIR rule failed, ret:%d\n", | |
1887 | vf->vf_id, ret); | |
b4a01ace | 1888 | goto err_clr_irq; |
1f7ea1cd QZ |
1889 | } |
1890 | ||
1f7ea1cd | 1891 | exit: |
1f7ea1cd QZ |
1892 | kfree(stat); |
1893 | return ret; | |
1894 | ||
b4a01ace | 1895 | err_clr_irq: |
d6218317 | 1896 | ice_vc_fdir_clear_irq_ctx(vf); |
b4a01ace | 1897 | err_rem_entry: |
d6218317 | 1898 | ice_vc_fdir_remove_entry(vf, conf, conf->flow_id); |
1f7ea1cd QZ |
1899 | err_free_conf: |
1900 | devm_kfree(dev, conf); | |
1901 | err_exit: | |
1902 | ret = ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_ADD_FDIR_FILTER, v_ret, | |
1903 | (u8 *)stat, len); | |
1904 | kfree(stat); | |
1905 | return ret; | |
1906 | } | |
1907 | ||
1908 | /** | |
1909 | * ice_vc_del_fdir_fltr - delete a FDIR filter for VF by the msg buffer | |
1910 | * @vf: pointer to the VF info | |
1911 | * @msg: pointer to the msg buffer | |
1912 | * | |
1913 | * Return: 0 on success, and other on error. | |
1914 | */ | |
1915 | int ice_vc_del_fdir_fltr(struct ice_vf *vf, u8 *msg) | |
1916 | { | |
1917 | struct virtchnl_fdir_del *fltr = (struct virtchnl_fdir_del *)msg; | |
1918 | struct virtchnl_fdir_del *stat = NULL; | |
1919 | struct virtchnl_fdir_fltr_conf *conf; | |
1920 | enum virtchnl_status_code v_ret; | |
1921 | struct device *dev; | |
1922 | struct ice_pf *pf; | |
1923 | int is_tun = 0; | |
1924 | int len = 0; | |
1925 | int ret; | |
1926 | ||
1927 | pf = vf->pf; | |
1928 | dev = ice_pf_to_dev(pf); | |
1929 | ret = ice_vc_fdir_param_check(vf, fltr->vsi_id); | |
1930 | if (ret) { | |
1931 | v_ret = VIRTCHNL_STATUS_ERR_PARAM; | |
1932 | dev_dbg(dev, "Parameter check for VF %d failed\n", vf->vf_id); | |
1933 | goto err_exit; | |
1934 | } | |
1935 | ||
1936 | stat = kzalloc(sizeof(*stat), GFP_KERNEL); | |
1937 | if (!stat) { | |
1938 | v_ret = VIRTCHNL_STATUS_ERR_NO_MEMORY; | |
1939 | dev_dbg(dev, "Alloc stat for VF %d failed\n", vf->vf_id); | |
1940 | goto err_exit; | |
1941 | } | |
1942 | ||
1943 | len = sizeof(*stat); | |
1944 | ||
1945 | conf = ice_vc_fdir_lookup_entry(vf, fltr->flow_id); | |
1946 | if (!conf) { | |
1947 | v_ret = VIRTCHNL_STATUS_SUCCESS; | |
1948 | stat->status = VIRTCHNL_FDIR_FAILURE_RULE_NONEXIST; | |
1949 | dev_dbg(dev, "VF %d: FDIR invalid flow_id:0x%X\n", | |
1950 | vf->vf_id, fltr->flow_id); | |
1951 | goto err_exit; | |
1952 | } | |
1953 | ||
1954 | /* Just return failure when ctrl_vsi idx is invalid */ | |
1955 | if (vf->ctrl_vsi_idx == ICE_NO_VSI) { | |
1956 | v_ret = VIRTCHNL_STATUS_SUCCESS; | |
1957 | stat->status = VIRTCHNL_FDIR_FAILURE_RULE_NORESOURCE; | |
1958 | dev_err(dev, "Invalid FDIR ctrl_vsi for VF %d\n", vf->vf_id); | |
1959 | goto err_exit; | |
1960 | } | |
1961 | ||
d6218317 QZ |
1962 | ret = ice_vc_fdir_set_irq_ctx(vf, conf, VIRTCHNL_OP_DEL_FDIR_FILTER); |
1963 | if (ret) { | |
1964 | v_ret = VIRTCHNL_STATUS_SUCCESS; | |
1965 | stat->status = VIRTCHNL_FDIR_FAILURE_RULE_NORESOURCE; | |
1966 | dev_dbg(dev, "VF %d: set FDIR context failed\n", vf->vf_id); | |
1967 | goto err_exit; | |
1968 | } | |
1969 | ||
1f7ea1cd QZ |
1970 | ret = ice_vc_fdir_write_fltr(vf, conf, false, is_tun); |
1971 | if (ret) { | |
1972 | v_ret = VIRTCHNL_STATUS_SUCCESS; | |
1973 | stat->status = VIRTCHNL_FDIR_FAILURE_RULE_NORESOURCE; | |
1974 | dev_err(dev, "VF %d: writing FDIR rule failed, ret:%d\n", | |
1975 | vf->vf_id, ret); | |
d6218317 | 1976 | goto err_del_tmr; |
1f7ea1cd QZ |
1977 | } |
1978 | ||
d6218317 | 1979 | kfree(stat); |
1f7ea1cd | 1980 | |
d6218317 | 1981 | return ret; |
1f7ea1cd | 1982 | |
d6218317 QZ |
1983 | err_del_tmr: |
1984 | ice_vc_fdir_clear_irq_ctx(vf); | |
1f7ea1cd QZ |
1985 | err_exit: |
1986 | ret = ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_DEL_FDIR_FILTER, v_ret, | |
1987 | (u8 *)stat, len); | |
1988 | kfree(stat); | |
1989 | return ret; | |
1990 | } | |
1991 | ||
1992 | /** | |
1993 | * ice_vf_fdir_init - init FDIR resource for VF | |
1994 | * @vf: pointer to the VF info | |
1995 | */ | |
1996 | void ice_vf_fdir_init(struct ice_vf *vf) | |
1997 | { | |
1998 | struct ice_vf_fdir *fdir = &vf->fdir; | |
1999 | ||
2000 | idr_init(&fdir->fdir_rule_idr); | |
2001 | INIT_LIST_HEAD(&fdir->fdir_rule_list); | |
d6218317 QZ |
2002 | |
2003 | spin_lock_init(&fdir->ctx_lock); | |
2004 | fdir->ctx_irq.flags = 0; | |
2005 | fdir->ctx_done.flags = 0; | |
83c911dc | 2006 | ice_vc_fdir_reset_cnt_all(fdir); |
1f7ea1cd QZ |
2007 | } |
2008 | ||
2009 | /** | |
2010 | * ice_vf_fdir_exit - destroy FDIR resource for VF | |
2011 | * @vf: pointer to the VF info | |
2012 | */ | |
2013 | void ice_vf_fdir_exit(struct ice_vf *vf) | |
2014 | { | |
2015 | ice_vc_fdir_flush_entry(vf); | |
2016 | idr_destroy(&vf->fdir.fdir_rule_idr); | |
2017 | ice_vc_fdir_rem_prof_all(vf); | |
2018 | ice_vc_fdir_free_prof_all(vf); | |
2019 | } |