Skip to content

Commit 62488e4

Browse files
Kumar Sanghvidavem330
authored andcommitted
cxgb4: add basic tc flower offload support
Add support to add/remove flows for offload. Following match and action are supported for offloading a flow: Match: ether-protocol, IPv4/IPv6 addresses, L4 ports (TCP/UDP) Action: drop, redirect to another port on the device. The qualifying flows can have accompanying mask information. Signed-off-by: Kumar Sanghvi <kumaras@chelsio.com> Signed-off-by: Rahul Lakkireddy <rahul.lakkireddy@chelsio.com> Signed-off-by: Ganesh Goudar <ganeshgr@chelsio.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 6a345b3 commit 62488e4

File tree

6 files changed

+325
-2
lines changed

6 files changed

+325
-2
lines changed

drivers/net/ethernet/chelsio/cxgb4/cxgb4.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -905,6 +905,9 @@ struct adapter {
905905
/* TC u32 offload */
906906
struct cxgb4_tc_u32_table *tc_u32;
907907
struct chcr_stats_debug chcr_stats;
908+
909+
/* TC flower offload */
910+
DECLARE_HASHTABLE(flower_anymatch_tbl, 9);
908911
};
909912

910913
/* Support for "sched-class" command to allow a TX Scheduling Class to be

drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,30 @@ static int get_filter_steerq(struct net_device *dev,
148148
return iq;
149149
}
150150

151+
int cxgb4_get_free_ftid(struct net_device *dev, int family)
152+
{
153+
struct adapter *adap = netdev2adap(dev);
154+
struct tid_info *t = &adap->tids;
155+
int ftid;
156+
157+
spin_lock_bh(&t->ftid_lock);
158+
if (family == PF_INET) {
159+
ftid = find_first_zero_bit(t->ftid_bmap, t->nftids);
160+
if (ftid >= t->nftids)
161+
ftid = -1;
162+
} else {
163+
ftid = bitmap_find_free_region(t->ftid_bmap, t->nftids, 2);
164+
if (ftid < 0)
165+
goto out_unlock;
166+
167+
/* this is only a lookup, keep the found region unallocated */
168+
bitmap_release_region(t->ftid_bmap, ftid, 2);
169+
}
170+
out_unlock:
171+
spin_unlock_bh(&t->ftid_lock);
172+
return ftid;
173+
}
174+
151175
static int cxgb4_set_ftid(struct tid_info *t, int fidx, int family)
152176
{
153177
spin_lock_bh(&t->ftid_lock);

drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5105,6 +5105,8 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
51055105
if (!adapter->tc_u32)
51065106
dev_warn(&pdev->dev,
51075107
"could not offload tc u32, continuing\n");
5108+
5109+
cxgb4_init_tc_flower(adapter);
51085110
}
51095111

