@@ -613,6 +613,31 @@ static void modify_fte(struct fs_fte *fte)
613
613
fte -> act_dests .modify_mask = 0 ;
614
614
}
615
615
616
+ static void del_sw_hw_dup_rule (struct fs_node * node )
617
+ {
618
+ struct mlx5_flow_rule * rule ;
619
+ struct fs_fte * fte ;
620
+
621
+ fs_get_obj (rule , node );
622
+ fs_get_obj (fte , rule -> node .parent );
623
+ trace_mlx5_fs_del_rule (rule );
624
+
625
+ if (is_fwd_next_action (rule -> sw_action )) {
626
+ mutex_lock (& rule -> dest_attr .ft -> lock );
627
+ list_del (& rule -> next_ft );
628
+ mutex_unlock (& rule -> dest_attr .ft -> lock );
629
+ }
630
+
631
+ /* If a pending rule is being deleted it means
632
+ * this is a NO APPEND rule, so there are no partial deletions,
633
+ * all the rules of the mlx5_flow_handle are going to be deleted
634
+ * and the rules aren't shared with any other mlx5_flow_handle instance
635
+ * so no need to do any bookkeeping like in del_sw_hw_rule().
636
+ */
637
+
638
+ kfree (rule );
639
+ }
640
+
616
641
static void del_sw_hw_rule (struct fs_node * node )
617
642
{
618
643
struct mlx5_flow_rule * rule ;
@@ -658,12 +683,33 @@ static void del_sw_hw_rule(struct fs_node *node)
658
683
kfree (rule );
659
684
}
660
685
686
+ static void switch_to_pending_act_dests (struct fs_fte * fte )
687
+ {
688
+ struct fs_node * iter ;
689
+
690
+ memcpy (& fte -> act_dests , & fte -> dup -> act_dests , sizeof (fte -> act_dests ));
691
+
692
+ list_bulk_move_tail (& fte -> node .children ,
693
+ fte -> dup -> children .next ,
694
+ fte -> dup -> children .prev );
695
+
696
+ list_for_each_entry (iter , & fte -> node .children , list )
697
+ iter -> del_sw_func = del_sw_hw_rule ;
698
+
699
+ /* Make sure the fte isn't deleted
700
+ * as mlx5_del_flow_rules() decreases the refcount
701
+ * of the fte to trigger deletion.
702
+ */
703
+ tree_get_node (& fte -> node );
704
+ }
705
+
661
706
static void del_hw_fte (struct fs_node * node )
662
707
{
663
708
struct mlx5_flow_root_namespace * root ;
664
709
struct mlx5_flow_table * ft ;
665
710
struct mlx5_flow_group * fg ;
666
711
struct mlx5_core_dev * dev ;
712
+ bool pending_used = false;
667
713
struct fs_fte * fte ;
668
714
int err ;
669
715
@@ -675,16 +721,33 @@ static void del_hw_fte(struct fs_node *node)
675
721
WARN_ON (fte -> act_dests .dests_size );
676
722
dev = get_dev (& ft -> node );
677
723
root = find_root (& ft -> node );
724
+
725
+ if (fte -> dup && !list_empty (& fte -> dup -> children )) {
726
+ switch_to_pending_act_dests (fte );
727
+ pending_used = true;
728
+ } else {
729
+ /* Avoid double call to del_hw_fte */
730
+ node -> del_hw_func = NULL ;
731
+ }
732
+
678
733
if (node -> active ) {
679
- err = root -> cmds -> delete_fte (root , ft , fte );
680
- if (err )
681
- mlx5_core_warn (dev ,
682
- "flow steering can't delete fte in index %d of flow group id %d\n" ,
683
- fte -> index , fg -> id );
684
- node -> active = false;
734
+ if (pending_used ) {
735
+ err = root -> cmds -> update_fte (root , ft , fg ,
736
+ fte -> act_dests .modify_mask , fte );
737
+ if (err )
738
+ mlx5_core_warn (dev ,
739
+ "flow steering can't update to pending rule in index %d of flow group id %d\n" ,
740
+ fte -> index , fg -> id );
741
+ fte -> act_dests .modify_mask = 0 ;
742
+ } else {
743
+ err = root -> cmds -> delete_fte (root , ft , fte );
744
+ if (err )
745
+ mlx5_core_warn (dev ,
746
+ "flow steering can't delete fte in index %d of flow group id %d\n" ,
747
+ fte -> index , fg -> id );
748
+ node -> active = false;
749
+ }
685
750
}
686
- /* Avoid double call to del_hw_fte */
687
- fte -> node .del_hw_func = NULL ;
688
751
}
689
752
690
753
static void del_sw_fte (struct fs_node * node )
@@ -702,6 +765,7 @@ static void del_sw_fte(struct fs_node *node)
702
765
rhash_fte );
703
766
WARN_ON (err );
704
767
ida_free (& fg -> fte_allocator , fte -> index - fg -> start_index );
768
+ kvfree (fte -> dup );
705
769
kmem_cache_free (steering -> ftes_cache , fte );
706
770
}
707
771
@@ -1105,27 +1169,55 @@ static int update_root_ft_create(struct mlx5_flow_table *ft, struct fs_prio
1105
1169
return err ;
1106
1170
}
1107
1171
1172
+ static bool rule_is_pending (struct fs_fte * fte , struct mlx5_flow_rule * rule )
1173
+ {
1174
+ struct mlx5_flow_rule * tmp_rule ;
1175
+ struct fs_node * iter ;
1176
+
1177
+ if (!fte -> dup || list_empty (& fte -> dup -> children ))
1178
+ return false;
1179
+
1180
+ list_for_each_entry (iter , & fte -> dup -> children , list ) {
1181
+ tmp_rule = container_of (iter , struct mlx5_flow_rule , node );
1182
+
1183
+ if (tmp_rule == rule )
1184
+ return true;
1185
+ }
1186
+
1187
+ return false;
1188
+ }
1189
+
1108
1190
static int _mlx5_modify_rule_destination (struct mlx5_flow_rule * rule ,
1109
1191
struct mlx5_flow_destination * dest )
1110
1192
{
1111
1193
struct mlx5_flow_root_namespace * root ;
1194
+ struct fs_fte_action * act_dests ;
1112
1195
struct mlx5_flow_table * ft ;
1113
1196
struct mlx5_flow_group * fg ;
1197
+ bool pending = false;
1114
1198
struct fs_fte * fte ;
1115
1199
int modify_mask = BIT (MLX5_SET_FTE_MODIFY_ENABLE_MASK_DESTINATION_LIST );
1116
1200
int err = 0 ;
1117
1201
1118
1202
fs_get_obj (fte , rule -> node .parent );
1119
- if (!(fte -> act_dests .action .action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST ))
1203
+
1204
+ pending = rule_is_pending (fte , rule );
1205
+ if (pending )
1206
+ act_dests = & fte -> dup -> act_dests ;
1207
+ else
1208
+ act_dests = & fte -> act_dests ;
1209
+
1210
+ if (!(act_dests -> action .action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST ))
1120
1211
return - EINVAL ;
1121
1212
down_write_ref_node (& fte -> node , false);
1122
1213
fs_get_obj (fg , fte -> node .parent );
1123
1214
fs_get_obj (ft , fg -> node .parent );
1124
1215
1125
1216
memcpy (& rule -> dest_attr , dest , sizeof (* dest ));
1126
1217
root = find_root (& ft -> node );
1127
- err = root -> cmds -> update_fte (root , ft , fg ,
1128
- modify_mask , fte );
1218
+ if (!pending )
1219
+ err = root -> cmds -> update_fte (root , ft , fg ,
1220
+ modify_mask , fte );
1129
1221
up_write_ref_node (& fte -> node , false);
1130
1222
1131
1223
return err ;
@@ -1455,6 +1547,16 @@ static struct mlx5_flow_handle *alloc_handle(int num_rules)
1455
1547
return handle ;
1456
1548
}
1457
1549
1550
+ static void destroy_flow_handle_dup (struct mlx5_flow_handle * handle ,
1551
+ int i )
1552
+ {
1553
+ for (; -- i >= 0 ;) {
1554
+ list_del (& handle -> rule [i ]-> node .list );
1555
+ kfree (handle -> rule [i ]);
1556
+ }
1557
+ kfree (handle );
1558
+ }
1559
+
1458
1560
static void destroy_flow_handle (struct fs_fte * fte ,
1459
1561
struct mlx5_flow_handle * handle ,
1460
1562
struct mlx5_flow_destination * dest ,
@@ -1470,6 +1572,61 @@ static void destroy_flow_handle(struct fs_fte *fte,
1470
1572
kfree (handle );
1471
1573
}
1472
1574
1575
+ static struct mlx5_flow_handle *
1576
+ create_flow_handle_dup (struct list_head * children ,
1577
+ struct mlx5_flow_destination * dest ,
1578
+ int dest_num ,
1579
+ struct fs_fte_action * act_dests )
1580
+ {
1581
+ static int dst = BIT (MLX5_SET_FTE_MODIFY_ENABLE_MASK_DESTINATION_LIST );
1582
+ static int count = BIT (MLX5_SET_FTE_MODIFY_ENABLE_MASK_FLOW_COUNTERS );
1583
+ struct mlx5_flow_rule * rule = NULL ;
1584
+ struct mlx5_flow_handle * handle ;
1585
+ int i = 0 ;
1586
+ int type ;
1587
+
1588
+ handle = alloc_handle ((dest_num ) ? dest_num : 1 );
1589
+ if (!handle )
1590
+ return NULL ;
1591
+
1592
+ do {
1593
+ rule = alloc_rule (dest + i );
1594
+ if (!rule )
1595
+ goto free_rules ;
1596
+
1597
+ /* Add dest to dests list- we need flow tables to be in the
1598
+ * end of the list for forward to next prio rules.
1599
+ */
1600
+ tree_init_node (& rule -> node , NULL , del_sw_hw_dup_rule );
1601
+ if (dest &&
1602
+ dest [i ].type != MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE )
1603
+ list_add (& rule -> node .list , children );
1604
+ else
1605
+ list_add_tail (& rule -> node .list , children );
1606
+
1607
+ if (dest ) {
1608
+ act_dests -> dests_size ++ ;
1609
+
1610
+ if (is_fwd_dest_type (dest [i ].type ))
1611
+ act_dests -> fwd_dests ++ ;
1612
+
1613
+ type = dest [i ].type ==
1614
+ MLX5_FLOW_DESTINATION_TYPE_COUNTER ;
1615
+ act_dests -> modify_mask |= type ? count : dst ;
1616
+ }
1617
+ handle -> rule [i ] = rule ;
1618
+ } while (++ i < dest_num );
1619
+
1620
+ return handle ;
1621
+
1622
+ free_rules :
1623
+ destroy_flow_handle_dup (handle , i );
1624
+ act_dests -> dests_size = 0 ;
1625
+ act_dests -> fwd_dests = 0 ;
1626
+
1627
+ return NULL ;
1628
+ }
1629
+
1473
1630
static struct mlx5_flow_handle *
1474
1631
create_flow_handle (struct fs_fte * fte ,
1475
1632
struct mlx5_flow_destination * dest ,
@@ -1963,6 +2120,62 @@ lookup_fte_locked(struct mlx5_flow_group *g,
1963
2120
return fte_tmp ;
1964
2121
}
1965
2122
2123
+ /* Native capability lacks support for adding an additional match with the same value
2124
+ * to the same flow group. To accommodate the NO APPEND flag in these scenarios,
2125
+ * we include the new rule in the existing flow table entry (fte) without immediate
2126
+ * hardware commitment. When a request is made to delete the corresponding hardware rule,
2127
+ * we then commit the pending rule to hardware.
2128
+ */
2129
+ static struct mlx5_flow_handle *
2130
+ add_rule_dup_match_fte (struct fs_fte * fte ,
2131
+ const struct mlx5_flow_spec * spec ,
2132
+ struct mlx5_flow_act * flow_act ,
2133
+ struct mlx5_flow_destination * dest ,
2134
+ int dest_num )
2135
+ {
2136
+ struct mlx5_flow_handle * handle ;
2137
+ struct fs_fte_dup * dup ;
2138
+ int i = 0 ;
2139
+
2140
+ if (!fte -> dup ) {
2141
+ dup = kvzalloc (sizeof (* dup ), GFP_KERNEL );
2142
+ if (!dup )
2143
+ return ERR_PTR (- ENOMEM );
2144
+ /* dup will be freed when the fte is freed
2145
+ * this way we don't allocate / free dup on every rule deletion
2146
+ * or creation
2147
+ */
2148
+ INIT_LIST_HEAD (& dup -> children );
2149
+ fte -> dup = dup ;
2150
+ }
2151
+
2152
+ if (!list_empty (& fte -> dup -> children )) {
2153
+ mlx5_core_warn (get_dev (& fte -> node ),
2154
+ "Can have only a single duplicate rule\n" );
2155
+
2156
+ return ERR_PTR (- EEXIST );
2157
+ }
2158
+
2159
+ fte -> dup -> act_dests .action = * flow_act ;
2160
+ fte -> dup -> act_dests .flow_context = spec -> flow_context ;
2161
+ fte -> dup -> act_dests .dests_size = 0 ;
2162
+ fte -> dup -> act_dests .fwd_dests = 0 ;
2163
+ fte -> dup -> act_dests .modify_mask = BIT (MLX5_SET_FTE_MODIFY_ENABLE_MASK_ACTION );
2164
+
2165
+ handle = create_flow_handle_dup (& fte -> dup -> children ,
2166
+ dest , dest_num ,
2167
+ & fte -> dup -> act_dests );
2168
+ if (!handle )
2169
+ return ERR_PTR (- ENOMEM );
2170
+
2171
+ for (i = 0 ; i < handle -> num_rules ; i ++ ) {
2172
+ tree_add_node (& handle -> rule [i ]-> node , & fte -> node );
2173
+ trace_mlx5_fs_add_rule (handle -> rule [i ]);
2174
+ }
2175
+
2176
+ return handle ;
2177
+ }
2178
+
1966
2179
static struct mlx5_flow_handle *
1967
2180
try_add_to_existing_fg (struct mlx5_flow_table * ft ,
1968
2181
struct list_head * match_head ,
@@ -1973,6 +2186,7 @@ try_add_to_existing_fg(struct mlx5_flow_table *ft,
1973
2186
int ft_version )
1974
2187
{
1975
2188
struct mlx5_flow_steering * steering = get_steering (& ft -> node );
2189
+ struct mlx5_flow_root_namespace * root = find_root (& ft -> node );
1976
2190
struct mlx5_flow_group * g ;
1977
2191
struct mlx5_flow_handle * rule ;
1978
2192
struct match_list * iter ;
@@ -1986,7 +2200,9 @@ try_add_to_existing_fg(struct mlx5_flow_table *ft,
1986
2200
return ERR_PTR (- ENOMEM );
1987
2201
1988
2202
search_again_locked :
1989
- if (flow_act -> flags & FLOW_ACT_NO_APPEND )
2203
+ if (flow_act -> flags & FLOW_ACT_NO_APPEND &&
2204
+ (root -> cmds -> get_capabilities (root , root -> table_type ) &
2205
+ MLX5_FLOW_STEERING_CAP_DUPLICATE_MATCH ))
1990
2206
goto skip_search ;
1991
2207
version = matched_fgs_get_version (match_head );
1992
2208
/* Try to find an fte with identical match value and attempt update its
@@ -1999,7 +2215,10 @@ try_add_to_existing_fg(struct mlx5_flow_table *ft,
1999
2215
fte_tmp = lookup_fte_locked (g , spec -> match_value , take_write );
2000
2216
if (!fte_tmp )
2001
2217
continue ;
2002
- rule = add_rule_fg (g , spec , flow_act , dest , dest_num , fte_tmp );
2218
+ if (flow_act -> flags & FLOW_ACT_NO_APPEND )
2219
+ rule = add_rule_dup_match_fte (fte_tmp , spec , flow_act , dest , dest_num );
2220
+ else
2221
+ rule = add_rule_fg (g , spec , flow_act , dest , dest_num , fte_tmp );
2003
2222
/* No error check needed here, because insert_fte() is not called */
2004
2223
up_write_ref_node (& fte_tmp -> node , false);
2005
2224
tree_put_node (& fte_tmp -> node , false);
0 commit comments