Quelle sja1105_vl.c
Sprache: C
// SPDX-License-Identifier: GPL-2.0
/* Copyright 2020 NXP
*/
#include <net/tc_act/tc_gate.h>
#include <linux/dsa/8021 q.h>
#include "sja1105_vl.h"
#define SJA1105_SIZE_VL_STATUS 8
/* Insert into the global gate list, sorted by gate action time. */
static int sja1105_insert_gate_entry(struct sja1105_gating_config *gating_cfg,
struct sja1105_rule *rule,
u8 gate_state, s64 entry_time,
struct netlink_ext_ack *extack)
{
struct sja1105_gate_entry *e;
int rc;
e = kzalloc(sizeof (*e), GFP_KERNEL);
if (!e)
return -ENOMEM;
e->rule = rule;
e->gate_state = gate_state;
e->interval = entry_time;
if (list_empty(&gating_cfg->entries)) {
list_add(&e->list, &gating_cfg->entries);
} else {
struct sja1105_gate_entry *p;
list_for_each_entry(p, &gating_cfg->entries, list) {
if (p->interval == e->interval) {
NL_SET_ERR_MSG_MOD(extack,
"Gate conflict" );
rc = -EBUSY;
goto err;
}
if (e->interval < p->interval)
break ;
}
list_add(&e->list, p->list.prev);
}
gating_cfg->num_entries++;
return 0 ;
err:
kfree(e);
return rc;
}
/* The gate entries contain absolute times in their e->interval field. Convert
* that to proper intervals ( i . e . " 0 , 5 , 10 , 15 " to " 5 , 5 , 5 , 5 " ) .
*/
static void
sja1105_gating_cfg_time_to_interval(struct sja1105_gating_config *gating_cfg,
u64 cycle_time)
{
struct sja1105_gate_entry *last_e;
struct sja1105_gate_entry *e;
struct list_head *prev;
list_for_each_entry(e, &gating_cfg->entries, list) {
struct sja1105_gate_entry *p;
prev = e->list.prev;
if (prev == &gating_cfg->entries)
continue ;
p = list_entry(prev, struct sja1105_gate_entry, list);
p->interval = e->interval - p->interval;
}
last_e = list_last_entry(&gating_cfg->entries,
struct sja1105_gate_entry, list);
last_e->interval = cycle_time - last_e->interval;
}
static void sja1105_free_gating_config(struct sja1105_gating_config *gating_cfg)
{
struct sja1105_gate_entry *e, *n;
list_for_each_entry_safe(e, n, &gating_cfg->entries, list) {
list_del(&e->list);
kfree(e);
}
}
static int sja1105_compose_gating_subschedule(struct sja1105_private *priv,
struct netlink_ext_ack *extack)
{
struct sja1105_gating_config *gating_cfg = &priv->tas_data.gating_cfg;
struct sja1105_rule *rule;
s64 max_cycle_time = 0 ;
s64 its_base_time = 0 ;
int i, rc = 0 ;
sja1105_free_gating_config(gating_cfg);
list_for_each_entry(rule, &priv->flow_block.rules, list) {
if (rule->type != SJA1105_RULE_VL)
continue ;
if (rule->vl.type != SJA1105_VL_TIME_TRIGGERED)
continue ;
if (max_cycle_time < rule->vl.cycle_time) {
max_cycle_time = rule->vl.cycle_time;
its_base_time = rule->vl.base_time;
}
}
if (!max_cycle_time)
return 0 ;
dev_dbg(priv->ds->dev, "max_cycle_time %lld its_base_time %lld\n" ,
max_cycle_time, its_base_time);
gating_cfg->base_time = its_base_time;
gating_cfg->cycle_time = max_cycle_time;
gating_cfg->num_entries = 0 ;
list_for_each_entry(rule, &priv->flow_block.rules, list) {
s64 time;
s64 rbt;
if (rule->type != SJA1105_RULE_VL)
continue ;
if (rule->vl.type != SJA1105_VL_TIME_TRIGGERED)
continue ;
/* Calculate the difference between this gating schedule's
* base time , and the base time of the gating schedule with the
* longest cycle time . We call it the relative base time ( rbt ) .
*/
rbt = future_base_time(rule->vl.base_time, rule->vl.cycle_time,
its_base_time);
rbt -= its_base_time;
time = rbt;
for (i = 0 ; i < rule->vl.num_entries; i++) {
u8 gate_state = rule->vl.entries[i].gate_state;
s64 entry_time = time;
while (entry_time < max_cycle_time) {
rc = sja1105_insert_gate_entry(gating_cfg, rule,
gate_state,
entry_time,
extack);
if (rc)
goto err;
entry_time += rule->vl.cycle_time;
}
time += rule->vl.entries[i].interval;
}
}
sja1105_gating_cfg_time_to_interval(gating_cfg, max_cycle_time);
return 0 ;
err:
sja1105_free_gating_config(gating_cfg);
return rc;
}
/* The switch flow classification core implements TTEthernet, which 'thinks' in
* terms of Virtual Links ( VL ) , a concept borrowed from ARINC 664 part 7 .
* However it also has one other operating mode ( VLLUPFORMAT = 0 ) where it acts
* somewhat closer to a pre - standard implementation of IEEE 802 . 1 Qci
* ( Per - Stream Filtering and Policing ) , which is what the driver is going to be
* implementing .
*
* VL Lookup
* Key = { DMAC & & VLANID + - - - - - - - - - + Key = { ( DMAC [ 47 : 16 ] & VLMASK = =
* & & VLAN PCP | | VLMARKER )
* & & INGRESS PORT } + - - - - - - - - - + ( both fixed )
* ( exact match , | & & DMAC [ 15 : 0 ] = = VLID
* all specified in rule ) | ( specified in rule )
* v & & INGRESS PORT }
* - - - - - - - - - - - -
* 0 ( PSFP ) / \ 1 ( ARINC664 )
* + - - - - - - - - - - - / VLLUPFORMAT \ - - - - - - - - - - +
* | \ ( fixed ) / |
* | \ / |
* 0 ( forwarding ) v - - - - - - - - - - - - |
* - - - - - - - - - - - - |
* / \ 1 ( QoS classification ) |
* + - - - / ISCRITICAL \ - - - - - - - - - - - + |
* | \ ( per rule ) / | |
* | \ / VLID taken from VLID taken from
* v - - - - - - - - - - - - index of rule contents of rule
* select that matched that matched
* DESTPORTS | |
* | + - - - - - - - - - + - - - - - - - - +
* | |
* | v
* | VL Forwarding
* | ( indexed by VLID )
* | + - - - - - - - - - +
* | + - - - - - - - - - - - - - - | |
* | | select TYPE + - - - - - - - - - +
* | v
* | 0 ( rate - - - - - - - - - - - - 1 ( time
* | constrained ) / \ triggered )
* | + - - - - - - / TYPE \ - - - - - - - - - - - - +
* | | \ ( per VLID ) / |
* | v \ / v
* | VL Policing - - - - - - - - - - - - VL Policing
* | ( indexed by VLID ) ( indexed by VLID )
* | + - - - - - - - - - + + - - - - - - - - - +
* | | TYPE = 0 | | TYPE = 1 |
* | + - - - - - - - - - + + - - - - - - - - - +
* | select SHARINDX select SHARINDX to
* | to rate - limit re - enter VL Forwarding
* | groups of VL ' s with new VLID for egress
* | to same quota |
* | | |
* | select MAXLEN - > exceed = > drop select MAXLEN - > exceed = > drop
* | | |
* | v v
* | VL Forwarding VL Forwarding
* | ( indexed by SHARINDX ) ( indexed by SHARINDX )
* | + - - - - - - - - - + + - - - - - - - - - +
* | | TYPE = 0 | | TYPE = 1 |
* | + - - - - - - - - - + + - - - - - - - - - +
* | select PRIORITY , select PRIORITY ,
* | PARTITION , DESTPORTS PARTITION , DESTPORTS
* | | |
* | v v
* | VL Policing VL Policing
* | ( indexed by SHARINDX ) ( indexed by SHARINDX )
* | + - - - - - - - - - + + - - - - - - - - - +
* | | TYPE = 0 | | TYPE = 1 |
* | + - - - - - - - - - + + - - - - - - - - - +
* | | |
* | v |
* | select BAG , - > exceed = > drop |
* | JITTER v
* | | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* | | / Reception Window is open for this VL \
* | | / ( the Schedule Table executes an entry i \
* | | / M < = i < N , for which these conditions hold ) : \ no
* | | + - - - - / \ - +
* | | | yes \ WINST [ M ] = = 1 & & WINSTINDEX [ M ] = = VLID / |
* | | | \ WINEND [ N ] = = 1 & & WINSTINDEX [ N ] = = VLID / |
* | | | \ / |
* | | | \ ( the VL window has opened and not yet closed ) / |
* | | | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
* | | v v
* | | dispatch to DESTPORTS when the Schedule Table drop
* | | executes an entry i with TXEN = = 1 & & VLINDEX = = i
* v v
* dispatch immediately to DESTPORTS
*
* The per - port classification key is always composed of { DMAC , VID , PCP } and
* is non - maskable . This ' looks like ' the NULL stream identification function
* from IEEE 802 . 1 CB clause 6 , except for the extra VLAN PCP . When the switch
* ports operate as VLAN - unaware , we do allow the user to not specify the VLAN
* ID and PCP , and then the port - based defaults will be used .
*
* In TTEthernet , routing is something that needs to be done manually for each
* Virtual Link . So the flow action must always include one of :
* a . ' redirect ' , ' trap ' or ' drop ' : select the egress port list
* Additionally , the following actions may be applied on a Virtual Link ,
* turning it into ' critical ' traffic :
* b . ' police ' : turn it into a rate - constrained VL , with bandwidth limitation
* given by the maximum frame length , bandwidth allocation gap ( BAG ) and
* maximum jitter .
* c . ' gate ' : turn it into a time - triggered VL , which can be only be received
* and forwarded according to a given schedule .
*/
static bool sja1105_vl_key_lower(struct sja1105_vl_lookup_entry *a,
struct sja1105_vl_lookup_entry *b)
{
if (a->macaddr < b->macaddr)
return true ;
if (a->macaddr > b->macaddr)
return false ;
if (a->vlanid < b->vlanid)
return true ;
if (a->vlanid > b->vlanid)
return false ;
if (a->port < b->port)
return true ;
if (a->port > b->port)
return false ;
if (a->vlanprior < b->vlanprior)
return true ;
if (a->vlanprior > b->vlanprior)
return false ;
/* Keys are equal */
return false ;
}
/* FIXME: this should change when the bridge upper of the port changes. */
static u16 sja1105_port_get_tag_8021q_vid(struct dsa_port *dp)
{
unsigned long bridge_num;
if (!dp->bridge)
return dsa_tag_8021q_standalone_vid(dp);
bridge_num = dsa_port_bridge_num_get(dp);
return dsa_tag_8021q_bridge_vid(bridge_num);
}
static int sja1105_init_virtual_links(struct sja1105_private *priv,
struct netlink_ext_ack *extack)
{
struct sja1105_vl_policing_entry *vl_policing;
struct sja1105_vl_forwarding_entry *vl_fwd;
struct sja1105_vl_lookup_entry *vl_lookup;
bool have_critical_virtual_links = false ;
struct sja1105_table *table;
struct sja1105_rule *rule;
int num_virtual_links = 0 ;
int max_sharindx = 0 ;
int i, j, k;
/* Figure out the dimensioning of the problem */
list_for_each_entry(rule, &priv->flow_block.rules, list) {
if (rule->type != SJA1105_RULE_VL)
continue ;
/* Each VL lookup entry matches on a single ingress port */
num_virtual_links += hweight_long(rule->port_mask);
if (rule->vl.type != SJA1105_VL_NONCRITICAL)
have_critical_virtual_links = true ;
if (max_sharindx < rule->vl.sharindx)
max_sharindx = rule->vl.sharindx;
}
if (num_virtual_links > SJA1105_MAX_VL_LOOKUP_COUNT) {
NL_SET_ERR_MSG_MOD(extack, "Not enough VL entries available" );
return -ENOSPC;
}
if (max_sharindx + 1 > SJA1105_MAX_VL_LOOKUP_COUNT) {
NL_SET_ERR_MSG_MOD(extack, "Policer index out of range" );
return -ENOSPC;
}
max_sharindx = max_t(int , num_virtual_links, max_sharindx) + 1 ;
/* Discard previous VL Lookup Table */
table = &priv->static_config.tables[BLK_IDX_VL_LOOKUP];
if (table->entry_count) {
kfree(table->entries);
table->entry_count = 0 ;
}
/* Discard previous VL Policing Table */
table = &priv->static_config.tables[BLK_IDX_VL_POLICING];
if (table->entry_count) {
kfree(table->entries);
table->entry_count = 0 ;
}
/* Discard previous VL Forwarding Table */
table = &priv->static_config.tables[BLK_IDX_VL_FORWARDING];
if (table->entry_count) {
kfree(table->entries);
table->entry_count = 0 ;
}
/* Discard previous VL Forwarding Parameters Table */
table = &priv->static_config.tables[BLK_IDX_VL_FORWARDING_PARAMS];
if (table->entry_count) {
kfree(table->entries);
table->entry_count = 0 ;
}
/* Nothing to do */
if (!num_virtual_links)
return 0 ;
/* Pre-allocate space in the static config tables */
/* VL Lookup Table */
table = &priv->static_config.tables[BLK_IDX_VL_LOOKUP];
table->entries = kcalloc(num_virtual_links,
table->ops->unpacked_entry_size,
GFP_KERNEL);
if (!table->entries)
return -ENOMEM;
table->entry_count = num_virtual_links;
vl_lookup = table->entries;
k = 0 ;
list_for_each_entry(rule, &priv->flow_block.rules, list) {
unsigned long port;
if (rule->type != SJA1105_RULE_VL)
continue ;
for_each_set_bit(port, &rule->port_mask, SJA1105_MAX_NUM_PORTS) {
vl_lookup[k].format = SJA1105_VL_FORMAT_PSFP;
vl_lookup[k].port = port;
vl_lookup[k].macaddr = rule->key.vl.dmac;
if (rule->key.type == SJA1105_KEY_VLAN_AWARE_VL) {
vl_lookup[k].vlanid = rule->key.vl.vid;
vl_lookup[k].vlanprior = rule->key.vl.pcp;
} else {
/* FIXME */
struct dsa_port *dp = dsa_to_port(priv->ds, port);
u16 vid = sja1105_port_get_tag_8021q_vid(dp);
vl_lookup[k].vlanid = vid;
vl_lookup[k].vlanprior = 0 ;
}
/* For critical VLs, the DESTPORTS mask is taken from
* the VL Forwarding Table , so no point in putting it
* in the VL Lookup Table
*/
if (rule->vl.type == SJA1105_VL_NONCRITICAL)
vl_lookup[k].destports = rule->vl.destports;
else
vl_lookup[k].iscritical = true ;
vl_lookup[k].flow_cookie = rule->cookie;
k++;
}
}
/* UM10944.pdf chapter 4.2.3 VL Lookup table:
* " the entries in the VL Lookup table must be sorted in ascending
* order ( i . e . the smallest value must be loaded first ) according to
* the following sort order : MACADDR , VLANID , PORT , VLANPRIOR . "
*/
for (i = 0 ; i < num_virtual_links; i++) {
struct sja1105_vl_lookup_entry *a = &vl_lookup[i];
for (j = i + 1 ; j < num_virtual_links; j++) {
struct sja1105_vl_lookup_entry *b = &vl_lookup[j];
if (sja1105_vl_key_lower(b, a)) {
struct sja1105_vl_lookup_entry tmp = *a;
*a = *b;
*b = tmp;
}
}
}
if (!have_critical_virtual_links)
return 0 ;
/* VL Policing Table */
table = &priv->static_config.tables[BLK_IDX_VL_POLICING];
table->entries = kcalloc(max_sharindx, table->ops->unpacked_entry_size,
GFP_KERNEL);
if (!table->entries)
return -ENOMEM;
table->entry_count = max_sharindx;
vl_policing = table->entries;
/* VL Forwarding Table */
table = &priv->static_config.tables[BLK_IDX_VL_FORWARDING];
table->entries = kcalloc(max_sharindx, table->ops->unpacked_entry_size,
GFP_KERNEL);
if (!table->entries)
return -ENOMEM;
table->entry_count = max_sharindx;
vl_fwd = table->entries;
/* VL Forwarding Parameters Table */
table = &priv->static_config.tables[BLK_IDX_VL_FORWARDING_PARAMS];
table->entries = kcalloc(1 , table->ops->unpacked_entry_size,
GFP_KERNEL);
if (!table->entries)
return -ENOMEM;
table->entry_count = 1 ;
for (i = 0 ; i < num_virtual_links; i++) {
unsigned long cookie = vl_lookup[i].flow_cookie;
struct sja1105_rule *rule = sja1105_rule_find(priv, cookie);
if (rule->vl.type == SJA1105_VL_NONCRITICAL)
continue ;
if (rule->vl.type == SJA1105_VL_TIME_TRIGGERED) {
int sharindx = rule->vl.sharindx;
vl_policing[i].type = 1 ;
vl_policing[i].sharindx = sharindx;
vl_policing[i].maxlen = rule->vl.maxlen;
vl_policing[sharindx].type = 1 ;
vl_fwd[i].type = 1 ;
vl_fwd[sharindx].type = 1 ;
vl_fwd[sharindx].priority = rule->vl.ipv;
vl_fwd[sharindx].partition = 0 ;
vl_fwd[sharindx].destports = rule->vl.destports;
}
}
sja1105_frame_memory_partitioning(priv);
return 0 ;
}
int sja1105_vl_redirect(struct sja1105_private *priv, int port,
struct netlink_ext_ack *extack, unsigned long cookie,
struct sja1105_key *key, unsigned long destports,
bool append)
{
struct sja1105_rule *rule = sja1105_rule_find(priv, cookie);
struct dsa_port *dp = dsa_to_port(priv->ds, port);
bool vlan_aware = dsa_port_is_vlan_filtering(dp);
int rc;
if (!vlan_aware && key->type != SJA1105_KEY_VLAN_UNAWARE_VL) {
NL_SET_ERR_MSG_MOD(extack,
"Can only redirect based on DMAC" );
return -EOPNOTSUPP;
} else if (vlan_aware && key->type != SJA1105_KEY_VLAN_AWARE_VL) {
NL_SET_ERR_MSG_MOD(extack,
"Can only redirect based on {DMAC, VID, PCP}" );
return -EOPNOTSUPP;
}
if (!rule) {
rule = kzalloc(sizeof (*rule), GFP_KERNEL);
if (!rule)
return -ENOMEM;
rule->cookie = cookie;
rule->type = SJA1105_RULE_VL;
rule->key = *key;
list_add(&rule->list, &priv->flow_block.rules);
}
rule->port_mask |= BIT(port);
if (append)
rule->vl.destports |= destports;
else
rule->vl.destports = destports;
rc = sja1105_init_virtual_links(priv, extack);
if (rc) {
rule->port_mask &= ~BIT(port);
if (!rule->port_mask) {
list_del(&rule->list);
kfree(rule);
}
}
return rc;
}
int sja1105_vl_delete(struct sja1105_private *priv, int port,
struct sja1105_rule *rule, struct netlink_ext_ack *extack)
{
int rc;
rule->port_mask &= ~BIT(port);
if (!rule->port_mask) {
list_del(&rule->list);
kfree(rule);
}
rc = sja1105_compose_gating_subschedule(priv, extack);
if (rc)
return rc;
rc = sja1105_init_virtual_links(priv, extack);
if (rc)
return rc;
rc = sja1105_init_scheduling(priv);
if (rc < 0 )
return rc;
return sja1105_static_config_reload(priv, SJA1105_VIRTUAL_LINKS);
}
int sja1105_vl_gate(struct sja1105_private *priv, int port,
struct netlink_ext_ack *extack, unsigned long cookie,
struct sja1105_key *key, u32 index, s32 prio,
u64 base_time, u64 cycle_time, u64 cycle_time_ext,
u32 num_entries, struct action_gate_entry *entries)
{
struct sja1105_rule *rule = sja1105_rule_find(priv, cookie);
struct dsa_port *dp = dsa_to_port(priv->ds, port);
bool vlan_aware = dsa_port_is_vlan_filtering(dp);
int ipv = -1 ;
int i, rc;
s32 rem;
if (cycle_time_ext) {
NL_SET_ERR_MSG_MOD(extack,
"Cycle time extension not supported" );
return -EOPNOTSUPP;
}
div_s64_rem(base_time, sja1105_delta_to_ns(1 ), &rem);
if (rem) {
NL_SET_ERR_MSG_MOD(extack,
"Base time must be multiple of 200 ns" );
return -ERANGE;
}
div_s64_rem(cycle_time, sja1105_delta_to_ns(1 ), &rem);
if (rem) {
NL_SET_ERR_MSG_MOD(extack,
"Cycle time must be multiple of 200 ns" );
return -ERANGE;
}
if (!vlan_aware && key->type != SJA1105_KEY_VLAN_UNAWARE_VL) {
NL_SET_ERR_MSG_MOD(extack,
"Can only gate based on DMAC" );
return -EOPNOTSUPP;
} else if (vlan_aware && key->type != SJA1105_KEY_VLAN_AWARE_VL) {
NL_SET_ERR_MSG_MOD(extack,
"Can only gate based on {DMAC, VID, PCP}" );
return -EOPNOTSUPP;
}
if (!rule) {
rule = kzalloc(sizeof (*rule), GFP_KERNEL);
if (!rule)
return -ENOMEM;
list_add(&rule->list, &priv->flow_block.rules);
rule->cookie = cookie;
rule->type = SJA1105_RULE_VL;
rule->key = *key;
rule->vl.type = SJA1105_VL_TIME_TRIGGERED;
rule->vl.sharindx = index;
rule->vl.base_time = base_time;
rule->vl.cycle_time = cycle_time;
rule->vl.num_entries = num_entries;
rule->vl.entries = kcalloc(num_entries,
sizeof (struct action_gate_entry),
GFP_KERNEL);
if (!rule->vl.entries) {
rc = -ENOMEM;
goto out;
}
for (i = 0 ; i < num_entries; i++) {
div_s64_rem(entries[i].interval,
sja1105_delta_to_ns(1 ), &rem);
if (rem) {
NL_SET_ERR_MSG_MOD(extack,
"Interval must be multiple of 200 ns" );
rc = -ERANGE;
goto out;
}
if (!entries[i].interval) {
NL_SET_ERR_MSG_MOD(extack,
"Interval cannot be zero" );
rc = -ERANGE;
goto out;
}
if (ns_to_sja1105_delta(entries[i].interval) >
SJA1105_TAS_MAX_DELTA) {
NL_SET_ERR_MSG_MOD(extack,
"Maximum interval is 52 ms" );
rc = -ERANGE;
goto out;
}
if (entries[i].maxoctets != -1 ) {
NL_SET_ERR_MSG_MOD(extack,
"Cannot offload IntervalOctetMax" );
rc = -EOPNOTSUPP;
goto out;
}
if (ipv == -1 ) {
ipv = entries[i].ipv;
} else if (ipv != entries[i].ipv) {
NL_SET_ERR_MSG_MOD(extack,
"Only support a single IPV per VL" );
rc = -EOPNOTSUPP;
goto out;
}
rule->vl.entries[i] = entries[i];
}
if (ipv == -1 ) {
if (key->type == SJA1105_KEY_VLAN_AWARE_VL)
ipv = key->vl.pcp;
else
ipv = 0 ;
}
/* TODO: support per-flow MTU */
rule->vl.maxlen = VLAN_ETH_FRAME_LEN + ETH_FCS_LEN;
rule->vl.ipv = ipv;
}
rule->port_mask |= BIT(port);
rc = sja1105_compose_gating_subschedule(priv, extack);
if (rc)
goto out;
rc = sja1105_init_virtual_links(priv, extack);
if (rc)
goto out;
if (sja1105_gating_check_conflicts(priv, -1 , extack)) {
NL_SET_ERR_MSG_MOD(extack, "Conflict with tc-taprio schedule" );
rc = -ERANGE;
goto out;
}
out:
if (rc) {
rule->port_mask &= ~BIT(port);
if (!rule->port_mask) {
list_del(&rule->list);
kfree(rule->vl.entries);
kfree(rule);
}
}
return rc;
}
static int sja1105_find_vlid(struct sja1105_private *priv, int port,
struct sja1105_key *key)
{
struct sja1105_vl_lookup_entry *vl_lookup;
struct sja1105_table *table;
int i;
if (WARN_ON(key->type != SJA1105_KEY_VLAN_AWARE_VL &&
key->type != SJA1105_KEY_VLAN_UNAWARE_VL))
return -1 ;
table = &priv->static_config.tables[BLK_IDX_VL_LOOKUP];
vl_lookup = table->entries;
for (i = 0 ; i < table->entry_count; i++) {
if (key->type == SJA1105_KEY_VLAN_AWARE_VL) {
if (vl_lookup[i].port == port &&
vl_lookup[i].macaddr == key->vl.dmac &&
vl_lookup[i].vlanid == key->vl.vid &&
vl_lookup[i].vlanprior == key->vl.pcp)
return i;
} else {
if (vl_lookup[i].port == port &&
vl_lookup[i].macaddr == key->vl.dmac)
return i;
}
}
return -1 ;
}
int sja1105_vl_stats(struct sja1105_private *priv, int port,
struct sja1105_rule *rule, struct flow_stats *stats,
struct netlink_ext_ack *extack)
{
const struct sja1105_regs *regs = priv->info->regs;
u8 buf[SJA1105_SIZE_VL_STATUS] = {0 };
u64 unreleased;
u64 timingerr;
u64 lengtherr;
int vlid, rc;
u64 pkts;
if (rule->vl.type != SJA1105_VL_TIME_TRIGGERED)
return 0 ;
vlid = sja1105_find_vlid(priv, port, &rule->key);
if (vlid < 0 )
return 0 ;
rc = sja1105_xfer_buf(priv, SPI_READ, regs->vl_status + 2 * vlid, buf,
SJA1105_SIZE_VL_STATUS);
if (rc) {
NL_SET_ERR_MSG_MOD(extack, "SPI access failed" );
return rc;
}
sja1105_unpack(buf, &timingerr, 31 , 16 , SJA1105_SIZE_VL_STATUS);
sja1105_unpack(buf, &unreleased, 15 , 0 , SJA1105_SIZE_VL_STATUS);
sja1105_unpack(buf, &lengtherr, 47 , 32 , SJA1105_SIZE_VL_STATUS);
pkts = timingerr + unreleased + lengtherr;
flow_stats_update(stats, 0 , pkts - rule->vl.stats.pkts, 0 ,
jiffies - rule->vl.stats.lastused,
FLOW_ACTION_HW_STATS_IMMEDIATE);
rule->vl.stats.pkts = pkts;
rule->vl.stats.lastused = jiffies;
return 0 ;
}
Messung V0.5 in Prozent C=98 H=87 G=92
¤ Dauer der Verarbeitung: 0.14 Sekunden
¤
*© Formatika GbR, Deutschland
2026-10-11
Neuigkeiten
Aktuelles
Motto des Tages
Open Source Software
Jenseits des Üblichen ....
Besucherstatistik
Statistik
#Sources=1127926
#Domains=2039723