diff options
author | David S. Miller <davem@davemloft.net> | 2018-05-16 14:11:23 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2018-05-16 14:11:23 -0400 |
commit | ac22bfb15c83636c28df2754456fb0a00c6e82da (patch) | |
tree | baef285c02d4616782e6c11976041e3f3889bcf0 | |
parent | e1b505a60366399d735312ca38b0a6753a684123 (diff) | |
parent | 1942adf64214df370350aa46954ba27654456f68 (diff) |
Merge branch 'dsa-bcm_sf2-CFP-fixes'
Florian Fainelli says:
====================
net: dsa: bcm_sf2: CFP fixes
This patch series fixes a number of usability issues with the SF2 Compact Field
Processor code:
- we would not be properly bound checking the location when we let the kernel
automatically place rules with RX_CLS_LOC_ANY
- when using IPv6 rules and user space specifies a location identifier we
would be off by one in what the chain ID (within the Broadcom tag) indicates
- it would be possible to delete one of the two slices of an IPv6 while leaving
the other one programming leading to various problems
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/dsa/bcm_sf2_cfp.c | 36 |
1 files changed, 22 insertions, 14 deletions
diff --git a/drivers/net/dsa/bcm_sf2_cfp.c b/drivers/net/dsa/bcm_sf2_cfp.c index 23b45da784cb..b89acaee12d4 100644 --- a/drivers/net/dsa/bcm_sf2_cfp.c +++ b/drivers/net/dsa/bcm_sf2_cfp.c | |||
@@ -354,10 +354,13 @@ static int bcm_sf2_cfp_ipv4_rule_set(struct bcm_sf2_priv *priv, int port, | |||
354 | /* Locate the first rule available */ | 354 | /* Locate the first rule available */ |
355 | if (fs->location == RX_CLS_LOC_ANY) | 355 | if (fs->location == RX_CLS_LOC_ANY) |
356 | rule_index = find_first_zero_bit(priv->cfp.used, | 356 | rule_index = find_first_zero_bit(priv->cfp.used, |
357 | bcm_sf2_cfp_rule_size(priv)); | 357 | priv->num_cfp_rules); |
358 | else | 358 | else |
359 | rule_index = fs->location; | 359 | rule_index = fs->location; |
360 | 360 | ||
361 | if (rule_index > bcm_sf2_cfp_rule_size(priv)) | ||
362 | return -ENOSPC; | ||
363 | |||
361 | layout = &udf_tcpip4_layout; | 364 | layout = &udf_tcpip4_layout; |
362 | /* We only use one UDF slice for now */ | 365 | /* We only use one UDF slice for now */ |
363 | slice_num = bcm_sf2_get_slice_number(layout, 0); | 366 | slice_num = bcm_sf2_get_slice_number(layout, 0); |
@@ -562,19 +565,21 @@ static int bcm_sf2_cfp_ipv6_rule_set(struct bcm_sf2_priv *priv, int port, | |||
562 | * first half because the HW search is by incrementing addresses. | 565 | * first half because the HW search is by incrementing addresses. |
563 | */ | 566 | */ |
564 | if (fs->location == RX_CLS_LOC_ANY) | 567 | if (fs->location == RX_CLS_LOC_ANY) |
565 | rule_index[0] = find_first_zero_bit(priv->cfp.used, | 568 | rule_index[1] = find_first_zero_bit(priv->cfp.used, |
566 | bcm_sf2_cfp_rule_size(priv)); | 569 | priv->num_cfp_rules); |
567 | else | 570 | else |
568 | rule_index[0] = fs->location; | 571 | rule_index[1] = fs->location; |
572 | if (rule_index[1] > bcm_sf2_cfp_rule_size(priv)) | ||
573 | return -ENOSPC; | ||
569 | 574 | ||
570 | /* Flag it as used (cleared on error path) such that we can immediately | 575 | /* Flag it as used (cleared on error path) such that we can immediately |
571 | * obtain a second one to chain from. | 576 | * obtain a second one to chain from. |
572 | */ | 577 | */ |
573 | set_bit(rule_index[0], priv->cfp.used); | 578 | set_bit(rule_index[1], priv->cfp.used); |
574 | 579 | ||
575 | rule_index[1] = find_first_zero_bit(priv->cfp.used, | 580 | rule_index[0] = find_first_zero_bit(priv->cfp.used, |
576 | bcm_sf2_cfp_rule_size(priv)); | 581 | priv->num_cfp_rules); |
577 | if (rule_index[1] > bcm_sf2_cfp_rule_size(priv)) { | 582 | if (rule_index[0] > bcm_sf2_cfp_rule_size(priv)) { |
578 | ret = -ENOSPC; | 583 | ret = -ENOSPC; |
579 | goto out_err; | 584 | goto out_err; |
580 | } | 585 | } |
@@ -712,14 +717,14 @@ static int bcm_sf2_cfp_ipv6_rule_set(struct bcm_sf2_priv *priv, int port, | |||
712 | /* Flag the second half rule as being used now, return it as the | 717 | /* Flag the second half rule as being used now, return it as the |
713 | * location, and flag it as unique while dumping rules | 718 | * location, and flag it as unique while dumping rules |
714 | */ | 719 | */ |
715 | set_bit(rule_index[1], priv->cfp.used); | 720 | set_bit(rule_index[0], priv->cfp.used); |
716 | set_bit(rule_index[1], priv->cfp.unique); | 721 | set_bit(rule_index[1], priv->cfp.unique); |
717 | fs->location = rule_index[1]; | 722 | fs->location = rule_index[1]; |
718 | 723 | ||
719 | return ret; | 724 | return ret; |
720 | 725 | ||
721 | out_err: | 726 | out_err: |
722 | clear_bit(rule_index[0], priv->cfp.used); | 727 | clear_bit(rule_index[1], priv->cfp.used); |
723 | return ret; | 728 | return ret; |
724 | } | 729 | } |
725 | 730 | ||
@@ -785,10 +790,6 @@ static int bcm_sf2_cfp_rule_del_one(struct bcm_sf2_priv *priv, int port, | |||
785 | int ret; | 790 | int ret; |
786 | u32 reg; | 791 | u32 reg; |
787 | 792 | ||
788 | /* Refuse deletion of unused rules, and the default reserved rule */ | ||
789 | if (!test_bit(loc, priv->cfp.used) || loc == 0) | ||
790 | return -EINVAL; | ||
791 | |||
792 | /* Indicate which rule we want to read */ | 793 | /* Indicate which rule we want to read */ |
793 | bcm_sf2_cfp_rule_addr_set(priv, loc); | 794 | bcm_sf2_cfp_rule_addr_set(priv, loc); |
794 | 795 | ||
@@ -826,6 +827,13 @@ static int bcm_sf2_cfp_rule_del(struct bcm_sf2_priv *priv, int port, | |||
826 | u32 next_loc = 0; | 827 | u32 next_loc = 0; |
827 | int ret; | 828 | int ret; |
828 | 829 | ||
830 | /* Refuse deleting unused rules, and those that are not unique since | ||
831 | * that could leave IPv6 rules with one of the chained rule in the | ||
832 | * table. | ||
833 | */ | ||
834 | if (!test_bit(loc, priv->cfp.unique) || loc == 0) | ||
835 | return -EINVAL; | ||
836 | |||
829 | ret = bcm_sf2_cfp_rule_del_one(priv, port, loc, &next_loc); | 837 | ret = bcm_sf2_cfp_rule_del_one(priv, port, loc, &next_loc); |
830 | if (ret) | 838 | if (ret) |
831 | return ret; | 839 | return ret; |