diff options
-rw-r--r-- | net/sched/cls_flower.c | 64 |
1 files changed, 44 insertions, 20 deletions
diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index 6050e3caee31..2763176e369c 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c | |||
@@ -1459,6 +1459,28 @@ static int fl_set_parms(struct net *net, struct tcf_proto *tp, | |||
1459 | return 0; | 1459 | return 0; |
1460 | } | 1460 | } |
1461 | 1461 | ||
1462 | static int fl_ht_insert_unique(struct cls_fl_filter *fnew, | ||
1463 | struct cls_fl_filter *fold, | ||
1464 | bool *in_ht) | ||
1465 | { | ||
1466 | struct fl_flow_mask *mask = fnew->mask; | ||
1467 | int err; | ||
1468 | |||
1469 | err = rhashtable_insert_fast(&mask->ht, | ||
1470 | &fnew->ht_node, | ||
1471 | mask->filter_ht_params); | ||
1472 | if (err) { | ||
1473 | *in_ht = false; | ||
1474 | /* It is okay if filter with same key exists when | ||
1475 | * overwriting. | ||
1476 | */ | ||
1477 | return fold && err == -EEXIST ? 0 : err; | ||
1478 | } | ||
1479 | |||
1480 | *in_ht = true; | ||
1481 | return 0; | ||
1482 | } | ||
1483 | |||
1462 | static int fl_change(struct net *net, struct sk_buff *in_skb, | 1484 | static int fl_change(struct net *net, struct sk_buff *in_skb, |
1463 | struct tcf_proto *tp, unsigned long base, | 1485 | struct tcf_proto *tp, unsigned long base, |
1464 | u32 handle, struct nlattr **tca, | 1486 | u32 handle, struct nlattr **tca, |
@@ -1470,6 +1492,7 @@ static int fl_change(struct net *net, struct sk_buff *in_skb, | |||
1470 | struct cls_fl_filter *fnew; | 1492 | struct cls_fl_filter *fnew; |
1471 | struct fl_flow_mask *mask; | 1493 | struct fl_flow_mask *mask; |
1472 | struct nlattr **tb; | 1494 | struct nlattr **tb; |
1495 | bool in_ht; | ||
1473 | int err; | 1496 | int err; |
1474 | 1497 | ||
1475 | if (!tca[TCA_OPTIONS]) { | 1498 | if (!tca[TCA_OPTIONS]) { |
@@ -1528,10 +1551,14 @@ static int fl_change(struct net *net, struct sk_buff *in_skb, | |||
1528 | if (err) | 1551 | if (err) |
1529 | goto errout; | 1552 | goto errout; |
1530 | 1553 | ||
1554 | err = fl_ht_insert_unique(fnew, fold, &in_ht); | ||
1555 | if (err) | ||
1556 | goto errout_mask; | ||
1557 | |||
1531 | if (!tc_skip_hw(fnew->flags)) { | 1558 | if (!tc_skip_hw(fnew->flags)) { |
1532 | err = fl_hw_replace_filter(tp, fnew, rtnl_held, extack); | 1559 | err = fl_hw_replace_filter(tp, fnew, rtnl_held, extack); |
1533 | if (err) | 1560 | if (err) |
1534 | goto errout_mask; | 1561 | goto errout_ht; |
1535 | } | 1562 | } |
1536 | 1563 | ||
1537 | if (!tc_in_hw(fnew->flags)) | 1564 | if (!tc_in_hw(fnew->flags)) |
@@ -1557,10 +1584,17 @@ static int fl_change(struct net *net, struct sk_buff *in_skb, | |||
1557 | 1584 | ||
1558 | fnew->handle = handle; | 1585 | fnew->handle = handle; |
1559 | 1586 | ||
1560 | err = rhashtable_insert_fast(&fnew->mask->ht, &fnew->ht_node, | 1587 | if (!in_ht) { |
1561 | fnew->mask->filter_ht_params); | 1588 | struct rhashtable_params params = |
1562 | if (err) | 1589 | fnew->mask->filter_ht_params; |
1563 | goto errout_hw; | 1590 | |
1591 | err = rhashtable_insert_fast(&fnew->mask->ht, | ||
1592 | &fnew->ht_node, | ||
1593 | params); | ||
1594 | if (err) | ||
1595 | goto errout_hw; | ||
1596 | in_ht = true; | ||
1597 | } | ||
1564 | 1598 | ||
1565 | rhashtable_remove_fast(&fold->mask->ht, | 1599 | rhashtable_remove_fast(&fold->mask->ht, |
1566 | &fold->ht_node, | 1600 | &fold->ht_node, |
@@ -1582,11 +1616,6 @@ static int fl_change(struct net *net, struct sk_buff *in_skb, | |||
1582 | refcount_dec(&fold->refcnt); | 1616 | refcount_dec(&fold->refcnt); |
1583 | __fl_put(fold); | 1617 | __fl_put(fold); |
1584 | } else { | 1618 | } else { |
1585 | if (__fl_lookup(fnew->mask, &fnew->mkey)) { | ||
1586 | err = -EEXIST; | ||
1587 | goto errout_hw; | ||
1588 | } | ||
1589 | |||
1590 | if (handle) { | 1619 | if (handle) { |
1591 | /* user specifies a handle and it doesn't exist */ | 1620 | /* user specifies a handle and it doesn't exist */ |
1592 | err = idr_alloc_u32(&head->handle_idr, fnew, &handle, | 1621 | err = idr_alloc_u32(&head->handle_idr, fnew, &handle, |
@@ -1609,12 +1638,6 @@ static int fl_change(struct net *net, struct sk_buff *in_skb, | |||
1609 | goto errout_hw; | 1638 | goto errout_hw; |
1610 | 1639 | ||
1611 | fnew->handle = handle; | 1640 | fnew->handle = handle; |
1612 | |||
1613 | err = rhashtable_insert_fast(&fnew->mask->ht, &fnew->ht_node, | ||
1614 | fnew->mask->filter_ht_params); | ||
1615 | if (err) | ||
1616 | goto errout_idr; | ||
1617 | |||
1618 | list_add_tail_rcu(&fnew->list, &fnew->mask->filters); | 1641 | list_add_tail_rcu(&fnew->list, &fnew->mask->filters); |
1619 | spin_unlock(&tp->lock); | 1642 | spin_unlock(&tp->lock); |
1620 | } | 1643 | } |
@@ -1625,17 +1648,18 @@ static int fl_change(struct net *net, struct sk_buff *in_skb, | |||
1625 | kfree(mask); | 1648 | kfree(mask); |
1626 | return 0; | 1649 | return 0; |
1627 | 1650 | ||
1628 | errout_idr: | ||
1629 | idr_remove(&head->handle_idr, fnew->handle); | ||
1630 | errout_hw: | 1651 | errout_hw: |
1631 | spin_unlock(&tp->lock); | 1652 | spin_unlock(&tp->lock); |
1632 | if (!tc_skip_hw(fnew->flags)) | 1653 | if (!tc_skip_hw(fnew->flags)) |
1633 | fl_hw_destroy_filter(tp, fnew, rtnl_held, NULL); | 1654 | fl_hw_destroy_filter(tp, fnew, rtnl_held, NULL); |
1655 | errout_ht: | ||
1656 | if (in_ht) | ||
1657 | rhashtable_remove_fast(&fnew->mask->ht, &fnew->ht_node, | ||
1658 | fnew->mask->filter_ht_params); | ||
1634 | errout_mask: | 1659 | errout_mask: |
1635 | fl_mask_put(head, fnew->mask, true); | 1660 | fl_mask_put(head, fnew->mask, true); |
1636 | errout: | 1661 | errout: |
1637 | tcf_exts_destroy(&fnew->exts); | 1662 | tcf_queue_work(&fnew->rwork, fl_destroy_filter_work); |
1638 | kfree(fnew); | ||
1639 | errout_tb: | 1663 | errout_tb: |
1640 | kfree(tb); | 1664 | kfree(tb); |
1641 | errout_mask_alloc: | 1665 | errout_mask_alloc: |