Jianbo Liu says: ==================== flow_offload: add tc police parameters As a preparation for more advanced police offload in mlx5 (e.g., jumping to another chain when bandwidth is not exceeded), extend the flow offload API with more tc-police parameters. Adjust existing drivers to reject unsupported configurations. Changes since v2: * Rename index to extval in exceed and notexceed acts. * Add policer validate functions for all drivers. Changes since v1: * Add one more strict validation for the control of drop/ok. ====================
Unified · Split
| @@ -300,6 +300,46 @@ static int sja1105_flower_parse_key(struct sja1105_private *priv, · 299 unmodified lines | |||
| 300 | return -EOPNOTSUPP; | 300 | return -EOPNOTSUPP; |
| 301 | } | 301 | } |
| 302 | 302 | ||
| 303 | static int sja1105_policer_validate(const struct flow_action *action, | ||
| 304 | const struct flow_action_entry *act, | ||
| 305 | struct netlink_ext_ack *extack) | ||
| 306 | { | ||
| 307 | if (act->police.exceed.act_id != FLOW_ACTION_DROP) { | ||
| 308 | NL_SET_ERR_MSG_MOD(extack, | ||
| 309 | "Offload not supported when exceed action is not drop"); | ||
| 310 | return -EOPNOTSUPP; | ||
| 311 | } | ||
| 312 | |||
| 313 | if (act->police.notexceed.act_id != FLOW_ACTION_PIPE && | ||
| 314 | act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) { | ||
| 315 | NL_SET_ERR_MSG_MOD(extack, | ||
| 316 | "Offload not supported when conform action is not pipe or ok"); | ||
| 317 | return -EOPNOTSUPP; | ||
| 318 | } | ||
| 319 | |||
| 320 | if (act->police.notexceed.act_id == FLOW_ACTION_ACCEPT && | ||
| 321 | !flow_action_is_last_entry(action, act)) { | ||
| 322 | NL_SET_ERR_MSG_MOD(extack, | ||
| 323 | "Offload not supported when conform action is ok, but action is not last"); | ||
| 324 | return -EOPNOTSUPP; | ||
| 325 | } | ||
| 326 | |||
| 327 | if (act->police.peakrate_bytes_ps || | ||
| 328 | act->police.avrate || act->police.overhead) { | ||
| 329 | NL_SET_ERR_MSG_MOD(extack, | ||
| 330 | "Offload not supported when peakrate/avrate/overhead is configured"); | ||
| 331 | return -EOPNOTSUPP; | ||
| 332 | } | ||
| 333 | |||
| 334 | if (act->police.rate_pkt_ps) { | ||
| 335 | NL_SET_ERR_MSG_MOD(extack, | ||
| 336 | "QoS offload not support packets per second"); | ||
| 337 | return -EOPNOTSUPP; | ||
| 338 | } | ||
| 339 | |||
| 340 | return 0; | ||
| 341 | } | ||
| 342 | |||
| 303 | int sja1105_cls_flower_add(struct dsa_switch *ds, int port, | 343 | int sja1105_cls_flower_add(struct dsa_switch *ds, int port, |
| 304 | struct flow_cls_offload *cls, bool ingress) | 344 | struct flow_cls_offload *cls, bool ingress) |
| 305 | { | 345 | { |
| @@ -321,12 +361,9 @@ int sja1105_cls_flower_add(struct dsa_switch *ds, int port, · 15 unmodified lines | |||
| 321 | flow_action_for_each(i, act, &rule->action) { | 361 | flow_action_for_each(i, act, &rule->action) { |
| 322 | switch (act->id) { | 362 | switch (act->id) { |
| 323 | case FLOW_ACTION_POLICE: | 363 | case FLOW_ACTION_POLICE: |
| 324 | if (act->police.rate_pkt_ps) { | 364 | rc = sja1105_policer_validate(&rule->action, act, extack); |
| 325 | NL_SET_ERR_MSG_MOD(extack, | 365 | if (rc) |
| 326 | "QoS offload not support packets per second"); | ||
| 327 | rc = -EOPNOTSUPP; | ||
| 328 | goto out; | 366 | goto out; |
| 329 | } | ||
| 330 | 367 | ||
| 331 | rc = sja1105_flower_policer(priv, port, extack, cookie, | 368 | rc = sja1105_flower_policer(priv, port, extack, cookie, |
| 332 | &key, | 369 | &key, |
| @@ -8,6 +8,46 @@ · 7 unmodified lines | |||
| 8 | #include "cxgb4_filter.h" | 8 | #include "cxgb4_filter.h" |
| 9 | #include "cxgb4_tc_flower.h" | 9 | #include "cxgb4_tc_flower.h" |
| 10 | 10 | ||
| 11 | static int cxgb4_policer_validate(const struct flow_action *action, | ||
| 12 | const struct flow_action_entry *act, | ||
| 13 | struct netlink_ext_ack *extack) | ||
| 14 | { | ||
| 15 | if (act->police.exceed.act_id != FLOW_ACTION_DROP) { | ||
| 16 | NL_SET_ERR_MSG_MOD(extack, | ||
| 17 | "Offload not supported when exceed action is not drop"); | ||
| 18 | return -EOPNOTSUPP; | ||
| 19 | } | ||
| 20 | |||
| 21 | if (act->police.notexceed.act_id != FLOW_ACTION_PIPE && | ||
| 22 | act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) { | ||
| 23 | NL_SET_ERR_MSG_MOD(extack, | ||
| 24 | "Offload not supported when conform action is not pipe or ok"); | ||
| 25 | return -EOPNOTSUPP; | ||
| 26 | } | ||
| 27 | |||
| 28 | if (act->police.notexceed.act_id == FLOW_ACTION_ACCEPT && | ||
| 29 | !flow_action_is_last_entry(action, act)) { | ||
| 30 | NL_SET_ERR_MSG_MOD(extack, | ||
| 31 | "Offload not supported when conform action is ok, but action is not last"); | ||
| 32 | return -EOPNOTSUPP; | ||
| 33 | } | ||
| 34 | |||
| 35 | if (act->police.peakrate_bytes_ps || | ||
| 36 | act->police.avrate || act->police.overhead) { | ||
| 37 | NL_SET_ERR_MSG_MOD(extack, | ||
| 38 | "Offload not supported when peakrate/avrate/overhead is configured"); | ||
| 39 | return -EOPNOTSUPP; | ||
| 40 | } | ||
| 41 | |||
| 42 | if (act->police.rate_pkt_ps) { | ||
| 43 | NL_SET_ERR_MSG_MOD(extack, | ||
| 44 | "QoS offload not support packets per second"); | ||
| 45 | return -EOPNOTSUPP; | ||
| 46 | } | ||
| 47 | |||
| 48 | return 0; | ||
| 49 | } | ||
| 50 | |||
| 11 | static int cxgb4_matchall_egress_validate(struct net_device *dev, | 51 | static int cxgb4_matchall_egress_validate(struct net_device *dev, |
| 12 | struct tc_cls_matchall_offload *cls) | 52 | struct tc_cls_matchall_offload *cls) |
| 13 | { | 53 | { |
| @@ -48,11 +88,10 @@ static int cxgb4_matchall_egress_validate(struct net_device *dev, · 34 unmodified lines | |||
| 48 | flow_action_for_each(i, entry, actions) { | 88 | flow_action_for_each(i, entry, actions) { |
| 49 | switch (entry->id) { | 89 | switch (entry->id) { |
| 50 | case FLOW_ACTION_POLICE: | 90 | case FLOW_ACTION_POLICE: |
| 51 | if (entry->police.rate_pkt_ps) { | 91 | ret = cxgb4_policer_validate(actions, entry, extack); |
| 52 | NL_SET_ERR_MSG_MOD(extack, | 92 | if (ret) |
| 53 | "QoS offload not support packets per second"); | 93 | return ret; |
| 54 | return -EOPNOTSUPP; | 94 | |
| 55 | } | ||
| 56 | /* Convert bytes per second to bits per second */ | 95 | /* Convert bytes per second to bits per second */ |
| 57 | if (entry->police.rate_bytes_ps * 8 > max_link_rate) { | 96 | if (entry->police.rate_bytes_ps * 8 > max_link_rate) { |
| 58 | NL_SET_ERR_MSG_MOD(extack, | 97 | NL_SET_ERR_MSG_MOD(extack, |
| @@ -150,11 +189,11 @@ static int cxgb4_matchall_alloc_tc(struct net_device *dev, · 91 unmodified lines | |||
| 150 | flow_action_for_each(i, entry, &cls->rule->action) | 189 | flow_action_for_each(i, entry, &cls->rule->action) |
| 151 | if (entry->id == FLOW_ACTION_POLICE) | 190 | if (entry->id == FLOW_ACTION_POLICE) |
| 152 | break; | 191 | break; |
| 153 | if (entry->police.rate_pkt_ps) { | 192 | |
| 154 | NL_SET_ERR_MSG_MOD(extack, | 193 | ret = cxgb4_policer_validate(&cls->rule->action, entry, extack); |
| 155 | "QoS offload not support packets per second"); | 194 | if (ret) |
| 156 | return -EOPNOTSUPP; | 195 | return ret; |
| 157 | } | 196 | |
| 158 | /* Convert from bytes per second to Kbps */ | 197 | /* Convert from bytes per second to Kbps */ |
| 159 | p.u.params.maxrate = div_u64(entry->police.rate_bytes_ps * 8, 1000); | 198 | p.u.params.maxrate = div_u64(entry->police.rate_bytes_ps * 8, 1000); |
| 160 | p.u.params.channel = pi->tx_chan; | 199 | p.u.params.channel = pi->tx_chan; |
| @@ -1021,6 +1021,46 @@ static struct actions_fwd *enetc_check_flow_actions(u64 acts, · 1020 unmodified lines | |||
| 1021 | return NULL; | 1021 | return NULL; |
| 1022 | } | 1022 | } |
| 1023 | 1023 | ||
| 1024 | static int enetc_psfp_policer_validate(const struct flow_action *action, | ||
| 1025 | const struct flow_action_entry *act, | ||
| 1026 | struct netlink_ext_ack *extack) | ||
| 1027 | { | ||
| 1028 | if (act->police.exceed.act_id != FLOW_ACTION_DROP) { | ||
| 1029 | NL_SET_ERR_MSG_MOD(extack, | ||
| 1030 | "Offload not supported when exceed action is not drop"); | ||
| 1031 | return -EOPNOTSUPP; | ||
| 1032 | } | ||
| 1033 | |||
| 1034 | if (act->police.notexceed.act_id != FLOW_ACTION_PIPE && | ||
| 1035 | act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) { | ||
| 1036 | NL_SET_ERR_MSG_MOD(extack, | ||
| 1037 | "Offload not supported when conform action is not pipe or ok"); | ||
| 1038 | return -EOPNOTSUPP; | ||
| 1039 | } | ||
| 1040 | |||
| 1041 | if (act->police.notexceed.act_id == FLOW_ACTION_ACCEPT && | ||
| 1042 | !flow_action_is_last_entry(action, act)) { | ||
| 1043 | NL_SET_ERR_MSG_MOD(extack, | ||
| 1044 | "Offload not supported when conform action is ok, but action is not last"); | ||
| 1045 | return -EOPNOTSUPP; | ||
| 1046 | } | ||
| 1047 | |||
| 1048 | if (act->police.peakrate_bytes_ps || | ||
| 1049 | act->police.avrate || act->police.overhead) { | ||
| 1050 | NL_SET_ERR_MSG_MOD(extack, | ||
| 1051 | "Offload not supported when peakrate/avrate/overhead is configured"); | ||
| 1052 | return -EOPNOTSUPP; | ||
| 1053 | } | ||
| 1054 | |||
| 1055 | if (act->police.rate_pkt_ps) { | ||
| 1056 | NL_SET_ERR_MSG_MOD(extack, | ||
| 1057 | "QoS offload not support packets per second"); | ||
| 1058 | return -EOPNOTSUPP; | ||
| 1059 | } | ||
| 1060 | |||
| 1061 | return 0; | ||
| 1062 | } | ||
| 1063 | |||
| 1024 | static int enetc_psfp_parse_clsflower(struct enetc_ndev_priv *priv, | 1064 | static int enetc_psfp_parse_clsflower(struct enetc_ndev_priv *priv, |
| 1025 | struct flow_cls_offload *f) | 1065 | struct flow_cls_offload *f) |
| 1026 | { | 1066 | { |
| @@ -1177,11 +1217,10 @@ static int enetc_psfp_parse_clsflower(struct enetc_ndev_priv *priv, · 150 unmodified lines | |||
| 1177 | 1217 | ||
| 1178 | /* Flow meter and max frame size */ | 1218 | /* Flow meter and max frame size */ |
| 1179 | if (entryp) { | 1219 | if (entryp) { |
| 1180 | if (entryp->police.rate_pkt_ps) { | 1220 | err = enetc_psfp_policer_validate(&rule->action, entryp, extack); |
| 1181 | NL_SET_ERR_MSG_MOD(extack, "QoS offload not support packets per second"); | 1221 | if (err) |
| 1182 | err = -EOPNOTSUPP; | ||
| 1183 | goto free_sfi; | 1222 | goto free_sfi; |
| 1184 | } | 1223 | |
| 1185 | if (entryp->police.burst) { | 1224 | if (entryp->police.burst) { |
| 1186 | fmi = kzalloc(sizeof(*fmi), GFP_KERNEL); | 1225 | fmi = kzalloc(sizeof(*fmi), GFP_KERNEL); |
| 1187 | if (!fmi) { | 1226 | if (!fmi) { |
| @@ -190,6 +190,40 @@ static int otx2_tc_validate_flow(struct otx2_nic *nic, · 189 unmodified lines | |||
| 190 | return 0; | 190 | return 0; |
| 191 | } | 191 | } |
| 192 | 192 | ||
| 193 | static int otx2_policer_validate(const struct flow_action *action, | ||
| 194 | const struct flow_action_entry *act, | ||
| 195 | struct netlink_ext_ack *extack) | ||
| 196 | { | ||
| 197 | if (act->police.exceed.act_id != FLOW_ACTION_DROP) { | ||
| 198 | NL_SET_ERR_MSG_MOD(extack, | ||
| 199 | "Offload not supported when exceed action is not drop"); | ||
| 200 | return -EOPNOTSUPP; | ||
| 201 | } | ||
| 202 | |||
| 203 | if (act->police.notexceed.act_id != FLOW_ACTION_PIPE && | ||
| 204 | act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) { | ||
| 205 | NL_SET_ERR_MSG_MOD(extack, | ||
| 206 | "Offload not supported when conform action is not pipe or ok"); | ||
| 207 | return -EOPNOTSUPP; | ||
| 208 | } | ||
| 209 | |||
| 210 | if (act->police.notexceed.act_id == FLOW_ACTION_ACCEPT && | ||
| 211 | !flow_action_is_last_entry(action, act)) { | ||
| 212 | NL_SET_ERR_MSG_MOD(extack, | ||
| 213 | "Offload not supported when conform action is ok, but action is not last"); | ||
| 214 | return -EOPNOTSUPP; | ||
| 215 | } | ||
| 216 | |||
| 217 | if (act->police.peakrate_bytes_ps || | ||
| 218 | act->police.avrate || act->police.overhead) { | ||
| 219 | NL_SET_ERR_MSG_MOD(extack, | ||
| 220 | "Offload not supported when peakrate/avrate/overhead is configured"); | ||
| 221 | return -EOPNOTSUPP; | ||
| 222 | } | ||
| 223 | |||
| 224 | return 0; | ||
| 225 | } | ||
| 226 | |||
| 193 | static int otx2_tc_egress_matchall_install(struct otx2_nic *nic, | 227 | static int otx2_tc_egress_matchall_install(struct otx2_nic *nic, |
| 194 | struct tc_cls_matchall_offload *cls) | 228 | struct tc_cls_matchall_offload *cls) |
| 195 | { | 229 | { |
| @@ -212,6 +246,10 @@ static int otx2_tc_egress_matchall_install(struct otx2_nic *nic, · 16 unmodified lines | |||
| 212 | entry = &cls->rule->action.entries[0]; | 246 | entry = &cls->rule->action.entries[0]; |
| 213 | switch (entry->id) { | 247 | switch (entry->id) { |
| 214 | case FLOW_ACTION_POLICE: | 248 | case FLOW_ACTION_POLICE: |
| 249 | err = otx2_policer_validate(&cls->rule->action, entry, extack); | ||
| 250 | if (err) | ||
| 251 | return err; | ||
| 252 | |||
| 215 | if (entry->police.rate_pkt_ps) { | 253 | if (entry->police.rate_pkt_ps) { |
| 216 | NL_SET_ERR_MSG_MOD(extack, "QoS offload not support packets per second"); | 254 | NL_SET_ERR_MSG_MOD(extack, "QoS offload not support packets per second"); |
| 217 | return -EOPNOTSUPP; | 255 | return -EOPNOTSUPP; |
| @@ -315,6 +353,7 @@ static int otx2_tc_parse_actions(struct otx2_nic *nic, · 97 unmodified lines | |||
| 315 | u8 nr_police = 0; | 353 | u8 nr_police = 0; |
| 316 | bool pps = false; | 354 | bool pps = false; |
| 317 | u64 rate; | 355 | u64 rate; |
| 356 | int err; | ||
| 318 | int i; | 357 | int i; |
| 319 | 358 | ||
| 320 | if (!flow_action_has_entries(flow_action)) { | 359 | if (!flow_action_has_entries(flow_action)) { |
| @@ -355,6 +394,10 @@ static int otx2_tc_parse_actions(struct otx2_nic *nic, · 34 unmodified lines | |||
| 355 | return -EOPNOTSUPP; | 394 | return -EOPNOTSUPP; |
| 356 | } | 395 | } |
| 357 | 396 | ||
| 397 | err = otx2_policer_validate(flow_action, act, extack); | ||
| 398 | if (err) | ||
| 399 | return err; | ||
| 400 | |||
| 358 | if (act->police.rate_bytes_ps > 0) { | 401 | if (act->police.rate_bytes_ps > 0) { |
| 359 | rate = act->police.rate_bytes_ps * 8; | 402 | rate = act->police.rate_bytes_ps * 8; |
| 360 | burst = act->police.burst; | 403 | burst = act->police.burst; |
| @@ -4482,6 +4482,46 @@ static int apply_police_params(struct mlx5e_priv *priv, u64 rate, · 4481 unmodified lines | |||
| 4482 | return err; | 4482 | return err; |
| 4483 | } | 4483 | } |
| 4484 | 4484 | ||
| 4485 | static int mlx5e_policer_validate(const struct flow_action *action, | ||
| 4486 | const struct flow_action_entry *act, | ||
| 4487 | struct netlink_ext_ack *extack) | ||
| 4488 | { | ||
| 4489 | if (act->police.exceed.act_id != FLOW_ACTION_DROP) { | ||
| 4490 | NL_SET_ERR_MSG_MOD(extack, | ||
| 4491 | "Offload not supported when exceed action is not drop"); | ||
| 4492 | return -EOPNOTSUPP; | ||
| 4493 | } | ||
| 4494 | |||
| 4495 | if (act->police.notexceed.act_id != FLOW_ACTION_PIPE && | ||
| 4496 | act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) { | ||
| 4497 | NL_SET_ERR_MSG_MOD(extack, | ||
| 4498 | "Offload not supported when conform action is not pipe or ok"); | ||
| 4499 | return -EOPNOTSUPP; | ||
| 4500 | } | ||
| 4501 | |||
| 4502 | if (act->police.notexceed.act_id == FLOW_ACTION_ACCEPT && | ||
| 4503 | !flow_action_is_last_entry(action, act)) { | ||
| 4504 | NL_SET_ERR_MSG_MOD(extack, | ||
| 4505 | "Offload not supported when conform action is ok, but action is not last"); | ||
| 4506 | return -EOPNOTSUPP; | ||
| 4507 | } | ||
| 4508 | |||
| 4509 | if (act->police.peakrate_bytes_ps || | ||
| 4510 | act->police.avrate || act->police.overhead) { | ||
| 4511 | NL_SET_ERR_MSG_MOD(extack, | ||
| 4512 | "Offload not supported when peakrate/avrate/overhead is configured"); | ||
| 4513 | return -EOPNOTSUPP; | ||
| 4514 | } | ||
| 4515 | |||
| 4516 | if (act->police.rate_pkt_ps) { | ||
| 4517 | NL_SET_ERR_MSG_MOD(extack, | ||
| 4518 | "QoS offload not support packets per second"); | ||
| 4519 | return -EOPNOTSUPP; | ||
| 4520 | } | ||
| 4521 | |||
| 4522 | return 0; | ||
| 4523 | } | ||
| 4524 | |||
| 4485 | static int scan_tc_matchall_fdb_actions(struct mlx5e_priv *priv, | 4525 | static int scan_tc_matchall_fdb_actions(struct mlx5e_priv *priv, |
| 4486 | struct flow_action *flow_action, | 4526 | struct flow_action *flow_action, |
| 4487 | struct netlink_ext_ack *extack) | 4527 | struct netlink_ext_ack *extack) |
| @@ -4509,10 +4549,10 @@ static int scan_tc_matchall_fdb_actions(struct mlx5e_priv *priv, · 21 unmodified lines | |||
| 4509 | flow_action_for_each(i, act, flow_action) { | 4549 | flow_action_for_each(i, act, flow_action) { |
| 4510 | switch (act->id) { | 4550 | switch (act->id) { |
| 4511 | case FLOW_ACTION_POLICE: | 4551 | case FLOW_ACTION_POLICE: |
| 4512 | if (act->police.rate_pkt_ps) { | 4552 | err = mlx5e_policer_validate(flow_action, act, extack); |
| 4513 | NL_SET_ERR_MSG_MOD(extack, "QoS offload not support packets per second"); | 4553 | if (err) |
| 4514 | return -EOPNOTSUPP; | 4554 | return err; |
| 4515 | } | 4555 | |
| 4516 | err = apply_police_params(priv, act->police.rate_bytes_ps, extack); | 4556 | err = apply_police_params(priv, act->police.rate_bytes_ps, extack); |
| 4517 | if (err) | 4557 | if (err) |
| 4518 | return err; | 4558 | return err; |
| @@ -15,6 +15,46 @@ · 14 unmodified lines | |||
| 15 | #include "spectrum.h" | 15 | #include "spectrum.h" |
| 16 | #include "core_acl_flex_keys.h" | 16 | #include "core_acl_flex_keys.h" |
| 17 | 17 | ||
| 18 | static int mlxsw_sp_policer_validate(const struct flow_action *action, | ||
| 19 | const struct flow_action_entry *act, | ||
| 20 | struct netlink_ext_ack *extack) | ||
| 21 | { | ||
| 22 | if (act->police.exceed.act_id != FLOW_ACTION_DROP) { | ||
| 23 | NL_SET_ERR_MSG_MOD(extack, | ||
| 24 | "Offload not supported when exceed action is not drop"); | ||
| 25 | return -EOPNOTSUPP; | ||
| 26 | } | ||
| 27 | |||
| 28 | if (act->police.notexceed.act_id != FLOW_ACTION_PIPE && | ||
| 29 | act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) { | ||
| 30 | NL_SET_ERR_MSG_MOD(extack, | ||
| 31 | "Offload not supported when conform action is not pipe or ok"); | ||
| 32 | return -EOPNOTSUPP; | ||
| 33 | } | ||
| 34 | |||
| 35 | if (act->police.notexceed.act_id == FLOW_ACTION_ACCEPT && | ||
| 36 | !flow_action_is_last_entry(action, act)) { | ||
| 37 | NL_SET_ERR_MSG_MOD(extack, | ||
| 38 | "Offload not supported when conform action is ok, but action is not last"); | ||
| 39 | return -EOPNOTSUPP; | ||
| 40 | } | ||
| 41 | |||
| 42 | if (act->police.peakrate_bytes_ps || | ||
| 43 | act->police.avrate || act->police.overhead) { | ||
| 44 | NL_SET_ERR_MSG_MOD(extack, | ||
| 45 | "Offload not supported when peakrate/avrate/overhead is configured"); | ||
| 46 | return -EOPNOTSUPP; | ||
| 47 | } | ||
| 48 | |||
| 49 | if (act->police.rate_pkt_ps) { | ||
| 50 | NL_SET_ERR_MSG_MOD(extack, | ||
| 51 | "QoS offload not support packets per second"); | ||
| 52 | return -EOPNOTSUPP; | ||
| 53 | } | ||
| 54 | |||
| 55 | return 0; | ||
| 56 | } | ||
| 57 | |||
| 18 | static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp, | 58 | static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp, |
| 19 | struct mlxsw_sp_flow_block *block, | 59 | struct mlxsw_sp_flow_block *block, |
| 20 | struct mlxsw_sp_acl_rule_info *rulei, | 60 | struct mlxsw_sp_acl_rule_info *rulei, |
| @@ -191,10 +231,9 @@ static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp, · 170 unmodified lines | |||
| 191 | return -EOPNOTSUPP; | 231 | return -EOPNOTSUPP; |
| 192 | } | 232 | } |
| 193 | 233 | ||
| 194 | if (act->police.rate_pkt_ps) { | 234 | err = mlxsw_sp_policer_validate(flow_action, act, extack); |
| 195 | NL_SET_ERR_MSG_MOD(extack, "QoS offload not support packets per second"); | 235 | if (err) |
| 196 | return -EOPNOTSUPP; | 236 | return err; |
| 197 | } | ||
| 198 | 237 | ||
| 199 | /* The kernel might adjust the requested burst size so | 238 | /* The kernel might adjust the requested burst size so |
| 200 | * that it is not exactly a power of two. Re-adjust it | 239 | * that it is not exactly a power of two. Re-adjust it |
| @@ -6,6 +6,7 @@ · 5 unmodified lines | |||
| 6 | #include <net/pkt_cls.h> | 6 | #include <net/pkt_cls.h> |
| 7 | #include <net/tc_act/tc_gact.h> | 7 | #include <net/tc_act/tc_gact.h> |
| 8 | #include <soc/mscc/ocelot_vcap.h> | 8 | #include <soc/mscc/ocelot_vcap.h> |
| 9 | #include "ocelot_police.h" | ||
| 9 | #include "ocelot_vcap.h" | 10 | #include "ocelot_vcap.h" |
| 10 | 11 | ||
| 11 | /* Arbitrarily chosen constants for encoding the VCAP block and lookup number | 12 | /* Arbitrarily chosen constants for encoding the VCAP block and lookup number |
| @@ -217,6 +218,7 @@ static int ocelot_flower_parse_action(struct ocelot *ocelot, int port, · 205 unmodified lines | |||
| 217 | bool ingress, struct flow_cls_offload *f, | 218 | bool ingress, struct flow_cls_offload *f, |
| 218 | struct ocelot_vcap_filter *filter) | 219 | struct ocelot_vcap_filter *filter) |
| 219 | { | 220 | { |
| 221 | const struct flow_action *action = &f->rule->action; | ||
| 220 | struct netlink_ext_ack *extack = f->common.extack; | 222 | struct netlink_ext_ack *extack = f->common.extack; |
| 221 | bool allow_missing_goto_target = false; | 223 | bool allow_missing_goto_target = false; |
| 222 | const struct flow_action_entry *a; | 224 | const struct flow_action_entry *a; |
| @@ -244,7 +246,7 @@ static int ocelot_flower_parse_action(struct ocelot *ocelot, int port, · 21 unmodified lines | |||
| 244 | filter->goto_target = -1; | 246 | filter->goto_target = -1; |
| 245 | filter->type = OCELOT_VCAP_FILTER_DUMMY; | 247 | filter->type = OCELOT_VCAP_FILTER_DUMMY; |
| 246 | 248 | ||
| 247 | flow_action_for_each(i, a, &f->rule->action) { | 249 | flow_action_for_each(i, a, action) { |
| 248 | switch (a->id) { | 250 | switch (a->id) { |
| 249 | case FLOW_ACTION_DROP: | 251 | case FLOW_ACTION_DROP: |
| 250 | if (filter->block_id != VCAP_IS2) { | 252 | if (filter->block_id != VCAP_IS2) { |
| @@ -297,11 +299,11 @@ static int ocelot_flower_parse_action(struct ocelot *ocelot, int port, · 46 unmodified lines | |||
| 297 | "Last action must be GOTO"); | 299 | "Last action must be GOTO"); |
| 298 | return -EOPNOTSUPP; | 300 | return -EOPNOTSUPP; |
| 299 | } | 301 | } |
| 300 | if (a->police.rate_pkt_ps) { | 302 | |
| 301 | NL_SET_ERR_MSG_MOD(extack, | 303 | err = ocelot_policer_validate(action, a, extack); |
| 302 | "QoS offload not support packets per second"); | 304 | if (err) |
| 303 | return -EOPNOTSUPP; | 305 | return err; |
| 304 | } | 306 | |
| 305 | filter->action.police_ena = true; | 307 | filter->action.police_ena = true; |
| 306 | 308 | ||
| 307 | pol_ix = a->hw_index + ocelot->vcap_pol.base; | 309 | pol_ix = a->hw_index + ocelot->vcap_pol.base; |
| @@ -14,6 +14,7 @@ · 13 unmodified lines | |||
| 14 | #include <linux/phy/phy.h> | 14 | #include <linux/phy/phy.h> |
| 15 | #include <net/pkt_cls.h> | 15 | #include <net/pkt_cls.h> |
| 16 | #include "ocelot.h" | 16 | #include "ocelot.h" |
| 17 | #include "ocelot_police.h" | ||
| 17 | #include "ocelot_vcap.h" | 18 | #include "ocelot_vcap.h" |
| 18 | #include "ocelot_fdma.h" | 19 | #include "ocelot_fdma.h" |
| 19 | 20 | ||
| @@ -258,11 +259,10 @@ static int ocelot_setup_tc_cls_matchall(struct ocelot_port_private *priv, · 238 unmodified lines | |||
| 258 | return -EEXIST; | 259 | return -EEXIST; |
| 259 | } | 260 | } |
| 260 | 261 | ||
| 261 | if (action->police.rate_pkt_ps) { | 262 | err = ocelot_policer_validate(&f->rule->action, action, |
| 262 | NL_SET_ERR_MSG_MOD(extack, | 263 | extack); |
| 263 | "QoS offload not support packets per second"); | 264 | if (err) |
| 264 | return -EOPNOTSUPP; | 265 | return err; |
| 265 | } | ||
| 266 | 266 | ||
| 267 | pol.rate = (u32)div_u64(action->police.rate_bytes_ps, 1000) * 8; | 267 | pol.rate = (u32)div_u64(action->police.rate_bytes_ps, 1000) * 8; |
| 268 | pol.burst = action->police.burst; | 268 | pol.burst = action->police.burst; |
| @@ -154,6 +154,47 @@ int qos_policer_conf_set(struct ocelot *ocelot, int port, u32 pol_ix, · 153 unmodified lines | |||
| 154 | return 0; | 154 | return 0; |
| 155 | } | 155 | } |
| 156 | 156 | ||
| 157 | int ocelot_policer_validate(const struct flow_action *action, | ||
| 158 | const struct flow_action_entry *a, | ||
| 159 | struct netlink_ext_ack *extack) | ||
| 160 | { | ||
| 161 | if (a->police.exceed.act_id != FLOW_ACTION_DROP) { | ||
| 162 | NL_SET_ERR_MSG_MOD(extack, | ||
| 163 | "Offload not supported when exceed action is not drop"); | ||
| 164 | return -EOPNOTSUPP; | ||
| 165 | } | ||
| 166 | |||
| 167 | if (a->police.notexceed.act_id != FLOW_ACTION_PIPE && | ||
| 168 | a->police.notexceed.act_id != FLOW_ACTION_ACCEPT) { | ||
| 169 | NL_SET_ERR_MSG_MOD(extack, | ||
| 170 | "Offload not supported when conform action is not pipe or ok"); | ||
| 171 | return -EOPNOTSUPP; | ||
| 172 | } | ||
| 173 | |||
| 174 | if (a->police.notexceed.act_id == FLOW_ACTION_ACCEPT && | ||
| 175 | !flow_action_is_last_entry(action, a)) { | ||
| 176 | NL_SET_ERR_MSG_MOD(extack, | ||
| 177 | "Offload not supported when conform action is ok, but police action is not last"); | ||
| 178 | return -EOPNOTSUPP; | ||
| 179 | } | ||
| 180 | |||
| 181 | if (a->police.peakrate_bytes_ps || | ||
| 182 | a->police.avrate || a->police.overhead) { | ||
| 183 | NL_SET_ERR_MSG_MOD(extack, | ||
| 184 | "Offload not supported when peakrate/avrate/overhead is configured"); | ||
| 185 | return -EOPNOTSUPP; | ||
| 186 | } | ||
| 187 | |||
| 188 | if (a->police.rate_pkt_ps) { | ||
| 189 | NL_SET_ERR_MSG_MOD(extack, | ||
| 190 | "Offload does not support packets per second"); | ||
| 191 | return -EOPNOTSUPP; | ||
| 192 | } | ||
| 193 | |||
| 194 | return 0; | ||
| 195 | } | ||
| 196 | EXPORT_SYMBOL(ocelot_policer_validate); | ||
| 197 | |||
| 157 | int ocelot_port_policer_add(struct ocelot *ocelot, int port, | 198 | int ocelot_port_policer_add(struct ocelot *ocelot, int port, |
| 158 | struct ocelot_policer *pol) | 199 | struct ocelot_policer *pol) |
| 159 | { | 200 | { |
| @@ -8,6 +8,7 @@ · 7 unmodified lines | |||
| 8 | #define _MSCC_OCELOT_POLICE_H_ | 8 | #define _MSCC_OCELOT_POLICE_H_ |
| 9 | 9 | ||
| 10 | #include "ocelot.h" | 10 | #include "ocelot.h" |
| 11 | #include <net/flow_offload.h> | ||
| 11 | 12 | ||
| 12 | enum mscc_qos_rate_mode { | 13 | enum mscc_qos_rate_mode { |
| 13 | MSCC_QOS_RATE_MODE_DISABLED, /* Policer/shaper disabled */ | 14 | MSCC_QOS_RATE_MODE_DISABLED, /* Policer/shaper disabled */ |
| @@ -33,4 +34,8 @@ struct qos_policer_conf { · 19 unmodified lines | |||
| 33 | int qos_policer_conf_set(struct ocelot *ocelot, int port, u32 pol_ix, | 34 | int qos_policer_conf_set(struct ocelot *ocelot, int port, u32 pol_ix, |
| 34 | struct qos_policer_conf *conf); | 35 | struct qos_policer_conf *conf); |
| 35 | 36 | ||
| 37 | int ocelot_policer_validate(const struct flow_action *action, | ||
| 38 | const struct flow_action_entry *a, | ||
| 39 | struct netlink_ext_ack *extack); | ||
| 40 | |||
| 36 | #endif /* _MSCC_OCELOT_POLICE_H_ */ | 41 | #endif /* _MSCC_OCELOT_POLICE_H_ */ |
| @@ -117,6 +117,40 @@ int nfp_flower_offload_one_police(struct nfp_app *app, bool ingress, · 116 unmodified lines | |||
| 117 | return 0; | 117 | return 0; |
| 118 | } | 118 | } |
| 119 | 119 | ||
| 120 | static int nfp_policer_validate(const struct flow_action *action, | ||
| 121 | const struct flow_action_entry *act, | ||
| 122 | struct netlink_ext_ack *extack) | ||
| 123 | { | ||
| 124 | if (act->police.exceed.act_id != FLOW_ACTION_DROP) { | ||
| 125 | NL_SET_ERR_MSG_MOD(extack, | ||
| 126 | "Offload not supported when exceed action is not drop"); | ||
| 127 | return -EOPNOTSUPP; | ||
| 128 | } | ||
| 129 | |||
| 130 | if (act->police.notexceed.act_id != FLOW_ACTION_PIPE && | ||
| 131 | act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) { | ||
| 132 | NL_SET_ERR_MSG_MOD(extack, | ||
| 133 | "Offload not supported when conform action is not pipe or ok"); | ||
| 134 | return -EOPNOTSUPP; | ||
| 135 | } | ||
| 136 | |||
| 137 | if (act->police.notexceed.act_id == FLOW_ACTION_ACCEPT && | ||
| 138 | !flow_action_is_last_entry(action, act)) { | ||
| 139 | NL_SET_ERR_MSG_MOD(extack, | ||
| 140 | "Offload not supported when conform action is ok, but action is not last"); | ||
| 141 | return -EOPNOTSUPP; | ||
| 142 | } | ||
| 143 | |||
| 144 | if (act->police.peakrate_bytes_ps || | ||
| 145 | act->police.avrate || act->police.overhead) { | ||
| 146 | NL_SET_ERR_MSG_MOD(extack, | ||
| 147 | "Offload not supported when peakrate/avrate/overhead is configured"); | ||
| 148 | return -EOPNOTSUPP; | ||
| 149 | } | ||
| 150 | |||
| 151 | return 0; | ||
| 152 | } | ||
| 153 | |||
| 120 | static int | 154 | static int |
| 121 | nfp_flower_install_rate_limiter(struct nfp_app *app, struct net_device *netdev, | 155 | nfp_flower_install_rate_limiter(struct nfp_app *app, struct net_device *netdev, |
| 122 | struct tc_cls_matchall_offload *flow, | 156 | struct tc_cls_matchall_offload *flow, |
| @@ -135,6 +169,7 @@ nfp_flower_install_rate_limiter(struct nfp_app *app, struct net_device *netdev, · 12 unmodified lines | |||
| 135 | u32 burst; | 169 | u32 burst; |
| 136 | bool pps; | 170 | bool pps; |
| 137 | u64 rate; | 171 | u64 rate; |
| 172 | int err; | ||
| 138 | 173 | ||
| 139 | if (!nfp_netdev_is_nfp_repr(netdev)) { | 174 | if (!nfp_netdev_is_nfp_repr(netdev)) { |
| 140 | NL_SET_ERR_MSG_MOD(extack, "unsupported offload: qos rate limit offload not supported on higher level port"); | 175 | NL_SET_ERR_MSG_MOD(extack, "unsupported offload: qos rate limit offload not supported on higher level port"); |
| @@ -181,6 +216,11 @@ nfp_flower_install_rate_limiter(struct nfp_app *app, struct net_device *netdev, · 40 unmodified lines | |||
| 181 | "unsupported offload: qos rate limit offload requires police action"); | 216 | "unsupported offload: qos rate limit offload requires police action"); |
| 182 | return -EOPNOTSUPP; | 217 | return -EOPNOTSUPP; |
| 183 | } | 218 | } |
| 219 | |||
| 220 | err = nfp_policer_validate(&flow->rule->action, action, extack); | ||
| 221 | if (err) | ||
| 222 | return err; | ||
| 223 | |||
| 184 | if (action->police.rate_bytes_ps > 0) { | 224 | if (action->police.rate_bytes_ps > 0) { |
| 185 | if (bps_num++) { | 225 | if (bps_num++) { |
| 186 | NL_SET_ERR_MSG_MOD(extack, | 226 | NL_SET_ERR_MSG_MOD(extack, |
| @@ -148,6 +148,8 @@ enum flow_action_id { · 147 unmodified lines | |||
| 148 | FLOW_ACTION_MPLS_MANGLE, | 148 | FLOW_ACTION_MPLS_MANGLE, |
| 149 | FLOW_ACTION_GATE, | 149 | FLOW_ACTION_GATE, |
| 150 | FLOW_ACTION_PPPOE_PUSH, | 150 | FLOW_ACTION_PPPOE_PUSH, |
| 151 | FLOW_ACTION_JUMP, | ||
| 152 | FLOW_ACTION_PIPE, | ||
| 151 | NUM_FLOW_ACTIONS, | 153 | NUM_FLOW_ACTIONS, |
| 152 | }; | 154 | }; |
| 153 | 155 | ||
| @@ -235,9 +237,16 @@ struct flow_action_entry { · 81 unmodified lines | |||
| 235 | struct { /* FLOW_ACTION_POLICE */ | 237 | struct { /* FLOW_ACTION_POLICE */ |
| 236 | u32 burst; | 238 | u32 burst; |
| 237 | u64 rate_bytes_ps; | 239 | u64 rate_bytes_ps; |
| 240 | u64 peakrate_bytes_ps; | ||
| 241 | u32 avrate; | ||
| 242 | u16 overhead; | ||
| 238 | u64 burst_pkt; | 243 | u64 burst_pkt; |
| 239 | u64 rate_pkt_ps; | 244 | u64 rate_pkt_ps; |
| 240 | u32 mtu; | 245 | u32 mtu; |
| 246 | struct { | ||
| 247 | enum flow_action_id act_id; | ||
| 248 | u32 extval; | ||
| 249 | } exceed, notexceed; | ||
| 241 | } police; | 250 | } police; |
| 242 | struct { /* FLOW_ACTION_CT */ | 251 | struct { /* FLOW_ACTION_CT */ |
| 243 | int action; | 252 | int action; |
| @@ -302,6 +311,12 @@ static inline bool flow_offload_has_one_action(const struct flow_action *action) · 58 unmodified lines | |||
| 302 | return action->num_entries == 1; | 311 | return action->num_entries == 1; |
| 303 | } | 312 | } |
| 304 | 313 | ||
| 314 | static inline bool flow_action_is_last_entry(const struct flow_action *action, | ||
| 315 | const struct flow_action_entry *entry) | ||
| 316 | { | ||
| 317 | return entry == &action->entries[action->num_entries - 1]; | ||
| 318 | } | ||
| 319 | |||
| 305 | #define flow_action_for_each(__i, __act, __actions) \ | 320 | #define flow_action_for_each(__i, __act, __actions) \ |
| 306 | for (__i = 0, __act = &(__actions)->entries[0]; \ | 321 | for (__i = 0, __act = &(__actions)->entries[0]; \ |
| 307 | __i < (__actions)->num_entries; \ | 322 | __i < (__actions)->num_entries; \ |
| @@ -159,4 +159,34 @@ static inline u32 tcf_police_tcfp_mtu(const struct tc_action *act) · 158 unmodified lines | |||
| 159 | return params->tcfp_mtu; | 159 | return params->tcfp_mtu; |
| 160 | } | 160 | } |
| 161 | 161 | ||
| 162 | static inline u64 tcf_police_peakrate_bytes_ps(const struct tc_action *act) | ||
| 163 | { | ||
| 164 | struct tcf_police *police = to_police(act); | ||
| 165 | struct tcf_police_params *params; | ||
| 166 | |||
| 167 | params = rcu_dereference_protected(police->params, | ||
| 168 | lockdep_is_held(&police->tcf_lock)); | ||
| 169 | return params->peak.rate_bytes_ps; | ||
| 170 | } | ||
| 171 | |||
| 172 | static inline u32 tcf_police_tcfp_ewma_rate(const struct tc_action *act) | ||
| 173 | { | ||
| 174 | struct tcf_police *police = to_police(act); | ||
| 175 | struct tcf_police_params *params; | ||
| 176 | |||
| 177 | params = rcu_dereference_protected(police->params, | ||
| 178 | lockdep_is_held(&police->tcf_lock)); | ||
| 179 | return params->tcfp_ewma_rate; | ||
| 180 | } | ||
| 181 | |||
| 182 | static inline u16 tcf_police_rate_overhead(const struct tc_action *act) | ||
| 183 | { | ||
| 184 | struct tcf_police *police = to_police(act); | ||
| 185 | struct tcf_police_params *params; | ||
| 186 | |||
| 187 | params = rcu_dereference_protected(police->params, | ||
| 188 | lockdep_is_held(&police->tcf_lock)); | ||
| 189 | return params->rate.overhead; | ||
| 190 | } | ||
| 191 | |||
| 162 | #endif /* __NET_TC_POLICE_H */ | 192 | #endif /* __NET_TC_POLICE_H */ |
| @@ -419,20 +419,66 @@ static int tcf_police_search(struct net *net, struct tc_action **a, u32 index) · 418 unmodified lines | |||
| 419 | return tcf_idr_search(tn, a, index); | 419 | return tcf_idr_search(tn, a, index); |
| 420 | } | 420 | } |
| 421 | 421 | ||
| 422 | static int tcf_police_act_to_flow_act(int tc_act, u32 *extval) | ||
| 423 | { | ||
| 424 | int act_id = -EOPNOTSUPP; | ||
| 425 | |||
| 426 | if (!TC_ACT_EXT_OPCODE(tc_act)) { | ||
| 427 | if (tc_act == TC_ACT_OK) | ||
| 428 | act_id = FLOW_ACTION_ACCEPT; | ||
| 429 | else if (tc_act == TC_ACT_SHOT) | ||
| 430 | act_id = FLOW_ACTION_DROP; | ||
| 431 | else if (tc_act == TC_ACT_PIPE) | ||
| 432 | act_id = FLOW_ACTION_PIPE; | ||
| 433 | } else if (TC_ACT_EXT_CMP(tc_act, TC_ACT_GOTO_CHAIN)) { | ||
| 434 | act_id = FLOW_ACTION_GOTO; | ||
| 435 | *extval = tc_act & TC_ACT_EXT_VAL_MASK; | ||
| 436 | } else if (TC_ACT_EXT_CMP(tc_act, TC_ACT_JUMP)) { | ||
| 437 | act_id = FLOW_ACTION_JUMP; | ||
| 438 | *extval = tc_act & TC_ACT_EXT_VAL_MASK; | ||
| 439 | } | ||
| 440 | |||
| 441 | return act_id; | ||
| 442 | } | ||
| 443 | |||
| 422 | static int tcf_police_offload_act_setup(struct tc_action *act, void *entry_data, | 444 | static int tcf_police_offload_act_setup(struct tc_action *act, void *entry_data, |
| 423 | u32 *index_inc, bool bind) | 445 | u32 *index_inc, bool bind) |
| 424 | { | 446 | { |
| 425 | if (bind) { | 447 | if (bind) { |
| 426 | struct flow_action_entry *entry = entry_data; | 448 | struct flow_action_entry *entry = entry_data; |
| 449 | struct tcf_police *police = to_police(act); | ||
| 450 | struct tcf_police_params *p; | ||
| 451 | int act_id; | ||
| 452 | |||
| 453 | p = rcu_dereference_protected(police->params, | ||
| 454 | lockdep_is_held(&police->tcf_lock)); | ||
| 427 | 455 | ||
| 428 | entry->id = FLOW_ACTION_POLICE; | 456 | entry->id = FLOW_ACTION_POLICE; |
| 429 | entry->police.burst = tcf_police_burst(act); | 457 | entry->police.burst = tcf_police_burst(act); |
| 430 | entry->police.rate_bytes_ps = | 458 | entry->police.rate_bytes_ps = |
| 431 | tcf_police_rate_bytes_ps(act); | 459 | tcf_police_rate_bytes_ps(act); |
| 460 | entry->police.peakrate_bytes_ps = tcf_police_peakrate_bytes_ps(act); | ||
| 461 | entry->police.avrate = tcf_police_tcfp_ewma_rate(act); | ||
| 462 | entry->police.overhead = tcf_police_rate_overhead(act); | ||
| 432 | entry->police.burst_pkt = tcf_police_burst_pkt(act); | 463 | entry->police.burst_pkt = tcf_police_burst_pkt(act); |
| 433 | entry->police.rate_pkt_ps = | 464 | entry->police.rate_pkt_ps = |
| 434 | tcf_police_rate_pkt_ps(act); | 465 | tcf_police_rate_pkt_ps(act); |
| 435 | entry->police.mtu = tcf_police_tcfp_mtu(act); | 466 | entry->police.mtu = tcf_police_tcfp_mtu(act); |
| 467 | |||
| 468 | act_id = tcf_police_act_to_flow_act(police->tcf_action, | ||
| 469 | &entry->police.exceed.extval); | ||
| 470 | if (act_id < 0) | ||
| 471 | return act_id; | ||
| 472 | |||
| 473 | entry->police.exceed.act_id = act_id; | ||
| 474 | |||
| 475 | act_id = tcf_police_act_to_flow_act(p->tcfp_result, | ||
| 476 | &entry->police.notexceed.extval); | ||
| 477 | if (act_id < 0) | ||
| 478 | return act_id; | ||
| 479 | |||
| 480 | entry->police.notexceed.act_id = act_id; | ||
| 481 | |||
| 436 | *index_inc = 1; | 482 | *index_inc = 1; |
| 437 | } else { | 483 | } else { |
| 438 | struct flow_offload_action *fl_action = entry_data; | 484 | struct flow_offload_action *fl_action = entry_data; |