aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVlad Buslov <vladbu@mellanox.com>2019-04-05 13:56:26 -0400
committerDavid S. Miller <davem@davemloft.net>2019-04-07 22:33:07 -0400
commit1f17f7742eeba73dbd5ae8bdec1a85ce5877001e (patch)
tree8c840f22564fd622b6a199041c87416acb024733
parent9186c90bbb9525f46eddb590be26c749b5b1def7 (diff)
net: sched: flower: insert filter to ht before offloading it to hw
John reports: Recent refactoring of fl_change aims to use the classifier spinlock to avoid the need for rtnl lock. In doing so, the fl_hw_replace_filer() function was moved to before the lock is taken. This can create problems for drivers if duplicate filters are created (commmon in ovs tc offload due to filters being triggered by user-space matches). Drivers registered for such filters will now receive multiple copies of the same rule, each with a different cookie value. This means that the drivers would need to do a full match field lookup to determine duplicates, repeating work that will happen in flower __fl_lookup(). Currently, drivers do not expect to receive duplicate filters. To fix this, verify that filter with same key is not present in flower classifier hash table and insert the new filter to the flower hash table before offloading it to hardware. Implement helper function fl_ht_insert_unique() to atomically verify/insert a filter. This change makes filter visible to fast path at the beginning of fl_change() function, which means it can no longer be freed directly in case of error. Refactor fl_change() error handling code to deallocate the filter with rcu timeout. Fixes: 620da4860827 ("net: sched: flower: refactor fl_change") Reported-by: John Hurley <john.hurley@netronome.com> Signed-off-by: Vlad Buslov <vladbu@mellanox.com> Acked-by: Jiri Pirko <jiri@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--net/sched/cls_flower.c64
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
1462static 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
1462static int fl_change(struct net *net, struct sk_buff *in_skb, 1484static 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
1628errout_idr:
1629 idr_remove(&head->handle_idr, fnew->handle);
1630errout_hw: 1651errout_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);
1655errout_ht:
1656 if (in_ht)
1657 rhashtable_remove_fast(&fnew->mask->ht, &fnew->ht_node,
1658 fnew->mask->filter_ht_params);
1634errout_mask: 1659errout_mask:
1635 fl_mask_put(head, fnew->mask, true); 1660 fl_mask_put(head, fnew->mask, true);
1636errout: 1661errout:
1637 tcf_exts_destroy(&fnew->exts); 1662 tcf_queue_work(&fnew->rwork, fl_destroy_filter_work);
1638 kfree(fnew);
1639errout_tb: 1663errout_tb:
1640 kfree(tb); 1664 kfree(tb);
1641errout_mask_alloc: 1665errout_mask_alloc: