-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Govindarajulu Varadarajan says: ==================== enic updates This series fixes minor bugs and adds new features like Accelerated RFS, busy_poll, tx clean-up in napi_poll. v3: * While doing tx cleanup in napi, ignore budget and clean up all desc possible. v2: * Fix #ifdef coding style issue in '[PATCH 4/8] enic: alloc/free rx_cpu_rmap' And [PATCH 5/8] enic: Add Accelerated RFS support' ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
- Loading branch information
Showing
17 changed files
with
786 additions
and
104 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,279 @@ | ||
#include <linux/if.h> | ||
#include <linux/if_ether.h> | ||
#include <linux/if_link.h> | ||
#include <linux/netdevice.h> | ||
#include <linux/in.h> | ||
#include <linux/types.h> | ||
#include <linux/skbuff.h> | ||
#include <net/flow_keys.h> | ||
#include "enic_res.h" | ||
#include "enic_clsf.h" | ||
|
||
/* enic_addfltr_5t - Add ipv4 5tuple filter | ||
* @enic: enic struct of vnic | ||
* @keys: flow_keys of ipv4 5tuple | ||
* @rq: rq number to steer to | ||
* | ||
* This function returns filter_id(hardware_id) of the filter | ||
* added. In case of error it returns an negative number. | ||
*/ | ||
int enic_addfltr_5t(struct enic *enic, struct flow_keys *keys, u16 rq) | ||
{ | ||
int res; | ||
struct filter data; | ||
|
||
switch (keys->ip_proto) { | ||
case IPPROTO_TCP: | ||
data.u.ipv4.protocol = PROTO_TCP; | ||
break; | ||
case IPPROTO_UDP: | ||
data.u.ipv4.protocol = PROTO_UDP; | ||
break; | ||
default: | ||
return -EPROTONOSUPPORT; | ||
}; | ||
data.type = FILTER_IPV4_5TUPLE; | ||
data.u.ipv4.src_addr = ntohl(keys->src); | ||
data.u.ipv4.dst_addr = ntohl(keys->dst); | ||
data.u.ipv4.src_port = ntohs(keys->port16[0]); | ||
data.u.ipv4.dst_port = ntohs(keys->port16[1]); | ||
data.u.ipv4.flags = FILTER_FIELDS_IPV4_5TUPLE; | ||
|
||
spin_lock_bh(&enic->devcmd_lock); | ||
res = vnic_dev_classifier(enic->vdev, CLSF_ADD, &rq, &data); | ||
spin_unlock_bh(&enic->devcmd_lock); | ||
res = (res == 0) ? rq : res; | ||
|
||
return res; | ||
} | ||
|
||
/* enic_delfltr - Delete clsf filter | ||
* @enic: enic struct of vnic | ||
* @filter_id: filter_is(hardware_id) of filter to be deleted | ||
* | ||
* This function returns zero in case of success, negative number incase of | ||
* error. | ||
*/ | ||
int enic_delfltr(struct enic *enic, u16 filter_id) | ||
{ | ||
int ret; | ||
|
||
spin_lock_bh(&enic->devcmd_lock); | ||
ret = vnic_dev_classifier(enic->vdev, CLSF_DEL, &filter_id, NULL); | ||
spin_unlock_bh(&enic->devcmd_lock); | ||
|
||
return ret; | ||
} | ||
|
||
#ifdef CONFIG_RFS_ACCEL | ||
void enic_flow_may_expire(unsigned long data) | ||
{ | ||
struct enic *enic = (struct enic *)data; | ||
bool res; | ||
int j; | ||
|
||
spin_lock(&enic->rfs_h.lock); | ||
for (j = 0; j < ENIC_CLSF_EXPIRE_COUNT; j++) { | ||
struct hlist_head *hhead; | ||
struct hlist_node *tmp; | ||
struct enic_rfs_fltr_node *n; | ||
|
||
hhead = &enic->rfs_h.ht_head[enic->rfs_h.toclean++]; | ||
hlist_for_each_entry_safe(n, tmp, hhead, node) { | ||
res = rps_may_expire_flow(enic->netdev, n->rq_id, | ||
n->flow_id, n->fltr_id); | ||
if (res) { | ||
res = enic_delfltr(enic, n->fltr_id); | ||
if (unlikely(res)) | ||
continue; | ||
hlist_del(&n->node); | ||
kfree(n); | ||
enic->rfs_h.free++; | ||
} | ||
} | ||
} | ||
spin_unlock(&enic->rfs_h.lock); | ||
mod_timer(&enic->rfs_h.rfs_may_expire, jiffies + HZ/4); | ||
} | ||
|
||
/* enic_rfs_flw_tbl_init - initialize enic->rfs_h members | ||
* @enic: enic data | ||
*/ | ||
void enic_rfs_flw_tbl_init(struct enic *enic) | ||
{ | ||
int i; | ||
|
||
spin_lock_init(&enic->rfs_h.lock); | ||
for (i = 0; i <= ENIC_RFS_FLW_MASK; i++) | ||
INIT_HLIST_HEAD(&enic->rfs_h.ht_head[i]); | ||
enic->rfs_h.max = enic->config.num_arfs; | ||
enic->rfs_h.free = enic->rfs_h.max; | ||
enic->rfs_h.toclean = 0; | ||
init_timer(&enic->rfs_h.rfs_may_expire); | ||
enic->rfs_h.rfs_may_expire.function = enic_flow_may_expire; | ||
enic->rfs_h.rfs_may_expire.data = (unsigned long)enic; | ||
mod_timer(&enic->rfs_h.rfs_may_expire, jiffies + HZ/4); | ||
} | ||
|
||
void enic_rfs_flw_tbl_free(struct enic *enic) | ||
{ | ||
int i, res; | ||
|
||
del_timer_sync(&enic->rfs_h.rfs_may_expire); | ||
spin_lock(&enic->rfs_h.lock); | ||
enic->rfs_h.free = 0; | ||
for (i = 0; i < (1 << ENIC_RFS_FLW_BITSHIFT); i++) { | ||
struct hlist_head *hhead; | ||
struct hlist_node *tmp; | ||
struct enic_rfs_fltr_node *n; | ||
|
||
hhead = &enic->rfs_h.ht_head[i]; | ||
hlist_for_each_entry_safe(n, tmp, hhead, node) { | ||
enic_delfltr(enic, n->fltr_id); | ||
hlist_del(&n->node); | ||
kfree(n); | ||
} | ||
} | ||
spin_unlock(&enic->rfs_h.lock); | ||
} | ||
|
||
static struct enic_rfs_fltr_node *htbl_key_search(struct hlist_head *h, | ||
struct flow_keys *k) | ||
{ | ||
struct enic_rfs_fltr_node *tpos; | ||
|
||
hlist_for_each_entry(tpos, h, node) | ||
if (tpos->keys.src == k->src && | ||
tpos->keys.dst == k->dst && | ||
tpos->keys.ports == k->ports && | ||
tpos->keys.ip_proto == k->ip_proto && | ||
tpos->keys.n_proto == k->n_proto) | ||
return tpos; | ||
return NULL; | ||
} | ||
|
||
int enic_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb, | ||
u16 rxq_index, u32 flow_id) | ||
{ | ||
struct flow_keys keys; | ||
struct enic_rfs_fltr_node *n; | ||
struct enic *enic; | ||
u16 tbl_idx; | ||
int res, i; | ||
|
||
enic = netdev_priv(dev); | ||
res = skb_flow_dissect(skb, &keys); | ||
if (!res || keys.n_proto != htons(ETH_P_IP) || | ||
(keys.ip_proto != IPPROTO_TCP && keys.ip_proto != IPPROTO_UDP)) | ||
return -EPROTONOSUPPORT; | ||
|
||
tbl_idx = skb_get_hash_raw(skb) & ENIC_RFS_FLW_MASK; | ||
spin_lock(&enic->rfs_h.lock); | ||
n = htbl_key_search(&enic->rfs_h.ht_head[tbl_idx], &keys); | ||
|
||
if (n) { /* entry already present */ | ||
if (rxq_index == n->rq_id) { | ||
res = -EEXIST; | ||
goto ret_unlock; | ||
} | ||
|
||
/* desired rq changed for the flow, we need to delete | ||
* old fltr and add new one | ||
* | ||
* The moment we delete the fltr, the upcoming pkts | ||
* are put it default rq based on rss. When we add | ||
* new filter, upcoming pkts are put in desired queue. | ||
* This could cause ooo pkts. | ||
* | ||
* Lets 1st try adding new fltr and then del old one. | ||
*/ | ||
i = --enic->rfs_h.free; | ||
/* clsf tbl is full, we have to del old fltr first*/ | ||
if (unlikely(i < 0)) { | ||
enic->rfs_h.free++; | ||
res = enic_delfltr(enic, n->fltr_id); | ||
if (unlikely(res < 0)) | ||
goto ret_unlock; | ||
res = enic_addfltr_5t(enic, &keys, rxq_index); | ||
if (res < 0) { | ||
hlist_del(&n->node); | ||
enic->rfs_h.free++; | ||
goto ret_unlock; | ||
} | ||
/* add new fltr 1st then del old fltr */ | ||
} else { | ||
int ret; | ||
|
||
res = enic_addfltr_5t(enic, &keys, rxq_index); | ||
if (res < 0) { | ||
enic->rfs_h.free++; | ||
goto ret_unlock; | ||
} | ||
ret = enic_delfltr(enic, n->fltr_id); | ||
/* deleting old fltr failed. Add old fltr to list. | ||
* enic_flow_may_expire() will try to delete it later. | ||
*/ | ||
if (unlikely(ret < 0)) { | ||
struct enic_rfs_fltr_node *d; | ||
struct hlist_head *head; | ||
|
||
head = &enic->rfs_h.ht_head[tbl_idx]; | ||
d = kmalloc(sizeof(*d), GFP_ATOMIC); | ||
if (d) { | ||
d->fltr_id = n->fltr_id; | ||
INIT_HLIST_NODE(&d->node); | ||
hlist_add_head(&d->node, head); | ||
} | ||
} else { | ||
enic->rfs_h.free++; | ||
} | ||
} | ||
n->rq_id = rxq_index; | ||
n->fltr_id = res; | ||
n->flow_id = flow_id; | ||
/* entry not present */ | ||
} else { | ||
i = --enic->rfs_h.free; | ||
if (i <= 0) { | ||
enic->rfs_h.free++; | ||
res = -EBUSY; | ||
goto ret_unlock; | ||
} | ||
|
||
n = kmalloc(sizeof(*n), GFP_ATOMIC); | ||
if (!n) { | ||
res = -ENOMEM; | ||
enic->rfs_h.free++; | ||
goto ret_unlock; | ||
} | ||
|
||
res = enic_addfltr_5t(enic, &keys, rxq_index); | ||
if (res < 0) { | ||
kfree(n); | ||
enic->rfs_h.free++; | ||
goto ret_unlock; | ||
} | ||
n->rq_id = rxq_index; | ||
n->fltr_id = res; | ||
n->flow_id = flow_id; | ||
n->keys = keys; | ||
INIT_HLIST_NODE(&n->node); | ||
hlist_add_head(&n->node, &enic->rfs_h.ht_head[tbl_idx]); | ||
} | ||
|
||
ret_unlock: | ||
spin_unlock(&enic->rfs_h.lock); | ||
return res; | ||
} | ||
|
||
#else | ||
|
||
void enic_rfs_flw_tbl_init(struct enic *enic) | ||
{ | ||
} | ||
|
||
void enic_rfs_flw_tbl_free(struct enic *enic) | ||
{ | ||
} | ||
|
||
#endif /* CONFIG_RFS_ACCEL */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
#ifndef _ENIC_CLSF_H_ | ||
#define _ENIC_CLSF_H_ | ||
|
||
#include "vnic_dev.h" | ||
#include "enic.h" | ||
|
||
#define ENIC_CLSF_EXPIRE_COUNT 128 | ||
|
||
int enic_addfltr_5t(struct enic *enic, struct flow_keys *keys, u16 rq); | ||
int enic_delfltr(struct enic *enic, u16 filter_id); | ||
|
||
#ifdef CONFIG_RFS_ACCEL | ||
void enic_rfs_flw_tbl_init(struct enic *enic); | ||
void enic_rfs_flw_tbl_free(struct enic *enic); | ||
int enic_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb, | ||
u16 rxq_index, u32 flow_id); | ||
#endif /* CONFIG_RFS_ACCEL */ | ||
|
||
#endif /* _ENIC_CLSF_H_ */ |
Oops, something went wrong.