Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PSA Proposed description of PortId_t translation in PSA #562

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 156 additions & 9 deletions p4-16/psa/PSA.mdk
Original file line number Diff line number Diff line change
Expand Up @@ -276,22 +276,19 @@ few packets dropped for these reasons (perhaps only one).

# PSA Data types

## PSA type definitions
## PSA type definitions {#sec-psa-type-definitions}

Each PSA implementation will have specific bit widths for the
following types. These widths should be defined in the target
specific implementation of the PSA file.
specific implementation of the `psa.p4` include file.

The P4 Runtime API plans to use 64-bit values to hold values of these
types. Typical PSA implementations are expected to choose
significantly smaller sizes than 64 bits for the data plane
implementation of these types. If for some reason a PSA
implementation chooses to use more than 64 bits for one or more of
these types, note that the P4 Runtime API will be able to take
advantage of at most 64 of those bits.
The P4 Runtime API uses 64-bit values to hold values of these types.
All PSA implementations must use sizes for these types no wider than
the corresponding types with `InHeader_t` in their names shown below.

```
[INCLUDE=psa.p4:Type_defns]
[INCLUDE=psa.p4:Type_defns2]
```

## PSA supported metadata types
Expand All @@ -309,6 +306,156 @@ Additional supported match_kind types
```


## Data plane vs. control plane data representations

A PSA data plane implementation that supports the P4 Runtime
API[^P4RuntimeAPI] includes software called a "P4 Runtime Agent" that
enables runtime programming of the PSA device from a controller. A
controller may control multiple devices with different PSA
implementations. We will refer to these pieces of software as an
"agent" and "controller" for brevity.

As mentioned in section [#sec-psa-type-definitions], different PSA
implementations are expected to customize the size of the data types
that refer directly to those objects, i.e. ports, multicast group ids,
etc.

Some PSA implementations are expected to use noticeably fewer
resources for things like table keys and action parameters if the data
plane stores only the fewest number of bits required for values of
these types, for that implementation. For example, an implementation
that defines `PortId_t` as `bit<6>` instead of `bit<16>`, and can take
advantage of this difference, could save 10 Mbits of storage in a
table with a million entries[^CacheCost].

[^CacheCost]: While 10 Mbits sounds tiny to one accustomed to computers with hundreds of gigabytes of DRAM, the highest speed PSA implementations are ASICs that must keep tables in on-chip memories, similar to caches in general purpose CPUs. The Intel i9-7980XE released in 2017 has 198 Mbits of on-chip L3 cache shared by its CPU cores. Among Intel processors in Intel's 7th generation Core released in 2017 with at least 100 Mbits of L3 cache, they all cost close to $9 per Mbit of L3 cache. https://en.wikipedia.org/wiki/List_of_Intel_microprocessors

The P4 Runtime API uses 64-bit quantities to hold values of the types
listed in section [#sec-psa-type-definitions] to simplify the
manipulation of these values in the agent software. For control plane
operations on tables, any trimming or padding of values will be
performed in the agent (trimming from controller to device direction,
padding in device to controller direction).

There are multiple channels of communication over which such values
might be carried between the controller and the data plane. These
channels of communication include:

+ Control plane operations on tables, where values of these types may
be included as part of the key, or as an action parameter.
+ Packets sent to the CPU ("packet in" from the controller's
perspective), or received from the CPU ("packet out" from the
controller's perspective).
+ Fields in a `Digest` extern notification message (section
[#sec-packet-digest]).
+ Fields in the data contents of a `Register` array (section
[#sec-registers]).

Note: There may be other channels not listed above.

For packets between the control plane and the PSA device, there is the
issue that many PSA implementations are expected to restrict P4
programs to have headers with fields with a total length that is a
multiple of 8 bits. To make it possible to define P4 header types
that meet this restriction, and contain values of fields with these
PSA-specific types, and be source-compatible across multiple PSA
implementations, additional types are defined that contain `InHeader` in
their name. For example, `PortIdInHeader_t` is the same as `PortId_t`,
except it must be a multiple of 8 bits long, and contain at least as
many bits as `PortId_t` does.

Because these types are guaranteed to be a multiple of 8 bits long,
you may include any combination of them in a P4 header type
definition, as long as the other fields in the header satisfy the
multiple of 8 bits restriction. The controller or P4 program
generating packets with such headers should fill in any most
significant "padding" bits with 0. You may do this with a normal
assignment in statement in your P4 program, where the value on the
right hand side is cast to the wider `InHeader` type. Similarly,
casting a value of a wider type such as `PortIdInHeader_t` to the
corresponding narrower type `PortId_t` truncates the excess most
significant bits as part of the cast.

Values of type `PortId_t` have a distinctive property in PSA
implementations. Because it can make some hardware implementations
more straightforward, the numerical values of fields with type
`PortId_t` in the P4 data plane might not be a simple range of values
such as 0 through 31, as one might choose when writing control plane
software for a 32-port device. The agent is expected to implement
numerical translation between controller port id values and data plane
port id values, for each of the channels of communication between the
controller and data plane described above.

Occurrences of values of type `PortId_t` or `PortIdInHeader_t` must
include a `port_translation` annotation so the compiler can mark
occurrences of these values appropriately, so the agent can know which
values need this translation. See the example P4 header definition
below.

```
[INCLUDE=examples/psa-example-parser-error-handling.p4:PortId_Annotation_Example]
```

Because of this translation, we recommend that values of type
`PortId_t` be treated similarly to values of an `enum` type.
Comparing two values of this type for equality or inequality is
reasonable, as well as assigning the values to other variables or
parameters of the same type, but nearly any other operations are prone
to error. When matching values of type `PortId_t` as part of a table
key, always match a complete value exactly, or wildcard every bit of
the value (i.e. a `ternary` match kind with all bits wildcard, or an
`lpm` match kind with prefix length 0). If you attempt to do any of
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe P4Runtime disallows such behavior: all wildcards or LPM with prefix of 0. @antoninbas can confirm.

Copy link
Collaborator Author

@jafingerhut jafingerhut Feb 1, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cc10512 I am pretty sure P4Runtime allows one to create table entries that completely wildcard ternary fields, or do length 0 prefix of lpm fields -- it is just that they represent it in a way where the field isn't even mentioned in the P4Runtime API message.

My reference for this is this recent PR, including my clarifying question and Antonin's answer: p4lang/PI#293

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jafingerhut is correct; we do allow wildcards for ternary & LPM but require a certain formatting for the messages.

the following things on a value with type `PortId_t`, the numerical
translation performed may lead to functional errors in your program:

+ Do a table key match on a subset of the bits, or a range match.
+ Compare port values with relational operators like `<` or `>`.
+ Perform arithmetic on the value, and expect to get a value that
corresponds to another port of the device. Some numerical values
may not correspond to any port of the device, and the values
corresponding to ports need not be consecutive.

The list above is not intended to be exhaustive.

All of the comments above apply to all types where numerical
translation occurs between the controller and the data plane.
Below is a complete list of such types, and an example of the
annotation to use in your P4 program.

+ `PortId_t` or `PortIdInHeader_t` - annotation `@p4runtime_translation("port")`
+ `ClassOfService_t` or `ClassOfServiceInHeader_t` - annotation `@p4runtime_translation("cos")`

For the types listed below, no numerical translation occurs, and the
data plane must support all numerical values from 0 up to the maximum
value that it supports. Except for `Timestamp_t` values, the number
of values supported by the data plane need not be a power of 2.
Controllers must have a way to discover each PSA device's maximum
supported value for each of these types.

+ `MulticastGroup_t`
+ `CloneSessionId_t`
+ `PacketLength_t`
+ `EgressInstance_t`
+ `Timestamp_t`

~ TBD
It is not clear yet whether it is reasonable to use a value of type
`PortId_t` as an index to of a `Register` extern, or of an indexed
`Counter` or `Meter` extern. A likely result of attempting to do this
in a P4 program is that the indexes accessed will be the data plane
specific values of `PortId_t`, and when the control plane attempts to
access values in these externs, the numerical translation may not be
performed on the index. It should be reasonable to use a
`DirectCounter` or `DirectMeter` with a table key that includes a
`PortId_t` field, as long as it is annotated as described above,
because the agent port translation should be performed for table keys.

If this is not a reasonable thing to do, the example program
psa-example-counters.p4 should be modified so that it does not do
this, and scrub all other examples for any similar behavior.
~


# Programmable blocks {#sec-programmable-blocks}

The following declarations provide a template for the programmable
Expand Down
17 changes: 12 additions & 5 deletions p4-16/psa/examples/psa-example-parser-error-handling.p4
Original file line number Diff line number Diff line change
Expand Up @@ -165,16 +165,23 @@ struct empty_metadata_t {
struct fwd_metadata_t {
}

// This example assumes that PortId_t is at most 20 bits.
typedef bit<20> ToCpuErrorHeaderPortId_t;

// BEGIN:PortId_Annotation_Example
header to_cpu_error_header_t {
bit<8> error_idx;
bit<1> ingress;
bit<3> packet_path;
bit<4> reserved1;

// port is ingress_port if ingress is 1, else egress_port
ToCpuErrorHeaderPortId_t port;

// This annotation is needed by the P4 compiler and tool chain so
// that a P4 Runtime API agent knows that this is a field of type
// PortId_t or PortIdInHeader_t that needs its values numerically
// translated between the control plane and data plane.
@p4runtime_translation("port")
PortIdInHeader_t port;
}
// END:PortId_Annotation_Example

enum CloneReason_t {
NONE,
Expand Down Expand Up @@ -372,7 +379,7 @@ control handle_parser_errors(
to_cpu_error_hdr.setValid();
to_cpu_error_hdr.error_idx = error_idx;
packet_path_to_bits.apply(to_cpu_error_hdr.packet_path, packet_path);
to_cpu_error_hdr.port = (ToCpuErrorHeaderPortId_t) port;
to_cpu_error_hdr.port = (PortIdInHeader_t) port;
}
}

Expand Down
22 changes: 22 additions & 0 deletions p4-16/psa/psa.p4
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,28 @@ const CloneSessionId_t PSA_CLONE_SESSION_TO_CPU = unspecified;

#endif

// BEGIN:Type_defns2

/* Note: All of the widths for types with `InHeader` in their name are
* intended only to carry values of the corresponding types in packet
* headers between a controller and a PSA device. The widths are
* intended to be large enough for all PSA devices, so that a
* controller can fill in headers with these fields in a common way
* for all PSA devices.
*
* All widths must be a multiple of 8, so that any subset of these
* fields may be used in a single P4 header definition, even on P4
* implementations that restrict headers to contain fields with a
* total length that is a multiple of 8 bits. */
typedef bit<16> PortIdInHeader_t;
typedef bit<32> MulticastGroupInHeader_t;
typedef bit<16> CloneSessionIdInHeader_t;
typedef bit<8> ClassOfServiceInHeader_t;
typedef bit<16> PacketLengthInHeader_t;
typedef bit<16> EgressInstanceInHeader_t;
typedef bit<64> TimestampInHeader_t;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we explicitly state that we do not need translation annotations for these other types?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vgurevich @antoninbas @samar-abdi Any thoughts on whether any of these other types would need conversion of values?

Can the control plane API just use whatever multicast group numbers and egress instance numbers it wants? I assume different implementations will have different numbers of these things that they support, and some might have more restricted numerical ranges than others. Does a P4Runtime API controller have a way to discover such restrictions on these values for different PSA devices in a heterogenous network?

Do we expect ClassOfService_t values to be numerically consecutive in a range [0, N] for all PSA devices, and should say that in PSA?

Same question for CloneSessionId values.

I think we are safe from such issues for PacketLength_t and Timestamp_t values, i.e. some implementations might have different maximum PacketLength_t values they actually support, but all of them will be [my_min, my_max] for packet length support for some my_min, my_max integers, and timestamps should be all X-bit-wide values for the value of X supported by the implementation.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very likely class of service will need translation. It has the same issue as ports. Different controllers may have different notions of classes of service and their own numbering scheme for them. The switch config handler on the target will map the controller's class of service to the ASIC's. Other metadata values are either programmed by the controller (mcast_group/egress instance) or have universally agreed values (packet_length/timestamp) IMO.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we should explicitly document that type ClassOfService_t fields will be translated between controller and P4 data plane programs, as PortId_t fields will be, but no others will ?

Do we expect, and should we perhaps document, that for all of the following types, the data plane and the control plane will both support all numerical values in the range [0, N] for whatever maximum value of N is supported by the PSA device (which can vary from one PSA implementation to another)?

typedef bit<unspecified> MulticastGroup_t;
typedef bit<unspecified> CloneSessionId_t;
typedef bit<unspecified> EgressInstance_t;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes for documenting translation of ClassOfService_t field values. The P4Runtime annotation will be cos_translation("controller_bitwidth:32").

As for your second point, I suggest : "The values of multicast_group, clone_session and egress_instance are completely in the purview of the controller, and are limited only by the bitwidth of the respective types on the PSA device(s) being controlled by the controller."

I believe it is implicit that if a psa.p4 has bit<7> multicast_group_id, it will allow all values from 0 to 127.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know it is common for things in computers to have sizes that are powers of 2, but it sometimes happens that people implement SRAMs with a number of entries that aren't powers of 2 (or a collection of them whose total number of entries is not a power of 2, more likely, but have a common address space spanning all of them).

I would hate for a controller to have an easily avoided bug if this turns out to be the case, because it made such power-of-2 size assumption.

// END:Type_defns2

// BEGIN:Metadata_types
enum PSA_PacketPath_t {
NORMAL, /// Packet received by ingress that is none of the cases below.
Expand Down