Hi, Kumar On 2017/9/21 15:33, Rahul Lakkireddy wrote: > From: Kumar Sanghvi <kuma...@chelsio.com> > > 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 <kuma...@chelsio.com> > Signed-off-by: Rahul Lakkireddy <rahul.lakkire...@chelsio.com> > Signed-off-by: Ganesh Goudar <ganes...@chelsio.com> > --- > drivers/net/ethernet/chelsio/cxgb4/cxgb4.h | 3 + > drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c | 26 ++ > drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 2 + > .../net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c | 285 > ++++++++++++++++++++- > .../net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.h | 17 ++ > drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h | 1 + > 6 files changed, 332 insertions(+), 2 deletions(-) > > diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h > b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h > index ea72d2d2e1b4..26eac599ab2c 100644 > --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h > +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h > @@ -904,6 +904,9 @@ struct adapter { > /* TC u32 offload */ > struct cxgb4_tc_u32_table *tc_u32; > struct chcr_stats_debug chcr_stats; > + > + /* TC flower offload */ > + DECLARE_HASHTABLE(flower_anymatch_tbl, 9); > }; > > /* Support for "sched-class" command to allow a TX Scheduling Class to be > diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c > b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c > index 45b5853ca2f1..07a4619e2164 100644 > --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c > +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c > @@ -148,6 +148,32 @@ static int get_filter_steerq(struct net_device *dev, > return iq; > } > > +int cxgb4_get_free_ftid(struct net_device *dev, int family) > +{ > + struct adapter *adap = netdev2adap(dev); > + struct tid_info *t = &adap->tids; > + int ftid; > + > + spin_lock_bh(&t->ftid_lock); > + if (family == PF_INET) { > + ftid = find_first_zero_bit(t->ftid_bmap, t->nftids); > + if (ftid >= t->nftids) > + ftid = -1; > + } else { > + ftid = bitmap_find_free_region(t->ftid_bmap, t->nftids, 2); > + if (ftid < 0) { > + ftid = -1;
ftid = -1 is not needed? > + goto out_unlock; > + } > + > + /* this is only a lookup, keep the found region unallocated */ > + bitmap_release_region(t->ftid_bmap, ftid, 2); > + } > +out_unlock: > + spin_unlock_bh(&t->ftid_lock); > + return ftid; > +} > + > static int cxgb4_set_ftid(struct tid_info *t, int fidx, int family) > { > spin_lock_bh(&t->ftid_lock); > diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c > b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c > index 8923affbdaf8..3ba4e1ff8486 100644 > --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c > +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c > @@ -5105,6 +5105,8 @@ static int init_one(struct pci_dev *pdev, const struct > pci_device_id *ent) > if (!adapter->tc_u32) > dev_warn(&pdev->dev, > "could not offload tc u32, continuing\n"); > + > + cxgb4_init_tc_flower(adapter); > } > > if (is_offload(adapter)) { > diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c > b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c > index 16dff71e4d02..1af01101faaf 100644 > --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c > +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c > @@ -38,16 +38,292 @@ > #include "cxgb4.h" > #include "cxgb4_tc_flower.h" > > +static struct ch_tc_flower_entry *allocate_flower_entry(void) > +{ > + struct ch_tc_flower_entry *new = kzalloc(sizeof(*new), GFP_KERNEL); > + return new; > +} > + > +/* Must be called with either RTNL or rcu_read_lock */ > +static struct ch_tc_flower_entry *ch_flower_lookup(struct adapter *adap, > + unsigned long flower_cookie) > +{ > + struct ch_tc_flower_entry *flower_entry; > + > + hash_for_each_possible_rcu(adap->flower_anymatch_tbl, flower_entry, > + link, flower_cookie) > + if (flower_entry->tc_flower_cookie == flower_cookie) > + return flower_entry; > + return NULL; > +} > + > +static void cxgb4_process_flow_match(struct net_device *dev, > + struct tc_cls_flower_offload *cls, > + struct ch_filter_specification *fs) > +{ > + u16 addr_type = 0; > + > + if (dissector_uses_key(cls->dissector, FLOW_DISSECTOR_KEY_CONTROL)) { > + struct flow_dissector_key_control *key = > + skb_flow_dissector_target(cls->dissector, > + FLOW_DISSECTOR_KEY_CONTROL, > + cls->key); > + > + addr_type = key->addr_type; > + } > + > + if (dissector_uses_key(cls->dissector, FLOW_DISSECTOR_KEY_BASIC)) { > + struct flow_dissector_key_basic *key = > + skb_flow_dissector_target(cls->dissector, > + FLOW_DISSECTOR_KEY_BASIC, > + cls->key); > + struct flow_dissector_key_basic *mask = > + skb_flow_dissector_target(cls->dissector, > + FLOW_DISSECTOR_KEY_BASIC, > + cls->mask); > + u16 ethtype_key = ntohs(key->n_proto); > + u16 ethtype_mask = ntohs(mask->n_proto); > + > + if (ethtype_key == ETH_P_ALL) { > + ethtype_key = 0; > + ethtype_mask = 0; > + } > + > + fs->val.ethtype = ethtype_key; > + fs->mask.ethtype = ethtype_mask; > + fs->val.proto = key->ip_proto; > + fs->mask.proto = mask->ip_proto; > + } > + > + if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) { > + struct flow_dissector_key_ipv4_addrs *key = > + skb_flow_dissector_target(cls->dissector, > + FLOW_DISSECTOR_KEY_IPV4_ADDRS, > + cls->key); > + struct flow_dissector_key_ipv4_addrs *mask = > + skb_flow_dissector_target(cls->dissector, > + FLOW_DISSECTOR_KEY_IPV4_ADDRS, > + cls->mask); > + fs->type = 0; > + memcpy(&fs->val.lip[0], &key->dst, sizeof(key->dst)); > + memcpy(&fs->val.fip[0], &key->src, sizeof(key->src)); > + memcpy(&fs->mask.lip[0], &mask->dst, sizeof(mask->dst)); > + memcpy(&fs->mask.fip[0], &mask->src, sizeof(mask->src)); > + } > + > + if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) { > + struct flow_dissector_key_ipv6_addrs *key = > + skb_flow_dissector_target(cls->dissector, > + FLOW_DISSECTOR_KEY_IPV6_ADDRS, > + cls->key); > + struct flow_dissector_key_ipv6_addrs *mask = > + skb_flow_dissector_target(cls->dissector, > + FLOW_DISSECTOR_KEY_IPV6_ADDRS, > + cls->mask); > + > + fs->type = 1; > + memcpy(&fs->val.lip[0], key->dst.s6_addr, sizeof(key->dst)); > + memcpy(&fs->val.fip[0], key->src.s6_addr, sizeof(key->src)); > + memcpy(&fs->mask.lip[0], mask->dst.s6_addr, sizeof(mask->dst)); > + memcpy(&fs->mask.fip[0], mask->src.s6_addr, sizeof(mask->src)); > + } > + > + if (dissector_uses_key(cls->dissector, FLOW_DISSECTOR_KEY_PORTS)) { > + struct flow_dissector_key_ports *key, *mask; > + > + key = skb_flow_dissector_target(cls->dissector, > + FLOW_DISSECTOR_KEY_PORTS, > + cls->key); > + mask = skb_flow_dissector_target(cls->dissector, > + FLOW_DISSECTOR_KEY_PORTS, > + cls->mask); > + fs->val.lport = cpu_to_be16(key->dst); > + fs->mask.lport = cpu_to_be16(mask->dst); > + fs->val.fport = cpu_to_be16(key->src); > + fs->mask.fport = cpu_to_be16(mask->src); > + } > + > + /* Match only packets coming from the ingress port where this > + * filter will be created. > + */ > + fs->val.iport = netdev2pinfo(dev)->port_id; > + fs->mask.iport = ~0; > +} > + > +static int cxgb4_validate_flow_match(struct net_device *dev, > + struct tc_cls_flower_offload *cls) > +{ > + if (cls->dissector->used_keys & > + ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) | > + BIT(FLOW_DISSECTOR_KEY_BASIC) | > + BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) | > + BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) | > + BIT(FLOW_DISSECTOR_KEY_PORTS))) { > + netdev_warn(dev, "Unsupported key used: 0x%x\n", > + cls->dissector->used_keys); > + return -EOPNOTSUPP; > + } > + return 0; > +} > + > +static void cxgb4_process_flow_actions(struct net_device *in, > + struct tc_cls_flower_offload *cls, > + struct ch_filter_specification *fs) > +{ > + const struct tc_action *a; > + LIST_HEAD(actions); > + > + tcf_exts_to_list(cls->exts, &actions); > + list_for_each_entry(a, &actions, list) { > + if (is_tcf_gact_shot(a)) { > + fs->action = FILTER_DROP; > + } else if (is_tcf_mirred_egress_redirect(a)) { > + int ifindex = tcf_mirred_ifindex(a); > + struct net_device *out = __dev_get_by_index(dev_net(in), > + ifindex); > + struct port_info *pi = netdev_priv(out); > + > + fs->action = FILTER_SWITCH; > + fs->eport = pi->port_id; > + } > + } > +} > + > +static int cxgb4_validate_flow_actions(struct net_device *dev, > + struct tc_cls_flower_offload *cls) > +{ > + const struct tc_action *a; > + LIST_HEAD(actions); > + > + tcf_exts_to_list(cls->exts, &actions); > + list_for_each_entry(a, &actions, list) { > + if (is_tcf_gact_shot(a)) { > + /* Do nothing */ > + } else if (is_tcf_mirred_egress_redirect(a)) { > + struct adapter *adap = netdev2adap(dev); > + struct net_device *n_dev; > + unsigned int i, ifindex; > + bool found = false; > + > + ifindex = tcf_mirred_ifindex(a); > + for_each_port(adap, i) { > + n_dev = adap->port[i]; > + if (ifindex == n_dev->ifindex) { > + found = true; > + break; > + } > + } > + > + /* If interface doesn't belong to our hw, then > + * the provided output port is not valid > + */ > + if (!found) { > + netdev_err(dev, "%s: Out port invalid\n", > + __func__); > + return -EINVAL; > + } > + } else { > + netdev_err(dev, "%s: Unsupported action\n", __func__); > + return -EOPNOTSUPP; > + } > + } > + return 0; > +} > + > int cxgb4_tc_flower_replace(struct net_device *dev, > struct tc_cls_flower_offload *cls) > { > - return -EOPNOTSUPP; > + struct adapter *adap = netdev2adap(dev); > + struct ch_tc_flower_entry *ch_flower; > + struct ch_filter_specification *fs; > + struct filter_ctx ctx; > + int fidx; > + int ret; > + > + if (cxgb4_validate_flow_actions(dev, cls)) > + return -EOPNOTSUPP; > + > + if (cxgb4_validate_flow_match(dev, cls)) > + return -EOPNOTSUPP; > + > + ch_flower = allocate_flower_entry(); > + if (!ch_flower) { > + netdev_err(dev, "%s: ch_flower alloc failed.\n", __func__); > + ret = -ENOMEM; > + goto err; Just return, err label is needed? > + } > + > + fs = &ch_flower->fs; > + fs->hitcnts = 1; > + cxgb4_process_flow_actions(dev, cls, fs); > + cxgb4_process_flow_match(dev, cls, fs); > + > + fidx = cxgb4_get_free_ftid(dev, fs->type ? PF_INET6 : PF_INET); > + if (fidx < 0) { > + netdev_err(dev, "%s: No fidx for offload.\n", __func__); > + ret = -ENOMEM; > + goto free_entry; > + } > + > + init_completion(&ctx.completion); > + ret = __cxgb4_set_filter(dev, fidx, fs, &ctx); > + if (ret) { > + netdev_err(dev, "%s: filter creation err %d\n", > + __func__, ret); > + goto free_entry; > + } > + > + /* Wait for reply */ > + ret = wait_for_completion_timeout(&ctx.completion, 10 * HZ); > + if (!ret) { > + ret = -ETIMEDOUT; > + goto free_entry; > + } > + > + ret = ctx.result; > + /* Check if hw returned error for filter creation */ > + if (ret) { > + netdev_err(dev, "%s: filter creation err %d\n", > + __func__, ret); > + goto free_entry; > + } > + > + INIT_HLIST_NODE(&ch_flower->link); > + ch_flower->tc_flower_cookie = cls->cookie; > + ch_flower->filter_id = ctx.tid; > + hash_add_rcu(adap->flower_anymatch_tbl, &ch_flower->link, cls->cookie); > + > + return ret; > + > +free_entry: > + kfree(ch_flower); > +err: > + return ret; > } > > int cxgb4_tc_flower_destroy(struct net_device *dev, > struct tc_cls_flower_offload *cls) > { > - return -EOPNOTSUPP; > + struct adapter *adap = netdev2adap(dev); > + struct ch_tc_flower_entry *ch_flower; > + int ret; > + > + ch_flower = ch_flower_lookup(adap, cls->cookie); > + if (!ch_flower) { > + ret = -ENOENT; > + goto err; Same as above > + } > + > + ret = cxgb4_del_filter(dev, ch_flower->filter_id); > + if (ret) > + goto err; > + > + hash_del_rcu(&ch_flower->link); > + kfree_rcu(ch_flower, rcu); > + return ret; > + > +err: > + return ret; > } > > int cxgb4_tc_flower_stats(struct net_device *dev, > @@ -55,3 +331,8 @@ int cxgb4_tc_flower_stats(struct net_device *dev, > { > return -EOPNOTSUPP; > } > + > +void cxgb4_init_tc_flower(struct adapter *adap) > +{ > + hash_init(adap->flower_anymatch_tbl); > +} > diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.h > b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.h > index b321fc205b5a..6145a9e056eb 100644 > --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.h > +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.h > @@ -37,10 +37,27 @@ > > #include <net/pkt_cls.h> > > +struct ch_tc_flower_stats { > + u64 packet_count; > + u64 byte_count; > + u64 last_used; > +}; > + > +struct ch_tc_flower_entry { > + struct ch_filter_specification fs; > + struct ch_tc_flower_stats stats; > + unsigned long tc_flower_cookie; > + struct hlist_node link; > + struct rcu_head rcu; > + u32 filter_id; > +}; > + > int cxgb4_tc_flower_replace(struct net_device *dev, > struct tc_cls_flower_offload *cls); > int cxgb4_tc_flower_destroy(struct net_device *dev, > struct tc_cls_flower_offload *cls); > int cxgb4_tc_flower_stats(struct net_device *dev, > struct tc_cls_flower_offload *cls); > + > +void cxgb4_init_tc_flower(struct adapter *adap); > #endif /* __CXGB4_TC_FLOWER_H */ > diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h > b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h > index 84541fce94c5..88487095d14f 100644 > --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h > +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h > @@ -212,6 +212,7 @@ struct filter_ctx { > > struct ch_filter_specification; > > +int cxgb4_get_free_ftid(struct net_device *dev, int family); > int __cxgb4_set_filter(struct net_device *dev, int filter_id, > struct ch_filter_specification *fs, > struct filter_ctx *ctx); >