162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <linux/if.h> 362306a36Sopenharmony_ci#include <linux/if_ether.h> 462306a36Sopenharmony_ci#include <linux/if_link.h> 562306a36Sopenharmony_ci#include <linux/netdevice.h> 662306a36Sopenharmony_ci#include <linux/in.h> 762306a36Sopenharmony_ci#include <linux/types.h> 862306a36Sopenharmony_ci#include <linux/skbuff.h> 962306a36Sopenharmony_ci#include <net/flow_dissector.h> 1062306a36Sopenharmony_ci#include "enic_res.h" 1162306a36Sopenharmony_ci#include "enic_clsf.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci/* enic_addfltr_5t - Add ipv4 5tuple filter 1462306a36Sopenharmony_ci * @enic: enic struct of vnic 1562306a36Sopenharmony_ci * @keys: flow_keys of ipv4 5tuple 1662306a36Sopenharmony_ci * @rq: rq number to steer to 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * This function returns filter_id(hardware_id) of the filter 1962306a36Sopenharmony_ci * added. In case of error it returns a negative number. 2062306a36Sopenharmony_ci */ 2162306a36Sopenharmony_ciint enic_addfltr_5t(struct enic *enic, struct flow_keys *keys, u16 rq) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci int res; 2462306a36Sopenharmony_ci struct filter data; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci switch (keys->basic.ip_proto) { 2762306a36Sopenharmony_ci case IPPROTO_TCP: 2862306a36Sopenharmony_ci data.u.ipv4.protocol = PROTO_TCP; 2962306a36Sopenharmony_ci break; 3062306a36Sopenharmony_ci case IPPROTO_UDP: 3162306a36Sopenharmony_ci data.u.ipv4.protocol = PROTO_UDP; 3262306a36Sopenharmony_ci break; 3362306a36Sopenharmony_ci default: 3462306a36Sopenharmony_ci return -EPROTONOSUPPORT; 3562306a36Sopenharmony_ci } 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci data.type = FILTER_IPV4_5TUPLE; 3862306a36Sopenharmony_ci data.u.ipv4.src_addr = ntohl(keys->addrs.v4addrs.src); 3962306a36Sopenharmony_ci data.u.ipv4.dst_addr = ntohl(keys->addrs.v4addrs.dst); 4062306a36Sopenharmony_ci data.u.ipv4.src_port = ntohs(keys->ports.src); 4162306a36Sopenharmony_ci data.u.ipv4.dst_port = ntohs(keys->ports.dst); 4262306a36Sopenharmony_ci data.u.ipv4.flags = FILTER_FIELDS_IPV4_5TUPLE; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci spin_lock_bh(&enic->devcmd_lock); 4562306a36Sopenharmony_ci res = vnic_dev_classifier(enic->vdev, CLSF_ADD, &rq, &data); 4662306a36Sopenharmony_ci spin_unlock_bh(&enic->devcmd_lock); 4762306a36Sopenharmony_ci res = (res == 0) ? rq : res; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci return res; 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* enic_delfltr - Delete clsf filter 5362306a36Sopenharmony_ci * @enic: enic struct of vnic 5462306a36Sopenharmony_ci * @filter_id: filter_is(hardware_id) of filter to be deleted 5562306a36Sopenharmony_ci * 5662306a36Sopenharmony_ci * This function returns zero in case of success, negative number incase of 5762306a36Sopenharmony_ci * error. 5862306a36Sopenharmony_ci */ 5962306a36Sopenharmony_ciint enic_delfltr(struct enic *enic, u16 filter_id) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci int ret; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci spin_lock_bh(&enic->devcmd_lock); 6462306a36Sopenharmony_ci ret = vnic_dev_classifier(enic->vdev, CLSF_DEL, &filter_id, NULL); 6562306a36Sopenharmony_ci spin_unlock_bh(&enic->devcmd_lock); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci return ret; 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/* enic_rfs_flw_tbl_init - initialize enic->rfs_h members 7162306a36Sopenharmony_ci * @enic: enic data 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_civoid enic_rfs_flw_tbl_init(struct enic *enic) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci int i; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci spin_lock_init(&enic->rfs_h.lock); 7862306a36Sopenharmony_ci for (i = 0; i <= ENIC_RFS_FLW_MASK; i++) 7962306a36Sopenharmony_ci INIT_HLIST_HEAD(&enic->rfs_h.ht_head[i]); 8062306a36Sopenharmony_ci enic->rfs_h.max = enic->config.num_arfs; 8162306a36Sopenharmony_ci enic->rfs_h.free = enic->rfs_h.max; 8262306a36Sopenharmony_ci enic->rfs_h.toclean = 0; 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_civoid enic_rfs_flw_tbl_free(struct enic *enic) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci int i; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci enic_rfs_timer_stop(enic); 9062306a36Sopenharmony_ci spin_lock_bh(&enic->rfs_h.lock); 9162306a36Sopenharmony_ci for (i = 0; i < (1 << ENIC_RFS_FLW_BITSHIFT); i++) { 9262306a36Sopenharmony_ci struct hlist_head *hhead; 9362306a36Sopenharmony_ci struct hlist_node *tmp; 9462306a36Sopenharmony_ci struct enic_rfs_fltr_node *n; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci hhead = &enic->rfs_h.ht_head[i]; 9762306a36Sopenharmony_ci hlist_for_each_entry_safe(n, tmp, hhead, node) { 9862306a36Sopenharmony_ci enic_delfltr(enic, n->fltr_id); 9962306a36Sopenharmony_ci hlist_del(&n->node); 10062306a36Sopenharmony_ci kfree(n); 10162306a36Sopenharmony_ci enic->rfs_h.free++; 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci spin_unlock_bh(&enic->rfs_h.lock); 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistruct enic_rfs_fltr_node *htbl_fltr_search(struct enic *enic, u16 fltr_id) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci int i; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci for (i = 0; i < (1 << ENIC_RFS_FLW_BITSHIFT); i++) { 11262306a36Sopenharmony_ci struct hlist_head *hhead; 11362306a36Sopenharmony_ci struct hlist_node *tmp; 11462306a36Sopenharmony_ci struct enic_rfs_fltr_node *n; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci hhead = &enic->rfs_h.ht_head[i]; 11762306a36Sopenharmony_ci hlist_for_each_entry_safe(n, tmp, hhead, node) 11862306a36Sopenharmony_ci if (n->fltr_id == fltr_id) 11962306a36Sopenharmony_ci return n; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci return NULL; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci#ifdef CONFIG_RFS_ACCEL 12662306a36Sopenharmony_civoid enic_flow_may_expire(struct timer_list *t) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci struct enic *enic = from_timer(enic, t, rfs_h.rfs_may_expire); 12962306a36Sopenharmony_ci bool res; 13062306a36Sopenharmony_ci int j; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci spin_lock_bh(&enic->rfs_h.lock); 13362306a36Sopenharmony_ci for (j = 0; j < ENIC_CLSF_EXPIRE_COUNT; j++) { 13462306a36Sopenharmony_ci struct hlist_head *hhead; 13562306a36Sopenharmony_ci struct hlist_node *tmp; 13662306a36Sopenharmony_ci struct enic_rfs_fltr_node *n; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci hhead = &enic->rfs_h.ht_head[enic->rfs_h.toclean++]; 13962306a36Sopenharmony_ci hlist_for_each_entry_safe(n, tmp, hhead, node) { 14062306a36Sopenharmony_ci res = rps_may_expire_flow(enic->netdev, n->rq_id, 14162306a36Sopenharmony_ci n->flow_id, n->fltr_id); 14262306a36Sopenharmony_ci if (res) { 14362306a36Sopenharmony_ci res = enic_delfltr(enic, n->fltr_id); 14462306a36Sopenharmony_ci if (unlikely(res)) 14562306a36Sopenharmony_ci continue; 14662306a36Sopenharmony_ci hlist_del(&n->node); 14762306a36Sopenharmony_ci kfree(n); 14862306a36Sopenharmony_ci enic->rfs_h.free++; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci spin_unlock_bh(&enic->rfs_h.lock); 15362306a36Sopenharmony_ci mod_timer(&enic->rfs_h.rfs_may_expire, jiffies + HZ/4); 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic struct enic_rfs_fltr_node *htbl_key_search(struct hlist_head *h, 15762306a36Sopenharmony_ci struct flow_keys *k) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci struct enic_rfs_fltr_node *tpos; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci hlist_for_each_entry(tpos, h, node) 16262306a36Sopenharmony_ci if (tpos->keys.addrs.v4addrs.src == k->addrs.v4addrs.src && 16362306a36Sopenharmony_ci tpos->keys.addrs.v4addrs.dst == k->addrs.v4addrs.dst && 16462306a36Sopenharmony_ci tpos->keys.ports.ports == k->ports.ports && 16562306a36Sopenharmony_ci tpos->keys.basic.ip_proto == k->basic.ip_proto && 16662306a36Sopenharmony_ci tpos->keys.basic.n_proto == k->basic.n_proto) 16762306a36Sopenharmony_ci return tpos; 16862306a36Sopenharmony_ci return NULL; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ciint enic_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb, 17262306a36Sopenharmony_ci u16 rxq_index, u32 flow_id) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci struct flow_keys keys; 17562306a36Sopenharmony_ci struct enic_rfs_fltr_node *n; 17662306a36Sopenharmony_ci struct enic *enic; 17762306a36Sopenharmony_ci u16 tbl_idx; 17862306a36Sopenharmony_ci int res, i; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci enic = netdev_priv(dev); 18162306a36Sopenharmony_ci res = skb_flow_dissect_flow_keys(skb, &keys, 0); 18262306a36Sopenharmony_ci if (!res || keys.basic.n_proto != htons(ETH_P_IP) || 18362306a36Sopenharmony_ci (keys.basic.ip_proto != IPPROTO_TCP && 18462306a36Sopenharmony_ci keys.basic.ip_proto != IPPROTO_UDP)) 18562306a36Sopenharmony_ci return -EPROTONOSUPPORT; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci tbl_idx = skb_get_hash_raw(skb) & ENIC_RFS_FLW_MASK; 18862306a36Sopenharmony_ci spin_lock_bh(&enic->rfs_h.lock); 18962306a36Sopenharmony_ci n = htbl_key_search(&enic->rfs_h.ht_head[tbl_idx], &keys); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (n) { /* entry already present */ 19262306a36Sopenharmony_ci if (rxq_index == n->rq_id) { 19362306a36Sopenharmony_ci res = -EEXIST; 19462306a36Sopenharmony_ci goto ret_unlock; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* desired rq changed for the flow, we need to delete 19862306a36Sopenharmony_ci * old fltr and add new one 19962306a36Sopenharmony_ci * 20062306a36Sopenharmony_ci * The moment we delete the fltr, the upcoming pkts 20162306a36Sopenharmony_ci * are put it default rq based on rss. When we add 20262306a36Sopenharmony_ci * new filter, upcoming pkts are put in desired queue. 20362306a36Sopenharmony_ci * This could cause ooo pkts. 20462306a36Sopenharmony_ci * 20562306a36Sopenharmony_ci * Lets 1st try adding new fltr and then del old one. 20662306a36Sopenharmony_ci */ 20762306a36Sopenharmony_ci i = --enic->rfs_h.free; 20862306a36Sopenharmony_ci /* clsf tbl is full, we have to del old fltr first*/ 20962306a36Sopenharmony_ci if (unlikely(i < 0)) { 21062306a36Sopenharmony_ci enic->rfs_h.free++; 21162306a36Sopenharmony_ci res = enic_delfltr(enic, n->fltr_id); 21262306a36Sopenharmony_ci if (unlikely(res < 0)) 21362306a36Sopenharmony_ci goto ret_unlock; 21462306a36Sopenharmony_ci res = enic_addfltr_5t(enic, &keys, rxq_index); 21562306a36Sopenharmony_ci if (res < 0) { 21662306a36Sopenharmony_ci hlist_del(&n->node); 21762306a36Sopenharmony_ci enic->rfs_h.free++; 21862306a36Sopenharmony_ci goto ret_unlock; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci /* add new fltr 1st then del old fltr */ 22162306a36Sopenharmony_ci } else { 22262306a36Sopenharmony_ci int ret; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci res = enic_addfltr_5t(enic, &keys, rxq_index); 22562306a36Sopenharmony_ci if (res < 0) { 22662306a36Sopenharmony_ci enic->rfs_h.free++; 22762306a36Sopenharmony_ci goto ret_unlock; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci ret = enic_delfltr(enic, n->fltr_id); 23062306a36Sopenharmony_ci /* deleting old fltr failed. Add old fltr to list. 23162306a36Sopenharmony_ci * enic_flow_may_expire() will try to delete it later. 23262306a36Sopenharmony_ci */ 23362306a36Sopenharmony_ci if (unlikely(ret < 0)) { 23462306a36Sopenharmony_ci struct enic_rfs_fltr_node *d; 23562306a36Sopenharmony_ci struct hlist_head *head; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci head = &enic->rfs_h.ht_head[tbl_idx]; 23862306a36Sopenharmony_ci d = kmalloc(sizeof(*d), GFP_ATOMIC); 23962306a36Sopenharmony_ci if (d) { 24062306a36Sopenharmony_ci d->fltr_id = n->fltr_id; 24162306a36Sopenharmony_ci INIT_HLIST_NODE(&d->node); 24262306a36Sopenharmony_ci hlist_add_head(&d->node, head); 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci } else { 24562306a36Sopenharmony_ci enic->rfs_h.free++; 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci n->rq_id = rxq_index; 24962306a36Sopenharmony_ci n->fltr_id = res; 25062306a36Sopenharmony_ci n->flow_id = flow_id; 25162306a36Sopenharmony_ci /* entry not present */ 25262306a36Sopenharmony_ci } else { 25362306a36Sopenharmony_ci i = --enic->rfs_h.free; 25462306a36Sopenharmony_ci if (i <= 0) { 25562306a36Sopenharmony_ci enic->rfs_h.free++; 25662306a36Sopenharmony_ci res = -EBUSY; 25762306a36Sopenharmony_ci goto ret_unlock; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci n = kmalloc(sizeof(*n), GFP_ATOMIC); 26162306a36Sopenharmony_ci if (!n) { 26262306a36Sopenharmony_ci res = -ENOMEM; 26362306a36Sopenharmony_ci enic->rfs_h.free++; 26462306a36Sopenharmony_ci goto ret_unlock; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci res = enic_addfltr_5t(enic, &keys, rxq_index); 26862306a36Sopenharmony_ci if (res < 0) { 26962306a36Sopenharmony_ci kfree(n); 27062306a36Sopenharmony_ci enic->rfs_h.free++; 27162306a36Sopenharmony_ci goto ret_unlock; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci n->rq_id = rxq_index; 27462306a36Sopenharmony_ci n->fltr_id = res; 27562306a36Sopenharmony_ci n->flow_id = flow_id; 27662306a36Sopenharmony_ci n->keys = keys; 27762306a36Sopenharmony_ci INIT_HLIST_NODE(&n->node); 27862306a36Sopenharmony_ci hlist_add_head(&n->node, &enic->rfs_h.ht_head[tbl_idx]); 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ciret_unlock: 28262306a36Sopenharmony_ci spin_unlock_bh(&enic->rfs_h.lock); 28362306a36Sopenharmony_ci return res; 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci#endif /* CONFIG_RFS_ACCEL */ 287