1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /* Copyright 2020, NXP Semiconductors
3*4882a593Smuzhiyun */
4*4882a593Smuzhiyun #include <net/tc_act/tc_gate.h>
5*4882a593Smuzhiyun #include <linux/dsa/8021q.h>
6*4882a593Smuzhiyun #include "sja1105_vl.h"
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun #define SJA1105_SIZE_VL_STATUS 8
9*4882a593Smuzhiyun
10*4882a593Smuzhiyun /* Insert into the global gate list, sorted by gate action time. */
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)11*4882a593Smuzhiyun static int sja1105_insert_gate_entry(struct sja1105_gating_config *gating_cfg,
12*4882a593Smuzhiyun struct sja1105_rule *rule,
13*4882a593Smuzhiyun u8 gate_state, s64 entry_time,
14*4882a593Smuzhiyun struct netlink_ext_ack *extack)
15*4882a593Smuzhiyun {
16*4882a593Smuzhiyun struct sja1105_gate_entry *e;
17*4882a593Smuzhiyun int rc;
18*4882a593Smuzhiyun
19*4882a593Smuzhiyun e = kzalloc(sizeof(*e), GFP_KERNEL);
20*4882a593Smuzhiyun if (!e)
21*4882a593Smuzhiyun return -ENOMEM;
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun e->rule = rule;
24*4882a593Smuzhiyun e->gate_state = gate_state;
25*4882a593Smuzhiyun e->interval = entry_time;
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun if (list_empty(&gating_cfg->entries)) {
28*4882a593Smuzhiyun list_add(&e->list, &gating_cfg->entries);
29*4882a593Smuzhiyun } else {
30*4882a593Smuzhiyun struct sja1105_gate_entry *p;
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun list_for_each_entry(p, &gating_cfg->entries, list) {
33*4882a593Smuzhiyun if (p->interval == e->interval) {
34*4882a593Smuzhiyun NL_SET_ERR_MSG_MOD(extack,
35*4882a593Smuzhiyun "Gate conflict");
36*4882a593Smuzhiyun rc = -EBUSY;
37*4882a593Smuzhiyun goto err;
38*4882a593Smuzhiyun }
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun if (e->interval < p->interval)
41*4882a593Smuzhiyun break;
42*4882a593Smuzhiyun }
43*4882a593Smuzhiyun list_add(&e->list, p->list.prev);
44*4882a593Smuzhiyun }
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun gating_cfg->num_entries++;
47*4882a593Smuzhiyun
48*4882a593Smuzhiyun return 0;
49*4882a593Smuzhiyun err:
50*4882a593Smuzhiyun kfree(e);
51*4882a593Smuzhiyun return rc;
52*4882a593Smuzhiyun }
53*4882a593Smuzhiyun
54*4882a593Smuzhiyun /* The gate entries contain absolute times in their e->interval field. Convert
55*4882a593Smuzhiyun * that to proper intervals (i.e. "0, 5, 10, 15" to "5, 5, 5, 5").
56*4882a593Smuzhiyun */
57*4882a593Smuzhiyun static void
sja1105_gating_cfg_time_to_interval(struct sja1105_gating_config * gating_cfg,u64 cycle_time)58*4882a593Smuzhiyun sja1105_gating_cfg_time_to_interval(struct sja1105_gating_config *gating_cfg,
59*4882a593Smuzhiyun u64 cycle_time)
60*4882a593Smuzhiyun {
61*4882a593Smuzhiyun struct sja1105_gate_entry *last_e;
62*4882a593Smuzhiyun struct sja1105_gate_entry *e;
63*4882a593Smuzhiyun struct list_head *prev;
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun list_for_each_entry(e, &gating_cfg->entries, list) {
66*4882a593Smuzhiyun struct sja1105_gate_entry *p;
67*4882a593Smuzhiyun
68*4882a593Smuzhiyun prev = e->list.prev;
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun if (prev == &gating_cfg->entries)
71*4882a593Smuzhiyun continue;
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun p = list_entry(prev, struct sja1105_gate_entry, list);
74*4882a593Smuzhiyun p->interval = e->interval - p->interval;
75*4882a593Smuzhiyun }
76*4882a593Smuzhiyun last_e = list_last_entry(&gating_cfg->entries,
77*4882a593Smuzhiyun struct sja1105_gate_entry, list);
78*4882a593Smuzhiyun last_e->interval = cycle_time - last_e->interval;
79*4882a593Smuzhiyun }
80*4882a593Smuzhiyun
sja1105_free_gating_config(struct sja1105_gating_config * gating_cfg)81*4882a593Smuzhiyun static void sja1105_free_gating_config(struct sja1105_gating_config *gating_cfg)
82*4882a593Smuzhiyun {
83*4882a593Smuzhiyun struct sja1105_gate_entry *e, *n;
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun list_for_each_entry_safe(e, n, &gating_cfg->entries, list) {
86*4882a593Smuzhiyun list_del(&e->list);
87*4882a593Smuzhiyun kfree(e);
88*4882a593Smuzhiyun }
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun
sja1105_compose_gating_subschedule(struct sja1105_private * priv,struct netlink_ext_ack * extack)91*4882a593Smuzhiyun static int sja1105_compose_gating_subschedule(struct sja1105_private *priv,
92*4882a593Smuzhiyun struct netlink_ext_ack *extack)
93*4882a593Smuzhiyun {
94*4882a593Smuzhiyun struct sja1105_gating_config *gating_cfg = &priv->tas_data.gating_cfg;
95*4882a593Smuzhiyun struct sja1105_rule *rule;
96*4882a593Smuzhiyun s64 max_cycle_time = 0;
97*4882a593Smuzhiyun s64 its_base_time = 0;
98*4882a593Smuzhiyun int i, rc = 0;
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun sja1105_free_gating_config(gating_cfg);
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun list_for_each_entry(rule, &priv->flow_block.rules, list) {
103*4882a593Smuzhiyun if (rule->type != SJA1105_RULE_VL)
104*4882a593Smuzhiyun continue;
105*4882a593Smuzhiyun if (rule->vl.type != SJA1105_VL_TIME_TRIGGERED)
106*4882a593Smuzhiyun continue;
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun if (max_cycle_time < rule->vl.cycle_time) {
109*4882a593Smuzhiyun max_cycle_time = rule->vl.cycle_time;
110*4882a593Smuzhiyun its_base_time = rule->vl.base_time;
111*4882a593Smuzhiyun }
112*4882a593Smuzhiyun }
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun if (!max_cycle_time)
115*4882a593Smuzhiyun return 0;
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun dev_dbg(priv->ds->dev, "max_cycle_time %lld its_base_time %lld\n",
118*4882a593Smuzhiyun max_cycle_time, its_base_time);
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun gating_cfg->base_time = its_base_time;
121*4882a593Smuzhiyun gating_cfg->cycle_time = max_cycle_time;
122*4882a593Smuzhiyun gating_cfg->num_entries = 0;
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun list_for_each_entry(rule, &priv->flow_block.rules, list) {
125*4882a593Smuzhiyun s64 time;
126*4882a593Smuzhiyun s64 rbt;
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun if (rule->type != SJA1105_RULE_VL)
129*4882a593Smuzhiyun continue;
130*4882a593Smuzhiyun if (rule->vl.type != SJA1105_VL_TIME_TRIGGERED)
131*4882a593Smuzhiyun continue;
132*4882a593Smuzhiyun
133*4882a593Smuzhiyun /* Calculate the difference between this gating schedule's
134*4882a593Smuzhiyun * base time, and the base time of the gating schedule with the
135*4882a593Smuzhiyun * longest cycle time. We call it the relative base time (rbt).
136*4882a593Smuzhiyun */
137*4882a593Smuzhiyun rbt = future_base_time(rule->vl.base_time, rule->vl.cycle_time,
138*4882a593Smuzhiyun its_base_time);
139*4882a593Smuzhiyun rbt -= its_base_time;
140*4882a593Smuzhiyun
141*4882a593Smuzhiyun time = rbt;
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun for (i = 0; i < rule->vl.num_entries; i++) {
144*4882a593Smuzhiyun u8 gate_state = rule->vl.entries[i].gate_state;
145*4882a593Smuzhiyun s64 entry_time = time;
146*4882a593Smuzhiyun
147*4882a593Smuzhiyun while (entry_time < max_cycle_time) {
148*4882a593Smuzhiyun rc = sja1105_insert_gate_entry(gating_cfg, rule,
149*4882a593Smuzhiyun gate_state,
150*4882a593Smuzhiyun entry_time,
151*4882a593Smuzhiyun extack);
152*4882a593Smuzhiyun if (rc)
153*4882a593Smuzhiyun goto err;
154*4882a593Smuzhiyun
155*4882a593Smuzhiyun entry_time += rule->vl.cycle_time;
156*4882a593Smuzhiyun }
157*4882a593Smuzhiyun time += rule->vl.entries[i].interval;
158*4882a593Smuzhiyun }
159*4882a593Smuzhiyun }
160*4882a593Smuzhiyun
161*4882a593Smuzhiyun sja1105_gating_cfg_time_to_interval(gating_cfg, max_cycle_time);
162*4882a593Smuzhiyun
163*4882a593Smuzhiyun return 0;
164*4882a593Smuzhiyun err:
165*4882a593Smuzhiyun sja1105_free_gating_config(gating_cfg);
166*4882a593Smuzhiyun return rc;
167*4882a593Smuzhiyun }
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun /* The switch flow classification core implements TTEthernet, which 'thinks' in
170*4882a593Smuzhiyun * terms of Virtual Links (VL), a concept borrowed from ARINC 664 part 7.
171*4882a593Smuzhiyun * However it also has one other operating mode (VLLUPFORMAT=0) where it acts
172*4882a593Smuzhiyun * somewhat closer to a pre-standard implementation of IEEE 802.1Qci
173*4882a593Smuzhiyun * (Per-Stream Filtering and Policing), which is what the driver is going to be
174*4882a593Smuzhiyun * implementing.
175*4882a593Smuzhiyun *
176*4882a593Smuzhiyun * VL Lookup
177*4882a593Smuzhiyun * Key = {DMAC && VLANID +---------+ Key = { (DMAC[47:16] & VLMASK ==
178*4882a593Smuzhiyun * && VLAN PCP | | VLMARKER)
179*4882a593Smuzhiyun * && INGRESS PORT} +---------+ (both fixed)
180*4882a593Smuzhiyun * (exact match, | && DMAC[15:0] == VLID
181*4882a593Smuzhiyun * all specified in rule) | (specified in rule)
182*4882a593Smuzhiyun * v && INGRESS PORT }
183*4882a593Smuzhiyun * ------------
184*4882a593Smuzhiyun * 0 (PSFP) / \ 1 (ARINC664)
185*4882a593Smuzhiyun * +-----------/ VLLUPFORMAT \----------+
186*4882a593Smuzhiyun * | \ (fixed) / |
187*4882a593Smuzhiyun * | \ / |
188*4882a593Smuzhiyun * 0 (forwarding) v ------------ |
189*4882a593Smuzhiyun * ------------ |
190*4882a593Smuzhiyun * / \ 1 (QoS classification) |
191*4882a593Smuzhiyun * +---/ ISCRITICAL \-----------+ |
192*4882a593Smuzhiyun * | \ (per rule) / | |
193*4882a593Smuzhiyun * | \ / VLID taken from VLID taken from
194*4882a593Smuzhiyun * v ------------ index of rule contents of rule
195*4882a593Smuzhiyun * select that matched that matched
196*4882a593Smuzhiyun * DESTPORTS | |
197*4882a593Smuzhiyun * | +---------+--------+
198*4882a593Smuzhiyun * | |
199*4882a593Smuzhiyun * | v
200*4882a593Smuzhiyun * | VL Forwarding
201*4882a593Smuzhiyun * | (indexed by VLID)
202*4882a593Smuzhiyun * | +---------+
203*4882a593Smuzhiyun * | +--------------| |
204*4882a593Smuzhiyun * | | select TYPE +---------+
205*4882a593Smuzhiyun * | v
206*4882a593Smuzhiyun * | 0 (rate ------------ 1 (time
207*4882a593Smuzhiyun * | constrained) / \ triggered)
208*4882a593Smuzhiyun * | +------/ TYPE \------------+
209*4882a593Smuzhiyun * | | \ (per VLID) / |
210*4882a593Smuzhiyun * | v \ / v
211*4882a593Smuzhiyun * | VL Policing ------------ VL Policing
212*4882a593Smuzhiyun * | (indexed by VLID) (indexed by VLID)
213*4882a593Smuzhiyun * | +---------+ +---------+
214*4882a593Smuzhiyun * | | TYPE=0 | | TYPE=1 |
215*4882a593Smuzhiyun * | +---------+ +---------+
216*4882a593Smuzhiyun * | select SHARINDX select SHARINDX to
217*4882a593Smuzhiyun * | to rate-limit re-enter VL Forwarding
218*4882a593Smuzhiyun * | groups of VL's with new VLID for egress
219*4882a593Smuzhiyun * | to same quota |
220*4882a593Smuzhiyun * | | |
221*4882a593Smuzhiyun * | select MAXLEN -> exceed => drop select MAXLEN -> exceed => drop
222*4882a593Smuzhiyun * | | |
223*4882a593Smuzhiyun * | v v
224*4882a593Smuzhiyun * | VL Forwarding VL Forwarding
225*4882a593Smuzhiyun * | (indexed by SHARINDX) (indexed by SHARINDX)
226*4882a593Smuzhiyun * | +---------+ +---------+
227*4882a593Smuzhiyun * | | TYPE=0 | | TYPE=1 |
228*4882a593Smuzhiyun * | +---------+ +---------+
229*4882a593Smuzhiyun * | select PRIORITY, select PRIORITY,
230*4882a593Smuzhiyun * | PARTITION, DESTPORTS PARTITION, DESTPORTS
231*4882a593Smuzhiyun * | | |
232*4882a593Smuzhiyun * | v v
233*4882a593Smuzhiyun * | VL Policing VL Policing
234*4882a593Smuzhiyun * | (indexed by SHARINDX) (indexed by SHARINDX)
235*4882a593Smuzhiyun * | +---------+ +---------+
236*4882a593Smuzhiyun * | | TYPE=0 | | TYPE=1 |
237*4882a593Smuzhiyun * | +---------+ +---------+
238*4882a593Smuzhiyun * | | |
239*4882a593Smuzhiyun * | v |
240*4882a593Smuzhiyun * | select BAG, -> exceed => drop |
241*4882a593Smuzhiyun * | JITTER v
242*4882a593Smuzhiyun * | | ----------------------------------------------
243*4882a593Smuzhiyun * | | / Reception Window is open for this VL \
244*4882a593Smuzhiyun * | | / (the Schedule Table executes an entry i \
245*4882a593Smuzhiyun * | | / M <= i < N, for which these conditions hold): \ no
246*4882a593Smuzhiyun * | | +----/ \-+
247*4882a593Smuzhiyun * | | |yes \ WINST[M] == 1 && WINSTINDEX[M] == VLID / |
248*4882a593Smuzhiyun * | | | \ WINEND[N] == 1 && WINSTINDEX[N] == VLID / |
249*4882a593Smuzhiyun * | | | \ / |
250*4882a593Smuzhiyun * | | | \ (the VL window has opened and not yet closed)/ |
251*4882a593Smuzhiyun * | | | ---------------------------------------------- |
252*4882a593Smuzhiyun * | | v v
253*4882a593Smuzhiyun * | | dispatch to DESTPORTS when the Schedule Table drop
254*4882a593Smuzhiyun * | | executes an entry i with TXEN == 1 && VLINDEX == i
255*4882a593Smuzhiyun * v v
256*4882a593Smuzhiyun * dispatch immediately to DESTPORTS
257*4882a593Smuzhiyun *
258*4882a593Smuzhiyun * The per-port classification key is always composed of {DMAC, VID, PCP} and
259*4882a593Smuzhiyun * is non-maskable. This 'looks like' the NULL stream identification function
260*4882a593Smuzhiyun * from IEEE 802.1CB clause 6, except for the extra VLAN PCP. When the switch
261*4882a593Smuzhiyun * ports operate as VLAN-unaware, we do allow the user to not specify the VLAN
262*4882a593Smuzhiyun * ID and PCP, and then the port-based defaults will be used.
263*4882a593Smuzhiyun *
264*4882a593Smuzhiyun * In TTEthernet, routing is something that needs to be done manually for each
265*4882a593Smuzhiyun * Virtual Link. So the flow action must always include one of:
266*4882a593Smuzhiyun * a. 'redirect', 'trap' or 'drop': select the egress port list
267*4882a593Smuzhiyun * Additionally, the following actions may be applied on a Virtual Link,
268*4882a593Smuzhiyun * turning it into 'critical' traffic:
269*4882a593Smuzhiyun * b. 'police': turn it into a rate-constrained VL, with bandwidth limitation
270*4882a593Smuzhiyun * given by the maximum frame length, bandwidth allocation gap (BAG) and
271*4882a593Smuzhiyun * maximum jitter.
272*4882a593Smuzhiyun * c. 'gate': turn it into a time-triggered VL, which can be only be received
273*4882a593Smuzhiyun * and forwarded according to a given schedule.
274*4882a593Smuzhiyun */
275*4882a593Smuzhiyun
sja1105_vl_key_lower(struct sja1105_vl_lookup_entry * a,struct sja1105_vl_lookup_entry * b)276*4882a593Smuzhiyun static bool sja1105_vl_key_lower(struct sja1105_vl_lookup_entry *a,
277*4882a593Smuzhiyun struct sja1105_vl_lookup_entry *b)
278*4882a593Smuzhiyun {
279*4882a593Smuzhiyun if (a->macaddr < b->macaddr)
280*4882a593Smuzhiyun return true;
281*4882a593Smuzhiyun if (a->macaddr > b->macaddr)
282*4882a593Smuzhiyun return false;
283*4882a593Smuzhiyun if (a->vlanid < b->vlanid)
284*4882a593Smuzhiyun return true;
285*4882a593Smuzhiyun if (a->vlanid > b->vlanid)
286*4882a593Smuzhiyun return false;
287*4882a593Smuzhiyun if (a->port < b->port)
288*4882a593Smuzhiyun return true;
289*4882a593Smuzhiyun if (a->port > b->port)
290*4882a593Smuzhiyun return false;
291*4882a593Smuzhiyun if (a->vlanprior < b->vlanprior)
292*4882a593Smuzhiyun return true;
293*4882a593Smuzhiyun if (a->vlanprior > b->vlanprior)
294*4882a593Smuzhiyun return false;
295*4882a593Smuzhiyun /* Keys are equal */
296*4882a593Smuzhiyun return false;
297*4882a593Smuzhiyun }
298*4882a593Smuzhiyun
sja1105_init_virtual_links(struct sja1105_private * priv,struct netlink_ext_ack * extack)299*4882a593Smuzhiyun static int sja1105_init_virtual_links(struct sja1105_private *priv,
300*4882a593Smuzhiyun struct netlink_ext_ack *extack)
301*4882a593Smuzhiyun {
302*4882a593Smuzhiyun struct sja1105_vl_policing_entry *vl_policing;
303*4882a593Smuzhiyun struct sja1105_vl_forwarding_entry *vl_fwd;
304*4882a593Smuzhiyun struct sja1105_vl_lookup_entry *vl_lookup;
305*4882a593Smuzhiyun bool have_critical_virtual_links = false;
306*4882a593Smuzhiyun struct sja1105_table *table;
307*4882a593Smuzhiyun struct sja1105_rule *rule;
308*4882a593Smuzhiyun int num_virtual_links = 0;
309*4882a593Smuzhiyun int max_sharindx = 0;
310*4882a593Smuzhiyun int i, j, k;
311*4882a593Smuzhiyun
312*4882a593Smuzhiyun /* Figure out the dimensioning of the problem */
313*4882a593Smuzhiyun list_for_each_entry(rule, &priv->flow_block.rules, list) {
314*4882a593Smuzhiyun if (rule->type != SJA1105_RULE_VL)
315*4882a593Smuzhiyun continue;
316*4882a593Smuzhiyun /* Each VL lookup entry matches on a single ingress port */
317*4882a593Smuzhiyun num_virtual_links += hweight_long(rule->port_mask);
318*4882a593Smuzhiyun
319*4882a593Smuzhiyun if (rule->vl.type != SJA1105_VL_NONCRITICAL)
320*4882a593Smuzhiyun have_critical_virtual_links = true;
321*4882a593Smuzhiyun if (max_sharindx < rule->vl.sharindx)
322*4882a593Smuzhiyun max_sharindx = rule->vl.sharindx;
323*4882a593Smuzhiyun }
324*4882a593Smuzhiyun
325*4882a593Smuzhiyun if (num_virtual_links > SJA1105_MAX_VL_LOOKUP_COUNT) {
326*4882a593Smuzhiyun NL_SET_ERR_MSG_MOD(extack, "Not enough VL entries available");
327*4882a593Smuzhiyun return -ENOSPC;
328*4882a593Smuzhiyun }
329*4882a593Smuzhiyun
330*4882a593Smuzhiyun if (max_sharindx + 1 > SJA1105_MAX_VL_LOOKUP_COUNT) {
331*4882a593Smuzhiyun NL_SET_ERR_MSG_MOD(extack, "Policer index out of range");
332*4882a593Smuzhiyun return -ENOSPC;
333*4882a593Smuzhiyun }
334*4882a593Smuzhiyun
335*4882a593Smuzhiyun max_sharindx = max_t(int, num_virtual_links, max_sharindx) + 1;
336*4882a593Smuzhiyun
337*4882a593Smuzhiyun /* Discard previous VL Lookup Table */
338*4882a593Smuzhiyun table = &priv->static_config.tables[BLK_IDX_VL_LOOKUP];
339*4882a593Smuzhiyun if (table->entry_count) {
340*4882a593Smuzhiyun kfree(table->entries);
341*4882a593Smuzhiyun table->entry_count = 0;
342*4882a593Smuzhiyun }
343*4882a593Smuzhiyun
344*4882a593Smuzhiyun /* Discard previous VL Policing Table */
345*4882a593Smuzhiyun table = &priv->static_config.tables[BLK_IDX_VL_POLICING];
346*4882a593Smuzhiyun if (table->entry_count) {
347*4882a593Smuzhiyun kfree(table->entries);
348*4882a593Smuzhiyun table->entry_count = 0;
349*4882a593Smuzhiyun }
350*4882a593Smuzhiyun
351*4882a593Smuzhiyun /* Discard previous VL Forwarding Table */
352*4882a593Smuzhiyun table = &priv->static_config.tables[BLK_IDX_VL_FORWARDING];
353*4882a593Smuzhiyun if (table->entry_count) {
354*4882a593Smuzhiyun kfree(table->entries);
355*4882a593Smuzhiyun table->entry_count = 0;
356*4882a593Smuzhiyun }
357*4882a593Smuzhiyun
358*4882a593Smuzhiyun /* Discard previous VL Forwarding Parameters Table */
359*4882a593Smuzhiyun table = &priv->static_config.tables[BLK_IDX_VL_FORWARDING_PARAMS];
360*4882a593Smuzhiyun if (table->entry_count) {
361*4882a593Smuzhiyun kfree(table->entries);
362*4882a593Smuzhiyun table->entry_count = 0;
363*4882a593Smuzhiyun }
364*4882a593Smuzhiyun
365*4882a593Smuzhiyun /* Nothing to do */
366*4882a593Smuzhiyun if (!num_virtual_links)
367*4882a593Smuzhiyun return 0;
368*4882a593Smuzhiyun
369*4882a593Smuzhiyun /* Pre-allocate space in the static config tables */
370*4882a593Smuzhiyun
371*4882a593Smuzhiyun /* VL Lookup Table */
372*4882a593Smuzhiyun table = &priv->static_config.tables[BLK_IDX_VL_LOOKUP];
373*4882a593Smuzhiyun table->entries = kcalloc(num_virtual_links,
374*4882a593Smuzhiyun table->ops->unpacked_entry_size,
375*4882a593Smuzhiyun GFP_KERNEL);
376*4882a593Smuzhiyun if (!table->entries)
377*4882a593Smuzhiyun return -ENOMEM;
378*4882a593Smuzhiyun table->entry_count = num_virtual_links;
379*4882a593Smuzhiyun vl_lookup = table->entries;
380*4882a593Smuzhiyun
381*4882a593Smuzhiyun k = 0;
382*4882a593Smuzhiyun
383*4882a593Smuzhiyun list_for_each_entry(rule, &priv->flow_block.rules, list) {
384*4882a593Smuzhiyun unsigned long port;
385*4882a593Smuzhiyun
386*4882a593Smuzhiyun if (rule->type != SJA1105_RULE_VL)
387*4882a593Smuzhiyun continue;
388*4882a593Smuzhiyun
389*4882a593Smuzhiyun for_each_set_bit(port, &rule->port_mask, SJA1105_NUM_PORTS) {
390*4882a593Smuzhiyun vl_lookup[k].format = SJA1105_VL_FORMAT_PSFP;
391*4882a593Smuzhiyun vl_lookup[k].port = port;
392*4882a593Smuzhiyun vl_lookup[k].macaddr = rule->key.vl.dmac;
393*4882a593Smuzhiyun if (rule->key.type == SJA1105_KEY_VLAN_AWARE_VL) {
394*4882a593Smuzhiyun vl_lookup[k].vlanid = rule->key.vl.vid;
395*4882a593Smuzhiyun vl_lookup[k].vlanprior = rule->key.vl.pcp;
396*4882a593Smuzhiyun } else {
397*4882a593Smuzhiyun u16 vid = dsa_8021q_rx_vid(priv->ds, port);
398*4882a593Smuzhiyun
399*4882a593Smuzhiyun vl_lookup[k].vlanid = vid;
400*4882a593Smuzhiyun vl_lookup[k].vlanprior = 0;
401*4882a593Smuzhiyun }
402*4882a593Smuzhiyun /* For critical VLs, the DESTPORTS mask is taken from
403*4882a593Smuzhiyun * the VL Forwarding Table, so no point in putting it
404*4882a593Smuzhiyun * in the VL Lookup Table
405*4882a593Smuzhiyun */
406*4882a593Smuzhiyun if (rule->vl.type == SJA1105_VL_NONCRITICAL)
407*4882a593Smuzhiyun vl_lookup[k].destports = rule->vl.destports;
408*4882a593Smuzhiyun else
409*4882a593Smuzhiyun vl_lookup[k].iscritical = true;
410*4882a593Smuzhiyun vl_lookup[k].flow_cookie = rule->cookie;
411*4882a593Smuzhiyun k++;
412*4882a593Smuzhiyun }
413*4882a593Smuzhiyun }
414*4882a593Smuzhiyun
415*4882a593Smuzhiyun /* UM10944.pdf chapter 4.2.3 VL Lookup table:
416*4882a593Smuzhiyun * "the entries in the VL Lookup table must be sorted in ascending
417*4882a593Smuzhiyun * order (i.e. the smallest value must be loaded first) according to
418*4882a593Smuzhiyun * the following sort order: MACADDR, VLANID, PORT, VLANPRIOR."
419*4882a593Smuzhiyun */
420*4882a593Smuzhiyun for (i = 0; i < num_virtual_links; i++) {
421*4882a593Smuzhiyun struct sja1105_vl_lookup_entry *a = &vl_lookup[i];
422*4882a593Smuzhiyun
423*4882a593Smuzhiyun for (j = i + 1; j < num_virtual_links; j++) {
424*4882a593Smuzhiyun struct sja1105_vl_lookup_entry *b = &vl_lookup[j];
425*4882a593Smuzhiyun
426*4882a593Smuzhiyun if (sja1105_vl_key_lower(b, a)) {
427*4882a593Smuzhiyun struct sja1105_vl_lookup_entry tmp = *a;
428*4882a593Smuzhiyun
429*4882a593Smuzhiyun *a = *b;
430*4882a593Smuzhiyun *b = tmp;
431*4882a593Smuzhiyun }
432*4882a593Smuzhiyun }
433*4882a593Smuzhiyun }
434*4882a593Smuzhiyun
435*4882a593Smuzhiyun if (!have_critical_virtual_links)
436*4882a593Smuzhiyun return 0;
437*4882a593Smuzhiyun
438*4882a593Smuzhiyun /* VL Policing Table */
439*4882a593Smuzhiyun table = &priv->static_config.tables[BLK_IDX_VL_POLICING];
440*4882a593Smuzhiyun table->entries = kcalloc(max_sharindx, table->ops->unpacked_entry_size,
441*4882a593Smuzhiyun GFP_KERNEL);
442*4882a593Smuzhiyun if (!table->entries)
443*4882a593Smuzhiyun return -ENOMEM;
444*4882a593Smuzhiyun table->entry_count = max_sharindx;
445*4882a593Smuzhiyun vl_policing = table->entries;
446*4882a593Smuzhiyun
447*4882a593Smuzhiyun /* VL Forwarding Table */
448*4882a593Smuzhiyun table = &priv->static_config.tables[BLK_IDX_VL_FORWARDING];
449*4882a593Smuzhiyun table->entries = kcalloc(max_sharindx, table->ops->unpacked_entry_size,
450*4882a593Smuzhiyun GFP_KERNEL);
451*4882a593Smuzhiyun if (!table->entries)
452*4882a593Smuzhiyun return -ENOMEM;
453*4882a593Smuzhiyun table->entry_count = max_sharindx;
454*4882a593Smuzhiyun vl_fwd = table->entries;
455*4882a593Smuzhiyun
456*4882a593Smuzhiyun /* VL Forwarding Parameters Table */
457*4882a593Smuzhiyun table = &priv->static_config.tables[BLK_IDX_VL_FORWARDING_PARAMS];
458*4882a593Smuzhiyun table->entries = kcalloc(1, table->ops->unpacked_entry_size,
459*4882a593Smuzhiyun GFP_KERNEL);
460*4882a593Smuzhiyun if (!table->entries)
461*4882a593Smuzhiyun return -ENOMEM;
462*4882a593Smuzhiyun table->entry_count = 1;
463*4882a593Smuzhiyun
464*4882a593Smuzhiyun for (i = 0; i < num_virtual_links; i++) {
465*4882a593Smuzhiyun unsigned long cookie = vl_lookup[i].flow_cookie;
466*4882a593Smuzhiyun struct sja1105_rule *rule = sja1105_rule_find(priv, cookie);
467*4882a593Smuzhiyun
468*4882a593Smuzhiyun if (rule->vl.type == SJA1105_VL_NONCRITICAL)
469*4882a593Smuzhiyun continue;
470*4882a593Smuzhiyun if (rule->vl.type == SJA1105_VL_TIME_TRIGGERED) {
471*4882a593Smuzhiyun int sharindx = rule->vl.sharindx;
472*4882a593Smuzhiyun
473*4882a593Smuzhiyun vl_policing[i].type = 1;
474*4882a593Smuzhiyun vl_policing[i].sharindx = sharindx;
475*4882a593Smuzhiyun vl_policing[i].maxlen = rule->vl.maxlen;
476*4882a593Smuzhiyun vl_policing[sharindx].type = 1;
477*4882a593Smuzhiyun
478*4882a593Smuzhiyun vl_fwd[i].type = 1;
479*4882a593Smuzhiyun vl_fwd[sharindx].type = 1;
480*4882a593Smuzhiyun vl_fwd[sharindx].priority = rule->vl.ipv;
481*4882a593Smuzhiyun vl_fwd[sharindx].partition = 0;
482*4882a593Smuzhiyun vl_fwd[sharindx].destports = rule->vl.destports;
483*4882a593Smuzhiyun }
484*4882a593Smuzhiyun }
485*4882a593Smuzhiyun
486*4882a593Smuzhiyun sja1105_frame_memory_partitioning(priv);
487*4882a593Smuzhiyun
488*4882a593Smuzhiyun return 0;
489*4882a593Smuzhiyun }
490*4882a593Smuzhiyun
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)491*4882a593Smuzhiyun int sja1105_vl_redirect(struct sja1105_private *priv, int port,
492*4882a593Smuzhiyun struct netlink_ext_ack *extack, unsigned long cookie,
493*4882a593Smuzhiyun struct sja1105_key *key, unsigned long destports,
494*4882a593Smuzhiyun bool append)
495*4882a593Smuzhiyun {
496*4882a593Smuzhiyun struct sja1105_rule *rule = sja1105_rule_find(priv, cookie);
497*4882a593Smuzhiyun int rc;
498*4882a593Smuzhiyun
499*4882a593Smuzhiyun if (priv->vlan_state == SJA1105_VLAN_UNAWARE &&
500*4882a593Smuzhiyun key->type != SJA1105_KEY_VLAN_UNAWARE_VL) {
501*4882a593Smuzhiyun NL_SET_ERR_MSG_MOD(extack,
502*4882a593Smuzhiyun "Can only redirect based on DMAC");
503*4882a593Smuzhiyun return -EOPNOTSUPP;
504*4882a593Smuzhiyun } else if ((priv->vlan_state == SJA1105_VLAN_BEST_EFFORT ||
505*4882a593Smuzhiyun priv->vlan_state == SJA1105_VLAN_FILTERING_FULL) &&
506*4882a593Smuzhiyun key->type != SJA1105_KEY_VLAN_AWARE_VL) {
507*4882a593Smuzhiyun NL_SET_ERR_MSG_MOD(extack,
508*4882a593Smuzhiyun "Can only redirect based on {DMAC, VID, PCP}");
509*4882a593Smuzhiyun return -EOPNOTSUPP;
510*4882a593Smuzhiyun }
511*4882a593Smuzhiyun
512*4882a593Smuzhiyun if (!rule) {
513*4882a593Smuzhiyun rule = kzalloc(sizeof(*rule), GFP_KERNEL);
514*4882a593Smuzhiyun if (!rule)
515*4882a593Smuzhiyun return -ENOMEM;
516*4882a593Smuzhiyun
517*4882a593Smuzhiyun rule->cookie = cookie;
518*4882a593Smuzhiyun rule->type = SJA1105_RULE_VL;
519*4882a593Smuzhiyun rule->key = *key;
520*4882a593Smuzhiyun list_add(&rule->list, &priv->flow_block.rules);
521*4882a593Smuzhiyun }
522*4882a593Smuzhiyun
523*4882a593Smuzhiyun rule->port_mask |= BIT(port);
524*4882a593Smuzhiyun if (append)
525*4882a593Smuzhiyun rule->vl.destports |= destports;
526*4882a593Smuzhiyun else
527*4882a593Smuzhiyun rule->vl.destports = destports;
528*4882a593Smuzhiyun
529*4882a593Smuzhiyun rc = sja1105_init_virtual_links(priv, extack);
530*4882a593Smuzhiyun if (rc) {
531*4882a593Smuzhiyun rule->port_mask &= ~BIT(port);
532*4882a593Smuzhiyun if (!rule->port_mask) {
533*4882a593Smuzhiyun list_del(&rule->list);
534*4882a593Smuzhiyun kfree(rule);
535*4882a593Smuzhiyun }
536*4882a593Smuzhiyun }
537*4882a593Smuzhiyun
538*4882a593Smuzhiyun return rc;
539*4882a593Smuzhiyun }
540*4882a593Smuzhiyun
sja1105_vl_delete(struct sja1105_private * priv,int port,struct sja1105_rule * rule,struct netlink_ext_ack * extack)541*4882a593Smuzhiyun int sja1105_vl_delete(struct sja1105_private *priv, int port,
542*4882a593Smuzhiyun struct sja1105_rule *rule, struct netlink_ext_ack *extack)
543*4882a593Smuzhiyun {
544*4882a593Smuzhiyun int rc;
545*4882a593Smuzhiyun
546*4882a593Smuzhiyun rule->port_mask &= ~BIT(port);
547*4882a593Smuzhiyun if (!rule->port_mask) {
548*4882a593Smuzhiyun list_del(&rule->list);
549*4882a593Smuzhiyun kfree(rule);
550*4882a593Smuzhiyun }
551*4882a593Smuzhiyun
552*4882a593Smuzhiyun rc = sja1105_compose_gating_subschedule(priv, extack);
553*4882a593Smuzhiyun if (rc)
554*4882a593Smuzhiyun return rc;
555*4882a593Smuzhiyun
556*4882a593Smuzhiyun rc = sja1105_init_virtual_links(priv, extack);
557*4882a593Smuzhiyun if (rc)
558*4882a593Smuzhiyun return rc;
559*4882a593Smuzhiyun
560*4882a593Smuzhiyun rc = sja1105_init_scheduling(priv);
561*4882a593Smuzhiyun if (rc < 0)
562*4882a593Smuzhiyun return rc;
563*4882a593Smuzhiyun
564*4882a593Smuzhiyun return sja1105_static_config_reload(priv, SJA1105_VIRTUAL_LINKS);
565*4882a593Smuzhiyun }
566*4882a593Smuzhiyun
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)567*4882a593Smuzhiyun int sja1105_vl_gate(struct sja1105_private *priv, int port,
568*4882a593Smuzhiyun struct netlink_ext_ack *extack, unsigned long cookie,
569*4882a593Smuzhiyun struct sja1105_key *key, u32 index, s32 prio,
570*4882a593Smuzhiyun u64 base_time, u64 cycle_time, u64 cycle_time_ext,
571*4882a593Smuzhiyun u32 num_entries, struct action_gate_entry *entries)
572*4882a593Smuzhiyun {
573*4882a593Smuzhiyun struct sja1105_rule *rule = sja1105_rule_find(priv, cookie);
574*4882a593Smuzhiyun int ipv = -1;
575*4882a593Smuzhiyun int i, rc;
576*4882a593Smuzhiyun s32 rem;
577*4882a593Smuzhiyun
578*4882a593Smuzhiyun if (cycle_time_ext) {
579*4882a593Smuzhiyun NL_SET_ERR_MSG_MOD(extack,
580*4882a593Smuzhiyun "Cycle time extension not supported");
581*4882a593Smuzhiyun return -EOPNOTSUPP;
582*4882a593Smuzhiyun }
583*4882a593Smuzhiyun
584*4882a593Smuzhiyun div_s64_rem(base_time, sja1105_delta_to_ns(1), &rem);
585*4882a593Smuzhiyun if (rem) {
586*4882a593Smuzhiyun NL_SET_ERR_MSG_MOD(extack,
587*4882a593Smuzhiyun "Base time must be multiple of 200 ns");
588*4882a593Smuzhiyun return -ERANGE;
589*4882a593Smuzhiyun }
590*4882a593Smuzhiyun
591*4882a593Smuzhiyun div_s64_rem(cycle_time, sja1105_delta_to_ns(1), &rem);
592*4882a593Smuzhiyun if (rem) {
593*4882a593Smuzhiyun NL_SET_ERR_MSG_MOD(extack,
594*4882a593Smuzhiyun "Cycle time must be multiple of 200 ns");
595*4882a593Smuzhiyun return -ERANGE;
596*4882a593Smuzhiyun }
597*4882a593Smuzhiyun
598*4882a593Smuzhiyun if (priv->vlan_state == SJA1105_VLAN_UNAWARE &&
599*4882a593Smuzhiyun key->type != SJA1105_KEY_VLAN_UNAWARE_VL) {
600*4882a593Smuzhiyun NL_SET_ERR_MSG_MOD(extack,
601*4882a593Smuzhiyun "Can only gate based on DMAC");
602*4882a593Smuzhiyun return -EOPNOTSUPP;
603*4882a593Smuzhiyun } else if ((priv->vlan_state == SJA1105_VLAN_BEST_EFFORT ||
604*4882a593Smuzhiyun priv->vlan_state == SJA1105_VLAN_FILTERING_FULL) &&
605*4882a593Smuzhiyun key->type != SJA1105_KEY_VLAN_AWARE_VL) {
606*4882a593Smuzhiyun NL_SET_ERR_MSG_MOD(extack,
607*4882a593Smuzhiyun "Can only gate based on {DMAC, VID, PCP}");
608*4882a593Smuzhiyun return -EOPNOTSUPP;
609*4882a593Smuzhiyun }
610*4882a593Smuzhiyun
611*4882a593Smuzhiyun if (!rule) {
612*4882a593Smuzhiyun rule = kzalloc(sizeof(*rule), GFP_KERNEL);
613*4882a593Smuzhiyun if (!rule)
614*4882a593Smuzhiyun return -ENOMEM;
615*4882a593Smuzhiyun
616*4882a593Smuzhiyun list_add(&rule->list, &priv->flow_block.rules);
617*4882a593Smuzhiyun rule->cookie = cookie;
618*4882a593Smuzhiyun rule->type = SJA1105_RULE_VL;
619*4882a593Smuzhiyun rule->key = *key;
620*4882a593Smuzhiyun rule->vl.type = SJA1105_VL_TIME_TRIGGERED;
621*4882a593Smuzhiyun rule->vl.sharindx = index;
622*4882a593Smuzhiyun rule->vl.base_time = base_time;
623*4882a593Smuzhiyun rule->vl.cycle_time = cycle_time;
624*4882a593Smuzhiyun rule->vl.num_entries = num_entries;
625*4882a593Smuzhiyun rule->vl.entries = kcalloc(num_entries,
626*4882a593Smuzhiyun sizeof(struct action_gate_entry),
627*4882a593Smuzhiyun GFP_KERNEL);
628*4882a593Smuzhiyun if (!rule->vl.entries) {
629*4882a593Smuzhiyun rc = -ENOMEM;
630*4882a593Smuzhiyun goto out;
631*4882a593Smuzhiyun }
632*4882a593Smuzhiyun
633*4882a593Smuzhiyun for (i = 0; i < num_entries; i++) {
634*4882a593Smuzhiyun div_s64_rem(entries[i].interval,
635*4882a593Smuzhiyun sja1105_delta_to_ns(1), &rem);
636*4882a593Smuzhiyun if (rem) {
637*4882a593Smuzhiyun NL_SET_ERR_MSG_MOD(extack,
638*4882a593Smuzhiyun "Interval must be multiple of 200 ns");
639*4882a593Smuzhiyun rc = -ERANGE;
640*4882a593Smuzhiyun goto out;
641*4882a593Smuzhiyun }
642*4882a593Smuzhiyun
643*4882a593Smuzhiyun if (!entries[i].interval) {
644*4882a593Smuzhiyun NL_SET_ERR_MSG_MOD(extack,
645*4882a593Smuzhiyun "Interval cannot be zero");
646*4882a593Smuzhiyun rc = -ERANGE;
647*4882a593Smuzhiyun goto out;
648*4882a593Smuzhiyun }
649*4882a593Smuzhiyun
650*4882a593Smuzhiyun if (ns_to_sja1105_delta(entries[i].interval) >
651*4882a593Smuzhiyun SJA1105_TAS_MAX_DELTA) {
652*4882a593Smuzhiyun NL_SET_ERR_MSG_MOD(extack,
653*4882a593Smuzhiyun "Maximum interval is 52 ms");
654*4882a593Smuzhiyun rc = -ERANGE;
655*4882a593Smuzhiyun goto out;
656*4882a593Smuzhiyun }
657*4882a593Smuzhiyun
658*4882a593Smuzhiyun if (entries[i].maxoctets != -1) {
659*4882a593Smuzhiyun NL_SET_ERR_MSG_MOD(extack,
660*4882a593Smuzhiyun "Cannot offload IntervalOctetMax");
661*4882a593Smuzhiyun rc = -EOPNOTSUPP;
662*4882a593Smuzhiyun goto out;
663*4882a593Smuzhiyun }
664*4882a593Smuzhiyun
665*4882a593Smuzhiyun if (ipv == -1) {
666*4882a593Smuzhiyun ipv = entries[i].ipv;
667*4882a593Smuzhiyun } else if (ipv != entries[i].ipv) {
668*4882a593Smuzhiyun NL_SET_ERR_MSG_MOD(extack,
669*4882a593Smuzhiyun "Only support a single IPV per VL");
670*4882a593Smuzhiyun rc = -EOPNOTSUPP;
671*4882a593Smuzhiyun goto out;
672*4882a593Smuzhiyun }
673*4882a593Smuzhiyun
674*4882a593Smuzhiyun rule->vl.entries[i] = entries[i];
675*4882a593Smuzhiyun }
676*4882a593Smuzhiyun
677*4882a593Smuzhiyun if (ipv == -1) {
678*4882a593Smuzhiyun if (key->type == SJA1105_KEY_VLAN_AWARE_VL)
679*4882a593Smuzhiyun ipv = key->vl.pcp;
680*4882a593Smuzhiyun else
681*4882a593Smuzhiyun ipv = 0;
682*4882a593Smuzhiyun }
683*4882a593Smuzhiyun
684*4882a593Smuzhiyun /* TODO: support per-flow MTU */
685*4882a593Smuzhiyun rule->vl.maxlen = VLAN_ETH_FRAME_LEN + ETH_FCS_LEN;
686*4882a593Smuzhiyun rule->vl.ipv = ipv;
687*4882a593Smuzhiyun }
688*4882a593Smuzhiyun
689*4882a593Smuzhiyun rule->port_mask |= BIT(port);
690*4882a593Smuzhiyun
691*4882a593Smuzhiyun rc = sja1105_compose_gating_subschedule(priv, extack);
692*4882a593Smuzhiyun if (rc)
693*4882a593Smuzhiyun goto out;
694*4882a593Smuzhiyun
695*4882a593Smuzhiyun rc = sja1105_init_virtual_links(priv, extack);
696*4882a593Smuzhiyun if (rc)
697*4882a593Smuzhiyun goto out;
698*4882a593Smuzhiyun
699*4882a593Smuzhiyun if (sja1105_gating_check_conflicts(priv, -1, extack)) {
700*4882a593Smuzhiyun NL_SET_ERR_MSG_MOD(extack, "Conflict with tc-taprio schedule");
701*4882a593Smuzhiyun rc = -ERANGE;
702*4882a593Smuzhiyun goto out;
703*4882a593Smuzhiyun }
704*4882a593Smuzhiyun
705*4882a593Smuzhiyun out:
706*4882a593Smuzhiyun if (rc) {
707*4882a593Smuzhiyun rule->port_mask &= ~BIT(port);
708*4882a593Smuzhiyun if (!rule->port_mask) {
709*4882a593Smuzhiyun list_del(&rule->list);
710*4882a593Smuzhiyun kfree(rule->vl.entries);
711*4882a593Smuzhiyun kfree(rule);
712*4882a593Smuzhiyun }
713*4882a593Smuzhiyun }
714*4882a593Smuzhiyun
715*4882a593Smuzhiyun return rc;
716*4882a593Smuzhiyun }
717*4882a593Smuzhiyun
sja1105_find_vlid(struct sja1105_private * priv,int port,struct sja1105_key * key)718*4882a593Smuzhiyun static int sja1105_find_vlid(struct sja1105_private *priv, int port,
719*4882a593Smuzhiyun struct sja1105_key *key)
720*4882a593Smuzhiyun {
721*4882a593Smuzhiyun struct sja1105_vl_lookup_entry *vl_lookup;
722*4882a593Smuzhiyun struct sja1105_table *table;
723*4882a593Smuzhiyun int i;
724*4882a593Smuzhiyun
725*4882a593Smuzhiyun if (WARN_ON(key->type != SJA1105_KEY_VLAN_AWARE_VL &&
726*4882a593Smuzhiyun key->type != SJA1105_KEY_VLAN_UNAWARE_VL))
727*4882a593Smuzhiyun return -1;
728*4882a593Smuzhiyun
729*4882a593Smuzhiyun table = &priv->static_config.tables[BLK_IDX_VL_LOOKUP];
730*4882a593Smuzhiyun vl_lookup = table->entries;
731*4882a593Smuzhiyun
732*4882a593Smuzhiyun for (i = 0; i < table->entry_count; i++) {
733*4882a593Smuzhiyun if (key->type == SJA1105_KEY_VLAN_AWARE_VL) {
734*4882a593Smuzhiyun if (vl_lookup[i].port == port &&
735*4882a593Smuzhiyun vl_lookup[i].macaddr == key->vl.dmac &&
736*4882a593Smuzhiyun vl_lookup[i].vlanid == key->vl.vid &&
737*4882a593Smuzhiyun vl_lookup[i].vlanprior == key->vl.pcp)
738*4882a593Smuzhiyun return i;
739*4882a593Smuzhiyun } else {
740*4882a593Smuzhiyun if (vl_lookup[i].port == port &&
741*4882a593Smuzhiyun vl_lookup[i].macaddr == key->vl.dmac)
742*4882a593Smuzhiyun return i;
743*4882a593Smuzhiyun }
744*4882a593Smuzhiyun }
745*4882a593Smuzhiyun
746*4882a593Smuzhiyun return -1;
747*4882a593Smuzhiyun }
748*4882a593Smuzhiyun
sja1105_vl_stats(struct sja1105_private * priv,int port,struct sja1105_rule * rule,struct flow_stats * stats,struct netlink_ext_ack * extack)749*4882a593Smuzhiyun int sja1105_vl_stats(struct sja1105_private *priv, int port,
750*4882a593Smuzhiyun struct sja1105_rule *rule, struct flow_stats *stats,
751*4882a593Smuzhiyun struct netlink_ext_ack *extack)
752*4882a593Smuzhiyun {
753*4882a593Smuzhiyun const struct sja1105_regs *regs = priv->info->regs;
754*4882a593Smuzhiyun u8 buf[SJA1105_SIZE_VL_STATUS] = {0};
755*4882a593Smuzhiyun u64 unreleased;
756*4882a593Smuzhiyun u64 timingerr;
757*4882a593Smuzhiyun u64 lengtherr;
758*4882a593Smuzhiyun int vlid, rc;
759*4882a593Smuzhiyun u64 pkts;
760*4882a593Smuzhiyun
761*4882a593Smuzhiyun if (rule->vl.type != SJA1105_VL_TIME_TRIGGERED)
762*4882a593Smuzhiyun return 0;
763*4882a593Smuzhiyun
764*4882a593Smuzhiyun vlid = sja1105_find_vlid(priv, port, &rule->key);
765*4882a593Smuzhiyun if (vlid < 0)
766*4882a593Smuzhiyun return 0;
767*4882a593Smuzhiyun
768*4882a593Smuzhiyun rc = sja1105_xfer_buf(priv, SPI_READ, regs->vl_status + 2 * vlid, buf,
769*4882a593Smuzhiyun SJA1105_SIZE_VL_STATUS);
770*4882a593Smuzhiyun if (rc) {
771*4882a593Smuzhiyun NL_SET_ERR_MSG_MOD(extack, "SPI access failed");
772*4882a593Smuzhiyun return rc;
773*4882a593Smuzhiyun }
774*4882a593Smuzhiyun
775*4882a593Smuzhiyun sja1105_unpack(buf, &timingerr, 31, 16, SJA1105_SIZE_VL_STATUS);
776*4882a593Smuzhiyun sja1105_unpack(buf, &unreleased, 15, 0, SJA1105_SIZE_VL_STATUS);
777*4882a593Smuzhiyun sja1105_unpack(buf, &lengtherr, 47, 32, SJA1105_SIZE_VL_STATUS);
778*4882a593Smuzhiyun
779*4882a593Smuzhiyun pkts = timingerr + unreleased + lengtherr;
780*4882a593Smuzhiyun
781*4882a593Smuzhiyun flow_stats_update(stats, 0, pkts - rule->vl.stats.pkts, 0,
782*4882a593Smuzhiyun jiffies - rule->vl.stats.lastused,
783*4882a593Smuzhiyun FLOW_ACTION_HW_STATS_IMMEDIATE);
784*4882a593Smuzhiyun
785*4882a593Smuzhiyun rule->vl.stats.pkts = pkts;
786*4882a593Smuzhiyun rule->vl.stats.lastused = jiffies;
787*4882a593Smuzhiyun
788*4882a593Smuzhiyun return 0;
789*4882a593Smuzhiyun }
790