51105112
if (is_offload(adapter)) {

drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c

Lines changed: 278 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,20 +38,296 @@
3838
#include "cxgb4.h"
3939
#include "cxgb4_tc_flower.h"
4040

41+
static struct ch_tc_flower_entry *allocate_flower_entry(void)
42+
{
43+
struct ch_tc_flower_entry *new = kzalloc(sizeof(*new), GFP_KERNEL);
44+
return new;
45+
}
46+
47+
/* Must be called with either RTNL or rcu_read_lock */
48+
static struct ch_tc_flower_entry *ch_flower_lookup(struct adapter *adap,
49+
unsigned long flower_cookie)
50+
{
51+
struct ch_tc_flower_entry *flower_entry;
52+
53+
hash_for_each_possible_rcu(adap->flower_anymatch_tbl, flower_entry,
54+
link, flower_cookie)
55+
if (flower_entry->tc_flower_cookie == flower_cookie)
56+
return flower_entry;
57+
return NULL;
58+
}
59+
60+
static void cxgb4_process_flow_match(struct net_device *dev,
61+
struct tc_cls_flower_offload *cls,
62+
struct ch_filter_specification *fs)
63+
{
64+
u16 addr_type = 0;
65+
66+
if (dissector_uses_key(cls->dissector, FLOW_DISSECTOR_KEY_CONTROL)) {
67+
struct flow_dissector_key_control *key =
68+
skb_flow_dissector_target(cls->dissector,
69+
FLOW_DISSECTOR_KEY_CONTROL,
70+
cls->key);
71+
72+
addr_type = key->addr_type;
73+
}
74+
75+
if (dissector_uses_key(cls->dissector, FLOW_DISSECTOR_KEY_BASIC)) {
76+
struct flow_dissector_key_basic *key =
77+
skb_flow_dissector_target(cls->dissector,
78+
FLOW_DISSECTOR_KEY_BASIC,
79+
cls->key);
80+
struct flow_dissector_key_basic *mask =
81+
skb_flow_dissector_target(cls->dissector,
82+
FLOW_DISSECTOR_KEY_BASIC,
83+
cls->mask);
84+
u16 ethtype_key = ntohs(key->n_proto);
85+
u16 ethtype_mask = ntohs(mask->n_proto);
86+
87+
if (ethtype_key == ETH_P_ALL) {
88+
ethtype_key = 0;
89+
ethtype_mask = 0;
90+
}
91+
92+
fs->val.ethtype = ethtype_key;
93+
fs->mask.ethtype = ethtype_mask;
94+
fs->val.proto = key->ip_proto;
95+
fs->mask.proto = mask->ip_proto;
96+
}
97+
98+
if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
99+
struct flow_dissector_key_ipv4_addrs *key =
100+
skb_flow_dissector_target(cls->dissector,
101+
FLOW_DISSECTOR_KEY_IPV4_ADDRS,
102+
cls->key);
103+
struct flow_dissector_key_ipv4_addrs *mask =
104+
skb_flow_dissector_target(cls->dissector,
105+
FLOW_DISSECTOR_KEY_IPV4_ADDRS,
106+
cls->mask);
107+
fs->type = 0;
108+
memcpy(&fs->val.lip[0], &key->dst, sizeof(key->dst));
109+
memcpy(&fs->val.fip[0], &key->src, sizeof(key->src));
110+
memcpy(&fs->mask.lip[0], &mask->dst, sizeof(mask->dst));
111+
memcpy(&fs->mask.fip[0], &mask->src, sizeof(mask->src));
112+
}
113+
114+
if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
115+
struct flow_dissector_key_ipv6_addrs *key =
116+
skb_flow_dissector_target(cls->dissector,
117+
FLOW_DISSECTOR_KEY_IPV6_ADDRS,
118+
cls->key);
119+
struct flow_dissector_key_ipv6_addrs *mask =
120+
skb_flow_dissector_target(cls->dissector,
121+
FLOW_DISSECTOR_KEY_IPV6_ADDRS,
122+
cls->mask);
123+
124+
fs->type = 1;
125+
memcpy(&fs->val.lip[0], key->dst.s6_addr, sizeof(key->dst));
126+
memcpy(&fs->val.fip[0], key->src.s6_addr, sizeof(key->src));
127+
memcpy(&fs->mask.lip[0], mask->dst.s6_addr, sizeof(mask->dst));
128+
memcpy(&fs->mask.fip[0], mask->src.s6_addr, sizeof(mask->src));
129+
}
130+
131+
if (dissector_uses_key(cls->dissector, FLOW_DISSECTOR_KEY_PORTS)) {
132+
struct flow_dissector_key_ports *key, *mask;
133+
134+
key = skb_flow_dissector_target(cls->dissector,
135+
FLOW_DISSECTOR_KEY_PORTS,
136+
cls->key);
137+
mask = skb_flow_dissector_target(cls->dissector,
138+
FLOW_DISSECTOR_KEY_PORTS,
139+
cls->mask);
140+
fs->val.lport = cpu_to_be16(key->dst);
141+
fs->mask.lport = cpu_to_be16(mask->dst);
142+
fs->val.fport = cpu_to_be16(key->src);
143+
fs->mask.fport = cpu_to_be16(mask->src);
144+
}
145+
146+
/* Match only packets coming from the ingress port where this
147+
* filter will be created.
148+
*/
149+
fs->val.iport = netdev2pinfo(dev)->port_id;
150+
fs->mask.iport = ~0;
151+
}
152+
153+
static int cxgb4_validate_flow_match(struct net_device *dev,
154+
struct tc_cls_flower_offload *cls)
155+
{
156+
if (cls->dissector->used_keys &
157+
~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
158+
BIT(FLOW_DISSECTOR_KEY_BASIC) |
159+
BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
160+
BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
161+
BIT(FLOW_DISSECTOR_KEY_PORTS))) {
162+
netdev_warn(dev, "Unsupported key used: 0x%x\n",
163+
cls->dissector->used_keys);
164+
return -EOPNOTSUPP;
165+
}
166+
return 0;
167+
}
168+
169+
static void cxgb4_process_flow_actions(struct net_device *in,
170+
struct tc_cls_flower_offload *cls,
171+
struct ch_filter_specification *fs)
172+
{
173+
const struct tc_action *a;
174+
LIST_HEAD(actions);
175+
176+
tcf_exts_to_list(cls->exts, &actions);
177+
list_for_each_entry(a, &actions, list) {
178+
if (is_tcf_gact_shot(a)) {
179+
fs->action = FILTER_DROP;
180+
} else if (is_tcf_mirred_egress_redirect(a)) {
181+
int ifindex = tcf_mirred_ifindex(a);
182+
struct net_device *out = __dev_get_by_index(dev_net(in),
183+
ifindex);
184+
struct port_info *pi = netdev_priv(out);
185+
186+
fs->action = FILTER_SWITCH;
187+
fs->eport = pi->port_id;
188+
}
189+
}
190+
}
191+
192+
static int cxgb4_validate_flow_actions(struct net_device *dev,
193+
struct tc_cls_flower_offload *cls)
194+
{
195+
const struct tc_action *a;
196+
LIST_HEAD(actions);
197+
198+
tcf_exts_to_list(cls->exts, &actions);
199+
list_for_each_entry(a, &actions, list) {
200+
if (is_tcf_gact_shot(a)) {
201+
/* Do nothing */
202+
} else if (is_tcf_mirred_egress_redirect(a)) {
203+
struct adapter *adap = netdev2adap(dev);
204+
struct net_device *n_dev;
205+
unsigned int i, ifindex;
206+
bool found = false;
207+
208+
ifindex = tcf_mirred_ifindex(a);
209+
for_each_port(adap, i) {
210+
n_dev = adap->port[i];
211+
if (ifindex == n_dev->ifindex) {
212+
found = true;
213+
break;
214+
}
215+
}
216+
217+
/* If interface doesn't belong to our hw, then
218+
* the provided output port is not valid
219+
*/
220+
if (!found) {
221+
netdev_err(dev, "%s: Out port invalid\n",
222+
__func__);
223+
return -EINVAL;
224+
}
225+
} else {
226+
netdev_err(dev, "%s: Unsupported action\n", __func__);
227+
return -EOPNOTSUPP;
228+
}
229+
}
230+
return 0;
231+
}
232+
41233
int cxgb4_tc_flower_replace(struct net_device *dev,
42234
struct tc_cls_flower_offload *cls)
43235
{
44-
return -EOPNOTSUPP;
236+
struct adapter *adap = netdev2adap(dev);
237+
struct ch_tc_flower_entry *ch_flower;
238+
struct ch_filter_specification *fs;
239+
struct filter_ctx ctx;
240+
int fidx;
241+
int ret;
242+
243+
if (cxgb4_validate_flow_actions(dev, cls))
244+
return -EOPNOTSUPP;
245+
246+
if (cxgb4_validate_flow_match(dev, cls))
247+
return -EOPNOTSUPP;
248+
249+
ch_flower = allocate_flower_entry();
250+
if (!ch_flower) {
251+
netdev_err(dev, "%s: ch_flower alloc failed.\n", __func__);
252+
return -ENOMEM;
253+
}
254+
255+
fs = &ch_flower->fs;
256+
fs->hitcnts = 1;
257+
cxgb4_process_flow_actions(dev, cls, fs);
258+
cxgb4_process_flow_match(dev, cls, fs);
259+
260+
fidx = cxgb4_get_free_ftid(dev, fs->type ? PF_INET6 : PF_INET);
261+
if (fidx < 0) {
262+
netdev_err(dev, "%s: No fidx for offload.\n", __func__);
263+
ret = -ENOMEM;
264+
goto free_entry;
265+
}
266+
267+
init_completion(&ctx.completion);
268+
ret = __cxgb4_set_filter(dev, fidx, fs, &ctx);
269+
if (ret) {
270+
netdev_err(dev, "%s: filter creation err %d\n",
271+
__func__, ret);
272+
goto free_entry;
273+
}
274+
275+
/* Wait for reply */
276+
ret = wait_for_completion_timeout(&ctx.completion, 10 * HZ);
277+
if (!ret) {
278+
ret = -ETIMEDOUT;
279+
goto free_entry;
280+
}
281+
282+
ret = ctx.result;
283+
/* Check if hw returned error for filter creation */
284+
if (ret) {
285+
netdev_err(dev, "%s: filter creation err %d\n",
286+
__func__, ret);
287+
goto free_entry;
288+
}
289+
290+
INIT_HLIST_NODE(&ch_flower->link);
291+
ch_flower->tc_flower_cookie = cls->cookie;
292+
ch_flower->filter_id = ctx.tid;
293+
hash_add_rcu(adap->flower_anymatch_tbl, &ch_flower->link, cls->cookie);
294+
295+
return ret;
296+
297+
free_entry:
298+
kfree(ch_flower);
299+
return ret;
45300
}
46301

47302
int cxgb4_tc_flower_destroy(struct net_device *dev,
48303
struct tc_cls_flower_offload *cls)
49304
{
50-
return -EOPNOTSUPP;
305+
struct adapter *adap = netdev2adap(dev);
306+
struct ch_tc_flower_entry *ch_flower;
307+
int ret;
308+
309+
ch_flower = ch_flower_lookup(adap, cls->cookie);
310+
if (!ch_flower)
311+
return -ENOENT;
312+
313+
ret = cxgb4_del_filter(dev, ch_flower->filter_id);
314+
if (ret)
315+
goto err;
316+
317+
hash_del_rcu(&ch_flower->link);
318+
kfree_rcu(ch_flower, rcu);
319+
320+
err:
321+
return ret;
51322
}
52323

53324
int cxgb4_tc_flower_stats(struct net_device *dev,
54325
struct tc_cls_flower_offload *cls)
55326
{
56327
return -EOPNOTSUPP;
57328
}
329+
330+
void cxgb4_init_tc_flower(struct adapter *adap)
331+
{
332+
hash_init(adap->flower_anymatch_tbl);
333+
}

drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,27 @@
3737

3838
#include <net/pkt_cls.h>
3939

40+
struct ch_tc_flower_stats {
41+
u64 packet_count;
42+
u64 byte_count;
43+
u64 last_used;
44+
};
45+
46+
struct ch_tc_flower_entry {
47+
struct ch_filter_specification fs;
48+
struct ch_tc_flower_stats stats;
49+
unsigned long tc_flower_cookie;
50+
struct hlist_node link;
51+
struct rcu_head rcu;
52+
u32 filter_id;
53+
};
54+
4055
int cxgb4_tc_flower_replace(struct net_device *dev,
4156
struct tc_cls_flower_offload *cls);
4257
int cxgb4_tc_flower_destroy(struct net_device *dev,
4358
struct tc_cls_flower_offload *cls);
4459
int cxgb4_tc_flower_stats(struct net_device *dev,
4560
struct tc_cls_flower_offload *cls);
61+
62+
void cxgb4_init_tc_flower(struct adapter *adap);
4663
#endif /* __CXGB4_TC_FLOWER_H */

drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ struct filter_ctx {
212212

213213
struct ch_filter_specification;
214214

215+
int cxgb4_get_free_ftid(struct net_device *dev, int family);
215216
int __cxgb4_set_filter(struct net_device *dev, int filter_id,
216217
struct ch_filter_specification *fs,
217218
struct filter_ctx *ctx);

0 commit comments

Comments
 (0)