Skip to content

Commit 36203ae

Browse files
committed
Add SAI TAM per-flow telemetry (TELEMETRY_TYPE_FLOW)
Define the attribute block for SAI_TAM_TELEMETRY_TYPE_FLOW, which previously existed as an enum value with no operational attributes. - saitam.h: add SAI_TAM_TEL_TYPE_ATTR_FLOW_REPORT_INTERVAL_US to configure per-flow streaming cadence (conditional on FLOW type). - saiswitch.h: add switch-level attributes for hardware autonomous flow learning: TAM_FLOW_AUTO_LEARN_ENABLE, _AGING_TIME_S, and the read-only _FAILURES counter. - doc: add SAI-TAM-Flow-Telemetry.md describing the design, IPFIX record schema, cleanup semantics, and sample workflow. Operator-configured flows reuse the existing ACL TAM_OBJECT action; COUNTER_SUBSCRIPTION telemetry is unchanged. Signed-off-by: Aakash Kumar S <saakashkumar@marvell.com>
1 parent c0203c0 commit 36203ae

4 files changed

Lines changed: 480 additions & 0 deletions

File tree

doc/TAM/SAI-TAM-Flow-Telemetry.md

Lines changed: 355 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,355 @@
1+
# SAI TAM per-flow telemetry
2+
3+
---
4+
5+
| Title | SAI TAM per-flow telemetry (TELEMETRY_TYPE_FLOW) |
6+
| ------- | ----------------------------------------------------------------------------------- |
7+
| Authors | `Aakash Kumar S, Garvith V` |
8+
| Status | In review |
9+
| Type | Standards track |
10+
| Created | `06/07/2026` |
11+
12+
---
13+
14+
## Overview
15+
16+
This proposal defines the attribute block for `SAI_TAM_TELEMETRY_TYPE_FLOW` and, separately, adds a set of switch-level attributes that configure hardware autonomous flow learning. The FLOW telemetry-type enum value already exists in `sai_tam_telemetry_type_t` with the header comment "All the data relevant to a given flow", but `sai_tam_tel_type_attr_t` currently defines no attributes for it — it is a declared category with no operational specification.
17+
18+
Per-flow telemetry is a distinct capability from object-counter telemetry (`SAI_TAM_TELEMETRY_TYPE_COUNTER_SUBSCRIPTION`): the unit is a flow (identified by a classification key), not a named object (identified by OID). This proposal fills in the FLOW attribute block so that SAI TAM can carry per-flow telemetry. Flows can be populated from two independent sources: operator-configured (an ACL entry with an action referencing the enclosing TAM object) and hardware auto-learned (the ASIC autonomously identifies flows from live traffic, when enabled at the switch level). Both sources can be active simultaneously. `COUNTER_SUBSCRIPTION` is not modified.
19+
20+
## Requirements
21+
22+
- Enable per-flow packet/byte streaming through the existing TAM report/collector chain, using IPFIX records.
23+
- Support operator-configured per-flow monitoring via the existing `SAI_ACL_ENTRY_ATTR_ACTION_TAM_OBJECT` mechanism (no new ACL attribute required).
24+
- Support hardware autonomous flow learning as an independent, switch-level capability that operators can enable or disable at runtime.
25+
- Express operator policy at the intent layer (aging time) — not at the implementation-mechanism layer.
26+
- Leave the internal mechanism by which the implementation learns, ages, or reclaims flows unspecified.
27+
- Do not modify `SAI_TAM_TELEMETRY_TYPE_COUNTER_SUBSCRIPTION` or the existing event-side flow telemetry (`SAI_TAM_EVENT_TYPE_FLOW_STATE / _WATCHLIST / _TCPFLAG`).
28+
29+
## Design summary
30+
31+
The proposal decouples two concerns that were previously entangled:
32+
33+
- **Per-flow telemetry configuration** lives on the FLOW tel_type. It says "produce per-flow IPFIX records at this cadence, wire them through this report chain."
34+
- **Hardware autonomous flow learning** lives on the switch object. It is a HW-plane capability, independent of any specific telemetry configuration. When enabled, the implementation autonomously identifies flows from live traffic.
35+
36+
Flows populated into a FLOW tel_type can come from either source, or both, or neither:
37+
38+
- **Operator-configured flows** appear when the operator creates ACL entries with `SAI_ACL_ENTRY_ATTR_ACTION_TAM_OBJECT` pointing at the TAM object that references the FLOW tel_type. This is the existing SAI mechanism; no new ACL attribute is added.
39+
- **HW-learned flows** appear when `SAI_SWITCH_ATTR_TAM_FLOW_AUTO_LEARN_ENABLE` is true at the switch level. The switch attributes govern operator policy (aging) for autonomous learning.
40+
41+
The two sources are independent. Neither depends on the other.
42+
43+
## FLOW tel_type attributes
44+
45+
The following attributes are added to `sai_tam_tel_type_attr_t` in `saitam.h`. Both apply to `TELEMETRY_TYPE_FLOW` regardless of how flows are populated.
46+
47+
```
48+
+ /**
49+
+ * @brief Streaming cadence per active flow (microseconds)
50+
+ *
51+
+ * Operator-visible cadence at which per-flow records are emitted.
52+
+ * The implementation uses whichever internal mechanism (HW push,
53+
+ * polled read, or otherwise) meets it.
54+
+ *
55+
+ * @type sai_uint32_t
56+
+ * @flags MANDATORY_ON_CREATE | CREATE_AND_SET
57+
+ * @condition SAI_TAM_TEL_TYPE_ATTR_TAM_TELEMETRY_TYPE == SAI_TAM_TELEMETRY_TYPE_FLOW
58+
+ */
59+
+ SAI_TAM_TEL_TYPE_ATTR_FLOW_REPORT_INTERVAL_US,
60+
```
61+
62+
## Switch attributes for HW autonomous flow learning
63+
64+
The following attributes are added to `sai_switch_attr_t` in `saiswitch.h`. They govern hardware autonomous flow learning independently of any specific tel_type or TAM object.
65+
66+
```
67+
+ /**
68+
+ * @brief Enable hardware autonomous flow learning
69+
+ *
70+
+ * When true, the implementation autonomously identifies new flows
71+
+ * from live traffic and populates them into any FLOW tel_type that
72+
+ * is configured on the switch. When false, only flows explicitly
73+
+ * bound via ACL entries with SAI_ACL_ENTRY_ATTR_ACTION_TAM_OBJECT
74+
+ * populate FLOW tel_types; the implementation does not
75+
+ * autonomously identify new flows.
76+
+ *
77+
+ * The handling of flows that were already being tracked at the
78+
+ * moment this attribute transitions from true to false is
79+
+ * implementation-defined.
80+
+ *
81+
+ * Default is false. Autonomous learning must be explicitly enabled.
82+
+ *
83+
+ * @type bool
84+
+ * @flags CREATE_AND_SET
85+
+ * @default false
86+
+ */
87+
+ SAI_SWITCH_ATTR_TAM_FLOW_AUTO_LEARN_ENABLE,
88+
+
89+
+ /**
90+
+ * @brief Aging time for HW-learned flows (seconds)
91+
+ *
92+
+ * Operator-intent aging: a HW-learned flow idle for more than this
93+
+ * long is eligible for reclaim by the implementation. The reclaim
94+
+ * mechanism (autonomous HW sweep, HW-assisted SW loop, or
95+
+ * otherwise) is deliberately implementation-defined.
96+
+ *
97+
+ * Applies only to flows populated by HW auto-learn. Operator-
98+
+ * configured flows (installed via ACL entries) do not age; the
99+
+ * operator owns their lifetime by adding or removing ACL entries.
100+
+ *
101+
+ * A value of 0 means the implementation chooses its own aging time.
102+
+ *
103+
+ * @type sai_uint32_t
104+
+ * @flags CREATE_AND_SET
105+
+ * @default 0
106+
+ * @validonly SAI_SWITCH_ATTR_TAM_FLOW_AUTO_LEARN_ENABLE == true
107+
+ */
108+
+ SAI_SWITCH_ATTR_TAM_FLOW_AUTO_LEARN_AGING_TIME_S,
109+
+
110+
+ /**
111+
+ * @brief HW auto-learn failure count
112+
+ *
113+
+ * Count of new-flow learn attempts that were rejected because the
114+
+ * implementation could not admit the flow (capacity exhausted).
115+
+ * Without this counter, operators would have no way to distinguish
116+
+ * "table running fine" from "table chronically saturated, missing
117+
+ * new flows".
118+
+ *
119+
+ * Valid only when SAI_SWITCH_ATTR_TAM_FLOW_AUTO_LEARN_ENABLE is true;
120+
+ * otherwise the returned value is 0.
121+
+ *
122+
+ * @type sai_uint64_t
123+
+ * @flags READ_ONLY
124+
+ */
125+
+ SAI_SWITCH_ATTR_TAM_FLOW_AUTO_LEARN_FAILURES,
126+
+
127+
+ /**
128+
+ * @brief Overflow policy for HW-learned flows
129+
+ *
130+
+ * Operator intent for how an implementation behaves when it cannot
131+
+ * admit a newly seen flow because per-flow auto-learn capacity is
132+
+ * exhausted. The cause of overflow is implementation-specific; the
133+
+ * policy response is expressed as an observable outcome.
134+
+ *
135+
+ * SAI_TAM_FLOW_OVERFLOW_POLICY_REJECT_NEW is the default and is the
136+
+ * baseline behavior every implementation supports. An implementation
137+
+ * that cannot honor a requested policy returns
138+
+ * SAI_STATUS_ATTR_NOT_SUPPORTED_0 on set.
139+
+ *
140+
+ * @type sai_tam_flow_overflow_policy_t
141+
+ * @flags CREATE_AND_SET
142+
+ * @default SAI_TAM_FLOW_OVERFLOW_POLICY_REJECT_NEW
143+
+ * @validonly SAI_SWITCH_ATTR_TAM_FLOW_AUTO_LEARN_ENABLE == true
144+
+ */
145+
+ SAI_SWITCH_ATTR_TAM_FLOW_AUTO_LEARN_OVERFLOW_POLICY,
146+
```
147+
148+
## Overflow policy type
149+
150+
The following enum is added to `saitypes.h`. Policies are expressed as observable outcomes; the selection of which existing flow (if any) is displaced, and the internal capacity model, are implementation-defined — keeping the contract vendor-neutral.
151+
152+
```
153+
+/**
154+
+ * @brief TAM per-flow auto-learn overflow policy
155+
+ */
156+
+typedef enum _sai_tam_flow_overflow_policy_t
157+
+{
158+
+ /**
159+
+ * @brief Preserve existing flows; reject the new flow
160+
+ *
161+
+ * Flows already being tracked continue unchanged. Visibility of the
162+
+ * new flow is lost until aging or eviction frees capacity, and
163+
+ * SAI_SWITCH_ATTR_TAM_FLOW_AUTO_LEARN_FAILURES is incremented.
164+
+ */
165+
+ SAI_TAM_FLOW_OVERFLOW_POLICY_REJECT_NEW,
166+
+
167+
+ /**
168+
+ * @brief Admit the new flow; displace an existing flow
169+
+ *
170+
+ * The new flow is admitted by evicting one existing tracked flow.
171+
+ * Which flow is chosen (e.g. oldest, least-recently-active) is
172+
+ * implementation-defined. The evicted flow's final record carries
173+
+ * flowEndReason = "lack of resources" (RFC 7011 / RFC 5102).
174+
+ */
175+
+ SAI_TAM_FLOW_OVERFLOW_POLICY_EVICT_EXISTING,
176+
+
177+
+ /*
178+
+ * This enum can be extended with additional values in future
179+
+ * proposals as vendor capabilities converge.
180+
+ */
181+
+} sai_tam_flow_overflow_policy_t;
182+
```
183+
184+
## IPFIX record schema
185+
186+
The FLOW tel_type's IPFIX template is generated by the implementation and retrievable via the existing `SAI_TAM_TEL_TYPE_ATTR_IPFIX_TEMPLATES` attribute. Each record carries, per active flow, per interval:
187+
188+
- Flow key values (one Information Element per key field). The specific fields are determined by the implementation and advertised in the template. For operator-configured flows, the keys correspond to the referencing ACL entry's match fields; for HW-learned flows, the keys correspond to whatever the implementation keys on autonomously.
189+
- Statistics (packet count and byte count at minimum; additional per-flow counters if the implementation supports them and includes them in the advertised template).
190+
- Snapshot timestamp (`observationTimeNanoseconds` — existing RFC 7011 IE).
191+
- Flow-end reason (`flowEndReason` — existing RFC 7011 IE) on the final record for a flow: idle timeout (HW auto-learn aging), operator-initiated end (ACL entry removed), or lack of resources (overflow eviction under `EVICT_EXISTING`).
192+
- IPFIX sequence number (existing RFC 7011 field) so consumers can detect gaps in the stream.
193+
194+
On flow termination, the implementation SHOULD emit one final record with a non-zero `flowEndReason` before releasing the flow's HW state, so that consumers capture the last counter increment between the previous interval snapshot and termination. This is SHOULD rather than MUST because some implementations may not guarantee it under memory-pressure eviction.
195+
196+
## Cleanup semantics
197+
198+
Stated as a semantic contract, not a prescribed mechanism:
199+
200+
- **HW-learned flows — idle reclaim.** A HW-learned flow idle for longer than `SAI_SWITCH_ATTR_TAM_FLOW_AUTO_LEARN_AGING_TIME_S` must eventually be reclaimed. The final record's `flowEndReason` indicates idle timeout. The detection interval and mechanism are implementation-defined.
201+
- **HW-learned flows — capacity exhaustion.** When the implementation cannot admit a new flow because per-flow capacity is exhausted, behavior follows `SAI_SWITCH_ATTR_TAM_FLOW_AUTO_LEARN_OVERFLOW_POLICY`. Under `REJECT_NEW` (the default), the new flow is not learned, existing flows continue unchanged, and `SAI_SWITCH_ATTR_TAM_FLOW_AUTO_LEARN_FAILURES` is incremented so operators can observe the rejection rate. Under `EVICT_EXISTING`, the new flow is admitted by displacing one existing flow (selection implementation-defined); the evicted flow emits a final record with `flowEndReason` = "lack of resources", and `_FAILURES` does not increment.
202+
- **HW-learned flows — auto-learn disable.** When `SAI_SWITCH_ATTR_TAM_FLOW_AUTO_LEARN_ENABLE` transitions from true to false, the implementation stops autonomously identifying new flows. The handling of HW-learned flows that were already being tracked at the moment of the transition is implementation-defined.
203+
- **Operator-configured flows — operator-driven.** Flows exist as long as their defining ACL entries exist. Removal of an ACL entry (or clearing its `ACTION_TAM_OBJECT` action) causes the corresponding flow to be terminated and a final record emitted. Operator-configured flows do not age; the operator owns their lifetime.
204+
- **Tel_type destruction.** When the application removes the FLOW tel_type object, all state the implementation was maintaining for that tel_type must be released. Whether a final-record burst precedes destruction is implementation-defined; consumers must not depend on it.
205+
206+
## Sample workflow
207+
208+
The following sequences show HW auto-learn mode and operator-configured mode. In practice, both can be active simultaneously — the two mechanisms are independent and either or both can populate flows into the FLOW tel_type.
209+
210+
### Common setup — TAM report and tel_type
211+
212+
1. Create Transport and Collector objects (existing TAM machinery, not shown).
213+
214+
2. Create a report object (for example, of type `SAI_TAM_REPORT_TYPE_IPFIX`).
215+
216+
```
217+
count = 0;
218+
attr_list[count].id = SAI_TAM_REPORT_ATTR_TYPE;
219+
attr_list[count].value.s32 = SAI_TAM_REPORT_TYPE_IPFIX;
220+
count++;
221+
attr_list[count].id = SAI_TAM_REPORT_ATTR_REPORT_MODE;
222+
attr_list[count].value.s32 = SAI_TAM_REPORT_MODE_ALL;
223+
count++;
224+
225+
rc = tam_api_p->create_tam_report(&tam_report_id, switch_id, count, attr_list);
226+
```
227+
228+
3. Create a FLOW tel_type. The tel_type is the same regardless of how flows will be populated.
229+
230+
```
231+
count = 0;
232+
attr_list[count].id = SAI_TAM_TEL_TYPE_ATTR_TAM_TELEMETRY_TYPE;
233+
attr_list[count].value.s32 = SAI_TAM_TELEMETRY_TYPE_FLOW;
234+
count++;
235+
attr_list[count].id = SAI_TAM_TEL_TYPE_ATTR_REPORT_ID;
236+
attr_list[count].value.oid = tam_report_id;
237+
count++;
238+
attr_list[count].id = SAI_TAM_TEL_TYPE_ATTR_FLOW_REPORT_INTERVAL_US;
239+
attr_list[count].value.u32 = 500000; /* 500 ms cadence */
240+
count++;
241+
242+
rc = tam_api_p->create_tam_tel_type(&tam_tel_type_id, switch_id, count, attr_list);
243+
```
244+
245+
4. Create the TAM object referencing the tel_type and bind it at switch scope.
246+
247+
```
248+
count = 0;
249+
attr_list[count].id = SAI_TAM_ATTR_TELEMETRY_OBJECTS_LIST;
250+
attr_list[count].value.objlist.count = 1;
251+
attr_list[count].value.objlist.list = (sai_object_id_t *)malloc(1*sizeof(sai_object_id_t));
252+
attr_list[count].value.objlist.list[0] = tam_tel_type_id;
253+
count++;
254+
255+
bind_point_types[0] = SAI_TAM_BIND_POINT_TYPE_SWITCH;
256+
attr_list[count].id = SAI_TAM_ATTR_TAM_BIND_POINT_TYPE_LIST;
257+
attr_list[count].value.s32list.count = 1;
258+
attr_list[count].value.s32list.list = bind_point_types;
259+
count++;
260+
261+
rc = tam_api_p->create_tam(&tam_id, switch_id, count, attr_list);
262+
```
263+
264+
5. Query the IPFIX template the implementation will use. The existing `SAI_TAM_TEL_TYPE_ATTR_IPFIX_TEMPLATES` attribute returns the FLOW-mode record template once the tel_type is sufficiently configured (`REPORT_ID` set).
265+
266+
```
267+
attr.id = SAI_TAM_TEL_TYPE_ATTR_IPFIX_TEMPLATES;
268+
attr.value.u8list.list = template_buf;
269+
attr.value.u8list.count = sizeof(template_buf);
270+
271+
rc = tam_api_p->get_tam_tel_type_attribute(tam_tel_type_id, 1, &attr);
272+
```
273+
274+
### Enable HW autonomous flow learning
275+
276+
6. Enable HW auto-learn at the switch level. This is independent of any FLOW tel_type — it is a switch-wide capability that, when enabled, causes the implementation to autonomously identify flows.
277+
278+
```
279+
count = 0;
280+
attr_list[count].id = SAI_SWITCH_ATTR_TAM_FLOW_AUTO_LEARN_ENABLE;
281+
attr_list[count].value.booldata = true;
282+
count++;
283+
attr_list[count].id = SAI_SWITCH_ATTR_TAM_FLOW_AUTO_LEARN_AGING_TIME_S;
284+
attr_list[count].value.u32 = 60; /* 60 s idle timeout */
285+
count++;
286+
287+
for (int i = 0; i < count; i++) {
288+
rc = switch_api_p->set_switch_attribute(switch_id, &attr_list[i]);
289+
}
290+
```
291+
292+
7. Periodically read the switch-level learn-failure count to observe rejection rate.
293+
294+
```
295+
attr.id = SAI_SWITCH_ATTR_TAM_FLOW_AUTO_LEARN_FAILURES;
296+
rc = switch_api_p->get_switch_attribute(switch_id, 1, &attr);
297+
/* attr.value.u64 = cumulative count of rejected learn attempts */
298+
```
299+
300+
8. To disable HW auto-learn later, set the enable attribute to false. The handling of currently-tracked HW-learned flows at that moment is implementation-defined.
301+
302+
```
303+
attr.id = SAI_SWITCH_ATTR_TAM_FLOW_AUTO_LEARN_ENABLE;
304+
attr.value.booldata = false;
305+
rc = switch_api_p->set_switch_attribute(switch_id, &attr);
306+
```
307+
308+
### Operator-configured flows
309+
310+
To monitor specific flows the operator defines (independently of, or in addition to, HW auto-learn):
311+
312+
9. Create an ACL table with `SAI_ACL_ACTION_TYPE_TAM_OBJECT` in its allowed action list.
313+
314+
```
315+
count = 0;
316+
action_list[0] = SAI_ACL_ACTION_TYPE_TAM_OBJECT;
317+
attr_list[count].id = SAI_ACL_TABLE_ATTR_ACL_ACTION_TYPE_LIST;
318+
attr_list[count].value.s32list.list = action_list;
319+
attr_list[count].value.s32list.count = 1;
320+
count++;
321+
/* ... other standard ACL table attributes (stage, bind points, match fields) ... */
322+
323+
rc = acl_api_p->create_acl_table(&acl_table_oid, switch_id, count, attr_list);
324+
```
325+
326+
10. Add ACL entries, each with the `TAM_OBJECT` action pointing at the TAM object. Each such entry defines one monitored flow.
327+
328+
```
329+
count = 0;
330+
attr_list[count].id = SAI_ACL_ENTRY_ATTR_TABLE_ID;
331+
attr_list[count].value.oid = acl_table_oid;
332+
count++;
333+
attr_list[count].id = SAI_ACL_ENTRY_ATTR_FIELD_SRC_IP;
334+
attr_list[count].value.aclfield.enable = true;
335+
attr_list[count].value.aclfield.data.ip4 = 0x0a000001;
336+
attr_list[count].value.aclfield.mask.ip4 = 0xffffffff;
337+
count++;
338+
/* ... other match fields defining this flow ... */
339+
340+
attr_list[count].id = SAI_ACL_ENTRY_ATTR_ACTION_TAM_OBJECT;
341+
attr_list[count].value.aclaction.parameter.oid = tam_id;
342+
attr_list[count].value.aclaction.enable = true;
343+
count++;
344+
345+
rc = acl_api_p->create_acl_entry(&acl_entry_oid, switch_id, count, attr_list);
346+
```
347+
348+
The ACL entry's action tells the implementation "monitor this flow under the referenced TAM object's policy." Removing the entry (or clearing its action) ends that flow's monitoring; a final IPFIX record is emitted.
349+
350+
## Composition with existing TAM machinery
351+
352+
- The FLOW tel_type binds through existing TAM machinery (`SAI_TAM_ATTR_TELEMETRY_OBJECTS_LIST` on the parent TAM object, bound at `SAI_TAM_BIND_POINT_TYPE_SWITCH` by default). No new bind-point type is introduced.
353+
- Operator-configured flows use `SAI_ACL_ENTRY_ATTR_ACTION_TAM_OBJECT`, which already exists in `saiacl.h`. No new ACL attribute or action type is introduced by this proposal.
354+
- The FLOW tel_type coexists with the existing event-side flow telemetry (`SAI_TAM_EVENT_TYPE_FLOW_STATE / _WATCHLIST / _TCPFLAG`). The two paths are independent and composable.
355+
- `SAI_TAM_TELEMETRY_TYPE_COUNTER_SUBSCRIPTION` is unchanged; it continues to serve object-counter telemetry (PORT / QUEUE / BUFFER_POOL / INGRESS_PRIORITY_GROUP).

0 commit comments

Comments
 (0)