Commit 4426b78c authored by Horatiu Vultur's avatar Horatiu Vultur Committed by Paolo Abeni

net: lan966x: Add port keyset config and callback interface

Implement vcap_operations and enable default port keyset configuration
for each port. Now it is possible actually write/read/move entries in
the VCAP.
Signed-off-by: default avatarHoratiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parent 61caac2d
......@@ -13,6 +13,13 @@
#define LAN966X_IS2_LOOKUPS 2
enum vcap_is2_port_sel_ipv6 {
VCAP_IS2_PS_IPV6_TCPUDP_OTHER,
VCAP_IS2_PS_IPV6_STD,
VCAP_IS2_PS_IPV6_IP4_TCPUDP_IP4_OTHER,
VCAP_IS2_PS_IPV6_MAC_ETYPE,
};
static struct lan966x_vcap_inst {
enum vcap_type vtype; /* type of vcap */
int tgt_inst; /* hardware instance number */
......@@ -73,6 +80,347 @@ static void __lan966x_vcap_range_init(struct lan966x *lan966x,
lan966x_vcap_wait_update(lan966x, admin->tgt_inst);
}
static int lan966x_vcap_cid_to_lookup(int cid)
{
if (cid >= LAN966X_VCAP_CID_IS2_L1 &&
cid < LAN966X_VCAP_CID_IS2_MAX)
return 1;
return 0;
}
static int
lan966x_vcap_is2_get_port_keysets(struct net_device *dev, int lookup,
struct vcap_keyset_list *keysetlist,
u16 l3_proto)
{
struct lan966x_port *port = netdev_priv(dev);
struct lan966x *lan966x = port->lan966x;
bool found = false;
u32 val;
/* Check if the port keyset selection is enabled */
val = lan_rd(lan966x, ANA_VCAP_S2_CFG(port->chip_port));
if (!ANA_VCAP_S2_CFG_ENA_GET(val))
return -ENOENT;
/* Collect all keysets for the port in a list */
if (l3_proto == ETH_P_ALL)
vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE);
if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_SNAP) {
if (ANA_VCAP_S2_CFG_SNAP_DIS_GET(val) & (BIT(0) << lookup))
vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_LLC);
else
vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_SNAP);
found = true;
}
if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_CFM) {
if (ANA_VCAP_S2_CFG_OAM_DIS_GET(val) & (BIT(0) << lookup))
vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE);
else
vcap_keyset_list_add(keysetlist, VCAP_KFS_OAM);
found = true;
}
if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_ARP) {
if (ANA_VCAP_S2_CFG_ARP_DIS_GET(val) & (BIT(0) << lookup))
vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE);
else
vcap_keyset_list_add(keysetlist, VCAP_KFS_ARP);
found = true;
}
if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_IP) {
if (ANA_VCAP_S2_CFG_IP_OTHER_DIS_GET(val) & (BIT(0) << lookup))
vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE);
else
vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_OTHER);
if (ANA_VCAP_S2_CFG_IP_TCPUDP_DIS_GET(val) & (BIT(0) << lookup))
vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE);
else
vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_TCP_UDP);
found = true;
}
if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_IPV6) {
switch (ANA_VCAP_S2_CFG_IP6_CFG_GET(val) & (0x3 << lookup)) {
case VCAP_IS2_PS_IPV6_TCPUDP_OTHER:
vcap_keyset_list_add(keysetlist, VCAP_KFS_IP6_OTHER);
vcap_keyset_list_add(keysetlist, VCAP_KFS_IP6_TCP_UDP);
break;
case VCAP_IS2_PS_IPV6_STD:
vcap_keyset_list_add(keysetlist, VCAP_KFS_IP6_STD);
break;
case VCAP_IS2_PS_IPV6_IP4_TCPUDP_IP4_OTHER:
vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_OTHER);
vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_TCP_UDP);
break;
case VCAP_IS2_PS_IPV6_MAC_ETYPE:
vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE);
break;
}
found = true;
}
if (!found)
vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE);
return 0;
}
static enum vcap_keyfield_set
lan966x_vcap_validate_keyset(struct net_device *dev,
struct vcap_admin *admin,
struct vcap_rule *rule,
struct vcap_keyset_list *kslist,
u16 l3_proto)
{
struct vcap_keyset_list keysetlist = {};
enum vcap_keyfield_set keysets[10] = {};
int lookup;
int err;
if (!kslist || kslist->cnt == 0)
return VCAP_KFS_NO_VALUE;
lookup = lan966x_vcap_cid_to_lookup(rule->vcap_chain_id);
keysetlist.max = ARRAY_SIZE(keysets);
keysetlist.keysets = keysets;
err = lan966x_vcap_is2_get_port_keysets(dev, lookup, &keysetlist,
l3_proto);
if (err)
return VCAP_KFS_NO_VALUE;
/* Check if there is a match and return the match */
for (int i = 0; i < kslist->cnt; ++i)
for (int j = 0; j < keysetlist.cnt; ++j)
if (kslist->keysets[i] == keysets[j])
return kslist->keysets[i];
return VCAP_KFS_NO_VALUE;
}
static bool lan966x_vcap_is_first_chain(struct vcap_rule *rule)
{
return (rule->vcap_chain_id >= LAN966X_VCAP_CID_IS2_L0 &&
rule->vcap_chain_id < LAN966X_VCAP_CID_IS2_L1);
}
static void lan966x_vcap_add_default_fields(struct net_device *dev,
struct vcap_admin *admin,
struct vcap_rule *rule)
{
struct lan966x_port *port = netdev_priv(dev);
vcap_rule_add_key_u32(rule, VCAP_KF_IF_IGR_PORT_MASK, 0,
~BIT(port->chip_port));
if (lan966x_vcap_is_first_chain(rule))
vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS,
VCAP_BIT_1);
else
vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS,
VCAP_BIT_0);
}
static void lan966x_vcap_cache_erase(struct vcap_admin *admin)
{
memset(admin->cache.keystream, 0, STREAMSIZE);
memset(admin->cache.maskstream, 0, STREAMSIZE);
memset(admin->cache.actionstream, 0, STREAMSIZE);
memset(&admin->cache.counter, 0, sizeof(admin->cache.counter));
}
static void lan966x_vcap_cache_write(struct net_device *dev,
struct vcap_admin *admin,
enum vcap_selection sel,
u32 start,
u32 count)
{
struct lan966x_port *port = netdev_priv(dev);
struct lan966x *lan966x = port->lan966x;
u32 *keystr, *mskstr, *actstr;
keystr = &admin->cache.keystream[start];
mskstr = &admin->cache.maskstream[start];
actstr = &admin->cache.actionstream[start];
switch (sel) {
case VCAP_SEL_ENTRY:
for (int i = 0; i < count; ++i) {
lan_wr(keystr[i] & mskstr[i], lan966x,
VCAP_ENTRY_DAT(admin->tgt_inst, i));
lan_wr(~mskstr[i], lan966x,
VCAP_MASK_DAT(admin->tgt_inst, i));
}
break;
case VCAP_SEL_ACTION:
for (int i = 0; i < count; ++i)
lan_wr(actstr[i], lan966x,
VCAP_ACTION_DAT(admin->tgt_inst, i));
break;
case VCAP_SEL_COUNTER:
admin->cache.sticky = admin->cache.counter > 0;
lan_wr(admin->cache.counter, lan966x,
VCAP_CNT_DAT(admin->tgt_inst, 0));
break;
default:
break;
}
}
static void lan966x_vcap_cache_read(struct net_device *dev,
struct vcap_admin *admin,
enum vcap_selection sel,
u32 start,
u32 count)
{
struct lan966x_port *port = netdev_priv(dev);
struct lan966x *lan966x = port->lan966x;
int instance = admin->tgt_inst;
u32 *keystr, *mskstr, *actstr;
keystr = &admin->cache.keystream[start];
mskstr = &admin->cache.maskstream[start];
actstr = &admin->cache.actionstream[start];
if (sel & VCAP_SEL_ENTRY) {
for (int i = 0; i < count; ++i) {
keystr[i] =
lan_rd(lan966x, VCAP_ENTRY_DAT(instance, i));
mskstr[i] =
~lan_rd(lan966x, VCAP_MASK_DAT(instance, i));
}
}
if (sel & VCAP_SEL_ACTION)
for (int i = 0; i < count; ++i)
actstr[i] =
lan_rd(lan966x, VCAP_ACTION_DAT(instance, i));
if (sel & VCAP_SEL_COUNTER) {
admin->cache.counter =
lan_rd(lan966x, VCAP_CNT_DAT(instance, 0));
admin->cache.sticky = admin->cache.counter > 0;
}
}
static void lan966x_vcap_range_init(struct net_device *dev,
struct vcap_admin *admin,
u32 addr,
u32 count)
{
struct lan966x_port *port = netdev_priv(dev);
struct lan966x *lan966x = port->lan966x;
__lan966x_vcap_range_init(lan966x, admin, addr, count);
}
static void lan966x_vcap_update(struct net_device *dev,
struct vcap_admin *admin,
enum vcap_command cmd,
enum vcap_selection sel,
u32 addr)
{
struct lan966x_port *port = netdev_priv(dev);
struct lan966x *lan966x = port->lan966x;
bool clear;
clear = (cmd == VCAP_CMD_INITIALIZE);
lan_wr(VCAP_MV_CFG_MV_NUM_POS_SET(0) |
VCAP_MV_CFG_MV_SIZE_SET(0),
lan966x, VCAP_MV_CFG(admin->tgt_inst));
lan_wr(VCAP_UPDATE_CTRL_UPDATE_CMD_SET(cmd) |
VCAP_UPDATE_CTRL_UPDATE_ENTRY_DIS_SET((VCAP_SEL_ENTRY & sel) == 0) |
VCAP_UPDATE_CTRL_UPDATE_ACTION_DIS_SET((VCAP_SEL_ACTION & sel) == 0) |
VCAP_UPDATE_CTRL_UPDATE_CNT_DIS_SET((VCAP_SEL_COUNTER & sel) == 0) |
VCAP_UPDATE_CTRL_UPDATE_ADDR_SET(addr) |
VCAP_UPDATE_CTRL_CLEAR_CACHE_SET(clear) |
VCAP_UPDATE_CTRL_UPDATE_SHOT,
lan966x, VCAP_UPDATE_CTRL(admin->tgt_inst));
lan966x_vcap_wait_update(lan966x, admin->tgt_inst);
}
static void lan966x_vcap_move(struct net_device *dev,
struct vcap_admin *admin,
u32 addr, int offset, int count)
{
struct lan966x_port *port = netdev_priv(dev);
struct lan966x *lan966x = port->lan966x;
enum vcap_command cmd;
u16 mv_num_pos;
u16 mv_size;
mv_size = count - 1;
if (offset > 0) {
mv_num_pos = offset - 1;
cmd = VCAP_CMD_MOVE_DOWN;
} else {
mv_num_pos = -offset - 1;
cmd = VCAP_CMD_MOVE_UP;
}
lan_wr(VCAP_MV_CFG_MV_NUM_POS_SET(mv_num_pos) |
VCAP_MV_CFG_MV_SIZE_SET(mv_size),
lan966x, VCAP_MV_CFG(admin->tgt_inst));
lan_wr(VCAP_UPDATE_CTRL_UPDATE_CMD_SET(cmd) |
VCAP_UPDATE_CTRL_UPDATE_ENTRY_DIS_SET(0) |
VCAP_UPDATE_CTRL_UPDATE_ACTION_DIS_SET(0) |
VCAP_UPDATE_CTRL_UPDATE_CNT_DIS_SET(0) |
VCAP_UPDATE_CTRL_UPDATE_ADDR_SET(addr) |
VCAP_UPDATE_CTRL_CLEAR_CACHE_SET(false) |
VCAP_UPDATE_CTRL_UPDATE_SHOT,
lan966x, VCAP_UPDATE_CTRL(admin->tgt_inst));
lan966x_vcap_wait_update(lan966x, admin->tgt_inst);
}
static int lan966x_vcap_port_info(struct net_device *dev,
struct vcap_admin *admin,
struct vcap_output_print *out)
{
return 0;
}
static int lan966x_vcap_enable(struct net_device *dev,
struct vcap_admin *admin,
bool enable)
{
struct lan966x_port *port = netdev_priv(dev);
struct lan966x *lan966x = port->lan966x;
lan_rmw(ANA_VCAP_S2_CFG_ENA_SET(enable),
ANA_VCAP_S2_CFG_ENA,
lan966x, ANA_VCAP_S2_CFG(port->chip_port));
return 0;
}
static struct vcap_operations lan966x_vcap_ops = {
.validate_keyset = lan966x_vcap_validate_keyset,
.add_default_fields = lan966x_vcap_add_default_fields,
.cache_erase = lan966x_vcap_cache_erase,
.cache_write = lan966x_vcap_cache_write,
.cache_read = lan966x_vcap_cache_read,
.init = lan966x_vcap_range_init,
.update = lan966x_vcap_update,
.move = lan966x_vcap_move,
.port_info = lan966x_vcap_port_info,
.enable = lan966x_vcap_enable,
};
static void lan966x_vcap_admin_free(struct vcap_admin *admin)
{
if (!admin)
......@@ -98,12 +446,19 @@ lan966x_vcap_admin_alloc(struct lan966x *lan966x, struct vcap_control *ctrl,
mutex_init(&admin->lock);
INIT_LIST_HEAD(&admin->list);
INIT_LIST_HEAD(&admin->rules);
INIT_LIST_HEAD(&admin->enabled);
admin->vtype = cfg->vtype;
admin->vinst = 0;
admin->w32be = true;
admin->tgt_inst = cfg->tgt_inst;
admin->lookups = cfg->lookups;
admin->lookups_per_instance = cfg->lookups;
admin->first_cid = cfg->first_cid;
admin->last_cid = cfg->last_cid;
admin->cache.keystream = kzalloc(STREAMSIZE, GFP_KERNEL);
admin->cache.maskstream = kzalloc(STREAMSIZE, GFP_KERNEL);
admin->cache.actionstream = kzalloc(STREAMSIZE, GFP_KERNEL);
......@@ -135,6 +490,13 @@ static void lan966x_vcap_block_init(struct lan966x *lan966x,
admin->first_valid_addr);
}
static void lan966x_vcap_port_key_deselection(struct lan966x *lan966x,
struct vcap_admin *admin)
{
for (int p = 0; p < lan966x->num_phys_ports; ++p)
lan_wr(0, lan966x, ANA_VCAP_S2_CFG(p));
}
int lan966x_vcap_init(struct lan966x *lan966x)
{
struct lan966x_vcap_inst *cfg;
......@@ -147,6 +509,7 @@ int lan966x_vcap_init(struct lan966x *lan966x)
ctrl->vcaps = lan966x_vcaps;
ctrl->stats = &lan966x_vcap_stats;
ctrl->ops = &lan966x_vcap_ops;
INIT_LIST_HEAD(&ctrl->list);
for (int i = 0; i < ARRAY_SIZE(lan966x_vcap_inst_cfg); ++i) {
......@@ -157,6 +520,8 @@ int lan966x_vcap_init(struct lan966x *lan966x)
return PTR_ERR(admin);
lan966x_vcap_block_init(lan966x, admin, cfg);
lan966x_vcap_port_key_deselection(lan966x, admin);
list_add_tail(&admin->list, &ctrl->list);
}
......@@ -175,6 +540,7 @@ void lan966x_vcap_deinit(struct lan966x *lan966x)
return;
list_for_each_entry_safe(admin, admin_next, &ctrl->list, list) {
lan966x_vcap_port_key_deselection(lan966x, admin);
vcap_del_rules(ctrl, admin);
list_del(&admin->list);
lan966x_vcap_admin_free(admin);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment